diff --git a/Animations/pom.xml b/Animations/pom.xml new file mode 100644 index 0000000..8e7b4c1 --- /dev/null +++ b/Animations/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + + fr.radi3nt + JavaUtil + 1.0 + + + Animations + + + 8 + 8 + UTF-8 + + + + fr.radi3nt + MathsHelper + 1.0 + compile + + + fr.radi3nt + SplineHelper + 1.0 + compile + + + + \ No newline at end of file diff --git a/Animations/src/main/java/fr/radi3nt/animations/AnimatedPropertyProvider.java b/Animations/src/main/java/fr/radi3nt/animations/AnimatedPropertyProvider.java new file mode 100644 index 0000000..4702fe1 --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/AnimatedPropertyProvider.java @@ -0,0 +1,14 @@ +package fr.radi3nt.animations; + +import fr.radi3nt.animations.channels.ChannelIdentifier; + +import java.util.Set; +import java.util.function.Supplier; + +public interface AnimatedPropertyProvider { + + void incrementTime(float delta); + Supplier get(ChannelIdentifier identifier); + + void addConcernedObjects(Set objects); +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/AnimationClip.java b/Animations/src/main/java/fr/radi3nt/animations/AnimationClip.java new file mode 100644 index 0000000..fe04e22 --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/AnimationClip.java @@ -0,0 +1,19 @@ +package fr.radi3nt.animations; + +import fr.radi3nt.animations.channels.ChannelIdentifier; +import fr.radi3nt.animations.channels.KeyframeChannel; + +import java.util.Map; + +public class AnimationClip { + + public final float duration; + public final Map> channels; + + public AnimationClip(float duration, Map> channels) { + this.duration = duration; + this.channels = channels; + } + + +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/ProcessedAnimation.java b/Animations/src/main/java/fr/radi3nt/animations/ProcessedAnimation.java new file mode 100644 index 0000000..cfc8ba7 --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/ProcessedAnimation.java @@ -0,0 +1,58 @@ +package fr.radi3nt.animations; + +import fr.radi3nt.animations.channels.ChannelIdentifier; +import fr.radi3nt.animations.channels.KeyframeChannel; + +import java.util.Set; +import java.util.function.Supplier; + +public class ProcessedAnimation implements AnimatedPropertyProvider { + + public final AnimationClip animationClip; + private final boolean looping; + private float relativeTime; + + public ProcessedAnimation(AnimationClip animationClip, boolean looping) { + this.animationClip = animationClip; + this.looping = looping; + } + + public ProcessedAnimation(AnimationClip animationClip, boolean looping, float relativeTime) { + this.animationClip = animationClip; + this.looping = looping; + this.relativeTime = relativeTime; + } + + public float getRelativeTime() { + return relativeTime; + } + + public boolean isDone() { + return relativeTime>animationClip.duration; + } + + @Override + public void incrementTime(float delta) { + relativeTime+=delta; + if (isDone() && looping) + relativeTime-=animationClip.duration; + } + + @Override + public Supplier get(ChannelIdentifier identifier) { + return () -> { + KeyframeChannel keyframeChannel = animationClip.channels.get(identifier); + if (keyframeChannel==null) + return null; + return (A) keyframeChannel.transform(relativeTime); + }; + } + + @Override + public void addConcernedObjects(Set objects) { + for (ChannelIdentifier channelIdentifier : animationClip.channels.keySet()) { + objects.add(channelIdentifier.objectName); + } + } + +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/channels/ChannelIdentifier.java b/Animations/src/main/java/fr/radi3nt/animations/channels/ChannelIdentifier.java new file mode 100644 index 0000000..2483d97 --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/channels/ChannelIdentifier.java @@ -0,0 +1,45 @@ +package fr.radi3nt.animations.channels; + +import java.util.Objects; + +public class ChannelIdentifier { + + public final String objectName; + public final String channelType; + + public ChannelIdentifier(String objectName, String channelType) { + this.objectName = objectName; + this.channelType = channelType; + } + + public static ChannelIdentifier translation(String objectName) { + return new ChannelIdentifier(objectName, "translate"); + } + + + public static ChannelIdentifier rotation(String objectName) { + return new ChannelIdentifier(objectName, "rotate"); + } + + + public static ChannelIdentifier scale(String objectName) { + return new ChannelIdentifier(objectName, "scale"); + } + + + @Override + public final boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ChannelIdentifier)) return false; + + ChannelIdentifier that = (ChannelIdentifier) o; + return Objects.equals(objectName, that.objectName) && Objects.equals(channelType, that.channelType); + } + + @Override + public int hashCode() { + int result = Objects.hashCode(objectName); + result = 31 * result + Objects.hashCode(channelType); + return result; + } +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/channels/KeyframeChannel.java b/Animations/src/main/java/fr/radi3nt/animations/channels/KeyframeChannel.java new file mode 100644 index 0000000..ba08860 --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/channels/KeyframeChannel.java @@ -0,0 +1,7 @@ +package fr.radi3nt.animations.channels; + +public interface KeyframeChannel { + + A transform(float t); + +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/channels/actors/AnimationActor.java b/Animations/src/main/java/fr/radi3nt/animations/channels/actors/AnimationActor.java new file mode 100644 index 0000000..cff6813 --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/channels/actors/AnimationActor.java @@ -0,0 +1,4 @@ +package fr.radi3nt.animations.channels.actors; + +public interface AnimationActor { +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/channels/actors/AnimationTransformActor.java b/Animations/src/main/java/fr/radi3nt/animations/channels/actors/AnimationTransformActor.java new file mode 100644 index 0000000..df24997 --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/channels/actors/AnimationTransformActor.java @@ -0,0 +1,18 @@ +package fr.radi3nt.animations.channels.actors; + +import fr.radi3nt.maths.components.advanced.matrix.Matrix4x4; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; + +public interface AnimationTransformActor extends AnimationActor { + + void setTranslation(Vector3f translation); + void setRotation(Quaternion rotation); + void setScale(Vector3f scale); + + Matrix4x4 getTransform(); + Vector3f getTranslation(); + Quaternion getRotation(); + Vector3f getScale(); + +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/channels/types/KeyframeSplitEulerXYZRotationChannel.java b/Animations/src/main/java/fr/radi3nt/animations/channels/types/KeyframeSplitEulerXYZRotationChannel.java new file mode 100644 index 0000000..516640c --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/channels/types/KeyframeSplitEulerXYZRotationChannel.java @@ -0,0 +1,26 @@ +package fr.radi3nt.animations.channels.types; + +import fr.radi3nt.spline.interpolation.InterpolationData; +import fr.radi3nt.maths.components.advanced.matrix.angle.JavaMathAngle; +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; + +public class KeyframeSplitEulerXYZRotationChannel extends KeyframeSplitInterpolationDataChannel { + + private final float relativeDuration; + + public KeyframeSplitEulerXYZRotationChannel(InterpolationData[] interpolationData, float relativeDuration) { + super(interpolationData); + this.relativeDuration = relativeDuration; + } + + @Override + public Quaternion transform(float t) { + float transformedTime = t/ relativeDuration; + float x = interpolationData[0]==null ? 0 : interpolationData[0].interpolate(transformedTime); + float y = interpolationData[1]==null ? 0 : interpolationData[1].interpolate(transformedTime); + float z = interpolationData[2]==null ? 0 : interpolationData[2].interpolate(transformedTime); + + return ComponentsQuaternion.fromEulerAngles(JavaMathAngle.fromDegree(x), JavaMathAngle.fromDegree(y), JavaMathAngle.fromDegree(z)); + } +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/channels/types/KeyframeSplitInterpolationDataChannel.java b/Animations/src/main/java/fr/radi3nt/animations/channels/types/KeyframeSplitInterpolationDataChannel.java new file mode 100644 index 0000000..ad4697a --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/channels/types/KeyframeSplitInterpolationDataChannel.java @@ -0,0 +1,17 @@ +package fr.radi3nt.animations.channels.types; + +import fr.radi3nt.animations.channels.KeyframeChannel; +import fr.radi3nt.spline.interpolation.InterpolationData; + +public abstract class KeyframeSplitInterpolationDataChannel implements KeyframeChannel { + + protected final InterpolationData[] interpolationData; + + protected KeyframeSplitInterpolationDataChannel(InterpolationData[] interpolationData) { + this.interpolationData = interpolationData; + } + + public InterpolationData[] getInterpolationData() { + return interpolationData; + } +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/channels/types/KeyframeSplitVectorChannel.java b/Animations/src/main/java/fr/radi3nt/animations/channels/types/KeyframeSplitVectorChannel.java new file mode 100644 index 0000000..0459c4a --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/channels/types/KeyframeSplitVectorChannel.java @@ -0,0 +1,25 @@ +package fr.radi3nt.animations.channels.types; + +import fr.radi3nt.spline.interpolation.InterpolationData; +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; + +public class KeyframeSplitVectorChannel extends KeyframeSplitInterpolationDataChannel { + + private final float relativeDuration; + + public KeyframeSplitVectorChannel(InterpolationData[] interpolationData, float relativeDuration) { + super(interpolationData); + this.relativeDuration = relativeDuration; + } + + @Override + public Vector3f transform(float t) { + float transformedTime = t/ relativeDuration; + float x = interpolationData[0].interpolate(transformedTime); + float y = interpolationData[1].interpolate(transformedTime); + float z = interpolationData[2].interpolate(transformedTime); + return new SimpleVector3f(x, y, z); + } + +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/channels/types/KeyframeTranslateChannel.java b/Animations/src/main/java/fr/radi3nt/animations/channels/types/KeyframeTranslateChannel.java new file mode 100644 index 0000000..b1cdbe6 --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/channels/types/KeyframeTranslateChannel.java @@ -0,0 +1,47 @@ +package fr.radi3nt.animations.channels.types; + +import fr.radi3nt.animations.channels.KeyframeChannel; +import fr.radi3nt.spline.interpolation.InterpolationData; +import fr.radi3nt.maths.components.vectors.Vector3f; + +public class KeyframeTranslateChannel implements KeyframeChannel { + + private final InterpolationData interpolationData; + private final Vector3f[] positions; + private final float[] times; + + public KeyframeTranslateChannel(InterpolationData interpolationData, Vector3f[] positions, float[] times) { + this.interpolationData = interpolationData; + this.positions = positions; + this.times = times; + } + + @Override + public Vector3f transform(float t) { + float lastTime = 0; + int lastTimeIndex = 0; + float nextTime = 0; + for (int i = 0; i < times.length; i++) { + float time = times[i]; + nextTime = time; + if (time > t) + break; + lastTime = time; + lastTimeIndex = i; + } + if (lastTimeIndex==positions.length) + return positions[positions.length-1]; + + int nextTimeIndex = lastTimeIndex+1; + + float duration = nextTime - lastTime; + + float interpolated = interpolationData.interpolate((t-lastTime) / duration); + + Vector3f start = positions[lastTimeIndex]; + Vector3f end = positions[nextTimeIndex]; + + return start.duplicate().mul(1-interpolated).add(end.duplicate().mul(interpolated)); + } + +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/timeline/AnimationTimeline.java b/Animations/src/main/java/fr/radi3nt/animations/timeline/AnimationTimeline.java new file mode 100644 index 0000000..77bfad8 --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/timeline/AnimationTimeline.java @@ -0,0 +1,13 @@ +package fr.radi3nt.animations.timeline; + +import fr.radi3nt.animations.channels.ChannelIdentifier; + +import java.util.Collection; + +public interface AnimationTimeline { + + void update(float delta); + T get(ChannelIdentifier channelIdentifier, T defaultResult); + Collection getAvailableObjects(); + +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/timeline/LayeredAnimationTimeline.java b/Animations/src/main/java/fr/radi3nt/animations/timeline/LayeredAnimationTimeline.java new file mode 100644 index 0000000..abf6883 --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/timeline/LayeredAnimationTimeline.java @@ -0,0 +1,49 @@ +package fr.radi3nt.animations.timeline; + +import fr.radi3nt.animations.channels.ChannelIdentifier; +import fr.radi3nt.animations.timeline.blending.BlendingRepository; +import fr.radi3nt.animations.timeline.layers.AnimationLayer; + +import java.util.*; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class LayeredAnimationTimeline implements AnimationTimeline { + + private final Queue animationLayers = new ConcurrentLinkedQueue<>(); + private final BlendingRepository blending; + + public LayeredAnimationTimeline(BlendingRepository blending) { + this.blending = blending; + } + + public Queue getAnimationLayers() { + return animationLayers; + } + + @Override + public void update(float delta) { + for (AnimationLayer animationLayer : animationLayers) { + animationLayer.getTimeline().update(delta); + } + } + + @Override + public T get(ChannelIdentifier channelIdentifier, T defaultResult) { + T result = defaultResult; + for (AnimationLayer animationLayer : animationLayers) { + result = animationLayer.getLayerMode().blend(blending, animationLayer.getTimeline(), channelIdentifier, result); + } + return result; + } + + @Override + public Collection getAvailableObjects() { + Set result = new HashSet<>(); + for (AnimationLayer animationLayer : animationLayers) { + result.addAll(animationLayer.getTimeline().getAvailableObjects()); + } + + return result; + } + +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/timeline/SetAnimationTimeline.java b/Animations/src/main/java/fr/radi3nt/animations/timeline/SetAnimationTimeline.java new file mode 100644 index 0000000..ee2f25f --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/timeline/SetAnimationTimeline.java @@ -0,0 +1,54 @@ +package fr.radi3nt.animations.timeline; + +import fr.radi3nt.animations.AnimationClip; +import fr.radi3nt.animations.AnimatedPropertyProvider; +import fr.radi3nt.animations.ProcessedAnimation; +import fr.radi3nt.animations.channels.ChannelIdentifier; + +import java.util.*; +import java.util.function.Supplier; + +public class SetAnimationTimeline implements AnimationTimeline { + + private final List processedAnimations = new ArrayList<>(); + + public void addAnimation(AnimationClip animationClip, boolean looping) { + processedAnimations.add(new ProcessedAnimation(animationClip, looping)); + } + + public void addAnimation(AnimatedPropertyProvider provider) { + processedAnimations.add(provider); + } + + public void removeAnimation(AnimatedPropertyProvider provider) { + processedAnimations.remove(provider); + } + + @Override + public void update(float delta) { + for (AnimatedPropertyProvider processedAnimation : processedAnimations) { + processedAnimation.incrementTime(delta); + } + } + + @Override + public T get(ChannelIdentifier identifier, T defaultResult) { + Supplier lastChannel = null; + + for (AnimatedPropertyProvider processedAnimation : processedAnimations) { + lastChannel = processedAnimation.get(identifier); + } + + return lastChannel==null ? defaultResult : (T) lastChannel.get(); + } + + @Override + public Collection getAvailableObjects() { + Set objects = new HashSet<>(); + for (AnimatedPropertyProvider processedAnimation : processedAnimations) { + processedAnimation.addConcernedObjects(objects); + } + + return objects; + } +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/timeline/blending/BlendingRepository.java b/Animations/src/main/java/fr/radi3nt/animations/timeline/blending/BlendingRepository.java new file mode 100644 index 0000000..39a050a --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/timeline/blending/BlendingRepository.java @@ -0,0 +1,33 @@ +package fr.radi3nt.animations.timeline.blending; + +import java.util.HashMap; +import java.util.Map; + +public class BlendingRepository { + + public static final BlendingRepository DEFAULT = new BlendingRepository(new QuaternionBlending()); + private final Map, ObjectBlending> blendingMap = new HashMap<>(); + + + public BlendingRepository(ObjectBlending... blendings) { + for (ObjectBlending blending : blendings) { + for (Class aClass : blending.supported()) { + blendingMap.put(aClass, blending); + } + } + } + + public void add(ObjectBlending blending) { + blendingMap.put(blending.getClass(), blending); + } + + public T blend(T first, T second, float weight) { + ObjectBlending objectBlending = (ObjectBlending) blendingMap.get(second.getClass()); + return objectBlending.blend(first, second, weight); + } + + public T additive(T first, T second, float weight) { + ObjectBlending objectBlending = (ObjectBlending) blendingMap.get(second.getClass()); + return objectBlending.add(first, second, weight); + } +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/timeline/blending/ObjectBlending.java b/Animations/src/main/java/fr/radi3nt/animations/timeline/blending/ObjectBlending.java new file mode 100644 index 0000000..f44d315 --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/timeline/blending/ObjectBlending.java @@ -0,0 +1,9 @@ +package fr.radi3nt.animations.timeline.blending; + +public interface ObjectBlending { + + T blend(T first, T second, float secondWeight); + T add(T first, T second, float weight); + + Class[] supported(); +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/timeline/blending/QuaternionBlending.java b/Animations/src/main/java/fr/radi3nt/animations/timeline/blending/QuaternionBlending.java new file mode 100644 index 0000000..f7e82b4 --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/timeline/blending/QuaternionBlending.java @@ -0,0 +1,37 @@ +package fr.radi3nt.animations.timeline.blending; + +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; + +public class QuaternionBlending implements ObjectBlending { + @Override + public Quaternion blend(Quaternion first, Quaternion second, float secondWeight) { + if (first==null) + first = ComponentsQuaternion.zero(); + Quaternion result = ComponentsQuaternion.zero(); + result.copy(first); + result.interpolate(second, secondWeight); + + return result; + } + + @Override + public Quaternion add(Quaternion first, Quaternion second, float weight) { + if (first==null) + first = ComponentsQuaternion.zero(); + + Quaternion interpolation = ComponentsQuaternion.zero(); + interpolation.interpolate(second, weight); + + Quaternion result = ComponentsQuaternion.zero(); + result.copy(first); + result.multiply(interpolation); + + return result; + } + + @Override + public Class[] supported() { + return new Class[] {Quaternion.class, ComponentsQuaternion.class}; + } +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/timeline/layers/AdditiveLayerBlending.java b/Animations/src/main/java/fr/radi3nt/animations/timeline/layers/AdditiveLayerBlending.java new file mode 100644 index 0000000..342cf35 --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/timeline/layers/AdditiveLayerBlending.java @@ -0,0 +1,24 @@ +package fr.radi3nt.animations.timeline.layers; + +import fr.radi3nt.animations.channels.ChannelIdentifier; +import fr.radi3nt.animations.timeline.AnimationTimeline; +import fr.radi3nt.animations.timeline.blending.BlendingRepository; + +public class AdditiveLayerBlending implements LayerBlending { + + private final float weight; + + public AdditiveLayerBlending(float weight) { + this.weight = weight; + } + + @Override + public T blend(BlendingRepository repository, AnimationTimeline timeline, ChannelIdentifier identifier, T defaultResult) { + if (weight==0) + return defaultResult; + T second = timeline.get(identifier, null); + if (second==null) + return defaultResult; + return repository.additive(defaultResult, second, weight); + } +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/timeline/layers/AnimationLayer.java b/Animations/src/main/java/fr/radi3nt/animations/timeline/layers/AnimationLayer.java new file mode 100644 index 0000000..52cdef5 --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/timeline/layers/AnimationLayer.java @@ -0,0 +1,27 @@ +package fr.radi3nt.animations.timeline.layers; + +import fr.radi3nt.animations.timeline.AnimationTimeline; +import fr.radi3nt.animations.timeline.SetAnimationTimeline; + +public class AnimationLayer { + + private final AnimationTimeline timeline; + private final LayerBlending layerMode; + + public AnimationLayer(AnimationTimeline timeline, LayerBlending layerMode) { + this.timeline = timeline; + this.layerMode = layerMode; + } + + public AnimationLayer(LayerBlending layerMode) { + this(new SetAnimationTimeline(), layerMode); + } + + public LayerBlending getLayerMode() { + return layerMode; + } + + public AnimationTimeline getTimeline() { + return timeline; + } +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/timeline/layers/LayerBlending.java b/Animations/src/main/java/fr/radi3nt/animations/timeline/layers/LayerBlending.java new file mode 100644 index 0000000..01994d9 --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/timeline/layers/LayerBlending.java @@ -0,0 +1,11 @@ +package fr.radi3nt.animations.timeline.layers; + +import fr.radi3nt.animations.channels.ChannelIdentifier; +import fr.radi3nt.animations.timeline.AnimationTimeline; +import fr.radi3nt.animations.timeline.blending.BlendingRepository; + +public interface LayerBlending { + + T blend(BlendingRepository repository, AnimationTimeline timeline, ChannelIdentifier identifier, T defaultResult); + +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/timeline/layers/ReplacingLayerBlending.java b/Animations/src/main/java/fr/radi3nt/animations/timeline/layers/ReplacingLayerBlending.java new file mode 100644 index 0000000..de98807 --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/timeline/layers/ReplacingLayerBlending.java @@ -0,0 +1,12 @@ +package fr.radi3nt.animations.timeline.layers; + +import fr.radi3nt.animations.channels.ChannelIdentifier; +import fr.radi3nt.animations.timeline.AnimationTimeline; +import fr.radi3nt.animations.timeline.blending.BlendingRepository; + +public class ReplacingLayerBlending implements LayerBlending { + @Override + public T blend(BlendingRepository repository, AnimationTimeline timeline, ChannelIdentifier identifier, T previousResult) { + return timeline.get(identifier, previousResult); + } +} diff --git a/Animations/src/main/java/fr/radi3nt/animations/timeline/layers/WeightedLayerBlending.java b/Animations/src/main/java/fr/radi3nt/animations/timeline/layers/WeightedLayerBlending.java new file mode 100644 index 0000000..5083dc9 --- /dev/null +++ b/Animations/src/main/java/fr/radi3nt/animations/timeline/layers/WeightedLayerBlending.java @@ -0,0 +1,24 @@ +package fr.radi3nt.animations.timeline.layers; + +import fr.radi3nt.animations.channels.ChannelIdentifier; +import fr.radi3nt.animations.timeline.AnimationTimeline; +import fr.radi3nt.animations.timeline.blending.BlendingRepository; + +public class WeightedLayerBlending implements LayerBlending { + + private final float weight; + + public WeightedLayerBlending(float weight) { + this.weight = weight; + } + + @Override + public T blend(BlendingRepository repository, AnimationTimeline timeline, ChannelIdentifier identifier, T defaultResult) { + if (weight==0) + return defaultResult; + T second = timeline.get(identifier, null); + if (second==null) + return defaultResult; + return repository.blend(defaultResult, second, weight); + } +} diff --git a/AnimationsImport/pom.xml b/AnimationsImport/pom.xml new file mode 100644 index 0000000..d3a3c92 --- /dev/null +++ b/AnimationsImport/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + fr.radi3nt + JavaUtil + 1.0 + + + AnimationsImport + + + 8 + 8 + UTF-8 + + + + fr.radi3nt + SplineHelper + 1.0 + compile + + + fr.radi3nt + FileHelper + 1.0 + compile + + + fr.radi3nt + Animations + 1.0 + compile + + + + \ No newline at end of file diff --git a/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/AnimFormatAnimationImporter.java b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/AnimFormatAnimationImporter.java new file mode 100644 index 0000000..14841e6 --- /dev/null +++ b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/AnimFormatAnimationImporter.java @@ -0,0 +1,100 @@ +package fr.radi3nt.animations.importing.anim; + +import fr.radi3nt.animations.AnimationClip; +import fr.radi3nt.animations.channels.ChannelIdentifier; +import fr.radi3nt.spline.interpolation.InterpolationData; +import fr.radi3nt.spline.interpolation.PlotInterpolationData; +import fr.radi3nt.animations.channels.types.KeyframeSplitEulerXYZRotationChannel; +import fr.radi3nt.animations.channels.types.KeyframeSplitInterpolationDataChannel; +import fr.radi3nt.animations.channels.types.KeyframeSplitVectorChannel; +import fr.radi3nt.animations.importing.anim.animation.AnimationBody; +import fr.radi3nt.animations.importing.anim.animation.AnimationBodyBuilder; +import fr.radi3nt.animations.importing.anim.animation.data.AnimData; +import fr.radi3nt.animations.importing.anim.animation.data.ChannelInfo; +import fr.radi3nt.animations.importing.anim.content.AnimFileContent; +import fr.radi3nt.animations.importing.anim.header.AnimHeader; +import fr.radi3nt.animations.importing.anim.header.AnimHeaderBuilder; +import fr.radi3nt.spline.splines.dimensions.Spline2D; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +public class AnimFormatAnimationImporter { + + private final AnimHeaderBuilder headerBuilder; + private final AnimationBodyBuilder bodyBuilder; + + public AnimFormatAnimationImporter(AnimHeaderBuilder headerBuilder, AnimationBodyBuilder bodyBuilder) { + this.headerBuilder = headerBuilder; + this.bodyBuilder = bodyBuilder; + } + + public AnimImportedResult importFromContent(AnimFileContent animFileContent) { + AnimHeader animHeader = headerBuilder.build(animFileContent); + AnimationBody animationBody = bodyBuilder.build(animFileContent, animHeader); + + return new AnimImportedResult(animHeader, animationBody); + } + + + public static class AnimImportedResult { + + private static final float POINTS_PER_SECONDS = 60; + private final AnimHeader header; + private final AnimationBody animationBody; + + public AnimImportedResult(AnimHeader header, AnimationBody animationBody) { + this.header = header; + this.animationBody = animationBody; + } + + public AnimationClip toClip() { + float duration = header.getEndFrameTime()/header.getTimeUnit().getFrameEquivalent(); + + Map> channels = new HashMap<>(); + + for (Map.Entry channelInfoAnimDataEntry : animationBody.getAnimData().entrySet()) { + AnimData value = channelInfoAnimDataEntry.getValue(); + ChannelInfo key = channelInfoAnimDataEntry.getKey(); + Supplier> channelSupplier = createChannelSupplier(key); + KeyframeSplitInterpolationDataChannel keyframeChannel = channels.computeIfAbsent(new ChannelIdentifier(key.getObjectName(), key.getMainChannelType()), (c) -> channelSupplier.get()); + Spline2D spline = value.getKeyframesData().getDataSpline(); + keyframeChannel.getInterpolationData()[key.getAttributeInt()] = PlotInterpolationData.create(spline, duration, spline.getSegmentCount(), 0, (int) Math.ceil(duration*POINTS_PER_SECONDS), 50); + } + + return new AnimationClip(duration, channels); + } + + private static Supplier> createChannelSupplier(ChannelInfo key) { + Supplier> channelSupplier = null; + switch (key.getMainChannelType()) { + case "rotate": + channelSupplier = () -> new KeyframeSplitEulerXYZRotationChannel(new InterpolationData[3], 1f); + break; + case "translate": + case "scale": + channelSupplier = () -> new KeyframeSplitVectorChannel(new InterpolationData[3], 1f); + break; + } + return channelSupplier; + } + + + public AnimHeader getHeader() { + return header; + } + + public AnimationBody getAnimationBody() { + return animationBody; + } + + @Override + public String toString() { + return "AnimImportedResult{" + + "header=" + header + + ", animationBody=" + animationBody + + '}'; + } + } +} diff --git a/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/AnimationBody.java b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/AnimationBody.java new file mode 100644 index 0000000..edb7c85 --- /dev/null +++ b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/AnimationBody.java @@ -0,0 +1,26 @@ +package fr.radi3nt.animations.importing.anim.animation; + +import fr.radi3nt.animations.importing.anim.animation.data.AnimData; +import fr.radi3nt.animations.importing.anim.animation.data.ChannelInfo; + +import java.util.Map; + +public class AnimationBody { + + private final Map animData; + + public AnimationBody(Map animData) { + this.animData = animData; + } + + public Map getAnimData() { + return animData; + } + + @Override + public String toString() { + return "AnimationBody{" + + "animData=" + animData + + '}'; + } +} diff --git a/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/AnimationBodyBuilder.java b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/AnimationBodyBuilder.java new file mode 100644 index 0000000..6693cc0 --- /dev/null +++ b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/AnimationBodyBuilder.java @@ -0,0 +1,10 @@ +package fr.radi3nt.animations.importing.anim.animation; + +import fr.radi3nt.animations.importing.anim.content.AnimFileContent; +import fr.radi3nt.animations.importing.anim.header.AnimHeader; + +public interface AnimationBodyBuilder { + + AnimationBody build(AnimFileContent content, AnimHeader header); + +} diff --git a/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/FailureSafeAnimationBodyBuilder.java b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/FailureSafeAnimationBodyBuilder.java new file mode 100644 index 0000000..bdd3c66 --- /dev/null +++ b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/FailureSafeAnimationBodyBuilder.java @@ -0,0 +1,116 @@ +package fr.radi3nt.animations.importing.anim.animation; + +import fr.radi3nt.animations.importing.anim.animation.data.AnimData; +import fr.radi3nt.animations.importing.anim.animation.data.ChannelInfo; +import fr.radi3nt.spline.imports.key.CurveHandleData; +import fr.radi3nt.spline.imports.key.InterpolationType; +import fr.radi3nt.spline.imports.key.KeyData; +import fr.radi3nt.spline.imports.key.KeyframesData; +import fr.radi3nt.spline.imports.spline.KeyedSplineBuilder; +import fr.radi3nt.animations.importing.anim.content.AnimFileContent; +import fr.radi3nt.animations.importing.anim.header.AnimHeader; +import fr.radi3nt.animations.importing.anim.units.UnitType; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class FailureSafeAnimationBodyBuilder implements AnimationBodyBuilder { + + private static final String ANIM_STOP_SEGMENT = "keys"; + private static final String INPUT_YPE_PROPERTY_ID = "input"; + private static final String OUTPUT_TYPE_PROPERTY_ID = "output"; + private static final String WEIGHTED_PROPERTY_ID = "weighted"; + private final KeyedSplineBuilder splineBuilder; + + public FailureSafeAnimationBodyBuilder(KeyedSplineBuilder splineBuilder) { + this.splineBuilder = splineBuilder; + } + + @Override + public AnimationBody build(AnimFileContent content, AnimHeader header) { + Map animDataMap = new HashMap<>(); + while (content.hasLine()) { + String line = content.readLine(); + String[] splitLine = line.split(" "); + + if (splitLine.length<=5) + continue; + + + String fullChannelType = splitLine[1]; + String mainChannelType = fullChannelType.split("\\.")[0]; + String leafChannelType = splitLine[2]; + + String objectName = splitLine[3]; + + int attributeInt = Integer.parseInt(splitLine[6]); + + AnimData animData = buildAnimData(content, header); + animDataMap.put(new ChannelInfo(fullChannelType, mainChannelType, leafChannelType, attributeInt, objectName), animData); + } + + return new AnimationBody(animDataMap); + } + + private AnimData buildAnimData(AnimFileContent content, AnimHeader header) { + Map propertyAndValue = new HashMap<>(); + fillAnimProperties(content, propertyAndValue); + + UnitType inputType = UnitType.valueOf(propertyAndValue.getOrDefault(INPUT_YPE_PROPERTY_ID, "time").toUpperCase()); + UnitType outputType = UnitType.valueOf(propertyAndValue.getOrDefault(OUTPUT_TYPE_PROPERTY_ID, "time").toUpperCase()); + boolean weighted = stringToBool(propertyAndValue.getOrDefault(WEIGHTED_PROPERTY_ID, "0")); + + KeyframesData keyframesData = buildKeysData(content, header); + return new AnimData(inputType, outputType, null, null, weighted, keyframesData); + } + + private static void fillAnimProperties(AnimFileContent content, Map propertyAndValue) { + while (content.hasLine()) { + String peak = content.readLine(); + if (peak.startsWith(ANIM_STOP_SEGMENT)) + break; + String[] propertyAndValueString = peak.split(" "); + propertyAndValue.put(propertyAndValueString[0], propertyAndValueString[1]); + } + } + + private KeyframesData buildKeysData(AnimFileContent content, AnimHeader header) { + List keyData = getKeyData(content); + return new KeyframesData(keyData, splineBuilder.build(keyData, 1f/header.getTimeUnit().getFrameEquivalent())); + } + + private List getKeyData(AnimFileContent content) { + List keyData = new ArrayList<>(); + while (content.hasLine()) { + String line = content.readLine(); + if (line.startsWith("}")) + break; + String[] splitLine = line.split(" "); + float frameIndex = Float.parseFloat(splitLine[0]); + float correspondingValue = Float.parseFloat(splitLine[1]); + InterpolationType inInterpolation = InterpolationType.valueOf(splitLine[2].toUpperCase()); + InterpolationType outInterpolation = InterpolationType.valueOf(splitLine[3].toUpperCase()); + boolean tanLocked = stringToBool(splitLine[4]); + boolean weightLocked = stringToBool(splitLine[5]); + + int additionalDataAmount = splitLine.length-7; + int curveHandleDataAmount = Math.floorDiv(additionalDataAmount, 2); + CurveHandleData[] curveHandleData = new CurveHandleData[curveHandleDataAmount]; + for (int i = 0; i < curveHandleDataAmount; i++) { + String tanAngle = splitLine[7+i*2]; + String tanWeight = splitLine[8+i*2]; + curveHandleData[i] = new CurveHandleData(Float.parseFloat(tanAngle), Float.parseFloat(tanWeight)); + } + + keyData.add(new KeyData(frameIndex, correspondingValue, inInterpolation, outInterpolation, tanLocked, weightLocked, curveHandleData)); + } + content.nextLine(); + return keyData; + } + + private boolean stringToBool(String str) { + return str.equals("1"); + } +} diff --git a/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/data/AnimData.java b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/data/AnimData.java new file mode 100644 index 0000000..df2c86e --- /dev/null +++ b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/data/AnimData.java @@ -0,0 +1,64 @@ +package fr.radi3nt.animations.importing.anim.animation.data; + +import fr.radi3nt.spline.imports.key.KeyframesData; +import fr.radi3nt.animations.importing.anim.header.AnimHeader; +import fr.radi3nt.animations.importing.anim.units.AnimUnit; +import fr.radi3nt.animations.importing.anim.units.UnitType; + +public class AnimData { + + private final UnitType inputType; + private final UnitType outputType; + + private final AnimUnit inputUnit; + private final AnimUnit outputUnit; + + private final boolean weighted; + + private final KeyframesData keyframesData; + + public AnimData(UnitType inputType, UnitType outputType, AnimUnit inputUnit, AnimUnit outputUnit, boolean weighted, KeyframesData keyframesData) { + this.inputType = inputType; + this.outputType = outputType; + this.inputUnit = inputUnit; + this.outputUnit = outputUnit; + this.weighted = weighted; + this.keyframesData = keyframesData; + } + + public UnitType getInputType() { + return inputType; + } + + public UnitType getOutputType() { + return outputType; + } + + public AnimUnit getInputUnit(AnimHeader header) { + return inputUnit==null ? header.getUnitForType(getInputType()) : inputUnit; + } + + public AnimUnit getOutputUnit(AnimHeader header) { + return outputUnit==null ? header.getUnitForType(getOutputType()) : outputUnit; + } + + public boolean isWeighted() { + return weighted; + } + + public KeyframesData getKeyframesData() { + return keyframesData; + } + + @Override + public String toString() { + return "AnimData{" + + "inputType=" + inputType + + ", outputType=" + outputType + + ", inputUnit=" + inputUnit + + ", outputUnit=" + outputUnit + + ", weighted=" + weighted + + ", keyframesData=" + keyframesData + + '}'; + } +} diff --git a/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/data/ChannelInfo.java b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/data/ChannelInfo.java new file mode 100644 index 0000000..5b359bd --- /dev/null +++ b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/data/ChannelInfo.java @@ -0,0 +1,39 @@ +package fr.radi3nt.animations.importing.anim.animation.data; + +public class ChannelInfo { + + private final String fullChannelType; + private final String mainChannelType; + private final String leafChannelType; + private final int attributeInt; + + private final String objectName; + + public ChannelInfo(String fullChannelType, String mainChannelType, String leafChannelType, int attributeInt, String objectName) { + this.fullChannelType = fullChannelType; + this.mainChannelType = mainChannelType; + this.leafChannelType = leafChannelType; + this.attributeInt = attributeInt; + this.objectName = objectName; + } + + public String getFullChannelType() { + return fullChannelType; + } + + public String getMainChannelType() { + return mainChannelType; + } + + public String getLeafChannelType() { + return leafChannelType; + } + + public int getAttributeInt() { + return attributeInt; + } + + public String getObjectName() { + return objectName; + } +} diff --git a/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/data/ChannelType.java b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/data/ChannelType.java new file mode 100644 index 0000000..1dc126c --- /dev/null +++ b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/animation/data/ChannelType.java @@ -0,0 +1,9 @@ +package fr.radi3nt.animations.importing.anim.animation.data; + +public enum ChannelType { + + SCALE, + ROTATE, + TRANSLATE + +} diff --git a/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/content/AnimFileContent.java b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/content/AnimFileContent.java new file mode 100644 index 0000000..ee0f764 --- /dev/null +++ b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/content/AnimFileContent.java @@ -0,0 +1,10 @@ +package fr.radi3nt.animations.importing.anim.content; + +public interface AnimFileContent { + + String peakLine(); + String readLine(); + void nextLine(); + boolean hasLine(); + +} diff --git a/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/content/ReadableAnimFileContent.java b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/content/ReadableAnimFileContent.java new file mode 100644 index 0000000..20982b5 --- /dev/null +++ b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/content/ReadableAnimFileContent.java @@ -0,0 +1,60 @@ +package fr.radi3nt.animations.importing.anim.content; + +import fr.radi3nt.file.files.ReadableFile; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class ReadableAnimFileContent implements AnimFileContent { + + private final ReadableFile readableFile; + private List lines = new ArrayList<>(); + private int lineIndex = 0; + + public ReadableAnimFileContent(ReadableFile readableFile) { + this.readableFile = readableFile; + } + + public ReadableAnimFileContent readAll() { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(readableFile.getInputStream()))) { + lines = reader.lines().collect(Collectors.toList()); + lineIndex = 0; + } catch (IOException e) { + e.printStackTrace(); + } + return this; + } + + @Override + public String peakLine() { + return formatLine(lines.get(lineIndex)); + } + + @Override + public String readLine() { + return formatLine(lines.get(lineIndex++)); + } + + @Override + public void nextLine() { + lineIndex++; + } + + @Override + public boolean hasLine() { + return lineIndex propertyAndValue = new HashMap<>(); + fillHeaderProperties(content, propertyAndValue); + + String version = propertyAndValue.getOrDefault(VERSION_PROPERTY_ID, "1.0"); + TimeUnit timeUnit = TimeUnit.valueOf(propertyAndValue.getOrDefault(TIME_UNIT_PROPERTY_ID, "ntsc").toUpperCase()); + LinearUnit linearUnit = LinearUnit.valueOf(propertyAndValue.getOrDefault(LINEAR_UNIT_PROPERTY_ID, "m").toUpperCase()); + AngularUnit angularUnit = AngularUnit.valueOf(propertyAndValue.getOrDefault(ANGULAR_UNIT_PROPERTY_ID, "rad").toUpperCase()); + int startTime = Integer.parseInt(propertyAndValue.getOrDefault(START_TIME_PROPERTY_ID, "0")); + int endTime = Integer.parseInt(propertyAndValue.getOrDefault(END_TIME_PROPERTY_ID, "0")); + + return new AnimHeader(version, timeUnit, linearUnit, angularUnit, startTime, endTime); + } + + private static void fillHeaderProperties(AnimFileContent content, Map propertyAndValue) { + while (content.hasLine()) { + String peak = content.peakLine(); + if (peak.startsWith(HEADER_STOP_SEGMENT)) + break; + String[] propertyAndValueString = peak.split(" "); + propertyAndValue.put(propertyAndValueString[0], propertyAndValueString[1]); + content.nextLine(); + } + } +} diff --git a/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/units/AngularUnit.java b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/units/AngularUnit.java new file mode 100644 index 0000000..b62c044 --- /dev/null +++ b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/units/AngularUnit.java @@ -0,0 +1,10 @@ +package fr.radi3nt.animations.importing.anim.units; + +public enum AngularUnit implements AnimUnit { + + RAD, + DEG, + MIN, + SEC + +} diff --git a/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/units/AnimUnit.java b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/units/AnimUnit.java new file mode 100644 index 0000000..64d6840 --- /dev/null +++ b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/units/AnimUnit.java @@ -0,0 +1,4 @@ +package fr.radi3nt.animations.importing.anim.units; + +public interface AnimUnit { +} diff --git a/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/units/LinearUnit.java b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/units/LinearUnit.java new file mode 100644 index 0000000..127a64a --- /dev/null +++ b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/units/LinearUnit.java @@ -0,0 +1,14 @@ +package fr.radi3nt.animations.importing.anim.units; + +public enum LinearUnit implements AnimUnit { + + MM, + CM, + M, + KM, + IN, + FT, + YD, + MI + +} diff --git a/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/units/TimeUnit.java b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/units/TimeUnit.java new file mode 100644 index 0000000..4dd7c55 --- /dev/null +++ b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/units/TimeUnit.java @@ -0,0 +1,27 @@ +package fr.radi3nt.animations.importing.anim.units; + +public enum TimeUnit implements AnimUnit { + + GAME(15), + FILM(24), + PAL(25), + NTSC(30), + SHOW(48), + PALF(50), + NTSCF(60), + HOUR(1/60f/60f), + MIN(1/60f), + SEC(1), + MILLISEC(1_000); + + + private final float frameEquivalent; + + TimeUnit(float frameEquivalent) { + this.frameEquivalent = frameEquivalent; + } + + public float getFrameEquivalent() { + return frameEquivalent; + } +} diff --git a/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/units/UnitType.java b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/units/UnitType.java new file mode 100644 index 0000000..6e4a26e --- /dev/null +++ b/AnimationsImport/src/main/java/fr/radi3nt/animations/importing/anim/units/UnitType.java @@ -0,0 +1,10 @@ +package fr.radi3nt.animations.importing.anim.units; + +public enum UnitType { + + TIME, + LINEAR, + ANGULAR, + UNITLESS, + +} diff --git a/ArgParser/src/main/java/fr/radi3nt/argp/ArgParser.java b/ArgParser/src/main/java/fr/radi3nt/argp/ArgParser.java index 03ea377..98487f1 100644 --- a/ArgParser/src/main/java/fr/radi3nt/argp/ArgParser.java +++ b/ArgParser/src/main/java/fr/radi3nt/argp/ArgParser.java @@ -2,8 +2,8 @@ import fr.radi3nt.argp.exceptions.ArgumentException; import fr.radi3nt.argp.parsing.ArgumentParser; -import fr.radi3nt.argp.parsing.result.ArgumentParseResult; import fr.radi3nt.argp.parsing.repo.ArgumentRepository; +import fr.radi3nt.argp.parsing.result.ArgumentParseResult; import fr.radi3nt.argp.parsing.result.ParserResult; import fr.radi3nt.argp.parsing.result.ParserResultBuilder; import fr.radi3nt.argp.parsing.result.ParserResultBuilderFactory; diff --git a/ArgParser/src/main/java/fr/radi3nt/argp/parsing/ArgumentParser.java b/ArgParser/src/main/java/fr/radi3nt/argp/parsing/ArgumentParser.java index 8776dbe..04aa183 100644 --- a/ArgParser/src/main/java/fr/radi3nt/argp/parsing/ArgumentParser.java +++ b/ArgParser/src/main/java/fr/radi3nt/argp/parsing/ArgumentParser.java @@ -1,7 +1,7 @@ package fr.radi3nt.argp.parsing; -import fr.radi3nt.argp.parsing.result.ArgumentParseResult; import fr.radi3nt.argp.exceptions.ParsingException; +import fr.radi3nt.argp.parsing.result.ArgumentParseResult; public interface ArgumentParser { diff --git a/ArgParser/src/main/java/fr/radi3nt/argp/scheme/ArgScheme.java b/ArgParser/src/main/java/fr/radi3nt/argp/scheme/ArgScheme.java index f8364f5..edbaa5a 100644 --- a/ArgParser/src/main/java/fr/radi3nt/argp/scheme/ArgScheme.java +++ b/ArgParser/src/main/java/fr/radi3nt/argp/scheme/ArgScheme.java @@ -1,7 +1,7 @@ package fr.radi3nt.argp.scheme; -import fr.radi3nt.argp.parsing.result.ParserResult; import fr.radi3nt.argp.exceptions.ArgumentException; +import fr.radi3nt.argp.parsing.result.ParserResult; /** * Argument scheme precise the validity of a chain of arguments diff --git a/Armature/pom.xml b/Armature/pom.xml new file mode 100644 index 0000000..df1dd86 --- /dev/null +++ b/Armature/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + + fr.radi3nt + JavaUtil + 1.0 + + + Armature + + + 8 + 8 + UTF-8 + + + + fr.radi3nt + MathsHelper + 1.0 + compile + + + fr.radi3nt + Animations + 1.0 + compile + + + + \ No newline at end of file diff --git a/Armature/src/main/java/fr/radi3nt/armatures/armature/Armature.java b/Armature/src/main/java/fr/radi3nt/armatures/armature/Armature.java new file mode 100644 index 0000000..70366df --- /dev/null +++ b/Armature/src/main/java/fr/radi3nt/armatures/armature/Armature.java @@ -0,0 +1,15 @@ +package fr.radi3nt.armatures.armature; + +import fr.radi3nt.armatures.armature.bone.Bone; + +import java.nio.ByteBuffer; +import java.util.Collection; + +public interface Armature { + + void encode(ByteBuffer byteBuffer); + + Collection getRoots(); + int getBoneCount(); + +} diff --git a/Armature/src/main/java/fr/radi3nt/armatures/armature/ListArmature.java b/Armature/src/main/java/fr/radi3nt/armatures/armature/ListArmature.java new file mode 100644 index 0000000..0fe9ad0 --- /dev/null +++ b/Armature/src/main/java/fr/radi3nt/armatures/armature/ListArmature.java @@ -0,0 +1,60 @@ +package fr.radi3nt.armatures.armature; + +import fr.radi3nt.armatures.armature.bone.Bone; +import fr.radi3nt.armatures.armature.driver.DriverResult; +import fr.radi3nt.armatures.armature.driver.SetDriverResult; +import fr.radi3nt.maths.components.advanced.matrix.ArrayMatrix4x4; +import fr.radi3nt.maths.components.advanced.matrix.Matrix4x4; +import fr.radi3nt.maths.components.vectors.Vector3f; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collection; + +public class ListArmature implements Armature { + + private final Collection rootBones = new ArrayList<>(); + private final Matrix4x4 armatureTransform; + private final Bone[] bonesIds; + + public ListArmature(Matrix4x4 armatureTransform, Bone[] bonesIds, Collection roots) { + this.armatureTransform = armatureTransform; + this.bonesIds = bonesIds; + this.rootBones.addAll(roots); + } + + @Override + public void encode(ByteBuffer byteBuffer) { + for (Bone bonesId : bonesIds) { + writeMatrix(byteBuffer, bonesId.getJointTransform()); + } + } + + private void writeMatrix(ByteBuffer byteBuffer, Matrix4x4 worldSpaceTransform) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + byteBuffer.putFloat(worldSpaceTransform.get(i, j)); + } + } + } + + public void update() { + for (Bone rootBone : rootBones) { + rootBone.update(new SetDriverResult(armatureTransform)); + } + } + + public Matrix4x4 getArmatureTransform() { + return armatureTransform; + } + + @Override + public Collection getRoots() { + return rootBones; + } + + @Override + public int getBoneCount() { + return bonesIds.length; + } +} diff --git a/Armature/src/main/java/fr/radi3nt/armatures/armature/bone/Bone.java b/Armature/src/main/java/fr/radi3nt/armatures/armature/bone/Bone.java new file mode 100644 index 0000000..33e33f7 --- /dev/null +++ b/Armature/src/main/java/fr/radi3nt/armatures/armature/bone/Bone.java @@ -0,0 +1,21 @@ +package fr.radi3nt.armatures.armature.bone; + +import fr.radi3nt.armatures.armature.driver.BoneDriver; +import fr.radi3nt.armatures.armature.driver.BoneRestData; +import fr.radi3nt.armatures.armature.driver.DriverResult; +import fr.radi3nt.maths.components.advanced.matrix.Matrix4x4; + +public interface Bone { + + void update(DriverResult parentModelSpace); + + void setBoneDriver(BoneDriver boneDriver); + + BoneRestData getBoneRestData(); + + BoneDriver getBoneDriver(); + + Matrix4x4 getWorldTransform(); + Matrix4x4 getJointTransform(); + +} diff --git a/Armature/src/main/java/fr/radi3nt/armatures/armature/bone/ParentBone.java b/Armature/src/main/java/fr/radi3nt/armatures/armature/bone/ParentBone.java new file mode 100644 index 0000000..da19680 --- /dev/null +++ b/Armature/src/main/java/fr/radi3nt/armatures/armature/bone/ParentBone.java @@ -0,0 +1,76 @@ +package fr.radi3nt.armatures.armature.bone; + +import fr.radi3nt.armatures.armature.driver.BoneDriver; +import fr.radi3nt.armatures.armature.driver.BoneRestData; +import fr.radi3nt.armatures.armature.driver.DriverResult; +import fr.radi3nt.armatures.armature.driver.ParentedBoneDriver; +import fr.radi3nt.maths.components.advanced.matrix.ArrayMatrix4x4; +import fr.radi3nt.maths.components.advanced.matrix.Matrix4x4; + +import java.util.ArrayList; +import java.util.Collection; + +public class ParentBone implements Bone { + + private final Matrix4x4 jointSpaceTransform = ArrayMatrix4x4.newIdentity(); + private final Matrix4x4 modelSpaceTransform = ArrayMatrix4x4.newIdentity(); + + private final Matrix4x4 worldInverseBindTransform; + private final BoneRestData boneRestData; + + private BoneDriver boneDriver; + + public final Collection children = new ArrayList<>(); + + public ParentBone(Matrix4x4 worldInverseBindTransform, BoneRestData boneRestData, Collection children) { + this.worldInverseBindTransform = worldInverseBindTransform; + this.boneRestData = boneRestData; + + this.children.addAll(children); + + boneDriver = new ParentedBoneDriver(boneRestData); + } + + @Override + public void update(DriverResult parentModelSpace) { + DriverResult result = boneDriver.getModelSpaceForParentTransform(parentModelSpace); + + for (Bone child : children) { + child.update(result); + } + + modelSpaceTransform.copy(result.getCurrent()); + jointSpaceTransform.copy(modelSpaceTransform); + jointSpaceTransform.multiply(worldInverseBindTransform); + } + + @Override + public void setBoneDriver(BoneDriver boneDriver) { + this.boneDriver = boneDriver; + } + + public Collection getChildren() { + return children; + } + + @Override + public BoneRestData getBoneRestData() { + return boneRestData; + } + + @Override + public BoneDriver getBoneDriver() { + return boneDriver; + } + + @Override + public Matrix4x4 getWorldTransform() { + return modelSpaceTransform; + } + + @Override + public Matrix4x4 getJointTransform() { + return jointSpaceTransform; + } + +} diff --git a/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/BoneDriver.java b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/BoneDriver.java new file mode 100644 index 0000000..97aa860 --- /dev/null +++ b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/BoneDriver.java @@ -0,0 +1,21 @@ +package fr.radi3nt.armatures.armature.driver; + +import fr.radi3nt.maths.components.advanced.matrix.Matrix4x4; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; + +public interface BoneDriver { + + void setPosition(Vector3f position); + void setScale(Vector3f scale); + void setRotation(Quaternion quaternion); + void addRotation(Quaternion quaternion); + void setPositionAndRotation(Vector3f translation, Quaternion rotation); + void setAll(Vector3f translate, Quaternion rotate, Vector3f scale); + + DriverResult getModelSpaceForParentTransform(DriverResult parent); + + Quaternion getLocalRotation(); + Vector3f getLocalTranslation(); + Vector3f getLocalSize(); +} diff --git a/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/BoneRestData.java b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/BoneRestData.java new file mode 100644 index 0000000..226fcec --- /dev/null +++ b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/BoneRestData.java @@ -0,0 +1,48 @@ +package fr.radi3nt.armatures.armature.driver; + +import fr.radi3nt.maths.components.advanced.matrix.ArrayMatrix4x4; +import fr.radi3nt.maths.components.advanced.matrix.Matrix; +import fr.radi3nt.maths.components.advanced.matrix.Matrix4x4; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; + +public class BoneRestData { + + private final Vector3f position; + private final Quaternion rotation; + private final Vector3f scale; + + public BoneRestData(Vector3f position, Quaternion rotation, Vector3f scale) { + this.position = position; + this.rotation = rotation; + this.scale = scale; + } + + public static BoneRestData fromMatrix(Matrix4x4 matrix) { + return new BoneRestData(matrix.getTranslation(), matrix.getRotation(), matrix.getScale()); + } + + public Matrix4x4 toMatrix() { + Matrix4x4 result = ArrayMatrix4x4.transform(position, rotation, scale); + return result; + } + + public Vector3f getPosition() { + return position; + } + + public Quaternion getRotation() { + return rotation; + } + + public Vector3f getScale() { + return scale; + } + + public BoneRestData child(BoneRestData child) { + Matrix4x4 matrix4x4 = toMatrix(); + matrix4x4.multiply(child.toMatrix()); + return BoneRestData.fromMatrix(matrix4x4); + } +} diff --git a/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/DriverResult.java b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/DriverResult.java new file mode 100644 index 0000000..54e7da8 --- /dev/null +++ b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/DriverResult.java @@ -0,0 +1,11 @@ +package fr.radi3nt.armatures.armature.driver; + +import fr.radi3nt.maths.components.advanced.matrix.Matrix4x4; +import fr.radi3nt.maths.components.vectors.Vector3f; + +public interface DriverResult { + + Matrix4x4 getCurrent(); + void cancelScale(Matrix4x4 current); + +} diff --git a/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/EncapsulatingDriverResult.java b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/EncapsulatingDriverResult.java new file mode 100644 index 0000000..cc400ca --- /dev/null +++ b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/EncapsulatingDriverResult.java @@ -0,0 +1,24 @@ +package fr.radi3nt.armatures.armature.driver; + +import fr.radi3nt.maths.components.advanced.matrix.Matrix4x4; + +public class EncapsulatingDriverResult implements DriverResult { + + private final Matrix4x4 current; + private final DriverResult cancelScale; + + public EncapsulatingDriverResult(Matrix4x4 current, DriverResult cancelScale) { + this.current = current; + this.cancelScale = cancelScale; + } + + @Override + public Matrix4x4 getCurrent() { + return current; + } + + @Override + public void cancelScale(Matrix4x4 current) { + cancelScale.cancelScale(current); + } +} diff --git a/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/ParentedBoneDriver.java b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/ParentedBoneDriver.java new file mode 100644 index 0000000..0df02d5 --- /dev/null +++ b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/ParentedBoneDriver.java @@ -0,0 +1,144 @@ +package fr.radi3nt.armatures.armature.driver; + +import fr.radi3nt.maths.components.advanced.matrix.ArrayMatrix4x4; +import fr.radi3nt.maths.components.advanced.matrix.Matrix4x4; +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.Vector4f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector4f; + +public class ParentedBoneDriver implements BoneDriver { + + //If you choose to export with "Transform" category checked, set that to true + private static final boolean TRANSFORM_MODE = false; + + private final Matrix4x4 resultTransform = ArrayMatrix4x4.newIdentity(); + private final Matrix4x4 resultParentTransform = ArrayMatrix4x4.newIdentity(); + private final Matrix4x4 localTransform = ArrayMatrix4x4.newIdentity(); + private final Matrix4x4 localRestTransform = ArrayMatrix4x4.newIdentity(); + + private final Vector3f translation = new SimpleVector3f(); + private final Vector3f scale = new SimpleVector3f(1f, 1f, 1f); + private final Quaternion rotation = ComponentsQuaternion.zero(); + + private final BoneRestData restData; + + public ParentedBoneDriver(BoneRestData restData) { + this.restData = restData; + if (TRANSFORM_MODE) { + this.translation.copy(restData.getPosition()); + this.rotation.copy(restData.getRotation()); + } else { + this.localRestTransform.copy(restData.toMatrix()); + } + } + + public BoneRestData getRestData() { + return restData; + } + + public void updateLocalRestTransform() { + this.localRestTransform.copy(restData.toMatrix()); + } + + @Override + public void setPosition(Vector3f position) { + this.translation.copy(position); + } + + @Override + public void setScale(Vector3f scale) { + this.scale.copy(scale); + } + + @Override + public void setRotation(Quaternion quaternion) { + this.rotation.copy(quaternion); + } + + @Override + public void addRotation(Quaternion quaternion) { + rotation.multiply(quaternion); + } + + @Override + public void setPositionAndRotation(Vector3f translation, Quaternion rotation) { + setPosition(translation); + setRotation(rotation); + } + + @Override + public void setAll(Vector3f translate, Quaternion rotate, Vector3f scale) { + setPositionAndRotation(translate, rotate); + setScale(scale); + } + + @Override + public DriverResult getModelSpaceForParentTransform(DriverResult parent) { + Matrix4x4 parentTransform = parent.getCurrent(); + Vector3f applyingScale = scale.duplicate(); + + localTransform.quaternionRotation(rotation); + localTransform.translation(translation); + localTransform.scalar(applyingScale); + + resultTransform.copy(parentTransform); + resultTransform.multiply(ArrayMatrix4x4.transform(restData.getPosition())); + parent.cancelScale(resultTransform); + resultTransform.multiply(ArrayMatrix4x4.fromRotation(restData.getRotation())); + resultTransform.multiply(ArrayMatrix4x4.fromScale(restData.getScale())); + + resultTransform.multiply(localTransform); + return new ParentDriverResult(applyingScale); + } + + @Override + public Quaternion getLocalRotation() { + return rotation; + } + + @Override + public Vector3f getLocalTranslation() { + return translation; + } + + @Override + public Vector3f getLocalSize() { + return scale; + } + + public Quaternion getModelRotation(Quaternion parent) { + Quaternion result = ComponentsQuaternion.zero(); + result.multiply(parent); + result.multiply(restData.getRotation()); + result.multiply(getLocalRotation()); + return result; + } + + public float getBoneLength() { + Vector4f bone = new SimpleVector4f(0, 0, 0, 1); + localRestTransform.transform(bone); + return bone.length(); + } + + public class ParentDriverResult implements DriverResult { + + private final Vector3f applyingScale; + + public ParentDriverResult(Vector3f applyingScale) { + this.applyingScale = applyingScale; + } + + @Override + public Matrix4x4 getCurrent() { + return resultTransform; + } + + @Override + public void cancelScale(Matrix4x4 current) { + current.multiply(ArrayMatrix4x4.fromScale(new SimpleVector3f(1f, 1f, 1f).div(applyingScale))); + } + } +} diff --git a/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/ScaleDriverResult.java b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/ScaleDriverResult.java new file mode 100644 index 0000000..8791f60 --- /dev/null +++ b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/ScaleDriverResult.java @@ -0,0 +1,27 @@ +package fr.radi3nt.armatures.armature.driver; + +import fr.radi3nt.maths.components.advanced.matrix.ArrayMatrix4x4; +import fr.radi3nt.maths.components.advanced.matrix.Matrix4x4; +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; + +public class ScaleDriverResult implements DriverResult { + + private final Matrix4x4 result; + private final Vector3f scale; + + public ScaleDriverResult(Matrix4x4 result, Vector3f scale) { + this.result = result; + this.scale = scale; + } + + @Override + public Matrix4x4 getCurrent() { + return result; + } + + @Override + public void cancelScale(Matrix4x4 current) { + current.multiply(ArrayMatrix4x4.fromScale(new SimpleVector3f(1f, 1f, 1f).div(scale))); + } +} diff --git a/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/SetDriverResult.java b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/SetDriverResult.java new file mode 100644 index 0000000..7e594ba --- /dev/null +++ b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/SetDriverResult.java @@ -0,0 +1,22 @@ +package fr.radi3nt.armatures.armature.driver; + +import fr.radi3nt.maths.components.advanced.matrix.Matrix4x4; + +public class SetDriverResult implements DriverResult { + + private final Matrix4x4 result; + + public SetDriverResult(Matrix4x4 result) { + this.result = result; + } + + @Override + public Matrix4x4 getCurrent() { + return result; + } + + @Override + public void cancelScale(Matrix4x4 current) { + + } +} diff --git a/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/WorldIndependentBoneDriver.java b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/WorldIndependentBoneDriver.java new file mode 100644 index 0000000..0a042aa --- /dev/null +++ b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/WorldIndependentBoneDriver.java @@ -0,0 +1,86 @@ +package fr.radi3nt.armatures.armature.driver; + +import fr.radi3nt.maths.components.advanced.matrix.ArrayMatrix4x4; +import fr.radi3nt.maths.components.advanced.matrix.Matrix4x4; +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; + +public class WorldIndependentBoneDriver implements BoneDriver { + + private final Matrix4x4 resultTransform = ArrayMatrix4x4.newIdentity(); + private final Matrix4x4 localTransform = ArrayMatrix4x4.newIdentity(); + + private final Vector3f translation = new SimpleVector3f(); + private final Vector3f scale = new SimpleVector3f(1f, 1f, 1f); + private final Quaternion rotation = ComponentsQuaternion.zero(); + + public WorldIndependentBoneDriver() { + + } + + @Override + public void setPosition(Vector3f position) { + this.translation.copy(position); + } + + @Override + public void setScale(Vector3f scale) { + this.scale.copy(scale); + } + + @Override + public void setRotation(Quaternion quaternion) { + this.rotation.copy(quaternion); + } + + @Override + public void addRotation(Quaternion quaternion) { + rotation.multiply(quaternion); + } + + @Override + public void setPositionAndRotation(Vector3f translation, Quaternion rotation) { + setPosition(translation); + setRotation(rotation); + } + + @Override + public void setAll(Vector3f translate, Quaternion rotate, Vector3f scale) { + setPositionAndRotation(translate, rotate); + setScale(scale); + } + + @Override + public DriverResult getModelSpaceForParentTransform(DriverResult parent) { + localTransform.quaternionRotation(getLocalRotation()); + localTransform.translation(getTranslation()); + localTransform.scalar(getScale()); + + resultTransform.copy(localTransform); + return new SetDriverResult(resultTransform); + } + + public Quaternion getLocalRotation() { + return rotation; + } + + @Override + public Vector3f getLocalTranslation() { + return translation; + } + + @Override + public Vector3f getLocalSize() { + return scale; + } + + public Vector3f getTranslation() { + return translation; + } + + public Vector3f getScale() { + return scale; + } +} diff --git a/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/WorldRestedBoneDriver.java b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/WorldRestedBoneDriver.java new file mode 100644 index 0000000..0bb99bb --- /dev/null +++ b/Armature/src/main/java/fr/radi3nt/armatures/armature/driver/WorldRestedBoneDriver.java @@ -0,0 +1,97 @@ +package fr.radi3nt.armatures.armature.driver; + +import fr.radi3nt.maths.components.advanced.matrix.ArrayMatrix4x4; +import fr.radi3nt.maths.components.advanced.matrix.Matrix4x4; +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; + +public class WorldRestedBoneDriver implements BoneDriver { + + private final Matrix4x4 resultTransform = ArrayMatrix4x4.newIdentity(); + private final Matrix4x4 localTransform = ArrayMatrix4x4.newIdentity(); + private final Matrix4x4 localRestTransform = ArrayMatrix4x4.newIdentity(); + + private final Vector3f translation = new SimpleVector3f(); + private final Vector3f scale = new SimpleVector3f(1f, 1f, 1f); + private final Quaternion rotation = ComponentsQuaternion.zero(); + + private final BoneRestData restData; + + public WorldRestedBoneDriver(BoneRestData restData) { + this.restData = restData; + this.localRestTransform.copy(restData.toMatrix()); + } + + @Override + public void setPosition(Vector3f position) { + this.translation.copy(position); + } + + @Override + public void setScale(Vector3f scale) { + this.scale.copy(scale); + } + + @Override + public void setRotation(Quaternion quaternion) { + this.rotation.copy(quaternion); + } + + @Override + public void addRotation(Quaternion quaternion) { + rotation.multiply(quaternion); + } + + @Override + public void setPositionAndRotation(Vector3f translation, Quaternion rotation) { + setPosition(translation); + setRotation(rotation); + } + + @Override + public void setAll(Vector3f translate, Quaternion rotate, Vector3f scale) { + setPositionAndRotation(translate, rotate); + setScale(scale); + } + + @Override + public DriverResult getModelSpaceForParentTransform(DriverResult parent) { + localTransform.quaternionRotation(getLocalRotation()); + localTransform.translation(getTranslation()); + localTransform.scale(getScale()); + + resultTransform.copy(localRestTransform); + resultTransform.multiply(localTransform); + return new SetDriverResult(resultTransform); + } + + public Vector3f getTranslation() { + return translation; + } + + public Quaternion getLocalRotation() { + return rotation; + } + + @Override + public Vector3f getLocalTranslation() { + return translation; + } + + @Override + public Vector3f getLocalSize() { + return scale; + } + + public Quaternion getModelRotation(Quaternion parent) { + Quaternion duplicate = restData.getRotation().duplicate(); + duplicate.multiply(getLocalRotation()); + return duplicate; + } + + public Vector3f getScale() { + return scale; + } +} diff --git a/Armature/src/main/java/fr/radi3nt/armatures/armature/weights/BoneWeight.java b/Armature/src/main/java/fr/radi3nt/armatures/armature/weights/BoneWeight.java new file mode 100644 index 0000000..b9bd38f --- /dev/null +++ b/Armature/src/main/java/fr/radi3nt/armatures/armature/weights/BoneWeight.java @@ -0,0 +1,24 @@ +package fr.radi3nt.armatures.armature.weights; + +public class BoneWeight { + + private final int bone; + private float weight; + + public BoneWeight(int bone, float weight) { + this.bone = bone; + this.weight = weight; + } + + public int getBoneIndex() { + return bone; + } + + public float getWeight() { + return weight; + } + + public void setWeight(float weight) { + this.weight = weight; + } +} diff --git a/Armature/src/main/java/fr/radi3nt/armatures/controller/ArmatureAnimationController.java b/Armature/src/main/java/fr/radi3nt/armatures/controller/ArmatureAnimationController.java new file mode 100644 index 0000000..a140636 --- /dev/null +++ b/Armature/src/main/java/fr/radi3nt/armatures/controller/ArmatureAnimationController.java @@ -0,0 +1,39 @@ +package fr.radi3nt.armatures.controller; + +import fr.radi3nt.animations.channels.ChannelIdentifier; +import fr.radi3nt.animations.timeline.AnimationTimeline; +import fr.radi3nt.armatures.armature.driver.BoneDriver; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; + +public class ArmatureAnimationController { + + private final AnimationTimeline animationTimeline; + private final ArmatureController controller; + + public ArmatureAnimationController(AnimationTimeline animationTimeline, ArmatureController controller) { + this.animationTimeline = animationTimeline; + this.controller = controller; + } + + public void update() { + for (String availableObject : animationTimeline.getAvailableObjects()) { + BoneDriver driver = controller.getDriver(availableObject); + if (driver==null) { + System.err.println("Unable to find object '" + availableObject + "' referenced in the animation clip, ignoring"); + continue; + } + + Quaternion rotate = animationTimeline.get(ChannelIdentifier.rotation(availableObject), null); + Vector3f translate = animationTimeline.get(ChannelIdentifier.translation(availableObject), null); + Vector3f scale = animationTimeline.get(ChannelIdentifier.scale(availableObject), null); + + if (rotate!=null) + driver.setRotation(rotate); + if (translate!=null) + driver.setPosition(translate); + if (scale!=null) + driver.setScale(scale); + } + } +} diff --git a/Armature/src/main/java/fr/radi3nt/armatures/controller/ArmatureController.java b/Armature/src/main/java/fr/radi3nt/armatures/controller/ArmatureController.java new file mode 100644 index 0000000..6cb09c1 --- /dev/null +++ b/Armature/src/main/java/fr/radi3nt/armatures/controller/ArmatureController.java @@ -0,0 +1,29 @@ +package fr.radi3nt.armatures.controller; + +import fr.radi3nt.armatures.armature.bone.Bone; +import fr.radi3nt.armatures.armature.driver.BoneDriver; + +import java.util.HashMap; +import java.util.Map; + +public class ArmatureController { + + private final Map driverMap; + + public ArmatureController(Map driverMap) { + this.driverMap = driverMap; + } + + public static ArmatureController from(Map bones) { + return new ArmatureController(bones); + } + + public BoneDriver getDriver(String boneName) { + return driverMap.get(boneName).getBoneDriver(); + } + + public void setDriver(String boneName, BoneDriver driver) { + driverMap.get(boneName).setBoneDriver(driver); + } + +} diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/blackboard/Blackboard.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/blackboard/Blackboard.java deleted file mode 100644 index 164993c..0000000 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/blackboard/Blackboard.java +++ /dev/null @@ -1,9 +0,0 @@ -package fr.radi3nt.behavior.blackboard; - -public interface Blackboard { - - void put(BlackboardVariableId blackboardVariableValue, T value); - - T get(BlackboardVariableId type); - -} diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/blackboard/BlackboardContext.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/blackboard/BlackboardContext.java deleted file mode 100644 index 53fad01..0000000 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/blackboard/BlackboardContext.java +++ /dev/null @@ -1,7 +0,0 @@ -package fr.radi3nt.behavior.blackboard; - -public interface BlackboardContext { - - Blackboard getBlackboard(); - -} diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/blackboard/BlackboardVariableId.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/blackboard/BlackboardVariableId.java deleted file mode 100644 index 201c179..0000000 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/blackboard/BlackboardVariableId.java +++ /dev/null @@ -1,6 +0,0 @@ -package fr.radi3nt.behavior.blackboard; - -public interface BlackboardVariableId { - - -} diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/blackboard/MapBlackboard.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/blackboard/MapBlackboard.java deleted file mode 100644 index ff86118..0000000 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/blackboard/MapBlackboard.java +++ /dev/null @@ -1,19 +0,0 @@ -package fr.radi3nt.behavior.blackboard; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public class MapBlackboard implements Blackboard { - - private final Map, Object> infoMap = new ConcurrentHashMap<>(); - - @Override - public void put(BlackboardVariableId blackboardVariableValue, T value) { - infoMap.put(blackboardVariableValue, value); - } - - @Override - public T get(BlackboardVariableId type) { - return (T) infoMap.get(type); - } -} diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/blackboard/SetBlackboardContext.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/blackboard/SetBlackboardContext.java deleted file mode 100644 index 4f1098b..0000000 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/blackboard/SetBlackboardContext.java +++ /dev/null @@ -1,15 +0,0 @@ -package fr.radi3nt.behavior.blackboard; - -public class SetBlackboardContext implements BlackboardContext { - - private Blackboard blackboard; - - @Override - public Blackboard getBlackboard() { - return blackboard; - } - - public void setBlackboard(Blackboard blackboard) { - this.blackboard = blackboard; - } -} diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/TreeNode.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/BehaviorTreeNode.java similarity index 65% rename from BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/TreeNode.java rename to BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/BehaviorTreeNode.java index bf2abee..1589892 100644 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/TreeNode.java +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/BehaviorTreeNode.java @@ -1,6 +1,6 @@ package fr.radi3nt.behavior.tree.nodes; -public interface TreeNode { +public interface BehaviorTreeNode { NodeStatus run(); diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/composite/CompositeNode.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/composite/CompositeNode.java index 38d1321..66e6dd3 100644 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/composite/CompositeNode.java +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/composite/CompositeNode.java @@ -1,25 +1,32 @@ package fr.radi3nt.behavior.tree.nodes.composite; -import fr.radi3nt.behavior.tree.nodes.TreeNode; -import fr.radi3nt.behavior.tree.nodes.types.CollectionNode; +import fr.radi3nt.behavior.tree.nodes.BehaviorTreeNode; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; -public abstract class CompositeNode extends CollectionNode { +public abstract class CompositeNode implements BehaviorTreeNode { - public CompositeNode(Collection animationTreeNodes) { - super(animationTreeNodes); + protected final Collection children; + + public CompositeNode(Collection children) { + this.children = children; + } + + public CompositeNode(BehaviorTreeNode... children) { + this(new ArrayList<>(Arrays.asList(children))); } - public void add(TreeNode treeNode) { - treeNodes.add(treeNode); + public void add(BehaviorTreeNode behaviorTreeNode) { + children.add(behaviorTreeNode); } - public void remove(TreeNode treeNode) { - treeNodes.remove(treeNode); + public void remove(BehaviorTreeNode behaviorTreeNode) { + children.remove(behaviorTreeNode); } - public boolean contains(TreeNode treeNode) { - return treeNodes.contains(treeNode); + public boolean contains(BehaviorTreeNode behaviorTreeNode) { + return children.contains(behaviorTreeNode); } } diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/composite/FallbackNode.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/composite/FallbackNode.java index 785cb9f..97f6021 100644 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/composite/FallbackNode.java +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/composite/FallbackNode.java @@ -1,23 +1,24 @@ package fr.radi3nt.behavior.tree.nodes.composite; +import fr.radi3nt.behavior.tree.nodes.BehaviorTreeNode; import fr.radi3nt.behavior.tree.nodes.NodeStatus; -import fr.radi3nt.behavior.tree.nodes.TreeNode; -import java.util.ArrayList; +import java.util.Collection; -/** - * This BasicSelectorNode returns the first success or running result in its child. If not found, it returns 'NodeStatus.FAILURE' - */ public class FallbackNode extends CompositeNode { - public FallbackNode() { - super(new ArrayList<>()); + public FallbackNode(Collection nodes) { + super(nodes); + } + + public FallbackNode(BehaviorTreeNode... nodes) { + super(nodes); } @Override public NodeStatus run() { - for (TreeNode treeNode : treeNodes) { - NodeStatus nodeStatus = treeNode.run(); + for (BehaviorTreeNode behaviorTreeNode : children) { + NodeStatus nodeStatus = behaviorTreeNode.run(); if (nodeStatus != NodeStatus.FAILURE) return nodeStatus; } diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/composite/SequenceNode.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/composite/SequenceNode.java index b2b9494..7e39aaf 100644 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/composite/SequenceNode.java +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/composite/SequenceNode.java @@ -1,30 +1,24 @@ package fr.radi3nt.behavior.tree.nodes.composite; +import fr.radi3nt.behavior.tree.nodes.BehaviorTreeNode; import fr.radi3nt.behavior.tree.nodes.NodeStatus; -import fr.radi3nt.behavior.tree.nodes.TreeNode; -import java.util.ArrayList; +import java.util.Collection; -/** - * This BasicSelectorNode returns the first failure or running result in its child. If not found, it returns 'NodeStatus.SUCCESS' - */ public class SequenceNode extends CompositeNode { - public SequenceNode() { - super(new ArrayList<>()); + public SequenceNode(Collection nodes) { + super(nodes); } - public SequenceNode(TreeNode... treeNodes) { - this(); - for (TreeNode treeNode : treeNodes) { - add(treeNode); - } + public SequenceNode(BehaviorTreeNode... nodes) { + super(nodes); } @Override public NodeStatus run() { - for (TreeNode treeNode : treeNodes) { - NodeStatus nodeStatus = treeNode.run(); + for (BehaviorTreeNode behaviorTreeNode : children) { + NodeStatus nodeStatus = behaviorTreeNode.run(); if (nodeStatus != NodeStatus.SUCCESS) return nodeStatus; } diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/DecorationNode.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/DecorationNode.java index 28bc7c5..b974c92 100644 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/DecorationNode.java +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/DecorationNode.java @@ -1,24 +1,20 @@ package fr.radi3nt.behavior.tree.nodes.decoration; -import fr.radi3nt.behavior.tree.nodes.TreeNode; +import fr.radi3nt.behavior.tree.nodes.BehaviorTreeNode; -public abstract class DecorationNode implements TreeNode { +public abstract class DecorationNode implements BehaviorTreeNode { - protected TreeNode treeNode; + protected BehaviorTreeNode child; public DecorationNode() { } - public DecorationNode(TreeNode treeNode) { - this.treeNode = treeNode; + public DecorationNode(BehaviorTreeNode child) { + this.child = child; } - public void set(TreeNode treeNode) { - this.treeNode = treeNode; - } - - public TreeNode get() { - return treeNode; + public void set(BehaviorTreeNode behaviorTreeNode) { + this.child = behaviorTreeNode; } } diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/FailureNode.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/FailureNode.java index 4c986b5..40d08bc 100644 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/FailureNode.java +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/FailureNode.java @@ -1,20 +1,17 @@ package fr.radi3nt.behavior.tree.nodes.decoration; +import fr.radi3nt.behavior.tree.nodes.BehaviorTreeNode; import fr.radi3nt.behavior.tree.nodes.NodeStatus; -import fr.radi3nt.behavior.tree.nodes.TreeNode; public class FailureNode extends DecorationNode { - public FailureNode() { - } - - public FailureNode(TreeNode treeNode) { - super(treeNode); + public FailureNode(BehaviorTreeNode node) { + super(node); } @Override public NodeStatus run() { - treeNode.run(); + child.run(); return NodeStatus.FAILURE; } diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/ForNode.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/ForNode.java deleted file mode 100644 index cb49ba6..0000000 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/ForNode.java +++ /dev/null @@ -1,25 +0,0 @@ -package fr.radi3nt.behavior.tree.nodes.decoration; - -import fr.radi3nt.behavior.tree.nodes.NodeStatus; -import fr.radi3nt.behavior.tree.nodes.TreeNode; - -public class ForNode extends DecorationNode { - - private final int limit; - - public ForNode(TreeNode treeNode, int limit) { - super(treeNode); - this.limit = limit; - } - - @Override - public NodeStatus run() { - NodeStatus status = NodeStatus.FAILURE; - for (int i = 0; i < limit; i++) { - if ((status = treeNode.run()) != NodeStatus.FAILURE) - break; - } - - return status; - } -} diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/InverterNode.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/InverterNode.java index 1d1429f..bb56bd7 100644 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/InverterNode.java +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/InverterNode.java @@ -1,20 +1,17 @@ package fr.radi3nt.behavior.tree.nodes.decoration; +import fr.radi3nt.behavior.tree.nodes.BehaviorTreeNode; import fr.radi3nt.behavior.tree.nodes.NodeStatus; -import fr.radi3nt.behavior.tree.nodes.TreeNode; public class InverterNode extends DecorationNode { - public InverterNode() { - } - - public InverterNode(TreeNode treeNode) { - super(treeNode); + public InverterNode(BehaviorTreeNode node) { + super(node); } @Override public NodeStatus run() { - NodeStatus nodeStatus = treeNode.run(); + NodeStatus nodeStatus = child.run(); switch (nodeStatus) { case SUCCESS: return NodeStatus.FAILURE; diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/SuccessNode.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/SuccessNode.java index f8773c4..79fc2ff 100644 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/SuccessNode.java +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/SuccessNode.java @@ -1,20 +1,20 @@ package fr.radi3nt.behavior.tree.nodes.decoration; +import fr.radi3nt.behavior.tree.nodes.BehaviorTreeNode; import fr.radi3nt.behavior.tree.nodes.NodeStatus; -import fr.radi3nt.behavior.tree.nodes.TreeNode; public class SuccessNode extends DecorationNode { public SuccessNode() { } - public SuccessNode(TreeNode treeNode) { - super(treeNode); + public SuccessNode(BehaviorTreeNode node) { + super(node); } @Override public NodeStatus run() { - treeNode.run(); + child.run(); return NodeStatus.SUCCESS; } diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/WhileNode.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/WhileNode.java deleted file mode 100644 index 9289d84..0000000 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/WhileNode.java +++ /dev/null @@ -1,22 +0,0 @@ -package fr.radi3nt.behavior.tree.nodes.decoration; - -import fr.radi3nt.behavior.tree.nodes.NodeStatus; -import fr.radi3nt.behavior.tree.nodes.TreeNode; - -public class WhileNode extends DecorationNode { - - public WhileNode(TreeNode treeNode) { - super(treeNode); - } - - @Override - public NodeStatus run() { - NodeStatus status; - while (true) { - if ((status = treeNode.run()) != NodeStatus.FAILURE) - break; - } - - return status; - } -} diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/loops/WhileFailureNode.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/loops/WhileFailureNode.java new file mode 100644 index 0000000..e136b4c --- /dev/null +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/loops/WhileFailureNode.java @@ -0,0 +1,23 @@ +package fr.radi3nt.behavior.tree.nodes.decoration.loops; + +import fr.radi3nt.behavior.tree.nodes.BehaviorTreeNode; +import fr.radi3nt.behavior.tree.nodes.NodeStatus; +import fr.radi3nt.behavior.tree.nodes.decoration.DecorationNode; + +public class WhileFailureNode extends DecorationNode { + + public WhileFailureNode(BehaviorTreeNode behaviorTreeNode) { + super(behaviorTreeNode); + } + + @Override + public NodeStatus run() { + NodeStatus status; + while (true) { + if ((status = child.run()) != NodeStatus.FAILURE) + break; + } + + return status; + } +} diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/loops/WhileFailureOrLimitNode.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/loops/WhileFailureOrLimitNode.java new file mode 100644 index 0000000..c5fbed0 --- /dev/null +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/decoration/loops/WhileFailureOrLimitNode.java @@ -0,0 +1,26 @@ +package fr.radi3nt.behavior.tree.nodes.decoration.loops; + +import fr.radi3nt.behavior.tree.nodes.BehaviorTreeNode; +import fr.radi3nt.behavior.tree.nodes.NodeStatus; +import fr.radi3nt.behavior.tree.nodes.decoration.DecorationNode; + +public class WhileFailureOrLimitNode extends DecorationNode { + + private final int limit; + + public WhileFailureOrLimitNode(BehaviorTreeNode node, int limit) { + super(node); + this.limit = limit; + } + + @Override + public NodeStatus run() { + NodeStatus status = NodeStatus.FAILURE; + for (int i = 0; i < limit; i++) { + if ((status = child.run()) != NodeStatus.FAILURE) + break; + } + + return status; + } +} diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/leaf/ActionNode.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/leaf/ActionNode.java index 1a8f356..2b31359 100644 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/leaf/ActionNode.java +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/leaf/ActionNode.java @@ -1,7 +1,7 @@ package fr.radi3nt.behavior.tree.nodes.leaf; -import fr.radi3nt.behavior.tree.nodes.TreeNode; +import fr.radi3nt.behavior.tree.nodes.BehaviorTreeNode; -public interface ActionNode extends TreeNode { +public interface ActionNode extends BehaviorTreeNode { } diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/leaf/condition/ConditionalNode.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/leaf/condition/ConditionalNode.java index 5394979..eb5bdb3 100644 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/leaf/condition/ConditionalNode.java +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/leaf/condition/ConditionalNode.java @@ -1,7 +1,7 @@ package fr.radi3nt.behavior.tree.nodes.leaf.condition; -import fr.radi3nt.behavior.tree.nodes.TreeNode; +import fr.radi3nt.behavior.tree.nodes.BehaviorTreeNode; -public interface ConditionalNode extends TreeNode { +public interface ConditionalNode extends BehaviorTreeNode { } diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/types/CollectionNode.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/types/CollectionNode.java deleted file mode 100644 index 0f8170b..0000000 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/tree/nodes/types/CollectionNode.java +++ /dev/null @@ -1,14 +0,0 @@ -package fr.radi3nt.behavior.tree.nodes.types; - -import fr.radi3nt.behavior.tree.nodes.TreeNode; - -import java.util.Collection; - -public abstract class CollectionNode implements TreeNode { - - protected final Collection treeNodes; - - public CollectionNode(Collection animationTreeNodes) { - this.treeNodes = animationTreeNodes; - } -} diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/CompleteNodeVariable.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/CompleteNodeVariable.java new file mode 100644 index 0000000..789c2de --- /dev/null +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/CompleteNodeVariable.java @@ -0,0 +1,4 @@ +package fr.radi3nt.behavior.variable; + +public interface CompleteNodeVariable extends InputNodeVariable, OutputNodeVariable { +} diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/EncapsulatingNodeVariable.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/EncapsulatingNodeVariable.java deleted file mode 100644 index 2d46fa2..0000000 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/EncapsulatingNodeVariable.java +++ /dev/null @@ -1,15 +0,0 @@ -package fr.radi3nt.behavior.variable; - -public class EncapsulatingNodeVariable implements NodeVariable { - - private final NodeVariable variable; - - public EncapsulatingNodeVariable(NodeVariable variable) { - this.variable = variable; - } - - @Override - public T get() { - return variable.get(); - } -} diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/InputNodeVariable.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/InputNodeVariable.java new file mode 100644 index 0000000..0c92ad9 --- /dev/null +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/InputNodeVariable.java @@ -0,0 +1,7 @@ +package fr.radi3nt.behavior.variable; + +public interface InputNodeVariable extends NodeVariable { + + T get(); + +} diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/NodeVariable.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/NodeVariable.java index 1aea95b..685d118 100644 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/NodeVariable.java +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/NodeVariable.java @@ -2,6 +2,4 @@ public interface NodeVariable { - T get(); - } diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/OutputNodeVariable.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/OutputNodeVariable.java new file mode 100644 index 0000000..f2ce0ad --- /dev/null +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/OutputNodeVariable.java @@ -0,0 +1,7 @@ +package fr.radi3nt.behavior.variable; + +public interface OutputNodeVariable extends NodeVariable { + + void set(T set); + +} diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/encapsulation/EncapsulatingInputNodeVariable.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/encapsulation/EncapsulatingInputNodeVariable.java new file mode 100644 index 0000000..c9555af --- /dev/null +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/encapsulation/EncapsulatingInputNodeVariable.java @@ -0,0 +1,17 @@ +package fr.radi3nt.behavior.variable.encapsulation; + +import fr.radi3nt.behavior.variable.InputNodeVariable; + +public class EncapsulatingInputNodeVariable implements InputNodeVariable { + + private final InputNodeVariable variable; + + public EncapsulatingInputNodeVariable(InputNodeVariable variable) { + this.variable = variable; + } + + @Override + public T get() { + return variable.get(); + } +} diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/encapsulation/EncapsulatingOutputNodeVariable.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/encapsulation/EncapsulatingOutputNodeVariable.java new file mode 100644 index 0000000..448f2d5 --- /dev/null +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/encapsulation/EncapsulatingOutputNodeVariable.java @@ -0,0 +1,17 @@ +package fr.radi3nt.behavior.variable.encapsulation; + +import fr.radi3nt.behavior.variable.OutputNodeVariable; + +public class EncapsulatingOutputNodeVariable implements OutputNodeVariable { + + private final OutputNodeVariable variable; + + public EncapsulatingOutputNodeVariable(OutputNodeVariable variable) { + this.variable = variable; + } + + @Override + public void set(T set) { + variable.set(set); + } +} diff --git a/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/DirectNodeVariable.java b/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/impl/DirectNodeVariable.java similarity index 52% rename from BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/DirectNodeVariable.java rename to BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/impl/DirectNodeVariable.java index 3635a66..3f97c99 100644 --- a/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/DirectNodeVariable.java +++ b/BehaviorTree/src/main/java/fr/radi3nt/behavior/variable/impl/DirectNodeVariable.java @@ -1,6 +1,8 @@ -package fr.radi3nt.behavior.variable; +package fr.radi3nt.behavior.variable.impl; -public class DirectNodeVariable implements NodeVariable { +import fr.radi3nt.behavior.variable.CompleteNodeVariable; + +public class DirectNodeVariable implements CompleteNodeVariable { private T value; @@ -16,7 +18,8 @@ public T get() { return value; } - public void setValue(T value) { + @Override + public void set(T value) { this.value = value; } } diff --git a/DataStructures/.gitignore b/DataStructures/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/DataStructures/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/DataStructures/pom.xml b/DataStructures/pom.xml new file mode 100644 index 0000000..adb58a6 --- /dev/null +++ b/DataStructures/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + fr.radi3nt + JavaUtil + 1.0 + + + DataStructures + + + 8 + 8 + UTF-8 + + + \ No newline at end of file diff --git a/DataStructures/src/main/java/fr/radi3nt/data/collections/SparseArray.java b/DataStructures/src/main/java/fr/radi3nt/data/collections/SparseArray.java new file mode 100644 index 0000000..71588ab --- /dev/null +++ b/DataStructures/src/main/java/fr/radi3nt/data/collections/SparseArray.java @@ -0,0 +1,227 @@ +package fr.radi3nt.data.collections; + +import java.util.Arrays; + +public class SparseArray { + + private static final Object DELETED = new Object(); + private static final int DEFAULT_CAPACITY = 10; + private boolean garbage = false; + + private int[] keys; + private Object[] values; + private int size; + + public SparseArray() { + this(DEFAULT_CAPACITY); + } + + public SparseArray(int initialCapacity) { + if (initialCapacity == 0) { + keys = new int[0]; + values = new Object[0]; + } else { + values = new Object[initialCapacity]; + keys = new int[values.length]; + } + size = 0; + } + + public SparseArray(SparseArray array) { + this.keys = Arrays.copyOf(array.keys, array.keys.length); + this.values = Arrays.copyOf(array.values, array.values.length); + this.size = array.size; + } + + public T get(int key) { + return getOrDefault(key, null); + } + + @SuppressWarnings("unchecked") + public T getOrDefault(int key, T valueIfKeyNotFound) { + int i = binarySearch(keys, size, key); + if (i < 0 || values[i] == DELETED) { + return valueIfKeyNotFound; + } else { + return (T) values[i]; + } + } + + public void put(int key, T value) { + int i = binarySearch(keys, size, key); + if (i >= 0) { + values[i] = value; + } else { + i = ~i; + if (i < size && values[i] == DELETED) { + keys[i] = key; + values[i] = value; + return; + } + if (garbage && size >= keys.length) { + performGC(); + i = ~binarySearch(keys, size, key); + } + keys = insert(keys, size, i, key); + values = insert(values, size, i, value); + size++; + } + } + + public void remove(int key) { + int i = binarySearch(keys, size, key); + if (i >= 0) { + if (values[i] != DELETED) { + values[i] = DELETED; + garbage = true; + } + } + } + + public void removeAt(int index) { + if (values[index] != DELETED) { + values[index] = DELETED; + garbage = true; + } + } + + public int keyAt(int index) { + if (garbage) { + performGC(); + } + return keys[index]; + } + + @SuppressWarnings("unchecked") + public T valueAt(int index) { + if (garbage) { + performGC(); + } + return (T) values[index]; + } + + public void setValueAt(int index, T value) { + if (garbage) { + performGC(); + } + values[index] = value; + } + + public void clear() { + int n = size; + Object[] valArray = values; + for (int i = 0; i < n; i++) { + valArray[i] = null; + } + size = 0; + garbage = false; + } + + public int size() { + if (garbage) { + performGC(); + } + return size; + } + + public boolean isEmpty() { + return size() == 0; + } + + /** + * Method to perform GC + */ + private void performGC() { + int n = size; + int position = 0; + int[] keyArray = keys; + Object[] valArray = values; + + for (int i = 0; i < n; i++) { + Object val = valArray[i]; + if (val != DELETED) { + if (i != position) { + keyArray[position] = keyArray[i]; + valArray[position] = val; + valArray[i] = null; + } + position++; + } + garbage = false; + size = position; + } + + } + + /** + * Method to insert an element at a given index + * + * @param array + * @param currentSize + * @param index + * @param element + * @return {@link Object[]} + */ + private Object[] insert(Object[] array, int currentSize, int index, T element) { + assert currentSize <= array.length; + if (currentSize + 1 <= array.length) { + System.arraycopy(array, index, array, index + 1, currentSize - index); + array[index] = element; + return array; + } + Object[] newArray = new Object[currentSize * 2]; + System.arraycopy(array, 0, newArray, 0, index); + newArray[index] = element; + System.arraycopy(array, index, newArray, index + 1, array.length - index); + return newArray; + } + + /** + * Method to insert an integer at given index + * + * @param array + * @param currentSize + * @param index + * @param element + * @return {@link int[]} + */ + private static int[] insert(int[] array, int currentSize, int index, int element) { + assert currentSize <= array.length; + if (currentSize + 1 <= array.length) { + System.arraycopy(array, index, array, index + 1, currentSize - index); + array[index] = element; + return array; + } + int[] newArray = new int[currentSize * 2]; + System.arraycopy(array, 0, newArray, 0, index); + newArray[index] = element; + System.arraycopy(array, index, newArray, index + 1, array.length - index); + return newArray; + } + + /** + * Method to do a binary search on int array + * + * @param array + * @param size + * @param value + * @return {@link int} + */ + public static int binarySearch(int[] array, int size, int value) { + int lo = 0; + int hi = size - 1; + while (lo <= hi) { + final int mid = (lo + hi) >>> 1; + final int midVal = array[mid]; + if (midVal < value) { + lo = mid + 1; + } else if (midVal > value) { + hi = mid - 1; + } else { + return mid; + } + } + return ~lo; + } + +} \ No newline at end of file diff --git a/FileHelper/src/main/java/fr/radi3nt/file/files/CompleteFile.java b/FileHelper/src/main/java/fr/radi3nt/file/files/CompleteFile.java index 1c285ca..06c490e 100644 --- a/FileHelper/src/main/java/fr/radi3nt/file/files/CompleteFile.java +++ b/FileHelper/src/main/java/fr/radi3nt/file/files/CompleteFile.java @@ -3,7 +3,6 @@ import fr.radi3nt.file.FileAccess; import java.io.FileNotFoundException; -import java.io.IOException; import java.io.RandomAccessFile; public interface CompleteFile extends WritableFile, ReadableFile { diff --git a/FileHelper/src/main/java/fr/radi3nt/file/files/ReadableFile.java b/FileHelper/src/main/java/fr/radi3nt/file/files/ReadableFile.java index 34a7cc9..53ec463 100644 --- a/FileHelper/src/main/java/fr/radi3nt/file/files/ReadableFile.java +++ b/FileHelper/src/main/java/fr/radi3nt/file/files/ReadableFile.java @@ -1,10 +1,10 @@ package fr.radi3nt.file.files; -import java.io.FileNotFoundException; +import java.io.IOException; import java.io.InputStream; public interface ReadableFile extends File { - InputStream getInputStream() throws FileNotFoundException; + InputStream getInputStream() throws IOException; } diff --git a/FileHelper/src/main/java/fr/radi3nt/file/files/WritableFile.java b/FileHelper/src/main/java/fr/radi3nt/file/files/WritableFile.java index f969a65..3e14b6c 100644 --- a/FileHelper/src/main/java/fr/radi3nt/file/files/WritableFile.java +++ b/FileHelper/src/main/java/fr/radi3nt/file/files/WritableFile.java @@ -1,12 +1,12 @@ package fr.radi3nt.file.files; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; public interface WritableFile extends File { - OutputStream getOutputSteam() throws FileNotFoundException; + OutputStream getOutputSteam() throws IOException; + void create() throws IOException; void delete() throws IOException; diff --git a/FileHelper/src/main/java/fr/radi3nt/file/impl/NioFile.java b/FileHelper/src/main/java/fr/radi3nt/file/impl/NioFile.java index 5adfa1e..93ed6fb 100644 --- a/FileHelper/src/main/java/fr/radi3nt/file/impl/NioFile.java +++ b/FileHelper/src/main/java/fr/radi3nt/file/impl/NioFile.java @@ -1,7 +1,7 @@ package fr.radi3nt.file.impl; -import fr.radi3nt.file.files.CompleteFile; import fr.radi3nt.file.FileAccess; +import fr.radi3nt.file.files.CompleteFile; import java.io.*; import java.nio.file.Files; @@ -32,13 +32,13 @@ public boolean isCreated() { } @Override - public InputStream getInputStream() throws FileNotFoundException { - return new FileInputStream(path.toFile()); + public InputStream getInputStream() throws IOException { + return Files.newInputStream(path); } @Override - public OutputStream getOutputSteam() throws FileNotFoundException { - return new FileOutputStream(path.toFile()); + public OutputStream getOutputSteam() throws IOException { + return Files.newOutputStream(path); } @Override diff --git a/FileHelper/src/main/java/fr/radi3nt/file/impl/ResourceFile.java b/FileHelper/src/main/java/fr/radi3nt/file/impl/ResourceFile.java index e4fa5d2..d918d4a 100644 --- a/FileHelper/src/main/java/fr/radi3nt/file/impl/ResourceFile.java +++ b/FileHelper/src/main/java/fr/radi3nt/file/impl/ResourceFile.java @@ -2,17 +2,16 @@ import fr.radi3nt.file.files.ReadableFile; -import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; +import java.util.Objects; public class ResourceFile implements ReadableFile { private static final String FILE_SEPARATOR = "/"; private static final String BASE_PATH = ""; - private String path; + private final String path; private final String name; public ResourceFile(String path) { @@ -22,10 +21,11 @@ public ResourceFile(String path) { } public ResourceFile(String... paths) { - this.path = BASE_PATH; + String path = BASE_PATH; for (final String part : paths) { - this.path = this.path + FILE_SEPARATOR + part; + path += FILE_SEPARATOR + part; } + this.path = path; final String[] dirs = this.path.split(FILE_SEPARATOR); this.name = dirs[dirs.length - 1]; } @@ -36,10 +36,11 @@ public ResourceFile(ResourceFile file, String subFile) { } public ResourceFile(ResourceFile file, String... subFiles) { - this.path = file.path; + String path = file.path; for (String part : subFiles) { - this.path += (FILE_SEPARATOR + part); + path += (FILE_SEPARATOR + part); } + this.path = path; String[] dirs = path.split(FILE_SEPARATOR); this.name = dirs[dirs.length - 1]; } @@ -54,7 +55,10 @@ public String toString() { } public InputStream getInputStream() { - return ResourceFile.class.getResourceAsStream(path); + InputStream stream = ResourceFile.class.getResourceAsStream(path); + if (stream==null) + throw new IllegalStateException("Could not get resource '" + path + "', as it doesn't seem to exist"); + return stream; } public String getName() { @@ -77,4 +81,20 @@ public boolean isCreated() { return exists; } + + @Override + public final boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ResourceFile)) return false; + + ResourceFile that = (ResourceFile) o; + return Objects.equals(path, that.path) && Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + int result = Objects.hashCode(path); + result = 31 * result + Objects.hashCode(name); + return result; + } } diff --git a/FileHelper/src/main/java/fr/radi3nt/file/util/CopyFileVisitor.java b/FileHelper/src/main/java/fr/radi3nt/file/util/CopyFileVisitor.java new file mode 100644 index 0000000..1b4a08c --- /dev/null +++ b/FileHelper/src/main/java/fr/radi3nt/file/util/CopyFileVisitor.java @@ -0,0 +1,35 @@ +package fr.radi3nt.file.util; + +import java.io.IOException; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; + +public class CopyFileVisitor extends SimpleFileVisitor { + + private final Path target; + private final Path source; + + public CopyFileVisitor(Path target, Path source) { + this.target = target; + this.source = source; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + Path dst = resolve(dir); + Files.createDirectories(dst); + return super.preVisitDirectory(dir, attrs); + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Path dst = resolve(file); + Files.copy(Files.newInputStream(file), dst, StandardCopyOption.REPLACE_EXISTING); + return super.visitFile(file, attrs); + } + + private Path resolve(Path path) { + return target.resolve(source.relativize(path).toString()); + } + +} diff --git a/FileHelper/src/main/java/fr/radi3nt/file/util/Installer.java b/FileHelper/src/main/java/fr/radi3nt/file/util/Installer.java deleted file mode 100644 index 2684a39..0000000 --- a/FileHelper/src/main/java/fr/radi3nt/file/util/Installer.java +++ /dev/null @@ -1,55 +0,0 @@ -package fr.radi3nt.file.util; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.*; -import java.nio.file.attribute.BasicFileAttributes; - -public class Installer extends SimpleFileVisitor { - - public static void installResources(Path dst, Class cls, String root) throws URISyntaxException, IOException { - URL location = cls.getProtectionDomain().getCodeSource().getLocation(); - if (location.getProtocol().equals("file")) { - Path path = Paths.get(location.toURI()); - if (location.getPath().endsWith(".jar")) { - try (FileSystem fs = FileSystems.newFileSystem(path, null)) { - installResources(dst, fs.getPath("/" + root)); - } - } else { - installResources(dst, path.resolve(root)); - } - } else { - throw new IllegalArgumentException("Not supported: " + location); - } - } - - private static void installResources(Path dst, Path src) throws IOException { - Files.walkFileTree(src, new Installer(dst, src)); - } - - private final Path target, source; - - private Installer(Path dst, Path src) { - target = dst; - source = src; - } - - private Path resolve(Path path) { - return target.resolve(source.relativize(path).toString()); - } - - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - Path dst = resolve(dir); - Files.createDirectories(dst); - return super.preVisitDirectory(dir, attrs); - } - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - Path dst = resolve(file); - Files.copy(Files.newInputStream(file), dst, StandardCopyOption.REPLACE_EXISTING); - return super.visitFile(file, attrs); - } -} \ No newline at end of file diff --git a/FileHelper/src/main/java/fr/radi3nt/file/util/RecursiveResourceVisitor.java b/FileHelper/src/main/java/fr/radi3nt/file/util/RecursiveResourceVisitor.java new file mode 100644 index 0000000..36d58df --- /dev/null +++ b/FileHelper/src/main/java/fr/radi3nt/file/util/RecursiveResourceVisitor.java @@ -0,0 +1,35 @@ +package fr.radi3nt.file.util; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.*; + +public final class RecursiveResourceVisitor { + + public static void consume(VisitorSupplier consumer, Class cls, String root) throws URISyntaxException, IOException { + URL location = cls.getProtectionDomain().getCodeSource().getLocation(); + if (location.getProtocol().equals("file")) { + Path path = Paths.get(location.toURI()); + if (location.getPath().endsWith(".jar")) { + try (FileSystem fs = FileSystems.newFileSystem(path, (ClassLoader) null)) { + walkTree(consumer, fs.getPath("/" + root)); + } + } else { + walkTree(consumer, path.resolve(root)); + } + } else { + throw new IllegalArgumentException("Not supported: " + location); + } + } + + private static void walkTree(VisitorSupplier visitor, Path src) throws IOException { + Files.walkFileTree(src, visitor.get(src)); + } + + public interface VisitorSupplier { + + FileVisitor get(Path src); + + } +} \ No newline at end of file diff --git a/InverseKinematics/pom.xml b/InverseKinematics/pom.xml new file mode 100644 index 0000000..c65a72b --- /dev/null +++ b/InverseKinematics/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + fr.radi3nt + JavaUtil + 1.0 + + + InverseKinematics + + + 8 + 8 + UTF-8 + + + + fr.radi3nt + MathsHelper + 1.0 + compile + + + + \ No newline at end of file diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/IkIterativeSolver.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/IkIterativeSolver.java new file mode 100644 index 0000000..6339e1e --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/IkIterativeSolver.java @@ -0,0 +1,35 @@ +package fr.radi3nt.ik.solvers; + +public abstract class IkIterativeSolver implements IkSolver { + + private final int maxIterations; + protected final float allowedMarginOfError; + + public IkIterativeSolver(int maxIterations, float allowedMarginOfError) { + this.maxIterations = maxIterations; + this.allowedMarginOfError = allowedMarginOfError; + } + + + @Override + public void solve() { + for (int i = 0; i < maxIterations; i++) { + prepareSolve(); + if (isInAllowedMarginOfError()) + break; + iteration(); + } + endSolve(); + } + + protected void endSolve() { + + } + + protected void prepareSolve() { + + } + + protected abstract boolean isInAllowedMarginOfError(); + protected abstract void iteration(); +} diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/IkSolver.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/IkSolver.java new file mode 100644 index 0000000..fe90399 --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/IkSolver.java @@ -0,0 +1,7 @@ +package fr.radi3nt.ik.solvers; + +public interface IkSolver { + + void solve(); + +} diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/CCDIKSolver.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/CCDIKSolver.java new file mode 100644 index 0000000..60483b7 --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/CCDIKSolver.java @@ -0,0 +1,114 @@ +package fr.radi3nt.ik.solvers.ccdik; + +import fr.radi3nt.ik.solvers.IkIterativeSolver; +import fr.radi3nt.ik.solvers.ccdik.effectors.Effector; +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; + +public class CCDIKSolver extends IkIterativeSolver { + + private final Effector endEffector; + private final CCDJoint[] joints; + private final Vector3f upAxis; + private Vector3f goal; + + private Vector3f lastEndEffectorWorldPosition = null; + + public CCDIKSolver(int maxIterations, float allowedMarginOfError, Vector3f goal, Effector endEffector, Vector3f upAxis, CCDJoint[] joints) { + super(maxIterations, allowedMarginOfError); + this.goal = goal; + this.endEffector = endEffector; + this.upAxis = upAxis; + this.joints = joints; + } + + @Override + protected boolean isInAllowedMarginOfError() { + return evaluateMarginOfError() <= allowedMarginOfError*allowedMarginOfError; + } + + protected float evaluateMarginOfError() { + if (lastEndEffectorWorldPosition == null) + lastEndEffectorWorldPosition = endEffector.getWorldPosition(); + return lastEndEffectorWorldPosition.duplicate().sub(goal).lengthSquared(); + } + + @Override + protected void iteration() { + for (int jointIndex = getEndJointIndex(); jointIndex >= 0; jointIndex--) { + CCDJoint joint = joints[jointIndex]; + Quaternion fromToQuaternion = getRotationBetweenEndEffectorAndGoalUsingWorldSpace(jointIndex); + joint.rotation.multiply(fromToQuaternion); + joint.constrain(); + joint.limit(); + } + } + + private Quaternion getRotationBetweenEndEffectorAndGoalUsingWorldSpace(int joinIndex) { + Vector3f ei = lastEndEffectorWorldPosition==null ? endEffector.getWorldPosition() : lastEndEffectorWorldPosition; + Vector3f et = goal; + lastEndEffectorWorldPosition = null; + + Vector3f bonePos = getBonePos(joinIndex); + Quaternion invLinkRot = getAllPreviousRotation(joinIndex+1); + invLinkRot.inverse(); + + Vector3f localEi = convertToBoneLocalSpace(ei, bonePos, invLinkRot); + localEi.normalizeSafely(); + Vector3f localEt = convertToBoneLocalSpace(et, bonePos, invLinkRot); + localEt.normalizeSafely(); + if (localEt.lengthSquared()==0 || localEi.lengthSquared()==0) + return ComponentsQuaternion.zero(); + + return ComponentsQuaternion.fromTwoVectors(localEi, localEt); + } + + private static Vector3f convertToBoneLocalSpace(Vector3f worldPosition, Vector3f bonePos, Quaternion invLinkRot) { + Vector3f localEi = worldPosition.duplicate().sub(bonePos); + invLinkRot.transform(localEi); + return localEi; + } + + private Vector3f getBonePos(int index) { + Quaternion cumulatedRotation = ComponentsQuaternion.zero(); + Vector3f worldPos = new SimpleVector3f(); + for (int i = 0; i < index; i++) { + CCDJoint joint = joints[i]; + Quaternion rotation = joint.rotation; + float length = joint.getLength(); + + //rotation.multiply(joint.baseRot); + cumulatedRotation.multiply(rotation); + Vector3f localTranslation = upAxis.duplicate(); + cumulatedRotation.transform(localTranslation); + localTranslation.mul(length); + worldPos.add(localTranslation); + } + return worldPos; + } + + public void setGoal(Vector3f goal) { + this.goal = goal; + } + + private Quaternion getAllPreviousRotation(int index) { + Quaternion localSpaceRotation = ComponentsQuaternion.zero(); + for (int i = 0; i < index; i++) { + Quaternion linkInverseRot = joints[i].rotation.duplicate(); + Quaternion baseRotInv = joints[i].baseRot; + //linkInverseRot.multiply(baseRotInv); + localSpaceRotation.multiply(linkInverseRot); + } + return localSpaceRotation; + } + + private int getEndJointIndex() { + return joints.length-1; + } + + public Vector3f getGoal() { + return goal; + } +} diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/CCDJoint.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/CCDJoint.java new file mode 100644 index 0000000..a94780e --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/CCDJoint.java @@ -0,0 +1,41 @@ +package fr.radi3nt.ik.solvers.ccdik; + +import fr.radi3nt.ik.solvers.ccdik.constraint.JointConstraint; +import fr.radi3nt.ik.solvers.ccdik.limit.JointLimit; +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; + +public class CCDJoint { + + private final float length; + public Quaternion baseRot = ComponentsQuaternion.zero(); + public Quaternion rotation = ComponentsQuaternion.zero(); + + private final JointConstraint jointConstraint; + private final JointLimit jointLimit; + + public CCDJoint(float length, JointConstraint jointConstraint, JointLimit jointLimit) { + this.length = length; + this.jointConstraint = jointConstraint; + this.jointLimit = jointLimit; + } + + public CCDJoint(float length, JointConstraint jointConstraint, JointLimit jointLimit, Quaternion baseRot) { + this.length = length; + this.jointConstraint = jointConstraint; + this.jointLimit = jointLimit; + this.baseRot = baseRot.duplicate(); + } + + public void constrain() { + jointConstraint.constrain(this.rotation); + } + + public void limit() { + jointLimit.limit(this.rotation); + } + + public float getLength() { + return length; + } +} diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/constraint/HingeJointConstraint.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/constraint/HingeJointConstraint.java new file mode 100644 index 0000000..0b951a6 --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/constraint/HingeJointConstraint.java @@ -0,0 +1,31 @@ +package fr.radi3nt.ik.solvers.ccdik.constraint; + +import fr.radi3nt.ik.solvers.ccdik.CCDJoint; +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; + +public class HingeJointConstraint implements JointConstraint { + + private final Vector3f constrainedAxis; + + public HingeJointConstraint(Vector3f constrainedAxis) { + this.constrainedAxis = constrainedAxis; + } + + @Override + public void constrain(Quaternion rotation) { + Quaternion invRot = rotation.duplicate(); + invRot.inverse(); + + Vector3f parentAxis = constrainedAxis.duplicate(); + invRot.transform(parentAxis); + + Quaternion quaternion = ComponentsQuaternion.fromTwoVectors(constrainedAxis, parentAxis); + rotation.multiply(quaternion); + } + + public Vector3f getConstrainedAxis() { + return constrainedAxis; + } +} diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/constraint/JointConstraint.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/constraint/JointConstraint.java new file mode 100644 index 0000000..630e114 --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/constraint/JointConstraint.java @@ -0,0 +1,7 @@ +package fr.radi3nt.ik.solvers.ccdik.constraint; + +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; + +public interface JointConstraint { + void constrain(Quaternion rotation); +} diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/constraint/NoJointConstraint.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/constraint/NoJointConstraint.java new file mode 100644 index 0000000..fe3ede3 --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/constraint/NoJointConstraint.java @@ -0,0 +1,17 @@ +package fr.radi3nt.ik.solvers.ccdik.constraint; + +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; + +public class NoJointConstraint implements JointConstraint { + + public static final NoJointConstraint INSTANCE = new NoJointConstraint(); + + private NoJointConstraint() { + + } + + @Override + public void constrain(Quaternion rotation) { + + } +} diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/effectors/Effector.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/effectors/Effector.java new file mode 100644 index 0000000..759e6ba --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/effectors/Effector.java @@ -0,0 +1,9 @@ +package fr.radi3nt.ik.solvers.ccdik.effectors; + +import fr.radi3nt.maths.components.vectors.Vector3f; + +public interface Effector { + + Vector3f getWorldPosition(); + +} diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/effectors/JointsEffector.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/effectors/JointsEffector.java new file mode 100644 index 0000000..ad3b632 --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/effectors/JointsEffector.java @@ -0,0 +1,43 @@ +package fr.radi3nt.ik.solvers.ccdik.effectors; + +import fr.radi3nt.ik.solvers.ccdik.CCDJoint; +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; + +public class JointsEffector implements Effector { + + private final Vector3f startPos; + private final Vector3f upAxis; + + private final CCDJoint[] joints; + + public JointsEffector(Vector3f startPos, Vector3f upAxis, CCDJoint... joints) { + this.startPos = startPos; + this.joints = joints; + this.upAxis = upAxis; + } + + @Override + public Vector3f getWorldPosition() { + return getBonePos().add(startPos); + } + + private Vector3f getBonePos() { + Quaternion cumulatedRotation = ComponentsQuaternion.zero(); + Vector3f worldPos = new SimpleVector3f(); + for (CCDJoint joint : joints) { + Quaternion rotation = joint.rotation; + float length = joint.getLength(); + + cumulatedRotation.multiply(rotation); + Vector3f localTranslation = upAxis.duplicate(); + cumulatedRotation.transform(localTranslation); + localTranslation.mul(length); + worldPos.add(localTranslation); + } + return worldPos; + } + +} diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/limit/AngleClampJointLimit.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/limit/AngleClampJointLimit.java new file mode 100644 index 0000000..04cbe19 --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/limit/AngleClampJointLimit.java @@ -0,0 +1,60 @@ +package fr.radi3nt.ik.solvers.ccdik.limit; + +import fr.radi3nt.ik.solvers.ccdik.constraint.HingeJointConstraint; +import fr.radi3nt.maths.Maths; +import fr.radi3nt.maths.components.advanced.matrix.angle.Angle; +import fr.radi3nt.maths.components.advanced.matrix.angle.JavaMathAngle; +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; + +public class AngleClampJointLimit implements JointLimit { + + private final Angle minAngle; + private final Angle maxAngle; + private final Quaternion zeroAngle; + private final Vector3f defaultAxis; + + public AngleClampJointLimit(Angle minAngle, Angle maxAngle, Quaternion zeroAngle, Vector3f defaultAxis) { + this.minAngle = minAngle; + this.maxAngle = maxAngle; + this.zeroAngle = zeroAngle; + this.defaultAxis = defaultAxis; + } + + public AngleClampJointLimit(Angle minAngle, Angle maxAngle, HingeJointConstraint constraint) { + this(minAngle, maxAngle, constraint.getConstrainedAxis()); + } + + public AngleClampJointLimit(Angle minAngle, Angle maxAngle, Vector3f axis) { + this(minAngle, maxAngle, ComponentsQuaternion.fromAxisAndAngle(axis, JavaMathAngle.zero()), axis.duplicate()); + } + + @Override + public void limit(Quaternion rotation) { + + double minAngleRadiant = minAngle.getRadiant(); + double maxAngleRadiant = maxAngle.getRadiant(); + + double max = Math.max(maxAngleRadiant, minAngleRadiant); + double min = Math.min(maxAngleRadiant, minAngleRadiant); + + Quaternion q1 = rotation.duplicate(); + Quaternion q2 = zeroAngle.duplicate(); + q1.inverse(); + q1.multiply(q2); + + double angle = (2 * Math.atan2(q1.getVector().length()+0f, q1.getW())); + + Vector3f axis = rotation.getAxisOrDefault(defaultAxis.duplicate()); + if (axis.dot(defaultAxis)<0) { + angle=-angle; + axis.negate(); + } + + angle = Maths.clamp(angle, min, max); + + Quaternion result = ComponentsQuaternion.fromAxisAndAngle(axis, JavaMathAngle.fromRadiant(angle)); + rotation.copy(result); + } +} diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/limit/BallSocketJointLimit.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/limit/BallSocketJointLimit.java new file mode 100644 index 0000000..a0af327 --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/limit/BallSocketJointLimit.java @@ -0,0 +1,39 @@ +package fr.radi3nt.ik.solvers.ccdik.limit; + +import fr.radi3nt.maths.components.advanced.matrix.angle.Angle; +import fr.radi3nt.maths.components.advanced.matrix.angle.JavaMathAngle; +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; + +public class BallSocketJointLimit implements JointLimit { + + private final Vector3f upAxis; + private final Vector3f constrainingAxis; + private final Angle limit; + + public BallSocketJointLimit(Vector3f upAxis, Vector3f constrainingAxis, Angle limit) { + this.upAxis = upAxis; + this.constrainingAxis = constrainingAxis; + this.limit = limit; + } + + @Override + public void limit(Quaternion rotation) { + Quaternion toUpQuaternion = ComponentsQuaternion.fromTwoVectors(constrainingAxis, upAxis); + Quaternion toAxisFromUpQuaternion = toUpQuaternion.duplicate(); + toAxisFromUpQuaternion.inverse(); + + Quaternion rot = rotation.duplicate(); + rot.multiply(toUpQuaternion); + Angle angle = rot.getAngle(); + double rad = Math.min(angle.getRadiant(), limit.getRadiant()); + Angle constrainedAngle = JavaMathAngle.fromRadiant(rad); + + Quaternion newRotation = ComponentsQuaternion.fromAxisAndAngle(rot.getAxisOrDefault(new SimpleVector3f(0, 1, 0)), constrainedAngle); + newRotation.multiply(toAxisFromUpQuaternion); + + rotation.copy(newRotation); + } +} diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/limit/CompositionJointLimit.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/limit/CompositionJointLimit.java new file mode 100644 index 0000000..d72bd8d --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/limit/CompositionJointLimit.java @@ -0,0 +1,20 @@ +package fr.radi3nt.ik.solvers.ccdik.limit; + +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; + +public class CompositionJointLimit implements JointLimit { + + private final JointLimit[] jointLimits; + + public CompositionJointLimit(JointLimit... jointLimits) { + this.jointLimits = jointLimits; + } + + + @Override + public void limit(Quaternion ccdJoint) { + for (JointLimit jointLimit : jointLimits) { + jointLimit.limit(ccdJoint); + } + } +} diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/limit/JointLimit.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/limit/JointLimit.java new file mode 100644 index 0000000..6e9eb11 --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/ccdik/limit/JointLimit.java @@ -0,0 +1,9 @@ +package fr.radi3nt.ik.solvers.ccdik.limit; + +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; + +public interface JointLimit { + + void limit(Quaternion ccdJoint); + +} diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/fabrik/FABRIKSolver.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/fabrik/FABRIKSolver.java new file mode 100644 index 0000000..5939701 --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/fabrik/FABRIKSolver.java @@ -0,0 +1,84 @@ +package fr.radi3nt.ik.solvers.fabrik; + +import fr.radi3nt.ik.solvers.IkIterativeSolver; +import fr.radi3nt.ik.solvers.fabrik.chain.IKChain; +import fr.radi3nt.ik.solvers.fabrik.chain.IkJointTransform; +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; + +public class FABRIKSolver extends IkIterativeSolver { + + private final IKChain chain; + private IkJointTransform goal = new IkJointTransform(new SimpleVector3f(), ComponentsQuaternion.zero()); + private IkJointTransform start = new IkJointTransform(new SimpleVector3f(), ComponentsQuaternion.zero()); + + public FABRIKSolver(int maxIterations, float allowedMarginOfError, IKChain chain) { + super(maxIterations, allowedMarginOfError); + this.chain = chain; + } + + @Override + public void solve() { + Vector3f dirToGoal = goal.getResultPosition().duplicate().sub(start.getResultPosition()); + float chainLength = chain.getTotalLength(); + if (dirToGoal.lengthSquared()>chainLength*chainLength) + extendTowardsGoal(); + else + super.solve(); + } + + private void extendTowardsGoal() { + chain.setEndPointAndReturnCalculatedStart(goal, 0); + IkJointTransform lastTransform = chain.setStartPointAndReturnCalculatedEnd(start, 0); + for (int i = 1; i < chain.getLinkAmount(); i++) { + chain.setEndPointAndReturnCalculatedStart(goal, i); + lastTransform = chain.setStartPointAndReturnCalculatedEnd(lastTransform, i); + } + } + + @Override + protected void iteration() { + backwardsPass(); + forwardPass(); + } + + private void backwardsPass() { + IkJointTransform lastTransform = goal; + for (int i = getEndLinkIndex(); i >= 0; i--) { + lastTransform = chain.setEndPointAndReturnCalculatedStart(lastTransform, i); + } + } + + private void forwardPass() { + IkJointTransform lastTransform = start; + for (int i = 0; i < chain.getLinkAmount(); i++) { + lastTransform = chain.setStartPointAndReturnCalculatedEnd(lastTransform, i); + } + } + + @Override + protected boolean isInAllowedMarginOfError() { + return evaluateMarginOfError() <= allowedMarginOfError*allowedMarginOfError; + } + + protected float evaluateMarginOfError() { + return getTipJointPosition().duplicate().sub(goal.getResultPosition()).lengthSquared(); + } + + private Vector3f getTipJointPosition() { + return chain.getEndPoint(getEndLinkIndex()).getResultPosition(); + } + + private int getEndLinkIndex() { + return chain.getLinkAmount() - 1; + } + + public void setStart(IkJointTransform start) { + this.start = start; + } + + public void setGoal(IkJointTransform goal) { + this.goal = goal; + } +} diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/fabrik/chain/IKChain.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/fabrik/chain/IKChain.java new file mode 100644 index 0000000..9369a4b --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/fabrik/chain/IKChain.java @@ -0,0 +1,12 @@ +package fr.radi3nt.ik.solvers.fabrik.chain; + +public interface IKChain { + + IkJointTransform setEndPointAndReturnCalculatedStart(IkJointTransform transform, int index); + IkJointTransform setStartPointAndReturnCalculatedEnd(IkJointTransform transform, int index); + IkJointTransform getEndPoint(int index); + IkJointTransform getStartPoint(int index); + float getTotalLength(); + int getLinkAmount(); + +} diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/fabrik/chain/IkJointTransform.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/fabrik/chain/IkJointTransform.java new file mode 100644 index 0000000..b833056 --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/fabrik/chain/IkJointTransform.java @@ -0,0 +1,23 @@ +package fr.radi3nt.ik.solvers.fabrik.chain; + +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; + +public class IkJointTransform { + + private final Vector3f resultPosition; + private final Quaternion resultRotation; + + public IkJointTransform(Vector3f resultPosition, Quaternion resultRotation) { + this.resultPosition = resultPosition; + this.resultRotation = resultRotation; + } + + public Vector3f getResultPosition() { + return resultPosition; + } + + public Quaternion getResultRotation() { + return resultRotation; + } +} diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/fabrik/chain/IkLink.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/fabrik/chain/IkLink.java new file mode 100644 index 0000000..d43a43b --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/fabrik/chain/IkLink.java @@ -0,0 +1,41 @@ +package fr.radi3nt.ik.solvers.fabrik.chain; + +import fr.radi3nt.maths.components.vectors.Vector3f; + +public class IkLink { + + private final float length; + + private IkJointTransform startTransform; + private IkJointTransform endTransform; + + public IkLink(float length, IkJointTransform startTransform, IkJointTransform endTransform) { + this.length = length; + this.startTransform = startTransform; + this.endTransform = endTransform; + } + + public float getLength() { + return length; + } + + public IkJointTransform getStartTransform() { + return startTransform; + } + + public void setStartTransform(IkJointTransform startTransform) { + this.startTransform = startTransform; + } + + public IkJointTransform getEndTransform() { + return endTransform; + } + + public void setEndTransform(IkJointTransform endTransform) { + this.endTransform = endTransform; + } + + public Vector3f getDirection() { + return endTransform.getResultPosition().duplicate().sub(startTransform.getResultPosition()).normalize(); + } +} \ No newline at end of file diff --git a/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/fabrik/chain/SimpleIKChain.java b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/fabrik/chain/SimpleIKChain.java new file mode 100644 index 0000000..0cb3f66 --- /dev/null +++ b/InverseKinematics/src/main/java/fr/radi3nt/ik/solvers/fabrik/chain/SimpleIKChain.java @@ -0,0 +1,106 @@ +package fr.radi3nt.ik.solvers.fabrik.chain; + +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; + +public class SimpleIKChain implements IKChain { + + private final IkLink[] ikLinks; + private final float totalLength; + + public SimpleIKChain(IkLink... ikLinks) { + this.ikLinks = ikLinks; + float calculatingTotalLength = 0; + for (IkLink ikLink : ikLinks) { + calculatingTotalLength+= ikLink.getLength(); + } + totalLength = calculatingTotalLength; + } + + @Override + public IkJointTransform setEndPointAndReturnCalculatedStart(IkJointTransform transform, int index) { + IkLink currentLink = ikLinks[index]; + currentLink.setEndTransform(transform); + + calculateStartFromEnd(currentLink, index); + + return currentLink.getStartTransform(); + } + + private void calculateStartFromEnd(IkLink currentLink, int index) { + Vector3f startPos = currentLink.getStartTransform().getResultPosition(); + Vector3f endPos = currentLink.getEndTransform().getResultPosition(); + Vector3f dirEndToStart = startPos.duplicate().sub(endPos); + dirEndToStart.normalize(); + dirEndToStart.mul(currentLink.getLength()); + Quaternion quaternion = ComponentsQuaternion.fromTwoVectors(dirEndToStart, new SimpleVector3f(0, 0, 1)); + + Quaternion localSpaceRotation = ComponentsQuaternion.zero(); + for (int i = 0; i < index; i++) { + Quaternion linkInverseRot = ikLinks[i].getStartTransform().getResultRotation().duplicate(); + localSpaceRotation.multiply(linkInverseRot); + } + localSpaceRotation.inverse(); + + localSpaceRotation.multiply(quaternion); + + currentLink.setStartTransform(new IkJointTransform(endPos.duplicate().sub(dirEndToStart), localSpaceRotation)); + } + + @Override + public IkJointTransform setStartPointAndReturnCalculatedEnd(IkJointTransform transform, int index) { + IkLink currentLink = ikLinks[index]; + currentLink.setStartTransform(transform); + + calculateEndFromStart(currentLink, index); + + return currentLink.getEndTransform(); + } + + private void calculateEndFromStart(IkLink currentLink, int index) { + Vector3f startPos = currentLink.getStartTransform().getResultPosition(); + Vector3f endPos = currentLink.getEndTransform().getResultPosition(); + Vector3f dirStartToEnd = endPos.duplicate().sub(startPos); + dirStartToEnd.normalize(); + + Quaternion quaternion = ComponentsQuaternion.fromTwoVectors(new SimpleVector3f(0, 0, 1), dirStartToEnd); + + Quaternion localSpaceRotation = ComponentsQuaternion.zero(); + for (int i = 0; i < index; i++) { + Quaternion linkInverseRot = ikLinks[i].getStartTransform().getResultRotation().duplicate(); + localSpaceRotation.multiply(linkInverseRot); + } + localSpaceRotation.inverse(); + + localSpaceRotation.multiply(quaternion); + + currentLink.setEndTransform(new IkJointTransform(startPos.duplicate().sub(dirStartToEnd), ComponentsQuaternion.zero())); + currentLink.setStartTransform(new IkJointTransform(currentLink.getStartTransform().getResultPosition(), localSpaceRotation)); + } + + @Override + public IkJointTransform getEndPoint(int index) { + return ikLinks[index].getEndTransform(); + } + + @Override + public IkJointTransform getStartPoint(int index) { + return ikLinks[index].getStartTransform(); + } + + public IkLink[] getIkLinks() { + return ikLinks; + } + + @Override + public float getTotalLength() { + return totalLength; + } + + @Override + public int getLinkAmount() { + return ikLinks.length; + } +} diff --git a/JsonHelper/src/main/java/fr/radi3nt/json/JsonArray.java b/JsonHelper/src/main/java/fr/radi3nt/json/JsonArray.java index 49807d6..ddc7c72 100644 --- a/JsonHelper/src/main/java/fr/radi3nt/json/JsonArray.java +++ b/JsonHelper/src/main/java/fr/radi3nt/json/JsonArray.java @@ -11,7 +11,7 @@ public class JsonArray extends JsonValue implements Iterable { private final List values; public JsonArray() { - this.values = new ArrayList(); + this.values = new ArrayList<>(); } public JsonArray(final JsonArray array) { @@ -25,7 +25,7 @@ private JsonArray(final JsonArray array, final boolean unmodifiable) { if (unmodifiable) { this.values = Collections.unmodifiableList(array.values); } else { - this.values = new ArrayList(array.values); + this.values = new ArrayList<>(array.values); } } diff --git a/JsonHelper/src/main/java/fr/radi3nt/json/JsonObject.java b/JsonHelper/src/main/java/fr/radi3nt/json/JsonObject.java index dce42a8..7c18503 100644 --- a/JsonHelper/src/main/java/fr/radi3nt/json/JsonObject.java +++ b/JsonHelper/src/main/java/fr/radi3nt/json/JsonObject.java @@ -14,8 +14,8 @@ public class JsonObject extends JsonValue implements Iterable private transient HashIndexTable table; public JsonObject() { - this.names = new ArrayList(); - this.values = new ArrayList(); + this.names = new ArrayList<>(); + this.values = new ArrayList<>(); this.table = new HashIndexTable(); } @@ -31,8 +31,8 @@ private JsonObject(final JsonObject object, final boolean unmodifiable) { this.names = Collections.unmodifiableList(object.names); this.values = Collections.unmodifiableList(object.values); } else { - this.names = new ArrayList(object.names); - this.values = new ArrayList(object.values); + this.names = new ArrayList<>(object.names); + this.values = new ArrayList<>(object.values); } this.table = new HashIndexTable(); this.updateHashIndex(); @@ -173,34 +173,6 @@ public JsonObject merge(final JsonObject object) { return this; } - -/* - public JsonObject merge(final JsonObject object) { - if (object == null) { - throw new NullPointerException("object is null"); - } - ArrayList arrayList = new ArrayList<>(); - for (Member member : object) { - arrayList.add(member); - } - Collections.reverse(arrayList); - Member lastJson = new Member("null", new JsonString("null")); - for (Member member : arrayList) { - final int index = this.indexOf(member.getName()); - if (index!=0) { - JsonObject jsonObject= new JsonObject().add(member.getName(), member.getValue()); - jsonObject.add(lastJson.getName(), member.getValue()); - this.set(member.getName(), jsonObject); - } else { - this.add(member.getName(), member.getValue()); - } - lastJson=member; - } - return this; - } - - */ - public JsonValue get(final String name) { if (name == null) { throw new NullPointerException("name is null"); diff --git a/JsonHelper/src/main/java/fr/radi3nt/json/WriterConfig.java b/JsonHelper/src/main/java/fr/radi3nt/json/WriterConfig.java index 2f25478..0866f8a 100644 --- a/JsonHelper/src/main/java/fr/radi3nt/json/WriterConfig.java +++ b/JsonHelper/src/main/java/fr/radi3nt/json/WriterConfig.java @@ -3,18 +3,14 @@ import java.io.Writer; public abstract class WriterConfig { - public static WriterConfig MINIMAL; - public static WriterConfig PRETTY_PRINT; - static { - WriterConfig.MINIMAL = new WriterConfig() { - @Override - JsonWriter createWriter(final Writer writer) { - return new JsonWriter(writer); - } - }; - WriterConfig.PRETTY_PRINT = PrettyPrint.indentWithSpaces(2); - } + public static final WriterConfig MINIMAL = new WriterConfig() { + @Override + JsonWriter createWriter(final Writer writer) { + return new JsonWriter(writer); + } + }; + public static final WriterConfig PRETTY_PRINT = PrettyPrint.indentWithSpaces(2); abstract JsonWriter createWriter(final Writer p0); } diff --git a/LanguageHelper/src/main/java/fr/radi3nt/lang/util/FileUtils_.java b/LanguageHelper/src/main/java/fr/radi3nt/lang/util/FileUtils_.java index 5c14005..19e1376 100644 --- a/LanguageHelper/src/main/java/fr/radi3nt/lang/util/FileUtils_.java +++ b/LanguageHelper/src/main/java/fr/radi3nt/lang/util/FileUtils_.java @@ -5,6 +5,7 @@ import java.io.*; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.stream.Collectors; @@ -37,7 +38,7 @@ public static void saveJson(final File file, final JsonObject jsonObject) { } catch (IOException e) { e.printStackTrace(); } - try (final Writer fw = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) { + try (final Writer fw = new OutputStreamWriter(Files.newOutputStream(file.toPath()), StandardCharsets.UTF_8)) { jsonObject.writeTo(fw, WriterConfig.PRETTY_PRINT); fw.flush(); } catch (IOException e) { @@ -54,7 +55,7 @@ public static void copy(final InputStream source, final String destination) { e.printStackTrace(); } if (source != null) { - try (final OutputStream out = new FileOutputStream(file)) { + try (final OutputStream out = Files.newOutputStream(file.toPath())) { final byte[] buf = new byte[1024]; int len; while ((len = source.read(buf)) > 0) { @@ -88,7 +89,7 @@ public static String convert(final InputStream inputStream) { public static String loadContent(final File file) { if (file.exists()) { - try (final BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) { + try (final BufferedReader reader = new BufferedReader(new InputStreamReader(Files.newInputStream(file.toPath()), StandardCharsets.UTF_8))) { final StringBuilder text = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { @@ -103,11 +104,11 @@ public static String loadContent(final File file) { } public static String loadContent(List stringList) { - String line = ""; + StringBuilder line = new StringBuilder(); for (String s : stringList) { - line+=s; + line.append(s); } - return line; + return line.toString(); } public static String loadResourceContent(final Path path) { diff --git a/LogHelper/src/main/java/fr/radi3nt/loghelper/archiving/LogArchiver.java b/LogHelper/src/main/java/fr/radi3nt/loghelper/archiving/LogArchiver.java index 23a2f69..e0cfa2e 100644 --- a/LogHelper/src/main/java/fr/radi3nt/loghelper/archiving/LogArchiver.java +++ b/LogHelper/src/main/java/fr/radi3nt/loghelper/archiving/LogArchiver.java @@ -2,12 +2,17 @@ import fr.radi3nt.file.files.WritableFile; import fr.radi3nt.loghelper.archiving.formatters.LogFormatter; -import fr.radi3nt.loghelper.logs.logs.Log; import fr.radi3nt.loghelper.logs.actions.LogAction; +import fr.radi3nt.loghelper.logs.logs.Log; -import java.io.*; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; public class LogArchiver { @@ -48,7 +53,7 @@ private void writeLogs(List logs) throws IOException { outputStreamWriter.close(); } - private OutputStreamWriter getWriter() throws FileNotFoundException { + private OutputStreamWriter getWriter() throws IOException { OutputStream outputStream = writableFile.getOutputSteam(); return new OutputStreamWriter(outputStream, StandardCharsets.UTF_8); } diff --git a/LogHelper/src/main/java/fr/radi3nt/loghelper/archiving/LogFilter.java b/LogHelper/src/main/java/fr/radi3nt/loghelper/archiving/LogFilter.java index 0680040..63c7496 100644 --- a/LogHelper/src/main/java/fr/radi3nt/loghelper/archiving/LogFilter.java +++ b/LogHelper/src/main/java/fr/radi3nt/loghelper/archiving/LogFilter.java @@ -3,7 +3,6 @@ import fr.radi3nt.loghelper.logs.actions.LogAction; import java.util.Collection; -import java.util.Iterator; import java.util.function.Predicate; public class LogFilter { diff --git a/LogHelper/src/main/java/fr/radi3nt/loghelper/archiving/formatters/comp/DateFormatterComponent.java b/LogHelper/src/main/java/fr/radi3nt/loghelper/archiving/formatters/comp/DateFormatterComponent.java index 76587df..7d2f13f 100644 --- a/LogHelper/src/main/java/fr/radi3nt/loghelper/archiving/formatters/comp/DateFormatterComponent.java +++ b/LogHelper/src/main/java/fr/radi3nt/loghelper/archiving/formatters/comp/DateFormatterComponent.java @@ -1,6 +1,5 @@ package fr.radi3nt.loghelper.archiving.formatters.comp; -import fr.radi3nt.loghelper.archiving.formatters.LogFormatter; import fr.radi3nt.loghelper.logs.actions.LogAction; import java.time.Instant; diff --git a/LogHelper/src/main/java/fr/radi3nt/loghelper/archiving/formatters/comp/type/TypeFormatterComponent.java b/LogHelper/src/main/java/fr/radi3nt/loghelper/archiving/formatters/comp/type/TypeFormatterComponent.java index 7bd8e62..d4fe2c2 100644 --- a/LogHelper/src/main/java/fr/radi3nt/loghelper/archiving/formatters/comp/type/TypeFormatterComponent.java +++ b/LogHelper/src/main/java/fr/radi3nt/loghelper/archiving/formatters/comp/type/TypeFormatterComponent.java @@ -1,6 +1,5 @@ package fr.radi3nt.loghelper.archiving.formatters.comp.type; -import fr.radi3nt.loghelper.archiving.formatters.LogFormatter; import fr.radi3nt.loghelper.archiving.formatters.comp.FormatComponent; import fr.radi3nt.loghelper.archiving.formatters.comp.type.handler.UnknownTypeFormatHandler; import fr.radi3nt.loghelper.logs.actions.LogAction; diff --git a/LogHelper/src/main/java/fr/radi3nt/loghelper/examples/MainLogExample.java b/LogHelper/src/main/java/fr/radi3nt/loghelper/examples/MainLogExample.java index 61a0c21..7d9640f 100644 --- a/LogHelper/src/main/java/fr/radi3nt/loghelper/examples/MainLogExample.java +++ b/LogHelper/src/main/java/fr/radi3nt/loghelper/examples/MainLogExample.java @@ -9,10 +9,7 @@ import fr.radi3nt.loghelper.archiving.formatters.comp.DateFormatterComponent; import fr.radi3nt.loghelper.archiving.formatters.comp.type.TypeFormatterComponent; import fr.radi3nt.loghelper.archiving.formatters.comp.type.handler.EmptyUnknownTypeFormatHandler; -import fr.radi3nt.loghelper.logs.actions.TemplateLogAction; -import fr.radi3nt.loghelper.logs.actions.type.EnumLogType; import fr.radi3nt.loghelper.logs.actions.type.InfoLogAction; -import fr.radi3nt.loghelper.logs.actions.type.TemplateTypeLogAction; import fr.radi3nt.loghelper.logs.logs.WritableLog; import fr.radi3nt.loghelper.logs.logs.implementations.PrintStreamLog; @@ -20,8 +17,6 @@ import java.io.IOException; import java.nio.file.Paths; import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Collection; public class MainLogExample { diff --git a/LogHelper/src/main/java/fr/radi3nt/loghelper/logs/actions/type/InfoLogAction.java b/LogHelper/src/main/java/fr/radi3nt/loghelper/logs/actions/type/InfoLogAction.java index f43bbc3..8264eac 100644 --- a/LogHelper/src/main/java/fr/radi3nt/loghelper/logs/actions/type/InfoLogAction.java +++ b/LogHelper/src/main/java/fr/radi3nt/loghelper/logs/actions/type/InfoLogAction.java @@ -1,7 +1,5 @@ package fr.radi3nt.loghelper.logs.actions.type; -import fr.radi3nt.loghelper.logs.actions.TemplateLogAction; - public class InfoLogAction extends TemplateTypeLogAction { public InfoLogAction(String log, long timeStamp) { diff --git a/LogHelper/src/main/java/fr/radi3nt/loghelper/logs/logs/implementations/PrintStreamLog.java b/LogHelper/src/main/java/fr/radi3nt/loghelper/logs/logs/implementations/PrintStreamLog.java index eca3b68..8d05831 100644 --- a/LogHelper/src/main/java/fr/radi3nt/loghelper/logs/logs/implementations/PrintStreamLog.java +++ b/LogHelper/src/main/java/fr/radi3nt/loghelper/logs/logs/implementations/PrintStreamLog.java @@ -1,13 +1,12 @@ package fr.radi3nt.loghelper.logs.logs.implementations; import fr.radi3nt.loghelper.archiving.formatters.LogFormatter; -import fr.radi3nt.loghelper.logs.logs.ConsoleLog; import fr.radi3nt.loghelper.logs.actions.LogAction; +import fr.radi3nt.loghelper.logs.logs.ConsoleLog; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.io.PrintStream; import java.nio.charset.StandardCharsets; public class PrintStreamLog extends TemplateLog implements ConsoleLog { diff --git a/LogHelper/src/main/java/fr/radi3nt/loghelper/logs/logs/implementations/TemplateLog.java b/LogHelper/src/main/java/fr/radi3nt/loghelper/logs/logs/implementations/TemplateLog.java index 342d760..0a58162 100644 --- a/LogHelper/src/main/java/fr/radi3nt/loghelper/logs/logs/implementations/TemplateLog.java +++ b/LogHelper/src/main/java/fr/radi3nt/loghelper/logs/logs/implementations/TemplateLog.java @@ -3,7 +3,9 @@ import fr.radi3nt.loghelper.logs.actions.LogAction; import fr.radi3nt.loghelper.logs.logs.WritableLog; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class TemplateLog implements WritableLog { diff --git a/MathsHelper/pom.xml b/MathsHelper/pom.xml index 87d16fd..8ca8d48 100644 --- a/MathsHelper/pom.xml +++ b/MathsHelper/pom.xml @@ -7,6 +7,14 @@ fr.radi3nt 1.0 + + + org.junit.jupiter + junit-jupiter-api + 5.9.3 + test + + 4.0.0 MathsHelper diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/Easing.java b/MathsHelper/src/main/java/fr/radi3nt/maths/Easing.java index 8f85e0c..e9d4305 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/Easing.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/Easing.java @@ -34,227 +34,150 @@ public interface Easing { /** * Simple linear tweening - no easing. */ - Easing LINEAR = new Easing() { - public float ease(float t, float b, float c, float d) { - return c*t/d + b; - } - }; + Easing LINEAR = (t, b, c, d) -> c * t / d + b; ///////////// QUADRATIC EASING: t^2 /////////////////// - - /** + + /** * Quadratic easing in - accelerating from zero velocity. */ - Easing QUAD_IN = new Easing() { - public float ease(float t, float b, float c, float d) { - return c*(t/=d)*t + b; - } - }; + Easing QUAD_IN = (t, b, c, d) -> c * (t /= d) * t + b; - /** + /** * Quadratic easing out - decelerating to zero velocity. */ - Easing QUAD_OUT = new Easing() { - public float ease(float t, float b, float c, float d) { - return -c *(t/=d)*(t-2) + b; - } - }; - + Easing QUAD_OUT = (t, b, c, d) -> -c * (t /= d) * (t - 2) + b; + /** * Quadratic easing in/out - acceleration until halfway, then deceleration */ - Easing QUAD_IN_OUT = new Easing() { - public float ease(float t, float b, float c, float d) { - if ((t/=d/2) < 1) return c/2*t*t + b; - return -c/2 * ((--t)*(t-2) - 1) + b; - } + Easing QUAD_IN_OUT = (t, b, c, d) -> { + if ((t /= d / 2) < 1) return c / 2 * t * t + b; + return -c / 2 * ((--t) * (t - 2) - 1) + b; }; - - + + ///////////// CUBIC EASING: t^3 /////////////////////// - /** + /** * Cubic easing in - accelerating from zero velocity. */ - Easing CUBIC_IN = new Easing() { - public float ease(float t, float b, float c, float d) { - return c*(t/=d)*t*t + b; - } - }; - + Easing CUBIC_IN = (t, b, c, d) -> c * (t /= d) * t * t + b; + /** * Cubic easing out - decelerating to zero velocity. */ - Easing CUBIC_OUT = new Easing() { - public float ease(float t, float b, float c, float d) { - return c*((t=t/d-1)*t*t + 1) + b; - } - }; - - /** + Easing CUBIC_OUT = (t, b, c, d) -> c * ((t = t / d - 1) * t * t + 1) + b; + + /** * Cubic easing in/out - acceleration until halfway, then deceleration. */ - Easing CUBIC_IN_OUT = new Easing() { - public float ease(float t, float b, float c, float d) { - if ((t/=d/2) < 1) return c/2*t*t*t + b; - return c/2*((t-=2)*t*t + 2) + b; - } + Easing CUBIC_IN_OUT = (t, b, c, d) -> { + if ((t /= d / 2) < 1) return c / 2 * t * t * t + b; + return c / 2 * ((t -= 2) * t * t + 2) + b; }; - + ///////////// QUARTIC EASING: t^4 ///////////////////// /** * Quartic easing in - accelerating from zero velocity. */ - Easing QUARTIC_IN = new Easing() { - public float ease(float t, float b, float c, float d) { - return c*(t/=d)*t*t*t + b; - } - }; - - /** + Easing QUARTIC_IN = (t, b, c, d) -> c * (t /= d) * t * t * t + b; + + /** * Quartic easing out - decelerating to zero velocity. */ - Easing QUARTIC_OUT = new Easing() { - public float ease(float t, float b, float c, float d) { - return -c * ((t=t/d-1)*t*t*t - 1) + b; - } - }; + Easing QUARTIC_OUT = (t, b, c, d) -> -c * ((t = t / d - 1) * t * t * t - 1) + b; - /** + /** * Quartic easing in/out - acceleration until halfway, then deceleration. */ - Easing QUARTIC_IN_OUT = new Easing() { - public float ease(float t, float b, float c, float d) { - if ((t/=d/2) < 1) return c/2*t*t*t*t + b; - return -c/2 * ((t-=2)*t*t*t - 2) + b; - } + Easing QUARTIC_IN_OUT = (t, b, c, d) -> { + if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b; + return -c / 2 * ((t -= 2) * t * t * t - 2) + b; }; - + ///////////// QUINTIC EASING: t^5 //////////////////// - /** + /** * Quintic easing in - accelerating from zero velocity. */ - Easing QUINTIC_IN = new Easing() { - public float ease(float t, float b, float c, float d) { - return c*(t/=d)*t*t*t*t + b; - } - }; + Easing QUINTIC_IN = (t, b, c, d) -> c * (t /= d) * t * t * t * t + b; - /** + /** * Quintic easing out - decelerating to zero velocity. */ - Easing QUINTIC_OUT = new Easing() { - public float ease(float t, float b, float c, float d) { - return c*((t=t/d-1)*t*t*t*t + 1) + b; - } - }; - - /** + Easing QUINTIC_OUT = (t, b, c, d) -> c * ((t = t / d - 1) * t * t * t * t + 1) + b; + + /** * Quintic easing in/out - acceleration until halfway, then deceleration. */ - Easing QUINTIC_IN_OUT = new Easing() { - public float ease(float t, float b, float c, float d) { - if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; - return c/2*((t-=2)*t*t*t*t + 2) + b; - } + Easing QUINTIC_IN_OUT = (t, b, c, d) -> { + if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b; + return c / 2 * ((t -= 2) * t * t * t * t + 2) + b; }; - - - + + ///////////// SINUSOIDAL EASING: sin(t) /////////////// - /** + /** * Sinusoidal easing in - accelerating from zero velocity. */ - Easing SINE_IN = new Easing() { - public float ease(float t, float b, float c, float d) { - return -c * (float)FastTrig.cos(t/d * (Math.PI/2)) + c + b; - } - }; + Easing SINE_IN = (t, b, c, d) -> -c * (float) FastTrig.cos(t / d * (Math.PI / 2)) + c + b; - /** + /** * Sinusoidal easing out - decelerating to zero velocity. */ - Easing SINE_OUT = new Easing() { - public float ease(float t, float b, float c, float d) { - return c * (float)FastTrig.sin(t/d * (Math.PI/2)) + b; - } - }; - - /** + Easing SINE_OUT = (t, b, c, d) -> c * (float) FastTrig.sin(t / d * (Math.PI / 2)) + b; + + /** * Sinusoidal easing in/out - accelerating until halfway, then decelerating. */ - Easing SINE_IN_OUT = new Easing() { - public float ease(float t, float b, float c, float d) { - return -c/2 * ((float)FastTrig.cos(Math.PI*t/d) - 1) + b; - } - }; - - ///////////// EXPONENTIAL EASING: 2^t ///////////////// + Easing SINE_IN_OUT = (t, b, c, d) -> -c / 2 * ((float) FastTrig.cos(Math.PI * t / d) - 1) + b; + + ///////////// EXPONENTIAL EASING: 2^t ///////////////// - /** + /** * Exponential easing in - accelerating from zero velocity. */ - Easing EXPO_IN = new Easing() { - public float ease(float t, float b, float c, float d) { - return (t==0) ? b : c * (float)Math.pow(2, 10 * (t/d - 1)) + b; - } - }; + Easing EXPO_IN = (t, b, c, d) -> (t == 0) ? b : c * (float) Math.pow(2, 10 * (t / d - 1)) + b; - /** + /** * Exponential easing out - decelerating to zero velocity. */ - Easing EXPO_OUT = new Easing() { - public float ease(float t, float b, float c, float d) { - return (t==d) ? b+c : c * (-(float)Math.pow(2, -10 * t/d) + 1) + b; - } - }; - - /** + Easing EXPO_OUT = (t, b, c, d) -> (t == d) ? b + c : c * (-(float) Math.pow(2, -10 * t / d) + 1) + b; + + /** * Exponential easing in/out - accelerating until halfway, then decelerating. */ - Easing EXPO_IN_OUT = new Easing() { - public float ease(float t, float b, float c, float d) { - if (t==0) return b; - if (t==d) return b+c; - if ((t/=d/2) < 1) return c/2 * (float)Math.pow(2, 10 * (t - 1)) + b; - return c/2 * (-(float)Math.pow(2, -10 * --t) + 2) + b; - } + Easing EXPO_IN_OUT = (t, b, c, d) -> { + if (t == 0) return b; + if (t == d) return b + c; + if ((t /= d / 2) < 1) return c / 2 * (float) Math.pow(2, 10 * (t - 1)) + b; + return c / 2 * (-(float) Math.pow(2, -10 * --t) + 2) + b; }; - - + + /////////// CIRCULAR EASING: sqrt(1-t^2) ////////////// - /** + /** * Circular easing in - accelerating from zero velocity. */ - Easing CIRC_IN = new Easing() { - public float ease(float t, float b, float c, float d) { - return -c * ((float)Math.sqrt(1 - (t/=d)*t) - 1) + b; - } - }; - - /** + Easing CIRC_IN = (t, b, c, d) -> -c * ((float) Math.sqrt(1 - (t /= d) * t) - 1) + b; + + /** * Circular easing out - decelerating to zero velocity. */ - Easing CIRC_OUT = new Easing() { - public float ease(float t, float b, float c, float d) { - return c * (float)Math.sqrt(1 - (t=t/d-1)*t) + b; - } - }; - - /** + Easing CIRC_OUT = (t, b, c, d) -> c * (float) Math.sqrt(1 - (t = t / d - 1) * t) + b; + + /** * Circular easing in/out - acceleration until halfway, then deceleration. */ - Easing CIRC_IN_OUT = new Easing() { - public float ease(float t, float b, float c, float d) { - if ((t/=d/2) < 1) return -c/2 * ((float)Math.sqrt(1 - t*t) - 1) + b; - return c/2 * ((float)Math.sqrt(1 - (t-=2)*t) + 1) + b; - } + Easing CIRC_IN_OUT = (t, b, c, d) -> { + if ((t /= d / 2) < 1) return -c / 2 * ((float) Math.sqrt(1 - t * t) - 1) + b; + return c / 2 * ((float) Math.sqrt(1 - (t -= 2) * t) + 1) + b; }; - + /////////// ELASTIC EASING: exponentially decaying sine wave ////////////// /** @@ -464,34 +387,23 @@ public float ease(float t, float b, float c, float d) { } /////////// BOUNCE EASING: exponentially decaying parabolic bounce ////////////// - - /** Bounce easing in. */ - Easing BOUNCE_IN = new Easing() { - public float ease(float t, float b, float c, float d) { - return c - Easing.BOUNCE_OUT.ease(d-t, 0, c, d) + b; - } - }; - /** Bounce easing out. */ - Easing BOUNCE_OUT = new Easing() { - public float ease(float t, float b, float c, float d) { - if ((t/=d) < (1/2.75f)) { - return c*(7.5625f*t*t) + b; - } else if (t < (2/2.75f)) { - return c*(7.5625f*(t-=(1.5f/2.75f))*t + .75f) + b; - } else if (t < (2.5f/2.75f)) { - return c*(7.5625f*(t-=(2.25f/2.75f))*t + .9375f) + b; - } else { - return c*(7.5625f*(t-=(2.625f/2.75f))*t + .984375f) + b; - } + Easing BOUNCE_OUT = (t, b, c, d) -> { + if ((t /= d) < (1 / 2.75f)) { + return c * (7.5625f * t * t) + b; + } else if (t < (2 / 2.75f)) { + return c * (7.5625f * (t -= (1.5f / 2.75f)) * t + .75f) + b; + } else if (t < (2.5f / 2.75f)) { + return c * (7.5625f * (t -= (2.25f / 2.75f)) * t + .9375f) + b; + } else { + return c * (7.5625f * (t -= (2.625f / 2.75f)) * t + .984375f) + b; } }; - + /** Bounce easing in. */ + Easing BOUNCE_IN = (t, b, c, d) -> c - Easing.BOUNCE_OUT.ease(d - t, 0, c, d) + b; /** Bounce easing in/out. */ - Easing BOUNCE_IN_OUT = new Easing() { - public float ease(float t, float b, float c, float d) { - if (t < d/2) return Easing.BOUNCE_IN.ease(t*2, 0, c, d) * .5f + b; - return Easing.BOUNCE_OUT.ease(t*2-d, 0, c, d) * .5f + c*.5f + b; - } + Easing BOUNCE_IN_OUT = (t, b, c, d) -> { + if (t < d / 2) return Easing.BOUNCE_IN.ease(t * 2, 0, c, d) * .5f + b; + return Easing.BOUNCE_OUT.ease(t * 2 - d, 0, c, d) * .5f + c * .5f + b; }; } \ No newline at end of file diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/Maths.java b/MathsHelper/src/main/java/fr/radi3nt/maths/Maths.java index d9b16f0..e2004be 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/Maths.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/Maths.java @@ -1,7 +1,10 @@ package fr.radi3nt.maths; +import fr.radi3nt.maths.components.advanced.matrix.ArrayMatrix3x3; +import fr.radi3nt.maths.components.advanced.matrix.Matrix3x3; import fr.radi3nt.maths.components.vectors.Vector3f; +import java.util.Arrays; import java.util.Collection; public final class Maths { @@ -16,12 +19,16 @@ public static long clamp(long value, long min, long max) { return Math.max(Math.min(value, max), min); } + public static int clamp(int value, int min, int max) { + return Math.max(Math.min(value, max), min); + } + public static double clamp(double value, double min, double max) { return Math.max(Math.min(value, max), min); } - public static double exactDistance(Vector3f vec1, Vector3f vec2) { - return Math.abs(Math.sqrt(Math.pow(vec1.getX()-vec2.getX(), 2) + Math.pow(vec1.getY() - vec2.getY(), 2) + Math.pow(vec1.getZ() - vec2.getZ(), 2))); + public static float fastAbs(float v) { + return Float.intBitsToFloat(0x7fffffff & Float.floatToRawIntBits(v)); } public static byte[] floatToByteArray(float[] value) { @@ -89,4 +96,66 @@ public static byte[] intToByteArray(Collection value) { } return bytes; } + + public static float lerp(float a, float b, float f) { + return a + f * (b - a); + } + + public static float fma(float a, float b, float c) { + return a * b + c; + } + + public static Matrix3x3 skew(Vector3f vec) { + Matrix3x3 matrix3x3 = new ArrayMatrix3x3(); + matrix3x3.set(0, 0, 0); + matrix3x3.set(1, 0, -vec.getZ()); + matrix3x3.set(2, 0, vec.getY()); + + matrix3x3.set(0, 1, vec.getZ()); + matrix3x3.set(1, 1, 0); + matrix3x3.set(2, 1, -vec.getX()); + + matrix3x3.set(0, 2, -vec.getY()); + matrix3x3.set(1, 2, vec.getX()); + matrix3x3.set(2, 2, 0); + return matrix3x3; + } + + public static double[] nCopies(int amount, double value) { + double[] array = new double[amount]; + Arrays.fill(array, value); + return array; + } + + public static double[][] nCopies(int amount, double[] values) { + double[][] array = new double[amount][]; + Arrays.fill(array, values); + return array; + } + + public static boolean inRange(float v1, float v2, float c) { + return fastAbs(v1-v2) <= c; + } + + public static double max(double a, double b, double c) { + double maximum = a; + + if (maximum < b) + maximum = b; + + if (maximum < c) + maximum = c; + return maximum; + } + + public static double min(double a, double b, double c) { + double minimum = a; + + if (minimum > b) + minimum = b; + + if (minimum > c) + minimum = c; + return minimum; + } } diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/colour/colors/BasicColour.java b/MathsHelper/src/main/java/fr/radi3nt/maths/colour/colors/BasicColour.java index 0760a95..08d5067 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/colour/colors/BasicColour.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/colour/colors/BasicColour.java @@ -1,11 +1,15 @@ package fr.radi3nt.maths.colour.colors; import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; import java.util.Objects; public class BasicColour implements Colour { + public static final BasicColour WHITE = new BasicColour(1f, 1f, 1f); + public static final BasicColour BLACK = new BasicColour(0f, 0f, 0f); + private final float red; private final float green; private final float blue; @@ -20,12 +24,26 @@ public BasicColour(float red, float green, float blue) { this.blue = blue; } + public BasicColour(float red, float green, float blue, float intensity) { + this.red = red*intensity; + this.green = green*intensity; + this.blue = blue*intensity; + } + + public BasicColour(float color) { + this(color, color, color); + } + public BasicColour(int red, int green, int blue) { this.red = red/255f; this.green = green/255f; this.blue = blue/255f; } + public BasicColour(Colour color, float intensity) { + this(color.getRed(), color.getGreen(), color.getBlue(), intensity); + } + @Override public float getRed() { return red; @@ -67,4 +85,8 @@ public int hashCode() { public Colour duplicate() { return new BasicColour(getRed(), getGreen(), getBlue()); } + + public Vector3f toVector() { + return new SimpleVector3f(red, green, blue); + } } diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/colour/colors/ColorData.java b/MathsHelper/src/main/java/fr/radi3nt/maths/colour/colors/ColorData.java index 1da21d1..a5d2d5f 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/colour/colors/ColorData.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/colour/colors/ColorData.java @@ -1,8 +1,5 @@ package fr.radi3nt.maths.colour.colors; -import fr.radi3nt.maths.colour.colors.Colour; -import fr.radi3nt.maths.colour.colors.Colour; - public class ColorData { private final Colour colour; @@ -24,4 +21,12 @@ public Colour getColour() { public float getAlpha() { return alpha; } + + @Override + public String toString() { + return "ColorData{" + + "colour=" + colour + + ", alpha=" + alpha + + '}'; + } } diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/colour/util/BufferColorUtil.java b/MathsHelper/src/main/java/fr/radi3nt/maths/colour/util/BufferColorUtil.java deleted file mode 100644 index dded21d..0000000 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/colour/util/BufferColorUtil.java +++ /dev/null @@ -1,29 +0,0 @@ -package fr.radi3nt.maths.colour.util; - -import java.nio.ByteBuffer; - -public class BufferColorUtil { - - public static void encode(ByteBuffer buffer, int byteOffset, float red, float green, float blue, float alpha) { - byteOffset = storeRGB(buffer, byteOffset, red, green, blue); - storeColor(buffer, byteOffset, alpha); - } - - public static int storeRGB(ByteBuffer buffer, int byteOffset, float red, float green, float blue) { - storeColor(buffer, byteOffset, red); - byteOffset += Byte.BYTES; - - storeColor(buffer, byteOffset, green); - byteOffset += Byte.BYTES; - - storeColor(buffer, byteOffset, blue); - byteOffset += Byte.BYTES; - - return byteOffset; - } - - public static void storeColor(ByteBuffer buffer, int byteOffset, float color) { - buffer.put(byteOffset, (byte) (color * Byte.MAX_VALUE * 2 - Byte.MAX_VALUE)); - } - -} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/colour/util/ColorUtil.java b/MathsHelper/src/main/java/fr/radi3nt/maths/colour/util/ColorUtil.java new file mode 100644 index 0000000..a0b7c2b --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/colour/util/ColorUtil.java @@ -0,0 +1,54 @@ +package fr.radi3nt.maths.colour.util; + +import fr.radi3nt.maths.colour.colors.BasicColour; +import fr.radi3nt.maths.colour.colors.Colour; + +import java.nio.ByteBuffer; + +public final class ColorUtil { + + public static Colour hex2Rgb(String colorStr) { + return new BasicColour( + Integer.valueOf( colorStr.substring( 1, 3 ), 16 ), + Integer.valueOf( colorStr.substring( 3, 5 ), 16 ), + Integer.valueOf( colorStr.substring( 5, 7 ), 16 ) ); + } + + public static void encode(ByteBuffer buffer, float red, float green, float blue, float alpha) { + storeRGB(buffer, red, green, blue); + storeQuantizedFloat(buffer, alpha); + } + + public static void encode(ByteBuffer buffer, int byteOffset, float red, float green, float blue, float alpha) { + byteOffset = storeRGB(buffer, byteOffset, red, green, blue); + storeQuantizedFloat(buffer, byteOffset, alpha); + } + + public static int storeRGB(ByteBuffer buffer, int byteOffset, float red, float green, float blue) { + storeQuantizedFloat(buffer, byteOffset, red); + byteOffset += Byte.BYTES; + + storeQuantizedFloat(buffer, byteOffset, green); + byteOffset += Byte.BYTES; + + storeQuantizedFloat(buffer, byteOffset, blue); + byteOffset += Byte.BYTES; + + return byteOffset; + } + + public static void storeRGB(ByteBuffer buffer, float red, float green, float blue) { + storeQuantizedFloat(buffer, red); + storeQuantizedFloat(buffer, green); + storeQuantizedFloat(buffer, blue); + } + + public static void storeQuantizedFloat(ByteBuffer buffer, float color) { + buffer.put((byte) (color * Byte.MAX_VALUE * 2 - Byte.MAX_VALUE)); + } + + public static void storeQuantizedFloat(ByteBuffer buffer, int byteOffset, float color) { + buffer.put(byteOffset, (byte) (color * Byte.MAX_VALUE * 2 - Byte.MAX_VALUE)); + } + +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/MatrixNxNd.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/MatrixNxNd.java new file mode 100644 index 0000000..5ed1fcf --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/MatrixNxNd.java @@ -0,0 +1,29 @@ +package fr.radi3nt.maths.components; + +import fr.radi3nt.maths.components.arbitrary.VectorNd; + +import java.util.BitSet; + +public interface MatrixNxNd { + + double get(int x, int y); + + void set(int x, int y, double value); + + int getWidth(); + + int getHeight(); + + BitSet nonZero(); + + MatrixNxNd multiply(MatrixNxNd matrixNxN); + + MatrixNxNd multiplyWithTransposed(MatrixNxNd matrixNxN); + + MatrixNxNd multiplyTransposedOther(MatrixNxNd matrixNxN); + + VectorNd transform(VectorNd vec); + + MatrixNxNd duplicate(); + +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/Quaternion.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/Quaternion.java deleted file mode 100644 index 7f8819c..0000000 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/Quaternion.java +++ /dev/null @@ -1,146 +0,0 @@ -package fr.radi3nt.maths.components; - - -import fr.radi3nt.maths.components.vectors.Vector3f; -import fr.radi3nt.maths.components.vectors.creator.VectorFactory; - -public class Quaternion { - - private float x; - private float y; - private float z; - private float w; - - public Quaternion(float x, float y, float z, float w){ - this.setX(x); - this.setY(y); - this.setZ(z); - this.setW(w); - } - - public Quaternion(Vector3f v, float w){ - this.setX(v.getX()); - this.setY(v.getY()); - this.setZ(v.getZ()); - this.setW(w); - } - - public float length() - { - return (float) Math.sqrt(x*x + y*y + z*z + w*w); - } - - public Quaternion normalize() - { - float length = length(); - - x /= length; - y /= length; - z /= length; - w /= length; - - return this; - } - - public Quaternion conjugate() - { - return new Quaternion (-x, -y, -z, w); - } - - public Quaternion mul(Quaternion r) - { - float w_ = w * r.getW() - x * r.getX() - y * r.getY() - z * r.getZ(); - float x_ = x * r.getW() + w * r.getX() + y * r.getZ() - z * r.getY(); - float y_ = y * r.getW() + w * r.getY() + z * r.getX() - x * r.getZ(); - float z_ = z * r.getW() + w * r.getZ() + x * r.getY() - y * r.getX(); - - return new Quaternion(x_, y_, z_, w_); - } - - public Quaternion mul(Vector3f r) - { - float w_ = -x * r.getX() - y * r.getY() - z * r.getZ(); - float x_ = w * r.getX() + y * r.getZ() - z * r.getY(); - float y_ = w * r.getY() + z * r.getX() - x * r.getZ(); - float z_ = w * r.getZ() + x * r.getY() - y * r.getX(); - - return new Quaternion(x_, y_, z_, w_); - } - - public Quaternion div(float r) - { - float w_ = w/r; - float x_ = x/r; - float y_ = y/r; - float z_ = z/r; - return new Quaternion(x_, y_, z_, w_); - } - - public Quaternion mul(float r) - { - float w_ = w*r; - float x_ = x*r; - float y_ = y*r; - float z_ = z*r; - return new Quaternion(x_, y_, z_, w_); - } - - public Quaternion sub(Quaternion r) - { - float w_ = w - r.getW(); - float x_ = x - r.getX(); - float y_ = y - r.getY(); - float z_ = z - r.getZ(); - return new Quaternion(x_, y_, z_, w_); - } - - public Quaternion add(Quaternion r) - { - float w_ = w + r.getW(); - float x_ = x + r.getX(); - float y_ = y + r.getY(); - float z_ = z + r.getZ(); - return new Quaternion(x_, y_, z_, w_); - } - - public Vector3f xyz(){ - return VectorFactory.createVector3f(x,y,z); - } - - public String toString() - { - return "[" + this.x + "," + this.y + "," + this.z + "," + this.w + "]"; - } - - public float getX() { - return x; - } - - public void setX(float x) { - this.x = x; - } - - public float getY() { - return y; - } - - public void setY(float y) { - this.y = y; - } - - public float getZ() { - return z; - } - - public void setZ(float z) { - this.z = z; - } - - public float getW() { - return w; - } - - public void setW(float w) { - this.w = w; - } -} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/Transform.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/Transform.java deleted file mode 100644 index 3bbbdac..0000000 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/Transform.java +++ /dev/null @@ -1,91 +0,0 @@ -package fr.radi3nt.maths.components; - -import fr.radi3nt.maths.components.matrices.Matrix; -import fr.radi3nt.maths.components.matrices.implementation.MatrixCreator; -import fr.radi3nt.maths.components.vectors.Vector3f; -import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; - -public class Transform { - - private Vector3f translation; - private Vector3f rotation; - private Vector3f scaling; - - private Matrix modelMatrix; - private Matrix worldMatrix; - - private boolean changed = false; - - public Transform() - { - setTranslation(new SimpleVector3f(0,0,0)); - setRotation(new SimpleVector3f(0,0,0)); - setScaling(new SimpleVector3f(1,1,1)); - } - - public Matrix getWorldMatrix() - { - checkUpdate(); - return worldMatrix; - } - - public Matrix getModelMatrix() - { - checkUpdate(); - return modelMatrix; - } - - public Vector3f getTranslation() { - changed(); - return translation; - } - - public void setTranslation(Vector3f translation) { - this.translation = translation; - changed(); - } - - public Vector3f getRotation() { - changed(); - return rotation; - } - - public void setRotation(Vector3f rotation) { - this.rotation = rotation; - changed(); - } - - public Vector3f getScaling() { - changed(); - return scaling; - } - - public void setScaling(Vector3f scaling) { - this.scaling = scaling; - changed(); - } - - private void changed() { - changed = true; - } - - private void updateModelMatrix() { - Matrix rotate = MatrixCreator.createMatrix().rotate((float) Math.toRadians(rotation.getX()), new SimpleVector3f(1, 0, 0)).rotate((float) Math.toRadians(rotation.getY()), new SimpleVector3f(0, 1, 0)).rotate((float) Math.toRadians(rotation.getZ()), new SimpleVector3f(0, 0, 1)); - this.modelMatrix = rotate; - } - - private void updateWorldMatrix() { - Matrix translate = MatrixCreator.createMatrix().translation(translation); - Matrix rotate = MatrixCreator.createMatrix().rotate((float) Math.toRadians(rotation.getX()), new SimpleVector3f(1, 0, 0)).rotate((float) Math.toRadians(rotation.getY()), new SimpleVector3f(0, 1, 0)).rotate((float) Math.toRadians(rotation.getZ()), new SimpleVector3f(0, 0, 1)); - Matrix scale = MatrixCreator.createMatrix().scaling(scaling); - worldMatrix = translate.mul(rotate).mul(scale); - } - - private void checkUpdate() { - if (changed) { - updateWorldMatrix(); - updateModelMatrix(); - } - changed = false; - } -} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/Vector2D.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/Vector2D.java index 230596a..e426e9e 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/Vector2D.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/Vector2D.java @@ -1,12 +1,9 @@ package fr.radi3nt.maths.components; -import java.util.LinkedHashMap; import java.util.Map; public class Vector2D implements Cloneable { - private static final long serialVersionUID = 12973187389L; - private static final double epsilon = 0.000001; protected double x; @@ -459,15 +456,6 @@ public boolean equals(Object obj) { return Math.abs(x - other.x) < epsilon && Math.abs(y - other.y) < epsilon && (this.getClass().equals(obj.getClass())); } - public Map serialize() { - Map result = new LinkedHashMap(); - - result.put("x", getX()); - result.put("y", getY()); - - return result; - } - /** * Get a new vector. * diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/Vector3D.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/Vector3D.java index 9ed411b..f305736 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/Vector3D.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/Vector3D.java @@ -1,5 +1,9 @@ package fr.radi3nt.maths.components; +import fr.radi3nt.maths.components.vectors.Vector3f; + +import static fr.radi3nt.maths.Maths.EPSILON; + public class Vector3D implements Cloneable { protected double x; @@ -30,6 +34,12 @@ public Vector3D(float x, float y, float z) { this.z = z; } + public Vector3D(Vector3f vector) { + this.x = vector.getX(); + this.y = vector.getY(); + this.z = vector.getZ(); + } + public Vector3D add(Vector3D vec) { this.x += vec.x; this.y += vec.y; @@ -72,6 +82,13 @@ public Vector3D divide(double div) { return this; } + public Vector3D copy(double x, double y, double z) { + this.setX(x); + this.setY(y); + this.setZ(z); + return this; + } + public Vector3D copy(Vector3D vec) { this.x = vec.x; this.y = vec.y; @@ -101,16 +118,16 @@ public float angle(Vector3D other) { } public Vector3D midpoint(Vector3D other) { - this.x = (this.x + other.x) / 2.0D; - this.y = (this.y + other.y) / 2.0D; - this.z = (this.z + other.z) / 2.0D; + this.x = (this.x + other.x) / 2; + this.y = (this.y + other.y) / 2; + this.z = (this.z + other.z) / 2; return this; } public Vector3D getMidpoint(Vector3D other) { - double x = (this.x + other.x) / 2.0D; - double y = (this.y + other.y) / 2.0D; - double z = (this.z + other.z) / 2.0D; + double x = (this.x + other.x) / 2; + double y = (this.y + other.y) / 2; + double z = (this.z + other.z) / 2; return new Vector3D(x, y, z); } @@ -156,6 +173,16 @@ public Vector3D getCrossProduct(Vector3D o) { return new Vector3D(x, y, z); } + public Vector3D getCrossProduct(Vector3D o, Vector3D result) { + double x = this.y * o.z - o.y * this.z; + double y = this.z * o.x - o.z * this.x; + double z = this.x * o.y - o.x * this.y; + result.setX(x); + result.setY(y); + result.setZ(z); + return result; + } + public Vector3D normalize() { double length = this.length(); this.x /= length; @@ -262,7 +289,7 @@ public String toString() { } public static double getEpsilon() { - return 1.0E-6D; + return EPSILON; } public static Vector3D getMinimum(Vector3D v1, Vector3D v2) { diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/ArrayMatrix3x3.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/ArrayMatrix3x3.java new file mode 100644 index 0000000..7bac5b4 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/ArrayMatrix3x3.java @@ -0,0 +1,361 @@ +package fr.radi3nt.maths.components.advanced.matrix; + +import fr.radi3nt.maths.components.advanced.matrix.angle.Angle; +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; + +import java.util.Arrays; + +public class ArrayMatrix3x3 implements Matrix3x3 { + + private final float[][] m; + + public ArrayMatrix3x3() { + m = new float[3][3]; + } + + public ArrayMatrix3x3(ArrayMatrix3x3 copy) { + m = new float[3][3]; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + set(i, j, copy.get(i, j)); + } + } + } + + public ArrayMatrix3x3(float[][] m) { + this.m = m; + } + + public ArrayMatrix3x3(Matrix4x4 copy) { + m = new float[3][3]; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + set(i, j, copy.get(i, j)); + } + } + } + + public static ArrayMatrix3x3 newIdentity() { + ArrayMatrix3x3 arrayMatrix4x4 = new ArrayMatrix3x3(); + arrayMatrix4x4.identity(); + return arrayMatrix4x4; + } + + public static ArrayMatrix3x3 from(float[] m) { + float[][] correctM = new float[][] {new float[3], new float[3], new float[3]}; + for (int i = 0; i < m.length; i++) { + correctM[i%3][i/3] = m[i]; + } + return new ArrayMatrix3x3(correctM); + } + + @Override + public void identity() { + zero(); + m[0][0] = 1; + m[1][1] = 1; + m[2][2] = 1; + } + + @Override + public void zero() { + for (float[] floats : m) { + Arrays.fill(floats, 0); + } + } + + @Override + public void transpose() { + float m10 = m[0][1]; + float m20 = m[0][2]; + float m01 = m[1][0]; + float m21 = m[1][2]; + float m02 = m[2][0]; + float m12 = m[2][1]; + + m[1][0] = m10; + m[2][0] = m20; + m[0][1] = m01; + m[2][1] = m21; + m[0][2] = m02; + m[1][2] = m12; + } + + @Override + public void negate() { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + m[i][j] = -m[i][j]; + } + } + } + + @Override + public void invert() { + float det = get(0, 0) * (get(1, 1) * get(2, 2) - get(2, 1) * get(1, 2)) - + get(0, 1) * (get(1, 0) * get(2, 2) - get(1, 2) * get(2, 0)) + + get(0, 2) * (get(1, 0) * get(2, 1) - get(1, 1) * get(2, 0)); + + if (det == 0) + throw new IllegalStateException("Matrix isn't invertible"); + + float invdet = 1.0f / det; + + ArrayMatrix3x3 invM = new ArrayMatrix3x3(); + + invM.set(0, 0, (get(1, 1) * get(2, 2) - get(2, 1) * get(1, 2)) * invdet); + invM.set(0, 1, (get(0, 2) * get(2, 1) - get(0, 1) * get(2, 2)) * invdet); + invM.set(0, 2, (get(0, 1) * get(1, 2) - get(0, 2) * get(1, 1)) * invdet); + invM.set(1, 0, (get(1, 2) * get(2, 0) - get(1, 0) * get(2, 2)) * invdet); + invM.set(1, 1, (get(0, 0) * get(2, 2) - get(0, 2) * get(2, 0)) * invdet); + invM.set(1, 2, (get(1, 0) * get(0, 2) - get(0, 0) * get(1, 2)) * invdet); + invM.set(2, 0, (get(1, 0) * get(2, 1) - get(2, 0) * get(1, 1)) * invdet); + invM.set(2, 1, (get(2, 0) * get(0, 1) - get(0, 0) * get(2, 1)) * invdet); + invM.set(2, 2, (get(0, 0) * get(1, 1) - get(1, 0) * get(0, 1)) * invdet); + + this.copy(invM); + } + + @Override + public float get(int x, int y) { + return m[x][y]; + } + + @Override + public void set(int x, int y, float value) { + m[x][y] = value; + } + + @Override + public Matrix3x3 duplicate() { + return new ArrayMatrix3x3(this); + } + + @Override + public void subtract(Matrix3x3 skewRJ) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + this.set(i, j, this.get(i, j) - skewRJ.get(i, j)); + } + } + } + + @Override + public boolean isZero() { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (get(i, j)!=0) + return false; + } + } + return true; + } + + @Override + public Quaternion getRotation() { + return getCopySignRotation(); + } + + private Quaternion getCopySignRotation() { + float w = (float) (Math.sqrt(Math.max(0, 1 + m[0][0] + m[1][1] + m[2][2])) / 2); + float x = (float) (Math.sqrt(Math.max(0, 1 + m[0][0] - m[1][1] - m[2][2])) / 2); + float y = (float) (Math.sqrt(Math.max(0, 1 - m[0][0] + m[1][1] - m[2][2])) / 2); + float z = (float) (Math.sqrt(Math.max(0, 1 - m[0][0] - m[1][1] + m[2][2])) / 2); + x *= Math.signum(m[2][1] - m[1][2]); + y *= Math.signum(m[0][2] - m[2][0]); + z *= Math.signum(m[1][0] - m[0][1]); + return new ComponentsQuaternion(x, y, z, w); + } + + @Override + public void add(Matrix3x3 other) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + this.m[i][j] += other.get(i, j); + } + } + } + + @Override + public void scale(Vector3f scale) { + m[0][0] = scale.getX(); + m[1][1] = scale.getY(); + m[2][2] = scale.getZ(); + } + + @Override + public void rotationX(Angle angle) { + m[1][1] = angle.cos(); + m[1][2] = angle.sin(); + m[2][1] = -angle.sin(); + m[2][2] = angle.cos(); + } + + @Override + public void rotationY(Angle angle) { + m[0][0] = angle.cos(); + m[2][0] = angle.sin(); + m[0][2] = -angle.sin(); + m[2][2] = angle.cos(); + } + + @Override + public void rotationZ(Angle angle) { + m[0][0] = angle.cos(); + m[0][1] = angle.sin(); + m[1][0] = -angle.sin(); + m[1][1] = angle.cos(); + } + + @Override + public void eulerRotation(Angle x, Angle y, Angle z) { + float cosX = x.cos(); + float sinX = x.sin(); + float cosY = y.cos(); + float sinY = y.sin(); + float cosZ = z.cos(); + float sinZ = z.sin(); + + float cosXsinY = cosX * sinY; + float sinXsinY = sinX * sinY; + + m[0][0] = cosY * cosZ; + m[0][1] = -cosY * sinZ; + m[0][2] = sinY; + m[1][0] = sinXsinY * cosZ + cosX * sinZ; + m[1][1] = -sinXsinY * sinZ + cosX * cosZ; + m[1][2] = -sinX * cosY; + m[2][0] = -cosXsinY * cosZ + sinX * sinZ; + m[2][1] = cosXsinY * sinZ + sinX * cosZ; + m[2][2] = cosX * cosY; + } + + @Override + public void quaternionRotation(Quaternion quaternion) { + float x = quaternion.getX(); + float y = quaternion.getY(); + float z = quaternion.getZ(); + + float rotationValue = quaternion.getW(); + + float xx = x * x; + float xy = x * y; + float xz = x * z; + float xw = x * rotationValue; + + float yy = y * y; + float yz = y * z; + float yw = y * rotationValue; + + float zz = z * z; + float zw = z * rotationValue; + + + m[0][0] = 1 - 2 * (yy + zz); + m[1][0] = 2 * (xy - zw); + m[2][0] = 2 * (xz + yw); + + m[0][1] = 2 * (xy + zw); + m[1][1] = 1 - 2 * (xx + zz); + m[2][1] = 2 * (yz - xw); + + m[0][2] = 2 * (xz - yw); + m[1][2] = 2 * (yz + xw); + m[2][2] = 1 - 2 * (xx + yy); + } + + @Override + public void directionRotation(Vector3f direction, Vector3f up) { + Vector3f x = up.duplicate().cross(direction); + Vector3f y = direction.duplicate().cross(x); + + x.normalize(); + y.normalize(); + + m[0][0] = x.getX(); + m[0][1] = y.getX(); + m[0][2] = direction.getX(); + m[0][3] = 0; + m[1][0] = x.getY(); + m[1][1] = y.getY(); + m[1][2] = direction.getY(); + m[1][3] = 0; + m[2][0] = x.getZ(); + m[2][1] = y.getZ(); + m[2][2] = direction.getZ(); + } + + @Override + public void transform(Vector3f toTransform) { + Vector3f result = new SimpleVector3f(); + + for (int y = 0; y < 3; y++) { + for (int x = 0; x < 3; x++) { + float value = get(x, y) * toTransform.get(x); + result.add(y, value); + } + } + + toTransform.copy(result); + } + + @Override + public void multiply(Matrix matrix) { + Matrix result = new ArrayMatrix3x3(); + for (int x = 0; x < 3; x++) { + for (int y = 0; y < 3; y++) { + result.set(x, y, this.get(0, y) * matrix.get(x, 0) + + this.get(1, y) * matrix.get(x, 1) + + this.get(2, y) * matrix.get(x, 2)); + } + } + + this.copy(result); + } + + @Override + public void copy(Matrix result) { + for (int x = 0; x < 3; x++) { + for (int y = 0; y < 3; y++) { + set(x, y, result.get(x, y)); + } + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ArrayMatrix3x3 that = (ArrayMatrix3x3) o; + return Arrays.deepEquals(m, that.m); + } + + @Override + public int hashCode() { + return Arrays.deepHashCode(m); + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder("ArrayMatrix3x3{" + + "\n"); + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + stringBuilder.append(m[i][j]).append(" "); + } + stringBuilder.append("\n"); + } + + stringBuilder.append("}"); + + return stringBuilder.toString(); + } + + public float[][] getM() { + return m; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/ArrayMatrix4x4.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/ArrayMatrix4x4.java index 07007c0..459a56e 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/ArrayMatrix4x4.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/ArrayMatrix4x4.java @@ -1,7 +1,6 @@ package fr.radi3nt.maths.components.advanced.matrix; import fr.radi3nt.maths.components.advanced.matrix.angle.Angle; -import fr.radi3nt.maths.components.advanced.matrix.angle.JavaMathAngle; import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; import fr.radi3nt.maths.components.vectors.Vector3f; @@ -10,12 +9,22 @@ import java.util.Arrays; +import static java.lang.Math.sqrt; + public class ArrayMatrix4x4 implements Matrix4x4 { - private final float[][] m = new float[4][4]; + private final float[][] m; + + public ArrayMatrix4x4() { + m = new float[4][4]; + } + + public ArrayMatrix4x4(float[][] m) { + this.m = m; + } - public ArrayMatrix4x4(fr.radi3nt.maths.components.matrices.Matrix copy) { - zero(); + public ArrayMatrix4x4(ArrayMatrix4x4 copy) { + m = new float[4][4]; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { set(i, j, copy.get(i, j)); @@ -23,8 +32,12 @@ public ArrayMatrix4x4(fr.radi3nt.maths.components.matrices.Matrix copy) { } } - public ArrayMatrix4x4() { - zero(); + public static ArrayMatrix4x4 from(float[] m) { + float[][] correctM = new float[][] {new float[4], new float[4], new float[4], new float[4]}; + for (int i = 0; i < m.length; i++) { + correctM[i%4][i/4] = m[i]; + } + return new ArrayMatrix4x4(correctM); } public static ArrayMatrix4x4 newIdentity() { @@ -33,13 +46,76 @@ public static ArrayMatrix4x4 newIdentity() { return arrayMatrix4x4; } + public static Matrix4x4 transform(Vector3f translation) { + ArrayMatrix4x4 arrayMatrix4x4 = newIdentity(); + arrayMatrix4x4.translation(translation); + return arrayMatrix4x4; + } + + public static Matrix4x4 transform(Vector3f translation, Quaternion rotation) { + ArrayMatrix4x4 arrayMatrix4x4 = new ArrayMatrix4x4(); + arrayMatrix4x4.quaternionRotation(rotation); + arrayMatrix4x4.translation(translation); + return arrayMatrix4x4; + } + + public static Matrix4x4 transform(Vector3f translation, Quaternion rotation, Vector3f scale) { + ArrayMatrix4x4 result = new ArrayMatrix4x4(); + transform(result, translation, rotation, scale); + return result; + } + + + public static void transform(Matrix4x4 result, Vector3f translation, Quaternion rotation, Vector3f scale) { + result.quaternionRotation(rotation); + result.translation(translation); + + Matrix4x4 scaling = ArrayMatrix4x4.newIdentity(); + scaling.scale(scale); + result.multiply(scaling); + + } + + public static Matrix4x4 transform(Vector3f translation, Vector3f scale) { + ArrayMatrix4x4 result = ArrayMatrix4x4.newIdentity(); + + Matrix4x4 scaling = ArrayMatrix4x4.newIdentity(); + scaling.scale(scale); + result.multiply(scaling); + result.translation(translation); + + + return result; + } + + public static Matrix4x4 fromScale(Vector3f scale) { + ArrayMatrix4x4 arrayMatrix4x4 = new ArrayMatrix4x4(); + arrayMatrix4x4.m[3][3] = 1f; + arrayMatrix4x4.scale(scale); + return arrayMatrix4x4; + } + + public static Matrix4x4 mul(Matrix4x4... matrices) { + Matrix4x4 result = ArrayMatrix4x4.newIdentity(); + for (Matrix4x4 matrix : matrices) { + result.multiply(matrix); + } + return result; + } + + public static Matrix4x4 fromRotation(Quaternion rotation) { + Matrix4x4 result = ArrayMatrix4x4.newIdentity(); + result.quaternionRotation(rotation); + return result; + } + @Override public void identity() { - zero(); - m[0][0] = 1; - m[1][1] = 1; - m[2][2] = 1; - m[3][3] = 1; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + m[i][j] = i==j ? 1 : 0; + } + } } @Override @@ -51,18 +127,40 @@ public void zero() { @Override public void transpose() { - m[1][0] = m[0][1]; - m[2][0] = m[0][2]; - m[3][0] = m[0][3]; - m[0][1] = m[1][0]; - m[2][1] = m[1][2]; - m[3][1] = m[1][3]; - m[0][2] = m[2][0]; - m[1][2] = m[2][1]; - m[3][2] = m[2][3]; - m[0][3] = m[3][0]; - m[1][3] = m[3][1]; - m[2][3] = m[3][2]; + float m10 = m[0][1]; + float m20 = m[0][2]; + float m30 = m[0][3]; + float m01 = m[1][0]; + float m21 = m[1][2]; + float m31 = m[1][3]; + float m02 = m[2][0]; + float m12 = m[2][1]; + float m32 = m[2][3]; + float m03 = m[3][0]; + float m13 = m[3][1]; + float m23 = m[3][2]; + + m[1][0] = m10; + m[2][0] = m20; + m[3][0] = m30; + m[0][1] = m01; + m[2][1] = m21; + m[3][1] = m31; + m[0][2] = m02; + m[1][2] = m12; + m[3][2] = m32; + m[0][3] = m03; + m[1][3] = m13; + m[2][3] = m23; + } + + @Override + public void negate() { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + m[i][j] =-m[i][j]; + } + } } @Override @@ -125,24 +223,77 @@ public void set(int x, int y, float value) { @Override public Matrix4x4 duplicate() { - ArrayMatrix4x4 arrayMatrix = new ArrayMatrix4x4(); - arrayMatrix.copy(this); - return arrayMatrix; + return new ArrayMatrix4x4(this); + } + + @Override + public void scalar(Vector3f size) { + m[0][0]*=size.getX(); + m[0][1]*=size.getX(); + m[0][2]*=size.getX(); + + m[1][0]*=size.getY(); + m[1][1]*=size.getY(); + m[1][2]*=size.getY(); + + m[2][0]*=size.getZ(); + m[2][1]*=size.getZ(); + m[2][2]*=size.getZ(); + } + + @Override + public void scalar(float x, float y, float z) { + m[0][0]*=x; + m[0][1]*=x; + m[0][2]*=x; + + m[1][0]*=y; + m[1][1]*=y; + m[1][2]*=y; + + m[2][0]*=z; + m[2][1]*=z; + m[2][2]*=z; } @Override public Quaternion getRotation() { - return getCopySignRotation(); + return getOtherRotation(); } - private Quaternion getCopySignRotation() { - float w = (float) (Math.sqrt(Math.max(0, 1 + m[0][0] + m[1][1] + m[2][2])) / 2); - float x = (float) (Math.sqrt(Math.max(0, 1 + m[0][0] - m[1][1] - m[2][2])) / 2); - float y = (float) (Math.sqrt(Math.max(0, 1 - m[0][0] + m[1][1] - m[2][2])) / 2); - float z = (float) (Math.sqrt(Math.max(0, 1 - m[0][0] - m[1][1] + m[2][2])) / 2); - x *= Math.signum(m[2][1] - m[1][2]); - y *= Math.signum(m[0][2] - m[2][0]); - z *= Math.signum(m[1][0] - m[0][1]); + private Quaternion getOtherRotation() { + float x; + float y; + float z; + float w; + float trace = m[0][0] + m[1][1] + m[2][2]; // I removed + 1.0f; see discussion with Ethan + if( trace > 0 ) {// I changed M_EPSILON to 0 + float s = (float) (0.5f / sqrt(trace+ 1.0f)); + w = 0.25f / s; + x = (m[1][2] - m[2][1]) * s; + y = (m[2][0] - m[0][2]) * s; + z = (m[0][1] - m[1][0]) * s; + } else { + if ( m[0][0] > m[1][1] && m[0][0] > m[2][2] ) { + float s = (float) (2.0f * sqrt( 1.0f + m[0][0] - m[1][1] - m[2][2])); + w = (m[1][2] - m[2][1]) / s; + x = 0.25f * s; + y = (m[0][1] + m[1][0] ) / s; + z = (m[0][2] + m[2][0] ) / s; + } else if (m[1][1] > m[2][2]) { + float s = (float) (2.0f * sqrt( 1.0f + m[1][1] - m[0][0] - m[2][2])); + w = (m[2][0] - m[0][2]) / s; + x = (m[0][1] + m[1][0] ) / s; + y = 0.25f * s; + z = (m[1][2] + m[2][1] ) / s; + } else { + float s = (float) (2.0f * sqrt( 1.0f + m[2][2] - m[0][0] - m[1][1] )); + w = (m[0][1] - m[1][0]) / s; + x = (m[0][2] + m[2][0] ) / s; + y = (m[1][2] + m[2][1] ) / s; + z = 0.25f * s; + } + } return new ComponentsQuaternion(x, y, z, w); } @@ -167,6 +318,22 @@ public void translation(Vector3f translation) { m[3][2] = translation.getZ(); } + @Override + public void translation(float x, float y, float z) { + m[3][0] = x; + m[3][1] = y; + m[3][2] = z; + } + + @Override + public void add(Matrix4x4 rotation) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + this.m[i][j]+=rotation.get(i, j); + } + } + } + @Override public void scale(Vector3f scale) { m[0][0] = scale.getX(); @@ -174,6 +341,13 @@ public void scale(Vector3f scale) { m[2][2] = scale.getZ(); } + @Override + public void scale(float x, float y, float z) { + m[0][0] = x; + m[1][1] = y; + m[2][2] = z; + } + @Override public void rotationX(Angle angle) { m[1][1] = angle.cos(); @@ -246,15 +420,15 @@ public void quaternionRotation(Quaternion quaternion) { m[0][0] = 1 - 2 * (yy + zz); - m[0][1] = 2 * (xy - zw); - m[0][2] = 2 * (xz + yw); + m[1][0] = 2 * (xy - zw); + m[2][0] = 2 * (xz + yw); - m[1][0] = 2 * (xy + zw); + m[0][1] = 2 * (xy + zw); m[1][1] = 1 - 2 * (xx + zz); - m[1][2] = 2 * (yz - xw); + m[2][1] = 2 * (yz - xw); - m[2][0] = 2 * (xz - yw); - m[2][1] = 2 * (yz + xw); + m[0][2] = 2 * (xz - yw); + m[1][2] = 2 * (yz + xw); m[2][2] = 1 - 2 * (xx + yy); m[0][3] = m[1][3] = m[2][3] = m[3][0] = m[3][1] = m[3][2] = 0; @@ -263,35 +437,66 @@ public void quaternionRotation(Quaternion quaternion) { @Override public void directionRotation(Vector3f direction, Vector3f up) { - Vector3f xaxis = direction.duplicate().cross(up.duplicate()); - if (xaxis.getX() == 0 && xaxis.getY() == 0 && xaxis.getZ() == 0) { - return; - } - xaxis.normalize(); - float phi = (float) Math.acos(direction.dot(up)); - - /* - float u = xaxis.getX(); - float w = xaxis.getY(); - float v = xaxis.getZ(); - - - float rcos = (float) Math.cos(phi); - float rsin = (float) Math.sin(phi); - m[0][0] = rcos + u*u*(1-rcos); - m[0][1] = w * rsin + v*u*(1-rcos); - m[0][2] = -v * rsin + w*u*(1-rcos); - m[1][0] = -w * rsin + u*v*(1-rcos); - m[1][1] = rcos + v*v*(1-rcos); - m[1][2] = u * rsin + w*v*(1-rcos); - m[2][0] = v * rsin + u*w*(1-rcos); - m[2][1] = -u * rsin + v*w*(1-rcos); - m[2][2] = rcos + w*w*(1-rcos); + Vector3f x = up.duplicate().cross(direction); + Vector3f y = direction.duplicate().cross(x); + + x.normalize(); + y.normalize(); + + m[0][0] = x.getX(); + m[0][1] = y.getX(); + m[0][2] = direction.getX(); + m[0][3] = 0; + m[1][0] = x.getY(); + m[1][1] = y.getY(); + m[1][2] = direction.getY(); + m[1][3] = 0; + m[2][0] = x.getZ(); + m[2][1] = y.getZ(); + m[2][2] = direction.getZ(); + m[2][3] = 0; + m[3][0] = 0; + m[3][1] = 0; + m[3][2] = 0; + m[3][3] = 1.0f; + } + public void orthographic(float right, float left, float top, float bottom, float near, float far) { + m[0][0] = 2 / (right - left); + m[0][1] = 0; + m[0][2] = 0; + m[0][3] = 0; + + m[1][0] = 0; + m[1][1] = 2 / (top - bottom); + m[1][2] = 0; + m[1][3] = 0; + + m[2][0] = 0; + m[2][1] = 0; + m[2][2] = -2 / (far - near); + m[2][3] = 0; + + m[3][0] = -(right + left) / (right - left); + m[3][1] = -(top + bottom) / (top - bottom); + m[3][2] = -(far + near) / (far - near); + m[3][3] = 1; + } - */ - Quaternion quaternion = ComponentsQuaternion.fromAxisAndAngle(xaxis, JavaMathAngle.fromRadiant(phi)); - quaternionRotation(quaternion); + @Override + public void perspective(float fov, float aspect, float near, float far) { + //https://www.songho.ca/opengl/gl_projectionmatrix.html under "Perspective projection with horizontal FOV" + identity(); + + float y_scale = (float) ((1f / Math.tan(Math.toRadians(fov / 2f)))); + float frustum_length = far - near; + + set(0, 0, y_scale); + set(1, 1, y_scale*aspect); + set(2, 2, -((far + near) / frustum_length)); + set(2, 3, -1); + set(3, 2, -((2 * near * far) / frustum_length)); + set(3, 3, 0); } @Override @@ -306,19 +511,78 @@ public void transform(Vector4f toTransform) { toTransform.setW(w); } + @Override + public void transform(Vector3f toTransform, float w) { + float x = m[0][0] * toTransform.getX() + m[1][0] * toTransform.getY() + m[2][0] * toTransform.getZ() + m[3][0] * w; + float y = m[0][1] * toTransform.getX() + m[1][1] * toTransform.getY() + m[2][1] * toTransform.getZ() + m[3][1] * w; + float z = m[0][2] * toTransform.getX() + m[1][2] * toTransform.getY() + m[2][2] * toTransform.getZ() + m[3][2] * w; + toTransform.setX(x); + toTransform.setY(y); + toTransform.setZ(z); + } + @Override public void multiply(Matrix matrix) { - Matrix result = new ArrayMatrix4x4(); - for (int x = 0; x < 4; x++) { - for (int y = 0; y < 4; y++) { - result.set(x, y, this.get(0, y) * matrix.get(x, 0) + - this.get(1, y) * matrix.get(x, 1) + - this.get(2, y) * matrix.get(x, 2) + - this.get(3, y) * matrix.get(x, 3)); - } - } - this.copy(result); + float a00 = this.get(0, 0); + float a01 = this.get(0, 1); + float a02 = this.get(0, 2); + float a03 = this.get(0, 3); + + float a10 = this.get(1, 0); + float a11 = this.get(1, 1); + float a12 = this.get(1, 2); + float a13 = this.get(1, 3); + + float a20 = this.get(2, 0); + float a21 = this.get(2, 1); + float a22 = this.get(2, 2); + float a23 = this.get(2, 3); + + float a30 = this.get(3, 0); + float a31 = this.get(3, 1); + float a32 = this.get(3, 2); + float a33 = this.get(3, 3); + + float b00 = matrix.get(0, 0); + float b01 = matrix.get(0, 1); + float b02 = matrix.get(0, 2); + float b03 = matrix.get(0, 3); + + float b10 = matrix.get(1, 0); + float b11 = matrix.get(1, 1); + float b12 = matrix.get(1, 2); + float b13 = matrix.get(1, 3); + + float b20 = matrix.get(2, 0); + float b21 = matrix.get(2, 1); + float b22 = matrix.get(2, 2); + float b23 = matrix.get(2, 3); + + float b30 = matrix.get(3, 0); + float b31 = matrix.get(3, 1); + float b32 = matrix.get(3, 2); + float b33 = matrix.get(3, 3); + + this.set(0, 0, a00*b00+a10*b01+a20*b02+a30*b03); + this.set(1, 0, a00*b10+a10*b11+a20*b12+a30*b13); + this.set(2, 0, a00*b20+a10*b21+a20*b22+a30*b23); + this.set(3, 0, a00*b30+a10*b31+a20*b32+a30*b33); + + this.set(0, 1, a01*b00+a11*b01+a21*b02+a31*b03); + this.set(1, 1, a01*b10+a11*b11+a21*b12+a31*b13); + this.set(2, 1, a01*b20+a11*b21+a21*b22+a31*b23); + this.set(3, 1, a01*b30+a11*b31+a21*b32+a31*b33); + + this.set(0, 2, a02*b00+a12*b01+a22*b02+a32*b03); + this.set(1, 2, a02*b10+a12*b11+a22*b12+a32*b13); + this.set(2, 2, a02*b20+a12*b21+a22*b22+a32*b23); + this.set(3, 2, a02*b30+a12*b31+a22*b32+a32*b33); + + this.set(0, 3, a03*b00+a13*b01+a23*b02+a33*b03); + this.set(1, 3, a03*b10+a13*b11+a23*b12+a33*b13); + this.set(2, 3, a03*b20+a13*b21+a23*b22+a33*b23); + this.set(3, 3, a03*b30+a13*b31+a23*b32+a33*b33); } @Override diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/Matrix.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/Matrix.java index e33b51c..78f60fe 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/Matrix.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/Matrix.java @@ -8,6 +8,8 @@ public interface Matrix { void transpose(); + void negate(); + void invert(); float get(int x, int y); diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/Matrix3x3.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/Matrix3x3.java new file mode 100644 index 0000000..c6f8896 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/Matrix3x3.java @@ -0,0 +1,38 @@ +package fr.radi3nt.maths.components.advanced.matrix; + +import fr.radi3nt.maths.components.advanced.matrix.angle.Angle; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; + +public interface Matrix3x3 extends Matrix { + + Quaternion getRotation(); + + void add(Matrix3x3 rotation); + + void scale(Vector3f scale); + + void rotationX(Angle angle); + + void rotationY(Angle angle); + + void rotationZ(Angle angle); + + void eulerRotation(Angle x, Angle y, Angle z); + + void quaternionRotation(Quaternion quaternion); + + void directionRotation(Vector3f direction, Vector3f up); + + void transform(Vector3f toTransform); + + void multiply(Matrix matrix); + + void copy(Matrix result); + + Matrix3x3 duplicate(); + + void subtract(Matrix3x3 skewRJ); + + boolean isZero(); +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/Matrix4x4.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/Matrix4x4.java index e971e4f..f4e69a7 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/Matrix4x4.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/Matrix4x4.java @@ -15,8 +15,14 @@ public interface Matrix4x4 extends Matrix { void translation(Vector3f translation); + void translation(float x, float y, float z); + + void add(Matrix4x4 rotation); + void scale(Vector3f scale); + void scale(float x, float y, float z); + void rotationX(Angle angle); void rotationY(Angle angle); @@ -29,7 +35,11 @@ public interface Matrix4x4 extends Matrix { void directionRotation(Vector3f direction, Vector3f up); + void orthographic(float right, float left, float top, float bottom, float near, float far); + void perspective(float fov, float aspect, float near, float far); + void transform(Vector4f toTransform); + void transform(Vector3f toTransform, float w); void multiply(Matrix matrix); @@ -37,4 +47,8 @@ public interface Matrix4x4 extends Matrix { Matrix4x4 duplicate(); + void scalar(Vector3f size); + + void scalar(float x, float y, float z); + } diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/angle/JavaMathAngle.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/angle/JavaMathAngle.java index d853289..6fb89aa 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/angle/JavaMathAngle.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/matrix/angle/JavaMathAngle.java @@ -2,6 +2,9 @@ public class JavaMathAngle implements Angle { + public static final Angle POSITIVE_RIGHT_ANGLE = JavaMathAngle.fromDegree(90); + public static final Angle NEGATIVE_RIGHT_ANGLE = JavaMathAngle.fromDegree(-90); + private double angleValueInRadiant; private JavaMathAngle(double angleValueInRadiant) { @@ -49,4 +52,11 @@ public double getDegree() { public void setDegree(double degree) { angleValueInRadiant = Math.toRadians(degree); } + + @Override + public String toString() { + return "JavaMathAngle{" + + "angleValueInRadiant=" + angleValueInRadiant + + '}'; + } } diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/quaternions/ComponentsQuaternion.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/quaternions/ComponentsQuaternion.java index b653dcf..2f8dde8 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/quaternions/ComponentsQuaternion.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/quaternions/ComponentsQuaternion.java @@ -1,12 +1,21 @@ package fr.radi3nt.maths.components.advanced.quaternions; +import fr.radi3nt.maths.Maths; import fr.radi3nt.maths.components.advanced.matrix.angle.Angle; +import fr.radi3nt.maths.components.advanced.matrix.angle.JavaMathAngle; import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.Vector4f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; import java.util.Objects; +import static fr.radi3nt.maths.Maths.clamp; +import static java.lang.Math.abs; +import static java.lang.Math.sqrt; + public class ComponentsQuaternion implements Quaternion { + private static final Vector3f UP = new SimpleVector3f(0, 1, 0); private float x; private float y; private float z; @@ -19,14 +28,45 @@ public ComponentsQuaternion(float x, float y, float z, float w) { this.w = w; } + public ComponentsQuaternion(Vector3f vector, float w) { + this(vector.getX(), vector.getY(), vector.getZ(), w); + } + + public ComponentsQuaternion(Vector4f vector) { + this(vector.getX(), vector.getY(), vector.getZ(), vector.getW()); + } + + @Deprecated public static Quaternion fromVectorToAnother(Vector3f v1, Vector3f v2) { Vector3f vector = v1.duplicate().cross(v2); - float w = (float) (Math.sqrt((v1.lengthSquared()) * (v2.lengthSquared())) + v1.dot(v2)); - ComponentsQuaternion componentsQuaternion = new ComponentsQuaternion(vector.getX(), vector.getY(), vector.getZ(), w); + float w = (float) (sqrt((v1.lengthSquared()) * (v2.lengthSquared())) + v1.dot(v2)); + Quaternion componentsQuaternion = new ComponentsQuaternion(vector.getX(), vector.getY(), vector.getZ(), w); componentsQuaternion.normalise(); return componentsQuaternion; } + public static Quaternion fromTwoVectors(Vector3f u, Vector3f v) { + float norm_u_norm_v = (float) sqrt(u.lengthSquared() * v.lengthSquared()); + float real_part = norm_u_norm_v + u.dot(v); + Vector3f w; + + if (real_part < 1.e-6f * norm_u_norm_v) { + /* If u and v are exactly opposite, rotate 180 degrees + * around an arbitrary orthogonal axis. Axis normalisation + * can happen later, when we normalise the quaternion. */ + real_part = 0.0f; + w = abs(u.getX()) > abs(u.getZ()) ? new SimpleVector3f(-u.getY(), u.getX(), 0.f) + : new SimpleVector3f(0.f, -u.getZ(), u.getY()); + } else { + /* Otherwise, build quaternion the standard way. */ + w = u.duplicate().cross(v); + } + + Quaternion quaternion = new ComponentsQuaternion(w.getX(), w.getY(), w.getZ(), real_part); + quaternion.normaliseSafely(); + return quaternion; + } + public static Quaternion fromAxisAndAngle(Vector3f axis, Angle angle) { Vector3f actualAxis = axis.duplicate().normalize(); @@ -37,25 +77,77 @@ public static Quaternion fromAxisAndAngle(Vector3f axis, Angle angle) { float y = actualAxis.getY() * sin_a; float z = actualAxis.getZ() * sin_a; - return new ComponentsQuaternion(x, y, z, cos_a); + return new ComponentsQuaternion(x, y, z, clamp(cos_a, -1, 1)); } public static Quaternion fromEulerAngles(Angle angleX, Angle angleY, Angle angleZ) { - float sinPitch = (float) Math.sin(angleX.getRadiant() * 0.5F); - float cosPitch = (float) Math.cos(angleX.getRadiant() * 0.5F); - float sinYaw = (float) Math.sin(angleY.getRadiant() * 0.5F); - float cosYaw = (float) Math.cos(angleY.getRadiant() * 0.5F); - float sinRoll = (float) Math.sin(angleZ.getRadiant() * 0.5F); - float cosRoll = (float) Math.cos(angleZ.getRadiant() * 0.5F); - float cosPitchCosYaw = (cosPitch * cosYaw); - float sinPitchSinYaw = (sinPitch * sinYaw); - - float x = sinRoll * cosPitchCosYaw - cosRoll * sinPitchSinYaw; - float y = cosRoll * sinPitch * cosYaw + sinRoll * cosPitch * sinYaw; - float z = cosRoll * cosPitch * sinYaw - sinRoll * sinPitch * cosYaw; - float w = cosRoll * cosPitchCosYaw + sinRoll * sinPitchSinYaw; + Quaternion rotationX = ComponentsQuaternion.fromAxisAndAngle(new SimpleVector3f(1, 0, 0), angleX); + Quaternion rotationY = ComponentsQuaternion.fromAxisAndAngle(new SimpleVector3f(0, 1, 0), angleY); + Quaternion rotationZ = ComponentsQuaternion.fromAxisAndAngle(new SimpleVector3f(0, 0, 1), angleZ); - return new ComponentsQuaternion(x, y, z, w); + rotationZ.multiply(rotationY); + rotationZ.multiply(rotationX); + + return rotationZ; + } + + public static Quaternion zero() { + return new ComponentsQuaternion(0, 0, 0, 1); + } + + public static Quaternion fromVec4(Vector4f response) { + return new ComponentsQuaternion(response.getX(), response.getY(), response.getZ(), response.getW()); + } + + public static Quaternion fromUpVector(Vector3f other) { + return fromTwoVectors(UP, other); + } + + public static float distanceSquared(Quaternion orientation, Quaternion targetOrientation) { + float x = (orientation.getX()-targetOrientation.getX()); + float y = (orientation.getY()-targetOrientation.getY()); + float z = (orientation.getZ()-targetOrientation.getZ()); + float w = (orientation.getW()-targetOrientation.getW()); + return x*x + y*y + z*z + w*w; + } + + @Override + public Quaternion getRotationComponentAboutAxis( + Vector3f direction) { + Vector3f rotationAxis = new SimpleVector3f(this.x, this.y, this.z); + float dotProd = direction.dot(rotationAxis); + // Shortcut calculation of `projection` requires `direction` to be normalized + Vector3f projection = direction.duplicate().mul(dotProd); + ComponentsQuaternion twist = new ComponentsQuaternion( + projection.getX(), projection.getY(), projection.getZ(), this.w); + twist.normalise(); + if (dotProd < 0.0) { + // Ensure `twist` points towards `direction` + twist.w = -twist.w; + // Rotation angle `twist.angle()` is now reliable + } + return twist; + } + + @Override + public Vector3f getAxisOrDefault(Vector3f axis) { + float sinFromCos = (float) sqrt(Math.max(0, 1 - w * w)); + if (sinFromCos == 0 || Float.isNaN(sinFromCos)) + return axis; + + float x = this.x / sinFromCos; + float y = this.y / sinFromCos; + float z = this.z / sinFromCos; + + Vector3f vector = new SimpleVector3f(x, y, z); + if (vector.lengthSquared() == 0) + return axis; + return vector; + } + + @Override + public Vector3f getVector() { + return new SimpleVector3f(x, y, z); } @Override @@ -63,21 +155,56 @@ public float getX() { return x; } + @Override + public void setX(float x) { + this.x = x; + } + @Override public float getY() { return y; } + @Override + public void setY(float y) { + this.y = y; + } + @Override public float getZ() { return z; } + @Override + public void setZ(float z) { + this.z = z; + } + @Override public float getW() { return w; } + @Override + public void setW(float w) { + this.w = w; + } + + @Override + public void set(float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public void inverse() { + this.x = -x; + this.y = -y; + this.z = -z; + } + @Override public void normalise() { float magnitude = getMagnitude(); @@ -87,6 +214,17 @@ public void normalise() { this.w /= magnitude; } + @Override + public void normaliseSafely() { + float magnitude = getMagnitude(); + if (magnitude==0) + return; + this.x /= magnitude; + this.y /= magnitude; + this.z /= magnitude; + this.w /= magnitude; + } + @Override public void multiply(Quaternion quaternion) { float otherW = quaternion.getW(); @@ -94,10 +232,10 @@ public void multiply(Quaternion quaternion) { float otherY = quaternion.getY(); float otherZ = quaternion.getZ(); - float w = this.w * otherW - this.x * otherX - this.y * otherY - this.z * otherZ; - float x = this.w * otherX + this.x * otherW + this.y * otherZ - this.z * otherY; - float y = this.w * otherY + this.y * otherW + this.z * otherX - this.x * otherZ; - float z = this.w * otherZ + this.z * otherW + this.x * otherY - this.y * otherX; + float w = -this.x * otherX - this.y * otherY - this.z * otherZ + this.w * otherW; + float x = this.x * otherW + this.y * otherZ - this.z * otherY + this.w * otherX; + float y = -this.x * otherZ + this.y * otherW + this.z * otherX + this.w * otherY; + float z = this.x * otherY - this.y * otherX + this.z * otherW + this.w * otherZ; this.x = x; this.y = y; @@ -105,20 +243,68 @@ public void multiply(Quaternion quaternion) { this.w = w; } + @Override + public void multiply(Vector3f vec) { + float wRes = -(x * vec.getX() + y * vec.getY() + z * vec.getZ()); + float xRes = w * vec.getX() + y * vec.getZ() - z * vec.getY(); + float yRes = w * vec.getY() + z * vec.getX() - x * vec.getZ(); + float zRes = w * vec.getZ() + x * vec.getY() - y * vec.getX(); + x = xRes; + y = yRes; + z = zRes; + w = wRes; + } + + @Override + public void transform(Vector3f vec) { + 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); + + vec.set(Maths.fma((xx - yy - zz + ww) * k, vec.getX(), Maths.fma(2 * (xy - zw) * k, vec.getY(), (2 * (xz + yw) * k) * vec.getZ())), + Maths.fma(2 * (xy + zw) * k, vec.getX(), Maths.fma((yy - xx - zz + ww) * k, vec.getY(), (2 * (yz - xw) * k) * vec.getZ())), + Maths.fma(2 * (xz - yw) * k, vec.getX(), Maths.fma(2 * (yz + xw) * k, vec.getY(), ((zz - xx - yy + ww) * k) * vec.getZ()))); + } + + @Override + public void transformUnit(Vector3f vec) { + transform(vec); + } + + @Override + public void multiplyInv(Vector3f vec) { + float wRes = -(x * vec.getX() + y * vec.getY() + z * vec.getZ()); + float xRes = w * vec.getX() + z * vec.getY() - y * vec.getZ(); + float yRes = w * vec.getY() + x * vec.getZ() - z * vec.getX(); + float zRes = w * vec.getZ() + y * vec.getX() - x * vec.getY(); + x = xRes; + y = yRes; + z = zRes; + w = wRes; + } + + @Override + public void multiply(float s) { + this.x *= s; + this.y *= s; + this.z *= s; + this.w *= s; + } + @Override public void interpolate(Quaternion quaternionEnd, float ease) { double cosHalfTheta = dot(quaternionEnd); // if qa=quaternionEnd or qa=-quaternionEnd then theta = 0 and we can return qa - if (Math.abs(cosHalfTheta) >= 1.0) { + if (abs(cosHalfTheta) >= 1.0) { return; } // Calculate temporary values. double halfTheta = Math.acos(cosHalfTheta); - double sinHalfTheta = Math.sqrt(1.0 - cosHalfTheta * cosHalfTheta); + double sinHalfTheta = sqrt(1.0 - cosHalfTheta * cosHalfTheta); // if theta = 180 degrees then result is not fully defined // we could rotate around any axis normal to qa or quaternionEnd - if (Math.abs(sinHalfTheta) < 0) { // fabs is floating point absolute + if (abs(sinHalfTheta) < 0) { // fabs is floating point absolute this.w = (w * 0.5f + quaternionEnd.getW() * 0.5f); this.x = (x * 0.5f + quaternionEnd.getX() * 0.5f); this.y = (y * 0.5f + quaternionEnd.getY() * 0.5f); @@ -134,6 +320,17 @@ public void interpolate(Quaternion quaternionEnd, float ease) { z = (float) (z * ratioA + quaternionEnd.getZ() * ratioB); } + @Override + public Vector3f velocity(Quaternion other, float delta) { + Vector3f vel = new SimpleVector3f( + this.w * other.getX() - this.x * other.getW() - this.y * other.getZ() + this.z * other.getY(), + this.w * other.getY() + this.x * other.getZ() - this.y * other.getW() - this.z * other.getX(), + this.w * other.getZ() - this.x * other.getY() + this.y * other.getX() - this.z * other.getW() + ); + vel.mul((2 / delta)); + return vel; + } + @Override public float dot(Quaternion other) { return x * other.getX() + y * other.getY() + z * other.getZ() + w * other.getW(); @@ -141,7 +338,12 @@ public float dot(Quaternion other) { @Override public float getMagnitude() { - return (float) Math.sqrt(w * w + x * x + y * y + z * z); + return (float) sqrt(getSquaredMagnitude()); + } + + @Override + public float getSquaredMagnitude() { + return w * w + x * x + y * y + z * z; } @Override @@ -154,6 +356,41 @@ public Quaternion duplicate() { return new ComponentsQuaternion(x, y, z, w); } + @Override + public void copy(Quaternion rotation) { + this.x = rotation.getX(); + this.y = rotation.getY(); + this.z = rotation.getZ(); + this.w = rotation.getW(); + } + + @Override + public JavaMathAngle getAngle() { + return JavaMathAngle.fromRadiant(2 * Math.acos(clamp(w, -1, 1))); + } + + @Override + public void copy(Vector4f rotation) { + this.x = rotation.getX(); + this.y = rotation.getY(); + this.z = rotation.getZ(); + this.w = rotation.getW(); + } + + @Override + public Angle getAxisAngle(Vector3f axis) { + axis.copy(getAxisOrDefault(axis)); + return getAngle(); + } + + @Override + public void add(Quaternion quaternion) { + this.x += quaternion.getX(); + this.y += quaternion.getY(); + this.z += quaternion.getZ(); + this.w += quaternion.getW(); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/quaternions/Quaternion.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/quaternions/Quaternion.java index 218b6dc..ca210b2 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/quaternions/Quaternion.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/advanced/quaternions/Quaternion.java @@ -1,26 +1,74 @@ package fr.radi3nt.maths.components.advanced.quaternions; +import fr.radi3nt.maths.components.advanced.matrix.angle.Angle; +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.Vector4f; + public interface Quaternion { float getX(); + void setX(float x); + float getY(); + void setY(float y); + float getZ(); + void setZ(float z); + float getW(); + void setW(float w); + + void set(float x, float y, float z, float w); + + void inverse(); + void normalise(); + void normaliseSafely(); + + void add(Quaternion quaternion); + void multiply(Quaternion quaternion); + void multiply(Vector3f vec); + + void transform(Vector3f vec); + + void transformUnit(Vector3f result); + + void multiplyInv(Vector3f vec); + + void multiply(float s); + void interpolate(Quaternion quaternionEnd, float ease); + Vector3f velocity(Quaternion other, float delta); + float dot(Quaternion other); float getMagnitude(); + float getSquaredMagnitude(); Quaternion getConjugate(); Quaternion duplicate(); + + void copy(Quaternion rotation); + + Quaternion getRotationComponentAboutAxis( + Vector3f direction); + + Vector3f getAxisOrDefault(Vector3f axis); + + Vector3f getVector(); + + Angle getAngle(); + + void copy(Vector4f quaternion); + + Angle getAxisAngle(Vector3f axis); } diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/MatrixNxNf.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/MatrixNxNf.java new file mode 100644 index 0000000..625433a --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/MatrixNxNf.java @@ -0,0 +1,29 @@ +package fr.radi3nt.maths.components.arbitrary; + +import java.util.BitSet; + +public interface MatrixNxNf { + + float get(int x, int y); + + void set(int x, int y, float value); + + int getWidth(); + + int getHeight(); + + BitSet nonZero(); + + float[] getM(); + + MatrixNxNf multiply(MatrixNxNf matrixNxN); + + MatrixNxNf multiplyTransposed(MatrixNxNf matrixNxN); + + MatrixNxNf multiplyTransposedOther(MatrixNxNf matrixNxN); + + VectorNf transform(VectorNf vec); + + MatrixNxNf duplicate(); + +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/OperatingVectorNf.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/OperatingVectorNf.java new file mode 100644 index 0000000..40a207d --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/OperatingVectorNf.java @@ -0,0 +1,20 @@ +package fr.radi3nt.maths.components.arbitrary; + +public interface OperatingVectorNf { + + float get(int row); + + OperatingVectorNf duplicate(); + + OperatingVectorNf div(float number); + OperatingVectorNf mul(float number); + + OperatingVectorNf add(OperatingVectorNf other); + OperatingVectorNf sub(OperatingVectorNf other); + + int size(); + + float length(); + + void copy(OperatingVectorNf other); +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/VectorNd.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/VectorNd.java new file mode 100644 index 0000000..74d4298 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/VectorNd.java @@ -0,0 +1,24 @@ +package fr.radi3nt.maths.components.arbitrary; + +import fr.radi3nt.maths.components.vectors.Vector; + +public interface VectorNd extends Vector { + + double get(int row); + + void set(int row, double v); + + void add(int row, double v); + + void div(int row, double v); + + void clamp(int row, double min, double max); + + int size(); + + void scalar(VectorNd kd); + + void add(VectorNd damping); + + void scalar(double v); +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/VectorNf.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/VectorNf.java new file mode 100644 index 0000000..719e5f4 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/VectorNf.java @@ -0,0 +1,29 @@ +package fr.radi3nt.maths.components.arbitrary; + +import fr.radi3nt.maths.components.vectors.Vector; + +public interface VectorNf extends Vector, OperatingVectorNf { + + float get(int row); + + void set(int row, float v); + + void add(int row, float v); + + void div(int row, float v); + + void clamp(int row, float min, float max); + + int size(); + + VectorNf duplicate(); + + default void copy(OperatingVectorNf other) { + if (other.size()!=this.size()) { + throw new IllegalArgumentException("Other vector doesn't have the same size"); + } + for (int i = 0; i < this.size(); i++) { + this.set(i, other.get(i)); + } + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/ArrayMatrixNxNd.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/ArrayMatrixNxNd.java new file mode 100644 index 0000000..99fd154 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/ArrayMatrixNxNd.java @@ -0,0 +1,224 @@ +package fr.radi3nt.maths.components.arbitrary.matrix; + +import fr.radi3nt.maths.components.MatrixNxNd; +import fr.radi3nt.maths.components.arbitrary.VectorNd; +import fr.radi3nt.maths.components.arbitrary.vector.ArrayVectorNd; + +import java.util.Arrays; +import java.util.BitSet; + +public class ArrayMatrixNxNd implements MatrixNxNd { + + private final int width; + private final int height; + private final double[][] m; + private final BitSet zeroSet; + + public ArrayMatrixNxNd(int width, int height) { + this.width = width; + this.height = height; + m = new double[width][height]; + zeroSet = new BitSet(this.width * this.height); + } + + protected ArrayMatrixNxNd(int width, int height, double[][] m) { + this.width = width; + this.height = height; + this.m = m; + zeroSet = new BitSet(this.width * this.height); + } + + @Override + public ArrayMatrixNxNd multiply(MatrixNxNd matrixNxN) { + if (this.width != matrixNxN.getHeight()) + throw new UnsupportedOperationException("Unable to multiply matrices: " + matrixNxN.getHeight() + "!=" + this.width); + + int resultWidth = matrixNxN.getWidth(); + int resultHeight = this.height; + + BitSet nonZero = matrixNxN.nonZero(); + + ArrayMatrixNxNd result = new ArrayMatrixNxNd(resultWidth, resultHeight); + for (int x = 0; x < result.width; x++) { + for (int i = 0; i < this.width; i++) { + double cache = matrixNxN.get(x, i); + for (int y = 0; y < result.height; y++) { + if (nonZero.get(x + i * matrixNxN.getWidth())) + result.add(x, y, this.get(i, y) * cache); + } + } + } + result.markAllNonZero(); + + return result; + } + + @Override + public ArrayMatrixNxNd multiplyWithTransposed(MatrixNxNd matrixNxN) { + if (this.width != matrixNxN.getWidth()) + throw new UnsupportedOperationException("Unable to multiply matrices: " + matrixNxN.getWidth() + "!=" + this.width); + + int resultWidth = matrixNxN.getHeight(); + int resultHeight = this.height; + + BitSet nonZero = matrixNxN.nonZero(); + + ArrayMatrixNxNd result = new ArrayMatrixNxNd(resultWidth, resultHeight); + for (int x = 0; x < result.width; x++) { + for (int y = 0; y < result.height; y++) { + double total = 0; + for (int i = 0; i < this.width; i++) { + if (nonZero.get(i + x * matrixNxN.getWidth())) + total += this.get(i, y) * matrixNxN.get(i, x); + } + result.add(x, y, total); + } + } + result.markAllNonZero(); + + return result; + } + + @Override + public MatrixNxNd multiplyTransposedOther(MatrixNxNd matrixNxN) { + if (matrixNxN.getWidth() != this.width) + throw new UnsupportedOperationException("Unable to multiply matrices: " + matrixNxN.getWidth() + "!=" + this.width); + + int resultWidth = this.height; + int resultHeight = matrixNxN.getHeight(); + + BitSet nonZero = this.nonZero(); + + ArrayMatrixNxNd result = new ArrayMatrixNxNd(resultWidth, resultHeight); + for (int x = 0; x < result.width; x++) { + for (int y = 0; y < result.height; y++) { + double total = 0; + for (int i = 0; i < matrixNxN.getWidth(); i++) { + if (nonZero.get(i + x * this.getWidth())) + total += matrixNxN.get(i, y) * this.get(i, x); + } + result.add(x, y, total); + } + } + result.markAllNonZero(); + + return result; + } + + public double[] getWidthArray(int x) { + return m[x]; + } + + @Override + public double get(int x, int y) { + double[] array = m[x]; + return array==null ? 0 : array[y]; + } + + @Override + public void set(int x, int y, double value) { + double[] array = m[x]; + if (array==null) + m[x] = array = new double[this.width]; + array[y] = value; + zeroSet.set(x + y * this.width, value != 0); + } + + public void add(int x, int y, double total) { + double[] array = m[x]; + if (array==null) + m[x] = array = new double[this.width]; + array[y] += total; + } + + public void markAllNonZero() { + zeroSet.set(0, height * width, true); + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + @Override + public BitSet nonZero() { + return zeroSet; + } + + @Override + public ArrayMatrixNxNd duplicate() { + return new ArrayMatrixNxNd(width, height, deepCopy(m)); + } + + public double[][] getM() { + return m; + } + + @Override + public VectorNd transform(VectorNd vec) { + VectorNd result = new ArrayVectorNd(height); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + double value = get(x, y) * vec.get(x); + result.add(y, value); + } + } + + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ArrayMatrixNxNd)) return false; + + ArrayMatrixNxNd that = (ArrayMatrixNxNd) o; + + if (width != that.width) return false; + if (height != that.height) return false; + return Arrays.deepEquals(m, that.m); + } + + @Override + public int hashCode() { + int result = width; + result = 31 * result + height; + result = 31 * result + Arrays.deepHashCode(m); + return result; + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder("ArrayMatrixNxN{" + + "\n"); + stringBuilder.append("width=").append(width).append("\n"); + stringBuilder.append("height=").append(height).append("\n"); + + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { + stringBuilder.append(get(i, j)).append(" "); + } + stringBuilder.append("\n"); + } + + stringBuilder.append("}"); + + return stringBuilder.toString(); + } + + public static double[][] deepCopy(double[][] original) { + if (original == null) { + return null; + } + + final double[][] result = new double[original.length][]; + for (int i = 0; i < original.length; i++) { + result[i] = Arrays.copyOf(original[i], original[i].length); + } + return result; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/ArrayMatrixNxNf.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/ArrayMatrixNxNf.java new file mode 100644 index 0000000..52861b0 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/ArrayMatrixNxNf.java @@ -0,0 +1,171 @@ +package fr.radi3nt.maths.components.arbitrary.matrix; + +import fr.radi3nt.maths.components.arbitrary.MatrixNxNf; +import fr.radi3nt.maths.components.arbitrary.VectorNf; +import fr.radi3nt.maths.components.arbitrary.vector.ArrayVectorNf; + +import java.util.Arrays; +import java.util.BitSet; + +public class ArrayMatrixNxNf implements MatrixNxNf { + + private final int width; + private final int height; + private final float[] m; + private final BitSet zeroSet; + + public ArrayMatrixNxNf(int width, int height) { + this.width = width; + this.height = height; + m = new float[width * height]; + zeroSet = new BitSet(this.width * this.height); + } + + protected ArrayMatrixNxNf(int width, int height, float[] m) { + this.width = width; + this.height = height; + this.m = m; + zeroSet = new BitSet(this.width * this.height); + } + + @Override + public ArrayMatrixNxNf multiply(MatrixNxNf matrixNxN) { + if (this.width != matrixNxN.getHeight()) + throw new UnsupportedOperationException("Unable to multiply matrices: " + matrixNxN.getHeight() + "!=" + this.width); + + int resultWidth = matrixNxN.getWidth(); + int resultHeight = this.height; + + BitSet nonZero = matrixNxN.nonZero(); + + ArrayMatrixNxNf result = new ArrayMatrixNxNf(resultWidth, resultHeight); + for (int x = 0; x < result.width; x++) { + for (int y = 0; y < result.height; y++) { + float total = 0; + for (int i = 0; i < this.width; i++) { + if (nonZero.get(x + i * matrixNxN.getWidth())) + total += this.get(i, y) * matrixNxN.get(x, i); + } + result.set(x, y, total); + } + } + + return result; + } + + @Override + public ArrayMatrixNxNf multiplyTransposed(MatrixNxNf matrixNxN) { + if (this.width != matrixNxN.getWidth()) + throw new UnsupportedOperationException("Unable to multiply matrices: " + matrixNxN.getWidth() + "!=" + this.width); + + int resultWidth = this.height; + int resultHeight = matrixNxN.getHeight(); + + BitSet nonZero = matrixNxN.nonZero(); + + ArrayMatrixNxNf result = new ArrayMatrixNxNf(resultWidth, resultHeight); + for (int x = 0; x < result.width; x++) { + for (int y = 0; y < result.height; y++) { + float total = 0; + for (int i = 0; i < this.width; i++) { + if (nonZero.get(i + y * matrixNxN.getWidth())) + total += this.get(i, x) * matrixNxN.get(i, y); + } + result.set(x, y, total); + } + } + + return result; + } + + @Override + public MatrixNxNf multiplyTransposedOther(MatrixNxNf matrixNxN) { + if (this.width != matrixNxN.getWidth()) + throw new UnsupportedOperationException("Unable to multiply matrices: " + matrixNxN.getWidth() + "!=" + this.width); + + int resultWidth = matrixNxN.getHeight(); + int resultHeight = this.getHeight(); + + BitSet nonZero = this.nonZero(); + + ArrayMatrixNxNf result = new ArrayMatrixNxNf(resultWidth, resultHeight); + for (int x = 0; x < result.width; x++) { + for (int y = 0; y < result.height; y++) { + float total = 0; + for (int i = 0; i < this.width; i++) { + if (nonZero.get(i + y * this.getWidth())) + total += matrixNxN.get(i, x) * this.get(i, y); + } + result.set(x, y, total); + } + } + + return result; + } + + @Override + public float get(int x, int y) { + return m[x + y * width]; + } + + @Override + public void set(int x, int y, float value) { + m[x + y * width] = value; + zeroSet.set(x + y * width, value != 0); + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + @Override + public BitSet nonZero() { + return zeroSet; + } + + @Override + public ArrayMatrixNxNf duplicate() { + return new ArrayMatrixNxNf(width, height, Arrays.copyOf(m, m.length)); + } + + public float[] getM() { + return m; + } + + @Override + public VectorNf transform(VectorNf vec) { + VectorNf result = new ArrayVectorNf(height); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + float value = get(x, y) * vec.get(x); + result.add(y, value); + } + } + + return result; + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder("ArrayMatrixNxN{" + + "\n"); + stringBuilder.append("width=").append(width).append("\n"); + stringBuilder.append("height=").append(height).append("\n"); + + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { + stringBuilder.append(get(i, j)).append(" "); + } + stringBuilder.append("\n"); + } + + stringBuilder.append("}"); + + return stringBuilder.toString(); + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/sparse/SparseBlockd.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/sparse/SparseBlockd.java new file mode 100644 index 0000000..f338907 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/sparse/SparseBlockd.java @@ -0,0 +1,69 @@ +package fr.radi3nt.maths.components.arbitrary.matrix.sparse; + +public class SparseBlockd { + + private final int startX; + private final int startY; + private final int width; + private final int height; + + private final double[] values; + + public SparseBlockd(int startX, int startY, int width, int height, double[] values) { + this.startX = startX; + this.startY = startY; + this.width = width; + this.height = height; + this.values = values; + } + + public SparseBlockd(int startX, int startY, int width, int height, double[][] m) { + this.startX = startX; + this.startY = startY; + this.width = width; + this.height = height; + this.values = new double[]{width * height}; + + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + values[i + j * width] = m[i][j]; + } + } + } + + public double get(int i, int j) { + return values[(i - startX) + (j - startY) * width]; + } + + public int getStartX() { + return startX; + } + + public int getStartY() { + return startY; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public double[] getValues() { + return values; + } + + public boolean isInBound(int x, int y) { + return isInBoundX(x) && isInBoundY(y); + } + + protected boolean isInBoundY(int y) { + return y >= startY && y < startY + height; + } + + protected boolean isInBoundX(int x) { + return x >= startX && x < startX + width; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/sparse/SparseBlockf.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/sparse/SparseBlockf.java new file mode 100644 index 0000000..08d8741 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/sparse/SparseBlockf.java @@ -0,0 +1,61 @@ +package fr.radi3nt.maths.components.arbitrary.matrix.sparse; + +public class SparseBlockf { + + private final int startI; + private final int startJ; + private final int lengthI; + private final int lengthJ; + + private final float[] values; + + public SparseBlockf(int startI, int startJ, int lengthI, int lengthJ, float[] values) { + this.startI = startI; + this.startJ = startJ; + this.lengthI = lengthI; + this.lengthJ = lengthJ; + this.values = values; + } + + public SparseBlockf(int startI, int startJ, int lengthI, int lengthJ, float[][] m) { + this.startI = startI; + this.startJ = startJ; + this.lengthI = lengthI; + this.lengthJ = lengthJ; + this.values = new float[]{lengthI * lengthJ}; + + for (int i = 0; i < lengthI; i++) { + for (int j = 0; j < lengthJ; j++) { + values[i + j * lengthI] = m[i][j]; + } + } + } + + public float get(int i, int j) { + return values[(i - startI) + (j - startJ) * lengthI]; + } + + public int getStartI() { + return startI; + } + + public int getStartJ() { + return startJ; + } + + public int getLengthI() { + return lengthI; + } + + public int getLengthJ() { + return lengthJ; + } + + public float[] getValues() { + return values; + } + + public boolean isInBound(int x, int y) { + return x >= startI && y >= startJ && x < startI + lengthI && y < startJ + lengthJ; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/sparse/SparseMatrixd.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/sparse/SparseMatrixd.java new file mode 100644 index 0000000..ec76c12 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/sparse/SparseMatrixd.java @@ -0,0 +1,262 @@ +package fr.radi3nt.maths.components.arbitrary.matrix.sparse; + +import fr.radi3nt.maths.components.MatrixNxNd; +import fr.radi3nt.maths.components.arbitrary.VectorNd; +import fr.radi3nt.maths.components.arbitrary.matrix.ArrayMatrixNxNd; +import fr.radi3nt.maths.components.arbitrary.vector.ArrayVectorNd; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collection; + +public class SparseMatrixd implements MatrixNxNd { + + private final Collection sparseBlocks; + private int width; + private int height; + + public SparseMatrixd(Collection sparseBlocks, int width, int height) { + this.sparseBlocks = sparseBlocks; + this.width = width; + this.height = height; + } + + + public SparseMatrixd() { + this(new ArrayList<>(), 0, 0); + } + + public void add(SparseBlockd sparseBlock) { + sparseBlocks.add(sparseBlock); + computeCursors(sparseBlock); + } + + private void computeCursors(SparseBlockd sparseBlock) { + width = Math.max(width, sparseBlock.getStartX() + sparseBlock.getWidth()); + height = Math.max(height, sparseBlock.getStartY() + sparseBlock.getHeight()); + } + + @Override + public double get(int x, int y) { + for (SparseBlockd sparseBlock : sparseBlocks) { + if (sparseBlock.isInBound(x, y)) { + return sparseBlock.get(x, y); + } + } + return 0; + } + + @Override + public void set(int x, int y, double value) { + throw new UnsupportedOperationException(); + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public BitSet nonZero() { + BitSet bitSet = new BitSet(width * height); + //bitSet.set(0, width*height); + + for (SparseBlockd sparseBlock : sparseBlocks) { + for (int y = sparseBlock.getStartY(); y < sparseBlock.getStartY() + sparseBlock.getHeight(); y++) { + int row = y * width; + bitSet.set(sparseBlock.getStartX() + row, sparseBlock.getStartX() + sparseBlock.getWidth() + row); + } + } + + return bitSet; + } + + public VectorNd transform(VectorNd vector) { + VectorNd result = new ArrayVectorNd(height); + for (SparseBlockd sparseBlock : sparseBlocks) { + for (int y = sparseBlock.getStartY(); y < sparseBlock.getStartY() + sparseBlock.getHeight(); y++) { + for (int x = sparseBlock.getStartX(); x < sparseBlock.getStartX() + sparseBlock.getWidth(); x++) { + double value = sparseBlock.get(x, y); + result.add(y, value * vector.get(x)); + } + } + } + return result; + } + + public VectorNd transformTransposed(VectorNd vector) { + VectorNd result = new ArrayVectorNd(width); + for (SparseBlockd sparseBlock : sparseBlocks) { + for (int y = sparseBlock.getStartY(); y < sparseBlock.getStartY() + sparseBlock.getHeight(); y++) { + for (int x = sparseBlock.getStartX(); x < sparseBlock.getStartX() + sparseBlock.getWidth(); x++) { + double value = sparseBlock.get(x, y); + result.add(x, value * vector.get(y)); + } + } + } + return result; + } + + @Override + public MatrixNxNd duplicate() { + return new SparseMatrixd(new ArrayList<>(sparseBlocks), width, height); + } + + @Override + public MatrixNxNd multiply(MatrixNxNd matrixNxN) { + if (this.width != matrixNxN.getHeight()) + throw new UnsupportedOperationException("Unable to multiply matrices: " + matrixNxN.getHeight() + "!=" + this.width); + if (matrixNxN instanceof ArrayMatrixNxNd) { + return multiplyIntern((ArrayMatrixNxNd) matrixNxN); + } + return multiplyGeneral(matrixNxN); + } + + private MatrixNxNd multiplyIntern(ArrayMatrixNxNd matrixNxN) { + int resultWidth = matrixNxN.getWidth(); + int resultHeight = this.height; + + ArrayMatrixNxNd result = new ArrayMatrixNxNd(resultWidth, resultHeight); + for (SparseBlockd sparseBlock : sparseBlocks) { + for (int x = 0; x < resultWidth; x++) { + double[] array = matrixNxN.getWidthArray(x); + if (array==null) + continue; + for (int i = sparseBlock.getStartX(); i < sparseBlock.getStartX() + sparseBlock.getWidth(); i++) { + double temp = array[i]; + if (temp==0) + continue; + for (int y = sparseBlock.getStartY(); y < sparseBlock.getStartY() + sparseBlock.getHeight(); y++) { + double adding = temp*sparseBlock.get(i, y); + if (adding==0) + continue; + result.add(x, y, adding); + } + } + } + } + result.markAllNonZero(); + + return result; + } + + private ArrayMatrixNxNd multiplyGeneral(MatrixNxNd matrixNxN) { + int resultWidth = matrixNxN.getWidth(); + int resultHeight = this.height; + + ArrayMatrixNxNd result = new ArrayMatrixNxNd(resultWidth, resultHeight); + for (SparseBlockd sparseBlock : sparseBlocks) { + for (int x = 0; x < resultWidth; x++) { + for (int i = sparseBlock.getStartX(); i < sparseBlock.getStartX() + sparseBlock.getWidth(); i++) { + double temp = matrixNxN.get(x, i); + if (temp==0) + continue; + for (int y = sparseBlock.getStartY(); y < sparseBlock.getStartY() + sparseBlock.getHeight(); y++) { + double adding = temp*sparseBlock.get(i, y); + if (adding==0) + continue; + result.add(x, y, adding); + } + } + } + } + result.markAllNonZero(); + + return result; + } + + @Override + public ArrayMatrixNxNd multiplyWithTransposed(MatrixNxNd matrixNxN) { + if (this.width != matrixNxN.getWidth()) + throw new UnsupportedOperationException("Unable to multiply matrices: " + matrixNxN.getWidth() + "!=" + this.width); + + int resultWidth = matrixNxN.getHeight(); + int resultHeight = this.height; + + ArrayMatrixNxNd result = new ArrayMatrixNxNd(resultWidth, resultHeight); + + for (SparseBlockd sparseBlock : sparseBlocks) { + for (int y = sparseBlock.getStartY(); y < sparseBlock.getStartY() + sparseBlock.getHeight(); y++) { + for (int x = 0; x < resultWidth; x++) { + double total = 0; + for (int i = sparseBlock.getStartX(); i < sparseBlock.getStartX() + sparseBlock.getWidth(); i++) { + total += sparseBlock.get(i, y) * matrixNxN.get(i, x); + } + result.add(x, y, total); + } + } + } + result.markAllNonZero(); + + return result; + } + + @Override + public MatrixNxNd multiplyTransposedOther(MatrixNxNd matrixNxN) { + if (matrixNxN.getWidth() != this.width) + throw new UnsupportedOperationException("Unable to multiply matrices: " + matrixNxN.getWidth() + "!=" + this.width); + if (matrixNxN instanceof ArrayMatrixNxNd) + return multiplyTransposedOtherIntern((ArrayMatrixNxNd) matrixNxN); + return multiplyTransposedMatrixOtherGeneral(matrixNxN); + } + + private MatrixNxNd multiplyTransposedOtherIntern(ArrayMatrixNxNd matrixNxN) { + int resultWidth = this.height; + int resultHeight = matrixNxN.getHeight(); + + ArrayMatrixNxNd result = new ArrayMatrixNxNd(resultWidth, resultHeight); + BitSet resultNonZero = result.nonZero(); + for (SparseBlockd sparseBlock : sparseBlocks) { + for (int x = sparseBlock.getStartY(); x < sparseBlock.getStartY() + sparseBlock.getHeight(); x++) { + for (int i = sparseBlock.getStartX(); i < sparseBlock.getStartX() + sparseBlock.getWidth(); i++) { + double cache = sparseBlock.get(i, x); + if (cache==0) + continue; + double[] values = matrixNxN.getWidthArray(i); + if (values==null) + continue; + for (int y = 0; y < resultHeight; y++) { + double added = values[y] * cache; + if (added==0) + continue; + result.add(x, y, added); + resultNonZero.set(x + y * resultWidth, true); + } + } + } + } + + return result; + } + + private ArrayMatrixNxNd multiplyTransposedMatrixOtherGeneral(MatrixNxNd matrixNxN) { + int resultWidth = this.height; + int resultHeight = matrixNxN.getHeight(); + + ArrayMatrixNxNd result = new ArrayMatrixNxNd(resultWidth, resultHeight); + BitSet resultNonZero = result.nonZero(); + for (SparseBlockd sparseBlock : sparseBlocks) { + for (int x = sparseBlock.getStartY(); x < sparseBlock.getStartY() + sparseBlock.getHeight(); x++) { + for (int i = sparseBlock.getStartX(); i < sparseBlock.getStartX() + sparseBlock.getWidth(); i++) { + double cache = sparseBlock.get(i, x); + if (cache==0) + continue; + for (int y = 0; y < resultHeight; y++) { + double added = matrixNxN.get(i, y) * cache; + if (added==0) + continue; + result.add(x, y, added); + resultNonZero.set(x + y * resultWidth, true); + } + } + } + } + + return result; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/sparse/SparseMatrixf.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/sparse/SparseMatrixf.java new file mode 100644 index 0000000..21ef1ea --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/matrix/sparse/SparseMatrixf.java @@ -0,0 +1,218 @@ +package fr.radi3nt.maths.components.arbitrary.matrix.sparse; + +import fr.radi3nt.maths.components.arbitrary.MatrixNxNf; +import fr.radi3nt.maths.components.arbitrary.VectorNf; +import fr.radi3nt.maths.components.arbitrary.matrix.ArrayMatrixNxNf; +import fr.radi3nt.maths.components.arbitrary.vector.ArrayVectorNf; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collection; + +public class SparseMatrixf implements MatrixNxNf { + + private final Collection sparseBlocks; + private int width; + private int height; + + public SparseMatrixf(Collection sparseBlocks, int width, int height) { + this.sparseBlocks = sparseBlocks; + this.width = width; + this.height = height; + } + + + public SparseMatrixf() { + this(new ArrayList<>(), 0, 0); + } + + public void add(SparseBlockf sparseBlock) { + sparseBlocks.add(sparseBlock); + computeCursors(sparseBlock); + } + + private void computeCursors(SparseBlockf sparseBlock) { + width = Math.max(width, sparseBlock.getStartI() + sparseBlock.getLengthI()); + height = Math.max(height, sparseBlock.getStartJ() + sparseBlock.getLengthJ()); + } + + @Override + public float get(int x, int y) { + for (SparseBlockf sparseBlock : sparseBlocks) { + if (sparseBlock.isInBound(x, y)) { + return sparseBlock.get(x, y); + } + } + return 0; + } + + @Override + public void set(int x, int y, float value) { + throw new UnsupportedOperationException(); + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public BitSet nonZero() { + BitSet bitSet = new BitSet(width * height); + //bitSet.set(0, width*height); + + for (SparseBlockf sparseBlock : sparseBlocks) { + for (int j = sparseBlock.getStartJ(); j < sparseBlock.getStartJ() + sparseBlock.getLengthJ(); j++) { + int row = j * width; + bitSet.set(sparseBlock.getStartI() + row, sparseBlock.getStartI() + sparseBlock.getLengthI() + row); + } + } + + return bitSet; + } + + @Override + public float[] getM() { + throw new UnsupportedOperationException(); + } + + public VectorNf transform(VectorNf vector) { + VectorNf result = new ArrayVectorNf(height); + for (SparseBlockf sparseBlock : sparseBlocks) { + for (int j = sparseBlock.getStartJ(); j < sparseBlock.getStartJ() + sparseBlock.getLengthJ(); j++) { + for (int i = sparseBlock.getStartI(); i < sparseBlock.getStartI() + sparseBlock.getLengthI(); i++) { + float value = sparseBlock.get(i, j); + result.add(j, value * vector.get(i)); + } + } + } + return result; + } + + public VectorNf transformTransposed(VectorNf vector) { + VectorNf result = new ArrayVectorNf(width); + for (SparseBlockf sparseBlock : sparseBlocks) { + for (int j = sparseBlock.getStartJ(); j < sparseBlock.getStartJ() + sparseBlock.getLengthJ(); j++) { + for (int i = sparseBlock.getStartI(); i < sparseBlock.getStartI() + sparseBlock.getLengthI(); i++) { + float value = sparseBlock.get(i, j); + result.add(i, value * vector.get(j)); + } + } + } + return result; + } + + @Override + public MatrixNxNf duplicate() { + return new SparseMatrixf(new ArrayList<>(sparseBlocks), width, height); + } + + @Override + public MatrixNxNf multiply(MatrixNxNf matrixNxN) { + if (this.width != matrixNxN.getHeight()) + throw new UnsupportedOperationException("Unable to multiply matrices: " + matrixNxN.getHeight() + "!=" + this.width); + + int resultWidth = matrixNxN.getWidth(); + int resultHeight = this.height; + + ArrayMatrixNxNf result = new ArrayMatrixNxNf(resultWidth, resultHeight); + + for (SparseBlockf sparseBlock : sparseBlocks) { + for (int y = sparseBlock.getStartJ(); y < sparseBlock.getStartJ() + sparseBlock.getLengthJ(); y++) { + for (int x = sparseBlock.getStartI(); x < sparseBlock.getStartI() + sparseBlock.getLengthI(); x++) { + float total = 0; + for (int i = sparseBlock.getStartI(); i < sparseBlock.getStartI() + sparseBlock.getLengthI(); i++) { + total += sparseBlock.get(i, y) * matrixNxN.get(x, i); + } + result.set(x, y, total); + } + } + } + + /* + + for (int x = 0; x < result.getWidth(); x++) { + for (int y = 0; y < result.getHeight(); y++) { + float total = 0; + for (int i = 0; i < this.width; i++) { + total+=this.get(i, y) * matrixNxN.get(x, i); + } + result.set(x, y, total); + } + } + */ + + return result; + } + + @Override + public ArrayMatrixNxNf multiplyTransposed(MatrixNxNf matrixNxN) { + if (this.width != matrixNxN.getWidth()) + throw new UnsupportedOperationException("Unable to multiply matrices: " + matrixNxN.getWidth() + "!=" + this.width); + + int resultWidth = this.height; + int resultHeight = matrixNxN.getHeight(); + + ArrayMatrixNxNf result = new ArrayMatrixNxNf(resultWidth, resultHeight); + + for (SparseBlockf sparseBlock : sparseBlocks) { + for (int y = sparseBlock.getStartJ(); y < sparseBlock.getStartJ() + sparseBlock.getLengthJ(); y++) { + for (int x = sparseBlock.getStartI(); x < sparseBlock.getStartI() + sparseBlock.getLengthI(); x++) { + float total = 0; + for (int i = sparseBlock.getStartI(); i < sparseBlock.getStartI() + sparseBlock.getLengthI(); i++) { + total += sparseBlock.get(i, x) * + matrixNxN.get(i, y); + } + result.set(x, y, total); + } + } + } + + /* + ArrayMatrixNxN result = new ArrayMatrixNxN(resultWidth, resultHeight); + for (int x = 0; x < result.getWidth(); x++) { + for (int y = 0; y < result.getHeight(); y++) { + float total = 0; + for (int i = 0; i < this.width; i++) { + total+=this.get(i, x) * + matrixNxN.get(i, y); + } + result.set(x, y, total); + } + } + + */ + + return result; + } + + @Override + public MatrixNxNf multiplyTransposedOther(MatrixNxNf matrixNxN) { + if (this.width != matrixNxN.getWidth()) + throw new UnsupportedOperationException("Unable to multiply matrices: " + matrixNxN.getWidth() + "!=" + this.width); + + int resultWidth = matrixNxN.getHeight(); + int resultHeight = this.getHeight(); + + ArrayMatrixNxNf result = new ArrayMatrixNxNf(resultWidth, resultHeight); + + + for (SparseBlockf sparseBlock : sparseBlocks) { + for (int y = sparseBlock.getStartJ(); y < sparseBlock.getStartJ() + sparseBlock.getLengthJ(); y++) { + for (int x = sparseBlock.getStartI(); x < sparseBlock.getStartI() + sparseBlock.getLengthI(); x++) { + for (int i = 0; i < matrixNxN.getHeight(); i++) { + float total = matrixNxN.get(x, i) * sparseBlock.get(x, y); + result.set(y, i, result.get(y, i) + total); + } + } + } + } + + return result; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/vector/ArrayVectorNd.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/vector/ArrayVectorNd.java new file mode 100644 index 0000000..81a5f97 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/vector/ArrayVectorNd.java @@ -0,0 +1,79 @@ +package fr.radi3nt.maths.components.arbitrary.vector; + +import fr.radi3nt.maths.Maths; +import fr.radi3nt.maths.components.arbitrary.VectorNd; + +import java.util.Arrays; + +public class ArrayVectorNd implements VectorNd { + + private final double[] vector; + + public ArrayVectorNd(int size) { + vector = new double[size]; + } + + public ArrayVectorNd(VectorNd copy) { + vector = new double[copy.size()]; + for (int i = 0; i < copy.size(); i++) { + vector[i] = copy.get(i); + } + } + + @Override + public float length() { + throw new UnsupportedOperationException("Not implemented"); + } + + public int size() { + return vector.length; + } + + @Override + public void scalar(VectorNd kd) { + for (int i = 0; i < vector.length; i++) { + set(i, get(i) * kd.get(i)); + } + } + + @Override + public void add(VectorNd damping) { + for (int i = 0; i < vector.length; i++) { + set(i, get(i) + damping.get(i)); + } + } + + @Override + public void scalar(double v) { + for (int i = 0; i < vector.length; i++) { + set(i, get(i) * v); + } + } + + public double get(int row) { + return vector[row]; + } + + public void set(int row, double v) { + vector[row] = v; + } + + public void add(int row, double v) { + vector[row] += v; + } + + public void div(int row, double v) { + vector[row] /= v; + } + + public void clamp(int row, double min, double max) { + vector[row] = Maths.clamp(vector[row], min, max); + } + + @Override + public String toString() { + return "VectorNd{" + + "vector=" + Arrays.toString(vector) + + '}'; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/vector/ArrayVectorNf.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/vector/ArrayVectorNf.java new file mode 100644 index 0000000..ea035dd --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/vector/ArrayVectorNf.java @@ -0,0 +1,101 @@ +package fr.radi3nt.maths.components.arbitrary.vector; + +import fr.radi3nt.maths.Maths; +import fr.radi3nt.maths.components.arbitrary.OperatingVectorNf; +import fr.radi3nt.maths.components.arbitrary.VectorNf; + +import java.util.Arrays; + +public class ArrayVectorNf implements VectorNf { + + private final float[] vector; + + public ArrayVectorNf(int size) { + vector = new float[size]; + } + + public ArrayVectorNf(float... vector) { + this.vector = vector; + } + + @Override + public float length() { + float total = 0; + for (float v : vector) { + total+=v*v; + } + return (float) Math.sqrt(total); + } + + public int size() { + return vector.length; + } + + public float get(int row) { + return vector[row]; + } + + @Override + public ArrayVectorNf duplicate() { + return new ArrayVectorNf(Arrays.copyOf(vector, vector.length)); + } + + @Override + public ArrayVectorNf div(float number) { + for (int i = 0; i < vector.length; i++) { + vector[i]/=number; + } + return this; + } + + @Override + public ArrayVectorNf mul(float number) { + for (int i = 0; i < vector.length; i++) { + vector[i]*=number; + } + return this; + } + + @Override + public ArrayVectorNf add(OperatingVectorNf other) { + if (other.size()!=this.size()) + throw new IllegalArgumentException("This vector's size (" + size() +") does not match the argument vector size (" + other.size() + ")"); + for (int i = 0; i < vector.length; i++) { + vector[i]+=other.get(i); + } + return this; + } + + @Override + public ArrayVectorNf sub(OperatingVectorNf other) { + if (other.size()!=this.size()) + throw new IllegalArgumentException("This vector's size (" + size() +") does not match the argument vector size (" + other.size() + ")"); + for (int i = 0; i < vector.length; i++) { + vector[i]-=other.get(i); + } + return this; + } + + public void set(int row, float v) { + vector[row] = v; + } + + public void add(int row, float v) { + vector[row] += v; + } + + public void div(int row, float v) { + vector[row] /= v; + } + + public void clamp(int row, float min, float max) { + vector[row] = Maths.clamp(vector[row], min, max); + } + + @Override + public String toString() { + return "VectorNf{" + + "vector=" + Arrays.toString(vector) + + '}'; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/vector/ExtensibleVectorNd.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/vector/ExtensibleVectorNd.java new file mode 100644 index 0000000..d3718bb --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/vector/ExtensibleVectorNd.java @@ -0,0 +1,84 @@ +package fr.radi3nt.maths.components.arbitrary.vector; + +import fr.radi3nt.maths.Maths; +import fr.radi3nt.maths.components.arbitrary.VectorNd; + +import java.util.ArrayList; +import java.util.List; + +public class ExtensibleVectorNd implements VectorNd { + + private final List vector; + + public ExtensibleVectorNd() { + vector = new ArrayList<>(); + } + + @Override + public float length() { + throw new UnsupportedOperationException("Not implemented"); + } + + public int size() { + return vector.size(); + } + + @Override + public void scalar(VectorNd kd) { + for (int i = 0; i < vector.size(); i++) { + set(i, get(i) * kd.get(i)); + } + } + + @Override + public void add(VectorNd damping) { + for (int i = 0; i < vector.size(); i++) { + set(i, get(i) + damping.get(i)); + } + } + + @Override + public void scalar(double v) { + for (int i = 0; i < vector.size(); i++) { + set(i, get(i) * v); + } + } + + public double get(int row) { + return vector.get(row); + } + + public void set(int row, double v) { + if (row >= vector.size()) + vector.add(row, v); + else + vector.set(row, v); + } + + public void add(double v) { + vector.add(v); + } + + public void add(int row, double v) { + vector.set(row, getOrZero(row) + v); + } + + private Double getOrZero(int row) { + return row < vector.size() ? get(row) : 0; + } + + public void div(int row, double v) { + vector.set(row, getOrZero(row) / v); + } + + public void clamp(int row, double min, double max) { + vector.set(row, Maths.clamp(getOrZero(row), min, max)); + } + + @Override + public String toString() { + return "ExtensibleVectorNd{" + + "vector=" + vector + + '}'; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/vector/ExtensibleVectorNf.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/vector/ExtensibleVectorNf.java new file mode 100644 index 0000000..840f56d --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/arbitrary/vector/ExtensibleVectorNf.java @@ -0,0 +1,109 @@ +package fr.radi3nt.maths.components.arbitrary.vector; + +import fr.radi3nt.maths.Maths; +import fr.radi3nt.maths.components.arbitrary.OperatingVectorNf; +import fr.radi3nt.maths.components.arbitrary.VectorNf; + +import java.util.ArrayList; +import java.util.List; + +public class ExtensibleVectorNf implements VectorNf { + + private final List vector; + + public ExtensibleVectorNf() { + vector = new ArrayList<>(); + } + + private ExtensibleVectorNf(List vector) { + this.vector = vector; + } + + @Override + public float length() { + float total = 0; + for (float v : vector) { + total+=v*v; + } + return (float) Math.sqrt(total); + } + + public int size() { + return vector.size(); + } + + public float get(int row) { + return vector.get(row); + } + + @Override + public ExtensibleVectorNf duplicate() { + return new ExtensibleVectorNf(new ArrayList<>(vector)); + } + + @Override + public OperatingVectorNf div(float number) { + vector.replaceAll(aFloat -> aFloat/number); + return this; + } + + @Override + public OperatingVectorNf mul(float number) { + vector.replaceAll(aFloat -> aFloat*number); + return this; + } + + @Override + public OperatingVectorNf add(OperatingVectorNf other) { + if (other.size()!=this.size()) + throw new IllegalArgumentException(); + for (int i = 0; i < vector.size(); i++) { + vector.set(i, vector.get(i)+other.get(i)); + } + return this; + } + + @Override + public OperatingVectorNf sub(OperatingVectorNf other) { + if (other.size()!=this.size()) + throw new IllegalArgumentException(); + for (int i = 0; i < vector.size(); i++) { + vector.set(i, vector.get(i)-other.get(i)); + } + return this; + } + + public void set(int row, float v) { + if (row >= vector.size()) + vector.add(row, v); + else + vector.set(row, v); + } + + public void add(float v) { + vector.add(v); + } + + public void add(int row, float v) { + vector.set(row, getOrZero(row) + v); + } + + private Float getOrZero(int row) { + return row < vector.size() ? get(row) : 0; + } + + public void div(int row, float v) { + vector.set(row, getOrZero(row) / v); + } + + public void clamp(int row, float min, float max) { + vector.set(row, Maths.clamp(getOrZero(row), min, max)); + } + + @Override + public String toString() { + return "ExtensibleVectorNf{" + + "vector=" + vector + + '}'; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/matrices/Matrix.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/matrices/Matrix.java deleted file mode 100644 index 9bd9c27..0000000 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/matrices/Matrix.java +++ /dev/null @@ -1,53 +0,0 @@ -package fr.radi3nt.maths.components.matrices; - -import fr.radi3nt.maths.components.vectors.Vector3f; -import fr.radi3nt.maths.components.vectors.Vector4f; - -import java.nio.FloatBuffer; - -public interface Matrix { - - Matrix identity(); - Matrix zero(); - - Matrix translation(Vector3f vector3f); - - /** - * @param value Angle in radians - */ - Matrix rotation(float value, Vector3f axis); - /** - * @param rotation Angles in radians - */ - Matrix rotation(Vector3f rotation); - Matrix scaling(Vector3f scale); - - Matrix translate(Vector3f vector3f); - /** - * @param value Angle in radians - */ - Matrix rotate(float value, Vector3f axis); - - void rotationFromDirection(Vector3f direction, Vector3f up); - - Matrix scale(Vector3f scale); - - Matrix invert(); - Matrix transpose(); - - Vector4f transform(Vector4f vector4f); - - Matrix multiply(Matrix matrix); - - Matrix mul(Matrix matrix); - - Matrix set(int x, int y, float value); - - float get(int x, int y); - - Matrix store(FloatBuffer buffer); - - Matrix duplicate(); - - void copy(Matrix transform); -} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/matrices/PerspectiveMatrix.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/matrices/PerspectiveMatrix.java deleted file mode 100644 index 8676010..0000000 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/matrices/PerspectiveMatrix.java +++ /dev/null @@ -1,7 +0,0 @@ -package fr.radi3nt.maths.components.matrices; - -public interface PerspectiveMatrix extends Matrix { - - Matrix perspective(float fov, float aspect, float near, float far); - -} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/matrices/ViewMatrix.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/matrices/ViewMatrix.java deleted file mode 100644 index 13ec6cd..0000000 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/matrices/ViewMatrix.java +++ /dev/null @@ -1,9 +0,0 @@ -package fr.radi3nt.maths.components.matrices; - -import fr.radi3nt.maths.components.vectors.Vector3f; - -public interface ViewMatrix extends Matrix { - - ViewMatrix view(Vector3f position, Vector3f rotation); - -} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/matrices/implementation/ArrayMatrix.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/matrices/implementation/ArrayMatrix.java deleted file mode 100644 index b6bfd10..0000000 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/matrices/implementation/ArrayMatrix.java +++ /dev/null @@ -1,463 +0,0 @@ -package fr.radi3nt.maths.components.matrices.implementation; - -import fr.radi3nt.maths.components.advanced.matrix.ArrayMatrix4x4; -import fr.radi3nt.maths.components.matrices.Matrix; -import fr.radi3nt.maths.components.matrices.PerspectiveMatrix; -import fr.radi3nt.maths.components.matrices.ViewMatrix; -import fr.radi3nt.maths.components.vectors.Vector3f; -import fr.radi3nt.maths.components.vectors.Vector4f; -import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; -import fr.radi3nt.maths.components.vectors.implementations.SimpleVector4f; - -import java.nio.FloatBuffer; -import java.util.Arrays; - -public class ArrayMatrix implements Matrix, PerspectiveMatrix, ViewMatrix { - - private float[][] m; - - ArrayMatrix(float[][] m) { - this.m = m; - } - - ArrayMatrix() { - this(new float[4][4]); - identity(); - } - - public static Matrix from(ArrayMatrix4x4 arrayMatrix4x4) { - ArrayMatrix arrayMatrix = new ArrayMatrix(); - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - arrayMatrix.set(i, j, arrayMatrix4x4.get(i, j)); - } - } - return arrayMatrix; - } - - public static Matrix fromTransposing(ArrayMatrix4x4 arrayMatrix4x4) { - ArrayMatrix arrayMatrix = new ArrayMatrix(); - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - arrayMatrix.set(i, j, arrayMatrix4x4.get(j, i)); - } - } - return arrayMatrix; - } - - - @Override - public Matrix identity() { - m[0][0] = 1; m[0][1] = 0; m[0][2] = 0; m[0][3] = 0; - m[1][0] = 0; m[1][1] = 1; m[1][2] = 0; m[1][3] = 0; - m[2][0] = 0; m[2][1] = 0; m[2][2] = 1; m[2][3] = 0; - m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 1; - - return this; - } - - @Override - public Matrix zero() { - m[0][0] = 0; m[0][1] = 0; m[0][2] = 0; m[0][3] = 0; - m[1][0] = 0; m[1][1] = 0; m[1][2] = 0; m[1][3] = 0; - m[2][0] = 0; m[2][1] = 0; m[2][2] = 0; m[2][3] = 0; - m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 0; - - return this; - } - - @Override - public Matrix translation(Vector3f translation) { - identity(); - m[0][0] = 1; m[0][1] = 0; m[0][2] = 0; m[0][3] = translation.getX(); - m[1][0] = 0; m[1][1] = 1; m[1][2] = 0; m[1][3] = translation.getY(); - m[2][0] = 0; m[2][1] = 0; m[2][2] = 1; m[2][3] = translation.getZ(); - m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 1; - - return this; - } - - public static Matrix translate(Vector3f vec, Matrix src, Matrix dest) { - if (dest == null) { - dest = new ArrayMatrix(); - } - - dest.set(3, 0, dest.get(3, 0) + src.get(0, 0) * vec.getX() + src.get(1, 0) * vec.getY() + src.get(2, 0) * vec.getZ()); - dest.set(3, 1, dest.get(3, 1) + src.get(0, 1) * vec.getX() + src.get(1, 1) * vec.getY() + src.get(2, 1) * vec.getZ()); - dest.set(3, 2, dest.get(3, 2) + src.get(0, 2) * vec.getX() + src.get(1, 2) * vec.getY() + src.get(2, 2) * vec.getZ()); - dest.set(3, 3, dest.get(3, 3) + src.get(0, 3) * vec.getX() + src.get(1, 3) * vec.getY() + src.get(2, 3) * vec.getZ()); - return dest; - } - - @Override - public Matrix rotation(float value, Vector3f axis) { - identity(); - return rotate(value, axis, this, this); - } - - public static Matrix rotate(float angle, Vector3f axis, Matrix src, Matrix dest) { - if (dest == null) { - dest = new ArrayMatrix(); - } - - float c = (float) Math.cos(angle); - float s = (float) Math.sin(angle); - float oneminusc = 1.0F - c; - float xy = axis.getX() * axis.getY(); - float yz = axis.getY() * axis.getZ(); - float xz = axis.getX() * axis.getZ(); - float xs = axis.getX() * s; - float ys = axis.getY() * s; - float zs = axis.getZ() * s; - float f00 = axis.getX() * axis.getX() * oneminusc + c; - float f01 = xy * oneminusc + zs; - float f02 = xz * oneminusc - ys; - float f10 = xy * oneminusc - zs; - float f11 = axis.getY() * axis.getY() * oneminusc + c; - float f12 = yz * oneminusc + xs; - float f20 = xz * oneminusc + ys; - float f21 = yz * oneminusc - xs; - float f22 = axis.getZ() * axis.getZ() * oneminusc + c; - float t00 = src.get(0, 0) * f00 + src.get(1, 0) * f01 + src.get(2, 0) * f02; - float t01 = src.get(0, 1) * f00 + src.get(1, 1) * f01 + src.get(2, 1) * f02; - float t02 = src.get(0, 2) * f00 + src.get(1, 2) * f01 + src.get(2, 2) * f02; - float t03 = src.get(0, 3) * f00 + src.get(1, 3) * f01 + src.get(2, 3) * f02; - float t10 = src.get(0, 0) * f10 + src.get(1, 0) * f11 + src.get(2, 0) * f12; - float t11 = src.get(0, 1) * f10 + src.get(1, 1) * f11 + src.get(2, 1) * f12; - float t12 = src.get(0, 2) * f10 + src.get(1, 2) * f11 + src.get(2, 2) * f12; - float t13 = src.get(0, 3) * f10 + src.get(1, 3) * f11 + src.get(2, 3) * f12; - dest.set(2, 0, src.get(0, 0) * f20 + src.get(1, 0) * f21 + src.get(2, 0) * f22); - dest.set(2, 1, src.get(0, 1) * f20 + src.get(1, 1) * f21 + src.get(2, 1) * f22); - dest.set(2, 2, src.get(0, 2) * f20 + src.get(1, 2) * f21 + src.get(2, 2) * f22); - dest.set(2, 3, src.get(0, 3) * f20 + src.get(1, 3) * f21 + src.get(2, 3) * f22); - dest.set(0, 0, t00); - dest.set(0, 1, t01); - dest.set(0, 2, t02); - dest.set(0, 3, t03); - dest.set(1, 0, t10); - dest.set(1, 1, t11); - dest.set(1, 2, t12); - dest.set(1, 3, t13); - return dest; - } - - @Override - public Matrix rotation(Vector3f rotation) { - float x = rotation.getX(); - float y = rotation.getY(); - float z = rotation.getZ(); - - float sinX = (float) Math.sin(x); - float sinY = (float) Math.sin(y); - float sinZ = (float) Math.sin(z); - - float cosX = (float) Math.cos(x); - float cosY = (float) Math.cos(y); - float cosZ = (float) Math.cos(z); - - final float sinXsinY = sinX * sinY; - final float cosXsinY = cosX * sinY; - - m[0][0] = cosY * cosZ; - m[0][1] = cosY * sinZ; - m[0][2] = -sinY; - m[0][3] = 0f; - - m[1][0] = sinXsinY * cosZ - cosX * sinZ; - m[1][1] = sinXsinY * sinZ + cosX * cosZ; - m[1][2] = sinX * cosY; - m[1][3] = 0f; - - m[2][0] = cosXsinY * cosZ + sinX * sinZ; - m[2][1] = cosXsinY * sinZ - sinX * cosZ; - m[2][2] = cosX * cosY; - m[2][3] = 0f; - - m[3][0] = 0f; - m[3][1] = 0f; - m[3][2] = 0f; - m[3][3] = 1f; - - return this; - } - - @Override - public Matrix scaling(Vector3f scale) { - identity(); - return scale(scale, this, this); - } - - public static Matrix scale(Vector3f vec, Matrix src, Matrix dest) { - if (dest == null) { - dest = new ArrayMatrix(); - } - - dest.set(0, 0, src.get(0, 0)*vec.getX()); - dest.set(0, 1, src.get(0, 1)*vec.getX()); - dest.set(0, 2, src.get(0, 2)*vec.getX()); - dest.set(0, 3, src.get(0, 3)*vec.getX()); - - dest.set(1, 0, src.get(1, 0)*vec.getY()); - dest.set(1, 1, src.get(1, 1)*vec.getY()); - dest.set(1, 2, src.get(1, 2)*vec.getY()); - dest.set(1, 3, src.get(1, 3)*vec.getY()); - - dest.set(2, 0, src.get(2, 0)*vec.getZ()); - dest.set(2, 1, src.get(2, 1)*vec.getZ()); - dest.set(2, 2, src.get(2, 2)*vec.getZ()); - dest.set(2, 3, src.get(2, 3)*vec.getZ()); - - return dest; - } - - @Override - public Matrix translate(Vector3f vector3f) { - return translate(vector3f, this, this); - } - - @Override - public Matrix rotate(float value, Vector3f axis) { - return rotate(value, axis, this, this); - } - - @Override - public void rotationFromDirection(Vector3f direction, Vector3f up) { - Vector3f xaxis = up.duplicate().cross(direction); - xaxis.normalize(); - - Vector3f yaxis = direction.duplicate().cross(xaxis); - yaxis.normalize(); - - m[0][0] = xaxis.getX(); - m[0][1] = yaxis.getX(); - m[0][2] = direction.getX(); - - m[1][0] = xaxis.getY(); - m[1][1] = yaxis.getY(); - m[1][2] = direction.getY(); - - m[2][0] = xaxis.getZ(); - m[2][1] = yaxis.getZ(); - m[2][2] = direction.getZ(); - } - - @Override - public Matrix scale(Vector3f scale) { - return scale(scale, this, this); - } - - @Override - public Vector4f transform(Vector4f vector4f) { - float x = this.m[0][0] * vector4f.getX() + this.m[1][0] * vector4f.getY() + this.m[2][0] * vector4f.getZ() + this.m[3][0] * vector4f.getW(); - float y = this.m[0][1] * vector4f.getX() + this.m[1][1] * vector4f.getY() + this.m[2][1] * vector4f.getZ() + this.m[3][1] * vector4f.getW(); - float z = this.m[0][2] * vector4f.getX() + this.m[1][2] * vector4f.getY() + this.m[2][2] * vector4f.getZ() + this.m[3][2] * vector4f.getW(); - float w = this.m[0][3] * vector4f.getX() + this.m[1][3] * vector4f.getY() + this.m[2][3] * vector4f.getZ() + this.m[3][3] * vector4f.getW(); - return new SimpleVector4f(x, y, z, w); - } - - @Override - public Matrix invert() { - float s0 = get(0, 0) * get(1, 1) - get(1, 0) * get(0, 1); - float s1 = get(0, 0) * get(1, 2) - get(1, 0) * get(0, 2); - float s2 = get(0, 0) * get(1, 3) - get(1, 0) * get(0, 3); - float s3 = get(0, 1) * get(1, 2) - get(1, 1) * get(0, 2); - float s4 = get(0, 1) * get(1, 3) - get(1, 1) * get(0, 3); - float s5 = get(0, 2) * get(1, 3) - get(1, 2) * get(0, 3); - - float c5 = get(2, 2) * get(3, 3) - get(3, 2) * get(2, 3); - float c4 = get(2, 1) * get(3, 3) - get(3, 1) * get(2, 3); - float c3 = get(2, 1) * get(3, 2) - get(3, 1) * get(2, 2); - float c2 = get(2, 0) * get(3, 3) - get(3, 0) * get(2, 3); - float c1 = get(2, 0) * get(3, 2) - get(3, 0) * get(2, 2); - float c0 = get(2, 0) * get(3, 1) - get(3, 0) * get(2, 1); - - - float div = (s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0); - if (div == 0) System.err.println("not invertible"); - - float invdet = 1.0f / div; - - ArrayMatrix invM = new ArrayMatrix(); - - invM.set(0, 0, (get(1, 1) * c5 - get(1, 2) * c4 + get(1, 3) * c3) * invdet); - invM.set(0, 1, (-get(0, 1) * c5 + get(0, 2) * c4 - get(0, 3) * c3) * invdet); - invM.set(0, 2, (get(3, 1) * s5 - get(3, 2) * s4 + get(3, 3) * s3) * invdet); - invM.set(0, 3, (-get(2, 1) * s5 + get(2, 2) * s4 - get(2, 3) * s3) * invdet); - - invM.set(1, 0, (-get(1, 0) * c5 + get(1, 2) * c2 - get(1, 3) * c1) * invdet); - invM.set(1, 1, (get(0, 0) * c5 - get(0, 2) * c2 + get(0, 3) * c1) * invdet); - invM.set(1, 2, (-get(3, 0) * s5 + get(3, 2) * s2 - get(3, 3) * s1) * invdet); - invM.set(1, 3, (get(2, 0) * s5 - get(2, 2) * s2 + get(2, 3) * s1) * invdet); - - invM.set(2, 0, (get(1, 0) * c4 - get(1, 1) * c2 + get(1, 3) * c0) * invdet); - invM.set(2, 1, (-get(0, 0) * c4 + get(0, 1) * c2 - get(0, 3) * c0) * invdet); - invM.set(2, 2, (get(3, 0) * s4 - get(3, 1) * s2 + get(3, 3) * s0) * invdet); - invM.set(2, 3, (-get(2, 0) * s4 + get(2, 1) * s2 - get(2, 3) * s0) * invdet); - - invM.set(3, 0, (-get(1, 0) * c3 + get(1, 1) * c1 - get(1, 2) * c0) * invdet); - invM.set(3, 1, (get(0, 0) * c3 - get(0, 1) * c1 + get(0, 2) * c0) * invdet); - invM.set(3, 2, (-get(3, 0) * s3 + get(3, 1) * s1 - get(3, 2) * s0) * invdet); - invM.set(3, 3, (get(2, 0) * s3 - get(2, 1) * s1 + get(2, 2) * s0) * invdet); - - this.m = invM.getM(); - - return this; - } - - @Override - public Matrix transpose() { - ArrayMatrix result = new ArrayMatrix(); - - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - result.set(i, j, get(j, i)); - } - } - - this.m = result.getM(); - - return result; - } - - @Override - public Matrix multiply(Matrix matrix) { - ArrayMatrix res = new ArrayMatrix(); - - multiply(matrix, res); - - m = res.getM(); - - return this; - } - - @Override - public Matrix mul(Matrix matrix) { - Matrix res = new ArrayMatrix(); - - multiply(matrix, res); - - return res; - } - - private void multiply(Matrix matrix, Matrix res) { - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - res.set(i, j, this.get(i, 0) * matrix.get(0, j) + - this.get(i, 1) * matrix.get(1, j) + - this.get(i, 2) * matrix.get(2, j) + - this.get(i, 3) * matrix.get(3, j)); - } - } - } - - @Override - public Matrix perspective(float fov, float aspect, float near, float far) { - identity(); - - float y_scale = (float) ((1f / Math.tan(Math.toRadians(fov / 2f)))); - float frustum_length = far - near; - - set(0, 0, y_scale); - set(1, 1, y_scale*aspect); - set(2, 2, -((far + near) / frustum_length)); - set(2, 3, -1); - set(3, 2, -((2 * near * far) / frustum_length)); - set(3, 3, 0); - - return this; - } - - @Override - public ViewMatrix view(Vector3f position, Vector3f rotation) { //todo rotation no need to negate ? - - Matrix rotX = MatrixCreator.createMatrix().rotation(-rotation.getX(), new SimpleVector3f(1, 0, 0)); - Matrix rotY = MatrixCreator.createMatrix().rotation(-rotation.getY(), new SimpleVector3f(0, 1, 0)); - Matrix rotZ = MatrixCreator.createMatrix().rotation(-rotation.getZ(), new SimpleVector3f(0, 0, 1)); - - Matrix translate = MatrixCreator.createMatrix().translation(new SimpleVector3f(-position.getX(), -position.getY(), -position.getZ())); - - identity(); - this.multiply(rotX.mul(rotY).mul(rotZ).mul(translate)); - - return this; - } - - @Override - public Matrix store(FloatBuffer buffer) { - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - buffer.put(m[i][j]); - } - } - return this; - } - - @Override - public Matrix set(int x, int y, float value) { - m[x][y]=value; - return this; - } - - @Override - public float get(int x, int y) { - return m[x][y]; - } - - public float[][] getM() { - return m; - } - - public void setM(float[][] m) { - this.m = m; - } - - @Override - public ViewMatrix duplicate() { - float[][] m2 = new float[4][4]; - - for (int x = 0; x < m.length; x++) { - System.arraycopy(m[x], 0, m2[x], 0, m[x].length); - } - - return new ArrayMatrix(m2); - } - - @Override - public void copy(Matrix transform) { - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - set(i, j, transform.get(i, j)); - } - } - } - - @Override - public String toString() { - StringBuilder stringBuilder = new StringBuilder("ArrayMatrix{" + - "\n"); - - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - stringBuilder.append(m[i][j]).append(" "); - } - stringBuilder.append("\n"); - } - - stringBuilder.append("}"); - - return stringBuilder.toString(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ArrayMatrix that = (ArrayMatrix) o; - return Arrays.deepEquals(m, that.m); - } - - @Override - public int hashCode() { - return Arrays.deepHashCode(m); - } -} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/matrices/implementation/MatrixCreator.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/matrices/implementation/MatrixCreator.java deleted file mode 100644 index 20461a2..0000000 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/matrices/implementation/MatrixCreator.java +++ /dev/null @@ -1,20 +0,0 @@ -package fr.radi3nt.maths.components.matrices.implementation; - -import fr.radi3nt.maths.components.matrices.Matrix; -import fr.radi3nt.maths.components.matrices.PerspectiveMatrix; -import fr.radi3nt.maths.components.matrices.ViewMatrix; - -public class MatrixCreator { - - public static Matrix createMatrix() { - return new ArrayMatrix(); - } - - public static PerspectiveMatrix createProjection() { - return new ArrayMatrix(); - } - - public static ViewMatrix createView() { - return new ArrayMatrix(); - } -} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/Vector2f.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/Vector2f.java index f7e61da..74524aa 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/Vector2f.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/Vector2f.java @@ -1,6 +1,8 @@ package fr.radi3nt.maths.components.vectors; -public interface Vector2f extends Vector { +import fr.radi3nt.maths.components.arbitrary.OperatingVectorNf; + +public interface Vector2f extends Vector, OperatingVectorNf { float getX(); void setX(float x); @@ -22,8 +24,13 @@ public interface Vector2f extends Vector { Vector2f div(float mul); Vector2f clone(); + Vector2f duplicate(); float dot(Vector2f vector2f); Vector2f normalize(); + Vector2f normalizeSafely(); + + float lengthSquared(); + void copy(Vector2f vector2f); } diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/Vector3f.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/Vector3f.java index cc6e63f..6996dee 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/Vector3f.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/Vector3f.java @@ -1,6 +1,8 @@ package fr.radi3nt.maths.components.vectors; -public interface Vector3f extends Vector { +import fr.radi3nt.maths.components.arbitrary.OperatingVectorNf; + +public interface Vector3f extends Vector, OperatingVectorNf { float getX(); @@ -47,4 +49,14 @@ public interface Vector3f extends Vector { Vector3f negate(); float lengthSquared(); + + void copy(Vector3f vert1); + + void set(Vector3f set); + + void add(int row, float value); + + float get(int row); + + Vector3f normalizeSafely(); } diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/Vector4f.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/Vector4f.java index 319e986..c392041 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/Vector4f.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/Vector4f.java @@ -1,6 +1,8 @@ package fr.radi3nt.maths.components.vectors; -public interface Vector4f extends Vector { +import fr.radi3nt.maths.components.arbitrary.OperatingVectorNf; + +public interface Vector4f extends Vector, OperatingVectorNf { float getX(); void setX(float x); @@ -11,6 +13,15 @@ public interface Vector4f extends Vector { float getW(); void setW(float w); + Vector4f div(float div); + float dot(Vector4f other); + void normalize(); - + + void set(Vector3f vector3f, float w); + void set(float x, float y, float z, float w); + void mul(Vector4f other); + + Vector4f duplicate(); + } diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/implementations/SimpleVector2f.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/implementations/SimpleVector2f.java index 21acfe8..dbb6e8a 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/implementations/SimpleVector2f.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/implementations/SimpleVector2f.java @@ -1,5 +1,7 @@ package fr.radi3nt.maths.components.vectors.implementations; +import fr.radi3nt.maths.components.Vector2D; +import fr.radi3nt.maths.components.arbitrary.OperatingVectorNf; import fr.radi3nt.maths.components.vectors.Vector2f; import java.util.Objects; @@ -17,6 +19,27 @@ public SimpleVector2f(float x, float y) { public SimpleVector2f() { } + public SimpleVector2f(Vector2D vector) { + this.x = (float) vector.getX(); + this.y = (float) vector.getY(); + } + + public SimpleVector2f(Vector2f vector) { + this.copy(vector); + } + + public static float distanceSquared(Vector2f first, Vector2f second) { + float x = (first.getX()-second.getX()); + float y = (first.getY()-second.getY()); + return x*x + y*y; + } + + public static float distance(float x, float y, float xo, float yo) { + float xd = x-xo; + float yd = y-yo; + return (float) Math.sqrt(xd*xd+yd*yd); + } + @Override public float getX() { return x; @@ -78,6 +101,21 @@ public Vector2f mul(float mul) { return mul(mul, mul); } + @Override + public OperatingVectorNf add(OperatingVectorNf other) { + return add(other.get(0), other.get(1)); + } + + @Override + public OperatingVectorNf sub(OperatingVectorNf other) { + return sub(other.get(0), other.get(1)); + } + + @Override + public int size() { + return 2; + } + @Override public Vector2f div(Vector2f vector2f) { return div(vector2f.getX(), vector2f.getY()); @@ -90,6 +128,18 @@ public Vector2f div(float x, float y) { return this; } + @Override + public float get(int row) { + if (row>1 || row<0) + throw new UnsupportedOperationException(); + return row == 0 ? x : y; + } + + @Override + public Vector2f duplicate() { + return clone(); + } + @Override public Vector2f div(float mul) { return div(mul, mul); @@ -110,9 +160,35 @@ public Vector2f normalize() { return div(length()); } + @Override + public Vector2f normalizeSafely() { + float length = length(); + if (length==0) + mul(0); + else + div(length); + return this; + } + + @Override + public float lengthSquared() { + return x * x + y * y; + } + + @Override + public void copy(Vector2f vector2f) { + this.setX(vector2f.getX()); + this.setY(vector2f.getY()); + } + @Override public float length() { - return (float) Math.sqrt(x * x + y * y); + return (float) Math.sqrt(lengthSquared()); + } + + @Override + public void copy(OperatingVectorNf other) { + this.copy(new SimpleVector2f(other.get(0), other.get(1))); } @Override diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/implementations/SimpleVector3f.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/implementations/SimpleVector3f.java index d02e2c6..bc100f4 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/implementations/SimpleVector3f.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/implementations/SimpleVector3f.java @@ -1,8 +1,10 @@ package fr.radi3nt.maths.components.vectors.implementations; import fr.radi3nt.maths.components.Vector3D; +import fr.radi3nt.maths.components.arbitrary.OperatingVectorNf; import fr.radi3nt.maths.components.vectors.Vector3f; import fr.radi3nt.maths.components.vectors.Vector3i; +import fr.radi3nt.maths.components.vectors.Vector4f; import java.util.Objects; @@ -33,6 +35,25 @@ public SimpleVector3f(Vector3D multiply) { this((float) multiply.getX(), (float) multiply.getY(), (float) multiply.getZ()); } + public SimpleVector3f(Vector4f vector4f) { + this(vector4f.getX(), vector4f.getY(), vector4f.getZ()); + } + + public SimpleVector3f(double v, double v1, double v2) { + this((float) v, (float) v1, (float) v2); + } + + public static float distanceSquared(Vector3f first, Vector3f second) { + float x = (first.getX()-second.getX()); + float y = (first.getY()-second.getY()); + float z = (first.getZ()-second.getZ()); + return x*x + y*y + z*z; + } + + public static float distance(Vector3f first, Vector3f second) { + return (float) Math.sqrt(distanceSquared(first, second)); + } + @Override public float getX() { return x; @@ -114,6 +135,31 @@ public Vector3f mul(float mul) { return mul(mul, mul, mul); } + @Override + public OperatingVectorNf add(OperatingVectorNf other) { + if (other.size()!=this.size()) + throw new IllegalArgumentException(); + this.x += other.get(0); + this.y += other.get(1); + this.z += other.get(2); + return this; + } + + @Override + public OperatingVectorNf sub(OperatingVectorNf other) { + if (other.size()!=this.size()) + throw new IllegalArgumentException(); + this.x -= other.get(0); + this.y -= other.get(1); + this.z -= other.get(2); + return this; + } + + @Override + public int size() { + return 3; + } + @Override public Vector3f div(Vector3f vector2f) { return div(vector2f.getX(), vector2f.getY(), vector2f.getZ()); @@ -173,11 +219,59 @@ public float lengthSquared() { return x * x + y * y + z * z; } + @Override + public void copy(Vector3f vert1) { + this.set(vert1.getX(), vert1.getY(), vert1.getZ()); + } + + @Override + public void set(Vector3f set) { + this.set(set.getX(), set.getY(), set.getZ()); + } + + @Override + public void add(int row, float value) { + if (row == 0) + x += value; + if (row == 1) + y += value; + if (row == 2) + z += value; + } + + @Override + public float get(int row) { + if (row == 0) + return x; + if (row == 1) + return y; + if (row == 2) + return z; + throw new IllegalArgumentException(); + } + + @Override + public Vector3f normalizeSafely() { + if (this.lengthSquared()!=0) + this.normalize(); + return this; + } + @Override public float length() { return (float) Math.sqrt(x * x + y * y + z * z); } + @Override + public void copy(OperatingVectorNf other) { + if (other.size()!=this.size()) { + throw new IllegalArgumentException("Other vector doesn't have the same size"); + } + x = other.get(0); + y = other.get(1); + z = other.get(2); + } + @Override public String toString() { return "SimpleVector3f{" + diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/implementations/SimpleVector3i.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/implementations/SimpleVector3i.java index f45407a..004b531 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/implementations/SimpleVector3i.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/implementations/SimpleVector3i.java @@ -1,5 +1,6 @@ package fr.radi3nt.maths.components.vectors.implementations; +import fr.radi3nt.maths.components.vectors.Vector3f; import fr.radi3nt.maths.components.vectors.Vector3i; public class SimpleVector3i implements Vector3i { @@ -21,6 +22,16 @@ public SimpleVector3i(Vector3i vector3f) { public SimpleVector3i() { } + public SimpleVector3i(Vector3f position) { + this.x = (int) Math.floor(position.getX()); + this.y = (int) Math.floor(position.getY()); + this.z = (int) Math.floor(position.getZ()); + } + + public static Vector3i fromRound(Vector3f start) { + return new SimpleVector3i(Math.round(start.getX()), Math.round(start.getY()), Math.round(start.getZ())); + } + @Override public int getX() { return x; @@ -194,11 +205,20 @@ public boolean equals(Object o) { @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + x; - result = prime * result + y; - result = prime * result + z; - return result; + int seed = 3; + { + seed = getSeed(this.x, seed); + seed = getSeed(this.y, seed); + seed = getSeed(this.z, seed); + } + return seed; + } + + private static int getSeed(int number, int seed) { + int x = ((number >> 16) ^ number) * 0x45d9f3b; + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = (x >> 16) ^ x; + seed ^= x + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; } } diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/implementations/SimpleVector4f.java b/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/implementations/SimpleVector4f.java index 2f45f54..48ef9fe 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/implementations/SimpleVector4f.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/components/vectors/implementations/SimpleVector4f.java @@ -1,5 +1,8 @@ package fr.radi3nt.maths.components.vectors.implementations; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.arbitrary.OperatingVectorNf; +import fr.radi3nt.maths.components.vectors.Vector3f; import fr.radi3nt.maths.components.vectors.Vector4f; public class SimpleVector4f implements Vector4f { @@ -16,9 +19,34 @@ public SimpleVector4f(float x, float y, float z, float w) { this.w = w; } + public SimpleVector4f(Vector3f vec, float w) { + this.x = vec.getX(); + this.y = vec.getY(); + this.z = vec.getZ(); + this.w = w; + } + + public SimpleVector4f(float vec, float w) { + this.x = vec; + this.y = vec; + this.z = vec; + this.w = w; + } + public SimpleVector4f() { } + public SimpleVector4f(Quaternion quaternion) { + this(quaternion.getX(), quaternion.getY(), quaternion.getZ(), quaternion.getW()); + } + + public static Vector4f fromAbs(Quaternion quaternion) { + Vector4f vector = new SimpleVector4f(quaternion); + if (quaternion.getW()<0) + vector.mul(-1); + return vector; + } + @Override public float getX() { return x; @@ -64,11 +92,105 @@ public void normalize() { div(length()); } - private void div(float length) { + @Override + public void set(Vector3f vector3f, float w) { + setX(vector3f.getX()); + setY(vector3f.getY()); + setZ(vector3f.getZ()); + setW(w); + } + + @Override + public void set(float x, float y, float z, float w) { + setX(x); + setY(y); + setZ(z); + setW(w); + } + + @Override + public void mul(Vector4f other) { + this.x *= other.getX(); + this.y *= other.getY(); + this.z *= other.getZ(); + this.w *= other.getW(); + } + + @Override + public float get(int row) { + if (row==0) + return x; + if (row==1) + return y; + if (row==2) + return z; + if (row==3) + return w; + throw new IllegalArgumentException(); + } + + @Override + public Vector4f duplicate() { + return new SimpleVector4f(x, y, z, w); + } + + public Vector4f div(float length) { this.setX(getX()/length); this.setY(getY()/length); this.setZ(getZ()/length); this.setW(getW()/length); + return this; + } + + @Override + public float dot(Vector4f other) { + return x * other.getX() + y * other.getY() + z * other.getZ() + w * other.getW(); + } + + @Override + public OperatingVectorNf mul(float number) { + this.setX(getX()*number); + this.setY(getY()*number); + this.setZ(getZ()*number); + this.setW(getW()*number); + return this; + } + + @Override + public OperatingVectorNf add(OperatingVectorNf other) { + if (other.size()!=this.size()) + throw new IllegalArgumentException(); + this.x += other.get(0); + this.y += other.get(1); + this.z += other.get(2); + this.w += other.get(3); + return this; + } + + @Override + public OperatingVectorNf sub(OperatingVectorNf other) { + if (other.size()!=this.size()) + throw new IllegalArgumentException(); + this.x -= other.get(0); + this.y -= other.get(1); + this.z -= other.get(2); + this.w -= other.get(3); + return this; + } + + public Vector4f sub(Vector4f other) { + if (other.size()!=this.size()) + throw new IllegalArgumentException(); + this.x -= other.getX(); + this.y -= other.getY(); + this.z -= other.getZ(); + this.w -= other.getW(); + return this; + } + + @Override + public int size() { + return 4; } @Override @@ -76,6 +198,17 @@ public float length() { return (float) Math.sqrt(x * x + y * y + z * z + w * w); } + @Override + public void copy(OperatingVectorNf other) { + if (other.size()!=this.size()) { + throw new IllegalArgumentException("Other vector doesn't have the same size"); + } + x = other.get(0); + y = other.get(1); + z = other.get(2); + w = other.get(3); + } + @Override public String toString() { return "SimpleVector4f{" + diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/SmoothTransform.java b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/SmoothTransform.java new file mode 100644 index 0000000..0c8fd1b --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/SmoothTransform.java @@ -0,0 +1,28 @@ +package fr.radi3nt.maths.dynamics; + +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; + +public interface SmoothTransform

{ + + Vector3f getTargetPosition(); + Quaternion getTargetOrientation(); + + Vector3f getCurrentPosition(); + Quaternion getCurrentOrientation(); + + void setCurrentPosition(Vector3f position); + void setCurrentOrientation(Quaternion orientation); + + void setTargetPosition(Vector3f position); + void setTargetOrientation(Quaternion orientation); + + void setLinearProperties(P linear); + void setAngularProperties(P angular); + default void setAllProperties(P properties) { + setLinearProperties(properties); + setAngularProperties(properties); + } + + void update(float delta); +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/AdvancedSmoothTransform.java b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/AdvancedSmoothTransform.java new file mode 100644 index 0000000..70a65db --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/AdvancedSmoothTransform.java @@ -0,0 +1,141 @@ +package fr.radi3nt.maths.dynamics.advanced; + +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.Vector4f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector4f; +import fr.radi3nt.maths.dynamics.SmoothTransform; + +public class AdvancedSmoothTransform implements SmoothTransform { + + protected final VectorSmoothDynamics positionDynamics; + protected final VectorRotationSmoothDynamics rotationDynamics; + + public final Vector3f targetPosition = new SimpleVector3f(); + public final Quaternion targetRotation = ComponentsQuaternion.zero(); + + private final Vector3f position = new SimpleVector3f(); + private final Quaternion rotation = ComponentsQuaternion.zero(); + + public AdvancedSmoothTransform(DynamicsConstants constants) { + this.positionDynamics = new VectorSmoothDynamics<>(constants, new SimpleVector3f()); + this.rotationDynamics = new VectorRotationSmoothDynamics(constants, new SimpleVector4f(0, 0, 0, 1), true); + } + + public AdvancedSmoothTransform(DynamicsConstants position, DynamicsConstants rotation) { + this.positionDynamics = new VectorSmoothDynamics<>(position, new SimpleVector3f()); + this.rotationDynamics = new VectorRotationSmoothDynamics(rotation, new SimpleVector4f(0, 0, 0, 1), true); + } + + public AdvancedSmoothTransform(Vector3f position, Quaternion rotation, DynamicsConstants positionConstants, DynamicsConstants rotationConstants) { + targetPosition.copy(position); + targetRotation.copy(rotation); + + this.position.copy(position); + this.rotation.copy(rotation); + + this.positionDynamics = new VectorSmoothDynamics<>(positionConstants, targetPosition); + this.rotationDynamics = new VectorRotationSmoothDynamics(rotationConstants, new SimpleVector4f(targetRotation), true); + } + + public void reset(Vector3f pos, Quaternion rotation) { + this.targetPosition.copy(pos); + this.position.copy(pos); + + this.rotation.copy(rotation); + this.targetRotation.copy(rotation); + + this.positionDynamics.reset(targetPosition); + Vector4f currentRot = new SimpleVector4f(rotation); + this.rotationDynamics.reset(currentRot); + } + + @Override + public Vector3f getTargetPosition() { + return targetPosition; + } + + @Override + public Quaternion getTargetOrientation() { + return targetRotation; + } + + @Override + public Vector3f getCurrentPosition() { + return position; + } + + @Override + public Quaternion getCurrentOrientation() { + return rotation; + } + + @Override + public void setCurrentPosition(Vector3f position) { + this.positionDynamics.setCurrent(position); + } + + @Override + public void setCurrentOrientation(Quaternion orientation) { + this.rotationDynamics.setCurrent(new SimpleVector4f(orientation)); + } + + @Override + public void setTargetPosition(Vector3f position) { + this.targetPosition.copy(position); + } + + @Override + public void setTargetOrientation(Quaternion orientation) { + this.targetRotation.copy(orientation); + } + + @Override + public void setLinearProperties(DynamicsConstants linear) { + positionDynamics.setConstants(linear); + } + + @Override + public void setAngularProperties(DynamicsConstants angular) { + rotationDynamics.setConstants(angular); + } + + public void update(float delta) { + positionDynamics.setInputCurrent(targetPosition); + rotationDynamics.setInputCurrent(new SimpleVector4f(targetRotation)); + + positionDynamics.update(delta); + rotationDynamics.update(delta); + + this.position.copy(positionDynamics.getResponse()); + this.rotation.copy(rotationDynamics.getResponse()); + } + + public Vector3f getLinearVelocity() { + return positionDynamics.getResponseDerivative(); + } + + public Vector3f getPosition() { + return position; + } + + public Quaternion getRotation() { + return rotation; + } + + public void setConstants(DynamicsConstants constants) { + positionDynamics.setConstants(constants); + rotationDynamics.setConstants(constants); + } + + public void setPositionConstants(DynamicsConstants constants) { + positionDynamics.setConstants(constants); + } + + public void setRotationConstants(DynamicsConstants constants) { + rotationDynamics.setConstants(constants); + } + +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/DirectionRotationSmoothDynamics.java b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/DirectionRotationSmoothDynamics.java new file mode 100644 index 0000000..9cae97e --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/DirectionRotationSmoothDynamics.java @@ -0,0 +1,16 @@ +package fr.radi3nt.maths.dynamics.advanced; + +import fr.radi3nt.maths.components.vectors.Vector3f; + +public class DirectionRotationSmoothDynamics extends VectorSmoothDynamics { + + public DirectionRotationSmoothDynamics(DynamicsConstants constants, Vector3f start) { + super(constants, start); + } + + @Override + public void update(float step) { + super.update(step); + response.normalizeSafely(); + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/DynamicsConstants.java b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/DynamicsConstants.java new file mode 100644 index 0000000..9424452 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/DynamicsConstants.java @@ -0,0 +1,70 @@ +package fr.radi3nt.maths.dynamics.advanced; + +import static java.lang.Math.PI; +import static java.lang.Math.abs; + +public class DynamicsConstants { + + private final float k1; + private final float k2; + private final float k3; + + private final float w; + private final float z; + private final float d; + + public DynamicsConstants(float k1, float k2, float k3, float w, float z, float d) { + this.k1 = k1; + this.k2 = k2; + this.k3 = k3; + this.w = w; + this.z = z; + this.d = d; + } + + /** + * @param f frequency, speed at which the system responds to inputs + * @param z damping coefficient, it controls the vibration + * @param r initial response, + *

    + *
  • r>0 imply that the system reacts instantly
  • + *
  • r>1 overshoots the input
  • + *
  • r<0 anticipate the motion
  • + *
+ */ + public static DynamicsConstants from(float f, float z, float r) { + float w = (float) (2f*PI*f); + + float k1 = (float) (z/(PI * f)); + float k2 = 1f/ (w*w); + float k3 = r * z / w; + + float d = (float) (w * Math.sqrt(abs(z*z-1))); + + return new DynamicsConstants(k1, k2, k3, w, z, d); + } + + public float getW() { + return w; + } + + public float getZ() { + return z; + } + + public float getD() { + return d; + } + + public float getK1() { + return k1; + } + + public float getK2() { + return k2; + } + + public float getK3() { + return k3; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/FloatSmoothDynamics.java b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/FloatSmoothDynamics.java new file mode 100644 index 0000000..7b31f06 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/FloatSmoothDynamics.java @@ -0,0 +1,79 @@ +package fr.radi3nt.maths.dynamics.advanced; + +public class FloatSmoothDynamics { + + private DynamicsConstants constants; + protected float inputPrevious, inputCurrent; + protected float response, responseDerivative; + + public FloatSmoothDynamics(DynamicsConstants constants, float start) { + this.constants = constants; + this.response = start; + this.inputCurrent = start; + this.inputPrevious = start; + responseDerivative = 0; + } + + public void update(float step) { + if (step==0) + return; + float inputDerivative = (inputCurrent - inputPrevious)/step; + inputPrevious = inputCurrent; + float k1Stable, k2Stable; + if (systemIsAtHighSpeed(constants, step)) { + k1Stable = constants.getK1(); + k2Stable = Math.max(constants.getK2(), Math.max(step*step/2 + step*k1Stable/2, step*k1Stable)); + } else { + k1Stable = constants.getK1(); + k2Stable = Math.max(constants.getK2(), Math.max(step*step/2 + step*k1Stable/2, step*k1Stable)); + } + + response+=responseDerivative*step; + float multipliedByTTerm = (inputDerivative*constants.getK3()+inputCurrent-response)*step; + responseDerivative = (multipliedByTTerm+responseDerivative*k2Stable)/(k2Stable+step*k1Stable); + } + + public float getResponseDerivative() { + return responseDerivative; + } + + public void setInputCurrent(float inputCurrent) { + this.inputCurrent = inputCurrent; + } + + public float getResponse() { + return response; + } + + public float getInputCurrent() { + return inputCurrent; + } + + private boolean systemIsAtHighSpeed(DynamicsConstants constants, float step) { + return constants.getW()*step >= constants.getZ(); + } + + public void setConstants(DynamicsConstants constants) { + this.constants = constants; + } + + public void setCurrent(float currentLocalPos) { + response = currentLocalPos; + responseDerivative = 0; + } + + public void setCurrentBypass(float currentLocalPos) { + response = currentLocalPos; + } + + public void setCurrentAll(float currentLocalPos, float responseDerivative) { + response = currentLocalPos; + inputCurrent = currentLocalPos; + inputPrevious = currentLocalPos; + this.responseDerivative = responseDerivative; + } + + public void setResponseDerivative(float derivative) { + this.responseDerivative = derivative; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/VectorRotationSmoothDynamics.java b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/VectorRotationSmoothDynamics.java new file mode 100644 index 0000000..6423d16 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/VectorRotationSmoothDynamics.java @@ -0,0 +1,32 @@ +package fr.radi3nt.maths.dynamics.advanced; + +import fr.radi3nt.maths.components.vectors.Vector4f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector4f; + +public class VectorRotationSmoothDynamics extends VectorSmoothDynamics { + + private final boolean loop; + + public VectorRotationSmoothDynamics(DynamicsConstants constants, Vector4f start) { + this(constants, start, true); + } + + public VectorRotationSmoothDynamics(DynamicsConstants constants, Vector4f start, boolean loop) { + super(constants, start); + this.loop = loop; + } + + @Override + public void update(float step) { + if (loop && inputPrevious.dot(inputCurrent)<0) { + inputCurrent.mul(-1); + } + + super.update(step); + response.normalize(); + } + + public void setCurrentBypass(SimpleVector4f current) { + this.response = current; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/VectorSmoothDynamics.java b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/VectorSmoothDynamics.java new file mode 100644 index 0000000..bc83338 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/advanced/VectorSmoothDynamics.java @@ -0,0 +1,109 @@ +package fr.radi3nt.maths.dynamics.advanced; + +import fr.radi3nt.maths.components.arbitrary.OperatingVectorNf; + +public class VectorSmoothDynamics { + + private DynamicsConstants constants; + protected T inputPrevious, inputCurrent; + protected T response, responseDerivative; + + private final T inputVelocity; + private final T inputDerivativeCache; + private final T responseDerivativeCache; + + private float speed = 0; + + public VectorSmoothDynamics(DynamicsConstants constants, T start) { + this.constants = constants; + this.response = (T) start.duplicate(); + this.inputCurrent = (T) start.duplicate(); + this.inputPrevious = (T) start.duplicate(); + responseDerivative = (T) start.duplicate().mul(0); + inputDerivativeCache = (T) start.duplicate().mul(0); + inputVelocity = (T) start.duplicate().mul(0); + responseDerivativeCache = (T) start.duplicate().mul(0); + } + + public void update(float step) { + if (step==0) + return; + inputDerivativeCache.copy(inputCurrent); + inputDerivativeCache.sub(inputPrevious).div(step); + inputDerivativeCache.add(inputVelocity); + inputVelocity.mul(0); + + inputPrevious.copy(inputCurrent); + float k1Stable, k2Stable; + if (systemIsAtHighSpeed(constants, step)) { + k1Stable = constants.getK1(); + k2Stable = Math.max(constants.getK2(), Math.max(step*step/2 + step*k1Stable/2, step*k1Stable)); //todo use pole matching + } else { + k1Stable = constants.getK1(); + k2Stable = Math.max(constants.getK2(), Math.max(step*step/2 + step*k1Stable/2, step*k1Stable)); + } + + responseDerivativeCache.copy(responseDerivative); + response.add(responseDerivativeCache.mul(step)); + responseDerivativeCache.copy(responseDerivative); + + OperatingVectorNf multipliedByTTerm = inputDerivativeCache.mul(constants.getK3()).add(inputCurrent).sub(response).mul(step); + responseDerivative.copy(multipliedByTTerm.add(responseDerivativeCache.mul(k2Stable)).div(k2Stable+step*k1Stable)); + speed = responseDerivative.length(); + } + + public T getResponseDerivative() { + return responseDerivative; + } + + public float getSpeed() { + return speed; + } + + public void setInputCurrent(T inputCurrent) { + this.inputCurrent = inputCurrent; + } + + public T getInputCurrent() { + return inputCurrent; + } + + public T getResponse() { + return response; + } + + private boolean systemIsAtHighSpeed(DynamicsConstants constants, float step) { + return constants.getW()*step >= constants.getZ(); + } + + public void setConstants(DynamicsConstants constants) { + this.constants = constants; + } + + public void setCurrent(T currentLocalPos) { + response = currentLocalPos; + responseDerivative = (T) currentLocalPos.duplicate().mul(0); + speed = 0; + } + + public void reset(T currentLocalPos) { + response.copy(currentLocalPos); + inputCurrent.copy(currentLocalPos); + inputPrevious.copy(currentLocalPos); + + responseDerivative = (T) currentLocalPos.duplicate().mul(0); + speed = 0; + } + + public T getInputPrevious() { + return inputPrevious; + } + + public T getInputVelocity() { + return inputVelocity; + } + + public void setInputPrevious(T inputPrevious) { + this.inputPrevious = inputPrevious; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/DynamicValueFactory.java b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/DynamicValueFactory.java new file mode 100644 index 0000000..69c8362 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/DynamicValueFactory.java @@ -0,0 +1,8 @@ +package fr.radi3nt.maths.dynamics.values; + +public interface DynamicValueFactory> { + + T createDynamicValue(P param); + ValueDynamicArray createDynamicValue(P param, int amount); + +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/TargetValueDynamic.java b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/TargetValueDynamic.java new file mode 100644 index 0000000..29f9d94 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/TargetValueDynamic.java @@ -0,0 +1,33 @@ +package fr.radi3nt.maths.dynamics.values; + +public abstract class TargetValueDynamic

implements ValueDynamic

{ + + protected P properties; + + protected float targetValue; + protected float currentValue; + + public TargetValueDynamic(P properties) { + this.properties = properties; + } + + @Override + public void setTargetValue(float value) { + this.targetValue = value; + } + + @Override + public void setCurrentValue(float value) { + this.currentValue = value; + } + + @Override + public float getCurrentValue() { + return currentValue; + } + + @Override + public void setProperties(P properties) { + this.properties = properties; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/ValueDynamic.java b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/ValueDynamic.java new file mode 100644 index 0000000..e4834a9 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/ValueDynamic.java @@ -0,0 +1,13 @@ +package fr.radi3nt.maths.dynamics.values; + +public interface ValueDynamic

{ + + void setTargetValue(float value); + void setCurrentValue(float value); + + float getCurrentValue(); + void update(float delta); + + void setProperties(P properties); + +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/ValueDynamicArray.java b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/ValueDynamicArray.java new file mode 100644 index 0000000..71c53a1 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/ValueDynamicArray.java @@ -0,0 +1,26 @@ +package fr.radi3nt.maths.dynamics.values; + +public class ValueDynamicArray> { + + private final T[] values; + + public ValueDynamicArray(T[] values) { + this.values = values; + } + + public void setProperties(P properties) { + for (T value : values) { + value.setProperties(properties); + } + } + + public void update(float delta) { + for (T value : values) { + value.update(delta); + } + } + + public T get(int index) { + return values[index]; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/ValueDynamicSmoothDirection.java b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/ValueDynamicSmoothDirection.java new file mode 100644 index 0000000..679dfa0 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/ValueDynamicSmoothDirection.java @@ -0,0 +1,56 @@ +package fr.radi3nt.maths.dynamics.values; + +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; + +public class ValueDynamicSmoothDirection> { + + private final Vector3f targetDirection = new SimpleVector3f(); + + private final Vector3f direction = new SimpleVector3f(); + + private final ValueDynamicArray positionDynamic; + + public ValueDynamicSmoothDirection(DynamicValueFactory factory, P linear) { + this.positionDynamic = factory.createDynamicValue(linear, 3); + } + + public Vector3f getTargetDirection() { + return targetDirection; + } + + public Vector3f getCurrentDirection() { + return direction; + } + + public void setCurrentDirection(Vector3f direction) { + this.direction.set(direction); + this.positionDynamic.get(0).setCurrentValue(direction.getX()); + this.positionDynamic.get(1).setCurrentValue(direction.getY()); + this.positionDynamic.get(2).setCurrentValue(direction.getZ()); + } + + public void setTargetDirection(Vector3f direction) { + targetDirection.copy(direction); + this.positionDynamic.get(0).setTargetValue(targetDirection.getX()); + this.positionDynamic.get(1).setTargetValue(targetDirection.getY()); + this.positionDynamic.get(2).setTargetValue(targetDirection.getZ()); + } + + public void setLinearProperties(P linear) { + this.positionDynamic.setProperties(linear); + } + + + public void update(float delta) { + positionDynamic.update(delta); + + this.direction.set(positionDynamic.get(0).getCurrentValue(), + positionDynamic.get(1).getCurrentValue(), + positionDynamic.get(2).getCurrentValue()); + this.direction.normalizeSafely(); + this.positionDynamic.get(0).setCurrentValue(direction.getX()); + this.positionDynamic.get(1).setCurrentValue(direction.getY()); + this.positionDynamic.get(2).setCurrentValue(direction.getZ()); + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/ValueDynamicSmoothPosition.java b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/ValueDynamicSmoothPosition.java new file mode 100644 index 0000000..24fb4da --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/ValueDynamicSmoothPosition.java @@ -0,0 +1,55 @@ +package fr.radi3nt.maths.dynamics.values; + +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; +import fr.radi3nt.maths.dynamics.SmoothTransform; + +public class ValueDynamicSmoothPosition> { + + private final Vector3f targetPosition = new SimpleVector3f(); + + private final Vector3f position = new SimpleVector3f(); + + private final ValueDynamicArray positionDynamic; + + public ValueDynamicSmoothPosition(DynamicValueFactory factory, P linear) { + this.positionDynamic = factory.createDynamicValue(linear, 3); + } + + public Vector3f getTargetPosition() { + return targetPosition; + } + + public Vector3f getCurrentPosition() { + return position; + } + + public void setCurrentPosition(Vector3f position) { + this.position.set(position); + this.positionDynamic.get(0).setCurrentValue(position.getX()); + this.positionDynamic.get(1).setCurrentValue(position.getY()); + this.positionDynamic.get(2).setCurrentValue(position.getZ()); + } + + public void setTargetPosition(Vector3f position) { + targetPosition.copy(position); + this.positionDynamic.get(0).setTargetValue(targetPosition.getX()); + this.positionDynamic.get(1).setTargetValue(targetPosition.getY()); + this.positionDynamic.get(2).setTargetValue(targetPosition.getZ()); + } + + public void setLinearProperties(P linear) { + this.positionDynamic.setProperties(linear); + } + + + public void update(float delta) { + positionDynamic.update(delta); + + this.position.set(positionDynamic.get(0).getCurrentValue(), + positionDynamic.get(1).getCurrentValue(), + positionDynamic.get(2).getCurrentValue()); + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/ValueDynamicSmoothTransform.java b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/ValueDynamicSmoothTransform.java new file mode 100644 index 0000000..783bf6b --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/ValueDynamicSmoothTransform.java @@ -0,0 +1,112 @@ +package fr.radi3nt.maths.dynamics.values; + +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; +import fr.radi3nt.maths.dynamics.SmoothTransform; + +public class ValueDynamicSmoothTransform> implements SmoothTransform

{ + + private final Vector3f targetPosition = new SimpleVector3f(); + private final Quaternion targetOrientation = ComponentsQuaternion.zero(); + + private final Vector3f position = new SimpleVector3f(); + private final Quaternion orientation = ComponentsQuaternion.zero(); + + private final ValueDynamicArray positionDynamic; + private final ValueDynamicArray rotationDynamic; + + public ValueDynamicSmoothTransform(DynamicValueFactory factory, P linear, P angular) { + this.positionDynamic = factory.createDynamicValue(linear, 3); + this.rotationDynamic = factory.createDynamicValue(angular, 4); + } + + @Override + public Vector3f getTargetPosition() { + return targetPosition; + } + + @Override + public Quaternion getTargetOrientation() { + return targetOrientation; + } + + @Override + public Vector3f getCurrentPosition() { + return position; + } + + @Override + public Quaternion getCurrentOrientation() { + return orientation; + } + + @Override + public void setCurrentPosition(Vector3f position) { + this.position.set(position); + this.positionDynamic.get(0).setCurrentValue(position.getX()); + this.positionDynamic.get(1).setCurrentValue(position.getY()); + this.positionDynamic.get(2).setCurrentValue(position.getZ()); + } + + @Override + public void setCurrentOrientation(Quaternion orientation) { + this.orientation.copy(orientation); + } + + @Override + public void setTargetPosition(Vector3f position) { + targetPosition.copy(position); + this.positionDynamic.get(0).setTargetValue(targetPosition.getX()); + this.positionDynamic.get(1).setTargetValue(targetPosition.getY()); + this.positionDynamic.get(2).setTargetValue(targetPosition.getZ()); + } + + @Override + public void setTargetOrientation(Quaternion orientation) { + targetOrientation.copy(orientation); + } + + @Override + public void setLinearProperties(P linear) { + this.positionDynamic.setProperties(linear); + } + + @Override + public void setAngularProperties(P angular) { + this.rotationDynamic.setProperties(angular); + } + + @Override + public void update(float delta) { + Quaternion currentTarget = targetOrientation.duplicate(); + + if (orientation.dot(currentTarget) <= 0) { + orientation.multiply(-1f); + } + this.rotationDynamic.get(0).setCurrentValue(orientation.getX()); + this.rotationDynamic.get(1).setCurrentValue(orientation.getY()); + this.rotationDynamic.get(2).setCurrentValue(orientation.getZ()); + this.rotationDynamic.get(3).setCurrentValue(orientation.getW()); + + this.rotationDynamic.get(0).setTargetValue(currentTarget.getX()); + this.rotationDynamic.get(1).setTargetValue(currentTarget.getY()); + this.rotationDynamic.get(2).setTargetValue(currentTarget.getZ()); + this.rotationDynamic.get(3).setTargetValue(currentTarget.getW()); + + positionDynamic.update(delta); + rotationDynamic.update(delta); + + this.position.set(positionDynamic.get(0).getCurrentValue(), + positionDynamic.get(1).getCurrentValue(), + positionDynamic.get(2).getCurrentValue()); + + this.orientation.set( + rotationDynamic.get(0).getCurrentValue(), + rotationDynamic.get(1).getCurrentValue(), + rotationDynamic.get(2).getCurrentValue(), + rotationDynamic.get(3).getCurrentValue()); + this.orientation.normaliseSafely(); + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/automatic/AutomaticDynamicProperties.java b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/automatic/AutomaticDynamicProperties.java new file mode 100644 index 0000000..3d6cf64 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/automatic/AutomaticDynamicProperties.java @@ -0,0 +1,14 @@ +package fr.radi3nt.maths.dynamics.values.automatic; + +public class AutomaticDynamicProperties { + + private final float agility; + + public AutomaticDynamicProperties(float agility) { + this.agility = agility; + } + + public float getAgility() { + return agility; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/automatic/AutomaticDynamicValueFactory.java b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/automatic/AutomaticDynamicValueFactory.java new file mode 100644 index 0000000..2b452be --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/automatic/AutomaticDynamicValueFactory.java @@ -0,0 +1,33 @@ +package fr.radi3nt.maths.dynamics.values.automatic; + +import fr.radi3nt.maths.dynamics.values.DynamicValueFactory; +import fr.radi3nt.maths.dynamics.values.ValueDynamicArray; + +public class AutomaticDynamicValueFactory implements DynamicValueFactory { + + public static final DynamicValueFactory DEFAULT = new AutomaticDynamicValueFactory(); + + private final float defaultValue; + + public AutomaticDynamicValueFactory(float defaultValue) { + this.defaultValue = defaultValue; + } + + public AutomaticDynamicValueFactory() { + this(0); + } + + @Override + public AutomaticValueDynamic createDynamicValue(AutomaticDynamicProperties param) { + return new AutomaticValueDynamic(param, defaultValue); + } + + @Override + public ValueDynamicArray createDynamicValue(AutomaticDynamicProperties param, int amount) { + AutomaticValueDynamic[] values = new AutomaticValueDynamic[amount]; + for (int i = 0; i < amount; i++) { + values[i] = new AutomaticValueDynamic(param, defaultValue); + } + return new ValueDynamicArray<>(values); + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/automatic/AutomaticValueDynamic.java b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/automatic/AutomaticValueDynamic.java new file mode 100644 index 0000000..90db8c0 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/dynamics/values/automatic/AutomaticValueDynamic.java @@ -0,0 +1,18 @@ +package fr.radi3nt.maths.dynamics.values.automatic; + +import fr.radi3nt.maths.dynamics.values.TargetValueDynamic; + +public class AutomaticValueDynamic extends TargetValueDynamic { + + public AutomaticValueDynamic(AutomaticDynamicProperties properties, float currentValue) { + super(properties); + this.currentValue = currentValue; + this.targetValue = currentValue; + } + + public void update(float delta) { + float distance = targetValue - currentValue; + currentValue += distance * Math.min(properties.getAgility() * delta, 1); + } + +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/pool/ConcurrentQueuedPool.java b/MathsHelper/src/main/java/fr/radi3nt/maths/pool/ConcurrentQueuedPool.java new file mode 100644 index 0000000..820d200 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/pool/ConcurrentQueuedPool.java @@ -0,0 +1,25 @@ +package fr.radi3nt.maths.pool; + +import java.util.Deque; +import java.util.concurrent.ConcurrentLinkedDeque; + +public abstract class ConcurrentQueuedPool implements ObjectPool { + + protected final Deque queue = new ConcurrentLinkedDeque<>(); + + @Override + public T borrow() { + T object = queue.pollFirst(); + if (object == null) + return create(); + + return object; + } + + protected abstract T create(); + + @Override + public void free(T object) { + queue.addFirst(object); + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/pool/ConcurrentVector3fPool.java b/MathsHelper/src/main/java/fr/radi3nt/maths/pool/ConcurrentVector3fPool.java new file mode 100644 index 0000000..bed93b5 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/pool/ConcurrentVector3fPool.java @@ -0,0 +1,22 @@ +package fr.radi3nt.maths.pool; + +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; + +public class ConcurrentVector3fPool extends ConcurrentQueuedPool { + + public ConcurrentVector3fPool() { + } + + public ConcurrentVector3fPool(int baseObjectAmount) { + for (int i = 0; i < baseObjectAmount; i++) { + queue.add(create()); + } + } + + @Override + protected Vector3f create() { + return new SimpleVector3f(); + } + +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/pool/ListPool.java b/MathsHelper/src/main/java/fr/radi3nt/maths/pool/ListPool.java new file mode 100644 index 0000000..94babe3 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/pool/ListPool.java @@ -0,0 +1,28 @@ +package fr.radi3nt.maths.pool; + +import java.util.ArrayList; +import java.util.List; + +public abstract class ListPool implements ObjectPool { + + protected final List queue = new ArrayList<>(); + + @Override + public T borrow() { + if (queue.isEmpty()) + return create(); + + T object = queue.remove(queue.size()-1); + if (object == null) + return create(); + + return object; + } + + protected abstract T create(); + + @Override + public void free(T object) { + queue.add(object); + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/pool/ListVector3fPool.java b/MathsHelper/src/main/java/fr/radi3nt/maths/pool/ListVector3fPool.java new file mode 100644 index 0000000..9981c96 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/pool/ListVector3fPool.java @@ -0,0 +1,22 @@ +package fr.radi3nt.maths.pool; + +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; + +public class ListVector3fPool extends ListPool { + + public ListVector3fPool() { + } + + public ListVector3fPool(int baseObjectAmount) { + for (int i = 0; i < baseObjectAmount; i++) { + queue.add(create()); + } + } + + @Override + protected Vector3f create() { + return new SimpleVector3f(); + } + +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/pool/QueuedPool.java b/MathsHelper/src/main/java/fr/radi3nt/maths/pool/QueuedPool.java index f5796a8..2088153 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/pool/QueuedPool.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/pool/QueuedPool.java @@ -1,14 +1,15 @@ package fr.radi3nt.maths.pool; -import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.ArrayDeque; +import java.util.Deque; public abstract class QueuedPool implements ObjectPool { - protected final ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); + protected final Deque queue = new ArrayDeque<>(); @Override public T borrow() { - T object = queue.poll(); + T object = queue.pollLast(); if (object == null) return create(); diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/pool/WaitingVector3fPool.java b/MathsHelper/src/main/java/fr/radi3nt/maths/pool/WaitingVector3fPool.java index 32d9811..18a9b95 100644 --- a/MathsHelper/src/main/java/fr/radi3nt/maths/pool/WaitingVector3fPool.java +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/pool/WaitingVector3fPool.java @@ -9,6 +9,7 @@ public WaitingVector3fPool(int baseObjectAmount) { for (int i = 0; i < baseObjectAmount; i++) { queue.add(create()); } + queue.notifyAll(); } public Vector3f borrow() { @@ -16,9 +17,9 @@ public Vector3f borrow() { if (object == null) while ((object = queue.poll()) == null) { try { - Thread.sleep(1); + queue.wait(); } catch (InterruptedException e) { - throw new RuntimeException(e); + e.printStackTrace(); } } diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/sat/MTV.java b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/MTV.java new file mode 100644 index 0000000..d202aeb --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/MTV.java @@ -0,0 +1,22 @@ +package fr.radi3nt.maths.sat; + +import fr.radi3nt.maths.sat.components.SatAxis; + +public class MTV { + + private final SatAxis smallest; + private final double overlap; + + public MTV(SatAxis smallest, double overlap) { + this.smallest = smallest; + this.overlap = overlap; + } + + public SatAxis getSmallest() { + return smallest; + } + + public double getOverlap() { + return overlap; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/sat/clip/AxisAndVertexIndex.java b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/clip/AxisAndVertexIndex.java new file mode 100644 index 0000000..ebc152e --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/clip/AxisAndVertexIndex.java @@ -0,0 +1,22 @@ +package fr.radi3nt.maths.sat.clip; + +import fr.radi3nt.maths.components.Vector3D; + +public class AxisAndVertexIndex { + + private final Vector3D axis; + private final int index; + + public AxisAndVertexIndex(Vector3D axis, int index) { + this.axis = axis; + this.index = index; + } + + public Vector3D getAxis() { + return axis; + } + + public int getIndex() { + return index; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/sat/clip/ClipPlane.java b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/clip/ClipPlane.java new file mode 100644 index 0000000..9d87f15 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/clip/ClipPlane.java @@ -0,0 +1,47 @@ +package fr.radi3nt.maths.sat.clip; + +import fr.radi3nt.maths.components.Vector3D; + +public class ClipPlane { + + private final Vector3D normal; + private final Vector3D vertexOnPlane; + + public ClipPlane(Vector3D normal, Vector3D vertexOnPlane) { + this.normal = normal; + this.vertexOnPlane = vertexOnPlane; + } + + public void clip(Edge[] otherEdges) { + double projectedClip = normal.dot(vertexOnPlane); + + for (int i = 0; i < otherEdges.length; i++) { + Edge edge = otherEdges[i]; + if (edge==null) + continue; + + double projectedVertex1 = normal.dot(edge.getVertex1()); + double projectedVertex2 = normal.dot(edge.getVertex2()); + + if (!(projectedVertex1 >= projectedClip) || !(projectedVertex2 >= projectedClip)) { + if (projectedVertex1 >= projectedClip) { + double on = (projectedClip - projectedVertex1) / (projectedVertex2 - projectedVertex1); + Vector3D computedNewVertex = computeInterpolation(edge.getVertex1(), edge.getVertex2(), on); + edge.setVertices(edge.getVertex1(), computedNewVertex); + + } else if (projectedVertex2 >= projectedClip) { + double on = (projectedClip - projectedVertex2) / (projectedVertex1 - projectedVertex2); + Vector3D computedNewVertex = computeInterpolation(edge.getVertex2(), edge.getVertex1(), on); + edge.setVertices(computedNewVertex, edge.getVertex2()); + } else { + otherEdges[i] = null; + } + } + } + } + + private Vector3D computeInterpolation(Vector3D vertex1, Vector3D vertex2, double on) { + Vector3D edgeLocal = vertex2.clone().subtract(vertex1); + return vertex1.clone().add(edgeLocal.multiply(on)); + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/sat/clip/ClipPlanes.java b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/clip/ClipPlanes.java new file mode 100644 index 0000000..155b9b0 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/clip/ClipPlanes.java @@ -0,0 +1,41 @@ +package fr.radi3nt.maths.sat.clip; + +import fr.radi3nt.maths.components.Vector3D; + +import java.util.ArrayList; +import java.util.Collection; + +public class ClipPlanes { + + private static final double EPSILON = 1e-2f; + private final ClipPlane[] clipPlane; + + public ClipPlanes(ClipPlane... clipPlane) { + this.clipPlane = clipPlane; + } + + public Edge[] clipEdges(Edge[] edges) { + for (ClipPlane plane : clipPlane) { + plane.clip(edges); + } + return edges; + } + + public Collection clip(Edge[] edges) { + Edge[] edgeList = clipEdges(edges); + Collection result = new ArrayList<>(edgeList.length); + for (Edge edge : edgeList) { + if (edge==null) + continue; + result.add(edge.getVertex1()); + if (notSameVertices(edge)) + result.add(edge.getVertex2()); + } + return result; + } + + private static boolean notSameVertices(Edge edge) { + return edge.getVertex1().clone().subtract(edge.getVertex2()).lengthSquared() > EPSILON; + } + +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/sat/clip/Edge.java b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/clip/Edge.java new file mode 100644 index 0000000..ec9505a --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/clip/Edge.java @@ -0,0 +1,28 @@ +package fr.radi3nt.maths.sat.clip; + +import fr.radi3nt.maths.components.Vector3D; + +public class Edge { + + private Vector3D vertex1; + private Vector3D vertex2; + + public Edge(Vector3D vertex1, Vector3D vertex2) { + this.vertex1 = vertex1; + this.vertex2 = vertex2; + } + + public Vector3D getVertex1() { + return vertex1; + } + + public Vector3D getVertex2() { + return vertex2; + } + + public void setVertices(Vector3D vertex1, Vector3D vertex2) { + this.vertex1 = vertex1; + this.vertex2 = vertex2; + } + +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/sat/components/SatAxis.java b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/components/SatAxis.java new file mode 100644 index 0000000..888f03b --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/components/SatAxis.java @@ -0,0 +1,32 @@ +package fr.radi3nt.maths.sat.components; + +import fr.radi3nt.maths.components.Vector3D; + +public class SatAxis { + + private Vector3D normal; + + public SatAxis(Vector3D normal) { + this.normal = normal; + } + + public void set(Vector3D normal) { + this.normal = normal; + } + + public double dot(Vector3D dotted) { + return normal.dot(dotted); + } + + public Vector3D getNormal() { + return normal; + } + + public SatAxis useNewNormalVector(Vector3D newNormal) { + newNormal.setX(this.normal.getX()); + newNormal.setY(this.normal.getY()); + newNormal.setZ(this.normal.getZ()); + this.normal = newNormal; + return this; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/sat/components/SatEdge.java b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/components/SatEdge.java new file mode 100644 index 0000000..9db35e6 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/components/SatEdge.java @@ -0,0 +1,20 @@ +package fr.radi3nt.maths.sat.components; + +import fr.radi3nt.maths.components.Vector3D; + +public class SatEdge { + + private final Vector3D edge; + + public SatEdge(Vector3D edge) { + this.edge = edge; + } + + public SatAxis axis(SatEdge other) { + return new SatAxis(other.edge.getCrossProduct(edge, new Vector3D()).normalize()); + } + + public SatAxis axis(SatEdge other, Vector3D result) { + return new SatAxis(other.edge.getCrossProduct(edge, result).normalize()); + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/sat/projection/NewSatProjectionManager.java b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/projection/NewSatProjectionManager.java new file mode 100644 index 0000000..190b806 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/projection/NewSatProjectionManager.java @@ -0,0 +1,13 @@ +package fr.radi3nt.maths.sat.projection; + +public class NewSatProjectionManager implements SatProjectionProvider { + @Override + public SatProjection project(double min, double max) { + return new SatProjection(min, max); + } + + @Override + public SatProjectionProvider duplicate() { + return new NewSatProjectionManager(); + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/sat/projection/OverlapInfo.java b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/projection/OverlapInfo.java new file mode 100644 index 0000000..0d90628 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/projection/OverlapInfo.java @@ -0,0 +1,20 @@ +package fr.radi3nt.maths.sat.projection; + +public class OverlapInfo { + + private final boolean overlap; + private final double overlapAmount; + + public OverlapInfo(boolean overlap, double overlapAmount) { + this.overlap = overlap; + this.overlapAmount = overlapAmount; + } + + public boolean isOverlap() { + return overlap; + } + + public double getOverlapAmount() { + return overlapAmount; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/sat/projection/ReuseSatProjectionManager.java b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/projection/ReuseSatProjectionManager.java new file mode 100644 index 0000000..ab24ada --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/projection/ReuseSatProjectionManager.java @@ -0,0 +1,18 @@ +package fr.radi3nt.maths.sat.projection; + +public class ReuseSatProjectionManager implements SatProjectionProvider { + + private final SatProjection satProjection = new SatProjection(0, 0); + + @Override + public SatProjection project(double min, double max) { + satProjection.setMin(min); + satProjection.setMax(max); + return satProjection; + } + + @Override + public SatProjectionProvider duplicate() { + return new ReuseSatProjectionManager(); + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/sat/projection/SatProjection.java b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/projection/SatProjection.java new file mode 100644 index 0000000..1a61c25 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/projection/SatProjection.java @@ -0,0 +1,112 @@ +package fr.radi3nt.maths.sat.projection; + +import static java.lang.Math.abs; + +public class SatProjection { + + private static final OverlapInfo NO_OVERLAP = new OverlapInfo(false, 0); + + public double min; + public double max; + + private double tEnter; + private double tLeave; + + public SatProjection(double min, double max) { + this.min = min; + this.max = max; + } + + public OverlapInfo overlapInfo(SatProjection p2) { + if (this.min <= p2.min) { + if (this.max >= p2.min) + return new OverlapInfo(true, abs(p2.min - this.max)); + } else { + if (this.min <= p2.max) + return new OverlapInfo(true, abs(this.min - p2.max)); + } + return NO_OVERLAP; + } + + public boolean contains(SatProjection p2) { + return (min <= p2.min && max >= p2.max); + } + + public boolean noOverlap(SatProjection p2) { + return !overlap(p2); + } + + public boolean overlap(SatProjection p2) { + return this.min <= p2.max ? this.max >= p2.min : this.min <= p2.min; + } + + public boolean overlap(double p2) { + return this.min < p2 && this.max > p2; + } + + public void sweptOverlap(SatProjection p2, double speed) { + double tEnter; + double tLeave; + + tEnter = (p2.min - this.max) / speed; + tLeave = (p2.max - this.min) / speed; + + if (tEnter > tLeave) { + double oldTEnter = tEnter; + tEnter = tLeave; + tLeave = oldTEnter; + } + + this.tEnter = tEnter; + this.tLeave = tLeave; + } + + public double getOverlap(SatProjection p2) { + if (this.min <= p2.min) { + if (this.max >= p2.min) + return p2.min - this.max; + } else { + if (p2.max >= this.min) + return this.min - p2.max; + } + return Double.MAX_VALUE; + } + + public int getOverlapNormal(SatProjection p2) { + if (this.min <= p2.min) { + if (this.max >= p2.min) + return 1; + } else { + if (p2.max >= this.min) + return -1; + } + return 0; + } + + public void setMin(double min) { + this.min = min; + } + + public void setMax(double max) { + this.max = max; + } + + public double getTEnter() { + return tEnter; + } + + public double getTLeave() { + return tLeave; + } + + + @Override + public String toString() { + return "SatProjection{" + + "min=" + min + + ", max=" + max + + ", tEnter=" + tEnter + + ", tLeave=" + tLeave + + '}'; + } +} diff --git a/MathsHelper/src/main/java/fr/radi3nt/maths/sat/projection/SatProjectionProvider.java b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/projection/SatProjectionProvider.java new file mode 100644 index 0000000..9555a00 --- /dev/null +++ b/MathsHelper/src/main/java/fr/radi3nt/maths/sat/projection/SatProjectionProvider.java @@ -0,0 +1,8 @@ +package fr.radi3nt.maths.sat.projection; + +public interface SatProjectionProvider { + + SatProjection project(double min, double max); + SatProjectionProvider duplicate(); + +} diff --git a/MathsHelper/src/test/java/fr/radi3nt/maths/components/arbitrary/matrix/ArrayMatrixNxNdTest.java b/MathsHelper/src/test/java/fr/radi3nt/maths/components/arbitrary/matrix/ArrayMatrixNxNdTest.java new file mode 100644 index 0000000..eff70ca --- /dev/null +++ b/MathsHelper/src/test/java/fr/radi3nt/maths/components/arbitrary/matrix/ArrayMatrixNxNdTest.java @@ -0,0 +1,164 @@ +package fr.radi3nt.maths.components.arbitrary.matrix; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class ArrayMatrixNxNdTest { + + private static final ArrayMatrixNxNd resultOneRow = new ArrayMatrixNxNd(3, 4); + private static final ArrayMatrixNxNd resultMultiple = new ArrayMatrixNxNd(2, 3); + + @BeforeAll + static void setUp() { + resultOneRow.set(0, 0, 1); + resultOneRow.set(0, 1, 2); + resultOneRow.set(0, 2, 3); + resultOneRow.set(0, 3, 4); + + resultOneRow.set(1, 0, 2); + resultOneRow.set(1, 1, 4); + resultOneRow.set(1, 2, 6); + resultOneRow.set(1, 3, 8); + + resultOneRow.set(2, 0, 3); + resultOneRow.set(2, 1, 6); + resultOneRow.set(2, 2, 9); + resultOneRow.set(2, 3, 12); + + resultMultiple.set(0, 0, 22); + resultMultiple.set(1, 0, 28); + + resultMultiple.set(0, 1, 49); + resultMultiple.set(1, 1, 64); + + resultMultiple.set(0, 2, 76); + resultMultiple.set(1, 2, 100); + } + + @Test + void multiplyOneRow() { + ArrayMatrixNxNd arrayMatrixNxNd = new ArrayMatrixNxNd(1, 4); + arrayMatrixNxNd.set(0, 0, 1); + arrayMatrixNxNd.set(0, 1, 2); + arrayMatrixNxNd.set(0, 2, 3); + arrayMatrixNxNd.set(0, 3, 4); + ArrayMatrixNxNd arrayMatrixNxNd1 = new ArrayMatrixNxNd(3, 1); + arrayMatrixNxNd1.set(0, 0, 1); + arrayMatrixNxNd1.set(1, 0, 2); + arrayMatrixNxNd1.set(2, 0, 3); + Assertions.assertEquals(arrayMatrixNxNd.multiply(arrayMatrixNxNd1), resultOneRow); + } + + @Test + void multiplyMultiple() { + ArrayMatrixNxNd arrayMatrixNxNd = new ArrayMatrixNxNd(3, 3); + arrayMatrixNxNd.set(0, 0, 1); + arrayMatrixNxNd.set(1, 0, 2); + arrayMatrixNxNd.set(2, 0, 3); + + arrayMatrixNxNd.set(0, 1, 4); + arrayMatrixNxNd.set(1, 1, 5); + arrayMatrixNxNd.set(2, 1, 6); + + arrayMatrixNxNd.set(0, 2, 7); + arrayMatrixNxNd.set(1, 2, 8); + arrayMatrixNxNd.set(2, 2, 9); + + ArrayMatrixNxNd arrayMatrixNxNd1 = new ArrayMatrixNxNd(2, 3); + arrayMatrixNxNd1.set(0, 0, 1); + arrayMatrixNxNd1.set(1, 0, 2); + + arrayMatrixNxNd1.set(0, 1, 3); + arrayMatrixNxNd1.set(1, 1, 4); + + arrayMatrixNxNd1.set(0, 2, 5); + arrayMatrixNxNd1.set(1, 2, 6); + + Assertions.assertEquals(arrayMatrixNxNd.multiply(arrayMatrixNxNd1), resultMultiple); + } + + @Test + void multiplyWithTransposedOneRow() { + ArrayMatrixNxNd arrayMatrixNxNd = new ArrayMatrixNxNd(1, 4); + arrayMatrixNxNd.set(0, 0, 1); + arrayMatrixNxNd.set(0, 1, 2); + arrayMatrixNxNd.set(0, 2, 3); + arrayMatrixNxNd.set(0, 3, 4); + ArrayMatrixNxNd arrayMatrixNxNd1 = new ArrayMatrixNxNd(1, 3); + arrayMatrixNxNd1.set(0, 0, 1); + arrayMatrixNxNd1.set(0, 1, 2); + arrayMatrixNxNd1.set(0, 2, 3); + Assertions.assertEquals(arrayMatrixNxNd.multiplyWithTransposed(arrayMatrixNxNd1), resultOneRow); + } + + @Test + void multiplyWithTransposedMultiple() { + ArrayMatrixNxNd arrayMatrixNxNd = new ArrayMatrixNxNd(3, 3); + arrayMatrixNxNd.set(0, 0, 1); + arrayMatrixNxNd.set(1, 0, 2); + arrayMatrixNxNd.set(2, 0, 3); + + arrayMatrixNxNd.set(0, 1, 4); + arrayMatrixNxNd.set(1, 1, 5); + arrayMatrixNxNd.set(2, 1, 6); + + arrayMatrixNxNd.set(0, 2, 7); + arrayMatrixNxNd.set(1, 2, 8); + arrayMatrixNxNd.set(2, 2, 9); + + ArrayMatrixNxNd arrayMatrixNxNd1 = new ArrayMatrixNxNd(3, 2); + arrayMatrixNxNd1.set(0, 0, 1); + arrayMatrixNxNd1.set(0, 1, 2); + + arrayMatrixNxNd1.set(1, 0, 3); + arrayMatrixNxNd1.set(1, 1, 4); + + arrayMatrixNxNd1.set(2, 0, 5); + arrayMatrixNxNd1.set(2, 1, 6); + + Assertions.assertEquals(arrayMatrixNxNd.multiplyWithTransposed(arrayMatrixNxNd1), resultMultiple); + } + + @Test + void multiplyWithTransposedOtherOneRow() { + ArrayMatrixNxNd arrayMatrixNxNd = new ArrayMatrixNxNd(1, 4); + arrayMatrixNxNd.set(0, 0, 1); + arrayMatrixNxNd.set(0, 1, 2); + arrayMatrixNxNd.set(0, 2, 3); + arrayMatrixNxNd.set(0, 3, 4); + ArrayMatrixNxNd arrayMatrixNxNd1 = new ArrayMatrixNxNd(1, 3); + arrayMatrixNxNd1.set(0, 0, 1); + arrayMatrixNxNd1.set(0, 1, 2); + arrayMatrixNxNd1.set(0, 2, 3); + Assertions.assertEquals(arrayMatrixNxNd1.multiplyTransposedOther(arrayMatrixNxNd), resultOneRow); + } + + @Test + void multiplyWithTransposedOtherMultiple() { + ArrayMatrixNxNd arrayMatrixNxNd = new ArrayMatrixNxNd(3, 3); + arrayMatrixNxNd.set(0, 0, 1); + arrayMatrixNxNd.set(1, 0, 2); + arrayMatrixNxNd.set(2, 0, 3); + + arrayMatrixNxNd.set(0, 1, 4); + arrayMatrixNxNd.set(1, 1, 5); + arrayMatrixNxNd.set(2, 1, 6); + + arrayMatrixNxNd.set(0, 2, 7); + arrayMatrixNxNd.set(1, 2, 8); + arrayMatrixNxNd.set(2, 2, 9); + + ArrayMatrixNxNd arrayMatrixNxNd1 = new ArrayMatrixNxNd(3, 2); + arrayMatrixNxNd1.set(0, 0, 1); + arrayMatrixNxNd1.set(0, 1, 2); + + arrayMatrixNxNd1.set(1, 0, 3); + arrayMatrixNxNd1.set(1, 1, 4); + + arrayMatrixNxNd1.set(2, 0, 5); + arrayMatrixNxNd1.set(2, 1, 6); + Assertions.assertEquals(arrayMatrixNxNd1.multiplyTransposedOther(arrayMatrixNxNd), resultMultiple); + } + +} \ No newline at end of file diff --git a/MathsHelper/src/test/java/fr/radi3nt/maths/components/arbitrary/matrix/sparse/SparseMatrixdTest.java b/MathsHelper/src/test/java/fr/radi3nt/maths/components/arbitrary/matrix/sparse/SparseMatrixdTest.java new file mode 100644 index 0000000..5601aeb --- /dev/null +++ b/MathsHelper/src/test/java/fr/radi3nt/maths/components/arbitrary/matrix/sparse/SparseMatrixdTest.java @@ -0,0 +1,22 @@ +package fr.radi3nt.maths.components.arbitrary.matrix.sparse; + +import org.junit.jupiter.api.Test; + +class SparseMatrixdTest { + + @Test + void multiply() { + SparseMatrixd sparseMatrixd = new SparseMatrixd(); + sparseMatrixd.add(new SparseBlockd(0, 0, 1, 1, new double[]{1})); + sparseMatrixd.add(new SparseBlockd(0, 1, 1, 1, new double[]{2})); + sparseMatrixd.add(new SparseBlockd(0, 2, 1, 1, new double[]{3})); + sparseMatrixd.add(new SparseBlockd(0, 3, 1, 1, new double[]{4})); + + SparseMatrixd sparseMatrixd1 = new SparseMatrixd(); + sparseMatrixd1.add(new SparseBlockd(0, 0, 1, 1, new double[]{1})); + sparseMatrixd1.add(new SparseBlockd(1, 0, 1, 1, new double[]{2})); + sparseMatrixd1.add(new SparseBlockd(2, 0, 1, 1, new double[]{3})); + + System.out.println(sparseMatrixd.multiply(sparseMatrixd1)); + } +} \ No newline at end of file diff --git a/NoiseHelper/src/main/java/fr/radi3nt/noise/SeededFastSimplexNoise.java b/NoiseHelper/src/main/java/fr/radi3nt/noise/SeededFastSimplexNoise.java index da474de..c3315f0 100644 --- a/NoiseHelper/src/main/java/fr/radi3nt/noise/SeededFastSimplexNoise.java +++ b/NoiseHelper/src/main/java/fr/radi3nt/noise/SeededFastSimplexNoise.java @@ -1,5 +1,7 @@ package fr.radi3nt.noise; +import fr.radi3nt.noise.simplex.SimplexNoise; + import java.util.Random; public class SeededFastSimplexNoise { @@ -16,10 +18,10 @@ public SeededFastSimplexNoise(long seed) { } public double noise(double x, double y, double z) { - return FastSimplexNoise.noise(x + xOffset, y + yOffset, z + zOffset); + return SimplexNoise.noise((float) (x + xOffset), (float) (y + yOffset), (float) (z + zOffset)); } public double noise(double x, double z) { - return FastSimplexNoise.noise(x + xOffset, z + zOffset); + return SimplexNoise.noise((float) (x + xOffset), (float) (z + zOffset)); } } diff --git a/NoiseHelper/src/main/java/fr/radi3nt/noise/SimpleNoise3D.java b/NoiseHelper/src/main/java/fr/radi3nt/noise/SimpleNoise3D.java index 503ee4b..34daefb 100644 --- a/NoiseHelper/src/main/java/fr/radi3nt/noise/SimpleNoise3D.java +++ b/NoiseHelper/src/main/java/fr/radi3nt/noise/SimpleNoise3D.java @@ -1,7 +1,4 @@ -package fr.radi3nt.noise.noise; - -import fr.radi3nt.noise.Noise3D; -import fr.radi3nt.noise.SimplexNoise; +package fr.radi3nt.noise; public class SimpleNoise3D implements Noise3D { diff --git a/NoiseHelper/src/main/java/fr/radi3nt/noise/TerrainHeight2D.java b/NoiseHelper/src/main/java/fr/radi3nt/noise/TerrainHeight2D.java index 2bd34fc..5c84746 100644 --- a/NoiseHelper/src/main/java/fr/radi3nt/noise/TerrainHeight2D.java +++ b/NoiseHelper/src/main/java/fr/radi3nt/noise/TerrainHeight2D.java @@ -1,7 +1,4 @@ -package fr.radi3nt.noise.noise; - -import fr.radi3nt.noise.Noise3D; -import fr.radi3nt.noise.SimplexNoise; +package fr.radi3nt.noise; public class TerrainHeight2D implements Noise3D { diff --git a/NoiseHelper/src/main/java/fr/radi3nt/noise/VaryingSeedNoise.java b/NoiseHelper/src/main/java/fr/radi3nt/noise/VaryingSeedNoise.java new file mode 100644 index 0000000..aac5ed9 --- /dev/null +++ b/NoiseHelper/src/main/java/fr/radi3nt/noise/VaryingSeedNoise.java @@ -0,0 +1,26 @@ +package fr.radi3nt.noise; + +import fr.radi3nt.noise.simplex.SimplexNoise; + +import java.util.Random; + +public class VaryingSeedNoise { + + public static float noise(Random random, float x, float y, float z, float size) { + float xOffset = offsetBasedOnSeed(random); + float yOffset = offsetBasedOnSeed(random); + float zOffset = offsetBasedOnSeed(random); + return SimplexNoise.noise(xOffset+x/size, yOffset+y/size, zOffset+z/size); + } + + public static float noise(Random random, float x, float y, float size) { + float xOffset = offsetBasedOnSeed(random); + float yOffset = offsetBasedOnSeed(random); + return SimplexNoise.noise(xOffset+x/size, yOffset+y/size); + } + + private static float offsetBasedOnSeed(Random random) { + return random.nextFloat(); + } + +} diff --git a/NoiseHelper/src/main/java/fr/radi3nt/noise/generator/DoubleNoise3D.java b/NoiseHelper/src/main/java/fr/radi3nt/noise/generator/DoubleNoise3D.java new file mode 100644 index 0000000..17fe6e6 --- /dev/null +++ b/NoiseHelper/src/main/java/fr/radi3nt/noise/generator/DoubleNoise3D.java @@ -0,0 +1,8 @@ +package fr.radi3nt.noise.generator; + +public interface DoubleNoise3D { + + double noise(double x, double y, double z); + double noise(double x, double y, double z, double w); + +} diff --git a/NoiseHelper/src/main/java/fr/radi3nt/noise/generator/FastSimplexNoise3D.java b/NoiseHelper/src/main/java/fr/radi3nt/noise/generator/FastSimplexNoise3D.java new file mode 100644 index 0000000..0766435 --- /dev/null +++ b/NoiseHelper/src/main/java/fr/radi3nt/noise/generator/FastSimplexNoise3D.java @@ -0,0 +1,37 @@ +package fr.radi3nt.noise.generator; + +import fr.radi3nt.noise.FastSimplexNoise; + +import java.util.Random; + +public class FastSimplexNoise3D implements DoubleNoise3D { + + private final long seed; + private final double size; + + private final double offsetX; + private final double offsetY; + private final double offsetZ; + private final double offsetW; + + public FastSimplexNoise3D(long seed, double size) { + this.seed = seed; + this.size = size; + + Random random = new Random(seed); + this.offsetX = random.nextDouble()*1024D-512D; + this.offsetY = random.nextDouble()*1024D-512D; + this.offsetZ = random.nextDouble()*1024D-512D; + this.offsetW = random.nextDouble()*1024D-512D; + } + + @Override + public double noise(double x, double y, double z) { + return FastSimplexNoise.noise(x*size + offsetX, y*size+ offsetY, z*size+ offsetZ)*0.5d+0.5d; + } + + @Override + public double noise(double x, double y, double z, double w) { + return FastSimplexNoise.noise(x*size + offsetX, y*size+ offsetY, z*size+ offsetZ, w*size+offsetW)*0.5d+0.5d; + } +} diff --git a/NoiseHelper/src/main/java/fr/radi3nt/noise/simplex/SeededSimplexNoise.java b/NoiseHelper/src/main/java/fr/radi3nt/noise/simplex/SeededSimplexNoise.java new file mode 100644 index 0000000..fe48e8e --- /dev/null +++ b/NoiseHelper/src/main/java/fr/radi3nt/noise/simplex/SeededSimplexNoise.java @@ -0,0 +1,30 @@ +package fr.radi3nt.noise.simplex; + +import java.util.Random; + +public class SeededSimplexNoise { + + private final long seed; + + private final float offsetX; + private final float offsetY; + private final float offsetZ; + + public SeededSimplexNoise(long seed) { + this.seed = seed; + Random random = new Random(seed); + offsetX = (float) (random.nextDouble()*1024); + offsetY = (float) (random.nextDouble()*1024); + offsetZ = (float) (random.nextDouble()*1024); + } + + public float noise(final float x, final float y) { + return SimplexNoise.noise(x+offsetX, y+offsetY); + } + + public float noise(final float x, final float y, final float z) { + return SimplexNoise.noise(x+offsetX, y+offsetY, z+offsetZ); + } + + +} diff --git a/NoiseHelper/src/main/java/fr/radi3nt/noise/simplex/SimplexNoise.java b/NoiseHelper/src/main/java/fr/radi3nt/noise/simplex/SimplexNoise.java new file mode 100644 index 0000000..0f1eb85 --- /dev/null +++ b/NoiseHelper/src/main/java/fr/radi3nt/noise/simplex/SimplexNoise.java @@ -0,0 +1,455 @@ +package fr.radi3nt.noise.simplex; + +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); + } + +} \ No newline at end of file diff --git a/Pathfinding/pom.xml b/Pathfinding/pom.xml new file mode 100644 index 0000000..fe8847b --- /dev/null +++ b/Pathfinding/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + fr.radi3nt + JavaUtil + 1.0 + + + Pathfinding + + + 8 + 8 + UTF-8 + + + + fr.radi3nt + MathsHelper + 1.0 + compile + + + + \ No newline at end of file diff --git a/Pathfinding/src/main/java/fr/radi3nt/pathfinding/AStarPathfinder.java b/Pathfinding/src/main/java/fr/radi3nt/pathfinding/AStarPathfinder.java new file mode 100644 index 0000000..7781142 --- /dev/null +++ b/Pathfinding/src/main/java/fr/radi3nt/pathfinding/AStarPathfinder.java @@ -0,0 +1,129 @@ +package fr.radi3nt.pathfinding; + +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.pathfinding.heuristic.HeuristicEvaluator; +import fr.radi3nt.pathfinding.node.*; +import fr.radi3nt.pathfinding.path.IndexedPath; +import fr.radi3nt.pathfinding.path.Path; +import fr.radi3nt.pathfinding.path.optimisation.PathOptimiser; +import fr.radi3nt.pathfinding.tracking.Neighbour; +import fr.radi3nt.pathfinding.tracking.queue.PathPointQueue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class AStarPathfinder { + + private static final int MAX_ITERATIONS = 100_000; + private final PathPointQueue pathQueue; + private final PathfinderPointSupplier pathfinderPointSupplier; + private final HeuristicEvaluator heuristicEvaluator; + private final Collection pathOptimisers; + private final float reachThreshold; + private final float pathfindingLimit; + + public AStarPathfinder(PathPointQueue pathBuilder, PathfinderPointSupplier pathfinderPointSupplier, HeuristicEvaluator distanceCalculator, Collection pathOptimisers, float reachThreshold, float pathfindingLimit) { + this.pathQueue = pathBuilder; + this.pathfinderPointSupplier = pathfinderPointSupplier; + this.heuristicEvaluator = distanceCalculator; + this.pathOptimisers = pathOptimisers; + this.reachThreshold = reachThreshold; + this.pathfindingLimit = pathfindingLimit; + } + + public Path find(Vector3f start, Vector3f end) { + pathQueue.clear(); + pathfinderPointSupplier.prepare(); + + FindNode startPoint = pathfinderPointSupplier.pointFromPosition(start); + if (isEnd(end, startPoint)) + return new IndexedPath(new PathNode[0]); + + FindNode lastPoint = startPoint; + float lastDistanceToEnd = heuristicEvaluator.distanceBetween(startPoint.getFloatPosition(), end); + startPoint.set(0, 0, lastDistanceToEnd); + + pathQueue.queue(startPoint); + + boolean endFound = false; + + int iterations = 0; + while (pathQueue.hasPoints() && !endFound && iterations= pathfindingLimit) + continue; + + boolean opened = neighbourPoint.getStatus()== NodeStatus.OPENED; + if (opened && neighbourCostData.accumulatedCostFromStart pathNodes = new ArrayList<>(); + + FindNode current = lastPoint; + while (current!=null) { + pathNodes.add(pathfinderPointSupplier.nodeForPoint(current)); + current = current.getPrevious(); + } + + Collections.reverse(pathNodes); + + for (PathOptimiser pathOptimiser : pathOptimisers) { + pathNodes = pathOptimiser.optimise(pathNodes); + } + + return new IndexedPath(pathNodes.toArray(new PathNode[0])); + } + + private boolean isEnd(Vector3f end, FindNode current) { + return current.getFloatPosition().duplicate().sub(end).lengthSquared() { + + private final Vector3i position; + private final CostData costData; + private FindNode previous; + private NodeStatus nodeStatus = NodeStatus.UNDISCOVERED; + + public FindNode(Vector3i position, float weight) { + this.position = position; + this.costData = new CostData(weight); + } + + public void open() { + nodeStatus = NodeStatus.OPENED; + } + + public void visit() { + nodeStatus = NodeStatus.VISITED; + } + + public NodeStatus getStatus() { + return nodeStatus; + } + + public void set(float distanceFromStart, float accumulatedCostFromStart, float distanceFromEnd) { + costData.set(distanceFromStart, accumulatedCostFromStart, distanceFromEnd); + } + + public void set(float distanceFromStart, float accumulatedCostFromStart, float distanceFromEnd, FindNode previous) { + set(distanceFromStart, accumulatedCostFromStart, distanceFromEnd); + this.previous = previous; + } + + public boolean isCurrentPathFaster(float accumulatedOther) { + return costData.accumulatedCostFromStart optimise(List pathNodes); + +} diff --git a/Pathfinding/src/main/java/fr/radi3nt/pathfinding/path/optimisation/SimplifyPathOptimiser.java b/Pathfinding/src/main/java/fr/radi3nt/pathfinding/path/optimisation/SimplifyPathOptimiser.java new file mode 100644 index 0000000..b04e174 --- /dev/null +++ b/Pathfinding/src/main/java/fr/radi3nt/pathfinding/path/optimisation/SimplifyPathOptimiser.java @@ -0,0 +1,65 @@ +package fr.radi3nt.pathfinding.path.optimisation; + +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.pathfinding.node.PathNode; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +public class SimplifyPathOptimiser implements PathOptimiser { + + private final Collection simplifyPredicates; + + public SimplifyPathOptimiser(Collection simplifyPredicates) { + this.simplifyPredicates = simplifyPredicates; + } + + public SimplifyPathOptimiser(SimplifyPredicate... simplifyPredicates) { + this(new ArrayList<>(Arrays.asList(simplifyPredicates))); + } + + @Override + public List optimise(List pathNodes) { + List result = new ArrayList<>(); + PathNode lastNode = pathNodes.get(0); + Vector3f lastDirection = null; + + result.add(pathNodes.get(0)); + + for (int i = 1; i < pathNodes.size()-1; i++) { + PathNode currentNode = pathNodes.get(i); + PathNode nextNode = pathNodes.get(i+1); + Vector3f currentPos = currentNode.getPosition(); + Vector3f nextPos = nextNode.getPosition(); + + if (lastDirection==null) { + lastDirection = currentPos.duplicate().sub(lastNode.getPosition()); + lastDirection.normalize(); + } + + Vector3f directionToNext = nextPos.duplicate().sub(currentPos); + directionToNext.normalize(); + + if (canSimplify(directionToNext, lastDirection, lastNode, currentNode, nextNode)) + continue; + + result.add(currentNode); + lastNode = currentNode; + lastDirection = null; + } + + result.add(pathNodes.get(pathNodes.size()-1)); + + return result; + } + + protected boolean canSimplify(Vector3f directionToNext, Vector3f lastDirection, PathNode lastPos, PathNode currentPos, PathNode nextPos) { + for (SimplifyPredicate simplifyPredicate : simplifyPredicates) { + if (simplifyPredicate.canSimplify(directionToNext, lastDirection, lastPos, currentPos, nextPos)) + return true; + } + return false; + } +} diff --git a/Pathfinding/src/main/java/fr/radi3nt/pathfinding/path/optimisation/SimplifyPredicate.java b/Pathfinding/src/main/java/fr/radi3nt/pathfinding/path/optimisation/SimplifyPredicate.java new file mode 100644 index 0000000..dc00c07 --- /dev/null +++ b/Pathfinding/src/main/java/fr/radi3nt/pathfinding/path/optimisation/SimplifyPredicate.java @@ -0,0 +1,10 @@ +package fr.radi3nt.pathfinding.path.optimisation; + +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.pathfinding.node.PathNode; + +public interface SimplifyPredicate { + + boolean canSimplify(Vector3f directionToNext, Vector3f lastDirection, PathNode lastPos, PathNode currentPos, PathNode nextPos); + +} diff --git a/Pathfinding/src/main/java/fr/radi3nt/pathfinding/path/optimisation/StraightLineSimplification.java b/Pathfinding/src/main/java/fr/radi3nt/pathfinding/path/optimisation/StraightLineSimplification.java new file mode 100644 index 0000000..158e788 --- /dev/null +++ b/Pathfinding/src/main/java/fr/radi3nt/pathfinding/path/optimisation/StraightLineSimplification.java @@ -0,0 +1,18 @@ +package fr.radi3nt.pathfinding.path.optimisation; + +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.pathfinding.node.PathNode; + +public class StraightLineSimplification implements SimplifyPredicate { + + private final float threshold; + + public StraightLineSimplification(float threshold) { + this.threshold = threshold; + } + + @Override + public boolean canSimplify(Vector3f directionToNext, Vector3f lastDirection, PathNode lastPos, PathNode currentPos, PathNode nextPos) { + return directionToNext.duplicate().sub(lastDirection).lengthSquared() finderPoints = new PriorityQueue<>(); + + @Override + public FindNode dequeue() { + return finderPoints.poll(); + } + + @Override + public void queue(FindNode finderPoint) { + finderPoints.add(finderPoint); + } + + @Override + public boolean hasPoints() { + return !finderPoints.isEmpty(); + } + + @Override + public void clear() { + finderPoints.clear(); + } + + @Override + public void update(FindNode neighbour) { + finderPoints.remove(neighbour); + finderPoints.add(neighbour); + } +} diff --git a/SplineHelper/.gitignore b/SplineHelper/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/SplineHelper/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/SplineHelper/pom.xml b/SplineHelper/pom.xml new file mode 100644 index 0000000..dbfc846 --- /dev/null +++ b/SplineHelper/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + fr.radi3nt + JavaUtil + 1.0 + + + SplineHelper + + + 8 + 8 + UTF-8 + + + + fr.radi3nt + MathsHelper + 1.0 + compile + + + + \ No newline at end of file diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/MainSplineTesting.java b/SplineHelper/src/main/java/fr/radi3nt/spline/MainSplineTesting.java new file mode 100644 index 0000000..6a98521 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/MainSplineTesting.java @@ -0,0 +1,36 @@ +package fr.radi3nt.spline; + +import fr.radi3nt.spline.curve.Curve; +import fr.radi3nt.spline.curve.curves.cardinal.CardinalCurve; +import fr.radi3nt.spline.curve.curves.cardinal.CatmullRomCurve; +import fr.radi3nt.spline.splines.Spline; +import fr.radi3nt.spline.splines.builder.cardinal.dim1.DirectCardinalCurveController; + +public class MainSplineTesting { + + public static void main(String[] args) { + //testCurves(); + } + + private static void printSpline(Spline spline, int times) { + for (int i = 0; i <= 10*times; i++) { + System.out.println(spline.interpolate(i / 10f)); + } + System.out.println("--"); + } + + private static void testCurves() { + Curve curve = new CardinalCurve(new DirectCardinalCurveController(0, 1, 2, 3, 0.5f)); + printCurve(curve, 0); + curve = new CatmullRomCurve(0, 1, 2, 3); + printCurve(curve, 1); + } + + private static void printCurve(Curve curve, int index) { + for (int i = 0; i <= 10; i++) { + System.out.println(curve.interpolate(i / 10f+index)); + } + System.out.println("--"); + } + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/curve/CharacteristicCurve.java b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/CharacteristicCurve.java new file mode 100644 index 0000000..a7b57a4 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/CharacteristicCurve.java @@ -0,0 +1,19 @@ +package fr.radi3nt.spline.curve; + +public abstract class CharacteristicCurve implements Curve { + + @Override + public float interpolate(float t) { + float t2 = t*t; + float t3 = t*t*t; + return computeP(1, t, t2, t3); + } + + @Override + public float velocity(float t) { + float t2 = t*t; + return computeP(0, 1, 2*t, 3*t2); + } + + protected abstract float computeP(float a, float t, float t2, float t3); +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/curve/Curve.java b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/Curve.java new file mode 100644 index 0000000..d85b060 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/Curve.java @@ -0,0 +1,8 @@ +package fr.radi3nt.spline.curve; + +public interface Curve { + + float interpolate(float t); + float velocity(float t); + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/curve/controller/CurveController.java b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/controller/CurveController.java new file mode 100644 index 0000000..7f87ec7 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/controller/CurveController.java @@ -0,0 +1,7 @@ +package fr.radi3nt.spline.curve.controller; + +public interface CurveController { + + + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/HermiteCurve.java b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/HermiteCurve.java new file mode 100644 index 0000000..c5522ca --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/HermiteCurve.java @@ -0,0 +1,32 @@ +package fr.radi3nt.spline.curve.curves; + +import fr.radi3nt.spline.curve.CharacteristicCurve; +import fr.radi3nt.spline.curve.curves.bezier.CubicBezierCurve; +import fr.radi3nt.spline.splines.builder.bezier.dim1.DirectCubicBezierCurveController; + +public class HermiteCurve extends CharacteristicCurve { + + private final float startPoint; + private final float endPoint; + + private final float velocityStart; + private final float velocityEnd; + + public HermiteCurve(float startPoint, float endPoint, float velocityStart, float velocityEnd) { + this.startPoint = startPoint; + this.endPoint = endPoint; + this.velocityStart = velocityStart; + this.velocityEnd = velocityEnd; + } + + public CubicBezierCurve toBezier() { + return new CubicBezierCurve(new DirectCubicBezierCurveController(startPoint, endPoint, startPoint+velocityStart/3, endPoint-velocityEnd/3)); + } + + protected float computeP(float a, float t, float t2, float t3) { + return a * startPoint + + t * (velocityStart) + + t2 * (-3*startPoint - 2*velocityStart + 3*endPoint - velocityEnd) + + t3 * (2*startPoint + velocityStart - 2*endPoint + velocityEnd); + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/bcurve/BCurve.java b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/bcurve/BCurve.java new file mode 100644 index 0000000..f1da16b --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/bcurve/BCurve.java @@ -0,0 +1,29 @@ +package fr.radi3nt.spline.curve.curves.bcurve; + +import fr.radi3nt.spline.curve.CharacteristicCurve; + +public class BCurve extends CharacteristicCurve { + + private final BCurveController controller; + + public BCurve(BCurveController controller) { + this.controller = controller; + } + + @Override + protected float computeP(float a, float t, float t2, float t3) { + float pointA = controller.getPositionA(); + float pointB = controller.getPositionB(); + float pointC = controller.getPositionC(); + float pointD = controller.getPositionD(); + float p = a * (pointA + 4*pointB + pointC) + + t *(-3*pointA + 3*pointC) + + t2 *(3*pointA - 6*pointB + 3*pointC) + + t3 *(-pointA + 3*pointB - 3*pointC + pointD); + return p/6; + } + + public BCurveController getController() { + return controller; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/bcurve/BCurveController.java b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/bcurve/BCurveController.java new file mode 100644 index 0000000..bfced96 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/bcurve/BCurveController.java @@ -0,0 +1,12 @@ +package fr.radi3nt.spline.curve.curves.bcurve; + +import fr.radi3nt.spline.curve.controller.CurveController; + +public interface BCurveController extends CurveController { + + float getPositionA(); + float getPositionB(); + float getPositionC(); + float getPositionD(); + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/bezier/CubicBezierCurve.java b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/bezier/CubicBezierCurve.java new file mode 100644 index 0000000..97a94b4 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/bezier/CubicBezierCurve.java @@ -0,0 +1,30 @@ +package fr.radi3nt.spline.curve.curves.bezier; + +import fr.radi3nt.spline.curve.CharacteristicCurve; + +public class CubicBezierCurve extends CharacteristicCurve { + + private final CubicBezierCurveController controller; + + public CubicBezierCurve(CubicBezierCurveController controller) { + this.controller = controller; + } + + protected float computeP(float a, float t, float t2, float t3) { + + float startPoint = controller.getStartPoint(); + float startPointControl = controller.getStartPointControl(); + + float endPoint = controller.getEndPoint(); + float endPointControl = controller.getEndPointControl(); + + return a * startPoint + + t *(-3*startPoint+3*startPointControl) + + t2 *(3*startPoint-6*startPointControl+3*endPointControl) + + t3 *(-startPoint+3*startPointControl-3*endPointControl+endPoint); + } + + public CubicBezierCurveController getController() { + return controller; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/bezier/CubicBezierCurveController.java b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/bezier/CubicBezierCurveController.java new file mode 100644 index 0000000..82fd6e8 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/bezier/CubicBezierCurveController.java @@ -0,0 +1,11 @@ +package fr.radi3nt.spline.curve.curves.bezier; + +public interface CubicBezierCurveController { + + float getStartPoint(); + float getStartPointControl(); + + float getEndPoint(); + float getEndPointControl(); + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/cardinal/CardinalCurve.java b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/cardinal/CardinalCurve.java new file mode 100644 index 0000000..6bdd66d --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/cardinal/CardinalCurve.java @@ -0,0 +1,27 @@ +package fr.radi3nt.spline.curve.curves.cardinal; + +import fr.radi3nt.spline.curve.CharacteristicCurve; + +public class CardinalCurve extends CharacteristicCurve { + + private final CardinalCurveController cardinalCurveController; + + public CardinalCurve(CardinalCurveController cardinalCurveController) { + this.cardinalCurveController = cardinalCurveController; + } + + @Override + protected float computeP(float a, float t, float t2, float t3) { + + float pointA = cardinalCurveController.getPositionA(); + float pointB = cardinalCurveController.getPositionB(); + float pointC = cardinalCurveController.getPositionC(); + float pointD = cardinalCurveController.getPositionD(); + float scale = cardinalCurveController.getScale(); + + return a * pointB + + t *(-pointA*scale + pointC*scale) + + t2 *(2*pointA*scale + (scale-3)*pointB + (3-2*scale)*pointC - pointD*scale) + + t3 *(-pointA*scale + (2-scale)*pointB + (scale-2)*pointC + pointD*scale); + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/cardinal/CardinalCurveController.java b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/cardinal/CardinalCurveController.java new file mode 100644 index 0000000..1cc3239 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/cardinal/CardinalCurveController.java @@ -0,0 +1,14 @@ +package fr.radi3nt.spline.curve.curves.cardinal; + +import fr.radi3nt.spline.curve.controller.CurveController; + +public interface CardinalCurveController extends CurveController { + + float getPositionA(); + float getPositionB(); + float getPositionC(); + float getPositionD(); + + float getScale(); + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/cardinal/CatmullRomCurve.java b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/cardinal/CatmullRomCurve.java new file mode 100644 index 0000000..8d491bd --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/curve/curves/cardinal/CatmullRomCurve.java @@ -0,0 +1,27 @@ +package fr.radi3nt.spline.curve.curves.cardinal; + +import fr.radi3nt.spline.curve.CharacteristicCurve; + +public class CatmullRomCurve extends CharacteristicCurve { + + private final float pointA; + private final float pointB; + private final float pointC; + private final float pointD; + + public CatmullRomCurve(float pointA, float pointB, float pointC, float pointD) { + this.pointA = pointA; + this.pointB = pointB; + this.pointC = pointC; + this.pointD = pointD; + } + + @Override + protected float computeP(float a, float t, float t2, float t3) { + float p = a * 2 * pointB + + t *(-pointA+pointC) + + t2 *(2*pointA-5*pointB+4*pointC-pointD) + + t3 *(-pointA+3*pointB-3*pointC+pointD); + return p/2; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/imports/SplineReader.java b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/SplineReader.java new file mode 100644 index 0000000..9b42944 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/SplineReader.java @@ -0,0 +1,69 @@ +package fr.radi3nt.spline.imports; + +import fr.radi3nt.spline.imports.key.CurveHandleData; +import fr.radi3nt.spline.imports.key.InterpolationType; +import fr.radi3nt.spline.imports.key.KeyData; +import fr.radi3nt.spline.imports.spline.KeyedSplineBuilder; +import fr.radi3nt.spline.splines.dimensions.Spline2D; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class SplineReader { + + private final KeyedSplineBuilder splineBuilder; + + public SplineReader(KeyedSplineBuilder splineBuilder) { + this.splineBuilder = splineBuilder; + } + + public Spline2D build(BufferedReader content, float scaleX) throws IOException { + return splineBuilder.build(getKeyData(content), scaleX); + } + + private List getKeyData(BufferedReader content) throws IOException { + List keyData = new ArrayList<>(); + String line; + while ((line = content.readLine())!=null) { + line = formatLine(line); + if (line.isEmpty()) + continue; + if (line.startsWith("}")) + break; + String[] splitLine = line.split(" "); + float frameIndex = Float.parseFloat(splitLine[0]); + float correspondingValue = Float.parseFloat(splitLine[1]); + InterpolationType inInterpolation = InterpolationType.valueOf(splitLine[2].toUpperCase()); + InterpolationType outInterpolation = InterpolationType.valueOf(splitLine[3].toUpperCase()); + boolean tanLocked = stringToBool(splitLine[4]); + boolean weightLocked = stringToBool(splitLine[5]); + + int additionalDataAmount = splitLine.length-7; + int curveHandleDataAmount = Math.floorDiv(additionalDataAmount, 2); + CurveHandleData[] curveHandleData = new CurveHandleData[curveHandleDataAmount]; + for (int i = 0; i < curveHandleDataAmount; i++) { + String tanAngle = splitLine[7+i*2]; + String tanWeight = splitLine[8+i*2]; + curveHandleData[i] = new CurveHandleData(Float.parseFloat(tanAngle), Float.parseFloat(tanWeight)); + } + + keyData.add(new KeyData(frameIndex, correspondingValue, inInterpolation, outInterpolation, tanLocked, weightLocked, curveHandleData)); + } + return keyData; + } + + private String formatLine(String s) { + String formatted = s.trim(); + int index = formatted.lastIndexOf(";"); + if (index!=-1) + formatted = formatted.substring(0, index); + return formatted; + } + + private boolean stringToBool(String str) { + return str.equals("1"); + } + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/imports/key/CurveHandleData.java b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/key/CurveHandleData.java new file mode 100644 index 0000000..ab5d73c --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/key/CurveHandleData.java @@ -0,0 +1,28 @@ +package fr.radi3nt.spline.imports.key; + +public class CurveHandleData { + + private final float tangentAngle; + private final float tangentWeight; + + public CurveHandleData(float tangentAngle, float tangentWeight) { + this.tangentAngle = tangentAngle; + this.tangentWeight = tangentWeight; + } + + public float getTangentAngle() { + return tangentAngle; + } + + public float getTangentWeight() { + return tangentWeight; + } + + @Override + public String toString() { + return "CurveHandleData{" + + "tangentAngle=" + tangentAngle + + ", tangentWeight=" + tangentWeight + + '}'; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/imports/key/InterpolationType.java b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/key/InterpolationType.java new file mode 100644 index 0000000..664199b --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/key/InterpolationType.java @@ -0,0 +1,12 @@ +package fr.radi3nt.spline.imports.key; + +public enum InterpolationType { + + LINEAR, + AUTO, + SPLINE, + FIXED, + STEP, + FLAT, + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/imports/key/KeyData.java b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/key/KeyData.java new file mode 100644 index 0000000..8fb80f6 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/key/KeyData.java @@ -0,0 +1,69 @@ +package fr.radi3nt.spline.imports.key; + +import java.util.Arrays; + +public class KeyData { + + private final float x; + private final float y; + + private final InterpolationType inInterpolation; + private final InterpolationType outInterpolation; + + private final boolean tangentLocked; + private final boolean weightLocked; + + private final CurveHandleData[] curveHandleData; + + + public KeyData(float x, float y, InterpolationType inInterpolation, InterpolationType outInterpolation, boolean tangentLocked, boolean weightLocked, CurveHandleData[] curveHandleData) { + this.x = x; + this.y = y; + this.inInterpolation = inInterpolation; + this.outInterpolation = outInterpolation; + this.tangentLocked = tangentLocked; + this.weightLocked = weightLocked; + this.curveHandleData = curveHandleData; + } + + public float getX() { + return x; + } + + public float getY() { + return y; + } + + public InterpolationType getInInterpolation() { + return inInterpolation; + } + + public InterpolationType getOutInterpolation() { + return outInterpolation; + } + + public boolean isTangentLocked() { + return tangentLocked; + } + + public boolean isWeightLocked() { + return weightLocked; + } + + public CurveHandleData[] getCurveHandleData() { + return curveHandleData; + } + + @Override + public String toString() { + return "KeyData{" + + "x=" + x + + ", y=" + y + + ", inInterpolation=" + inInterpolation + + ", outInterpolation=" + outInterpolation + + ", tangentLocked=" + tangentLocked + + ", weightLocked=" + weightLocked + + ", curveHandleData=" + Arrays.toString(curveHandleData) + + '}'; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/imports/key/KeyframesData.java b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/key/KeyframesData.java new file mode 100644 index 0000000..edab524 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/key/KeyframesData.java @@ -0,0 +1,32 @@ +package fr.radi3nt.spline.imports.key; + +import fr.radi3nt.spline.splines.dimensions.Spline2D; + +import java.util.List; + +public class KeyframesData { + + private final List keyData; + private final Spline2D dataSpline; + + public KeyframesData(List keyData, Spline2D dataSpline) { + this.keyData = keyData; + this.dataSpline = dataSpline; + } + + public List getKeyData() { + return keyData; + } + + public Spline2D getDataSpline() { + return dataSpline; + } + + @Override + public String toString() { + return "KeyframesData{" + + "keyData=" + keyData + + ", dataSpline=" + dataSpline + + '}'; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/BezierKeyedSplineBuilder.java b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/BezierKeyedSplineBuilder.java new file mode 100644 index 0000000..fca7823 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/BezierKeyedSplineBuilder.java @@ -0,0 +1,111 @@ +package fr.radi3nt.spline.imports.spline; + +import fr.radi3nt.spline.imports.key.CurveHandleData; +import fr.radi3nt.spline.imports.key.InterpolationType; +import fr.radi3nt.spline.imports.key.KeyData; +import fr.radi3nt.spline.imports.spline.handle.AutoHandleDataBuilder; +import fr.radi3nt.spline.imports.spline.handle.FixedHandleDataBuilder; +import fr.radi3nt.spline.imports.spline.handle.GeneratedHandleDataBuilder; +import fr.radi3nt.spline.curve.Curve; +import fr.radi3nt.spline.curve.curves.bezier.CubicBezierCurve; +import fr.radi3nt.spline.splines.CollectionSpline; +import fr.radi3nt.spline.splines.Spline; +import fr.radi3nt.spline.splines.builder.bezier.dim1.DirectCubicBezierCurveController; +import fr.radi3nt.spline.splines.dimensions.EncapsulatingSpline2D; +import fr.radi3nt.spline.splines.dimensions.Spline2D; + +import java.util.*; + +public class BezierKeyedSplineBuilder implements KeyedSplineBuilder { + + + private static final Map DEFAULT_MAP = new HashMap<>(); + static { + DEFAULT_MAP.put(InterpolationType.FIXED, new FixedHandleDataBuilder()); + DEFAULT_MAP.put(InterpolationType.AUTO, new AutoHandleDataBuilder()); + DEFAULT_MAP.put(InterpolationType.SPLINE, new GeneratedHandleDataBuilder()); + } + + public static final Map DEFAULT_INTERPOLATIONS = Collections.unmodifiableMap(DEFAULT_MAP); + + + private final Map curveHandleDataBuilderMap; + private final float offsetX; + private final float offsetY; + private final float scaleX; + private final float scaleY; + + public BezierKeyedSplineBuilder(Map curveHandleDataBuilderMap, float offsetX, float offsetY, float scaleX, float scaleY) { + this.curveHandleDataBuilderMap = curveHandleDataBuilderMap; + this.offsetX = offsetX; + this.offsetY = offsetY; + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + public static KeyedSplineBuilder newDefault(float scaleX, float scaleY) { + return new BezierKeyedSplineBuilder(DEFAULT_INTERPOLATIONS, 0, 0, scaleX, scaleY); + } + + public static KeyedSplineBuilder newDefault(float offsetX, float offsetY, float scaleX, float scaleY) { + return new BezierKeyedSplineBuilder(DEFAULT_INTERPOLATIONS, offsetX, offsetY, scaleX, scaleY); + } + + @Override + public Spline2D build(List keyData, float scaleX) { + List xCurves = new ArrayList<>(); + List yCurves = new ArrayList<>(); + Spline xSpline = new CollectionSpline(xCurves); + Spline ySpline = new CollectionSpline(yCurves); + for (int i = 0; i < keyData.size()-1; i++) { + KeyData start = keyData.get(i); + KeyData end = keyData.get(i+1); + + InterpolationType startSplineType = start.getOutInterpolation(); + InterpolationType endSplineType = end.getInInterpolation(); + + CurveHandleData startCurveData = curveHandleDataBuilderMap.get(startSplineType).buildStartHandleData(keyData, i, start, end); + CurveHandleData endCurveData = curveHandleDataBuilderMap.get(endSplineType).buildEndHandleData(keyData, i, start, end); + + float cappedStartLength = capTangentLength(end.getX()-start.getX(), startCurveData); + float cappedEndLength = capTangentLength(end.getX()-start.getX(), endCurveData); + + float startXPointControl =(start.getX()+calcPosX(startCurveData, cappedStartLength)); + float endXPointControl = (end.getX()-calcPosX(endCurveData, cappedEndLength)); + + float startYPointControl = (start.getY()+calcPosY(startCurveData, cappedStartLength)); + float endYPointControl = (end.getY()-calcPosY(endCurveData, cappedEndLength)); + + CubicBezierCurve curveX = new CubicBezierCurve(new DirectCubicBezierCurveController( + start.getX()* this.scaleX *scaleX+offsetX, + end.getX()* this.scaleX *scaleX+offsetX, + startXPointControl* this.scaleX *scaleX+offsetX, + endXPointControl* this.scaleX *scaleX+offsetX)); + CubicBezierCurve curveY = new CubicBezierCurve(new DirectCubicBezierCurveController( + start.getY()*scaleY+offsetY, + end.getY()*scaleY+offsetY, + startYPointControl*scaleY+offsetY, + endYPointControl*scaleY+offsetY)); + + xCurves.add(curveX); + yCurves.add(curveY); + } + + return new EncapsulatingSpline2D(xSpline, ySpline); + } + + + private float capTangentLength(float frameDelta, CurveHandleData curveHandleData) { + float angleProjected = (float) Math.cos(Math.toRadians(curveHandleData.getTangentAngle())); + float maxLength = frameDelta/angleProjected; + return Math.min(curveHandleData.getTangentWeight(), maxLength); + } + + private float calcPosX(CurveHandleData curveHandleData, float length) { + return (float) (Math.cos(Math.toRadians(curveHandleData.getTangentAngle()))*length); + } + + private float calcPosY(CurveHandleData curveHandleData, float length) { + return (float) (Math.sin(Math.toRadians(curveHandleData.getTangentAngle()))*length); + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/CurveHandleDataBuilder.java b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/CurveHandleDataBuilder.java new file mode 100644 index 0000000..ba1a332 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/CurveHandleDataBuilder.java @@ -0,0 +1,13 @@ +package fr.radi3nt.spline.imports.spline; + +import fr.radi3nt.spline.imports.key.CurveHandleData; +import fr.radi3nt.spline.imports.key.KeyData; + +import java.util.List; + +public interface CurveHandleDataBuilder { + + CurveHandleData buildStartHandleData(List keyData, int i, KeyData start, KeyData end); + CurveHandleData buildEndHandleData(List keyData, int i, KeyData start, KeyData end); + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/KeyedSplineBuilder.java b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/KeyedSplineBuilder.java new file mode 100644 index 0000000..a967baa --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/KeyedSplineBuilder.java @@ -0,0 +1,12 @@ +package fr.radi3nt.spline.imports.spline; + +import fr.radi3nt.spline.imports.key.KeyData; +import fr.radi3nt.spline.splines.dimensions.Spline2D; + +import java.util.List; + +public interface KeyedSplineBuilder { + + Spline2D build(List keys, float intervalDuration); + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/handle/AutoHandleDataBuilder.java b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/handle/AutoHandleDataBuilder.java new file mode 100644 index 0000000..10ef47d --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/handle/AutoHandleDataBuilder.java @@ -0,0 +1,11 @@ +package fr.radi3nt.spline.imports.spline.handle; + +import fr.radi3nt.maths.components.vectors.Vector2f; + +public class AutoHandleDataBuilder extends GeneratedHandleDataBuilder { + + @Override + protected float getHandleAngle(Vector2f direction, float dirLength) { + return 0f; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/handle/FixedHandleDataBuilder.java b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/handle/FixedHandleDataBuilder.java new file mode 100644 index 0000000..c568eec --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/handle/FixedHandleDataBuilder.java @@ -0,0 +1,19 @@ +package fr.radi3nt.spline.imports.spline.handle; + +import fr.radi3nt.spline.imports.key.CurveHandleData; +import fr.radi3nt.spline.imports.key.KeyData; +import fr.radi3nt.spline.imports.spline.CurveHandleDataBuilder; + +import java.util.List; + +public class FixedHandleDataBuilder implements CurveHandleDataBuilder { + @Override + public CurveHandleData buildStartHandleData(List keyData, int i, KeyData start, KeyData end) { + return start.getCurveHandleData()[start.getCurveHandleData().length-1]; + } + + @Override + public CurveHandleData buildEndHandleData(List keyData, int i, KeyData start, KeyData end) { + return end.getCurveHandleData()[0]; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/handle/GeneratedHandleDataBuilder.java b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/handle/GeneratedHandleDataBuilder.java new file mode 100644 index 0000000..96d251e --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/imports/spline/handle/GeneratedHandleDataBuilder.java @@ -0,0 +1,63 @@ +package fr.radi3nt.spline.imports.spline.handle; + +import fr.radi3nt.spline.imports.key.CurveHandleData; +import fr.radi3nt.spline.imports.key.KeyData; +import fr.radi3nt.spline.imports.spline.CurveHandleDataBuilder; +import fr.radi3nt.maths.components.vectors.Vector2f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector2f; + +import java.util.List; + +public class GeneratedHandleDataBuilder implements CurveHandleDataBuilder { + @Override + public CurveHandleData buildStartHandleData(List keyData, int i, KeyData start, KeyData end) { + Vector2f endPos = new SimpleVector2f(end.getX(), end.getY()); + Vector2f beforeOnePos = new SimpleVector2f(start.getX(), end.getY()); + + float closeness = 0.5f; + float lengthMultiplier = 2; + if (i!=0) { + lengthMultiplier = 1f; + KeyData startBefore = keyData.get(i-1); + beforeOnePos = new SimpleVector2f(startBefore.getX(), startBefore.getY()); + closeness = (start.getX()-beforeOnePos.getX())/(endPos.getX()-beforeOnePos.getX()); + } + + Vector2f direction = endPos.clone().sub(beforeOnePos.clone()); + float dirLength = direction.length(); + float angle = getHandleAngle(direction, dirLength); + + return new CurveHandleData(angle, getTangentWeight(1 - closeness, dirLength, lengthMultiplier)); + } + + @Override + public CurveHandleData buildEndHandleData(List keyData, int i, KeyData start, KeyData end) { + Vector2f startPos = new SimpleVector2f(start.getX(), start.getY()); + Vector2f afterEndPos = new SimpleVector2f(end.getX(), end.getY()); + + float closeness = 0.5f; + float lengthMultiplier = 2; + + if (i=splineSegments) + currentGuess = splineSegments; + if (currentGuess<0) + currentGuess = 0; + float value = spline.interpolateX(currentGuess); + if (closeEnough(value, expectedX)) { + break; + } + float vel = spline.velocityX(currentGuess); + if (Math.abs(vel)<=EPSILON) + vel = EPSILON*Math.copySign(1, vel); //preventing catastrophic failure + currentGuess = currentGuess - (value-expectedX)/(vel); + } + + if (currentGuess>=splineSegments) + currentGuess = splineSegments; + if (currentGuess<0) + currentGuess = 0; + + return currentGuess; + } + + private static boolean closeEnough(float currentGuess, float expectedX) { + return abs(currentGuess-expectedX)=plotted.length) + return plotted[plotted.length-1]; + + if (indexPlusOne>=plotted.length) + return plotted[index]; + + float startValue = plotted[index]; + float endValue = plotted[indexPlusOne]; + float relativeTime = t/plotInterval - index; + + return relativeTime*endValue+(1-relativeTime)*startValue; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/CollectionSpline.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/CollectionSpline.java new file mode 100644 index 0000000..951453f --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/CollectionSpline.java @@ -0,0 +1,67 @@ +package fr.radi3nt.spline.splines; + +import fr.radi3nt.spline.curve.Curve; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class CollectionSpline implements Spline { + + protected final List curves; + + public CollectionSpline(List curves) { + this.curves = curves; + } + + @Override + public float interpolate(float t) { + int index = (int) Math.floor(t); + if (t==curves.size()) { + index = curves.size()-1; + if (curves.isEmpty()) + index = 0; + } + Curve curve = getCurveAtIndex(index); + return curve.interpolate(t-index); + } + + @Override + public float velocity(float t) { + int index = (int) Math.floor(t); + if (t==curves.size()) { + index = curves.size()-1; + if (curves.isEmpty()) + index = 0; + } + + Curve curve = getCurveAtIndex(index); + return curve.velocity(t-index); + } + + @Override + public int getSegmentCount() { + return curves.size(); + } + + protected Curve getCurveAtIndex(int index) { + checkForValidIndex(index); + return curves.get(index); + } + + private void checkForValidIndex(int index) { + if (index<0 || index>= curves.size()) + throw new IllegalArgumentException("Index is not in bounds 0 < " + curves.size() + " (index being: " + index + ")"); + } + + public List getCurves() { + return Collections.unmodifiableList(curves); + } + + @Override + public String toString() { + return "CollectionSpline{" + + "curves=" + Arrays.toString(curves.toArray(new Curve[0])) + + '}'; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/Spline.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/Spline.java new file mode 100644 index 0000000..4686aa0 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/Spline.java @@ -0,0 +1,9 @@ +package fr.radi3nt.spline.splines; + +public interface Spline { + + float interpolate(float t); + float velocity(float t); + + int getSegmentCount(); +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/PositionnedSplineController.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/PositionnedSplineController.java new file mode 100644 index 0000000..5108733 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/PositionnedSplineController.java @@ -0,0 +1,7 @@ +package fr.radi3nt.spline.splines.builder; + +public interface PositionnedSplineController extends SplineController { + + void setPosition(int index, float pos); + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/SplineBuilder.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/SplineBuilder.java new file mode 100644 index 0000000..f2d1482 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/SplineBuilder.java @@ -0,0 +1,9 @@ +package fr.radi3nt.spline.splines.builder; + +import fr.radi3nt.spline.splines.Spline; + +public interface SplineBuilder { + + Spline build(); + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/SplineController.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/SplineController.java new file mode 100644 index 0000000..75b89ed --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/SplineController.java @@ -0,0 +1,4 @@ +package fr.radi3nt.spline.splines.builder; + +public interface SplineController { +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim1/BezierPoint.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim1/BezierPoint.java new file mode 100644 index 0000000..4107b3a --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim1/BezierPoint.java @@ -0,0 +1,26 @@ +package fr.radi3nt.spline.splines.builder.bezier.dim1; + +public class BezierPoint { + + private final float point; + private final float pointControlBefore; + private final float pointControlAfter; + + public BezierPoint(float point, float pointControlBefore, float pointControlAfter) { + this.point = point; + this.pointControlBefore = pointControlBefore; + this.pointControlAfter = pointControlAfter; + } + + public float getPoint() { + return point; + } + + public float getPointControlBefore() { + return pointControlBefore; + } + + public float getPointControlAfter() { + return pointControlAfter; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim1/CubicBezierSplineBuilder.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim1/CubicBezierSplineBuilder.java new file mode 100644 index 0000000..8299d48 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim1/CubicBezierSplineBuilder.java @@ -0,0 +1,34 @@ +package fr.radi3nt.spline.splines.builder.bezier.dim1; + +import fr.radi3nt.spline.curve.curves.bezier.CubicBezierCurve; +import fr.radi3nt.spline.splines.CollectionSpline; +import fr.radi3nt.spline.splines.Spline; +import fr.radi3nt.spline.splines.builder.SplineBuilder; + +import java.util.ArrayList; +import java.util.List; + +public class CubicBezierSplineBuilder implements SplineBuilder { + + private final CollectionSpline spline; + private final CubicBezierSplineController controller; + + public CubicBezierSplineBuilder(BezierPoint[] pos) { + controller = new CubicBezierSplineController(pos); + List curves = new ArrayList<>(); + for (int i = 0; i < pos.length-1; i++) { + CubicBezierCurve cardinal = new CubicBezierCurve(controller.createForIndex(i)); + curves.add(cardinal); + } + spline = new CollectionSpline(curves); + } + + @Override + public Spline build() { + return spline; + } + + public CubicBezierSplineController controller() { + return controller; + } +} \ No newline at end of file diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim1/CubicBezierSplineController.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim1/CubicBezierSplineController.java new file mode 100644 index 0000000..d6e1ccb --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim1/CubicBezierSplineController.java @@ -0,0 +1,22 @@ +package fr.radi3nt.spline.splines.builder.bezier.dim1; + +import fr.radi3nt.spline.curve.curves.bezier.CubicBezierCurveController; +import fr.radi3nt.spline.splines.builder.SplineController; + +public class CubicBezierSplineController implements SplineController { + + private final BezierPoint[] positions; + + public CubicBezierSplineController(BezierPoint[] positions) { + this.positions = positions; + } + + public void setPoint(BezierPoint point, int index) { + positions[index] = point; + } + + public CubicBezierCurveController createForIndex(int index) { + return new InferredCubicBezierCurveController(index, positions); + } + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim1/DirectCubicBezierCurveController.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim1/DirectCubicBezierCurveController.java new file mode 100644 index 0000000..a559d3b --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim1/DirectCubicBezierCurveController.java @@ -0,0 +1,39 @@ +package fr.radi3nt.spline.splines.builder.bezier.dim1; + +import fr.radi3nt.spline.curve.curves.bezier.CubicBezierCurveController; + +public class DirectCubicBezierCurveController implements CubicBezierCurveController { + + private final float startPoint; + private final float endPoint; + + private final float startPointControl; + private final float endPointControl; + + public DirectCubicBezierCurveController(float startPoint, float endPoint, float startPointControl, float endPointControl) { + this.startPoint = startPoint; + this.endPoint = endPoint; + this.startPointControl = startPointControl; + this.endPointControl = endPointControl; + } + + @Override + public float getStartPoint() { + return startPoint; + } + + @Override + public float getEndPoint() { + return endPoint; + } + + @Override + public float getStartPointControl() { + return startPointControl; + } + + @Override + public float getEndPointControl() { + return endPointControl; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim1/InferredCubicBezierCurveController.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim1/InferredCubicBezierCurveController.java new file mode 100644 index 0000000..4e7bfc0 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim1/InferredCubicBezierCurveController.java @@ -0,0 +1,35 @@ +package fr.radi3nt.spline.splines.builder.bezier.dim1; + +import fr.radi3nt.spline.curve.curves.bezier.CubicBezierCurveController; + +public class InferredCubicBezierCurveController implements CubicBezierCurveController { + + private final int index; + + private final BezierPoint[] pos; + + public InferredCubicBezierCurveController(int index, BezierPoint[] pos) { + this.index = index; + this.pos = pos; + } + + @Override + public float getStartPoint() { + return pos[index].getPoint(); + } + + @Override + public float getStartPointControl() { + return pos[index].getPointControlAfter(); + } + + @Override + public float getEndPoint() { + return pos[index+1].getPoint(); + } + + @Override + public float getEndPointControl() { + return pos[index+1].getPointControlBefore(); + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim2/BezierPoint2D.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim2/BezierPoint2D.java new file mode 100644 index 0000000..0648a24 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim2/BezierPoint2D.java @@ -0,0 +1,26 @@ +package fr.radi3nt.spline.splines.builder.bezier.dim2; + +import fr.radi3nt.spline.splines.builder.bezier.dim1.BezierPoint; + +public class BezierPoint2D { + + private final BezierPoint xAxis; + private final BezierPoint yAxis; + + public BezierPoint2D(BezierPoint xAxis, BezierPoint yAxis) { + this.xAxis = xAxis; + this.yAxis = yAxis; + } + + public BezierPoint2D(float pointX, float pointControlBeforeX, float pointControlAfterX, float pointY, float pointControlBeforeY, float pointControlAfterY) { + this(new BezierPoint(pointX, pointControlBeforeX, pointControlAfterX), new BezierPoint(pointY, pointControlBeforeY, pointControlAfterY)); + } + + public BezierPoint getXAxis() { + return xAxis; + } + + public BezierPoint getYAxis() { + return yAxis; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim2/CubicBezierSplineBuilder2D.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim2/CubicBezierSplineBuilder2D.java new file mode 100644 index 0000000..d59f349 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim2/CubicBezierSplineBuilder2D.java @@ -0,0 +1,34 @@ +package fr.radi3nt.spline.splines.builder.bezier.dim2; + +import fr.radi3nt.spline.splines.builder.bezier.dim1.BezierPoint; +import fr.radi3nt.spline.splines.builder.bezier.dim1.CubicBezierSplineBuilder; +import fr.radi3nt.spline.splines.dimensions.EncapsulatingSpline2D; +import fr.radi3nt.spline.splines.dimensions.Spline2D; + +public class CubicBezierSplineBuilder2D { + + private final CubicBezierSplineBuilder[] splineBuilders = new CubicBezierSplineBuilder[3]; + + public CubicBezierSplineBuilder2D(BezierPoint2D... positions) { + BezierPoint[] xPos = new BezierPoint[positions.length]; + BezierPoint[] yPos = new BezierPoint[positions.length]; + + for (int i = 0; i < positions.length; i++) { + BezierPoint2D position = positions[i]; + xPos[i] = position.getXAxis(); + yPos[i] = position.getYAxis(); + } + + splineBuilders[0] = new CubicBezierSplineBuilder(xPos); + splineBuilders[1] = new CubicBezierSplineBuilder(yPos); + } + + public Spline2D build() { + return new EncapsulatingSpline2D(splineBuilders[0].build(), splineBuilders[1].build()); + } + + public CubicBezierSplineController2D controller() { + return new CubicBezierSplineController2D(splineBuilders[0].controller(), splineBuilders[1].controller()); + } + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim2/CubicBezierSplineController2D.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim2/CubicBezierSplineController2D.java new file mode 100644 index 0000000..727f74f --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim2/CubicBezierSplineController2D.java @@ -0,0 +1,18 @@ +package fr.radi3nt.spline.splines.builder.bezier.dim2; + +import fr.radi3nt.spline.splines.builder.bezier.dim1.CubicBezierSplineController; + +public class CubicBezierSplineController2D { + + private final CubicBezierSplineController[] controllers; + + public CubicBezierSplineController2D(CubicBezierSplineController... controllers) { + this.controllers = controllers; + } + + + public void setPosition(BezierPoint2D point, int index) { + controllers[0].setPoint(point.getXAxis(), index); + controllers[1].setPoint(point.getYAxis(), index); + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim3/BezierPoint3D.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim3/BezierPoint3D.java new file mode 100644 index 0000000..6690a0e --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim3/BezierPoint3D.java @@ -0,0 +1,37 @@ +package fr.radi3nt.spline.splines.builder.bezier.dim3; + +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.spline.splines.builder.bezier.dim1.BezierPoint; + +public class BezierPoint3D { + + private final BezierPoint xAxis; + private final BezierPoint yAxis; + private final BezierPoint zAxis; + + public BezierPoint3D(BezierPoint xAxis, BezierPoint yAxis, BezierPoint zAxis) { + this.xAxis = xAxis; + this.yAxis = yAxis; + this.zAxis = zAxis; + } + + public BezierPoint3D(float pointX, float pointControlBeforeX, float pointControlAfterX, float pointY, float pointControlBeforeY, float pointControlAfterY, float pointZ, float pointControlBeforeZ, float pointControlAfterZ) { + this(new BezierPoint(pointX, pointControlBeforeX, pointControlAfterX), new BezierPoint(pointY, pointControlBeforeY, pointControlAfterY), new BezierPoint(pointZ, pointControlBeforeZ, pointControlAfterZ)); + } + + public BezierPoint3D(Vector3f position, Vector3f beforeDirection, Vector3f afterDirection) { + this(new BezierPoint(position.getX(), beforeDirection.getX(), afterDirection.getX()), new BezierPoint(position.getY(), beforeDirection.getY(), afterDirection.getY()), new BezierPoint(position.getZ(), beforeDirection.getZ(), afterDirection.getZ())); + } + + public BezierPoint getXAxis() { + return xAxis; + } + + public BezierPoint getYAxis() { + return yAxis; + } + + public BezierPoint getZAxis() { + return zAxis; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim3/CubicBezierSplineBuilder3D.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim3/CubicBezierSplineBuilder3D.java new file mode 100644 index 0000000..71bca23 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim3/CubicBezierSplineBuilder3D.java @@ -0,0 +1,62 @@ +package fr.radi3nt.spline.splines.builder.bezier.dim3; + +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.spline.splines.builder.bezier.dim1.BezierPoint; +import fr.radi3nt.spline.splines.builder.bezier.dim1.CubicBezierSplineBuilder; +import fr.radi3nt.spline.splines.dimensions.EncapsulatingSpline3D; +import fr.radi3nt.spline.splines.dimensions.Spline3D; + +public class CubicBezierSplineBuilder3D { + + private final CubicBezierSplineBuilder[] splineBuilders = new CubicBezierSplineBuilder[3]; + + public static CubicBezierSplineBuilder3D fromTwoPoints(Vector3f pointA, Vector3f dirA, Vector3f pointB, Vector3f dirB) { + return new CubicBezierSplineBuilder3D( + new BezierPoint3D(pointA.getX(), 0, pointA.getX()+dirA.getX(), pointA.getY(), 0, pointA.getY()+dirA.getY(), pointA.getZ(), 0, pointA.getZ()+dirA.getZ()), + new BezierPoint3D(pointB.getX(), pointB.getX()+dirB.getX(), 0, pointB.getY(), pointB.getY()+dirB.getY(), 0, pointB.getZ(), pointB.getZ()+dirB.getZ(), 0) + ); + } + + public CubicBezierSplineBuilder3D(BezierPoint3D... positions) { + BezierPoint[] xPos = new BezierPoint[positions.length]; + BezierPoint[] yPos = new BezierPoint[positions.length]; + BezierPoint[] zPos = new BezierPoint[positions.length]; + + for (int i = 0; i < positions.length; i++) { + BezierPoint3D position = positions[i]; + xPos[i] = position.getXAxis(); + yPos[i] = position.getYAxis(); + zPos[i] = position.getZAxis(); + } + + splineBuilders[0] = new CubicBezierSplineBuilder(xPos); + splineBuilders[1] = new CubicBezierSplineBuilder(yPos); + splineBuilders[2] = new CubicBezierSplineBuilder(zPos); + } + + public CubicBezierSplineBuilder3D(int length) { + BezierPoint[] xPos = new BezierPoint[length]; + BezierPoint[] yPos = new BezierPoint[length]; + BezierPoint[] zPos = new BezierPoint[length]; + + for (int i = 0; i < length; i++) { + BezierPoint3D position = new BezierPoint3D(0, 0, 0, 0, 0, 0, 0, 0, 0); + xPos[i] = position.getXAxis(); + yPos[i] = position.getYAxis(); + zPos[i] = position.getZAxis(); + } + + splineBuilders[0] = new CubicBezierSplineBuilder(xPos); + splineBuilders[1] = new CubicBezierSplineBuilder(yPos); + splineBuilders[2] = new CubicBezierSplineBuilder(zPos); + } + + public Spline3D build() { + return new EncapsulatingSpline3D(splineBuilders[0].build(), splineBuilders[1].build(), splineBuilders[2].build()); + } + + public CubicBezierSplineController3D controller() { + return new CubicBezierSplineController3D(splineBuilders[0].controller(), splineBuilders[1].controller(), splineBuilders[2].controller()); + } + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim3/CubicBezierSplineController3D.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim3/CubicBezierSplineController3D.java new file mode 100644 index 0000000..cb72a86 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bezier/dim3/CubicBezierSplineController3D.java @@ -0,0 +1,19 @@ +package fr.radi3nt.spline.splines.builder.bezier.dim3; + +import fr.radi3nt.spline.splines.builder.bezier.dim1.CubicBezierSplineController; + +public class CubicBezierSplineController3D { + + private final CubicBezierSplineController[] controllers; + + public CubicBezierSplineController3D(CubicBezierSplineController... controllers) { + this.controllers = controllers; + } + + + public void setPosition(BezierPoint3D point, int index) { + controllers[0].setPoint(point.getXAxis(), index); + controllers[1].setPoint(point.getYAxis(), index); + controllers[2].setPoint(point.getZAxis(), index); + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim1/BSplineBuilder.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim1/BSplineBuilder.java new file mode 100644 index 0000000..2acc9d6 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim1/BSplineBuilder.java @@ -0,0 +1,34 @@ +package fr.radi3nt.spline.splines.builder.bspline.dim1; + +import fr.radi3nt.spline.curve.curves.bcurve.BCurve; +import fr.radi3nt.spline.splines.CollectionSpline; +import fr.radi3nt.spline.splines.Spline; +import fr.radi3nt.spline.splines.builder.SplineBuilder; + +import java.util.ArrayList; +import java.util.List; + +public class BSplineBuilder implements SplineBuilder { + + private final CollectionSpline spline; + private final BSplineController controller; + + public BSplineBuilder(float[] pos) { + controller = new BSplineController(pos); + List curves = new ArrayList<>(); + for (int i = 0; i < pos.length-1; i++) { + BCurve cardinal = new BCurve(controller.createForIndex(i)); + curves.add(cardinal); + } + spline = new CollectionSpline(curves); + } + + @Override + public Spline build() { + return spline; + } + + public BSplineController controller() { + return controller; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim1/BSplineController.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim1/BSplineController.java new file mode 100644 index 0000000..52e29eb --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim1/BSplineController.java @@ -0,0 +1,23 @@ +package fr.radi3nt.spline.splines.builder.bspline.dim1; + +import fr.radi3nt.spline.curve.curves.bcurve.BCurveController; +import fr.radi3nt.spline.splines.builder.PositionnedSplineController; + +public class BSplineController implements PositionnedSplineController { + + private final float[] positions; + + public BSplineController(float[] positions) { + this.positions = positions; + } + + @Override + public void setPosition(int index, float pos) { + positions[index] = pos; + } + + public BCurveController createForIndex(int index) { + return new InferredBCurveController(index, positions); + } + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim1/DirectBCurveController.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim1/DirectBCurveController.java new file mode 100644 index 0000000..0e1ef0e --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim1/DirectBCurveController.java @@ -0,0 +1,38 @@ +package fr.radi3nt.spline.splines.builder.bspline.dim1; + +import fr.radi3nt.spline.curve.curves.bcurve.BCurveController; + +public class DirectBCurveController implements BCurveController { + + private final float positionA; + private final float positionB; + private final float positionC; + private final float positionD; + + public DirectBCurveController(float positionA, float positionB, float positionC, float positionD) { + this.positionA = positionA; + this.positionB = positionB; + this.positionC = positionC; + this.positionD = positionD; + } + + @Override + public float getPositionA() { + return positionA; + } + + @Override + public float getPositionB() { + return positionB; + } + + @Override + public float getPositionC() { + return positionC; + } + + @Override + public float getPositionD() { + return positionD; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim1/InferredBCurveController.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim1/InferredBCurveController.java new file mode 100644 index 0000000..c8e91eb --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim1/InferredBCurveController.java @@ -0,0 +1,38 @@ +package fr.radi3nt.spline.splines.builder.bspline.dim1; + +import fr.radi3nt.spline.curve.curves.bcurve.BCurveController; + +public class InferredBCurveController implements BCurveController { + + private final int index; + private final float[] pos; + + public InferredBCurveController(int index, float[] pos) { + this.index = index; + this.pos = pos; + } + + @Override + public float getPositionA() { + return index == 0 ? (mirror(pos[index], pos[index + 1])) : pos[index - 1]; + } + + @Override + public float getPositionB() { + return pos[index]; + } + + @Override + public float getPositionC() { + return index + 1 >= pos.length ? mirror(getPositionB(), getPositionA()) : pos[index + 1]; + } + + @Override + public float getPositionD() { + return index == pos.length - 2 ? mirror(getPositionC(), getPositionB()) : pos[index + 2]; + } + + private float mirror(float middlePos, float endPos) { + return middlePos*2 - endPos; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim3/BSplineBuilder3D.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim3/BSplineBuilder3D.java new file mode 100644 index 0000000..eff2f87 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim3/BSplineBuilder3D.java @@ -0,0 +1,46 @@ +package fr.radi3nt.spline.splines.builder.bspline.dim3; + +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.spline.splines.builder.bspline.dim1.BSplineBuilder; +import fr.radi3nt.spline.splines.dimensions.EncapsulatingSpline3D; +import fr.radi3nt.spline.splines.dimensions.Spline3D; + +public class BSplineBuilder3D { + + private final BSplineBuilder[] splineBuilders = new BSplineBuilder[3]; + + public BSplineBuilder3D(Vector3f... positions) { + float[] xPos = new float[positions.length]; + float[] yPos = new float[positions.length]; + float[] zPos = new float[positions.length]; + + for (int i = 0; i < positions.length; i++) { + Vector3f position = positions[i]; + xPos[i] = position.getX(); + yPos[i] = position.getY(); + zPos[i] = position.getZ(); + } + + splineBuilders[0] = new BSplineBuilder(xPos); + splineBuilders[1] = new BSplineBuilder(yPos); + splineBuilders[2] = new BSplineBuilder(zPos); + } + + public BSplineBuilder3D(int count) { + float[] xPos = new float[count]; + float[] yPos = new float[count]; + float[] zPos = new float[count]; + + splineBuilders[0] = new BSplineBuilder(xPos); + splineBuilders[1] = new BSplineBuilder(yPos); + splineBuilders[2] = new BSplineBuilder(zPos); + } + + public Spline3D build() { + return new EncapsulatingSpline3D(splineBuilders[0].build(), splineBuilders[1].build(), splineBuilders[2].build()); + } + + public BSplineController3D controller() { + return new BSplineController3D(splineBuilders[0].controller(), splineBuilders[1].controller(), splineBuilders[2].controller()); + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim3/BSplineController3D.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim3/BSplineController3D.java new file mode 100644 index 0000000..adccd63 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/bspline/dim3/BSplineController3D.java @@ -0,0 +1,19 @@ +package fr.radi3nt.spline.splines.builder.bspline.dim3; + +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.spline.splines.builder.bspline.dim1.BSplineController; + +public class BSplineController3D { + + private final BSplineController[] controllers; + + public BSplineController3D(BSplineController... controllers) { + this.controllers = controllers; + } + + public void setPosition(int index, Vector3f position) { + controllers[0].setPosition(index, position.getX()); + controllers[1].setPosition(index, position.getY()); + controllers[2].setPosition(index, position.getZ()); + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim1/CardinalSplineBuilder.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim1/CardinalSplineBuilder.java new file mode 100644 index 0000000..2e04061 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim1/CardinalSplineBuilder.java @@ -0,0 +1,34 @@ +package fr.radi3nt.spline.splines.builder.cardinal.dim1; + +import fr.radi3nt.spline.curve.curves.cardinal.CardinalCurve; +import fr.radi3nt.spline.splines.CollectionSpline; +import fr.radi3nt.spline.splines.Spline; +import fr.radi3nt.spline.splines.builder.SplineBuilder; + +import java.util.ArrayList; +import java.util.List; + +public class CardinalSplineBuilder implements SplineBuilder { + + private final CollectionSpline spline; + private final CardinalSplineController controller; + + public CardinalSplineBuilder(float[] pos, float scale) { + controller = new CardinalSplineController(pos, scale); + List curves = new ArrayList<>(); + for (int i = 0; i < pos.length-1; i++) { + CardinalCurve cardinal = new CardinalCurve(controller.createForIndex(i)); + curves.add(cardinal); + } + spline = new CollectionSpline(curves); + } + + @Override + public Spline build() { + return spline; + } + + public CardinalSplineController controller() { + return controller; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim1/CardinalSplineController.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim1/CardinalSplineController.java new file mode 100644 index 0000000..71a4d91 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim1/CardinalSplineController.java @@ -0,0 +1,32 @@ +package fr.radi3nt.spline.splines.builder.cardinal.dim1; + +import fr.radi3nt.spline.curve.curves.cardinal.CardinalCurveController; +import fr.radi3nt.spline.splines.builder.PositionnedSplineController; + +import java.util.concurrent.atomic.AtomicReference; + +public class CardinalSplineController implements PositionnedSplineController { + + + private final float[] positions; + private final AtomicReference scale; + + public CardinalSplineController(float[] positions, float scale) { + this.positions = positions; + this.scale = new AtomicReference<>(scale); + } + + @Override + public void setPosition(int index, float pos) { + positions[index] = pos; + } + + public void setScale(float scale) { + this.scale.set(scale); + } + + public CardinalCurveController createForIndex(int index) { + return new InferredCardinalCurveController(index, positions, scale); + } + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim1/DirectCardinalCurveController.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim1/DirectCardinalCurveController.java new file mode 100644 index 0000000..9f2da12 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim1/DirectCardinalCurveController.java @@ -0,0 +1,45 @@ +package fr.radi3nt.spline.splines.builder.cardinal.dim1; + +import fr.radi3nt.spline.curve.curves.cardinal.CardinalCurveController; + +public class DirectCardinalCurveController implements CardinalCurveController { + + private final float positionA; + private final float positionB; + private final float positionC; + private final float positionD; + private final float scale; + + public DirectCardinalCurveController(float positionA, float positionB, float positionC, float positionD, float scale) { + this.positionA = positionA; + this.positionB = positionB; + this.positionC = positionC; + this.positionD = positionD; + this.scale = scale; + } + + @Override + public float getPositionA() { + return positionA; + } + + @Override + public float getPositionB() { + return positionB; + } + + @Override + public float getPositionC() { + return positionC; + } + + @Override + public float getPositionD() { + return positionD; + } + + @Override + public float getScale() { + return scale; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim1/InferredCardinalCurveController.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim1/InferredCardinalCurveController.java new file mode 100644 index 0000000..7b0ee9f --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim1/InferredCardinalCurveController.java @@ -0,0 +1,48 @@ +package fr.radi3nt.spline.splines.builder.cardinal.dim1; + +import fr.radi3nt.spline.curve.curves.cardinal.CardinalCurveController; + +import java.util.concurrent.atomic.AtomicReference; + +public class InferredCardinalCurveController implements CardinalCurveController { + + private final int index; + + private final float[] pos; + private final AtomicReference scale; + + public InferredCardinalCurveController(int index, float[] pos, AtomicReference scale) { + this.index = index; + this.pos = pos; + this.scale = scale; + } + + @Override + public float getPositionA() { + return index == 0 ? (mirror(pos[index], pos[index + 1])) : pos[index - 1]; + } + + @Override + public float getPositionB() { + return pos[index]; + } + + @Override + public float getPositionC() { + return index + 1 >= pos.length ? mirror(getPositionB(), getPositionA()) : pos[index + 1]; + } + + @Override + public float getPositionD() { + return index == pos.length - 2 ? mirror(getPositionC(), getPositionB()) : pos[index + 2]; + } + + @Override + public float getScale() { + return scale.get(); + } + + private float mirror(float middlePos, float endPos) { + return middlePos*2 - endPos; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim3/CardinalSplineBuilder3D.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim3/CardinalSplineBuilder3D.java new file mode 100644 index 0000000..6e718e8 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim3/CardinalSplineBuilder3D.java @@ -0,0 +1,36 @@ +package fr.radi3nt.spline.splines.builder.cardinal.dim3; + +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.spline.splines.builder.cardinal.dim1.CardinalSplineBuilder; +import fr.radi3nt.spline.splines.dimensions.EncapsulatingSpline3D; +import fr.radi3nt.spline.splines.dimensions.Spline3D; + +public class CardinalSplineBuilder3D { + + private final CardinalSplineBuilder[] splineBuilders = new CardinalSplineBuilder[3]; + + public CardinalSplineBuilder3D(Vector3f[] positions, float scale) { + float[] xPos = new float[positions.length]; + float[] yPos = new float[positions.length]; + float[] zPos = new float[positions.length]; + + for (int i = 0; i < positions.length; i++) { + Vector3f position = positions[i]; + xPos[i] = position.getX(); + yPos[i] = position.getY(); + zPos[i] = position.getZ(); + } + + splineBuilders[0] = new CardinalSplineBuilder(xPos, scale); + splineBuilders[1] = new CardinalSplineBuilder(yPos, scale); + splineBuilders[2] = new CardinalSplineBuilder(zPos, scale); + } + + public Spline3D build() { + return new EncapsulatingSpline3D(splineBuilders[0].build(), splineBuilders[1].build(), splineBuilders[2].build()); + } + + public CardinalSplineController3D controller() { + return new CardinalSplineController3D(splineBuilders[0].controller(), splineBuilders[1].controller(), splineBuilders[2].controller()); + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim3/CardinalSplineController3D.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim3/CardinalSplineController3D.java new file mode 100644 index 0000000..bf6bd3b --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/builder/cardinal/dim3/CardinalSplineController3D.java @@ -0,0 +1,26 @@ +package fr.radi3nt.spline.splines.builder.cardinal.dim3; + +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.spline.splines.builder.cardinal.dim1.CardinalSplineController; + +public class CardinalSplineController3D { + + private final CardinalSplineController[] controllers; + + public CardinalSplineController3D(CardinalSplineController... controllers) { + this.controllers = controllers; + } + + + public void setPosition(int index, Vector3f position) { + controllers[0].setPosition(index, position.getX()); + controllers[1].setPosition(index, position.getY()); + controllers[2].setPosition(index, position.getZ()); + } + + public void setScale(float scale) { + for (CardinalSplineController controller : controllers) { + controller.setScale(scale); + } + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/dimensions/EncapsulatingSpline2D.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/dimensions/EncapsulatingSpline2D.java new file mode 100644 index 0000000..a49e34e --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/dimensions/EncapsulatingSpline2D.java @@ -0,0 +1,71 @@ +package fr.radi3nt.spline.splines.dimensions; + +import fr.radi3nt.maths.components.vectors.Vector2f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector2f; +import fr.radi3nt.spline.splines.Spline; + +import java.util.Arrays; + +public class EncapsulatingSpline2D implements Spline2D { + + private final Spline[] splines; + + public EncapsulatingSpline2D(Spline... splines) { + this.splines = splines; + } + + + @Override + public float interpolateX(float t) { + return splines[0].interpolate(t); + } + + @Override + public float interpolateY(float t) { + return splines[1].interpolate(t); + } + + @Override + public Vector2f interpolate(float t) { + return new SimpleVector2f(interpolateIndex(0, t), interpolateIndex(1, t)); + } + + @Override + public float velocityX(float t) { + return splines[0].velocity(t); + } + + @Override + public float velocityY(float t) { + return splines[1].velocity(t); + } + + private float interpolateIndex(int index, float t) { + return splines[index].interpolate(t); + } + + @Override + public Vector2f velocity(float t) { + return new SimpleVector2f(velocityIndex(0, t), velocityIndex(1, t)); + } + + @Override + public int getSegmentCount() { + return splines[0].getSegmentCount(); + } + + private float velocityIndex(int index, float t) { + return splines[index].velocity(t); + } + + public Spline[] getSplines() { + return splines; + } + + @Override + public String toString() { + return "EncapsulatingSpline2D{" + + "splines=" + Arrays.toString(splines) + + '}'; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/dimensions/EncapsulatingSpline3D.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/dimensions/EncapsulatingSpline3D.java new file mode 100644 index 0000000..4010919 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/dimensions/EncapsulatingSpline3D.java @@ -0,0 +1,37 @@ +package fr.radi3nt.spline.splines.dimensions; + +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; +import fr.radi3nt.spline.splines.Spline; + +public class EncapsulatingSpline3D implements Spline3D { + + private final Spline[] splines; + + public EncapsulatingSpline3D(Spline... splines) { + this.splines = splines; + } + + + @Override + public Vector3f interpolate(float t) { + return new SimpleVector3f(interpolateIndex(0, t), interpolateIndex(1, t), interpolateIndex(2, t)); + } + + private float interpolateIndex(int index, float t) { + return splines[index].interpolate(t); + } + + @Override + public Vector3f velocity(float t) { + return new SimpleVector3f(velocityIndex(0, t), velocityIndex(1, t), velocityIndex(2, t)); + } + + private float velocityIndex(int index, float t) { + return splines[index].velocity(t); + } + + public Spline[] getSplines() { + return splines; + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/dimensions/Spline2D.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/dimensions/Spline2D.java new file mode 100644 index 0000000..e16a0dd --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/dimensions/Spline2D.java @@ -0,0 +1,17 @@ +package fr.radi3nt.spline.splines.dimensions; + +import fr.radi3nt.maths.components.vectors.Vector2f; + +public interface Spline2D { + + float interpolateX(float t); + float interpolateY(float t); + + Vector2f interpolate(float t); + float velocityX(float t); + float velocityY(float t); + Vector2f velocity(float t); + + int getSegmentCount(); + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/dimensions/Spline3D.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/dimensions/Spline3D.java new file mode 100644 index 0000000..0a6eebc --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/dimensions/Spline3D.java @@ -0,0 +1,10 @@ +package fr.radi3nt.spline.splines.dimensions; + +import fr.radi3nt.maths.components.vectors.Vector3f; + +public interface Spline3D { + + Vector3f interpolate(float t); + Vector3f velocity(float t); + +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/hybrid/cardinal/CardinalUsingHermitSpline.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/hybrid/cardinal/CardinalUsingHermitSpline.java new file mode 100644 index 0000000..66938a7 --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/hybrid/cardinal/CardinalUsingHermitSpline.java @@ -0,0 +1,48 @@ +package fr.radi3nt.spline.splines.hybrid.cardinal; + +import fr.radi3nt.spline.splines.Spline; +import fr.radi3nt.spline.splines.hybrid.hermite.HermiteSpline; + +public class CardinalUsingHermitSpline implements Spline { + + private final HermiteSpline hermiteSpline; + + public CardinalUsingHermitSpline(float[] pos, float scale) { + float[] velocities = new float[pos.length]; + for (int i = 0; i < pos.length; i++) { + int startIndex = i-1; + int endIndex = i+1; + float firstPos; + if (startIndex<0) + firstPos = pos[i]*2-pos[i+1]; + else + firstPos = pos[i-1]; + + float secondPos; + if (endIndex>=pos.length) + secondPos = pos[i]*2-pos[i-1]; + else + secondPos = pos[i+1]; + + float vel = secondPos-firstPos; + velocities[i] = vel*scale; + } + + hermiteSpline = new HermiteSpline(pos, velocities); + } + + @Override + public float interpolate(float t) { + return hermiteSpline.interpolate(t); + } + + @Override + public float velocity(float t) { + return hermiteSpline.velocity(t); + } + + @Override + public int getSegmentCount() { + return hermiteSpline.getSegmentCount(); + } +} diff --git a/SplineHelper/src/main/java/fr/radi3nt/spline/splines/hybrid/hermite/HermiteSpline.java b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/hybrid/hermite/HermiteSpline.java new file mode 100644 index 0000000..d4d72ef --- /dev/null +++ b/SplineHelper/src/main/java/fr/radi3nt/spline/splines/hybrid/hermite/HermiteSpline.java @@ -0,0 +1,32 @@ +package fr.radi3nt.spline.splines.hybrid.hermite; + +import fr.radi3nt.spline.curve.Curve; +import fr.radi3nt.spline.curve.curves.HermiteCurve; +import fr.radi3nt.spline.splines.CollectionSpline; + +import java.util.ArrayList; +import java.util.List; + +public class HermiteSpline extends CollectionSpline { + + public HermiteSpline(float[] pos, float[] velocities) { + this(new ArrayList<>(), pos, velocities); + } + + public HermiteSpline(List curves, float[] pos, float[] velocities) { + super(curves); + for (int i = 0; i < velocities.length-1; i++) { + float currentPos = pos[i]; + float nextPos = pos[i+1]; + float currentVel = velocities[i]; + float nextVel = velocities[i+1]; + + curves.add(new HermiteCurve(currentPos, nextPos, currentVel, nextVel)); + } + } + + @Override + public float interpolate(float t) { + return super.interpolate(t); + } +} diff --git a/TimingHelper/src/main/java/fr/radi3nt/tasks/tasks/ManagerTask.java b/TimingHelper/src/main/java/fr/radi3nt/tasks/tasks/ManagerTask.java index c11466d..9b1e5f2 100644 --- a/TimingHelper/src/main/java/fr/radi3nt/tasks/tasks/ManagerTask.java +++ b/TimingHelper/src/main/java/fr/radi3nt/tasks/tasks/ManagerTask.java @@ -9,4 +9,5 @@ public interface ManagerTask { long getId(); + void waitTillFinished() throws InterruptedException; } diff --git a/TimingHelper/src/main/java/fr/radi3nt/tasks/tasks/TemplateManagerTask.java b/TimingHelper/src/main/java/fr/radi3nt/tasks/tasks/TemplateManagerTask.java index 61a4a24..c2c3ba6 100644 --- a/TimingHelper/src/main/java/fr/radi3nt/tasks/tasks/TemplateManagerTask.java +++ b/TimingHelper/src/main/java/fr/radi3nt/tasks/tasks/TemplateManagerTask.java @@ -21,6 +21,16 @@ public void run() { started = true; run_(); finished = true; + synchronized (this) { + this.notifyAll(); + } + } + + @Override + public void waitTillFinished() throws InterruptedException { + synchronized (this) { + this.wait(); + } } protected abstract void run_(); diff --git a/TimingHelper/src/main/java/fr/radi3nt/timer/JavaSyncTimer.java b/TimingHelper/src/main/java/fr/radi3nt/timer/JavaSyncTimer.java index 45a9404..9389652 100644 --- a/TimingHelper/src/main/java/fr/radi3nt/timer/JavaSyncTimer.java +++ b/TimingHelper/src/main/java/fr/radi3nt/timer/JavaSyncTimer.java @@ -7,6 +7,14 @@ public class JavaSyncTimer implements SyncTimer { private double fps; private double delta; + public JavaSyncTimer(double delta) { + this.delta = delta; + this.fps = 1d / delta; + } + + public JavaSyncTimer() { + } + @Override public void start() { lastTime = System.nanoTime(); diff --git a/TimingHelper/src/main/java/fr/radi3nt/timing/Timing.java b/TimingHelper/src/main/java/fr/radi3nt/timing/Timing.java index 6253e8a..969bd67 100644 --- a/TimingHelper/src/main/java/fr/radi3nt/timing/Timing.java +++ b/TimingHelper/src/main/java/fr/radi3nt/timing/Timing.java @@ -37,9 +37,9 @@ public static Timing from(int amount, TimeConvention... time) { } public void waitUtilExpired() { - while (!isExpired()) { + synchronized (this) { try { - Thread.sleep(remainingTime()); + this.wait(remainingTime()); } catch (InterruptedException e) { e.printStackTrace(); } diff --git a/TimingHelper/src/main/java/fr/radi3nt/timing/TimingUtil.java b/TimingHelper/src/main/java/fr/radi3nt/timing/TimingUtil.java index f9b2124..0580ca5 100644 --- a/TimingHelper/src/main/java/fr/radi3nt/timing/TimingUtil.java +++ b/TimingHelper/src/main/java/fr/radi3nt/timing/TimingUtil.java @@ -4,13 +4,11 @@ public class TimingUtil { - public static void waitUntilTaskFinished(ManagerTask GLTask) { - while (!GLTask.isFinished()) { - try { - Thread.sleep(1); - } catch (InterruptedException e) { - e.printStackTrace(); - } + public static void waitUntilTaskFinished(ManagerTask gLTask) { + try { + gLTask.waitTillFinished(); + } catch (InterruptedException e) { + e.printStackTrace(); } } diff --git a/glTF/pom.xml b/glTF/pom.xml new file mode 100644 index 0000000..9998786 --- /dev/null +++ b/glTF/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + fr.radi3nt + JavaUtil + 1.0 + + + glTF + + + 8 + 8 + UTF-8 + + + + fr.radi3nt + MathsHelper + 1.0 + compile + + + fr.radi3nt + FileHelper + 1.0 + compile + + + fr.radi3nt + JsonHelper + 1.0 + compile + + + + \ No newline at end of file diff --git a/glTF/src/main/java/fr/radi3nt/gltf/GltfAllImporter.java b/glTF/src/main/java/fr/radi3nt/gltf/GltfAllImporter.java new file mode 100644 index 0000000..cb19733 --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/GltfAllImporter.java @@ -0,0 +1,45 @@ +package fr.radi3nt.gltf; + +import fr.radi3nt.file.impl.ResourceFile; +import fr.radi3nt.gltf.data.GlTFResult; +import fr.radi3nt.gltf.importer.*; +import fr.radi3nt.json.Json; +import fr.radi3nt.json.JsonValue; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collection; + +public class GltfAllImporter { + + public GlTFResult importFile(String fullPath) throws IOException { + int last = fullPath.lastIndexOf("/"); + String path = fullPath.substring(0, last); + String name = fullPath.substring(last+1); + return importFile(path, name); + } + + public GlTFResult importFile(String parent, String fileName) throws IOException { + GlTFResult result = new GlTFResult(); + + + Collection importers = new ArrayList<>(); + { + importers.add(new AccessorsImporter()); + importers.add(new MaterialsImporter()); + importers.add(new MeshesImporter()); + importers.add(new NodesImporter()); + importers.add(new ScenesImporter()); + importers.add(new SkinsImporter()); + importers.add(new ViewImporter()); + } + importers.add(new BuffersImporter(parent)); + + JsonValue parsed = Json.parse(new InputStreamReader(new ResourceFile(parent + "/" + fileName).getInputStream())); + importers.parallelStream().forEach(importer -> importer.parse(parsed.asObject(), result)); + + return result; + } + +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/GlTFResult.java b/glTF/src/main/java/fr/radi3nt/gltf/data/GlTFResult.java new file mode 100644 index 0000000..6051071 --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/GlTFResult.java @@ -0,0 +1,49 @@ +package fr.radi3nt.gltf.data; + +import fr.radi3nt.gltf.data.buffer.GlTFBuffer; +import fr.radi3nt.gltf.data.buffer.accessor.BufferAccessor; +import fr.radi3nt.gltf.data.buffer.view.BufferView; +import fr.radi3nt.gltf.data.mesh.GlTFMesh; +import fr.radi3nt.gltf.data.mesh.material.GlTFMaterial; +import fr.radi3nt.gltf.data.scene.GlTFNode; +import fr.radi3nt.gltf.data.scene.GlTFScene; +import fr.radi3nt.gltf.data.skins.GlTFSkin; + +import java.util.Arrays; + +public class GlTFResult { + + public GlTFScene[] scenes; + public GlTFNode[] nodes; + + public GlTFMesh[] meshes; + public GlTFSkin[] skins; + public GlTFMaterial[] materials; + + public BufferAccessor[] bufferAccessors; + public BufferView[] bufferViews; + public GlTFBuffer[] buffers; + + @Override + public String toString() { + return "GlTFResult{" + + "scenes=" + Arrays.toString(scenes) + + ", nodes=" + Arrays.toString(nodes) + + ", meshes=" + Arrays.toString(meshes) + + ", skins=" + Arrays.toString(skins) + + ", materials=" + Arrays.toString(materials) + + ", bufferAccessors=" + Arrays.toString(bufferAccessors) + + ", bufferViews=" + Arrays.toString(bufferViews) + + ", buffers=" + Arrays.toString(buffers) + + '}'; + } + + public GlTFNode getNodeByName(String name) { + for (GlTFNode node : nodes) { + if (node.getName().equals(name)) { + return node; + } + } + return null; + } +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/attributes/EnumGLTFAttribute.java b/glTF/src/main/java/fr/radi3nt/gltf/data/attributes/EnumGLTFAttribute.java new file mode 100644 index 0000000..cde325b --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/attributes/EnumGLTFAttribute.java @@ -0,0 +1,18 @@ +package fr.radi3nt.gltf.data.attributes; + +public enum EnumGLTFAttribute implements GlTFAttribute { + + POSITION, + NORMAL, + JOINTS_0, + JOINTS_1, + WEIGHTS_0, + WEIGHTS_1 + + ; + + @Override + public String getId() { + return name(); + } +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/attributes/GlTFAttribute.java b/glTF/src/main/java/fr/radi3nt/gltf/data/attributes/GlTFAttribute.java new file mode 100644 index 0000000..10699b5 --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/attributes/GlTFAttribute.java @@ -0,0 +1,7 @@ +package fr.radi3nt.gltf.data.attributes; + +public interface GlTFAttribute { + + String getId(); + +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/attributes/StringGLTFAttribute.java b/glTF/src/main/java/fr/radi3nt/gltf/data/attributes/StringGLTFAttribute.java new file mode 100644 index 0000000..9776953 --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/attributes/StringGLTFAttribute.java @@ -0,0 +1,15 @@ +package fr.radi3nt.gltf.data.attributes; + +public class StringGLTFAttribute implements GlTFAttribute { + + private final String id; + + public StringGLTFAttribute(String id) { + this.id = id; + } + + @Override + public String getId() { + return id; + } +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/buffer/GlTFBuffer.java b/glTF/src/main/java/fr/radi3nt/gltf/data/buffer/GlTFBuffer.java new file mode 100644 index 0000000..0ed7d6c --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/buffer/GlTFBuffer.java @@ -0,0 +1,77 @@ +package fr.radi3nt.gltf.data.buffer; + +import fr.radi3nt.file.files.ReadableFile; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class GlTFBuffer { + + private final ReadableFile readableFile; + private final long byteLength; + + public GlTFBuffer(ReadableFile readableFile, long byteLength) { + this.readableFile = readableFile; + this.byteLength = byteLength; + } + + public ByteBuffer read(long offset, int length) { + try { + return tryReading(offset, length); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private ByteBuffer tryReading(long offset, int length) throws IOException { + InputStream inputStream = readableFile.getInputStream(); + long totalSkipped = 0; + while (totalSkipped < offset) { + totalSkipped+=inputStream.skip(offset -totalSkipped); + } + byte[] bytes = readNBytes(inputStream, length); + inputStream.close(); + + ByteBuffer wrap = ByteBuffer.wrap(bytes); + wrap.order(ByteOrder.LITTLE_ENDIAN); + return wrap; + } + + public void read(long offset, int length, ByteBuffer resultBuff) { + try { + tryReading(offset, length, resultBuff); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } + + private void tryReading(long offset, int length, ByteBuffer result) throws IOException { + InputStream inputStream = readableFile.getInputStream(); + long totalSkipped = 0; + while (totalSkipped < offset) { + totalSkipped+=inputStream.skip(offset -totalSkipped); + } + byte[] bytes = readNBytes(inputStream, length); + inputStream.close(); + + result.put(bytes); + } + + + private byte[] readNBytes(InputStream stream, int length) { + byte[] bytes = new byte[length]; + + int readBytes = 0; + while (readBytes < length) { + try { + readBytes+=stream.read(bytes, readBytes, length-readBytes); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return bytes; + } +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/buffer/accessor/BufferAccessor.java b/glTF/src/main/java/fr/radi3nt/gltf/data/buffer/accessor/BufferAccessor.java new file mode 100644 index 0000000..37269fd --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/buffer/accessor/BufferAccessor.java @@ -0,0 +1,55 @@ +package fr.radi3nt.gltf.data.buffer.accessor; + +import fr.radi3nt.gltf.data.GlTFResult; + +import java.nio.ByteBuffer; + +public class BufferAccessor { + + private final int bufferView; + private final GlTFComponentType componentType; + private final GlTFAccessorType type; + private final int count; + + private final float[] min; + private final float[] max; + + public BufferAccessor(int bufferView, GlTFComponentType componentType, GlTFAccessorType type, int count, float[] min, float[] max) { + this.bufferView = bufferView; + this.componentType = componentType; + this.type = type; + this.count = count; + this.min = min; + this.max = max; + } + + public ByteBuffer getBuffer(GlTFResult result) { + ByteBuffer buffer = result.bufferViews[bufferView].getBuffer(result); + buffer.limit(getByteSize()); + return buffer; + } + + public void getBuffer(GlTFResult result, ByteBuffer buffer) { + result.bufferViews[bufferView].getBuffer(result, buffer); + } + + public GlTFAccessorType getType() { + return type; + } + + public int getByteSize() { + return count*type.amount()*componentType.bytes(); + } + + public int getComponentBytes() { + return componentType.bytes(); + } + + public int getComponentCount() { + return type.amount(); + } + + public int getCount() { + return count; + } +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/buffer/accessor/GlTFAccessorType.java b/glTF/src/main/java/fr/radi3nt/gltf/data/buffer/accessor/GlTFAccessorType.java new file mode 100644 index 0000000..deb79ee --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/buffer/accessor/GlTFAccessorType.java @@ -0,0 +1,19 @@ +package fr.radi3nt.gltf.data.buffer.accessor; + +public enum GlTFAccessorType { + + SCALAR(1), + VEC3(3), + VEC4(4), + MAT4(16); + + private final int amount; + + GlTFAccessorType(int amount) { + this.amount = amount; + } + + public int amount() { + return amount; + } +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/buffer/accessor/GlTFComponentType.java b/glTF/src/main/java/fr/radi3nt/gltf/data/buffer/accessor/GlTFComponentType.java new file mode 100644 index 0000000..b76333c --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/buffer/accessor/GlTFComponentType.java @@ -0,0 +1,29 @@ +package fr.radi3nt.gltf.data.buffer.accessor; + +public enum GlTFComponentType { + + GL_FLOAT(5126, 4), + GL_UNSIGNED_BYTE(5121, 1), + GL_UNSIGNED_SHORT(5123, 2), + + ; + + private final int id; + private final int bytes; + + GlTFComponentType(int id, int bytes) { + this.id = id; + this.bytes = bytes; + } + + public static GlTFComponentType fromId(int componentType) { + for (GlTFComponentType value : values()) { + if (value.id==componentType) return value; + } + return null; + } + + public int bytes() { + return bytes; + } +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/buffer/view/BufferView.java b/glTF/src/main/java/fr/radi3nt/gltf/data/buffer/view/BufferView.java new file mode 100644 index 0000000..c0698c1 --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/buffer/view/BufferView.java @@ -0,0 +1,30 @@ +package fr.radi3nt.gltf.data.buffer.view; + +import fr.radi3nt.gltf.data.GlTFResult; +import fr.radi3nt.gltf.data.buffer.GlTFBuffer; + +import java.nio.ByteBuffer; + +public class BufferView { + + private final int buffer; + private final int length; + private final long offset; + + public BufferView(int buffer, int length, long offset) { + this.buffer = buffer; + this.length = length; + this.offset = offset; + } + + public ByteBuffer getBuffer(GlTFResult result) { + GlTFBuffer currentBuffer = result.buffers[buffer]; + + return currentBuffer.read(offset, length); + } + + public void getBuffer(GlTFResult result, ByteBuffer resultBuff) { + GlTFBuffer currentBuffer = result.buffers[buffer]; + currentBuffer.read(offset, length, resultBuff); + } +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/mesh/GlTFMesh.java b/glTF/src/main/java/fr/radi3nt/gltf/data/mesh/GlTFMesh.java new file mode 100644 index 0000000..149b5f1 --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/mesh/GlTFMesh.java @@ -0,0 +1,20 @@ +package fr.radi3nt.gltf.data.mesh; + +public class GlTFMesh { + + private final String name; + private final Primitive[] primitives; + + public GlTFMesh(String name, Primitive[] primitives) { + this.name = name; + this.primitives = primitives; + } + + public String getName() { + return name; + } + + public Primitive[] getPrimitives() { + return primitives; + } +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/mesh/Primitive.java b/glTF/src/main/java/fr/radi3nt/gltf/data/mesh/Primitive.java new file mode 100644 index 0000000..1d3b41f --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/mesh/Primitive.java @@ -0,0 +1,71 @@ +package fr.radi3nt.gltf.data.mesh; + +import fr.radi3nt.gltf.data.GlTFResult; +import fr.radi3nt.gltf.data.attributes.GlTFAttribute; +import fr.radi3nt.gltf.data.buffer.accessor.BufferAccessor; +import fr.radi3nt.gltf.data.mesh.material.GlTFMaterial; + +import java.nio.ByteBuffer; +import java.util.Map; + +public class Primitive { + + private final Map attributesToAccessors; + private final int indicesAccessor; + private final int materialSlot; + + public Primitive(Map attributesToAccessors, int indicesAccessor, int materialSlot) { + this.attributesToAccessors = attributesToAccessors; + this.indicesAccessor = indicesAccessor; + this.materialSlot = materialSlot; + } + + public ByteBuffer get(GlTFAttribute attributes, GlTFResult result) { + BufferAccessor bufferAccessor = getAccessor(attributes, result); + return bufferAccessor.getBuffer(result); + } + + public void get(GlTFAttribute attributes, GlTFResult result, ByteBuffer buffer) { + getAccessor(attributes, result).getBuffer(result, buffer); + } + + public int getByteSize(GlTFAttribute attribute, GlTFResult result) { + BufferAccessor bufferAccessor = getAccessor(attribute, result); + return bufferAccessor.getByteSize(); + } + + public int getComponentByte(GlTFAttribute attribute, GlTFResult result) { + BufferAccessor bufferAccessor = getAccessor(attribute, result); + return bufferAccessor.getComponentBytes(); + } + + public int getComponentCount(GlTFAttribute attribute, GlTFResult result) { + BufferAccessor bufferAccessor = getAccessor(attribute, result); + return bufferAccessor.getComponentCount(); + } + + public int getCount(GlTFAttribute attribute, GlTFResult result) { + BufferAccessor bufferAccessor = getAccessor(attribute, result); + return bufferAccessor.getCount(); + } + + private BufferAccessor getAccessor(GlTFAttribute attribute, GlTFResult result) { + int accessor = attributesToAccessors.get(attribute.getId()); + return result.bufferAccessors[accessor]; + } + + public ByteBuffer getIndices(GlTFResult result) { + return result.bufferAccessors[indicesAccessor].getBuffer(result); + } + + public int getCount(GlTFResult result) { + return result.bufferAccessors[indicesAccessor].getCount(); + } + + public GlTFMaterial getMaterial(GlTFResult result) { + if (materialSlot==-1) + return null; + return result.materials[materialSlot]; + } + +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/mesh/material/GlTFMaterial.java b/glTF/src/main/java/fr/radi3nt/gltf/data/mesh/material/GlTFMaterial.java new file mode 100644 index 0000000..77369e4 --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/mesh/material/GlTFMaterial.java @@ -0,0 +1,14 @@ +package fr.radi3nt.gltf.data.mesh.material; + +public class GlTFMaterial { + + private final String name; + + public GlTFMaterial(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/mesh/material/GlTFMaterialIndexer.java b/glTF/src/main/java/fr/radi3nt/gltf/data/mesh/material/GlTFMaterialIndexer.java new file mode 100644 index 0000000..883fb3a --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/mesh/material/GlTFMaterialIndexer.java @@ -0,0 +1,9 @@ +package fr.radi3nt.gltf.data.mesh.material; + +import fr.radi3nt.gltf.data.GlTFResult; + +public interface GlTFMaterialIndexer { + + int getIndex(GlTFMaterial material, GlTFResult result); + +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/scene/GlTFAbstractNode.java b/glTF/src/main/java/fr/radi3nt/gltf/data/scene/GlTFAbstractNode.java new file mode 100644 index 0000000..76c10a6 --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/scene/GlTFAbstractNode.java @@ -0,0 +1,29 @@ +package fr.radi3nt.gltf.data.scene; + +import fr.radi3nt.gltf.data.GlTFResult; + +public class GlTFAbstractNode { + + private final String name; + private final int[] children; + + public GlTFAbstractNode(String name, int[] children) { + this.name = name; + this.children = children; + } + + public String getName() { + return name; + } + + public GlTFNode[] getChildren(GlTFResult result) { + if (this.children==null) + return new GlTFNode[0]; + + GlTFNode[] children = new GlTFNode[this.children.length]; + for (int i = 0; i < children.length; i++) { + children[i] = result.nodes[this.children[i]]; + } + return children; + } +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/scene/GlTFNode.java b/glTF/src/main/java/fr/radi3nt/gltf/data/scene/GlTFNode.java new file mode 100644 index 0000000..fa680ec --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/scene/GlTFNode.java @@ -0,0 +1,39 @@ +package fr.radi3nt.gltf.data.scene; + +import fr.radi3nt.gltf.data.GlTFResult; +import fr.radi3nt.gltf.data.mesh.GlTFMesh; +import fr.radi3nt.gltf.data.skins.GlTFSkin; + +public class GlTFNode extends GlTFAbstractNode { + + private final GlTFTransform localTransform; + + private final int meshIndex; + private final int skinIndex; + + public GlTFNode(String name, int[] children, GlTFTransform localTransform, int meshIndex, int skinIndex) { + super(name, children); + this.localTransform = localTransform; + this.meshIndex = meshIndex; + this.skinIndex = skinIndex; + } + + public GlTFTransform getLocalTransform() { + return localTransform; + } + + public GlTFMesh getMesh(GlTFResult result) { + if (meshIndex==-1) + return null; + return result.meshes[meshIndex]; + } + + public int getMeshIndex() { + return meshIndex; + } + + public GlTFSkin getSkin(GlTFResult result) { + return result.skins[skinIndex]; + } + +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/scene/GlTFScene.java b/glTF/src/main/java/fr/radi3nt/gltf/data/scene/GlTFScene.java new file mode 100644 index 0000000..577da9c --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/scene/GlTFScene.java @@ -0,0 +1,8 @@ +package fr.radi3nt.gltf.data.scene; + +public class GlTFScene extends GlTFAbstractNode { + + public GlTFScene(String name, int[] children) { + super(name, children); + } +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/scene/GlTFTransform.java b/glTF/src/main/java/fr/radi3nt/gltf/data/scene/GlTFTransform.java new file mode 100644 index 0000000..3d6353e --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/scene/GlTFTransform.java @@ -0,0 +1,97 @@ +package fr.radi3nt.gltf.data.scene; + +import fr.radi3nt.json.JsonArray; +import fr.radi3nt.json.JsonObject; +import fr.radi3nt.json.JsonValue; +import fr.radi3nt.maths.components.advanced.matrix.ArrayMatrix4x4; +import fr.radi3nt.maths.components.advanced.matrix.Matrix4x4; +import fr.radi3nt.maths.components.advanced.quaternions.ComponentsQuaternion; +import fr.radi3nt.maths.components.advanced.quaternions.Quaternion; +import fr.radi3nt.maths.components.vectors.Vector3f; +import fr.radi3nt.maths.components.vectors.implementations.SimpleVector3f; + +import java.util.function.Supplier; + +public class GlTFTransform { + + private final Vector3f translation; + private final Quaternion rotation; + private final Vector3f scale; + + public GlTFTransform(Vector3f translation, Quaternion rotation, Vector3f scale) { + this.translation = translation; + this.rotation = rotation; + this.scale = scale; + } + + public Matrix4x4 toMatrix() { + Matrix4x4 result = ArrayMatrix4x4.newIdentity(); + result.quaternionRotation(rotation); + result.translation(translation); + result.scalar(scale); + return result; + } + + public Vector3f transform(Vector3f original) { + Vector3f result = original.duplicate(); + result.mul(scale); + rotation.transform(result); + result.add(translation); + return result; + } + + public Vector3f transformOrientation(Vector3f original) { + Vector3f result = original.duplicate(); + rotation.transform(result); + return result; + } + + public Vector3f getTranslation() { + return translation; + } + + public Quaternion getRotation() { + return rotation; + } + + public Vector3f getScale() { + return scale; + } + + public static GlTFTransform parse(JsonObject nodeJson) { + Vector3f translation = parseTranslation(nodeJson); + Quaternion rotation = parseRotation(nodeJson); + Vector3f scale = parseScale(nodeJson); + + return new GlTFTransform(translation, rotation, scale); + } + + private static Vector3f parseScale(JsonObject nodeJson) { + JsonValue value = nodeJson.get("scale"); + return parseVector(value, () -> new SimpleVector3f(1f, 1f, 1f)); + } + + private static Quaternion parseRotation(JsonObject nodeJson) { + JsonValue value = nodeJson.get("rotation"); + if (value == null) { + return ComponentsQuaternion.zero(); + } + + JsonArray valueArray = value.asArray(); + return new ComponentsQuaternion(valueArray.get(0).asFloat(), valueArray.get(1).asFloat(), valueArray.get(2).asFloat(), valueArray.get(3).asFloat()); + } + + private static Vector3f parseTranslation(JsonObject nodeJson) { + JsonValue value = nodeJson.get("translation"); + return parseVector(value, SimpleVector3f::new); + } + + private static Vector3f parseVector(JsonValue value, Supplier defaultVec) { + if (value == null) { + return defaultVec.get(); + } + + JsonArray valueArray = value.asArray(); + return new SimpleVector3f(valueArray.get(0).asFloat(), valueArray.get(1).asFloat(), valueArray.get(2).asFloat()); + } +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/data/skins/GlTFSkin.java b/glTF/src/main/java/fr/radi3nt/gltf/data/skins/GlTFSkin.java new file mode 100644 index 0000000..0c52693 --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/data/skins/GlTFSkin.java @@ -0,0 +1,50 @@ +package fr.radi3nt.gltf.data.skins; + +import fr.radi3nt.gltf.data.GlTFResult; +import fr.radi3nt.gltf.data.scene.GlTFNode; +import fr.radi3nt.maths.components.advanced.matrix.ArrayMatrix4x4; +import fr.radi3nt.maths.components.advanced.matrix.Matrix4x4; + +import java.nio.ByteBuffer; + +public class GlTFSkin { + + private final String name; + private final int inverseBindMatricesAccessorIndex; + private final int[] joints; + + public GlTFSkin(String name, int inverseBindMatricesAccessorIndex, int[] joints) { + this.name = name; + this.inverseBindMatricesAccessorIndex = inverseBindMatricesAccessorIndex; + this.joints = joints; + } + + public String getName() { + return name; + } + + public GlTFNode[] getJoints(GlTFResult result) { + GlTFNode[] nodes = new GlTFNode[joints.length]; + for (int i = 0; i < joints.length; i++) { + nodes[i] = result.nodes[joints[i]]; + } + return nodes; + } + + public Matrix4x4[] getInverseBindMatrices(GlTFResult result) { + ByteBuffer buffer = result.bufferAccessors[inverseBindMatricesAccessorIndex].getBuffer(result); + Matrix4x4[] matrices = new Matrix4x4[joints.length]; + for (int jointIndex = 0; jointIndex < joints.length; jointIndex++) { + float[] mat = new float[4*4]; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + float value = buffer.getFloat(); + mat[i+j*4] = value; + } + } + Matrix4x4 inverseBind = ArrayMatrix4x4.from(mat); + matrices[jointIndex] = inverseBind; + } + return matrices; + } +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/importer/AccessorsImporter.java b/glTF/src/main/java/fr/radi3nt/gltf/importer/AccessorsImporter.java new file mode 100644 index 0000000..222f6c4 --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/importer/AccessorsImporter.java @@ -0,0 +1,58 @@ +package fr.radi3nt.gltf.importer; + +import fr.radi3nt.gltf.data.GlTFResult; +import fr.radi3nt.gltf.data.buffer.accessor.BufferAccessor; +import fr.radi3nt.gltf.data.buffer.accessor.GlTFAccessorType; +import fr.radi3nt.gltf.data.buffer.accessor.GlTFComponentType; +import fr.radi3nt.json.JsonArray; +import fr.radi3nt.json.JsonObject; +import fr.radi3nt.json.JsonValue; + +public class AccessorsImporter implements GlTFImporter { + + public void parse(JsonObject file, GlTFResult result) { + + JsonArray accessorsJson = file.get("accessors").asArray(); + BufferAccessor[] accessors = new BufferAccessor[accessorsJson.size()]; + for (int i = 0; i < accessors.length; i++) { + JsonObject accessorJson = accessorsJson.get(i).asObject(); + int bufferView = accessorJson.getInt("bufferView", -1); + GlTFComponentType componentType = GlTFComponentType.fromId(accessorJson.getInt("componentType", -1)); + int count = accessorJson.getInt("count", 0); + GlTFAccessorType accessorType = GlTFAccessorType.valueOf(accessorJson.getString("type")); + + float[] min = getMin(accessorJson); + float[] max = getMax(accessorJson); + + accessors[i] = new BufferAccessor(bufferView, componentType, accessorType, count, min, max); + } + + result.bufferAccessors = accessors; + } + + private float[] getMin(JsonObject accessorJson) { + JsonValue value = accessorJson.get("min"); + if (value==null) + return null; + + return parseArray(value); + } + + private float[] getMax(JsonObject accessorJson) { + JsonValue value = accessorJson.get("max"); + if (value==null) + return null; + + return parseArray(value); + } + + private static float[] parseArray(JsonValue value) { + JsonArray array = value.asArray(); + float[] floats = new float[array.size()]; + for (int i = 0; i < array.size(); i++) { + floats[i] = array.get(i).asFloat(); + } + return floats; + } + +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/importer/BuffersImporter.java b/glTF/src/main/java/fr/radi3nt/gltf/importer/BuffersImporter.java new file mode 100644 index 0000000..2cbc8b6 --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/importer/BuffersImporter.java @@ -0,0 +1,33 @@ +package fr.radi3nt.gltf.importer; + +import fr.radi3nt.file.files.ReadableFile; +import fr.radi3nt.file.impl.ResourceFile; +import fr.radi3nt.gltf.data.GlTFResult; +import fr.radi3nt.gltf.data.buffer.GlTFBuffer; +import fr.radi3nt.json.JsonArray; +import fr.radi3nt.json.JsonObject; + +public class BuffersImporter implements GlTFImporter { + + private final String basePath; + + public BuffersImporter(String basePath) { + this.basePath = basePath; + } + + public void parse(JsonObject file, GlTFResult result) { + + JsonArray buffersJson = file.get("buffers").asArray(); + GlTFBuffer[] buffers = new GlTFBuffer[buffersJson.size()]; + for (int i = 0; i < buffersJson.size(); i++) { + JsonObject bufferJson = buffersJson.get(i).asObject(); + long byteLength = bufferJson.getLong("byteLength", 0); + ReadableFile uri = new ResourceFile(basePath + "/" + bufferJson.getString("uri")); + buffers[i] = new GlTFBuffer(uri, byteLength); + } + + result.buffers = buffers; + + } + +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/importer/GlTFImporter.java b/glTF/src/main/java/fr/radi3nt/gltf/importer/GlTFImporter.java new file mode 100644 index 0000000..94d8b44 --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/importer/GlTFImporter.java @@ -0,0 +1,10 @@ +package fr.radi3nt.gltf.importer; + +import fr.radi3nt.gltf.data.GlTFResult; +import fr.radi3nt.json.JsonObject; + +public interface GlTFImporter { + + void parse(JsonObject file, GlTFResult result); + +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/importer/MaterialsImporter.java b/glTF/src/main/java/fr/radi3nt/gltf/importer/MaterialsImporter.java new file mode 100644 index 0000000..0cf7d41 --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/importer/MaterialsImporter.java @@ -0,0 +1,25 @@ +package fr.radi3nt.gltf.importer; + +import fr.radi3nt.gltf.data.GlTFResult; +import fr.radi3nt.gltf.data.mesh.material.GlTFMaterial; +import fr.radi3nt.json.JsonArray; +import fr.radi3nt.json.JsonObject; + +public class MaterialsImporter implements GlTFImporter { + + public void parse(JsonObject file, GlTFResult result) { + if (file.get("materials")==null) + return; + + JsonArray materialsJson = file.get("materials").asArray(); + GlTFMaterial[] materials = new GlTFMaterial[materialsJson.size()]; + for (int i = 0; i < materialsJson.size(); i++) { + JsonObject materialJson = materialsJson.get(i).asObject(); + String name = materialJson.getString("name", "unnamed"); + materials[i] = new GlTFMaterial(name); + } + + result.materials = materials; + } + +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/importer/MeshesImporter.java b/glTF/src/main/java/fr/radi3nt/gltf/importer/MeshesImporter.java new file mode 100644 index 0000000..48c94d7 --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/importer/MeshesImporter.java @@ -0,0 +1,46 @@ +package fr.radi3nt.gltf.importer; + +import fr.radi3nt.gltf.data.GlTFResult; +import fr.radi3nt.gltf.data.attributes.GlTFAttribute; +import fr.radi3nt.gltf.data.attributes.StringGLTFAttribute; +import fr.radi3nt.gltf.data.mesh.GlTFMesh; +import fr.radi3nt.gltf.data.mesh.Primitive; +import fr.radi3nt.json.JsonArray; +import fr.radi3nt.json.JsonObject; + +import java.util.HashMap; +import java.util.Map; + +public class MeshesImporter implements GlTFImporter { + + public void parse(JsonObject file, GlTFResult result) { + JsonArray meshesJson = file.get("meshes").asArray(); + GlTFMesh[] meshes = new GlTFMesh[meshesJson.size()]; + for (int i = 0; i < meshesJson.size(); i++) { + JsonObject meshJson = meshesJson.get(i).asObject(); + String name = meshJson.getString("name", "unnamed"); + + JsonArray primitivesJson = meshJson.get("primitives").asArray(); + Primitive[] primitives = new Primitive[primitivesJson.size()]; + for (int j = 0; j < primitivesJson.size(); j++) { + JsonObject primitiveJson = primitivesJson.get(j).asObject(); + JsonObject attributes = primitiveJson.get("attributes").asObject(); + Map attributesMap = new HashMap<>(); + for (JsonObject.Member attribute : attributes) { + int accessorIndex = attribute.getValue().asInt(); + attributesMap.put(attribute.getName(), accessorIndex); + } + + int indicesIndex = primitiveJson.getInt("indices", -1); + int material = primitiveJson.getInt("material", -1); + + primitives[j] = new Primitive(attributesMap, indicesIndex, material); + } + + meshes[i] = new GlTFMesh(name, primitives); + } + + result.meshes = meshes; + } + +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/importer/NodesImporter.java b/glTF/src/main/java/fr/radi3nt/gltf/importer/NodesImporter.java new file mode 100644 index 0000000..3d2bb5d --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/importer/NodesImporter.java @@ -0,0 +1,44 @@ +package fr.radi3nt.gltf.importer; + +import fr.radi3nt.gltf.data.GlTFResult; +import fr.radi3nt.gltf.data.scene.GlTFNode; +import fr.radi3nt.gltf.data.scene.GlTFTransform; +import fr.radi3nt.json.JsonArray; +import fr.radi3nt.json.JsonObject; +import fr.radi3nt.json.JsonValue; + +public class NodesImporter implements GlTFImporter { + + public void parse(JsonObject file, GlTFResult result) { + JsonArray nodesJsonArray = file.get("nodes").asArray(); + GlTFNode[] nodes = new GlTFNode[nodesJsonArray.size()]; + for (int i = 0; i < nodesJsonArray.size(); i++) { + JsonObject nodeJson = nodesJsonArray.get(i).asObject(); + + String name = nodeJson.getString("name", "undefined"); + int meshIndex = nodeJson.getInt("mesh", -1); + int skinIndex = nodeJson.getInt("skin", -1); + + int[] children = getChildrenOrNull(nodeJson); + GlTFTransform transform = GlTFTransform.parse(nodeJson); + + nodes[i] = new GlTFNode(name, children, transform, meshIndex, skinIndex); + } + + result.nodes = nodes; + } + + private static int[] getChildrenOrNull(JsonObject nodeJson) { + JsonValue childrenJson = nodeJson.get("children"); + if (childrenJson == null) { + return null; + } + JsonArray childrenJsonArray = childrenJson.asArray(); + int[] children = new int[childrenJsonArray.size()]; + for (int j = 0; j < childrenJsonArray.size(); j++) { + children[j] = childrenJsonArray.get(j).asInt(); + } + return children; + } + +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/importer/ScenesImporter.java b/glTF/src/main/java/fr/radi3nt/gltf/importer/ScenesImporter.java new file mode 100644 index 0000000..7cadc94 --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/importer/ScenesImporter.java @@ -0,0 +1,29 @@ +package fr.radi3nt.gltf.importer; + +import fr.radi3nt.gltf.data.GlTFResult; +import fr.radi3nt.gltf.data.scene.GlTFScene; +import fr.radi3nt.json.JsonArray; +import fr.radi3nt.json.JsonObject; + +public class ScenesImporter implements GlTFImporter { + + public void parse(JsonObject file, GlTFResult result) { + JsonArray scenesJson = file.get("scenes").asArray(); + GlTFScene[] scenes = new GlTFScene[scenesJson.size()]; + for (int i = 0; i < scenesJson.size(); i++) { + JsonObject sceneJson = scenesJson.get(i).asObject(); + String sceneName = sceneJson.asObject().getString("name"); + JsonArray nodesJson = sceneJson.asObject().get("nodes").asArray(); + int[] nodes = new int[nodesJson.size()]; + for (int j = 0; j < nodesJson.size(); j++) { + nodes[j] = nodesJson.get(j).asInt(); + } + + GlTFScene currentScene = new GlTFScene(sceneName, nodes); + scenes[i] = currentScene; + } + + result.scenes = scenes; + } + +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/importer/SkinsImporter.java b/glTF/src/main/java/fr/radi3nt/gltf/importer/SkinsImporter.java new file mode 100644 index 0000000..9e93d01 --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/importer/SkinsImporter.java @@ -0,0 +1,39 @@ +package fr.radi3nt.gltf.importer; + +import fr.radi3nt.gltf.data.GlTFResult; +import fr.radi3nt.gltf.data.skins.GlTFSkin; +import fr.radi3nt.json.JsonArray; +import fr.radi3nt.json.JsonObject; +import fr.radi3nt.json.JsonValue; + +public class SkinsImporter implements GlTFImporter { + + public void parse(JsonObject file, GlTFResult result) { + + JsonValue skinsValue = file.get("skins"); + if (skinsValue==null) + return; + JsonArray skinsJson = skinsValue.asArray(); + + GlTFSkin[] skins = new GlTFSkin[skinsJson.size()]; + for (int i = 0; i < skins.length; i++) { + JsonObject skinJson = skinsJson.get(i).asObject(); + + String name = skinJson.getString("name", "unnamed"); + + JsonArray jointsJson = skinJson.get("joints").asArray(); + int[] joints = new int[jointsJson.size()]; + for (int j = 0; j < joints.length; j++) { + joints[j] = jointsJson.get(j).asInt(); + } + + int inverseBindMatrixAccessorIndex = skinJson.get("inverseBindMatrices").asInt(); + + skins[i] = new GlTFSkin(name, inverseBindMatrixAccessorIndex, joints); + } + + result.skins = skins; + + } + +} diff --git a/glTF/src/main/java/fr/radi3nt/gltf/importer/ViewImporter.java b/glTF/src/main/java/fr/radi3nt/gltf/importer/ViewImporter.java new file mode 100644 index 0000000..bbc44c7 --- /dev/null +++ b/glTF/src/main/java/fr/radi3nt/gltf/importer/ViewImporter.java @@ -0,0 +1,26 @@ +package fr.radi3nt.gltf.importer; + +import fr.radi3nt.gltf.data.GlTFResult; +import fr.radi3nt.gltf.data.buffer.view.BufferView; +import fr.radi3nt.json.JsonArray; +import fr.radi3nt.json.JsonObject; + +public class ViewImporter implements GlTFImporter { + + public void parse(JsonObject file, GlTFResult result) { + + JsonArray bufferViewsJson = file.get("bufferViews").asArray(); + BufferView[] bufferViews = new BufferView[bufferViewsJson.size()]; + for (int i = 0; i < bufferViewsJson.size(); i++) { + JsonObject viewJson = bufferViewsJson.get(i).asObject(); + int buffer = viewJson.getInt("buffer", -1); + int byteLength = viewJson.getInt("byteLength", 0); + long byteOffset = viewJson.getLong("byteOffset", 0); + bufferViews[i] = new BufferView(buffer, byteLength, byteOffset); + } + + result.bufferViews = bufferViews; + + } + +} diff --git a/pom.xml b/pom.xml index ae7bb1b..3f1464c 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,14 @@ NoiseHelper BehaviorTree LanguageHelperV2 + DataStructures + SplineHelper + InverseKinematics + Pathfinding + AnimationsImport + Animations + glTF + Armature