From e1cb8cf71fedf9836372e26da75ff65ec3d390bc Mon Sep 17 00:00:00 2001 From: Speiger Date: Fri, 27 Aug 2021 06:07:15 +0200 Subject: [PATCH] Initial Commit --- .classpath | 32 + .gitattributes | 6 + .gitignore | 6 + .project | 23 + .settings/org.eclipse.buildship.core.prefs | 2 + build.gradle | 41 + gradle.properties | 4 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58694 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 183 +++ gradlew.bat | 103 ++ settings.gradle | 10 + .../src/coreengine/assets/AssetLocation.java | 92 ++ .../src/coreengine/assets/AssetManager.java | 186 +++ .../speiger/src/coreengine/assets/IAsset.java | 22 + .../src/coreengine/assets/IAssetPackage.java | 10 + .../src/coreengine/assets/MultiAsset.java | 41 + .../src/coreengine/assets/impl/FileAsset.java | 87 ++ .../assets/impl/FolderAssetPackage.java | 47 + .../src/coreengine/assets/impl/ZipAsset.java | 75 ++ .../assets/impl/ZipAssetPackage.java | 111 ++ .../src/coreengine/assets/language/I18n.java | 16 + .../coreengine/assets/language/Language.java | 47 + .../assets/language/LanguageManager.java | 151 +++ .../assets/reloader/IReloadableResource.java | 8 + .../assets/reloader/ResourceReloader.java | 62 + .../src/coreengine/math/ArrayUtil.java | 76 ++ .../speiger/src/coreengine/math/BitUtil.java | 61 + .../src/coreengine/math/MathUtils.java | 171 +++ .../src/coreengine/math/ShapeUtil.java | 23 + .../coreengine/math/collision2d/Circle.java | 91 ++ .../math/collision2d/I2DCollision.java | 12 + .../math/collision2d/Mixed2DCollision.java | 72 ++ .../coreengine/math/collision2d/Plane.java | 104 ++ .../src/coreengine/math/misc/ColorObject.java | 381 ++++++ .../src/coreengine/math/misc/Facing.java | 194 +++ .../src/coreengine/math/misc/FacingList.java | 312 +++++ .../coreengine/math/smooth/SmoothFloat.java | 31 + .../coreengine/math/smooth/SmoothVec3f.java | 44 + .../coreengine/math/value/ConstantValue.java | 29 + .../src/coreengine/math/value/CubicValue.java | 59 + .../src/coreengine/math/value/IValue.java | 15 + .../coreengine/math/value/LiniarValue.java | 46 + .../coreengine/math/value/QuadraticValue.java | 48 + .../src/coreengine/math/value/Value.java | 79 ++ .../src/coreengine/math/vector/Vec.java | 26 + .../coreengine/math/vector/VectorUtil.java | 21 + .../coreengine/math/vector/bytes/Vec2b.java | 149 +++ .../math/vector/bytes/Vec2bImmutable.java | 92 ++ .../math/vector/bytes/Vec2bMutable.java | 94 ++ .../coreengine/math/vector/bytes/Vec3b.java | 141 +++ .../math/vector/bytes/Vec3bImmutable.java | 108 ++ .../math/vector/bytes/Vec3bMutable.java | 111 ++ .../coreengine/math/vector/bytes/Vec4b.java | 145 +++ .../math/vector/bytes/Vec4bImmutable.java | 124 ++ .../math/vector/bytes/Vec4bMutable.java | 129 ++ .../coreengine/math/vector/bytes/Vecb.java | 29 + .../coreengine/math/vector/doubles/Vec2d.java | 187 +++ .../math/vector/doubles/Vec2dImmutable.java | 92 ++ .../math/vector/doubles/Vec2dMutable.java | 96 ++ .../coreengine/math/vector/doubles/Vec3d.java | 241 ++++ .../math/vector/doubles/Vec3dImmutable.java | 108 ++ .../math/vector/doubles/Vec3dMutable.java | 114 ++ .../coreengine/math/vector/doubles/Vec4d.java | 231 ++++ .../math/vector/doubles/Vec4dImmutable.java | 124 ++ .../math/vector/doubles/Vec4dMutable.java | 128 ++ .../coreengine/math/vector/doubles/Vecd.java | 33 + .../coreengine/math/vector/floats/Vec2f.java | 187 +++ .../math/vector/floats/Vec2fImmutable.java | 92 ++ .../math/vector/floats/Vec2fMutable.java | 94 ++ .../coreengine/math/vector/floats/Vec3f.java | 242 ++++ .../math/vector/floats/Vec3fImmutable.java | 108 ++ .../math/vector/floats/Vec3fMutable.java | 111 ++ .../coreengine/math/vector/floats/Vec4f.java | 232 ++++ .../math/vector/floats/Vec4fImmutable.java | 124 ++ .../math/vector/floats/Vec4fMutable.java | 132 ++ .../coreengine/math/vector/floats/Vecf.java | 34 + .../coreengine/math/vector/ints/Vec2i.java | 163 +++ .../math/vector/ints/Vec2iImmutable.java | 92 ++ .../math/vector/ints/Vec2iMutable.java | 94 ++ .../coreengine/math/vector/ints/Vec3i.java | 157 +++ .../math/vector/ints/Vec3iImmutable.java | 108 ++ .../math/vector/ints/Vec3iMutable.java | 111 ++ .../coreengine/math/vector/ints/Vec4i.java | 158 +++ .../math/vector/ints/Vec4iImmutable.java | 124 ++ .../math/vector/ints/Vec4iMutable.java | 128 ++ .../src/coreengine/math/vector/ints/Veci.java | 34 + .../coreengine/math/vector/longs/Vec2l.java | 163 +++ .../math/vector/longs/Vec2lImmutable.java | 92 ++ .../math/vector/longs/Vec2lMutable.java | 94 ++ .../coreengine/math/vector/longs/Vec3l.java | 156 +++ .../math/vector/longs/Vec3lImmutable.java | 108 ++ .../math/vector/longs/Vec3lMutable.java | 111 ++ .../coreengine/math/vector/longs/Vec4l.java | 158 +++ .../math/vector/longs/Vec4lImmutable.java | 124 ++ .../math/vector/longs/Vec4lMutable.java | 128 ++ .../coreengine/math/vector/longs/Vecl.java | 34 + .../math/vector/matrix/Matrix4f.java | 898 +++++++++++++ .../math/vector/quaternion/Quaternion.java | 215 ++++ .../quaternion/QuaternionImmutable.java | 116 ++ .../vector/quaternion/QuaternionMutable.java | 124 ++ .../coreengine/math/vector/shorts/Vec2s.java | 162 +++ .../math/vector/shorts/Vec2sImmutable.java | 92 ++ .../math/vector/shorts/Vec2sMutable.java | 94 ++ .../coreengine/math/vector/shorts/Vec3s.java | 156 +++ .../math/vector/shorts/Vec3sImmutable.java | 108 ++ .../math/vector/shorts/Vec3sMutable.java | 111 ++ .../coreengine/math/vector/shorts/Vec4s.java | 159 +++ .../math/vector/shorts/Vec4sImmutable.java | 124 ++ .../math/vector/shorts/Vec4sMutable.java | 128 ++ .../coreengine/math/vector/shorts/Vecs.java | 34 + .../coreengine/rendering/gui/FontLoader.java | 118 ++ .../src/coreengine/rendering/gui/GuiBase.java | 375 ++++++ .../rendering/gui/GuiComponent.java | 1115 +++++++++++++++++ .../coreengine/rendering/gui/GuiManager.java | 275 ++++ .../coreengine/rendering/gui/Tooltips.java | 49 + .../coreengine/rendering/gui/UITextures.java | 19 + .../rendering/gui/base/DebugOverlay.java | 19 + .../rendering/gui/base/GuiScreenBase.java | 511 ++++++++ .../rendering/gui/base/IButtonComponent.java | 52 + .../rendering/gui/base/IInputComponent.java | 16 + .../rendering/gui/base/IKeyComponent.java | 12 + .../gui/components/ButtonComponent.java | 64 + .../gui/components/CheckBoxComponent.java | 123 ++ .../gui/components/EmptyComponent.java | 23 + .../components/GradientSliderComponent.java | 58 + .../gui/components/IconButtonComponent.java | 66 + .../gui/components/IconComponent.java | 61 + .../gui/components/LabelComponent.java | 52 + .../gui/components/ListComponent.java | 680 ++++++++++ .../gui/components/PanelComponent.java | 21 + .../gui/components/PieComponent.java | 165 +++ .../gui/components/ProgressBarComponent.java | 123 ++ .../gui/components/ScrollBarComponent.java | 244 ++++ .../gui/components/ScrollPanelComponent.java | 96 ++ .../gui/components/ScrollWindowComponent.java | 109 ++ .../gui/components/SelectionComponent.java | 365 ++++++ .../gui/components/SliderComponent.java | 203 +++ .../gui/components/TextCheckBoxComponent.java | 56 + .../gui/components/TextComponent.java | 447 +++++++ .../gui/components/TextFieldComponent.java | 723 +++++++++++ .../gui/components/TextPanelComponent.java | 688 ++++++++++ .../gui/components/TooltipPanel.java | 79 ++ .../gui/components/TreeComponent.java | 676 ++++++++++ .../gui/components/WindowComponent.java | 355 ++++++ .../gui/components/icon/ArrowIcon.java | 40 + .../gui/components/icon/CrossIcon.java | 33 + .../rendering/gui/components/icon/IIcon.java | 10 + .../gui/components/icon/LineIcon.java | 28 + .../gui/components/icon/TexturedIcon.java | 53 + .../gui/components/layouts/FlowLayout.java | 55 + .../components/layouts/VerticalLayout.java | 84 ++ .../gui/components/list/BaseListEntry.java | 101 ++ .../gui/components/list/ExampleEntry.java | 96 ++ .../gui/components/list/FileEntry.java | 20 + .../gui/components/list/IListEntry.java | 26 + .../gui/components/list/SelectionEntry.java | 81 ++ .../gui/components/list/TextEntry.java | 25 + .../gui/components/menu/MenuBarComponent.java | 103 ++ .../menu/MenuCheckBoxComponent.java | 126 ++ .../gui/components/menu/MenuComponent.java | 319 +++++ .../components/menu/MenuItemComponent.java | 129 ++ .../components/menu/MenuTextComponent.java | 39 + .../gui/components/misc/CheckBoxGroup.java | 118 ++ .../gui/components/misc/ICheckBox.java | 10 + .../components/special/ConsoleComponent.java | 223 ++++ .../gui/components/tree/BaseTreeEntry.java | 142 +++ .../gui/components/tree/ITreeEntry.java | 28 + .../components/tree/ProfilerTreeEntry.java | 129 ++ .../color/ColorPickerWindowComponent.java | 154 +++ .../components/window/color/ColorPool.java | 102 ++ .../debug/PieProfilerWindowComponent.java | 306 +++++ .../debug/TreeProfilerWindowComponent.java | 184 +++ .../window/misc/ChoiceComponent.java | 60 + .../window/misc/MessageComponent.java | 74 ++ .../window/misc/TextInputComponent.java | 61 + .../rendering/gui/helper/Align.java | 19 + .../rendering/gui/helper/TextFilter.java | 24 + .../rendering/gui/helper/UIShapes.java | 175 +++ .../gui/helper/animations/Animation.java | 46 + .../helper/animations/AnimationInstance.java | 53 + .../helper/animations/AnimationTarget.java | 52 + .../gui/helper/animations/Animator.java | 178 +++ .../animations/transitions/ITransition.java | 10 + .../transitions/LiniarTransition.java | 45 + .../rendering/gui/helper/box/GuiBox.java | 316 +++++ .../rendering/gui/helper/box/IGuiBox.java | 101 ++ .../rendering/gui/helper/box/IScreenBox.java | 22 + .../rendering/gui/helper/box/ParentBox.java | 276 ++++ .../helper/constrains/CenterConstrain.java | 44 + .../constrains/ComponentConstrains.java | 52 + .../constrains/ConditionalConstraint.java | 34 + .../gui/helper/constrains/Constrain.java | 107 ++ .../gui/helper/constrains/Constraints.java | 69 + .../helper/constrains/DynamicConstrain.java | 27 + .../gui/helper/constrains/MenuConstrain.java | 59 + .../helper/constrains/ParentConstrain.java | 34 + .../gui/helper/constrains/PixelConstrain.java | 33 + .../helper/constrains/RelativeConstrain.java | 24 + .../gui/helper/constrains/TextConstrain.java | 46 + .../rendering/gui/renderer/FontRenderer.java | 441 +++++++ .../rendering/gui/renderer/GuiModel.java | 51 + .../rendering/gui/renderer/GuiShader.java | 50 + .../rendering/gui/renderer/IFontRenderer.java | 130 ++ .../rendering/gui/renderer/UIRenderer.java | 712 +++++++++++ .../renderer/buffer/DelayedRenderBuffer.java | 140 +++ .../gui/renderer/buffer/RenderBuffer.java | 80 ++ .../buffer/TranslatedVertexBuilder.java | 98 ++ .../rendering/gui/renderer/lexer/Line.java | 142 +++ .../gui/renderer/lexer/TextContext.java | 205 +++ .../gui/renderer/lexer/TextLexer.java | 128 ++ .../gui/renderer/lexer/TextMetadata.java | 158 +++ .../rendering/gui/renderer/lexer/Word.java | 189 +++ .../gui/renderer/lexer/WordType.java | 121 ++ .../rendering/input/DropListener.java | 38 + .../coreengine/rendering/input/Keyboard.java | 104 ++ .../src/coreengine/rendering/input/Mouse.java | 127 ++ .../coreengine/rendering/input/MouseRay.java | 76 ++ .../rendering/input/bindings/Axis.java | 173 +++ .../input/bindings/InputBinding.java | 170 +++ .../input/bindings/utils/BindingRegistry.java | 64 + .../input/bindings/utils/BindingType.java | 59 + .../input/bindings/utils/ModType.java | 36 + .../rendering/input/camera/Camera.java | 206 +++ .../input/camera/DefaultCameraController.java | 18 + .../rendering/input/camera/Frustrum.java | 127 ++ .../input/camera/ICameraController.java | 14 + .../rendering/input/events/FileDropEvent.java | 42 + .../rendering/input/events/KeyEvent.java | 34 + .../rendering/input/events/MouseEvent.java | 88 ++ .../rendering/input/raycast/Ray.java | 35 + .../input/raycast/RayCollisions.java | 44 + .../rendering/input/raycast/RayResult.java | 38 + .../input/window/IWindowListener.java | 7 + .../rendering/input/window/Monitor.java | 110 ++ .../input/window/ScaledResolution.java | 82 ++ .../rendering/input/window/VideoMode.java | 88 ++ .../rendering/input/window/Window.java | 603 +++++++++ .../input/window/WindowCallback.java | 30 + .../input/window/WindowProvider.java | 228 ++++ .../rendering/models/BufferAttribute.java | 86 ++ .../coreengine/rendering/models/DrawCall.java | 88 ++ .../rendering/models/GLDataType.java | 56 + .../models/GrowableVertexBuffer.java | 41 + .../rendering/models/MultiVertexBuffer.java | 99 ++ .../rendering/models/RenderBuffer.java | 23 + .../rendering/models/UniformBuffer.java | 19 + .../rendering/models/VertexArray.java | 169 +++ .../rendering/models/VertexBuffer.java | 353 ++++++ .../models/frameBuffer/BufferAttachment.java | 82 ++ .../models/frameBuffer/FrameBuffer.java | 221 ++++ .../models/frameBuffer/IFrameAttachment.java | 10 + .../models/frameBuffer/TextureAttachment.java | 96 ++ .../rendering/shader/ProjectionBuffer.java | 74 ++ .../rendering/shader/ShaderEntry.java | 23 + .../rendering/shader/ShaderProgram.java | 129 ++ .../rendering/shader/ShaderTracker.java | 142 +++ .../rendering/shader/uniforms/Uniform.java | 38 + .../shader/uniforms/UniformArrayBase.java | 61 + .../shader/uniforms/UniformBase.java | 30 + .../shader/uniforms/UniformBoolean.java | 26 + .../shader/uniforms/UniformBufferBlock.java | 37 + .../shader/uniforms/UniformFloat.java | 27 + .../rendering/shader/uniforms/UniformInt.java | 22 + .../shader/uniforms/UniformMatrix.java | 33 + .../shader/uniforms/UniformTexture.java | 44 + .../shader/uniforms/UniformVec2f.java | 41 + .../shader/uniforms/UniformVec3f.java | 46 + .../shader/uniforms/UniformVec3fArray.java | 54 + .../shader/uniforms/UniformVec4f.java | 49 + .../shader/uniforms/UniformVec4fArray.java | 54 + .../rendering/tesselation/GLCall.java | 77 ++ .../rendering/tesselation/IVertexBuilder.java | 38 + .../rendering/tesselation/Tesselator.java | 276 ++++ .../rendering/tesselation/VertexElement.java | 44 + .../rendering/tesselation/VertexList.java | 71 ++ .../rendering/tesselation/VertexType.java | 47 + .../rendering/textures/AbstractTexture.java | 69 + .../rendering/textures/DirectTexture.java | 99 ++ .../rendering/textures/DynamicTexture.java | 153 +++ .../rendering/textures/ITexture.java | 22 + .../rendering/textures/SimpleTexture.java | 84 ++ .../rendering/textures/TextureManager.java | 144 +++ .../rendering/textures/WrappedTexture.java | 69 + .../rendering/utils/AllocationTracker.java | 62 + .../coreengine/rendering/utils/Cursor.java | 113 ++ .../coreengine/rendering/utils/GLStamper.java | 151 +++ .../coreengine/rendering/utils/GLUtils.java | 131 ++ .../rendering/utils/ScissorsManager.java | 76 ++ .../rendering/utils/ScreenshotHandler.java | 248 ++++ .../rendering/utils/ViewPortStack.java | 51 + .../rendering/utils/states/BlendState.java | 37 + .../rendering/utils/states/CullState.java | 33 + .../rendering/utils/states/FloatState.java | 58 + .../rendering/utils/states/GLProvoking.java | 17 + .../rendering/utils/states/GLState.java | 65 + .../rendering/utils/states/GLWireFrame.java | 17 + .../rendering/utils/states/IGLState.java | 6 + .../rendering/utils/states/LineSize.java | 25 + .../rendering/utils/states/PointSize.java | 23 + .../utils/collections/CollectionUtils.java | 54 + .../utils/collections/FlagHolder.java | 60 + .../collections/collection/BitArray.java | 111 ++ .../collections/collection/FixedBitSet.java | 290 +++++ .../utils/collections/collection/Lazy.java | 40 + .../collections/collection/SaveState.java | 77 ++ .../collections/iterators/EnumIterator.java | 27 + .../collections/iterators/IndexIterator.java | 31 + .../iterators/IterableWrapper.java | 55 + .../iterators/ReadOnlyIterator.java | 26 + .../collections/iterators/ReaderIterator.java | 57 + .../iterators/ReverseIterator.java | 27 + .../managers/dynamic/DataSlot.java | 64 + .../managers/dynamic/DynamicDataManager.java | 216 ++++ .../managers/dynamic/IDynamicDataHandler.java | 11 + .../managers/fixed/FixedDataManager.java | 358 ++++++ .../collections/managers/fixed/FixedSlot.java | 40 + .../managers/fixed/IFixedDataHandler.java | 13 + .../utils/collections/pools/FixedPool.java | 96 ++ .../utils/collections/pools/IPool.java | 14 + .../utils/collections/pools/SimplePool.java | 79 ++ .../collections/pools/ThreadLocalPool.java | 81 ++ .../utils/collections/pools/ThreadPool.java | 79 ++ .../registries/DependencyResolver.java | 111 ++ .../registries/flat/FlatRegistry.java | 60 + .../registries/flat/FlatRegistryEntry.java | 20 + .../registries/flat/IFlatRegistryEntry.java | 11 + .../registries/mutable/IMutableRegistry.java | 12 + .../registries/mutable/MutableRegistry.java | 127 ++ .../registries/simple/IRegistry.java | 23 + .../registries/simple/IRegistryEntry.java | 13 + .../registries/simple/RegistryEntry.java | 33 + .../registries/simple/SimpleRegistry.java | 133 ++ .../simple/SimpleRegistryWithDefault.java | 32 + .../utils/counters/averager/Averager.java | 59 + .../utils/counters/averager/Counter.java | 33 + .../utils/counters/averager/RunningAvg.java | 48 + .../utils/counters/averager/TimeAverager.java | 63 + .../utils/counters/timers/CountdownSync.java | 37 + .../utils/counters/timers/FPSTimer.java | 89 ++ .../utils/counters/timers/FrameSyncer.java | 51 + .../utils/counters/timers/Stopwatch.java | 59 + .../utils/counters/timers/Timer.java | 58 + .../utils/counters/timers/TimerTarget.java | 46 + .../src/coreengine/utils/eventbus/Event.java | 29 + .../coreengine/utils/eventbus/EventBus.java | 231 ++++ .../utils/eventbus/EventPriority.java | 27 + .../coreengine/utils/eventbus/Listeners.java | 103 ++ .../utils/eventbus/SubscribeEvent.java | 18 + .../utils/functions/BiIntConsumer.java | 7 + .../utils/functions/ConsumerConverter.java | 23 + .../utils/functions/FloatSupplier.java | 7 + .../coreengine/utils/functions/Functions.java | 32 + .../utils/functions/ObjFloatFunction.java | 7 + .../utils/functions/TriConsumer.java | 6 + .../coreengine/utils/helpers/FileUtils.java | 48 + .../utils/helpers/InternalThreadPools.java | 11 + .../coreengine/utils/helpers/JsonUtil.java | 125 ++ .../coreengine/utils/helpers/TextUtil.java | 106 ++ .../src/coreengine/utils/io/GameLog.java | 185 +++ .../src/coreengine/utils/io/GifWriter.java | 126 ++ .../coreengine/utils/io/dataTag/DataTag.java | 91 ++ .../utils/io/dataTag/array/ByteArrayTag.java | 110 ++ .../io/dataTag/array/DoubleArrayTag.java | 119 ++ .../utils/io/dataTag/array/FloatArrayTag.java | 119 ++ .../utils/io/dataTag/array/IntArrayTag.java | 119 ++ .../utils/io/dataTag/array/LongArrayTag.java | 119 ++ .../io/dataTag/array/MediumArrayTag.java | 119 ++ .../utils/io/dataTag/array/ShortArrayTag.java | 119 ++ .../io/dataTag/array/StringArrayTag.java | 134 ++ .../dataTag/compression/DataTagLimiter.java | 36 + .../io/dataTag/compression/TagCompressor.java | 122 ++ .../dataTag/compression/TagDecompressor.java | 107 ++ .../utils/io/dataTag/parsing/DataTagIO.java | 110 ++ .../io/dataTag/parsing/DataTagIOBuilder.java | 37 + .../io/dataTag/parsing/DataTagParser.java | 330 +++++ .../IDataDeserializationContext.java | 10 + .../deserialize/IDataDeserializer.java | 10 + .../serialize/IDataSerializationContext.java | 10 + .../parsing/serialize/IDataSerializer.java | 8 + .../utils/io/dataTag/simple/ByteTag.java | 120 ++ .../utils/io/dataTag/simple/DoubleTag.java | 121 ++ .../utils/io/dataTag/simple/FloatTag.java | 121 ++ .../utils/io/dataTag/simple/IntTag.java | 121 ++ .../utils/io/dataTag/simple/LongTag.java | 121 ++ .../utils/io/dataTag/simple/MediumTag.java | 121 ++ .../utils/io/dataTag/simple/PrimitiveTag.java | 18 + .../utils/io/dataTag/simple/ShortTag.java | 121 ++ .../utils/io/dataTag/simple/StringTag.java | 125 ++ .../utils/io/dataTag/special/IListTag.java | 67 + .../utils/io/dataTag/special/IMapTag.java | 249 ++++ .../utils/io/dataTag/special/ListTag.java | 257 ++++ .../utils/io/dataTag/special/MapTag.java | 210 ++++ .../utils/io/settings/BaseSetting.java | 37 + .../utils/io/settings/BooleanSetting.java | 66 + .../utils/io/settings/ISetting.java | 16 + .../utils/io/settings/IntSetting.java | 66 + .../utils/io/settings/Settings.java | 50 + .../utils/io/streams/ExtendedDataInput.java | 211 ++++ .../utils/io/streams/ExtendedDataOutput.java | 100 ++ .../utils/io/streams/StringReader.java | 172 +++ .../utils/profiler/EmptyProfiler.java | 36 + .../utils/profiler/GPUProfiler.java | 173 +++ .../utils/profiler/GPUProfilerEntry.java | 151 +++ .../coreengine/utils/profiler/IProfiler.java | 164 +++ .../coreengine/utils/profiler/Profiler.java | 165 +++ .../utils/profiler/ProfilerEntry.java | 152 +++ .../coreengine/utils/tasks/ArrayRunCall.java | 24 + .../coreengine/utils/tasks/ArrayRunnable.java | 20 + .../src/coreengine/utils/tasks/ITask.java | 7 + .../utils/tasks/ListenableTask.java | 40 + .../utils/tasks/MainThreadTaskProcessor.java | 146 +++ 412 files changed, 44175 insertions(+) create mode 100644 .classpath create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .project create mode 100644 .settings/org.eclipse.buildship.core.prefs create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle create mode 100644 src/main/java/speiger/src/coreengine/assets/AssetLocation.java create mode 100644 src/main/java/speiger/src/coreengine/assets/AssetManager.java create mode 100644 src/main/java/speiger/src/coreengine/assets/IAsset.java create mode 100644 src/main/java/speiger/src/coreengine/assets/IAssetPackage.java create mode 100644 src/main/java/speiger/src/coreengine/assets/MultiAsset.java create mode 100644 src/main/java/speiger/src/coreengine/assets/impl/FileAsset.java create mode 100644 src/main/java/speiger/src/coreengine/assets/impl/FolderAssetPackage.java create mode 100644 src/main/java/speiger/src/coreengine/assets/impl/ZipAsset.java create mode 100644 src/main/java/speiger/src/coreengine/assets/impl/ZipAssetPackage.java create mode 100644 src/main/java/speiger/src/coreengine/assets/language/I18n.java create mode 100644 src/main/java/speiger/src/coreengine/assets/language/Language.java create mode 100644 src/main/java/speiger/src/coreengine/assets/language/LanguageManager.java create mode 100644 src/main/java/speiger/src/coreengine/assets/reloader/IReloadableResource.java create mode 100644 src/main/java/speiger/src/coreengine/assets/reloader/ResourceReloader.java create mode 100644 src/main/java/speiger/src/coreengine/math/ArrayUtil.java create mode 100644 src/main/java/speiger/src/coreengine/math/BitUtil.java create mode 100644 src/main/java/speiger/src/coreengine/math/MathUtils.java create mode 100644 src/main/java/speiger/src/coreengine/math/ShapeUtil.java create mode 100644 src/main/java/speiger/src/coreengine/math/collision2d/Circle.java create mode 100644 src/main/java/speiger/src/coreengine/math/collision2d/I2DCollision.java create mode 100644 src/main/java/speiger/src/coreengine/math/collision2d/Mixed2DCollision.java create mode 100644 src/main/java/speiger/src/coreengine/math/collision2d/Plane.java create mode 100644 src/main/java/speiger/src/coreengine/math/misc/ColorObject.java create mode 100644 src/main/java/speiger/src/coreengine/math/misc/Facing.java create mode 100644 src/main/java/speiger/src/coreengine/math/misc/FacingList.java create mode 100644 src/main/java/speiger/src/coreengine/math/smooth/SmoothFloat.java create mode 100644 src/main/java/speiger/src/coreengine/math/smooth/SmoothVec3f.java create mode 100644 src/main/java/speiger/src/coreengine/math/value/ConstantValue.java create mode 100644 src/main/java/speiger/src/coreengine/math/value/CubicValue.java create mode 100644 src/main/java/speiger/src/coreengine/math/value/IValue.java create mode 100644 src/main/java/speiger/src/coreengine/math/value/LiniarValue.java create mode 100644 src/main/java/speiger/src/coreengine/math/value/QuadraticValue.java create mode 100644 src/main/java/speiger/src/coreengine/math/value/Value.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/Vec.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/VectorUtil.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2b.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2bImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2bMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3b.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3bImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3bMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4b.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4bImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4bMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/bytes/Vecb.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2d.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2dImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2dMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3d.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3dImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3dMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4d.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4dImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4dMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/doubles/Vecd.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/floats/Vec2f.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/floats/Vec2fImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/floats/Vec2fMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/floats/Vec3f.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/floats/Vec3fImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/floats/Vec3fMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/floats/Vec4f.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/floats/Vec4fImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/floats/Vec4fMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/floats/Vecf.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/ints/Vec2i.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/ints/Vec2iImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/ints/Vec2iMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/ints/Vec3i.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/ints/Vec3iImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/ints/Vec3iMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/ints/Vec4i.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/ints/Vec4iImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/ints/Vec4iMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/ints/Veci.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/longs/Vec2l.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/longs/Vec2lImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/longs/Vec2lMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/longs/Vec3l.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/longs/Vec3lImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/longs/Vec3lMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/longs/Vec4l.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/longs/Vec4lImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/longs/Vec4lMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/longs/Vecl.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/matrix/Matrix4f.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/quaternion/Quaternion.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/quaternion/QuaternionImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/quaternion/QuaternionMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2s.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2sImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2sMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3s.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3sImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3sMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4s.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4sImmutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4sMutable.java create mode 100644 src/main/java/speiger/src/coreengine/math/vector/shorts/Vecs.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/FontLoader.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/GuiBase.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/GuiComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/GuiManager.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/Tooltips.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/UITextures.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/base/DebugOverlay.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/base/GuiScreenBase.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/base/IButtonComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/base/IInputComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/base/IKeyComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/ButtonComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/CheckBoxComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/EmptyComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/GradientSliderComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/IconButtonComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/IconComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/LabelComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/ListComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/PanelComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/PieComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/ProgressBarComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollBarComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollPanelComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollWindowComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/SelectionComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/SliderComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/TextCheckBoxComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/TextComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/TextFieldComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/TextPanelComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/TooltipPanel.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/TreeComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/WindowComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/icon/ArrowIcon.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/icon/CrossIcon.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/icon/IIcon.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/icon/LineIcon.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/icon/TexturedIcon.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/layouts/FlowLayout.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/layouts/VerticalLayout.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/list/BaseListEntry.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/list/ExampleEntry.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/list/FileEntry.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/list/IListEntry.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/list/SelectionEntry.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/list/TextEntry.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuBarComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuCheckBoxComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuItemComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuTextComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/misc/CheckBoxGroup.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/misc/ICheckBox.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/special/ConsoleComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/tree/BaseTreeEntry.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/tree/ITreeEntry.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/tree/ProfilerTreeEntry.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/window/color/ColorPickerWindowComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/window/color/ColorPool.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/window/debug/PieProfilerWindowComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/window/debug/TreeProfilerWindowComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/ChoiceComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/MessageComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/TextInputComponent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/Align.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/TextFilter.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/UIShapes.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/Animation.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/AnimationInstance.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/AnimationTarget.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/Animator.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/transitions/ITransition.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/transitions/LiniarTransition.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/box/GuiBox.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/box/IGuiBox.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/box/IScreenBox.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/box/ParentBox.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/constrains/CenterConstrain.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/constrains/ComponentConstrains.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/constrains/ConditionalConstraint.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/constrains/Constrain.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/constrains/Constraints.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/constrains/DynamicConstrain.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/constrains/MenuConstrain.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/constrains/ParentConstrain.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/constrains/PixelConstrain.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/constrains/RelativeConstrain.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/helper/constrains/TextConstrain.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/renderer/FontRenderer.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/renderer/GuiModel.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/renderer/GuiShader.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/renderer/IFontRenderer.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/renderer/UIRenderer.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/DelayedRenderBuffer.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/RenderBuffer.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/TranslatedVertexBuilder.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/Line.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/TextContext.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/TextLexer.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/TextMetadata.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/Word.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/WordType.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/DropListener.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/Keyboard.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/Mouse.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/MouseRay.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/bindings/Axis.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/bindings/InputBinding.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/bindings/utils/BindingRegistry.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/bindings/utils/BindingType.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/bindings/utils/ModType.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/camera/Camera.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/camera/DefaultCameraController.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/camera/Frustrum.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/camera/ICameraController.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/events/FileDropEvent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/events/KeyEvent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/events/MouseEvent.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/raycast/Ray.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/raycast/RayCollisions.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/raycast/RayResult.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/window/IWindowListener.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/window/Monitor.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/window/ScaledResolution.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/window/VideoMode.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/window/Window.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/window/WindowCallback.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/input/window/WindowProvider.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/models/BufferAttribute.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/models/DrawCall.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/models/GLDataType.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/models/GrowableVertexBuffer.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/models/MultiVertexBuffer.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/models/RenderBuffer.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/models/UniformBuffer.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/models/VertexArray.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/models/VertexBuffer.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/models/frameBuffer/BufferAttachment.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/models/frameBuffer/FrameBuffer.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/models/frameBuffer/IFrameAttachment.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/models/frameBuffer/TextureAttachment.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/ProjectionBuffer.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/ShaderEntry.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/ShaderProgram.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/ShaderTracker.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/uniforms/Uniform.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformArrayBase.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformBase.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformBoolean.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformBufferBlock.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformFloat.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformInt.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformMatrix.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformTexture.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec2f.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec3f.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec3fArray.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec4f.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec4fArray.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/tesselation/GLCall.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/tesselation/IVertexBuilder.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/tesselation/Tesselator.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/tesselation/VertexElement.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/tesselation/VertexList.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/tesselation/VertexType.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/textures/AbstractTexture.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/textures/DirectTexture.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/textures/DynamicTexture.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/textures/ITexture.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/textures/SimpleTexture.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/textures/TextureManager.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/textures/WrappedTexture.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/utils/AllocationTracker.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/utils/Cursor.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/utils/GLStamper.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/utils/GLUtils.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/utils/ScissorsManager.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/utils/ScreenshotHandler.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/utils/ViewPortStack.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/utils/states/BlendState.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/utils/states/CullState.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/utils/states/FloatState.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/utils/states/GLProvoking.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/utils/states/GLState.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/utils/states/GLWireFrame.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/utils/states/IGLState.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/utils/states/LineSize.java create mode 100644 src/main/java/speiger/src/coreengine/rendering/utils/states/PointSize.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/CollectionUtils.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/FlagHolder.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/collection/BitArray.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/collection/FixedBitSet.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/collection/Lazy.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/collection/SaveState.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/iterators/EnumIterator.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/iterators/IndexIterator.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/iterators/IterableWrapper.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/iterators/ReadOnlyIterator.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/iterators/ReaderIterator.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/iterators/ReverseIterator.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/DataSlot.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/DynamicDataManager.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/IDynamicDataHandler.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/FixedDataManager.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/FixedSlot.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/IFixedDataHandler.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/pools/FixedPool.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/pools/IPool.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/pools/SimplePool.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/pools/ThreadLocalPool.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/pools/ThreadPool.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/registries/DependencyResolver.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/registries/flat/FlatRegistry.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/registries/flat/FlatRegistryEntry.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/registries/flat/IFlatRegistryEntry.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/registries/mutable/IMutableRegistry.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/registries/mutable/MutableRegistry.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/registries/simple/IRegistry.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/registries/simple/IRegistryEntry.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/registries/simple/RegistryEntry.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/registries/simple/SimpleRegistry.java create mode 100644 src/main/java/speiger/src/coreengine/utils/collections/registries/simple/SimpleRegistryWithDefault.java create mode 100644 src/main/java/speiger/src/coreengine/utils/counters/averager/Averager.java create mode 100644 src/main/java/speiger/src/coreengine/utils/counters/averager/Counter.java create mode 100644 src/main/java/speiger/src/coreengine/utils/counters/averager/RunningAvg.java create mode 100644 src/main/java/speiger/src/coreengine/utils/counters/averager/TimeAverager.java create mode 100644 src/main/java/speiger/src/coreengine/utils/counters/timers/CountdownSync.java create mode 100644 src/main/java/speiger/src/coreengine/utils/counters/timers/FPSTimer.java create mode 100644 src/main/java/speiger/src/coreengine/utils/counters/timers/FrameSyncer.java create mode 100644 src/main/java/speiger/src/coreengine/utils/counters/timers/Stopwatch.java create mode 100644 src/main/java/speiger/src/coreengine/utils/counters/timers/Timer.java create mode 100644 src/main/java/speiger/src/coreengine/utils/counters/timers/TimerTarget.java create mode 100644 src/main/java/speiger/src/coreengine/utils/eventbus/Event.java create mode 100644 src/main/java/speiger/src/coreengine/utils/eventbus/EventBus.java create mode 100644 src/main/java/speiger/src/coreengine/utils/eventbus/EventPriority.java create mode 100644 src/main/java/speiger/src/coreengine/utils/eventbus/Listeners.java create mode 100644 src/main/java/speiger/src/coreengine/utils/eventbus/SubscribeEvent.java create mode 100644 src/main/java/speiger/src/coreengine/utils/functions/BiIntConsumer.java create mode 100644 src/main/java/speiger/src/coreengine/utils/functions/ConsumerConverter.java create mode 100644 src/main/java/speiger/src/coreengine/utils/functions/FloatSupplier.java create mode 100644 src/main/java/speiger/src/coreengine/utils/functions/Functions.java create mode 100644 src/main/java/speiger/src/coreengine/utils/functions/ObjFloatFunction.java create mode 100644 src/main/java/speiger/src/coreengine/utils/functions/TriConsumer.java create mode 100644 src/main/java/speiger/src/coreengine/utils/helpers/FileUtils.java create mode 100644 src/main/java/speiger/src/coreengine/utils/helpers/InternalThreadPools.java create mode 100644 src/main/java/speiger/src/coreengine/utils/helpers/JsonUtil.java create mode 100644 src/main/java/speiger/src/coreengine/utils/helpers/TextUtil.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/GameLog.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/GifWriter.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/DataTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/array/ByteArrayTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/array/DoubleArrayTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/array/FloatArrayTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/array/IntArrayTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/array/LongArrayTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/array/MediumArrayTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/array/ShortArrayTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/array/StringArrayTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/compression/DataTagLimiter.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/compression/TagCompressor.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/compression/TagDecompressor.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/DataTagIO.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/DataTagIOBuilder.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/DataTagParser.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/deserialize/IDataDeserializationContext.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/deserialize/IDataDeserializer.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/serialize/IDataSerializationContext.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/serialize/IDataSerializer.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/ByteTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/DoubleTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/FloatTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/IntTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/LongTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/MediumTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/PrimitiveTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/ShortTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/StringTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/special/IListTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/special/IMapTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/special/ListTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/dataTag/special/MapTag.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/settings/BaseSetting.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/settings/BooleanSetting.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/settings/ISetting.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/settings/IntSetting.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/settings/Settings.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/streams/ExtendedDataInput.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/streams/ExtendedDataOutput.java create mode 100644 src/main/java/speiger/src/coreengine/utils/io/streams/StringReader.java create mode 100644 src/main/java/speiger/src/coreengine/utils/profiler/EmptyProfiler.java create mode 100644 src/main/java/speiger/src/coreengine/utils/profiler/GPUProfiler.java create mode 100644 src/main/java/speiger/src/coreengine/utils/profiler/GPUProfilerEntry.java create mode 100644 src/main/java/speiger/src/coreengine/utils/profiler/IProfiler.java create mode 100644 src/main/java/speiger/src/coreengine/utils/profiler/Profiler.java create mode 100644 src/main/java/speiger/src/coreengine/utils/profiler/ProfilerEntry.java create mode 100644 src/main/java/speiger/src/coreengine/utils/tasks/ArrayRunCall.java create mode 100644 src/main/java/speiger/src/coreengine/utils/tasks/ArrayRunnable.java create mode 100644 src/main/java/speiger/src/coreengine/utils/tasks/ITask.java create mode 100644 src/main/java/speiger/src/coreengine/utils/tasks/ListenableTask.java create mode 100644 src/main/java/speiger/src/coreengine/utils/tasks/MainThreadTaskProcessor.java diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..8b3d57e --- /dev/null +++ b/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..00a51af --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# These are explicitly windows files and should use crlf +*.bat text eol=crlf + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a510247 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +build +/bin/ \ No newline at end of file diff --git a/.project b/.project new file mode 100644 index 0000000..0be527d --- /dev/null +++ b/.project @@ -0,0 +1,23 @@ + + + SimpleJavaEngine + Project SimpleJavaEngine created by Buildship. + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.buildship.core.gradleprojectnature + + diff --git a/.settings/org.eclipse.buildship.core.prefs b/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 0000000..e889521 --- /dev/null +++ b/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,2 @@ +connection.project.dir= +eclipse.preferences.version=1 diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..c86fcc6 --- /dev/null +++ b/build.gradle @@ -0,0 +1,41 @@ +apply plugin: 'java' +apply plugin: 'eclipse' + +eclipse { + classpath { + downloadJavadoc = true + downloadSources = true + } +} +sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' +repositories { + mavenCentral() + maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } + maven { + url = "https://maven.speiger.com/repository/main" + } +} + +dependencies { + //LWJGL 3 + compile platform("org.lwjgl:lwjgl-bom:$lwjglVersion") + + compile "org.lwjgl:lwjgl" + compile "org.lwjgl:lwjgl-glfw" + compile "org.lwjgl:lwjgl-jemalloc" + compile "org.lwjgl:lwjgl-openal" + compile "org.lwjgl:lwjgl-opengl" + compile "org.lwjgl:lwjgl-stb" + compile "org.lwjgl:lwjgl::$lwjglNatives" + compile "org.lwjgl:lwjgl-glfw::$lwjglNatives" + compile "org.lwjgl:lwjgl-jemalloc::$lwjglNatives" + compile "org.lwjgl:lwjgl-openal::$lwjglNatives" + compile "org.lwjgl:lwjgl-opengl::$lwjglNatives" + compile "org.lwjgl:lwjgl-stb::$lwjglNatives" + + //Gson + compile 'com.google.code.gson:gson:2.8.6' + + //Primitive Collections + compile 'de.speiger:Primitive-Collections:0.3.4' +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..096849f --- /dev/null +++ b/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx2G + +lwjglVersion = 3.3.0-SNAPSHOT +lwjglNatives = natives-windows \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..490fda8577df6c95960ba7077c43220e5bb2c0d9 GIT binary patch literal 58694 zcma&OV~}Oh(k5J8>Mq;1ZQHhO+v>7y+qO>Gc6Hgdjp>5?}0s%q%y~>Cv3(!c&iqe4q$^V<9O+7CU z|6d2bzlQvOI?4#hN{EUmDbvb`-pfo*NK4Vs&cR60P)<+IG%C_BGVL7RP11}?Ovy}9 zNl^cQJPR>SIVjSkXhS0@IVhqGLL)&%E<(L^ymkEXU!M5)A^-c;K>yy`Ihy@nZ}orr zK>gFl%+bKu+T{P~iuCWUZjJ`__9l-1*OFwCg_8CkKtLEEKtOc=d5NH%owJkk-}N#E z7Pd;x29C}qj>HVKM%D&SPSJ`JwhR2oJPU0u3?)GiA|6TndJ+~^eXL<%D)IcZ)QT?t zE7BJP>Ejq;`w$<dd^@|esR(;1Z@9EVR%7cZG`%Xr%6 zLHXY#GmPV!HIO3@j5yf7D{PN5E6tHni4mC;qIq0Fj_fE~F1XBdnzZIRlk<~?V{-Uc zt9ldgjf)@8NoAK$6OR|2is_g&pSrDGlQS);>YwV7C!=#zDSwF}{_1#LA*~RGwALm) zC^N1ir5_}+4!)@;uj92irB5_Ugihk&Uh|VHd924V{MiY7NySDh z|6TZCb1g`c)w{MWlMFM5NK@xF)M33F$ZElj@}kMu$icMyba8UlNQ86~I$sau*1pzZ z4P)NF@3(jN(thO5jwkx(M5HOe)%P1~F!hXMr%Rp$&OY0X{l_froFdbi(jCNHbHj#! z(G`_tuGxu#h@C9HlIQ8BV4>%8eN=MApyiPE0B3dR`bsa1=MM$lp+38RN4~`m>PkE? zARywuzZ#nV|0wt;22|ITkkrt>ahz7`sKXd2!vpFCC4i9VnpNvmqseE%XnxofI*-Mr6tjm7-3$I-v}hr6B($ALZ=#Q4|_2l#i5JyVQCE{hJAnFhZF>vfSZgnw`Vgn zIi{y#1e7`}xydrUAdXQ%e?_V6K(DK89yBJ;6Sf{Viv*GzER9C3Mns=nTFt6`Eu?yu<*Fb}WpP$iO#-y+^H>OQ< zw%DSM@I=@a)183hx!sz(#&cg-6HVfK(UMgo8l2jynx5RWEo8`?+^3x0sEoj9H8%m1 z87?l+w;0=@Dx_J86rA6vesuDQ^nY(n?SUdaY}V)$Tvr%>m9XV>G>6qxKxkH zN6|PyTD(7+fjtb}cgW1rctvZQR!3wX2S|ils!b%(=jj6lLdx#rjQ6XuJE1JhNqzXO zKqFyP8Y1tN91g;ahYsvdGsfyUQz6$HMat!7N1mHzYtN3AcB>par(Q>mP7^`@7@Ox14gD12*4RISSYw-L>xO#HTRgM)eLaOOFuN}_UZymIhu%J?D|k>Y`@ zYxTvA;=QLhu@;%L6;Ir_$g+v3;LSm8e3sB;>pI5QG z{Vl6P-+69G-P$YH-yr^3cFga;`e4NUYzdQy6vd|9${^b#WDUtxoNe;FCcl5J7k*KC z7JS{rQ1%=7o8to#i-`FD3C?X3!60lDq4CqOJ8%iRrg=&2(}Q95QpU_q ziM346!4()C$dHU@LtBmfKr!gZGrZzO{`dm%w_L1DtKvh8UY zTP3-|50~Xjdu9c%Cm!BN^&9r?*Wgd(L@E!}M!#`C&rh&c2fsGJ_f)XcFg~$#3S&Qe z_%R=Gd`59Qicu`W5YXk>vz5!qmn`G>OCg>ZfGGuI5;yQW9Kg*exE+tdArtUQfZ&kO ze{h37fsXuQA2Z(QW|un!G2Xj&Qwsk6FBRWh;mfDsZ-$-!YefG!(+bY#l3gFuj)OHV830Xl*NKp1-L&NPA3a8jx#yEn3>wea~ z9zp8G6apWn$0s)Pa!TJo(?lHBT1U4L>82jifhXlkv^a+p%a{Og8D?k6izWyhv`6prd7Yq5{AqtzA8n{?H|LeQFqn(+fiIbDG zg_E<1t%>753QV!erV^G4^7p1SE7SzIqBwa{%kLHzP{|6_rlM*ae{*y4WO?{%&eQ`| z>&}ZkQ;<)rw;d(Dw*om?J@3<~UrXsvW2*0YOq_-Lfq45PQGUVu?Ws3&6g$q+q{mx4 z$2s@!*|A+74>QNlK!D%R(u22>Jeu}`5dsv9q~VD!>?V86x;Fg4W<^I;;ZEq5z4W5c z#xMX=!iYaaW~O<(q>kvxdjNk15H#p0CSmMaZB$+%v90@w(}o$T7;(B+Zv%msQvjnW z`k7=uf(h=gkivBw?57m%k^SPxZnYu@^F% zKd`b)S#no`JLULZCFuP^y5ViChc;^3Wz#c|ehD+2MHbUuB3IH5+bJ_FChTdARM6Q2 zdyuu9eX{WwRasK!aRXE+0j zbTS8wg@ue{fvJ*=KtlWbrXl8YP88;GXto?_h2t@dY3F?=gX9Frwb8f1n!^xdOFDL7 zbddq6he>%k+5?s}sy?~Ya!=BnwSDWloNT;~UF4|1>rUY!SSl^*F6NRs_DT-rn=t-p z_Ga0p)`@!^cxW_DhPA=0O;88pCT*G9YL29_4fJ(b{| zuR~VCZZCR97e%B(_F5^5Eifes$8!7DCO_4(x)XZDGO%dY9Pkm~-b1-jF#2H4kfl<3 zsBes0sP@Zyon~Q&#<7%gxK{o+vAsIR>gOm$w+{VY8ul7OsSQ>07{|7jB6zyyeu+WU zME>m2s|$xvdsY^K%~nZ^%Y`D7^PCO(&)eV-Qw|2_PnL=Nd=}#4kY)PS=Y62Dzz1e2 z&*)`$OEBuC&M5f`I}A-pEzy^lyEEcd$n1mEgLj}u_b^d!5pg{v+>_FexoDxYj%X_F z5?4eHVXurS%&n2ISv2&Eik?@3ry}0qCwS9}N)`Zc_Q8}^SOViB_AB&o6Eh#bG;NnL zAhP2ZF_la`=dZv6Hs@78DfMjy*KMSExRZfccK=-DPGkqtCK%U1cUXxbTX-I0m~x$3 z&Oc&aIGWtcf|i~=mPvR^u6^&kCj|>axShGlPG}r{DyFp(Fu;SAYJ}9JfF*x0k zA@C(i5ZM*(STcccXkpV$=TznZKQVtec!A24VWu*oS0L(^tkEm2ZIaE4~~?#y9Z4 zlU!AB6?yc(jiB`3+{FC zl|IdP1Fdt#e5DI{W{d8^$EijTU(8FA@8V&_A*tO?!9rI zhoRk`Q*riCozP>F%4pDPmA>R#Zm>_mAHB~Y5$sE4!+|=qK0dhMi4~`<6sFHb=x8Naml}1*8}K_Es3#oh3-7@0W}BJDREnwWmw<{wY9p)3+Mq2CLcX?uAvItguqhk*Po!RoP`kR)!OQy3Ayi zL@ozJ!I_F2!pTC?OBAaOrJmpGX^O(dSR-yu5Wh)f+o5O262f6JOWuXiJS_Jxgl@lS z6A9c*FSHGP4HuwS)6j3~b}t{+B(dqG&)Y}C;wnb!j#S0)CEpARwcF4Q-5J1NVizx7 z(bMG>ipLI1lCq?UH~V#i3HV9|bw%XdZ3Q#c3)GB+{2$zoMAev~Y~(|6Ae z^QU~3v#*S>oV*SKvA0QBA#xmq9=IVdwSO=m=4Krrlw>6t;Szk}sJ+#7=ZtX(gMbrz zNgv}8GoZ&$=ZYiI2d?HnNNGmr)3I);U4ha+6uY%DpeufsPbrea>v!D50Q)k2vM=aF-zUsW*aGLS`^2&YbchmKO=~eX@k9B!r;d{G% zrJU~03(->>utR^5;q!i>dAt)DdR!;<9f{o@y2f}(z(e)jj^*pcd%MN{5{J=K<@T!z zseP#j^E2G31piu$O@3kGQ{9>Qd;$6rr1>t!{2CuT_XWWDRfp7KykI?kXz^{u_T2AZ z-@;kGj8Iy>lOcUyjQqK!1OHkY?0Kz+_`V8$Q-V|8$9jR|%Ng;@c%kF_!rE3w>@FtX zX1w7WkFl%Vg<mE0aAHX==DLjyxlfA}H|LVh;}qcWPd8pSE!_IUJLeGAW#ZJ?W}V7P zpVeo|`)a<#+gd}dH%l)YUA-n_Vq3*FjG1}6mE;@A5ailjH*lJaEJl*51J0)Xecn6X zz zDr~lx5`!ZJ`=>>Xb$}p-!3w;ZHtu zX@xB4PbX!J(Jl((<8K%)inh!-3o2S2sbI4%wu9-4ksI2%e=uS?Wf^Tp%(Xc&wD6lV z*DV()$lAR&##AVg__A=Zlu(o$3KE|N7ZN{X8oJhG+FYyF!(%&R@5lpCP%A|{Q1cdr>x0<+;T`^onat<6tlGfEwRR?ZgMTD-H zjWY?{Fd8=Fa6&d@0+pW9nBt-!muY@I9R>eD5nEDcU~uHUT04gH-zYB>Re+h4EX|IH zp`Ls>YJkwWD3+}DE4rC3kT-xE89^K@HsCt6-d;w*o8xIHua~||4orJ<7@4w_#C6>W z2X$&H38OoW8Y-*i=@j*yn49#_C3?@G2CLiJUDzl(6P&v`lW|=gQ&)DVrrx8Bi8I|$ z7(7`p=^Lvkz`=Cwd<0%_jn&6k_a(+@)G^D04}UylQax*l(bhJ~;SkAR2q*4>ND5nc zq*k9(R}Ijc1J8ab>%Tv{kb-4TouWfA?-r(ns#ghDW^izG3{ts{C7vHc5Mv?G;)|uX zk&Fo*xoN`OG9ZXc>9(`lpHWj~9!hI;2aa_n!Ms1i;BFHx6DS23u^D^e(Esh~H@&f}y z(=+*7I@cUGi`U{tbSUcSLK`S)VzusqEY)E$ZOokTEf2RGchpmTva?Fj! z<7{9Gt=LM|*h&PWv6Q$Td!|H`q-aMIgR&X*;kUHfv^D|AE4OcSZUQ|1imQ!A$W)pJtk z56G;0w?&iaNV@U9;X5?ZW>qP-{h@HJMt;+=PbU7_w`{R_fX>X%vnR&Zy1Q-A=7**t zTve2IO>eEKt(CHjSI7HQ(>L5B5{~lPm91fnR^dEyxsVI-wF@82$~FD@aMT%$`usqNI=ZzH0)u>@_9{U!3CDDC#xA$pYqK4r~9cc_T@$nF1yODjb{=(x^({EuO?djG1Hjb{u zm*mDO(e-o|v2tgXdy87*&xVpO-z_q)f0~-cf!)nb@t_uCict?p-L%v$_mzG`FafIV zPTvXK4l3T8wAde%otZhyiEVVU^5vF zQSR{4him-GCc-(U;tIi;qz1|Az0<4+yh6xFtqB-2%0@ z&=d_5y>5s^NQKAWu@U#IY_*&G73!iPmFkWxxEU7f9<9wnOVvSuOeQ3&&HR<>$!b%J z#8i?CuHx%la$}8}7F5-*m)iU{a7!}-m@#O}ntat&#d4eSrT1%7>Z?A-i^Y!Wi|(we z$PBfV#FtNZG8N-Ot#Y>IW@GtOfzNuAxd1%=it zDRV-dU|LP#v70b5w~fm_gPT6THi zNnEw&|Yc9u5lzTVMAL} zgj|!L&v}W(2*U^u^+-e?Tw#UiCZc2omzhOf{tJX*;i2=i=9!kS&zQN_hKQ|u7_3vo6MU0{U+h~` zckXGO+XK9{1w3Z$U%%Fw`lr7kK8PzU=8%0O8ZkW`aQLFlR4OCb^aQgGCBqu6AymXk zX!p(JDJtR`xB$j48h}&I2FJ*^LFJzJQJ0T>=z{*> zWesZ#%W?fm`?f^B^%o~Jzm|Km5$LP#d7j9a{NCv!j14axHvO<2CpidW=|o4^a|l+- zSQunLj;${`o%xrlcaXzOKp>nU)`m{LuUW!CXzbyvn;MeK#-D{Z4)+>xSC)km=&K%R zsXs3uRkta6-rggb8TyRPnquv1>wDd)C^9iN(5&CEaV9yAt zM+V+%KXhGDc1+N$UNlgofj8+aM*(F7U3=?grj%;Pd+p)U9}P3ZN`}g3`{N`bm;B(n z12q1D7}$``YQC7EOed!n5Dyj4yl~s0lptb+#IEj|!RMbC!khpBx!H-Kul(_&-Z^OS zQTSJA@LK!h^~LG@`D}sMr2VU#6K5Q?wqb7-`ct2(IirhhvXj?(?WhcNjJiPSrwL0} z8LY~0+&7<~&)J!`T>YQgy-rcn_nf+LjKGy+w+`C*L97KMD%0FWRl`y*piJz2=w=pj zxAHHdkk9d1!t#bh8Joi1hTQr#iOmt8v`N--j%JaO`oqV^tdSlzr#3 zw70~p)P8lk<4pH{_x$^i#=~E_ApdX6JpR`h{@<Y;PC#{0uBTe z1Puhl^q=DuaW}Gdak6kV5w);35im0PJ0F)Zur)CI*LXZxZQTh=4dWX}V}7mD#oMAn zbxKB7lai}G8C){LS`hn>?4eZFaEw-JoHI@K3RbP_kR{5eyuwBL_dpWR>#bo!n~DvoXvX`ZK5r|$dBp6%z$H@WZ6Pdp&(zFKGQ z2s6#ReU0WxOLti@WW7auSuyOHvVqjaD?kX;l)J8tj7XM}lmLxLvp5V|CPQrt6ep+t z>7uK|fFYALj>J%ou!I+LR-l9`z3-3+92j2G`ZQPf18rst;qXuDk-J!kLB?0_=O}*XQ5wZMn+?ZaL5MKlZie- z0aZ$*5~FFU*qGs|-}v-t5c_o-ReR@faw^*mjbMK$lzHSheO*VJY)tBVymS^5ol=ea z)W#2z8xCoh1{FGtJA+01Hwg-bx`M$L9Ex-xpy?w-lF8e*xJXS4(I^=k1zFy|V)=ll z#&yez3hRC5?@rPywJo2eOHWezUxZphm#wo`oyA-sP@|^+LV0^nzq|UJEZZM9wqa z5Y}M0Lu@0Qd%+Q=3kCSb6q4J60t_s(V|qRw^LC>UL7I`=EZ zvIO;P2n27=QJ1u;C+X)Si-P#WB#phpY3XOzK(3nEUF7ie$>sBEM3=hq+x<=giJjgS zo;Cr5uINL%4k@)X%+3xvx$Y09(?<6*BFId+399%SC)d# zk;Qp$I}Yiytxm^3rOxjmRZ@ws;VRY?6Bo&oWewe2i9Kqr1zE9AM@6+=Y|L_N^HrlT zAtfnP-P8>AF{f>iYuKV%qL81zOkq3nc!_?K7R3p$fqJ?};QPz6@V8wnGX>3%U%$m2 zdZv|X+%cD<`OLtC<>=ty&o{n-xfXae2~M-euITZY#X@O}bkw#~FMKb5vG?`!j4R_X%$ZSdwW zUA0Gy&Q_mL5zkhAadfCo(yAw1T@}MNo>`3Dwou#CMu#xQKY6Z+9H+P|!nLI;4r9@k zn~I*^*4aA(4y^5tLD+8eX;UJW;>L%RZZUBo(bc{)BDM!>l%t?jm~}eCH?OOF%ak8# z*t$YllfyBeT(9=OcEH(SHw88EOH0L1Ad%-Q`N?nqM)<`&nNrp>iEY_T%M6&U>EAv3 zMsvg1E#a__!V1E|ZuY!oIS2BOo=CCwK1oaCp#1ED_}FGP(~Xp*P5Gu(Pry_U zm{t$qF^G^0JBYrbFzPZkQ;#A63o%iwe;VR?*J^GgWxhdj|tj`^@i@R+vqQWt~^ z-dLl-Ip4D{U<;YiFjr5OUU8X^=i35CYi#j7R! zI*9do!LQrEr^g;nF`us=oR2n9ei?Gf5HRr&(G380EO+L6zJD)+aTh_<9)I^{LjLZ} z{5Jw5vHzucQ*knJ6t}Z6k+!q5a{DB-(bcN*)y?Sfete7Y}R9Lo2M|#nIDsYc({XfB!7_Db0Z99yE8PO6EzLcJGBlHe(7Q{uv zlBy7LR||NEx|QyM9N>>7{Btifb9TAq5pHQpw?LRe+n2FV<(8`=R}8{6YnASBj8x}i zYx*enFXBG6t+tmqHv!u~OC2nNWGK0K3{9zRJ(umqvwQ~VvD;nj;ihior5N$Hf@y0G z$7zrb=CbhyXSy`!vcXK-T}kisTgI$8vjbuCSe7Ev*jOqI&Pt@bOEf>WoQ!A?`UlO5 zSLDKE(-mN4a{PUu$QdGbfiC)pA}phS|A1DE(f<{Dp4kIB_1mKQ5!0fdA-K0h#_ z{qMsj@t^!n0Lq%)h3rJizin0wT_+9K>&u0%?LWm<{e4V8W$zZ1w&-v}y zY<6F2$6Xk>9v{0@K&s(jkU9B=OgZI(LyZSF)*KtvI~a5BKr_FXctaVNLD0NIIokM}S}-mCB^^Sgqo%e{4!Hp)$^S%q@ zU%d&|hkGHUKO2R6V??lfWCWOdWk74WI`xmM5fDh+hy6>+e)rG_w>_P^^G!$hSnRFy z5fMJx^0LAAgO5*2-rsN)qx$MYzi<_A=|xez#rsT9&K*RCblT2FLJvb?Uv3q^@Dg+J zQX_NaZza4dAajS!khuvt_^1dZzOZ@eLg~t02)m2+CSD=}YAaS^Y9S`iR@UcHE%+L0 zOMR~6r?0Xv#X8)cU0tpbe+kQ;ls=ZUIe2NsxqZFJQj87#g@YO%a1*^ zJZ+`ah#*3dVYZdeNNnm8=XOOc<_l-b*uh zJR8{yQJ#-FyZ!7yNxY|?GlLse1ePK!VVPytKmBwlJdG-bgTYW$3T5KinRY#^Cyu@& zd7+|b@-AC67VEHufv=r5(%_#WwEIKjZ<$JD%4!oi1XH65r$LH#nHHab{9}kwrjtf= zD}rEC65~TXt=5bg*UFLw34&*pE_(Cw2EL5Zl2i^!+*Vx+kbkT_&WhOSRB#8RInsh4 z#1MLczJE+GAHR^>8hf#zC{pJfZ>6^uGn6@eIxmZ6g_nHEjMUUfXbTH1ZgT7?La;~e zs3(&$@4FmUVw3n033!1+c9dvs&5g#a;ehO(-Z}aF{HqygqtHf=>raoWK9h7z)|DUJ zlE0#|EkzOcrAqUZF+Wd@4$y>^0eh!m{y@qv6=C zD(){00vE=5FU@Fs_KEpaAU1#$zpPJGyi0!aXI8jWaDeTW=B?*No-vfv=>`L`LDp$C zr4*vgJ5D2Scl{+M;M(#9w_7ep3HY#do?!r0{nHPd3x=;3j^*PQpXv<~Ozd9iWWlY_ zVtFYzhA<4@zzoWV-~in%6$}Hn$N;>o1-pMK+w$LaN1wA95mMI&Q6ayQO9 zTq&j)LJm4xXjRCse?rMnbm%7E#%zk!EQiZwt6gMD=U6A0&qXp%yMa(+C~^(OtJ8dH z%G1mS)K9xV9dlK>%`(o6dKK>DV07o46tBJfVxkIz#%VIv{;|)?#_}Qq(&| zd&;iIJt$|`te=bIHMpF1DJMzXKZp#7Fw5Q0MQe@;_@g$+ELRfh-UWeYy%L*A@SO^J zLlE}MRZt(zOi6yo!);4@-`i~q5OUAsac^;RpULJD(^bTLt9H{0a6nh0<)D6NS7jfB ze{x#X2FLD2deI8!#U@5$i}Wf}MzK&6lSkFy1m2c~J?s=!m}7%3UPXH_+2MnKNY)cI z(bLGQD4ju@^<+%T5O`#77fmRYxbs(7bTrFr=T@hEUIz1t#*ntFLGOz)B`J&3WQa&N zPEYQ;fDRC-nY4KN`8gp*uO@rMqDG6=_hHIX#u{TNpjYRJ9ALCl!f%ew7HeprH_I2L z6;f}G90}1x9QfwY*hxe&*o-^J#qQ6Ry%2rn=9G3*B@86`$Pk1`4Rb~}`P-8^V-x+s zB}Ne8)A3Ex29IIF2G8dGEkK^+^0PK36l3ImaSv1$@e=qklBmy~7>5IxwCD9{RFp%q ziejFT(-C>MdzgQK9#gC?iFYy~bjDcFA^%dwfTyVCk zuralB)EkA)*^8ZQd8T!ofh-tRQ#&mWFo|Y3taDm8(0=KK>xke#KPn8yLCXwq zc*)>?gGKvSK(}m0p4uL8oQ~!xRqzDRo(?wvwk^#Khr&lf9YEPLGwiZjwbu*p+mkWPmhoh0Fb(mhJEKXl+d68b6%U{E994D z3$NC=-avSg7s{si#CmtfGxsijK_oO7^V`s{?x=BsJkUR4=?e@9# z-u?V8GyQp-ANr%JpYO;3gxWS?0}zLmnTgC66NOqtf*p_09~M-|Xk6ss7$w#kdP8`n zH%UdedsMuEeS8Fq0RfN}Wz(IW%D%Tp)9owlGyx#i8YZYsxWimQ>^4ikb-?S+G;HDT zN4q1{0@|^k_h_VFRCBtku@wMa*bIQc%sKe0{X@5LceE`Uqqu7E9i9z-r}N2ypvdX1{P$*-pa$A8*~d0e5AYkh_aF|LHt7qOX>#d3QOp-iEO7Kq;+}w zb)Le}C#pfmSYYGnq$Qi4!R&T{OREvbk_;7 zHP<*B$~Qij1!9Me!@^GJE-icH=set0fF-#u5Z{JmNLny=S*9dbnU@H?OCXAr7nHQH zw?$mVH^W-Y89?MZo5&q{C2*lq}sj&-3@*&EZaAtpxiLU==S@m_PJ6boIC9+8fKz@hUDw==nNm9? z`#!-+AtyCOSDPZA)zYeB|EQ)nBq6!QI66xq*PBI~_;`fHEOor}>5jj^BQ;|-qS5}1 zRezNBpWm1bXrPw3VC_VHd z$B06#uyUhx)%6RkK2r8*_LZ3>-t5tG8Q?LU0Yy+>76dD(m|zCJ>)}9AB>y{*ftDP3 z(u8DDZd(m;TcxW-w$(vq7bL&s#U_bsIm67w{1n|y{k9Ei8Q9*8E^W0Jr@M?kBFJE< zR7Pu}#3rND;*ulO8X%sX>8ei7$^z&ZH45(C#SbEXrr3T~e`uhVobV2-@p5g9Of%!f z6?{|Pt*jW^oV0IV7V76Pd>Pcw5%?;s&<7xelwDKHz(KgGL7GL?IZO%upB+GMgBd3ReR9BS zL_FPE2>LuGcN#%&=eWWe;P=ylS9oIWY)Xu2dhNe6piyHMI#X4BFtk}C9v?B3V+zty zLFqiPB1!E%%mzSFV+n<(Rc*VbvZr)iJHu(HabSA_YxGNzh zN~O(jLq9bX41v{5C8%l%1BRh%NDH7Vx~8nuy;uCeXKo2Do{MzWQyblZsWdk>k0F~t z`~8{PWc86VJ)FDpj!nu))QgHjl7a%ArDrm#3heEHn|;W>xYCocNAqX{J(tD!)~rWu zlRPZ3i5sW;k^^%0SkgV4lypb zqKU2~tqa+!Z<)!?;*50pT&!3xJ7=7^xOO0_FGFw8ZSWlE!BYS2|hqhQT8#x zm2a$OL>CiGV&3;5-sXp>3+g+|p2NdJO>bCRs-qR(EiT&g4v@yhz(N5cU9UibBQ8wM z0gwd4VHEs(Mm@RP(Zi4$LNsH1IhR}R7c9Wd$?_+)r5@aj+!=1-`fU(vr5 z1c+GqAUKulljmu#ig5^SF#{ag10PEzO>6fMjOFM_Le>aUbw>xES_Ow|#~N%FoD{5!xir^;`L1kSb+I^f z?rJ0FZugo~sm)@2rP_8p$_*&{GcA4YyWT=!uriu+ZJ%~_OD4N%!DEtk9SCh+A!w=< z3af%$60rM%vdi%^X2mSb)ae>sk&DI_&+guIC88_Gq|I1_7q#}`9b8X zGj%idjshYiq&AuXp%CXk>zQ3d2Ce9%-?0jr%6-sX3J{*Rgrnj=nJ2`#m`TaW-13kl zS2>w8ehkYEx@ml2JPivxp zIa2l^?)!?Y*=-+jk_t;IMABQ5Uynh&LM^(QB{&VrD7^=pXNowzD9wtMkH_;`H|d0V z*rohM)wDg^EH_&~=1j1*?@~WvMG3lH=m#Btz?6d9$E*V5t~weSf4L%|H?z-^g>Fg` zI_Q+vgHOuz31?mB{v#4(aIP}^+RYU}^%XN}vX_KN=fc{lHc5;0^F2$2A+%}D=gk-) zi1qBh!1%xw*uL=ZzYWm-#W4PV(?-=hNF%1cXpWQ_m=ck1vUdTUs5d@2Jm zV8cXsVsu~*f6=_7@=1 zaV0n2`FeQ{62GMaozYS)v~i10wGoOs+Z8=g$F-6HH1qBbasAkkcZj-}MVz{%xf8`2 z1XJU;&QUY4Hf-I(AG8bX zhu~KqL}TXS6{)DhW=GFkCzMFMSf`Y00e{Gzu2wiS4zB|PczU^tjLhOJUv=i2KuFZHf-&`wi>CU0h_HUxCdaZ`s9J8|7F}9fZXg`UUL}ws7G=*n zImEd-k@tEXU?iKG#2I13*%OX#dXKTUuv1X3{*WEJS41ci+uy=>30LWCv*YfX_A2(M z9lnNAjLIzX=z;g;-=ARa<`z$x)$PYig1|#G;lnOs8-&rB2lT0#e;`EH8qZ_xNvwy7 zo_9>P@SHK(YPu*8r86f==eshYjM3yAPOHDn- zmuW04o02AGMz!S|S32(h560d(IP$;S7LIM(PC7Owwr$&XCbsQNY))+3HYS+ZcHTVq zJm;QsfA`#~_m8fwuI~DFb$@pE-h1t}*HZB7hc-CUM~x6aZ<4v9_Jr-))=El>(rphK z(@wMC$e>^o+cQ(9S+>&JfP;&KM6nff2{RNu;MqE9>L9t^lvzo^*B5>@$TG!gZlh0Z z%us8ys$1~v&&N-gPBvXl5b<#>-@lhAkg_4Ev6#R&r{ObIn=Qki&`wxR_OWj%kU_RW&w#Mxv%x zW|-sJ^jss+;xmxi8?gphNW{^HZ!xF?poe%mgZ>nwlqgvH@TrZ zad5)yJx3T|&$Afl$pkh=7bZAwBdv+tQEP=d3vE#o<&r6h+sTU$64ZZQ0e^Fu9FrnL zN-?**4ta&!+{cP=jt`w)5|dD&CP@-&*BsN#mlbUn!V*(E_gskcQ*%F#Nw#aTkp%x| z8^&g)1d!%Y+`L!Se2s_XzKfonT_BWbn}LQo#YUAx%f7L__h4Xi680GIk)s z8GHm59EYn(@4c&eAO)}0US@((t#0+rNZ680SS<=I^|Y=Yv)b<@n%L20qu7N%V1-k1 z*oxpOj$ZAc>L6T)SZX?Pyr#}Q?B`7ZlBrE1fHHx_Au{q9@ zLxwPOf>*Gtfv6-GYOcT^ZJ7RGEJTVXN=5(;{;{xAV3n`q1Z-USkK626;atcu%dTHU zBewQwrpcZkKoR(iF;fVev&D;m9q)URqvKP*eF9J=A?~0=jn3=_&80vhfBp?6@KUpgyS`kBk(S0@X5Xf%a~?#4Ct5nMB9q~)LP<`G#T-eA z+)6cl1H-2uMP=u<=saDj*;pOggb2(NJO^pW8O<6u^?*eiqn7h)w9{D`TrE1~k?Xuo z(r%NIhw3kcTHS%9nbff>-jK1k^~zr8kypQJ6W+?dkY7YS`Nm z5i;Q23ZpJw(F7|e?)Tm~1bL9IUKx6GC*JpUa_Y00Xs5nyxGmS~b{ zR!(TzwMuC%bB8&O->J82?@C|9V)#i3Aziv7?3Z5}d|0eTTLj*W3?I32?02>Eg=#{> zpAO;KQmA}fx?}j`@@DX-pp6{-YkYY81dkYQ(_B88^-J#rKVh8Wys-;z)LlPu{B)0m zeZr=9{@6=7mrjShh~-=rU}n&B%a7qs1JL_nBa>kJFQ8elV=2!WY1B5t2M5GD5lt|f zSAvTgLUv#8^>CX}cM(i(>(-)dxz;iDvWw5O!)c5)TBoWp3$>3rUI=pH9D1ffeIOUW zDbYx}+)$*+`hT}j226{;=*3(uc*ge(HQpTHM4iD&r<=JVc1(gCy}hK%<(6)^`uY4>Tj6rIHYB zqW5UAzpdS!34#jL;{)Fw{QUgJ~=w`e>PHMsnS1TcIXXHZ&3M~eK5l>Xu zKsoFCd%;X@qk#m-fefH;((&?Y9grF{Al#55A3~L5YF0plJ;G=;Tr^+W-7|6IO;Q+8 z(jAXq$ayf;ZkMZ4(*w?Oh@p8LhC6=8??!%@V(e}%*>fW^Gdn|qZVyvHhcn;7nP7e; z13!D$^-?^#x*6d1)88ft06hVZh%m4w`xR?!cnzuoOj(g9mdE2vbKT@RghJ)XOPj{9 z@)8!#=HRJvG=jDJ77XND;cYsC=CszC!<6GUC=XLuTJ&-QRa~EvJ1rk2+G!*oQJ-rv zDyHVZ{iQN$*5is?dNbqV8|qhc*O15)HGG)f2t9s^Qf|=^iI?0K-Y1iTdr3g=GJp?V z$xZiigo(pndUv;n1xV1r5+5qPf#vQQWw3m&pRT>G&vF( zUfKIQg9%G;R`*OdO#O;nP4o+BElMgmKt<>DmKO1)S$&&!q6#4HnU4||lxfMa-543{ zkyJ+ohEfq{OG3{kZszURE;Rw$%Q;egRKJ%zsVcXx!KIO0*3MFBx83sD=dDVsvc17i zIOZuEaaI~q`@!AR{gEL#Iw}zQpS$K6i&omY2n94@a^sD@tQSO(dA(npgkPs7kGm>;j?$Ia@Q-Xnzz?(tgpkA6VBPNX zE?K%$+e~B{@o>S+P?h6K=XP;caQ=3)I{@ZMNDz)9J2T#5m#h9nXd*33TEH^v7|~i) zeYctF*06eX)*0e{xXaPT!my1$Xq>KPJakJto3xnuT&z zSaL8NwRUFm?&xIMwA~gt4hc3=hAde#vDjQ!I)@;V<9h2YOvi-XzleP!g4blZm|$iV zF%c3G8Cs;FH8|zEczqGSY%F54h`$P_VsmJ6TaXRLc8lSf`Sv%s%6<4+;Wbs-3lya( z=9I>I%97Y~G945O48YaAq6ENPUs%EJvyC! zM4jMgJj}r~@D;cdaQ-j#`5zCRku}42aI<>CgraXuKDr19db~#|@UyM;f-uc!(KDsu z5EA@CsN>^t@oH+0!SALi;ud>`P5mQta+Lh*-#RHJ)Gin%>EaFLSoU`(TG7c|yeFvl zk|Yll%)h-*%WoI6M*j+4xw`OqiDVX{k-^V2{rzCIM9mzNHGP^D={!*P7T)%yDSI5- zkGA4}r3`)#Vl6JFJ3xG)8K;FTtII9o7jNHof_Z_Zc<%@-H4RPpyXudpf)ky zmTH$LFGxaIUGQ;l=>R>?+>ZSCU|@&+Gt@5Bj3w{L{KPpgQ<~)jqx0oNZSv9R&^A42 zzqJr?C#D-n>=9FjM=D=7h_$QO$KQ8*%0%)rI(Npai_JjE9_lBk75BQMI zkk4X5PATWgrub!fb5Hxi8{(Y<(GOO8^HECOA)eanyS{u%leQOkp;1W}_8eH?nPQxW zd#Z+uJfTK>g-TR3WPu~2Ru9A+NkuIICM@PyPmJn(GBZt;xFZNDMbw8`xzl2`(?UC- z#<*=*fo{UOvycb|b&4y0Nm!sHhFMI*Y$Olgh;BG#xBU+yxav82Ejj(ZvQ|64Wwy7I zN=DXx7(V^NTH3YRB4HOu6T5=DW86P`L#Ng!SuT{%&>Cq8>|o8lF^^U%MRU41TT?h& z!uJ$YdbM*2y?#`LJ2)XPoKq`hm$I3R{V5-;@u7!E9tH4sR(`Ab-Qh!|UN-a5fZ?P@2LWRvSv!hOk08;Yy!h&uEI-X}j+&v`X` zkqY%*F@{}DHL*Jgjg2}a54hwEV`63bK4>mL%D^YT|>m1-kX{876BRm&`Y#{$&oz($qWJL}T*tj42k+yu8fa=4b7VUPq()Wb~=L?DU0U-4*Iu^KMZBRByWn-@=_f(4){Or#| zpw}~Ajs6a=z!8_H59lqYlfnS77QY0pHpIz0#)}!EGhypupZeZe@%cv z6Dngnl*SsUy^a`v?>lARi6Yps@%32JpGQvrcd*A8LPLEInBEU2vriGvMqG!jh^=Gj zXvu5zpikqnt*e4&Un_e$2FAB?(yOS0JAzxh@nN?Blqc-)Pv`U}&E5|# z)97-9utpqi*`hR+$;eS)A+KK)CO)V`b?*}z&*+28mDfWI31)sF)tBg6LVlxS z225poL+O|x)5;skkj{rew<}TsDVqFMMLSgd;UK7^clMcObM~IgSq6!eJ($JP!KHPr zBJ&SHi{wLsgMzn1^#kV#_!NO@RG@B5lxBO7WfIAi@o`{_XQg(*{R=@Z(0ij+*i7sK zW5D%_fRN7l6qpytW2K1lUqP&W5jDT!AA9@q<;M!T=CKv*^MP)Er_uLL+Y53>**w7Y zQ!2?^4$wC;Soc!+#~d?Yec;NLdR z{~*hrSQS>UOMBe)1pHe0EsyO@d(IrU4ZiS&jL`wqv6Oqv=HbI^70qu9kn~wGkNL^> z!Pd2)i--+&zp^`#4@*Myg;3r(jt*h@RWgRt70byZr;0Na8n4!bmpuX1&gK=QK!@j< zH2fF7@2s0H0!9%VC-BIp(99@e@<%Ko?BB9uv*xPnZ5dQr z8r7~9cZXv(AZPY^<(X@}GARv&_}mfYA7`vdl=)g2GIyN(<}(b_S_N2--NKp$SgO<3 zRx|EabcjUSB44GaH3Kxmx3SW;E;Eia2Zs5SkbkQ8E%VQqr0J?tQjF~p;nbIXn+D;? zg;t3Jg7A@9U**@aaqs}9;%??Scm{zBIY2ceYAQd*W-hB-!+H&4#yrm*GtT*&#`FXx zGIVm}G<;Pj+h*KQ68S4rcIIGw-mkl039s@O4p9F%TC&&&xRL=N49v2PdBb$MxJoMo zQk8+Sv+F5m{xP1prZvn1=x-Q z&Yox|y&arZrLTm~<%o}VfPV#z+i&{)W5emXhx^g~8>eUe)|Vvwp8-x8d-MOj%@mSk zZ9i{-Hu8m-rfO##y(_Rv;Y@?6%h4Id#6%`7ah+IaQ13o7o>bG&ScMj&KO~QoCmNT6()+oo%B zugV3Da)t>unQq=tbD)FP{JmB~S5QCmb)lq9Fp(*|(UGeXr3kR?k35sKFs{{a*y+h0anA_K@iCi;BR6nFmKHC=@)rMmu=XWS1nVqD*=#${cFJ6<{e=U7!Rbg>Y0b~d#&viX+5m9aNAv=RAMt8=n6a&@t^|2LsKMR7xF z;Cmw>t0<=W2II;doX`p#bcjPV9z&3dhAObzcB9xXMslqr(y!P6+2kG>Eh!rx&ZKmW)Wk~_xh`?neJqVhJk~1eTvRF#ehRwpS>s1{vUx*qf&Jm z$)Wh|lmwYatW@U@*$<14>^|yYwmwFs)C5ke9hG42{gilSU#^ulO`M}`wJ_4*-3 zGb?hfQj_AGQBI?4ghGijqfu>uAYkLK#!^uGUXuctdn8Ae5I7}o+j{9MJiM|sf9Nc{ zuP&Ls@?rMe=IfJo!=iX?9&*4!Yjs5d?0Yx4cIFXrkSHRk17Fc@yM__fyFLLl6O9nT zQqaDXunH;!PpQ7+-&#wJVtJXl8LjIkh)5qmcqhErYrP31w5~#!tS{LYTWGKEtbpE%(hH>qV(!2KMfs#a z?ZzzbDB}(7+NWIiSBQ<_{3>;H;z}uZI;n2PKWJNxM=l;5-^zpu-}+1x|38lS-}6GX z6F=M~bUtHg98X@of>mgCH-&5g6UpXGAla<+g`b&MQANW6D^;zfSzq0mQ)*J%;&tPOYin?J*G7GqmQ=>jvWvOn6E?! z{$(CU7}zChEnl$(>xf`ZdeF2E9Bv=eH&T4HWAOQ!9gBs z{gl^|(78q-ioBS^rR2PEGZLe_4Rl**H(bB?84RHquCEKi8N#29u=Eoh(DV`ZX{+8< z3BIX<`sOFNBziFWS#-X%(e`0C_|Q8;Pw9izjNOF8h|kvmWCmDHM&pANC9MV<wEJ;W{-jXqm!zC+Y@Q1y_lLL zfV^(1{A;L%TWmyI)RPknVUB<4r+d42S(W=%bXd@YB(~d>ABq-E;t)ie6%ouy(Fg`p zuj<=I7^PDs5H+UsG}+GH}zoGt*{yKF&n23C7aW@ z4ydrRtFW-uuAUu@RWe&0c!N4!H;`!n@@t#u zxlGQB4rx(F7#&MKHPy}EI;d+l(G{1KG!ZBE)7)@P!AsUCCCb0IH!P5TW=GoNFcif`NB4en16Cp<7=fhz7^uQAjbJBH>@naf2ueMktmtZ|U|)ICDMN2r`mgMSl=qDwHL;}L-d~El>pf8UJRts_03eTj*hVy6H z5o!>?AcffORZq9!NJNa`-W4wMfe6I{3*rYUhIMA>y|T}KZ56HR5XEs{(|x#SDtP@N z5?12L0W7qfvWl8T-V+u=fkBH8!$}g)7hRs34m7~)^S&Ar zd`Kz7$S2Mz(|5H(Dwn$V7n8K2pqhHQ8!i{G4C~Y6_Ex&Y%EyXdw#Nj}VdG`XCN_1n zFg4;3DGjjUo$%=m@ui%z$JU66QK^qywvLKZpD6ZQ2Ve2VBps8rcvJ6^Cf^#H4?UQ5PW$4;b)55yIY9}@k@48RLtJa>7bofX{EUE7 z?0Cx0PeYbbLAelC-BfqHf_08;{lzC1kwr|a>5{O6*g<~wt6KYPfP5uW0w?VTO!M~Q z6H@n{cONp`{>hVjEIkOV6m^ZP^l;mGz=T&*5&`m84astyZ#XZ6CpH384tt%vSJ zsvYDC5u`D&U_u)1OJ&D2=F*ie-7!%N+V6*qoM6m-zj|}hDZ+@?`mJ10OX3K-`+R0m zNk$^+zBJK7%It=_&sIc}&DT>!LYU{|WPNrp-Nfly8u5&3@(l{!pcPxek3^{L`<9*! zE-0KukkD^^+<&3BNJM$e0=~B$=VQEp@V`L+PsUEL-_%+E_kyR-_mUjr|D1Z2J->y2 zZNHTrzP$=uEKQvy4DG&+4*o5^8Kd?eI>5S#b;NXlSrGVnj3~e^OLe4*Qe7%U#4WiX z)k7h@VHRERR_j{wp8ALHdD6bj&+Dl^?2(MuL9*oTRUI3SQ2jJ4x#!GR~b8F(H6|clt%g_O=v(@*;;5eW{e)CsR{UNDIE{C-1@qe z7NY&S7DeI4?z7tR9LJ$e6za%qLsF(>%M?m1nQQ4htpl?P)yj7_C#Ds5k5F z1h@YlI%a#k9x6}=hs(mkRr-fSrmikEk)Iv6D`S==)-dDVbNK;4F@J7iC(M!K6l<^lm@iXKpYbd7b{_0BDjc9ju~tFH7Qfcgu>A9~3tzmbFnXbS(pWES9955Vbu=iI zX>GH$kbD_?_fRojp{~Mz+%=%RHG!3l(wxQb{zQlW&MTlbr2*9|peUBo#YZ8u!UMPz zJo9lmW3isPrkErmxp&SA4Z4vpe~LLL-w6JUW}f*bf#w6lVyDvUhdK9fX!p#TT3fL+ z7im|;28gcWM)UdfRI;603BWd`d%7#sP0t)qNW*R*WmrD?hg37Zngmu{P;Lm`rlK_> zITGMQH~V(}6l6}TeG5nPEHYI3EHiY}TD%AAQ@%&*Q@w}lLp!VC>E;PCjzgVyNqNmA zYd0t~-pn55?#)1Tc-(xbL07m;Md14bPJOLyoRpLhRx-BtH{Z%<78P>0$olxWy4d9! zncKIDHrWFnBRUUqc`qiz@xrz52u-?2kq~5n$h}&*K?MxJ?xV?vVXvLErROVl7L9s; zedsv`#k1PCWY;`{${N?=R9%uy1P+jKf$&__RLHP zWVH#4;U{}bB4D^B*hm%nhRpQF{4?xW$&|oNp2CUE?Coyj1QI%P|w91%+*lty%ecgZ$I1|mJWq9_c?+4{KElHR%TIU zf+^4^hXY?f0&(|Q5=NG~AhiIVR+(a1gF)Q;L&vH%zPO{yydKt*(f#LehU3CVRIS&* zA1khb+xXe{29|Ggayz;nqv9M8n$JYj?Z!w0Sb}^lq#XQlg~=nkBhYxmlB{huZcL}F zA6sNZgJpJ|laA>P$V#ZhT+&$nvNM2sudEEeUaohc#ab+sC zrj7G)E-#;G-w=I1hTjN@b;lAjX40pR+<>)=n`V_!(JFk*yE zP3nDEs^C9DCSbs8`TV~U17Bmq%9I^$2xWK;N>;W~^^HOu)jQt*LH(-WD@UyR?lk$o z+mZhVgYn<1!ov1;W|rozPKN*0V#Xxdelr-6M$Gf?*Y~BQbHRK-&@B;ni(p_#pe0mg z(1pQKcH#lqe^P^eZVUta>(kWOPSnhH^E-oKtcJzCI^FSuJ zze(PI3_%VP4Fp7k#GyT8c6l?vndL`$$s5Z05+P==upnazJ>&{eIc?MW6fVO34pXfm zmmilQmRYtQ*e*BV>J{aqI%F$j*;=Tdx{msYgM{2Gd`D^TU>~NLKrbqtQDh6KPGcB& zYEY{fj~P1Q zY_vIx8j+W?nOTo{k7|A!vvlK?qYKZnTkm@qV7lWQf#;J@)(qh~m07vHwdQ@701t>}N2> zYt=Q^?p;5oP%enrkvLCarS2rlJ;zjT@1)Ha_28t7T(IMcZi3U?D_dTzMKnR%{b7 zXeWL6f-xfJvhsVNF_?I2^3gmv=2|f7azO~wc+o|=2cR+N_<9sF;vio2z;vtlV7U6o z%q9XNPhjS1Fv)QuRq|0#HVGw&HG!!t0wQo=W>hP)uYZ7o;_qdM=-*`k-Z%4+>VGZ; z{vGL`lv&#q*NFJmy`%{yAIPrAB%*freDk*5cHaNPB~B86YH zIw9gNDz9H+n0&}J-c0V{E(`My-2Nkt0NBY-PjL5r*s48D&j)h7pIpJUb+0ol1F*~` zp1!}vw0*&IA^z*SXZ}pIG9;ySrW01 zpU6d%LB2t@(;)LD!*G(DXK-!R!}Bp1mKS>Uu`^#p z>~WR%dn&;>iuz9Pv3W7EPX~GtnCg$63a-#A$1B7q;ZqH{xws^Pf-V1eO|D zHXE9qC~c)%CS>n>jc?m)ux2hN2UpKIU2hP(X}`Ljjc|CDFH%asVJH&6j5&Rb6aaVeQvSt z6VIX1X(pXAmxL>}wO&QIImzI9LcFhECJ|Mzi1FWhCgS$=^!!D3^vyEEY0HM0>?fsv zz1W(i8*H{v9APY$IW@J9NQ06Y@g$&STTrPC$I1{t0ptDZ=rHjEZnN2BSw{(Pn+6KD zRZ-hjn-KgzRa=ZoUs=W0cAc-}66Rmi)kZgub$G6zPQn>fM&}9X6!J^UsbVFdewj#M zt5erf{g$1$WV`h=0<2Y%iDK|HwH6hSu-8LDPknW`jl$UfmI_z9=GkC(@A$oVsRFl` zMYdksp797E2vzaH-N_%;t@q4}Z;FxZ(y&6&(#;_uzaGV+M%CB= zVNRMN3tj1#%##v%wdYNDfy0)|Q$>JYJ8-6o*K4hcC(;5F=_Mn-l)y@UX$ zt$YU7Q%o3cqwRC6;{vbL1No%d&)=)2$$;SD9a-=PfFh$6P1;*I*d z?C_52JLp$(UF}SCxJXTY+9?uE`@f35}k=i`#4Rk6e@*KDc^(tnQcw(jY^fcG z2hqo(q%7)o0YkX;lCq$o6hgCi3n%i#6vZ7x&_k#aW{QnPk2CWm8yVytzz-Xd_05x& zK3Vo>SFs-R)cf&`{&tL=xJVe`-HvE7&mAL^uj`W z%$d@~HtC6RV)R6}b6PqR$Pa7R8c3d_D4Hqq2NfG(>kTi!rOp%>Lc~n3!5mddW>>pR zt8tmTCxnr(Xk6g2^MqN08AmxcFLP;APA}^V80R_+K#agUx(RR48L2ZQej@XRm?OF3 z&jyIH+L2f<&wdR}X$XB~;2tBIf^AThY(zLA4*i6@9FdbT!Xy~7Ywt-zdi=wCIRuOL z73^T>|0wMU6&500dh%`EqjoMKS;Z+_5iFfnaLNy+B-@vyNWRdcmRaaBUdtQvT_Q17 zTG$aE4SA0iRA}+d@r;k~BwsTn@=r*;LgW8Q~>>Y9oke1Rm(xx!gv){TQFv|25IK_jjLj z_mxH%0-WoyI`)361H|?QVmz7;GfF~EKrTLxMMI`-GF&@Hdq@W!)mBLYniN*qL^iti)BMVHlCJ}6zkOoinJYolUHu!*(WoxKrxmw=1b&YHkFD)8! zM;5~XMl=~kcaLx%$51-XsJ|ZRi6_Vf{D(Kj(u!%R1@wR#`p!%eut#IkZ5eam1QVDF zeNm0!33OmxQ-rjGle>qhyZSvRfes@dC-*e=DD1-j%<$^~4@~AX+5w^Fr{RWL>EbUCcyC%19 z80kOZqZF0@@NNNxjXGN=X>Rfr=1-1OqLD8_LYcQ)$D0 zV4WKz{1eB#jUTU&+IVkxw9Vyx)#iM-{jY_uPY4CEH31MFZZ~+5I%9#6yIyZ(4^4b7 zd{2DvP>-bt9Zlo!MXFM`^@N?@*lM^n=7fmew%Uyz9numNyV{-J;~}``lz9~V9iX8` z1DJAS$ejyK(rPP!r43N(R`R%ay*Te2|MStOXlu&Na7^P-<-+VzRB!bKslVU1OQf;{WQ`}Nd5KDyDEr#7tB zKtpT2-pRh5N~}mdm+@1$<>dYcykdY94tDg4K3xZc?hfwps&VU*3x3>0ejY84MrKTz zQ{<&^lPi{*BCN1_IJ9e@#jCL4n*C;8Tt?+Z>1o$dPh;zywNm4zZ1UtJ&GccwZJcU+H_f@wLdeXfw(8tbE1{K>*X1 ze|9e`K}`)B-$3R$3=j~{{~fvi8H)b}WB$K`vRX}B{oC8@Q;vD8m+>zOv_w97-C}Uj zptN+8q@q-LOlVX|;3^J}OeiCg+1@1BuKe?*R`;8het}DM`|J7FjbK{KPdR!d6w7gD zO|GN!pO4!|Ja2BdXFKwKz}M{Eij2`urapNFP7&kZ!q)E5`811 z_Xf}teCb0lglZkv5g>#=E`*vPgFJd8W}fRPjC0QX=#7PkG2!}>Ei<<9g7{H%jpH%S zJNstSm;lCYoh_D}h>cSujzZYlE0NZj#!l_S$(^EB6S*%@gGHuW z<5$tex}v$HdO|{DmAY=PLn(L+V+MbIN)>nEdB)ISqMDSL{2W?aqO72SCCq${V`~Ze z#PFWr7?X~=08GVa5;MFqMPt$8e*-l$h* zw=_VR1PeIc$LXTeIf3X3_-JoIXLftZMg?JDcnctMTH0aJ`DvU{k}B1JrU(TEqa_F zPLhu~YI`*APCk%*IhBESX!*CLEKTI9vSD9IXLof$a4mLTe?Vowa0cRAGP!J;D)JC( z@n)MB^41Iari`eok4q+2rg;mKqmb)1b@CJ3gf$t{z;o0q4BPVPz_N!Zk0p~iR_&9f ztG4r5U0Fq~2siVlw3h6YEBh_KpiMbas0wAX_B{@z&V@{(7jze4fqf#OP(qSuE|aca zaMu)GD18I+Lq0`_7yC7Vbd44}0`E=pyfUq3poQ-ajw^kZ+BT=gnh{h>him533v+o7 zuI18YU5ZPG>90kTxI(#aFOh~_37&3NK|h?(K7M8_22UIYl$5*-E7X9K++N?J5X3@O z2ym8Yrt5Zekk;S{f3llyqQi)F-ZAq;PkePNF=?`k(ibbbYq)OsFBkC7^H7nb6&bhDx~F#muc#-a(ymv|)2@4)NQw!cgZ|NLJ@N6o#y!T* zi0kdtK#GC8e7m#SA9pSuiE5bOKs^ox%=l6KBL?8Rl;8R~V>7UCaz+Y_hEOZ^fT}$m{$;GJt9$l$m3ax6_ro{OH@r z8LmGIt2C9tM6fNUD<(Y1Q8w(aN2t@VPrjc;dLp9756VNLt9&>pX!L*6kyU=uui9e7 zrQ^&h7Nuk|fa1WH?@{DNg}C&i2BPX$%)+AMi%-ImT2Q_QnRV)3UbO2JW7T-JYoYnU!(}tii1LAN|D(%7cL@IEI0mCT0!t|kd)1KahVC2K z|9L76JA1F#-=|{!eJcN|r2bI={kK#3M*^rokSGIa zWe@gc$gT&!Q!WYqGHNy3PlhBvcjf&X0o_R>a?DGQ`e|uWa)>YuWk(ibM6r_Xpiaq4 zWtcFh6k&ih==f(%+T$`L1EYJ^CeevsviNKGK3iUF&1QI!EZOR4y2d?z{kh!@hfoR4 zR$n!oTq-{w^eSf-ckrX)rp`@DG4(8%e{AtoKlwoHjNIX8hY>P;3y*y_O8XZ8ien=J zQR{%EX3|XA79>Al$+8(rw$Y~9ydiaH!@*{;*H_Weng(B+tJe^@Hh~lm^J?rL_`0$g z%o51AI)M5AP4)R##rWU8U-|zQ>N#rK?x?C*TS+B3tQmUYjh6X32PBq4xJ`|D)tg%M zLwd8z7?Ds5CNhvE8H^bY$XD*~ke$yZo!3P40jio4f0GcqUohXX>C;+gOt>>PizdRd z?{b{G8+tZA!Aj6GmXFD*thAzMDL!h{90}jI=PdjS093DQi3v@l|5~^hKrwR6 zeUbcTjhPDLUg*ao;c>8JN}wB>MOIE^vN22t5147OVW>!BTDvz4xeP$B({i(Po~_BL z9*#5s@;l~%7S3?WkF0}E8>iN+UQZh{-D}3F##`x$+YG@H0vyyD%vY!zsJHcnGrN|& z;j<&E%0i6kwaMT{tjp$m5^V4*+9;13^DDjgaFvvOe3=j2hWU3(PY)kFXvfx#EJF(V zM!l@%;xJuF3pERftbWw~WnR$A&ok4UQ0dISRjNi-j7>!WdGm0^FUmns_uy2DYX1!< zihag3z-a%BI*WE?er9_UTY_Eui-R>cvS1;=N#Bv{mPKKIv5O9iXS- z3|WAAOhFjGB1il&5F9vj6Vm!t99VnZ6v)$mKW$!I)_=41msTtDQ`CAV`azZw#(aSt z5XK052F(2mTOy|hb~KaAM@(Gg9l3=rqXB79Zp!Q>)*)Hhm(8O3s53@BCx_ltYRV=o ztb3!SE4UlbZadeiDcr2NZnT1}MNd0Au}VRHKQ!`nW(2!sPW5ulYI zosR$tFs@ul-q2)^z}}Y;3$Jj4J#kik5ou3xxf)_JL$5C!E%MDFH5fza9unrHXXw5F zHY#AcZSU73&;sy;y;fM_*p0Txd{DmQVYSyT(8Bu@vSLZAPKlVDd&6%bHj%HaV1{=L z91uK99)#H)!*Q6S`Dv))pyUoDkMa0Sllw7Fvb!iKKjbR3>q-@zp>$lcNLt4(&F9yk z!g!~88ulk{z2xgG-3{{il~#8wah-S$PDsv)h$4v?e@iEW{%JRU21>lL%fw8~(DT#^ zywKIPee|O;<3lWQL$hEWAUeA2)~-xA7yV(I(Pe55DMTFD&6fP6bS3JXHE& ze2nS2pMh>pdB%}#XYcS*N|SMQmQ2J&7WZu72OP zj&wXEJHG2^_XZLJUco>yC|q(0L~1fPN+}|}7%$xcp-i$$kXV=D`~$(T`2Y)+8U2yu zvr%Mzd~RzcUfF#X_+uh&RV1fO9P&C;yFTuW5sb%e_xPYEB%AgtaOJ(ztnLEW_Hao2 zZHV-;f-^2epH zxn#@~NOA z11ZBV6tw5T5>Iz^Jb)0%OIlra;qJl^ufG156Ui{A2$qpZ_{^c1^R`+fbi*WT%;He@ zyieltZ{6ivdgz6i=@iEldc;jVS!5E5$rymBrD?v#K?Mr`?ocG-n&lL`@;sMYaM2m6 z)Tt641KSaR_(MIZi0J-0r(53x)8LPvfBwp-{yFxkKiTU)pdB)FGjC~7AfTS_$=v_Y z*Z#MJ`R|V^X!eb+h*>&0yC}OF{rl;vioX)<^+YRtY&IVpwZx%m(G%kbE0AM%G$dMnxO@9U~x`$qY-b?f@fkQ`9pNJeiFRud6ZB~-h_kWX>mCgONAn%y8FDS z1jJ5f3AGpr111cNW(=njoJxN_XIF;t1dO^e0km*ZO?76yVM(*B>Ix?cT=nC+o2XP$ zo!&hK$H9sd8H07(XoY2&7QG(*iL;qrs4U*82`MFg4P0Dzw%rEFXuGLBslk;D|Cf}sL{Bdj9TpChAGEEN*DvCLV(j_N-e zcLNc98=ZJ>3?UluoPSL2QwygpEHOrNp?KEVT77e1i3zzY%Y9lStpis{$m zm(cz{%HDxH)4xj^O$Qy@?AW%`NjkP|cWgVkW81cE+qP}nZ)X0p&N}nVoOeCvGhF+3 z?b@|#SADRMCTILsR4>rrHy4AU0PJ{|)~M^(@q-e3hLdj7_}OdzCb7?6jvhyQy!)3Gv3ELg)6!VjwA<}NC@GK%{NI0 zJT}T#aRk{>TXHs_T?t5eRw>v2ntXC6^p*jkWo`a)WZ0?8&JFWArnx^e@#->FsW0`H zaG;x(iE*;8ugY6Nhw%)c!hpKUyX3jhGA*i6J6@(fUBPL$z{4dz!^d6OL#hN?41I+g z!KjR5!+yZ+z+Y#U0p;s{fV{jmnQyy>%`Eu5GUWo&fsZL97=D~-b_O#00NQ+zO>XS` z6cn1v6jGixMb@=ItgwK*pbiAms3``uBok32wSnIF!(VPSH!Aca2(cTt_k_R zo!iTIMT0nvu%dfM`Tm^UEy_oqiKOy5hANU5*kqB?bbwBoz>e&)X{#5b+bFeY#FB}p zj#JFe|1ix8(itqE%U8Oe9{8p+lmPB#ITX?HhA~WU^`aMeLagZ?{J#$k1(<*Ga=!-# z(r?kozXS&T@4ut}e53yWT>JmB5K8z*I`ZXC(_u$bUyRSI0_sa;;}c3a_~)8{7*#4- z*hR0l-h`v$GUX!Y8S$OAGx`t7Oh5c~5aXowl-+DBh(YT4|& zz2Q~Iz2(b(#FdLc$(X>h-N-=%K&sS{-j3KfIshl~vZ(yd@zZNg`=RANO&IW5GfVZE zs6mU)V!n_RSxggdO;6lhUb4T6hUvzQ$bXz{bZkC4QCxql0E>+~jH^F@J~OC%bQSnw z!dVcM*I_fSE>Yp7Ty9TQ8VjoGh>2rpcziKFwP#ZBOnF7Eb+fb#57*n=S;keHfwc zH49H*3q*cDponQrD`v$M1l5b=n=zY6HiA!3d-3ZhDZ+LzKN9kDW#xrc^yy*`$5>{c zL~=_5`{q}NdlgOp5;!td)>hv&2umQuUJip0G-qJ0O^3tqXGdqmn}Z9DTz4j33Oh6* zRs?8e!2wbIsGfGP{9#WZD|RF{E86KJLEy$vz9KuntCBzNS(>A~j5a$SlK;1USU4_S zB~S;>^=U+8Kqh5?r+Nbfvr>prvVolf25hJ>p9%wx5ew2uyC4l%vXv}jkoT5T@NOml z^@+(g=Fks#f9@XKR3CWI`oEWac$gIO`*&M%ga!iQ{=d%2|J9ZRjEt@AzT>j~_r7Ge zrikzvS+U<-JIh%phK;}dvq;P%#NIq@*-Ro zG795&jLHtK3kt@gsFnVb^geyY&Q#0!O5NK<5l`92U6zg)2z^ixqqM;dD69k{pn5na zjzCXM7%i#qTM&x#D|7;Cs8qI%RB+HS5}ROsznNr@l{c2b$1$=!oSc;%3db4qHN!gG z%>$rEZM~8pIiTEB<|bT*mBLb{tT1uWu6OFJ)KF7(hj^P2rs5QyMx#q_*|BJuoXwJv zyh%!-X{q#YM`heA8Hj!57>5|U9qR_sVak1r z2ZH_d(s!DNqIuDZc5gkw(w^h@n7~LZ82aCz6|aG^n5bXeTCFdW z7m@2Ej5B%8MSD2HAr*BPh~b^9^;NJ~HXJJX7VeGl(#=!DS?r0mNIH^}d}=~&Ui+B^ z_wm)B4@6oIZ9FP|3#qxxW6-_;>b*pN_iexjXi=h}e`(krgGC?N9fbTnyYPYIO6K}B zFA_P-suUrOEb6b`R1i9SkQ*s2Jb7^Y-tOTodB9(}j@~WUg#QJE`jW#~0+;?p-Oyv- zf|?tPS8>)50*6Qh^}EqVu&_nQ+F^C-IvX6tCg-UDYg3UXsv^pjsXxyJD>pVkh$z=?hWh9Cyd8bJRGUUU{A@XK zEFVF%XrUA0yYJ(VcELR{+rh(`Av6SI^lRD?z)AQ$gLvakWpQF`_zp{aqZKUt@U1H2uD*qV*seS(QQ2Dy-oc-O8X zMKUd~h#|T^-6H}`fk?iJx;2kI2$Jj;QIf6%C{vhRVjqTvaHy7Wq*g(r%|c-3w(n|C zr9N;Rs9JfUDeCWJFL}uP;Y0FDf(Wy};!IZ2zFjeU(d+_6MEJlaX*p=3D!D0b>op*k zuYr23N1W0wly8w74c#W1LpXP|?)nWr(3eXs$E(c&PiERe!JWE^z0mm5cg@7F`_!@X za8nQpF$jOM+JDY~nb?BoW=-xIQ22c3TFS?M{R<~rPg$le_1#FXz85*d|IS}UP|x1z z+ey;M%HGW3JB?4_`{vKeW ztvEN4bJui=CcnsQr$FVybke#RDpaIHY{GaczId-A9x@ zD;Gi-lJ9Iau-2o;`eV1*3ztzN3!P`Jxrc)3ocRRAct^jD5E<^lS-Z2}IFL)oUQ<%h z4?B_#BP>07`M}`7ywGkk}UQpFIOvRZx*v_~StXIsHv% zk|F{D@%%dlD`92rZ1oTF`=>D~IOsVT{euA~R8PKHPL!_>)`|SN9}+Q?LbiX7V;y|` zxRlL>%Ik$H(5Pr(Mxx>JnH-I0{je|Ff^ zz-BM|Nl%;W&QA{{-tTu0O+e~5f#GiJBzZraC7MNqDOlr?|LhqN(b;MvwI7GKiU~0K z{eT373oTRU0c$+Rhw4@XlTr&~#ma@bzsx0Wj}{NwfD$q4FH;&|U+$&78LfwdW8CyW z;OP%PLaqA+xw`)8&GY!c(BaeeC9Brzjgx$h5BNTOB+6D5tkg^CsI*KLgPcM%ya0vp zbV@C>a?WQSn!)u=q#cuPB(|i9nbp{($Sdf>!kHiclcaabX4aUu7DhI!LxJ!}0zu6Q zTOuR4jCzAp4HQB~$lx0-I*OxW?+7`C+)yPz2LhTJcEWDtrjrKPGYcx7JOz5>Fq1BbCwdcc~)V(_dWb^W^Cg+d`E znHou4u_BxEZ#{w1)X2Kp1f&31bB$h<4(gDTg@SKrHdbYIH!LCpjoWx$m6H?^Rn_?n zQtIMb-Te>usVOR~oBNm|$%EuM-Al$LI7T(caHlUC_)EwIwb_}nTuQcJOCTkj73b`fRMv9KQcH|un^M#jXkC}A*2{;)>XL4t%9j;TE~jj=;kQxkt|4?2+jG$ zO>MA4Ihwb3fs%0QJ?(xri>|+HFKQwe~VKVDLRp+kcn%p&_N|cAcOg@pMI36hxJ}`pdX&g37 z;cjX3*$bO0ZP)WGjS+*#9BPg-k|%%ld(u(z6#Rs)CdDq3v`;~(3yzuCIThvMSR?)N8k)5*zG&`Z5~4mo5!kDs8X%#wWG=BAOu>f;BBx)i={ZF2%pg&8u9OHu$RwHWi(Zrnb_F!S4}H4Pemup{B?g&x zU#uE<^xzLw!p;7LfV$qJaB~})?F?0goeb3_q^thbL^rZUwm(m}&9u{(G_k#^JTnZ# z?ls#Ol&@v+(`?BLI#?e_JDXMXZ{(A&w5)*9@rU$xbIzoJK{+Kq$9~gGf?d^9H95ge z9~bmk_TQ;pQR=n`mb-!up;6q>rJg5h&~DXGOL10ZCpZElV9+NXAe{ z(U{+>WGl-7n9_cB;esbv`zQd5PGDmtwrS6_?5O|j?f&4!=Swn)P&{DTRm#Q z?lZCaTsQRukADw>9hvymR@=x9j+`A^;gGe7opW<)l3(+nJ@lsz+RXHLf8DN7;}xZk z?qsC(lwIfrLNr`%cX`j&a39Sp*W&E5ABI{ZAa5xsdUx~eii8JeRZF~w%iTbC#CrAF z-f(##d2g%O_TH()d(?*AHm2=rhVJdR;EgIyP9gikuT_JX+bTqZK_f(F?2|1`kjc^R zBzDQ!BZWG%cOfa7HvQaL{Ub@Sf-hnaA$2DxLI5WNxlEM_Y{{$4dSJMYh7u9pnQdxV z4jn2yc%eOWUGmF0IvlC|>3K7RbP86le>*$oQf1o9Hu$U5W?FiyW4x15Ke~2{<~fNTN9&{nZ5ltn)|0&e(%8lU!5}Jn=P4>{Wc_V#@<*& z#iR_5lKis*QVSbHPz*U4gh7_7OW&h{zBrzGiDu1}dlO-OKldzv6xfgM1;iJBv)(xV zL*nOH>}C4e_pM>gMOIgr7fA9zY$T{1XY4SU7$v!*x(F28!b*5-sBQdSve9%p&6M3A zoF)u_&hxDVt(HQi+d30wc#%MI?O*#P7A-(aDiQVoVBc|#+G2bKX3W9;9o8 zD4HbHZV4&TIV&gj0z6v7AXq7b^MENIMn!!BR-tnjn>8c7k|S+hdv8|W%?0CbQ$7B2 z*nZ5BW(Fd9tQJwZVVWzfGE-5!b%f6Gtb7t<-@dIT#=TMz3ERX_;%e*+5i3(E=Fe|ao}{&(4(W{aQ4Aoc)ELdd z5xg&)DFQ19QdauMEM#(&`Aef|XP5yeP7=4gf8P)3_V6z`))+>cj3Zt1W8V+5k z6@?Vs07*I%!{dvD{3k3PvAAMT~6`Iim@M4XaO_%YOCvyx_aZ#OE zEoQCTV=MOnIy3QCDFvy%ko~6YBp3`2U{rdbr*BHVsIz1!_!-at!VxNhO7NC`mw*3v z`Ttu;@xSWcS?XvTO7%Eu&JIN?8S!yGelAjipZZjjL?kL>E`1=KPegVn$cd#Q3 zmrT=BIxi`@g_jH)Xa+_?g2hpyNK%m(2OB8!%k?+{0(O|w)+-aJ*9?afapdUc!Kzrs z{bs76WLj({R!@J8BMHvCo3*s0;2pzhzGX)r8;v!#bHTvh^<3+|+&~E$E|kdCik&Q* zvXm9N43@#(!o=hFvr%fQ&OT-!rqBw$jx?HZJdVPlcdD=K;SDr6uCWgM^>3>bYYyzD zw(m$e)>4rAZ2TKb((Vb1@C$)B zlGwcqUCU-rWbV8uqUIsl`VCcnOj-itFqI_2Vd=!Iq?jNi9x#_YHyx#bWu>p$(+<#3 zm8~w;gB*jg_f08pzm}{qhFqd*D)ma%t4`7=-7rq(#5?lpDE3t^qTn!nJd{~h0E~E- zRQR>Q81&d@rddwej@!YvrbA+RoMKfi;I-d?R$U8^y^k3xwU)Hbm+Y+5OD;`JOia_@ z@eFpvBey;1Twd9l*KHO!*;QK5)5hjZ6$t;DMfiE(0a6m5?s6M|m_vXC)Q4Fs9sn_y zI!or%?trl8Gt;p&}Jf;`yVHP@rsXhgAkueW}cmxLXHXddup{SVk z>^B@F*hxOnbBoJ8BbZ4}yNfh{NlUbMcb;7pL3x^mNLtFPzQXori=YGCNI{)ZAZ2Ki zs3qvR(7N>3nl%-R(nxn9g25ba>ww@!Zk2n&Ba}d16bhv_#ER1_5xYp4v>EZSD=SiN zawHYv%hwEpP%wK16R};MR@m~tu!hMb+v9EDkD&DX5wQI`eh`K1)O`&W>qHzi z!b-DJ&}vPMc~072@*LfJeLTEC`v}F87}68vWOcpLQ|U|l0V(wYixZ*=QHzP%b48F5 zDzkei^(!En6E0%9u}ZGpvth=98Ab7vbAkWtt0*l8ho~bKg&k)N)D{X)Sw;9K%Rymb9ZkXRbICW~F^rHlD@gHfrM)$z@z z$hD#^b4Oa|U>c*}O;;{gCD0tASCj@XM=^K~@*b&A(W9HhBW7}y*>zs`L6&b(Numk+ z?}W2dTTY-k=m`2Mn)4HUL~E6!TYM-44baeHe*R4+@g^O;S2E_999y!?b&i{oCw2p8XKj8~?@*s%WZ!JnBS*(vHBdP{u*jZ;&mPhgW- z$TymUXpLsqmETA3RIEm7PvM~#n2jc{hcz=P?u0)H3}EOmNcTzyZTDabzVJS};Lw~R z^_n%#OhfmE{M47|-{~Pe!$80aEMfivs=~;(cxH+gPUI*ZYK)Fs^CUuPfB%5wwKIf`Er>NFR$wv_^&lqkC2)JPA$tSp%^o25 zAg&XPxP;|y!~aPnY+-Z{-RB5sI)^EdId1W3Ryen*fIbqnZ*#ViWDj((OR4xJM)(;? z@Cf4i$TZxF!ziNG;)MR>mr=gWYsSqO1fHC|%#CXi%S_NF)#i?IVU?g9jGmIR0)3Bq z;tln(pGsuhYpC|QPZ-M*8&b?$?(Qip*nJ?akUU7FF0*UvGnI!R3f3ehEjPhPEH4?iI+hc$O*6CpeI~ z4Sg%6ZtDeiGX3M@Xb0VgXkGxN8nJgs*k=MrN#I7+%!m&e>Y)R!$GXr{Ox1#dMkdI= zlKCh%&BnMT;qlKbqHxO{`^lO_0%GE1Wrg?yydI<3s6he$-Lq$K9S~S3G^v4nX^Z) zB1xZCP}vgY{yApKcg{ysSWd~`b){kFXX{Ue7MRxdIp*Pn%tWiA;G zK}!DfOQSN$&ZWcr5-u-l7x|fv7&wHK*XJt#+uRJnB2FM~@^XCA<8EU7^5gaHgUsjK zVOWSyGNZpfk~vg>rhqFct7@kb;0^O2Xsel9!;mh_$I zaKvjBu*O_)8H>OOS4ydd6g-9Aa_$Ws${Ws6Fz0|USEkulnyRswYM|urnEWUey-5v< zK|YioRQPd{ip*!92N>e3y5>A+Nv3n4toNold<;@)Cpa-}o{A3jKdb?O!_ZABIy-wA ztzaL_l_MAt9Aem+gcuy}HD3IYtK{aB*hzTjXq&0A@uXRXv^;8|0?@Am=!pbiG=C5N zM)McoW~TRnVW3NZq1KJj+xK2C;;K|}6aa~;Hr(bM#K7Rt=}86*!4%lv7!SYq>1?b! zoj=E)44db=!=F?h3B5g#AL`+B*zeH*a^T`<+KZ^BuwjR)kT#^@EDMz<=4WrL{?JQL z(Midu5k`G6nx|MAl2Y&qGSM%%J)+Yw(FWm|z4fu4I z{{3wjNT2C$ql;!i*H5F{3gKU*q?bZrK0;+SlBwYIPElp%gqUQ} zu~PZr#qYvYE(y1#z$@vrcmgY2xRG0o>lUpzY=8Rxlo4QAjRJzT;NnCL<(mUbSdA4= ztVE89jFFMl`L#!Zg%3PXupV$V{iK<4bVwi2|NAg#!f#s}|6Tho-?jh$0}cQ0{CR|dmG3a^sq@LvxXZ)+3$dF}+2P(mIEWS<*7dvo6~{*oVgRl! zQj7D|**X2unoU|<->1K~fm%Nsb}uww1XK5 zPTkQf9B`IX6+xXBtW=vbHP=GNFEGLjjx=4n!T8k>P0Dxgg)8?1odzkeL#&YQ#Ot0b z=PB19V^dl>CF9vFxxuNE`{qHrf083@(u~2?E+QAb|ND4Ak^;V`^p(&%y!)wtA0#DI~1sjPy=Gl=Jk_LKV+s!Y^j?t@%~H!tX2)H zm{hZ!i~RL`v`e690}D)}3FD}V(vmxXyhY%K5Guq{_Mv9?v2lT{bOWg4Zu^7y1ar8n zmAHd)JADf~14}K&Kd>r_R}_x(PBD?%GkD@IDUklYfy|?y1BVdi#9312{)remsr!-H zjW0tu#v*ygyWbLt^s5_5MkpYWOUgiCwk>cCafD`_APTvKBz%WJjzlS-G2A*dS)qkQzz504s~eJE&!(*U_>0mr$HykbwGNoNWwCEjL=c7M*D!Nb`PH zx2NPxryn>XZ%|N7#-LQKLHw1-kG_2=QJ2=JLW=C*nydd_?z&Q5N}%86-u%7SV*Gb- z@Bf(i5)`(qXJx-{k|yJdb?lP{@*FHb*?$CWe>MafB>S6?GqJ~&cUG(*a1pK4j zcf{!2#D*VPQ_jByclkm!s~C_7tTThdil^s=WdwIgp0IA$=lH>9hCTx z5Xr)>@*R|x(DjaQ$DHV74NS`Whn+KWt~fSy84>OBxriMf6kUU4Q-kS1l88`oJ;U37 zBQ0WgFx`l;cSai&{i2YGMjA#*3na}+e^znG8aHDsy4bZf z{#LURLOT3~vp8(Iz0R{4 z(_8XLA)?)amfcWVTsCQ-sSBOwSm)13fLBY`sl!Db%2|ifT=q zA}^pepW;deI;)PQ&|m^3N#3nC$*tDKC&*TfWst8|sxfW&I?b{?nN`JNk9Ca(mhRwR z;e*YDD(uF0O__g-j`;qano_bd|GzAsI+Vubzr}$(&aq;>^uHkxZUTeJ#UKKb;6ZDm zXJ;v)Dg@N3+lUox9T)|rNJr_O>1gvqMG~O-x)ZQ{39k$k* zrcOGGtVyrDyF9^lp_*9wqZg(DHLU6pbt5$?+x}t^@`ZWLSOY9S8qUS0f_DMG--u2U zVVx5|fL}q@Sl3A;632wqbUjvV!&-8wpc7-pG>olAC=&9uR9P+aLa{6Tryv9JHBdyU z`QqpdCu5x$noe5^wes^G-+w6U9@E!NDHQLKi5hO!OIh=Gi{cttNKdQZov`>`$0}qW zwz3-)$gk3`583rGJ_}20tDDcVxc&m|+f<1AbLy?n*OZa;*e5mRaNf1g%?~}~d-9qg z)YnEg7G_l=&u9@fFIBKaalRbC<3=@@*feY>lRsNADQ15TvdRTJZ<)eCYVPqzdL=Ef zN5(>Vd%-(d`|e!KyLWUEG);_E!J-fhAOl=zUcrgVX1&hj`Zz+wvF9Oz%X4gGuONcH z%h?(;os*+5gzz&rd5$4ULvA`P^W&(9fPMjG4QPG?KhaXi@O6O|U0j#gaaIq8)g2TV zw^p{f?V!a@N*#6eiN&o9wm34rAKw#f?N|a+zzc!gN;w?_aaFF$hD3`u9UipKy2=a?eobQF_M*REf$ zj;+{$jx7^GXy!mmwnHMf3B}G*11Dl+ur+U$HV>=|*rWme??d4H)D^+~34-e<&T4fK z9ektGZMEA`+wEVx>}pcQ8=?b3U&4M_&cEw^b7&G~t`IahA*>38X=Dd9PK+d+v5AchxFfgIsaho z3^g-d&4HLt@zfMHx9?onm0BKMiye@&M25!d0|j0nObOP+ni%+TRkv7Sys6+6#71_3 z=3c}|gh*XvU|-!JP`?&KXx|m7=3b=XOQhwATD=v29v@f&3!tGPuaC{Nnek)Hkat;U z8D}L&CC7!O1(_;b_eTUDwOd6z&YPOQpDHX}OEqX&rqBLxbi6Y+6raWRuS~FCMLRMt z&#=5pIeXB!uFvv)dfz7vM;+QgV~i`G1D= z-T1{F=Svc>DCY7thwMnMEmQWBpxlHg7sL~EN*8FEl-J$-QY%K%J<1cYy3$KV zG+EM%8p|KXJPMwGyQmer(9LR9MVP?GkZ=w}PhCJq%Z)LsM&!Gw6`W|6YLt|VXVknn zG+d8xv`&o*XpcrIyO?E>GlQ59W6fo)hgdm&!us+gk&~Z(xzd@ocd|b&VXN{1iqTsr*tppm%|xZev}kgETo?Ip)PrPEKQ`fJY27Z?+iQ zPb+`K9I8RYFXR$~Ml+_RwfhqjPI$G<^2eQukio^mMUAfca=8^`P$}-3av))0#reBX zJO?KRoQN}PfKy6EWE<${E5oA4psTIXI5R3P!`afUEO#@F#cW6?SdJ)pjcBxn{HXms zby#DnxcBA!a)&`0rbZD2SYTN$P0#hKE_J>aS6t>Fk>J=OkHFT(x{~rHi3m`WL<=kn zYqLhsunHC_IFkJ)nD=}RTK!-#DyN3zk?9q}WQ|y1rKvmlPWbjHi7UlXup~E2|PJyPAGVueL7){V%z~!0G zXAH|iVbtT<`S2``Tz}5WNHpQkL-$|7{gJQRQ z{~K-@lS>`6>%9heUPf-y_RL%GwF=+XQ~OK*X5E^AVS9Hz$Yi?j*y$}A5lRJRSrKl( z3QcA!z)W=;sR?}0Mz~&?X z!oKp_GaPNka5j@l=_W8i_Ofa*C=4c}Wn{Tg&f#Kv>KXE-R$KfXiUCcU6VXc% z=8i?pTr4YAqN+|9NHN6(T6PSGByZO+A&`CaMYXfh0S?fVLF)`1*NWI$0?QTU>kd1; zGzWn5_-2B({Gn)x14cpGBq|78lCZr3xPjhMM!`-370O&|EV~3vDVO@igfR9m|9LnF``CmprMnO!UW=7QAFV7bZS z&97u9G63r&&SVh|)l9V;7LLGCY8;X~D^VDNon%jj$@1u7VD2c4OvIF-u>sc%Ihq#3{;M1c1{1p*hfy2MCQDBv0zVR>fl{I|lfOf;-g+=$^M zq0Rs#+yN#^6GhBtw92LZA^WH9cMTdqHT|aKv9`5>skD<(_o8oU-&XLEN{BSkLfhlzuyX9QH{N}qaK6~?EU{Kz zFf*F$WS+nvgybofAOzsSJB2OZAEG_m7vlWn+^D;_jaN7gg(HGtYw~px zw}w`idAI|sf^=i2^*GKT7v~wW-*+2JZJYOB6^uJwuw86RE7aIFD9F(*S)1|L=(x*R zBloIwb9(ht1|YF%8f9femH5?zGAQAwWo zyqo4TV2R=B`U<5m8wAeMHEHpWnOW5wp)I$xr(kkl)R;Oi0isun=y}c-l7LZ7m;lm$ z$q4Iy6Sc&$7dUfcx*n3=`*`*UR zN1JtLOUYS-=7UaFQks;9^B@e^CN+Pz{Jd$gh_F`j>;ZkK-Md1}-@#73aDFjIwBy*d zTlwKK`nqGu3$(>F?Ap8A?q4y9mka`bxGNnAlZNNKWA&(V)8YwF5nmp7j%ul`_QG%4 zaeXBNd7~ytMg3#Xf>6W<>tYbEa%-$6=;P^Sh>aUHZ+e~0RG)Xi3%`rEs8MS8uYqwNdw4SWVkOjZaf` zG5VfUUiPoOG}N6 z<{qp@h!mly6=>7I?*}czyF3Y!CUIt=0}iD^XE&VrDA?Dp@(yuX{qsEJgb&Q}SNvXl zg?HrA?!MH-r4JN!Af3G9!#Qn(6l%OCA`)Ef2g8*M)Z!C4?WMK9NKh2jRTsnTgfut9 zpcZ7xAHd%`iq|80efZ31m3pN9wwBIl#Hqv=X)1r?($L>(#BR+)^)pSgbo+7#q<^S1nr$1&0=q$@M&POX?y?3L&3X z!%^Atu025LgEZ~|-)Cd0=o8K9A{$sT;SHj3M?l{!Er;st5w=T=K2^hJ<$(>&P!j2m zy3~(Qm?r5vh*EGKNLnP31{fhbiIU~c2GX_wqmM}ik7)NF$bEYKH^bK?MD+uJ24Qa=6~Fg-o!gSX*ZYoo{fzTLs$371<;7oLD|PiS3s zz;aIW1HVCV2r*#r`V-0hw_!s4!G4R|L@`u_;)KA?o(p8@$&bkWXV*taO%NC3k? zok=*KA5vswZe|5QOQd*4kD7Db^c|__5C;&|S5MvKdkPtu)vo}DGqDpc097%52V*z( zXp%Esq4?Rzj53SE6hKu;Xc!&LMZPPIj;O-Gnpq&!&u5db7Xi z64ox137#@4w5it68EPn<8RO48KG_2>?+Aa}Qo7fR%&wXJNf2J;Kwm6Opddsyx$gY# zU+b%y*{cBju|sw!wOcY_sMFWX9(C02d(;_YQh1*sH9?j$%`tKJyd(j0PtK#D+KLHI zL;b*n{CZ7IBb}MUGdG3l2vFGJn3TOYJD$Hz2OOy*%!5a{!!0mvok+e+N zaP?Ndm;SO(8-v%yvu#Rr;qFSgZrKJxV^uEnX@L(r4)dZeyh@yRqoi@3M|#Hz`hHN6 zA|8#&oFv8+1F8t(#j1%Ywdn%N2uREt;@bFAF}2zeI2KE&uZr$?-SIwKu<5ThXn_}f z`@RRcJ!3;pKi>mQe)VU5;c)zA@b#dd(J?}$sg0K5L^fIm8%TV4|>Q?qdfMwAh4AM8l8J|tiSF32B4q`!TYj_z!4Lowq99lipY?vlC zJssf0Vy+@In|fg`2sUl$wDGr$XY+4g*%PhDjM^G!Z{H44gwY-ymOqXka)G3ulfWdY ztNvx4oW*}=5^&NGhiS)Vzwb4;K`^*tjj8h$esujKb7&}?V_cU5kQElGgCL<358O^% zcT-EwP>hqb1%_8C_5R4e#7RH zp@tA$bVGG}q@TDR#-_^YT6}Zo5~p_5P%C_pRxwhgkor!;FtNFF#cncoEHm=#?xtY0 z1dHK{(;)5CQJ`0upxdRV?(5PH{JISW%d+@v8FmbTh9n5TXGnM`Cs}{(AbDxaIg&O2 zg<~{fKtj#r91u9PujPqhkFt7tid?IZ={dML<$3sh;A*Hw=VP++12;lVguAyio!na#kaYeX{|8h3_;g*K=UEf zU*{ZR($$Bw*(h;CSO4{alBraU^)52&nxLKUxg=1N5MCBUJ+3a^`9#f?7=4#`&oz?k zoz-#s4C)f8Uk@S*VF!Uc>X}9M`_*gkn0&GI2R*j zUlHUy5b;rLro3?bBLIt%dRd~2lT@kjcfY~OL5ZmTl)ExZyt!)^K#1p>U~rdclk``e z>=zHu6Qp^z%nX2U*RE14f{$U0*Cf)LfBz-c)t%iD%3wxsgHpRPvieqZgEC0IX_Vkd zxh27*KXpXxYD=^PP&EtX{NlX zC%v9)Wz6De((qH}Jqg-g`mwJ!IZ^L?eE2PE9@#9U0T>jD%e^K8-Phz7cZ-bP zU%h91CvGtNYmE{gk=tex+96fK^!I7P7YI3Ma}h)ty%NEN zn}d&kVV1DM4tPht`B!poikUOE396Uy+VE|E*eQuq zoT8M0M&bcREYOX7Q)F5+d!xec;2;H!WO+!r;v#uo402OEt*q%vj)mC@8wg}HO02G( zYG=<5*Vgl3R(5)N@{y+rvBY9CgUHeN`qQLm*3;$@Ez|2z2j3@V_m6j4Kc{5MTf}GG zMS_qp%5n(5$y|Ke#!!7w$4KKAJmhA@sJLcoS}Mv+l^X$2DS9H)ezLP0LfVpNMIPwL2U@Y%%7Q7jPXmGSPlRwa7*y~EkqObIDtyFm)q z-D~m~?At^+db`FvO2uEi2FuK@`RaSN*`T%G!}yA5f-hG1SYtty+Q}}`O^In~cgi>l z=zXVDDNVH?QHtgup3*d46+OEicA^)pIn2`}B}8}{g`msSbzzvq5zHCIjU>OrtmbrG zU26iOxr*A6%_LC(|3nH@ef$16q%glnTl}ob+(w=A9Uk48Pe(F^%ktv(oHC2Ve4|TE zc6J5le1ZqXdLP~+(UY@`Y?r~{B6_Alh8Q{OmhufQSf94*GFtAi(lV<=!6wqxL;jck zOnpR+=HK3Nh}Vv}%LXPzn;0b#^5Afk3y&G)X}NEkE`~TM%tU-P1@^=msCxOyP!IRO zBegW5wZ@10CM!9*_|kF~ZSxrk>r^zyCL|dy9$~*`OX?>1)fL1l(|lW|G!``CEq!N$ zMM)W~G2zDb6wA#)D5OmIMu_&UH_5B%DJ#NKl#R!?QVz>y5jLrK(-JpI6LIGVyD%W9 zg+7;cE40;Rcv9 zkCrUgZ-H}IaC=aY8~7*9+Ny?O=Ep;yso*#-SesEGSa3T&e&DQ`k!p#Zgb<6@KRjgn zG+Z?LoNstww}#+R`Y(?d>>GG^ncorkoKX@REYSTD zQTYHMwNiE~9MM(>u%!3KVR=O=by_thqeFR&Bm;D|lW@>^unOrb^k9yd-=S2LH0S7} z>ae^bwruKEB*7m=)u$5MIo(`)Y+RR5o>9(DDDV623UMVck1##|b`7H%yjK9unoDGkVIKrG*dvN;2S3P_9>ckR6c?7n{s5v!i;dE&<_aDaPA_ zi>Z&SHW^bWYJr-2sb7{WC|0k-a}7>k3)*YgZora(7dVnK7b6?Y7U|>t*u=-aLgC3` zvnz>+QQ_%r^ePEJA5X6^`Ey@^#{dDW(QZr*A_L9Y+QI4?xFXAQ-JDe?&YmeAVN{2b zK0DO+&S-fQWDg`ab0$mQodAEemrA3p{cHbqx{yVqz5Ns6)Rixse^k(i5spvs@22QF zAhsD~>)rC%n(#M+D1!s?DFCBTRfNF~`N7kC8by+1samiHH9dbid%Masz0;p`l^GuF z)taCc0FD9!#^qP3B`G>vZA2db%ma*@6WNWW{*kPq^|f^R%Ee|F-FM69H)u|#Qt{qt zoi{%@b&~<}!vBf99Ef=ih~RNSh2LT6zvdLf+KCi=hu6#d5v7kpppM&Z;F3;`{0FxW z@#nY=LnIjx1?~XD?48~y)>Y&odjWF%6G64~A_3<{rx6>R zqF2ozPyJzzmcF+3AQwJQ@C?KEo|5k3xP%;^ZN*zpQBm5ho(*e)*zn8NzzzG6V?5V0 z2<7tkys|TInay6or7^K(y0ZdwJz|6$blXL}SX7s2es~5{gYwS3d>6k|3V9vz-#G3! zh@|-B?^JP~seJrS$&XAfp`RknZ!pFw@e!a9WgKijDz3K#6@`ifTCWHTa}Tr}n!~;0 zh0~X4_sEKGZZ^}8+X9!T7NazNv{%@nJgpJ8M;Oa zaYo_2Qbk6_j7W15!`+XKC!`+_)IGZ>r6X=buKUkQ*5wXs5}A2D@eYvF0{q(=wm znxEYB{>rdO75{|gy2>`^UB!(y+9acVVRieAMG@Lhf)g>yr+Ccgf8oy1qUO@L$n8@A z;nKV>muW=<*rD@Su=A?nhxTpx>?1>jYOk(ytb|TNwq8q1{;WERaWZi0ov0xFjiIm} z)PkKhn`#2CSuR?p?4)9Vk#`#oL)#q8!B*j3s+x*6kQ~2Pog{K^{k(=xfv{IP9MecW zCB_bMVE;HQS12k5L;tHHjhJ8m%07IN<1N(vQCG+8IilmMo{g$Y5nrPhSx`OH03*55 z;^!ZP!KR|h3~K&8O?uAqKie(}FOYVMt}S-M;FF6%#pX@C<8P!jbk&G&a^_Oj+^2Ys z*1tnnx4eOpd*hgE$xD+(iTw1TaGNs=4*;Pf#P`fd%_%)Jk|eeooma)pR9ka)Ek(PX zq2N$R8sio=D*TQ0BaO+M*8wF-0cR8Bq6vZjr?NAFhjQ!V_)x?Yxmhd9T8#bPWJ^p2 zVbs{=P2C~;GV>Zlkw%u3?OM9&TE|2xMT@t3uSiNEt`MOO*Q>52Wh>pfXJR}YW6XQ{ zJfCN%^ZlJU=RD7Ip3^zMKT-4Q8#0faYOd#r>yK58)sH5XCS>Yj%p1^_p%gSNX4Iai z%;dio52O@`qrWD0>K#6CJvdGFcB%`pA47@W5qIzGe`HRY=O5CK4bZvl6IkJj{#%r? z|A5O4Uo8)Ng;t9f!sRAIsl1a8=TST_Vn(m0i`>XCa0r`>YP-LwxB%^wu8;8+GdQv( zG^usXB?ocI0_)y0MR`T!?Us5ehia8>M~+$sXlUCRovE--QR@;Ys?Ozq9P(Q7ZQ43> zpIo}_{z39UhS{5f8wKSDu+TKfi+#n{O-~4Uk zh*EmSxYYrfwOxCYV}}!zL%2uIc%Oe$XRV@rFeWeka?;Z(XI{}`X?HJGyIgFm@ZX;w zsc2~^A%MTLdqhpoV!jr)}36>dv>Px$jJImpFCzVcs)1b7l%&=qcE;^ zEoSbtk#6sYkpC=iQX(3 z5EUP%LDh0p49U2=$~DIZhi;dDRKwLN8`|PiC-Echa#PXZ|6)S}wWEA@3f!rX>G_!A zphhlmxu@3JVRr3xOWD}*UYv04{*WHt*vT;0@pVLmuu52Mb_Vg9Wg9EUuA2 zl8?Jv5GSU+*{PO$tBpirns`>?!VL-cX@gZO&q)OL%2_8U)8r*4jrGrH`p2zV!T-&| zaf{j)uCI!{A{R9~aJ?$SZ?kk?jfE7FM%1sOCd&S0B(^ckufHtAOetsuspYrqyZ)x8Z8=dG=GG1lcFtKmoxl{>m zAakHGc|f5ZKh>>}F8qu)Y29d2Op+uf?qK|dKPwE!pPkfGl#Sa#?TmJfv}jA5;1`#= zQqplM=!3^!2QZeCx7wu8uWl9!IN85^zrmqGDxsj;TVs=EU)ubiDaD<*@ss- zm%Y-l)9@TN+_0W7Ml5XnEz>_ep>fFIL{5V-n#cCKFhy#0p;!@D!D-=e{(8;*$#2G- z-~F3cHNv>%;D819xg3-F_yHg8bD1W}{1-kQ-da2kMRP?r=@>BD^b5H6=`Lf3y6VPn$`%)-GW}O^kSon7EBP;q9?=n_7O67v9pc>!pQb z)auPuaqG5v3l(E)_GSI_vFY2BtlPgw{(hIMip%d;>9vWnej@q%qMva4iRPI|N7n7w z(!_tL^K*((d428fyiU(eFYzyaICWGnFx_T^a$3(A4p<5kwVtGjOSNa=ey z3;wiIDZDmghb8BsMcSVyT9^W#{YkoGJ9As)0ccff5 zB`U1^TKO@jql!utGX7_6ceT=$mJTWcQ+7_Fk7=jIE7Lu2Ja%~~6K=X$o@5Q7)=`Ao z%Vptz#p~F$l82kO>0*a`LQ8HomkN}$Q0{w8GzfUMX3_$LbiUMT6?eJhshLtmT2m`2 zrK@zuUt8C6$2Zb?u5HM~2xm~H)s1rOJ^3v#{cdG~?xM<+6Lrd(chPMthvmtIcgJoV z-(H!YsUD=t^F)QFU+e|WYBXo`#ht!`&flPI?tga}(nLX13WI~;V?XO(57wx&_pbkw zBgcA$g+wx2w|Xvakrlw=n~x7nWeO7*SwR2(p1`8M*~Ae34SZ&}#$zt|Z%!C%XpOXbpLFv5`sjlu|+#!Pgo9FXG>J~QZn(O%YH zBWQs46dZC)E;!SviJp zefD-koJ?SaKCq_$3t)wALZM_9CQK zGw9iXX^iWLHTQFmME^y==>muB0FYBWAg>aJ#z};63aHSV~ z^&BI1Xx6m%m3k8-P|$7QUIaSpT%uDW?OD?BB+n%~l7+?9t%+Q~hX?=}`?8pcPE~ed z2_t~uEm#W0-QN{N#+ApD+=zZSaBm3ob`3@h+u^Gh4ttNN2s$sX!nzuwp?JOsGoHwj z2@l5>ME8YD3`fUA=$RfY>9hSG4D8@onJ^lTK8T>xz1g7`#v+8NaNr$;IubZHjA0js z2L>_#pi_KLjIjbU(W!eWi-1dyWY}RDad&1C;~9SzVCP+CjBSB%W;hBDGdrDHyErp5 z5X#cSZWs?oRzdJKA&bh!#B=h>1`ELv5fGsjM;8grEB_Ml5nw!Q?T_Fy!`b1Xw-Oi& zJK7`IPZ8{}^QU`YChTvFFb$*GF~83#Ejd(!t%MOOCWZs*(#FDY@nJtyM5ys3r$RH; zGwY5D3&8G^h`_zm90;)SqJ))TM><4FJcR=#j{NChP1sZn(R`H3fhIePF<1&VWkIAq zW^y3K#-asQg8eTLr4LygD9v;SEK4^GSPFI-K%^#fIhF$V7sl;-&O{IvfwyiWBC85G z7MZzT=Na3;D)1g*L}lf9j#XxMO|l*@z#B0U0n~;6Q((CogEzq;QX^ml3_auK-QH(! zYRlFYydetV8<%jvXTLoPZWwqE2_hCzy1W?cwt!a;Ak6maMa=Kjv3M;3Tu%5uArNL? z-SSL!&nS5679sOBE+%t6kqdtVcsdc$>26x21CM6sb)#h-?QyJ literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..a4b4429 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..2fe81a7 --- /dev/null +++ b/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..62bd9b9 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..6e22cf0 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,10 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/6.3/userguide/multi_project_builds.html + */ + +rootProject.name = 'SimpleJavaEngine' diff --git a/src/main/java/speiger/src/coreengine/assets/AssetLocation.java b/src/main/java/speiger/src/coreengine/assets/AssetLocation.java new file mode 100644 index 0000000..07d3d6d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/AssetLocation.java @@ -0,0 +1,92 @@ +package speiger.src.coreengine.assets; + +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; + +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; + +public final class AssetLocation +{ + static final Map LOCATION = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap()); + static final Function BUILDER = AssetLocation::compute; + final String domain; + final String location; + + private AssetLocation() {throw new UnsupportedOperationException("use AssetLocation.of instead");} + + private AssetLocation(String domain, String location) + { + this.domain = domain; + this.location = location; + } + + public String getDomain() + { + return domain; + } + + public String getLocation() + { + return location; + } + + public String getActualLocation() + { + return "assets/"+domain+"/"+location; + } + + public AssetLocation subAsset(String subPath) + { + return of(domain+":"+location + "/" + subPath); + } + + @Override + public String toString() + { + return domain+":"+location; + } + + @Override + public int hashCode() + { + return Objects.hash(domain, location); + } + + @Override + public boolean equals(Object obj) + { + if(obj == this) + { + return true; + } + if(obj instanceof AssetLocation) + { + AssetLocation location = (AssetLocation)obj; + return location.domain.equals(domain) && location.location.equals(this.location); + } + return false; + } + + public boolean matches(AssetLocation location) + { + return location.domain.equals(domain) && location.location.equals(this.location); + } + + public static final AssetLocation of(String domain, String location) + { + return of((domain == null || domain.isEmpty() ? "base" : domain)+":"+location); + } + + public static final AssetLocation of(String location) + { + return LOCATION.computeIfAbsent(location.indexOf(":") == -1 ? "base:"+location : location, BUILDER); + } + + private static final AssetLocation compute(String s) + { + int index = s.indexOf(":"); + return new AssetLocation(s.substring(0, index), s.substring(index+1, s.length())); + } +} diff --git a/src/main/java/speiger/src/coreengine/assets/AssetManager.java b/src/main/java/speiger/src/coreengine/assets/AssetManager.java new file mode 100644 index 0000000..0aaf702 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/AssetManager.java @@ -0,0 +1,186 @@ +package speiger.src.coreengine.assets; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.zip.ZipFile; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.coreengine.assets.impl.FolderAssetPackage; +import speiger.src.coreengine.assets.impl.ZipAssetPackage; +import speiger.src.coreengine.assets.reloader.IReloadableResource; + +public class AssetManager implements IReloadableResource +{ + Map domains = new Object2ObjectLinkedOpenHashMap(); + File file; + + public AssetManager(File file) + { + this.file = file; + } + + public File getMainAsset() + { + return file; + } + + @Override + public void reload() + { + domains.clear(); + loadAssetPackage(file); + } + + public void loadAssetPackage(File file) + { + if(file.isDirectory()) + { + loadAssets(new FolderAssetPackage(file)); + } + else if(isZipFile(file)) + { + loadAssets(new ZipAssetPackage(file)); + } + } + + public void loadAssets(IAssetPackage packages) + { + for(String s : packages.getDomains()) + { + DomainAssets entry = domains.get(s); + if(entry == null) + { + entry = new DomainAssets(); + domains.put(s, entry); + } + entry.addAssets(packages); + } + } + + public Set getDomains() + { + return domains.keySet(); + } + + public IAsset getAsset(AssetLocation location) throws IOException + { + DomainAssets asset = domains.get(location.getDomain()); + if(asset == null) + { + throw new FileNotFoundException("File["+location.toString()+"] not found"); + } + return asset.getAsset(location); + } + + public MultiAsset getAssets(AssetLocation...locations) + { + IAsset[] asset = new IAsset[locations.length]; + for(int i = 0,m=locations.length;i packages = new ObjectArrayList(); + + public void addAssets(IAssetPackage assets) + { + packages.add(assets); + } + + public IAsset getAsset(AssetLocation location) throws IOException + { + for(int i = packages.size() - 1;i>=0;i--) + { + IAsset asset = packages.get(i).getAsset(location); + if(asset != null) + { + return asset; + } + } + throw new FileNotFoundException("File["+location.toString()+"] not found"); + } + + public MultiAsset getMultiAsset(AssetLocation location, AssetLocation[] subLocations) + { + for(int i = packages.size() - 1;i>=0;i--) + { + IAssetPackage entry = packages.get(i); + IAsset asset = entry.getAsset(location); + if(asset != null) + { + IAsset[] result = new IAsset[1 + subLocations.length]; + result[0] = asset; + for(int x = 0;x assets = new ObjectArrayList(); + for(IAssetPackage entry : packages) + { + IAsset asset = entry.getAsset(location); + if(asset != null) + { + assets.add(asset); + } + } + if(assets.isEmpty()) + { + throw new FileNotFoundException("File["+location.toString()+"] not found"); + } + return new MultiAsset(assets.toArray(new IAsset[assets.size()])); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/assets/IAsset.java b/src/main/java/speiger/src/coreengine/assets/IAsset.java new file mode 100644 index 0000000..aa0ea29 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/IAsset.java @@ -0,0 +1,22 @@ +package speiger.src.coreengine.assets; + +import java.awt.image.BufferedImage; +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; + +import com.google.gson.JsonObject; + +public interface IAsset extends Closeable +{ + public AssetLocation getLocation(); + + public InputStream getStream() throws IOException; + + public BufferedImage getTexture() throws Exception; + + public BufferedReader getStringReader() throws IOException; + + public JsonObject getJsonObject() throws IOException; +} diff --git a/src/main/java/speiger/src/coreengine/assets/IAssetPackage.java b/src/main/java/speiger/src/coreengine/assets/IAssetPackage.java new file mode 100644 index 0000000..5d46f61 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/IAssetPackage.java @@ -0,0 +1,10 @@ +package speiger.src.coreengine.assets; + +import java.util.List; + +public interface IAssetPackage +{ + public List getDomains(); + + public IAsset getAsset(AssetLocation location); +} diff --git a/src/main/java/speiger/src/coreengine/assets/MultiAsset.java b/src/main/java/speiger/src/coreengine/assets/MultiAsset.java new file mode 100644 index 0000000..a89d7b2 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/MultiAsset.java @@ -0,0 +1,41 @@ +package speiger.src.coreengine.assets; + +import java.io.Closeable; +import java.io.IOException; + +public class MultiAsset implements Closeable +{ + IAsset[] assets; + + public MultiAsset(IAsset[] assets) + { + this.assets = assets; + } + + @Override + public void close() throws IOException + { + if(assets != null) + { + for(int i = 0,m=assets.length;i toClose = new ArrayList(); + + public FileAsset(File file, AssetLocation location) + { + this.file = file; + this.location = location; + } + + @Override + public void close() throws IOException + { + for(Closeable close : toClose) + { + try + { + close.close(); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + toClose.clear(); + } + + @Override + public AssetLocation getLocation() + { + return location; + } + + @Override + public InputStream getStream() throws IOException + { + return new BufferedInputStream(markClosed(new FileInputStream(file))); + } + + @Override + public BufferedReader getStringReader() throws IOException + { + return new BufferedReader(markClosed(new FileReader(file))); + } + + @Override + public BufferedImage getTexture() throws Exception + { + return ImageIO.read(file); + } + + @Override + public JsonObject getJsonObject() throws IOException + { + return JsonUtil.loadFile(file); + } + + protected T markClosed(T value) + { + toClose.add(value); + return value; + } +} diff --git a/src/main/java/speiger/src/coreengine/assets/impl/FolderAssetPackage.java b/src/main/java/speiger/src/coreengine/assets/impl/FolderAssetPackage.java new file mode 100644 index 0000000..f7efdf4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/impl/FolderAssetPackage.java @@ -0,0 +1,47 @@ +package speiger.src.coreengine.assets.impl; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.List; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.IAsset; +import speiger.src.coreengine.assets.IAssetPackage; + +public class FolderAssetPackage implements IAssetPackage +{ + static final FileFilter FOLDER_FILTER = new FileFilter(){ + @Override + public boolean accept(File f) + { + return f.isDirectory(); + } + }; + File file; + + public FolderAssetPackage(File file) + { + this.file = file; + } + + @Override + public List getDomains() + { + List result = new ArrayList(); + File asset = new File(file, "assets/"); + for(File file : asset.listFiles(FOLDER_FILTER)) + { + result.add(file.getAbsolutePath().substring(asset.getAbsolutePath().length() + 1).replace("\\", "/").replace("\"", "/")); + } + return result; + } + + @Override + public IAsset getAsset(AssetLocation location) + { + File asset = new File(file, location.getActualLocation()); + return asset.isFile() ? new FileAsset(asset, location) : null; + } + +} diff --git a/src/main/java/speiger/src/coreengine/assets/impl/ZipAsset.java b/src/main/java/speiger/src/coreengine/assets/impl/ZipAsset.java new file mode 100644 index 0000000..c0d7e32 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/impl/ZipAsset.java @@ -0,0 +1,75 @@ +package speiger.src.coreengine.assets.impl; + +import java.awt.image.BufferedImage; +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import javax.imageio.ImageIO; + +import com.google.gson.JsonObject; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.IAsset; +import speiger.src.coreengine.utils.helpers.JsonUtil; + +public class ZipAsset implements IAsset +{ + ZipFile file; + ZipEntry entry; + AssetLocation location; + Runnable runnable; + + public ZipAsset(ZipFile file, ZipEntry entry, AssetLocation location, Runnable runnable) + { + this.file = file; + this.entry = entry; + this.location = location; + this.runnable = runnable; + } + + @Override + public void close() throws IOException + { + if(runnable != null) + { + runnable.run(); + runnable = null; + } + } + + @Override + public AssetLocation getLocation() + { + return location; + } + + @Override + public InputStream getStream() throws IOException + { + return new BufferedInputStream(file.getInputStream(entry)); + } + + @Override + public BufferedImage getTexture() throws Exception + { + return ImageIO.read(file.getInputStream(entry)); + } + + @Override + public BufferedReader getStringReader() throws IOException + { + return new BufferedReader(new InputStreamReader(file.getInputStream(entry))); + } + + @Override + public JsonObject getJsonObject() throws IOException + { + return JsonUtil.loadFile(file.getInputStream(entry)); + } + +} diff --git a/src/main/java/speiger/src/coreengine/assets/impl/ZipAssetPackage.java b/src/main/java/speiger/src/coreengine/assets/impl/ZipAssetPackage.java new file mode 100644 index 0000000..98e6b8a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/impl/ZipAssetPackage.java @@ -0,0 +1,111 @@ +package speiger.src.coreengine.assets.impl; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.IAsset; +import speiger.src.coreengine.assets.IAssetPackage; +import speiger.src.coreengine.utils.collections.iterators.IterableWrapper; + +public class ZipAssetPackage implements IAssetPackage +{ + public static final int ASSET_OFFSET = "assets/".length(); + File file; + ZipFile cache = null; + AtomicInteger usedReferences = new AtomicInteger(); + + public ZipAssetPackage(File file) + { + this.file = file; + } + + protected ZipFile getReference() throws IOException + { + usedReferences.getAndIncrement(); + if(cache == null) cache = new ZipFile(file); + return cache; + } + + protected void onReferenceClosed() + { + int left = usedReferences.decrementAndGet(); + if(left == 0 && cache != null) + { + try + { + cache.close(); + } + catch(Exception e) {e.printStackTrace();} + cache = null; + } + else if(left < 0) + { + usedReferences.set(0); + } + } + + @Override + public List getDomains() + { + try + { + List result = new ArrayList(); + ZipFile zip = new ZipFile(file); + for(ZipEntry entry : IterableWrapper.wrap(zip.entries())) + { + String name = entry.getName(); + if(isPathValid(name)) + { + result.add(name.substring(ASSET_OFFSET, name.length() - 1)); + } + } + zip.close(); + return result; + } + catch(IOException e) + { + e.printStackTrace(); + return Collections.emptyList(); + } + } + + private boolean isPathValid(String input) + { + if(input.startsWith("assets/")) + { + int index = input.indexOf("/", ASSET_OFFSET); + if(index != -1 && input.length() - index <= 1) + { + return true; + } + } + return false; + } + + @Override + public IAsset getAsset(AssetLocation location) + { + try + { + ZipFile zip = getReference(); + ZipEntry entry = zip.getEntry(location.getActualLocation()); + if(entry != null) + { + return new ZipAsset(zip, entry, location, this::onReferenceClosed); + } + } + catch(Exception e) + { + e.printStackTrace(); + } + onReferenceClosed(); + return null; + } +} diff --git a/src/main/java/speiger/src/coreengine/assets/language/I18n.java b/src/main/java/speiger/src/coreengine/assets/language/I18n.java new file mode 100644 index 0000000..8431b66 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/language/I18n.java @@ -0,0 +1,16 @@ +package speiger.src.coreengine.assets.language; + +public class I18n +{ + static Language CURRENT_LANGUAGE; + + public static String translate(String key) + { + return CURRENT_LANGUAGE.translate(key); + } + + public String translate(String key, Object...args) + { + return CURRENT_LANGUAGE.translate(key, args); + } +} diff --git a/src/main/java/speiger/src/coreengine/assets/language/Language.java b/src/main/java/speiger/src/coreengine/assets/language/Language.java new file mode 100644 index 0000000..6fd7b20 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/language/Language.java @@ -0,0 +1,47 @@ +package speiger.src.coreengine.assets.language; + +import java.util.Map; + +public class Language +{ + String language; + String code; + Map translations; + + public Language(String code, String language) + { + this.code = code; + this.language = language; + } + + public void load(Map data) + { + translations = data; + } + + public void clear() + { + translations = null; + } + + public String translate(String key) + { + String result = translations.get(key); + return result == null ? key : result; + } + + public String translate(String key, Object...args) + { + return String.format(translate(key), args); + } + + public String getCode() + { + return code; + } + + public String getLanguage() + { + return language; + } +} diff --git a/src/main/java/speiger/src/coreengine/assets/language/LanguageManager.java b/src/main/java/speiger/src/coreengine/assets/language/LanguageManager.java new file mode 100644 index 0000000..e4fcf67 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/language/LanguageManager.java @@ -0,0 +1,151 @@ +package speiger.src.coreengine.assets.language; + +import java.util.Map; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.AssetManager; +import speiger.src.coreengine.assets.MultiAsset; +import speiger.src.coreengine.assets.reloader.IReloadableResource; +import speiger.src.coreengine.utils.collections.iterators.IterableWrapper; + +public class LanguageManager implements IReloadableResource +{ + final AssetLocation location = AssetLocation.of("lang"); + Map languages = new Object2ObjectOpenHashMap(); + AssetManager assets; + String currentLanguage; + + @Override + public void reload() + { + languages.clear(); + try(MultiAsset langs = assets.getAllAsset(location.subAsset("langs.json"))) + { + for(int i = 0,m=langs.size();i map = new Object2ObjectOpenHashMap(); + loadLanguage(loadingLang, map); + if(loadingLang != "en_US") + { + loadLanguage("en_US", map); + } + lang.load(map); + return true; + } + + protected void loadLanguage(String lang, Map data) + { + try(MultiAsset language = assets.getAllAsset(location.subAsset(lang+".lang"))) + { + for(int i = 0,m=language.size();i 16) + { + continue; + } + Language lang = languages.get(key); + if(lang == null) + { + languages.put(key, new Language(key, value)); + } + } + } + catch(Exception e) + { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/speiger/src/coreengine/assets/reloader/IReloadableResource.java b/src/main/java/speiger/src/coreengine/assets/reloader/IReloadableResource.java new file mode 100644 index 0000000..a5d47b4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/reloader/IReloadableResource.java @@ -0,0 +1,8 @@ +package speiger.src.coreengine.assets.reloader; + +public interface IReloadableResource +{ + public void reload(); + + public default void destroy() {}; +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/assets/reloader/ResourceReloader.java b/src/main/java/speiger/src/coreengine/assets/reloader/ResourceReloader.java new file mode 100644 index 0000000..1725b45 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/reloader/ResourceReloader.java @@ -0,0 +1,62 @@ +package speiger.src.coreengine.assets.reloader; + +import java.util.Set; + +import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet; + +public class ResourceReloader +{ +// public static ResourceReloader INSTANCE = new ResourceReloader(); + Set resources = new ObjectLinkedOpenHashSet(); + boolean globalRemoval = false; + boolean reloading = false; + + public T addReloadableResource(T resource) + { + return addReloadableResource(resource, false); + } + + public T addReloadableResource(T resource, boolean init) + { + if(resources.add(resource) && init) + { + resource.reload(); + } + return resource; + } + + public void removeReloadableResource(IReloadableResource resource) + { + if(globalRemoval || reloading) + { + return; + } + resources.remove(resource); + } + + public boolean isReloading() + { + return reloading; + } + + public void reloadResources() + { + reloading = true; + for(IReloadableResource resource : resources) + { + resource.reload(); + } + reloading = false; + } + + public void deleteResources() + { + globalRemoval = true; + for(IReloadableResource resource : resources) + { + resource.destroy(); + } + resources.clear(); + globalRemoval = false; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/ArrayUtil.java b/src/main/java/speiger/src/coreengine/math/ArrayUtil.java new file mode 100644 index 0000000..e77fc76 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/ArrayUtil.java @@ -0,0 +1,76 @@ +package speiger.src.coreengine.math; + +import java.util.Arrays; +import java.util.List; + +import speiger.src.collections.ints.lists.IntList; +import speiger.src.collections.objects.lists.ObjectArrayList; + +public class ArrayUtil +{ + public static int[] fromTo(int from, int to) + { + int[] result = new int[to - from]; + for(int i = 0,m=result.length;i List makeList(IntList indexes, List entries) + { + List result = new ObjectArrayList(indexes.size()); + for(int i = 0,m=indexes.size();i> 32) & 0xFFFFFFFFL); + } + + public static int toSecondInt(long value) + { + return (int)(value & 0xFFFFFFFFL); + } + + public static long addToLong(long value, long x, long z) + { + return ((x + toFirstInt(value)) & 0xFFFFFFFFL) << 32 | (((z + toSecondInt(value)) & 0xFFFFFFFFL)); + } + + public static int toInt(int firstShort, int secondShort) + { + return (firstShort & 0xFFFF) << 16 | (secondShort & 0xFFFF); + } + + public static int toFirstShort(int value) + { + return (short)(value >> 16 & 0xFFFF); + } + + public static int toSecondShort(int value) + { + return (short)(value & 0xFFFF); + } + + public static int addToInt(int value, int x, int z) + { + return ((x + toFirstShort(value)) & 0xFFFF) << 16 | ((z + toSecondShort(value)) & 0xFFFF); + } + + public static int addToInt(int value, Vec2i offset) + { + return ((offset.getX() + toFirstShort(value)) & 0xFFFF) << 16 | ((offset.getY() + toSecondShort(value)) & 0xFFFF); + } + + public static int pack_2_10_10_10_int_rev(float x, float y, float z, float w) + { + return (Math.round(w * 3F) & 3) << 30 | (Math.round(((z * 0.5F) + 0.5F) * 1023F) & 1023) << 20 | (Math.round(((y * 0.5F) + 0.5F) * 1023F) & 1023) << 10 | (Math.round(((x * 0.5F) + 0.5F) * 1023F) & 1023); + } + + public static int pack_10_10_10_2_int(float x, float y, float z, float w) + { + return (Math.round(((x * 0.5F) + 0.5F) * 1023F) & 1023) << 22 | (Math.round(((y * 0.5F) + 0.5F) * 1023F) & 1023) << 12 | (Math.round(((z * 0.5F) + 0.5F) * 1023F) & 1023) << 2 | (Math.round(w * 3F) & 3); + } +} diff --git a/src/main/java/speiger/src/coreengine/math/MathUtils.java b/src/main/java/speiger/src/coreengine/math/MathUtils.java new file mode 100644 index 0000000..9751414 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/MathUtils.java @@ -0,0 +1,171 @@ +package speiger.src.coreengine.math; + +public class MathUtils +{ + private static final float[] SIN_TABLE; + private static final float[] COS_TABLE; + private static final float TABLE_MASK; + private static final int SIN_MASK; + public static final double PI_INVERSION = 180D / Math.PI; + + public static float sin(float a) + { + return SIN_TABLE[(int)(a * TABLE_MASK) & SIN_MASK]; + } + + public static float sin(double a) + { + return SIN_TABLE[(int)(a * TABLE_MASK) & SIN_MASK]; + } + + public static float cos(float a) + { + return COS_TABLE[(int)(a * TABLE_MASK) & SIN_MASK]; + } + + public static float cos(double a) + { + return COS_TABLE[(int)(a * TABLE_MASK) & SIN_MASK]; + } + + public static byte clamp(byte min, byte max, byte current) + { + return current < min ? min : (current > max ? max : current); + } + + public static short clamp(short min, short max, short current) + { + return current < min ? min : (current > max ? max : current); + } + + public static int clamp(int min, int max, int current) + { + return current < min ? min : (current > max ? max : current); + } + + public static float clamp(float min, float max, float current) + { + return current < min ? min : (current > max ? max : current); + } + + public static double clamp(double min, double max, double current) + { + return current < min ? min : (current > max ? max : current); + } + + public static long clamp(long min, long max, long current) + { + return current < min ? min : (current > max ? max : current); + } + + public static byte sign(byte value) + { + return value > 0 ? 1 : (value < 0 ? -1 : (byte)0); + } + + public static short sign(short value) + { + return value > 0 ? 1 : (value < 0 ? -1 : (short)0); + } + + public static int sign(int value) + { + return value > 0 ? 1 : (value < 0 ? -1 : 0); + } + + public static long sign(long value) + { + return value > 0 ? 1 : (value < 0 ? -1 : 0); + } + + public static float sign(float value) + { + return value > 0 ? 1F : (value < 0 ? -1F : 0F); + } + + public static double sign(double value) + { + return value > 0 ? 1D : (value < 0 ? -1D : 0D); + } + + public static int ceil(double value) + { + int i = (int)value; + return value > i ? i + 1 : i; + } + + public static int ceil(float value) + { + int i = (int)value; + return value > i ? i + 1 : i; + } + + public static int floor(float value) + { + int i = (int)value; + return value < i ? i - 1 : i; + } + + public static int floor(double value) + { + int i = (int)value; + return value < i ? i - 1 : i; + } + + public static int pow(int base, int exp) + { + return (int)Math.pow(base, exp); + } + + public static float lerp(float start, float end, float progress) + { + return start + ((end - start) * progress); + } + + public static double lerp(double start, double end, float progress) + { + return start + ((end - start) * progress); + } + + public static float smoothLerp(float start, float end, float progress) + { + float cosProgress = (1F - MathUtils.cos(progress * Math.PI)) * 0.5F; + return start * (1F - cosProgress) + end * cosProgress; + } + + public static float quadraticCurve(float start, float median, float end, float progress) + { + return lerp(lerp(start, median, progress), lerp(median, end, progress), progress); + } + + public static float smoothQuadraticCurve(float start, float median, float end, float progress) + { + return smoothLerp(smoothLerp(start, median, progress), smoothLerp(median, end, progress), progress); + } + + static + { + SIN_MASK = ~(-1 << 12); + int SIN_COUNT = SIN_MASK + 1; + + float radFull = (float)(Math.PI * 2D); + float degFull = (float)(360.0); + float radToIndex = SIN_COUNT / radFull; + float degToIndex = SIN_COUNT / degFull; + + TABLE_MASK = radToIndex; + SIN_TABLE = new float[SIN_COUNT]; + COS_TABLE = new float[SIN_COUNT]; + + for(int i = 0;i < SIN_COUNT;i++) + { + SIN_TABLE[i] = (float)Math.sin((i + 0.5f) / SIN_COUNT * radFull); + COS_TABLE[i] = (float)Math.cos((i + 0.5f) / SIN_COUNT * radFull); + } + for(int i = 0;i < 360;i += 90) + { + SIN_TABLE[(int)(i * degToIndex) & SIN_MASK] = (float)Math.sin(i * Math.PI / 180D); + COS_TABLE[(int)(i * degToIndex) & SIN_MASK] = (float)Math.cos(i * Math.PI / 180D); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/math/ShapeUtil.java b/src/main/java/speiger/src/coreengine/math/ShapeUtil.java new file mode 100644 index 0000000..f956b4f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/ShapeUtil.java @@ -0,0 +1,23 @@ +package speiger.src.coreengine.math; + +import speiger.src.coreengine.math.vector.ints.Vec2i; + +public class ShapeUtil +{ + public static boolean isInCircle(int position, int radius, int testX, int testZ) + { + return isInCircle(BitUtil.toFirstShort(position), BitUtil.toSecondShort(position), radius, testX, testZ); + } + + public static boolean isInCircle(Vec2i position, int radius, int testX, int testZ) + { + return isInCircle(position.getX(), position.getY(), radius, testX, testZ); + } + + public static boolean isInCircle(int posX, int posZ, int radius, int testX, int testZ) + { + posX -= testX; + posZ -= testZ; + return (posX * posX) + (posZ * posZ) < radius * radius; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/collision2d/Circle.java b/src/main/java/speiger/src/coreengine/math/collision2d/Circle.java new file mode 100644 index 0000000..cb911fb --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/collision2d/Circle.java @@ -0,0 +1,91 @@ +package speiger.src.coreengine.math.collision2d; + +import java.util.Iterator; + +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.math.vector.ints.Vec3i; + +public class Circle implements I2DCollision +{ + final Vec2i center; + final int radius; + + public Circle(int x, int y, int radius) + { + center = Vec2i.newVec(x, y); + this.radius = radius; + } + + public Circle(Vec2i center, int radius) + { + this.center = center.asImmutable(); + this.radius = radius; + } + + public Circle(Vec3i data) + { + this(data.getX(), data.getY(), data.getZ()); + } + + public Vec2i getCenter() + { + return center; + } + + public int getRadius() + { + return radius; + } + + @Override + public boolean isColliding(int x, int y) + { + int xDiff = center.getX() - x; + int yDiff = center.getY() - y; + return ((xDiff * xDiff) + (yDiff * yDiff)) < radius * radius; + } + + @Override + public Iterator iterator() + { + return new Iterator(){ + Vec2i iter = step(Vec2i.newMutable(center).sub(radius)); + Vec2i result = Vec2i.newMutable(); + + @Override + public boolean hasNext() + { + return iter != null; + } + + @Override + public Vec2i next() + { + result.set(iter); + iter = step(iter); + return result; + } + + Vec2i step(Vec2i iter) + { + while(iter != null) + { + iter.add(1, 0); + if(iter.getX() >= center.getX() + radius) + { + iter.sub((radius * 2), 0).add(0, 1); + if(iter.getY() >= center.getY() + radius) + { + iter = null; + } + } + if(iter != null && isColliding(iter)) + { + break; + } + } + return iter; + } + }; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/collision2d/I2DCollision.java b/src/main/java/speiger/src/coreengine/math/collision2d/I2DCollision.java new file mode 100644 index 0000000..081ad6d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/collision2d/I2DCollision.java @@ -0,0 +1,12 @@ +package speiger.src.coreengine.math.collision2d; + +import speiger.src.coreengine.math.BitUtil; +import speiger.src.coreengine.math.vector.ints.Vec2i; + +public interface I2DCollision extends Iterable +{ + public default boolean isMixedCollision() {return false;} + public default boolean isColliding(int position){return isColliding(BitUtil.toFirstShort(position), BitUtil.toSecondShort(position));} + public default boolean isColliding(Vec2i pos){return isColliding(pos.getX(), pos.getY());} + public boolean isColliding(int x, int y); +} diff --git a/src/main/java/speiger/src/coreengine/math/collision2d/Mixed2DCollision.java b/src/main/java/speiger/src/coreengine/math/collision2d/Mixed2DCollision.java new file mode 100644 index 0000000..d9fb168 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/collision2d/Mixed2DCollision.java @@ -0,0 +1,72 @@ +package speiger.src.coreengine.math.collision2d; + +import java.util.Iterator; + +import speiger.src.coreengine.math.vector.ints.Vec2i; + +public class Mixed2DCollision implements I2DCollision +{ + I2DCollision mainBox; + I2DCollision[] subBoxes; + + public Mixed2DCollision(I2DCollision mainBox, I2DCollision...subBoxes) + { + this.mainBox = mainBox; + this.subBoxes = subBoxes; + } + + @Override + public boolean isColliding(int x, int y) + { + return mainBox.isColliding(x, y) && isCollidingInSubBox(x, y); + } + + public boolean isCollidingInSubBox(int x, int y) + { + for(int i = 0,m=subBoxes.length;i iterator() + { + return new Iterator(){ + Iterator mainIter = mainBox.iterator(); + Vec2i cache = findNext(Vec2i.newMutable()); + Vec2i result = Vec2i.newMutable(); + @Override + public boolean hasNext() + { + return cache != null; + } + + @Override + public Vec2i next() + { + result.set(cache); + cache = findNext(cache); + return result; + } + + Vec2i findNext(Vec2i input) + { + while(mainIter.hasNext()) + { + Vec2i next = mainIter.next(); + if(isCollidingInSubBox(next.getX(), next.getY())) + { + return input.set(next); + } + } + return null; + } + }; + } + +} diff --git a/src/main/java/speiger/src/coreengine/math/collision2d/Plane.java b/src/main/java/speiger/src/coreengine/math/collision2d/Plane.java new file mode 100644 index 0000000..593ac79 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/collision2d/Plane.java @@ -0,0 +1,104 @@ +package speiger.src.coreengine.math.collision2d; + +import java.util.Iterator; + +import speiger.src.coreengine.math.vector.ints.Vec2i; + +public class Plane implements I2DCollision +{ + Vec2i min; + Vec2i max; + + public Plane() + { + this(0, 0, 0, 0); + } + + public Plane(int minX, int minY, int maxX, int maxY) + { + this(Vec2i.newMutable(minX, minY), Vec2i.newMutable(maxX, maxY)); + } + + public Plane(Vec2i center, int radius) + { + this(center.sub(radius), center.add(radius)); + } + + public Plane(Vec2i min, Vec2i max) + { + this.min = min; + this.max = max; + } + + public Plane set(Plane other) + { + min.set(other.getMin()); + max.set(other.getMax()); + return this; + } + + public Plane set(int minX, int minY, int maxX, int maxY) + { + min.set(minX, minY); + max.set(maxX, maxY); + return this; + } + + @Override + public boolean isColliding(int x, int y) + { + return x >= min.getX() && y >= min.getY() && x < max.getX() && y < max.getY(); + } + + public boolean isIntersecting(Plane plane) + { + return isIntersecting(plane.getMinX(), plane.getMinY(), plane.getMaxX(), plane.getMaxY()); + } + + public boolean isIntersecting(int minX, int minY, int maxX, int maxY) + { + return min.getX() <= maxX && max.getX() >= minX && min.getY() <= maxY && max.getY() >= minY; + } + + public Vec2i getMin(){return min;} + public Vec2i getMax(){return max;} + public int getWidth(){return max.getX() - min.getX();} + public int getHeight(){return max.getY() - min.getY();} + public int getMinX(){return min.getX();} + public int getMinY(){return min.getY();} + public int getMaxX(){return max.getX();} + public int getMaxY(){return max.getY();} + + @Override + public Iterator iterator() + { + return new Iterator(){ + Vec2i iter = Vec2i.newMutable(min); + Vec2i result = Vec2i.newMutable(); + + @Override + public boolean hasNext() + { + return iter != null; + } + + @Override + public Vec2i next() + { + result.set(iter); + iter.add(1, 0); + if(iter.getX() == max.getX()) + { + iter.set(min.getX(), iter.getY() + 1); + if(iter.getY() == max.getY()) + { + iter = null; + } + } + return result; + } + + }; + } + +} diff --git a/src/main/java/speiger/src/coreengine/math/misc/ColorObject.java b/src/main/java/speiger/src/coreengine/math/misc/ColorObject.java new file mode 100644 index 0000000..d09cad8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/misc/ColorObject.java @@ -0,0 +1,381 @@ +package speiger.src.coreengine.math.misc; + +import java.awt.Color; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; + +import speiger.src.collections.floats.lists.FloatList; + +public class ColorObject +{ + static final float DEVIDER = 1F / 255F; + public static final int R = 0xFF << 16; + public static final int G = 0xFF << 8; + public static final int B = 0xFF; + public static final int A = 0xFF << 24; + public static final long SIGN = 0x00000000FFFFFFFFL; + static final int ALL = 0xFFFFFFFF; + + public static final ColorObject WHITE = new ColorObject(255, 255, 255); + public static final ColorObject LIGHT_GRAY = new ColorObject(192, 192, 192); + public static final ColorObject GRAY = new ColorObject(128, 128, 128); + public static final ColorObject DARK_GRAY = new ColorObject(64, 64, 64); + public static final ColorObject BLACK = new ColorObject(0, 0, 0); + public static final ColorObject RED = new ColorObject(255, 0, 0); + public static final ColorObject PINK = new ColorObject(255, 175, 175); + public static final ColorObject PURPLE = new ColorObject(106, 13, 173); + public static final ColorObject ORANGE = new ColorObject(255, 200, 0); + public static final ColorObject YELLOW = new ColorObject(255, 255, 0); + public static final ColorObject GREEN = new ColorObject(0, 255, 0); + public static final ColorObject DARK_GREEN = new ColorObject(7, 161, 0); + public static final ColorObject MAGENTA = new ColorObject(255, 0, 255); + public static final ColorObject CYAN = new ColorObject(0, 255, 255); + public static final ColorObject BLUE = new ColorObject(0, 0, 255); + public static final ColorObject LIGHT_BLUE = new ColorObject(0, 150, 255); + + //Specialized Components that get reused + public static final ColorObject INVISIBLE = new ColorObject(0, 0, 0, 0); + public static final ColorObject TEXT_DEFAULT_BACKGROUND = new ColorObject(80, 80, 80, 144); + public static final ColorObject WINDOW_DEFAULT_BACKGROUND = new ColorObject(64, 64, 64, 128); + public static final ColorObject POPUP_DEFAULT_BACKGROUND = new ColorObject(85, 85, 85); + public static final ColorObject DESTRUCTION = new ColorObject(255, 0, 0, 128); + + int rgba; + + public ColorObject(int rgba) + { + this.rgba = rgba; + } + + public ColorObject(int r, int g, int b) + { + rgba = A | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); + } + + public ColorObject(int r, int g, int b, int a) + { + rgba = ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); + } + + public static ColorObject rgb(int rgb) + { + return new ColorObject(rgb | (255 << 24)); + } + + public ColorObject copy() + { + return new ColorObject(rgba); + } + + public ColorObject setRGB(int rgba) + { + this.rgba = rgba; + return this; + } + + public ColorObject setRed(int r) + { + rgba = rgba & ~R | ((r & 0xFF) << 16); + return this; + } + + public ColorObject setGreen(int g) + { + rgba = rgba & ~G | ((g & 0xFF) << 8); + return this; + } + + public ColorObject setBlue(int b) + { + rgba = rgba & ~B | (b & 0xFF); + return this; + } + + public ColorObject setAlpha(int a) + { + rgba = rgba & ~A | ((a & 0xFF) << 24); + return this; + } + + public ColorObject setRed(float r) + { + return setRed((int)(r * 255F + 0.5F)); + } + + public ColorObject setGreen(float g) + { + return setGreen((int)(g * 255F + 0.5F)); + } + + public ColorObject setBlue(float b) + { + return setBlue((int)(b * 255F + 0.5F)); + } + + public ColorObject setAlpha(float a) + { + return setAlpha((int)(a * 255F + 0.5F)); + } + + public ColorObject setRGB(float hue, float brightness, float saturation, int alpha) + { + return setRGB(Color.HSBtoRGB(hue, saturation, brightness) | alpha << 24); + } + + public int getRGB() + { + return rgba; + } + + public int getRed() + { + return (rgba >> 16) & 0xFF; + } + + public int getGreen() + { + return (rgba >> 8) & 0xFF; + } + + public int getBlue() + { + return rgba & 0xFF; + } + + public int getAlpha() + { + return (rgba >> 24) & 0xFF; + } + + public float getRedFloat() + { + return ((rgba >> 16) & 0xFF) * DEVIDER; + } + + public float getGreenFloat() + { + return ((rgba >> 8) & 0xFF) * DEVIDER; + } + + public float getBlueFloat() + { + return (rgba & 0xFF) * DEVIDER; + } + + public float getAlphaFloat() + { + return ((rgba >> 24) & 0xFF) * DEVIDER; + } + + public String getHexCode(boolean alpha) + { + return "0x"+(alpha ? Long.toHexString(1 << 32 | rgba & SIGN) : Integer.toHexString((1 << 24) | (rgba & ~A))).substring(1); + } + + public String getHTMLCode(boolean alpha) + { + return "#"+(alpha ? Long.toHexString(1 << 32 | rgba & SIGN) : Integer.toHexString((1 << 24) | (rgba & ~A))).substring(1); + } + + public boolean needsDarkColor() + { + return getBrightness() >= 130; + } + + public int getBrightness() + { + return getBrightness(rgba); + } + + public Color toColor() + { + return new Color(rgba); + } + + public float[] toHue() + { + return Color.RGBtoHSB(getRed(), getGreen(), getBlue(), new float[3]); + } + + public ColorObject brighter() + { + return brighter(0.7F); + } + + public ColorObject brighter(float factor) + { + rgba = brighter(rgba, factor); + return this; + } + + public ColorObject darker() + { + return darker(0.7F); + } + + public ColorObject darker(float factor) + { + rgba = darker(rgba, factor); + return this; + } + + public ColorObject mix(ColorObject to, float factor) + { + rgba = mix(rgba, to.rgba, factor); + return this; + } + + public ColorObject mix(ColorObject from, ColorObject to, float factor) + { + rgba = mix(from.rgba, to.rgba, factor); + return this; + } + + public ColorObject write(FloatBuffer buffer, boolean alpha) + { + buffer.put(getRedFloat()).put(getGreenFloat()).put(getBlueFloat()); + if(alpha) + { + buffer.put(getAlphaFloat()); + } + return this; + } + + public ColorObject read(FloatBuffer buffer, boolean alpha) + { + setRed(buffer.get()).setGreen(buffer.get()).setBlue(buffer.get()); + if(alpha) + { + setAlpha(buffer.get()); + } + return this; + } + + public ColorObject write(ByteBuffer buffer, boolean alpha) + { + pack(rgba, alpha, buffer); + return this; + } + + public ColorObject read(ByteBuffer buffer, boolean alpha) + { + setRed(buffer.get()).setGreen(buffer.get()).setBlue(buffer.get()); + if(alpha) + { + setAlpha(buffer.get()); + } + return this; + } + + public ColorObject write(FloatList buffer, boolean alpha) + { + buffer.add(getRedFloat()); + buffer.add(getGreenFloat()); + buffer.add(getBlueFloat()); + if(alpha) + { + buffer.add(getAlphaFloat()); + } + return this; + } + + public ColorObject writeFloat(ByteBuffer buffer, boolean alpha) + { + buffer.putFloat(getRedFloat()).putFloat(getGreenFloat()).putFloat(getBlueFloat()); + if(alpha) + { + buffer.putFloat(getAlphaFloat()); + } + return this; + } + + @Override + public int hashCode() + { + return rgba; + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof ColorObject) + { + return ((ColorObject)obj).rgba == rgba; + } + return false; + } + + @Override + public String toString() + { + return "Color[r=" + getRed() + ", g=" + getGreen() + ", b=" + getBlue() + ", a=" + getAlpha() + "]"; + } + + public String getFloatColor() + { + return "Color[r=" + getRedFloat() + ", g=" + getGreenFloat() + ", b=" + getBlueFloat() + ", a=" + getAlphaFloat() + "]"; + } + + public static void pack(int color, boolean alpha, ByteBuffer buffer) + { + buffer.put((byte)((color >> 16) & 0xFF)).put((byte)((color >> 8) & 0xFF)).put((byte)(color & 0xFF)); + if(alpha) + { + buffer.put((byte)((color >> 24) & 0xFF)); + } + } + + public static void packFloat(int color, boolean alpha, FloatBuffer buffer) + { + buffer.put(((color >> 16) & 0xFF) * DEVIDER).put(((color >> 8) & 0xFF) * DEVIDER).put((color & 0xFF) * DEVIDER); + if(alpha) + { + buffer.put(((color >> 24) & 0xFF) * DEVIDER); + } + } + + public static int getBrightness(int rgba) + { + return getBrightness((rgba >> 16) & 0xFF, (rgba >> 8) & 0xFF, rgba & 0xFF); + } + + public static int getBrightness(int r, int g, int b) + { + return (int)Math.sqrt((r * r * 0.241F) + (g * g * 0.691F) + (b * b * 0.068F)); + } + + public static int mix(int from, int to, float factor) + { + float weight0 = (1F - factor); + float weight1 = factor; + int r = (int)((((from >> 16) & 0xFF) * weight0) + (((to >> 16) & 0xFF) * weight1)); + int g = (int)((((from >> 8) & 0xFF) * weight0) + (((to >> 8) & 0xFF) * weight1)); + int b = (int)(((from & 0xFF) * weight0) + ((to & 0xFF) * weight1)); + int a = (int)((((from >> 24) & 0xFF) * weight0) + (((to >> 24) & 0xFF) * weight1)); + return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | b & 0xFF; + } + + public static int darker(int color, float factor) + { + int r = Math.max(0, (int)(((color >> 16) & 0xFF) * factor)); + int g = Math.max(0, (int)(((color >> 8) & 0xFF) * factor)); + int b = Math.max(0, (int)((color & 0xFF) * factor)); + return (color & A) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); + } + + public static int brighter(int color, float factor) + { + int r = (color >> 16) & 0xFF; + int g = (color >> 8) & 0xFF; + int b = color & 0xFF; + int i = (int)(1.0 / (1.0 - factor)); + if(r == 0 && g == 0 && b == 0) + { + return (color & A) | ((i & 0xFF) << 16) | ((i & 0xFF) << 8) | (i & 0xFF); + } + if(r > 0 && r < i) r = i; + if(g > 0 && g < i) g = i; + if(b > 0 && b < i) b = i; + return (color & A) | Math.min(255, (int)(r / factor)) << 16 | Math.min(255, (int)(g / factor)) << 8 | Math.min(255, (int)(b / factor)); + } +} diff --git a/src/main/java/speiger/src/coreengine/math/misc/Facing.java b/src/main/java/speiger/src/coreengine/math/misc/Facing.java new file mode 100644 index 0000000..332b74f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/misc/Facing.java @@ -0,0 +1,194 @@ +package speiger.src.coreengine.math.misc; + +import java.util.function.Predicate; + +import speiger.src.coreengine.math.vector.ints.Vec2i; + +public enum Facing +{ + NORTH(0, 2, "North", Axis.VERTICAL, Vec2i.newVec(0, 1)), + EAST(1, 3, "East", Axis.HORIZONTAL, Vec2i.newVec(1, 0)), + SOUTH(2, 0, "South", Axis.VERTICAL, Vec2i.newVec(0, -1)), + WEST(3, 1, "West", Axis.HORIZONTAL, Vec2i.newVec(-1, 0)); + + private static final Facing[] VALUES; + private static final Facing[] ROTATIONS; + + final int index; + final int rotationIndex; + final String name; + final Axis axis; + final Vec2i offset; + final boolean positive; + + private Facing(int direction, int rotation, String display, Axis axis, Vec2i offset) + { + index = direction; + rotationIndex = rotation; + name = display; + this.axis = axis; + this.offset = offset; + positive = index < 2; + } + + public int getIndex() + { + return index; + } + + public boolean isXAxis() + { + return axis == Axis.HORIZONTAL; + } + + public boolean isZAxis() + { + return axis == Axis.VERTICAL; + } + + public Axis getAxis() + { + return axis; + } + + public boolean isPositive() + { + return positive; + } + + public Vec2i getOffset() + { + return offset; + } + + public float getMultiplier() + { + return positive ? 1F : -1F; + } + + public String getName() + { + return name; + } + + public int getRotationIndex() + { + return rotationIndex; + } + + public int getRotation() + { + return rotationIndex * 90; + } + + public int getRotation(Facing other) + { + if(other == backwards()) return getRotation() - 45; + else if(other == forward()) return getRotation() + 45; + return getRotation(); + } + + public Facing rotate(int amount) + { + return byIndex(index + amount); + } + + public Facing forward() + { + return byIndex(index + 1); + } + + public Facing backwards() + { + return byIndex(index - 1); + } + + public Facing opposite() + { + return byIndex(index + 2); + } + + @Override + public String toString() + { + return getName()+": "+offset; + } + + public static Facing byIndex(int index) + { + return VALUES[index & 3]; + } + + public static Facing byRotationIndex(int index) + { + return ROTATIONS[index & 3]; + } + + public static Facing byYaw(float value) + { + return byRotationIndex((int)(value / 90) & 3); + } + + static + { + Facing[] values = values(); + VALUES = new Facing[values.length]; + ROTATIONS = new Facing[values.length]; + for(Facing entry : values) + { + VALUES[entry.getIndex()] = entry; + ROTATIONS[entry.getRotationIndex()] = entry; + } + } + + public static enum Rotation + { + NONE(Facing.NORTH), + CLOCKWISE(Facing.EAST), + OPPOSITE(Facing.SOUTH), + COUNTER_CLOCKWISE(Facing.WEST); + + static final Rotation[] ROTATION = Rotation.values(); + Facing facing; + + private Rotation(Facing facing) + { + this.facing = facing; + } + + public static Rotation fromFacing(Facing facing) + { + return ROTATION[facing.getIndex()]; + } + + public Facing toFacing() + { + return facing; + } + } + + public static enum Axis implements Predicate + { + HORIZONTAL(5), + VERTICAL(10); + + int code; + + private Axis(int code) + { + this.code = code; + } + + public int getCode() + { + return code; + } + + @Override + public boolean test(Facing t) + { + return t.getAxis() == this; + } + } + +} diff --git a/src/main/java/speiger/src/coreengine/math/misc/FacingList.java b/src/main/java/speiger/src/coreengine/math/misc/FacingList.java new file mode 100644 index 0000000..b163d12 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/misc/FacingList.java @@ -0,0 +1,312 @@ +package speiger.src.coreengine.math.misc; + +import java.util.EnumSet; +import java.util.Iterator; +import java.util.Set; +import java.util.StringJoiner; +import java.util.function.Predicate; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.lists.ObjectList; +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.misc.Facing.Axis; +import speiger.src.coreengine.math.vector.ints.Vec2i; + +public final class FacingList implements Iterable, Predicate +{ + static final FacingList[] FACINGS = createArray(); + public static final FacingList EMPTY = ofNumber(0); + public static final FacingList NORTH = ofFacings(Facing.NORTH); + public static final FacingList EAST = ofFacings(Facing.EAST); + public static final FacingList SOUTH = ofFacings(Facing.SOUTH); + public static final FacingList WEST = ofFacings(Facing.WEST); + + public static final FacingList NORTH_EAST = ofFacings(Facing.NORTH, Facing.EAST); + public static final FacingList SOUTH_EAST = ofFacings(Facing.EAST, Facing.SOUTH); + public static final FacingList SOUTH_WEST = ofFacings(Facing.SOUTH, Facing.WEST); + public static final FacingList NORTH_WEST = ofFacings(Facing.WEST, Facing.NORTH); + + public static final FacingList VERTICAL = ofFacings(Facing.NORTH, Facing.SOUTH); + public static final FacingList HORIZONTAL = ofFacings(Facing.EAST, Facing.WEST); + public static final FacingList ALL = ofFacings(Facing.NORTH, Facing.SOUTH, Facing.EAST, Facing.WEST); + + final byte code; + final byte next; + final byte opposite; + final byte prev; + final byte count; + final Vec2i offset; + final Facing[] array; + + private FacingList(int initCode) + { + code = (byte)MathUtils.clamp(0, 15, initCode); + Vec2i pos = Vec2i.newMutable(); + ObjectList facings = new ObjectArrayList(); + for(int i = 0;i<4;i++) + { + if((code & 1 << i) != 0) + { + pos.add(Facing.byIndex(i).getOffset()); + facings.add(Facing.byIndex(i)); + } + } + int[] data = new int[3]; + for(int i = 0,m=facings.size();i toFacings() + { + return isEmpty() ? EnumSet.noneOf(Facing.class) : EnumSet.copyOf(ObjectArrayList.wrap(array)); + } + + public boolean[] toFlags() + { + return toFlags(array); + } + + public int getRotation() + { + switch(count) + { + case 1: return array[0].getRotation(); + case 2: return array[0].getRotation(array[1]); + default: return 0; + } + } + + public FacingList rotate(int amount) + { + switch(amount & 3) + { + case 1: return FACINGS[next]; + case 2: return FACINGS[opposite]; + case 3: return FACINGS[prev]; + default: return this; + } + } + + public FacingList invert() + { + return FACINGS[15 - code]; + } + + public FacingList opposite() + { + return FACINGS[opposite]; + } + + public FacingList add(Facing facing) + { + return FACINGS[code | (1 << facing.getIndex())]; + } + + public FacingList add(FacingList facings) + { + return FACINGS[code | facings.code]; + } + + public FacingList remove(Facing facing) + { + return FACINGS[code & ~(1 << facing.getIndex())]; + } + + public FacingList remove(FacingList facings) + { + return FACINGS[code & ~facings.code]; + } + + public boolean contains(Facing direction) + { + return (code & 1 << direction.getIndex()) != 0; + } + + public boolean contains(FacingList facings) + { + return (code & facings.code) == facings.code; + } + + public boolean containsAny(FacingList facings) + { + return (code & facings.code) != 0; + } + + public boolean notContains(Facing direction) + { + return (code & 1 << direction.getIndex()) == 0; + } + + public boolean notContains(FacingList facings) + { + return (code & facings.code) != facings.code; + } + + public FacingList flipFacing(Facing facing) + { + return contains(facing) ? remove(facing).add(facing.opposite()) : this; + } + + public FacingList flipAxis(Axis axis) + { + FacingList result = this; + for(int i = 0,m=array.length;i iterator() + { + return new Iterator(){ + int index = 0; + @Override + public boolean hasNext() + { + return index < size(); + } + + @Override + public Facing next() + { + return array[index++]; + } + }; + } + + public static int toNumber(Facing...facings) + { + int value = 0; + for(int i = 0,m=facings.length;i= start + duration) + { + onFinished(); + } + return value; + } + + @Override + public float get() + { + return value; + } + + @Override + public boolean isDone() + { + return done > 0; + } + + protected abstract float calculateProgress(float time); + + protected void onFinished() + { + progress = start; + done++; + } + + protected void onInitialValueSet() + { + flags |= 1; + } + + protected void finishProgress() + { + flags |= 2; + done++; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/Vec.java b/src/main/java/speiger/src/coreengine/math/vector/Vec.java new file mode 100644 index 0000000..e3683f6 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/Vec.java @@ -0,0 +1,26 @@ +package speiger.src.coreengine.math.vector; + +import java.nio.ByteBuffer; + +public interface Vec +{ + public static final int X = 1; + public static final int Y = 2; + public static final int Z = 4; + public static final int W = 8; + public static final int ALL = X | Y | Z | W; + + public Vec copy(); + public Vec abs(); + public Vec negate(); + public Vec invert(); + + public Vec store(ByteBuffer buffer); + public Vec load(ByteBuffer buffer); + + public boolean isMutable(); + public Vec asMutable(); + public Vec copyAsMutable(); + public Vec asImmutable(); + public Vec copyAsImmutable(); +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/VectorUtil.java b/src/main/java/speiger/src/coreengine/math/vector/VectorUtil.java new file mode 100644 index 0000000..6ecd435 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/VectorUtil.java @@ -0,0 +1,21 @@ +package speiger.src.coreengine.math.vector; + +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.floats.Vec3f; + +public class VectorUtil +{ + public static float barryCentric(Vec3f p1, Vec3f p2, Vec3f p3, Vec2f pos) + { + float det = (p2.getZ() - p3.getZ()) * (p1.getX() - p3.getX()) + (p3.getX() - p2.getX()) * (p1.getZ() - p3.getZ()); + float l1 = ((p2.getZ() - p3.getZ()) * (pos.getX() - p3.getX()) + (p3.getX() - p2.getX()) * (pos.getY() - p3.getZ())) / det; + float l2 = ((p3.getZ() - p1.getZ()) * (pos.getX() - p3.getX()) + (p1.getX() - p3.getX()) * (pos.getY() - p3.getZ())) / det; + float l3 = 1.0f - l1 - l2; + return l1 * p1.getY() + l2 * p2.getY() + l3 * p3.getY(); + } + + public static float fma(float x, float y, float z) + { + return x * y + z; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2b.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2b.java new file mode 100644 index 0000000..dc33ecb --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2b.java @@ -0,0 +1,149 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.nio.ByteBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.doubles.Vec2d; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.math.vector.longs.Vec2l; +import speiger.src.coreengine.math.vector.shorts.Vec2s; + + +public interface Vec2b extends Vecb +{ + public static final Vec2b ZERO = newVec(); + public static final Vec2b MINUS_ONE = newVec((byte)-1); + public static final Vec2b ONE = newVec((byte)1); + + public static Vec2b newMutable(){return new Vec2bMutable();} + public static Vec2b newMutable(byte value){return new Vec2bMutable(value);} + public static Vec2b newMutable(byte x, byte y){return new Vec2bMutable(x, y);} + public static Vec2b newMutable(Vec2b value){return newMutable(value.getX(), value.getY());} + + public static Vec2b newVec(){return new Vec2bImmutable();} + public static Vec2b newVec(byte value){return new Vec2bImmutable(value);} + public static Vec2b newVec(byte x, byte y){return new Vec2bImmutable(x, y);} + public static Vec2b newVec(Vec2b value){return newVec(value.getX(), value.getY());} + + public byte getX(); + public byte getY(); + public Vec2b setX(byte x); + public Vec2b setY(byte y); + @Override + public default byte[] asArray(){return new byte[]{getX(), getY()};} + @Override + public Vec2b copy(); + @Override + public default Vec2b abs(){return set((byte)Math.abs(getX()), (byte)Math.abs(getY()));} + @Override + public default Vec2b negate(){return set((byte)0, (byte)0);} + @Override + public default Vec2b invert(){return set((byte)(-getX()), (byte)(-getY()));}; + + @Override + public default Vec2b add(byte value) {return add(value, value);} + public default Vec2b add(Vec2b value) {return add(value.getX(), value.getY());} + public default Vec2b add(byte x, byte y) {return set((byte)(x + getX()), (byte)(y + getY()));} + + @Override + public default Vec2b sub(byte value){return sub(value, value);} + public default Vec2b sub(Vec2b value){return sub(value.getX(), value.getY());} + public default Vec2b sub(byte x, byte y) {return set((byte)(getX() - x), (byte)(getY() - y));} + + @Override + public default Vec2b multiply(byte value){return multiply(value, value);} + public default Vec2b multiply(Vec2b value){return multiply(value.getX(), value.getY());} + public default Vec2b multiply(byte x, byte y) {return set((byte)(x * getX()), (byte)(y * getY()));} + + @Override + public default Vec2b devide(byte value){return devide(value, value);} + public default Vec2b devide(Vec2b value){return devide(value.getX(), value.getY());} + public default Vec2b devide(byte x, byte y){return set((byte)(getX() / x), (byte)(getY() / y));} + + @Override + public default Vec2b set(byte value){return set(value, value);}; + public default Vec2b set(Vec2b value){return set(value.getX(), value.getY());} + public Vec2b set(byte x, byte y); + + public default double distanceTo(Vec2b value){return distanceTo(value.getX(), value.getY());} + public default double distanceTo(byte x, byte y){return Math.sqrt(distanceToSquared(x, y));} + + public default long distanceToSquared(Vec2b value){return distanceToSquared(value.getX(), value.getY());} + public default long distanceToSquared(byte x, byte y) + { + long xPos = getX() - x; + long yPos = getY() - y; + return (xPos * xPos) + (yPos * yPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY());} + + public default long dotProduct(Vec2b value){return dotProduct(value.getX(), value.getY());} + public default long dotProduct(byte x, byte y){return (getX() * x) + (getY() * y);} + + public default Vec2b rotate(byte angle, Vec2b center){return rotate(angle, center.getX(), center.getY());} + public default Vec2b rotate(byte angle, byte x, byte y) + { + byte xPos = (byte)(getX() - x); + byte yPos = (byte)(getY() - y); + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + return set((byte)((xPos * cos) + (yPos * sin) + x), (byte)(-(xPos * sin) + (yPos * cos) + y)); + } + + public default Vec2b min(Vec2b other) {return min(other, this);} + public default Vec2b min(Vec2b other, Vec2b result){return min(other.getX(), other.getY(), result);} + public default Vec2b min(byte x, byte y) {return min(x, y, this);} + public default Vec2b min(byte x, byte y, Vec2b result){return result.set((byte)Math.min(getX(), x), (byte)Math.min(getY(), y));} + + public default Vec2b max(Vec2b other) {return max(other, this);} + public default Vec2b max(Vec2b other, Vec2b result){return max(other.getX(), other.getY(), result);} + public default Vec2b max(byte x, byte y) {return max(x, y, this);} + public default Vec2b max(byte x, byte y, Vec2b result){return result.set((byte)Math.max(getX(), x), (byte)Math.max(getY(), y));} + + public default Vec2b difference(Vec2b other) {return difference(other, this);} + public default Vec2b difference(Vec2b other, Vec2b result){return difference(other.getX(), other.getY(), result);} + public default Vec2b difference(byte x, byte y) {return difference(x, y, this);} + public default Vec2b difference(byte x, byte y, Vec2b result){return result.set((byte)(getX() - x), (byte)(getY() - y));} + + @Override + public default Vec2b clamp(byte min, byte max){return clamp(min, max, ALL);} + public default Vec2b clamp(byte min, byte max, Vec2b result){return clamp(min, max, result, ALL);} + @Override + public default Vec2b clamp(byte min, byte max, int filter){return clamp(min, max, this, filter);} + public default Vec2b clamp(byte min, byte max, Vec2b result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()));} + + @Override + public default Vec2b store(ByteBuffer buffer) + { + buffer.put(getX()).put(getY()); + return this; + } + + @Override + public default Vec2b load(ByteBuffer buffer) + { + return set(buffer.get(), buffer.get()); + } + + @Override + public default Vec2s asShort(){return isMutable() ? Vec2s.newMutable(getX(), getY()) : Vec2s.newVec(getX(), getY());} + @Override + public default Vec2i asInt(){return isMutable() ? Vec2i.newMutable(getX(), getY()) : Vec2i.newVec(getX(), getY());} + @Override + public default Vec2l asLong(){return isMutable() ? Vec2l.newMutable(getX(), getY()) : Vec2l.newVec(getX(), getY());} + @Override + public default Vec2f asFloat() {return isMutable() ? Vec2f.newMutable(getX(), getY()) : Vec2f.newVec(getX(), getY());} + @Override + public default Vec2d asDouble(){return isMutable() ? Vec2d.newMutable(getX(), getY()) : Vec2d.newVec(getX(), getY());} + + @Override + public default Vec2b asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec2b asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec2b copyAsMutable(){return newMutable(this);} + @Override + public default Vec2b copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2bImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2bImmutable.java new file mode 100644 index 0000000..2c2878b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2bImmutable.java @@ -0,0 +1,92 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.util.Objects; + +public class Vec2bImmutable implements Vec2b +{ + final byte x; + final byte y; + + public Vec2bImmutable() + { + x = 0; + y = 0; + } + + public Vec2bImmutable(byte value) + { + x = value; + y = value; + } + + public Vec2bImmutable(byte x, byte y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public byte getX() + { + return x; + } + + @Override + public byte getY() + { + return y; + } + + @Override + public Vec2b setX(byte x) + { + return this.x == x ? this : Vec2b.newVec(x, y); + } + + @Override + public Vec2b setY(byte y) + { + return this.y == y ? this : Vec2b.newVec(x, y); + } + + @Override + public Vec2b copy() + { + return Vec2b.newVec(this); + } + + @Override + public Vec2b set(byte x, byte y) + { + return this.x == x && this.y == y ? this : Vec2b.newVec(x, y); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2b) + { + Vec2b vec = (Vec2b)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2b[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2bMutable.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2bMutable.java new file mode 100644 index 0000000..ecdde7c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2bMutable.java @@ -0,0 +1,94 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.util.Objects; + +public class Vec2bMutable implements Vec2b +{ + byte x; + byte y; + + public Vec2bMutable() + { + } + + public Vec2bMutable(byte value) + { + x = value; + y = value; + } + + public Vec2bMutable(byte x, byte y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public byte getX() + { + return x; + } + + @Override + public byte getY() + { + return y; + } + + @Override + public Vec2b setX(byte x) + { + this.x = x; + return this; + } + + @Override + public Vec2b setY(byte y) + { + this.y = y; + return this; + } + + @Override + public Vec2b copy() + { + return Vec2b.newMutable(this); + } + + @Override + public Vec2b set(byte x, byte y) + { + this.x = x; + this.y = y; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2b) + { + Vec2b vec = (Vec2b)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2b[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3b.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3b.java new file mode 100644 index 0000000..cc35c14 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3b.java @@ -0,0 +1,141 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.nio.ByteBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.doubles.Vec3d; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.ints.Vec3i; +import speiger.src.coreengine.math.vector.longs.Vec3l; +import speiger.src.coreengine.math.vector.shorts.Vec3s; + +public interface Vec3b extends Vecb +{ + public static final Vec3b ZERO = newVec(); + public static final Vec3b MINUS_ONE = newVec((byte)-1); + public static final Vec3b ONE = newVec((byte)1); + + public static Vec3b newMutable(){return new Vec3bMutable();} + public static Vec3b newMutable(byte value){return new Vec3bMutable(value);} + public static Vec3b newMutable(byte x, byte y, byte z){return new Vec3bMutable(x, y, z);} + public static Vec3b newMutable(Vec3b vec){return newMutable(vec.getX(), vec.getY(), vec.getZ());} + + public static Vec3b newVec(){return new Vec3bImmutable();} + public static Vec3b newVec(byte value){return new Vec3bImmutable(value);} + public static Vec3b newVec(byte x, byte y, byte z){return new Vec3bImmutable(x, y, z);} + public static Vec3b newVec(Vec3b vec){return newVec(vec.getX(), vec.getY(), vec.getZ());} + + public byte getX(); + public byte getY(); + public byte getZ(); + public Vec3b setX(byte x); + public Vec3b setY(byte y); + public Vec3b setZ(byte z); + @Override + public default byte[] asArray(){return new byte[]{getX(), getY(), getZ()};} + + @Override + public Vec3b copy(); + @Override + public default Vec3b abs(){return set((byte)Math.abs(getX()), (byte)Math.abs(getY()), (byte)Math.abs(getZ()));} + @Override + public default Vec3b negate(){return set((byte)0, (byte)0, (byte)0);} + @Override + public default Vec3b invert(){return set((byte)-getX(), (byte)-getY(), (byte)-getZ());} + @Override + public default Vec3b add(byte value){return add(value, value, value);} + public default Vec3b add(Vec3b value){return add(value.getX(), value.getY(), value.getZ());} + public default Vec3b add(byte x, byte y, byte z){return set((byte)(getX() + x), (byte)(getY() + y), (byte)(getZ() + z));} + + @Override + public default Vec3b sub(byte value){return sub(value, value, value);} + public default Vec3b sub(Vec3b value){return sub(value.getX(), value.getY(), value.getZ());} + public default Vec3b sub(byte x, byte y, byte z){return set((byte)(getX() - x), (byte)(getY() - y), (byte)(getZ() - z));} + + @Override + public default Vec3b multiply(byte value){return multiply(value, value, value);} + public default Vec3b multiply(Vec3b value){return multiply(value.getX(), value.getY(), value.getZ());} + public default Vec3b multiply(byte x, byte y, byte z){return set((byte)(getX() * x), (byte)(getY() * y), (byte)(getZ() * z));} + + @Override + public default Vec3b devide(byte value){return devide(value, value, value);} + public default Vec3b devide(Vec3b value){return devide(value.getX(), value.getY(), value.getZ());} + public default Vec3b devide(byte x, byte y, byte z){return set((byte)(getX() / x), (byte)(getY() / y), (byte)(getZ() / z));} + + @Override + public default Vec3b set(byte value){return set(value, value, value);} + public default Vec3b set(Vec3b value){return set(value.getX(), value.getY(), value.getZ());} + public Vec3b set(byte x, byte y, byte z); + + public default double distanceTo(Vec3b value){return distanceTo(value.getX(), value.getY(), value.getZ());} + public default double distanceTo(byte x, byte y, byte z){return Math.sqrt(distanceToSquared(x, y, z));} + + public default long distanceToSquared(Vec3b value){return distanceToSquared(value.getX(), value.getY(), value.getZ());} + public default long distanceToSquared(byte x, byte y, byte z) + { + long xPos = getX() - x; + long yPos = getY() - y; + long zPos = getZ() - z; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ());} + + public default long dotProduct(Vec3b value){return dotProduct(value.getX(), value.getY(), value.getZ());} + public default long dotProduct(byte x, byte y, byte z){return (getX() * x) + (getY() * y) + (getZ() * z);} + + public default Vec3b min(Vec3b other) {return min(other, this);} + public default Vec3b min(Vec3b other, Vec3b result){return min(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3b min(byte x, byte y, byte z) {return min(x, y, z, this);} + public default Vec3b min(byte x, byte y, byte z, Vec3b result){return result.set((byte)Math.min(getX(), x), (byte)Math.min(getY(), y), (byte)Math.min(getZ(), z));} + + public default Vec3b max(Vec3b other) {return max(other, this);} + public default Vec3b max(Vec3b other, Vec3b result){return max(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3b max(byte x, byte y, byte z) {return max(x, y, z, this);} + public default Vec3b max(byte x, byte y, byte z, Vec3b result){return result.set((byte)Math.max(getX(), x), (byte)Math.max(getY(), y), (byte)Math.max(getZ(), z));} + + public default Vec3b difference(Vec3b other) {return difference(other, this);} + public default Vec3b difference(Vec3b other, Vec3b result){return difference(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3b difference(byte x, byte y, byte z) {return difference(x, y, z, this);} + public default Vec3b difference(byte x, byte y, byte z, Vec3b result){return result.set((byte)(getX() - x), (byte)(getY() - y), (byte)(getZ() - z));} + + @Override + public default Vec3b clamp(byte min, byte max){return clamp(min, max, ALL);} + public default Vec3b clamp(byte min, byte max, Vec3b result){return clamp(min, max, result, ALL);} + @Override + public default Vec3b clamp(byte min, byte max, int filter){return clamp(min, max, this, filter);} + public default Vec3b clamp(byte min, byte max, Vec3b result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()));} + + @Override + public default Vec3b store(ByteBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()); + return this; + } + + @Override + public default Vec3b load(ByteBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec3s asShort(){return isMutable() ? Vec3s.newMutable(getX(), getY(), getZ()) : Vec3s.newVec(getX(), getY(), getZ());} + @Override + public default Vec3i asInt(){return isMutable() ? Vec3i.newMutable(getX(), getY(), getZ()) : Vec3i.newVec(getX(), getY(), getZ());} + @Override + public default Vec3l asLong(){return isMutable() ? Vec3l.newMutable(getX(), getY(), getZ()) : Vec3l.newVec(getX(), getY(), getZ());} + @Override + public default Vec3f asFloat() {return isMutable() ? Vec3f.newMutable(getX(), getY(), getZ()) : Vec3f.newVec(getX(), getY(), getZ());} + @Override + public default Vec3d asDouble(){return isMutable() ? Vec3d.newMutable(getX(), getY(), getZ()) : Vec3d.newVec(getX(), getY(), getZ());} + + @Override + public default Vec3b asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec3b asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec3b copyAsMutable(){return newMutable(this);} + @Override + public default Vec3b copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3bImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3bImmutable.java new file mode 100644 index 0000000..5e33e4d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3bImmutable.java @@ -0,0 +1,108 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.util.Objects; + +public class Vec3bImmutable implements Vec3b +{ + final byte x; + final byte y; + final byte z; + + public Vec3bImmutable() + { + x = 0; + y = 0; + z = 0; + } + + public Vec3bImmutable(byte value) + { + x = value; + y = value; + z = value; + } + + public Vec3bImmutable(byte x, byte y, byte z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public byte getX() + { + return x; + } + + @Override + public byte getY() + { + return y; + } + + @Override + public byte getZ() + { + return z; + } + + @Override + public Vec3b setX(byte x) + { + return this.x == x ? this : Vec3b.newVec(x, y, z); + } + + @Override + public Vec3b setY(byte y) + { + return this.y == y ? this : Vec3b.newVec(x, y, z); + } + + @Override + public Vec3b setZ(byte z) + { + return this.z == z ? this : Vec3b.newVec(x, y, z); + } + + @Override + public Vec3b copy() + { + return Vec3b.newVec(this); + } + + @Override + public Vec3b set(byte x, byte y, byte z) + { + return this.x == x && this.y == y && this.z == z ? this : Vec3b.newVec(x, y, z); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3b) + { + Vec3b vec = (Vec3b)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3b[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3bMutable.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3bMutable.java new file mode 100644 index 0000000..2c59e64 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3bMutable.java @@ -0,0 +1,111 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.util.Objects; + +public class Vec3bMutable implements Vec3b +{ + byte x; + byte y; + byte z; + + public Vec3bMutable() + { + } + + public Vec3bMutable(byte value) + { + x = value; + y = value; + z = value; + } + + public Vec3bMutable(byte x, byte y, byte z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public byte getX() + { + return x; + } + + @Override + public byte getY() + { + return y; + } + + @Override + public byte getZ() + { + return z; + } + + @Override + public Vec3b setX(byte x) + { + this.x = x; + return this; + } + + @Override + public Vec3b setY(byte y) + { + this.y = y; + return this; + } + + @Override + public Vec3b setZ(byte z) + { + this.z = z; + return this; + } + + @Override + public Vec3b copy() + { + return Vec3b.newMutable(this); + } + + @Override + public Vec3b set(byte x, byte y, byte z) + { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3b) + { + Vec3b vec = (Vec3b)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3b[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4b.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4b.java new file mode 100644 index 0000000..0355c70 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4b.java @@ -0,0 +1,145 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.nio.ByteBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.doubles.Vec4d; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.ints.Vec4i; +import speiger.src.coreengine.math.vector.longs.Vec4l; +import speiger.src.coreengine.math.vector.shorts.Vec4s; + +public interface Vec4b extends Vecb +{ + public static final Vec4b ZERO = newVec(); + public static final Vec4b MINUS_ONE = newVec((byte)-1); + public static final Vec4b ONE = newVec((byte)1); + + public static Vec4b newMutable(){return new Vec4bMutable();} + public static Vec4b newMutable(byte value){return new Vec4bMutable(value);} + public static Vec4b newMutable(byte x, byte y, byte z, byte w){return new Vec4bMutable(x, y, z, w);} + public static Vec4b newMutable(Vec4b vec){return newMutable(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public static Vec4b newVec(){return new Vec4bImmutable();} + public static Vec4b newVec(byte value){return new Vec4bImmutable(value);} + public static Vec4b newVec(byte x, byte y, byte z, byte w){return new Vec4bImmutable(x, y, z, w);} + public static Vec4b newVec(Vec4b vec){return newVec(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public byte getX(); + public byte getY(); + public byte getZ(); + public byte getW(); + public Vec4b setX(byte x); + public Vec4b setY(byte y); + public Vec4b setZ(byte z); + public Vec4b setW(byte w); + @Override + public default byte[] asArray(){return new byte[]{getX(), getY(), getZ(), getW()};} + @Override + public Vec4b copy(); + @Override + public default Vec4b abs(){return set((byte)Math.abs(getX()), (byte)Math.abs(getY()), (byte)Math.abs(getZ()), (byte)Math.abs(getW()));} + @Override + public default Vec4b negate(){return set((byte)0, (byte)0, (byte)0, (byte)0);} + @Override + public default Vec4b invert(){return set((byte)-getX(), (byte)-getY(), (byte)-getZ(), (byte)-getW());} + + @Override + public default Vec4b add(byte value){return add(value, value, value, value);} + public default Vec4b add(Vec4b value){return add(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4b add(byte x, byte y, byte z, byte w){return set((byte)(getX() + x), (byte)(getY() + y), (byte)(getZ() + z), (byte)(getW() + w));} + + @Override + public default Vec4b sub(byte value){return sub(value, value, value, value);} + public default Vec4b sub(Vec4b value){return sub(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4b sub(byte x, byte y, byte z, byte w){return set((byte)(getX() - x), (byte)(getY() - y), (byte)(getZ() - z), (byte)(getW() - w));} + + @Override + public default Vec4b multiply(byte value){return multiply(value, value, value, value);} + public default Vec4b multiply(Vec4b value){return multiply(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4b multiply(byte x, byte y, byte z, byte w){return set((byte)(getX() * x), (byte)(getY() * y), (byte)(getZ() * z), (byte)(getW() * w));} + + @Override + public default Vec4b devide(byte value){return devide(value, value, value, value);} + public default Vec4b devide(Vec4b value){return devide(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4b devide(byte x, byte y, byte z, byte w){return set((byte)(getX() / x), (byte)(getY() / y), (byte)(getZ() / z), (byte)(getW() / w));} + + @Override + public default Vec4b set(byte value){return set(value, value, value, value);} + public default Vec4b set(Vec4b value){return set(value.getX(), value.getY(), value.getZ(), value.getW());} + public Vec4b set(byte x, byte y, byte z, byte w); + + public default double distanceTo(Vec4b value){return distanceTo(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double distanceTo(byte x, byte y, byte z, byte w){return Math.sqrt(distanceTo(x, y, z, w));} + + public default long distanceToSquared(Vec4b value){return distanceToSquared(value.getX(), value.getY(), value.getZ(), value.getW());} + public default long distanceToSquared(byte x, byte y, byte z, byte w) + { + long xPos = getX() - x; + long yPos = getY() - y; + long zPos = getZ() - z; + long wPos = getW() - w; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos) + (wPos * wPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()) + (getW() * getW());} + + public default long dotProduct(Vec4b value){return dotProduct(value.getX(), value.getY(), value.getZ(), value.getW());} + public default long dotProduct(byte x, byte y, byte z, byte w){return (getX() * x) + (getY() * y) + (getZ() * z) + (getW() * w);}; + + public default Vec4b min(Vec4b other) {return min(other, this);} + public default Vec4b min(Vec4b other, Vec4b result){return min(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4b min(byte x, byte y, byte z, byte w) {return min(x, y, z, w, this);} + public default Vec4b min(byte x, byte y, byte z, byte w, Vec4b result){return result.set((byte)Math.min(getX(), x), (byte)Math.min(getY(), y), (byte)Math.min(getZ(), z), (byte)Math.min(getW(), w));} + + public default Vec4b max(Vec4b other) {return max(other, this);} + public default Vec4b max(Vec4b other, Vec4b result){return max(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4b max(byte x, byte y, byte z, byte w) {return max(x, y, z, w, this);} + public default Vec4b max(byte x, byte y, byte z, byte w, Vec4b result){return result.set((byte)Math.max(getX(), x), (byte)Math.max(getY(), y), (byte)Math.max(getZ(), z), (byte)Math.max(getZ(), z));} + + public default Vec4b difference(Vec4b other) {return difference(other, this);} + public default Vec4b difference(Vec4b other, Vec4b result){return difference(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4b difference(byte x, byte y, byte z, byte w) {return difference(x, y, z, w, this);} + public default Vec4b difference(byte x, byte y, byte z, byte w, Vec4b result){return result.set((byte)(getX() - x), (byte)(getY() - y), (byte)(getZ() - z), (byte)(getW() - w));} + + @Override + public default Vec4b clamp(byte min, byte max){return clamp(min, max, ALL);} + public default Vec4b clamp(byte min, byte max, Vec4b result){return clamp(min, max, result, ALL);} + @Override + public default Vec4b clamp(byte min, byte max, int filter){return clamp(min, max, this, filter);} + public default Vec4b clamp(byte min, byte max, Vec4b result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()), (filter & W) == 0 ? getW() : MathUtils.clamp(min, max, getW()));} + + @Override + public default Vec4b store(ByteBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()).put(getW()); + return this; + } + + @Override + public default Vec4b load(ByteBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec4s asShort(){return isMutable() ? Vec4s.newMutable(getX(), getY(), getZ(), getW()) : Vec4s.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4i asInt(){return isMutable() ? Vec4i.newMutable(getX(), getY(), getZ(), getW()) : Vec4i.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4l asLong(){return isMutable() ? Vec4l.newMutable(getX(), getY(), getZ(), getW()) : Vec4l.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4f asFloat() {return isMutable() ? Vec4f.newMutable(getX(), getY(), getZ(), getW()) : Vec4f.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4d asDouble(){return isMutable() ? Vec4d.newMutable(getX(), getY(), getZ(), getW()) : Vec4d.newVec(getX(), getY(), getZ(), getW());} + + + @Override + public default Vec4b asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec4b asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec4b copyAsMutable(){return newMutable(this);} + @Override + public default Vec4b copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4bImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4bImmutable.java new file mode 100644 index 0000000..040bde5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4bImmutable.java @@ -0,0 +1,124 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.util.Objects; + +public class Vec4bImmutable implements Vec4b +{ + final byte x; + final byte y; + final byte z; + final byte w; + + public Vec4bImmutable() + { + x = 0; + y = 0; + z = 0; + w = 0; + } + + public Vec4bImmutable(byte value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4bImmutable(byte x, byte y, byte z, byte w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public byte getX() + { + return x; + } + + @Override + public byte getY() + { + return y; + } + + @Override + public byte getZ() + { + return z; + } + + @Override + public byte getW() + { + return w; + } + + @Override + public Vec4b setX(byte x) + { + return this.x == x ? this : Vec4b.newVec(x, y, z, w); + } + + @Override + public Vec4b setY(byte y) + { + return this.y == y ? this : Vec4b.newVec(x, y, z, w); + } + + @Override + public Vec4b setZ(byte z) + { + return this.z == z ? this : Vec4b.newVec(x, y, z, w); + } + + @Override + public Vec4b setW(byte w) + { + return this.w == w ? this : Vec4b.newVec(x, y, z, w); + } + + @Override + public Vec4b copy() + { + return Vec4b.newVec(this); + } + + @Override + public Vec4b set(byte x, byte y, byte z, byte w) + { + return this.x == x && this.y == y && this.z == z && this.w == w ? this : Vec4b.newVec(x, y, z, w); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4b) + { + Vec4b vec = (Vec4b)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4b[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4bMutable.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4bMutable.java new file mode 100644 index 0000000..ca5c2fd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4bMutable.java @@ -0,0 +1,129 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.util.Objects; + +public class Vec4bMutable implements Vec4b +{ + byte x; + byte y; + byte z; + byte w; + + public Vec4bMutable() + { + } + + public Vec4bMutable(byte value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4bMutable(byte x, byte y, byte z, byte w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public byte getX() + { + return x; + } + + @Override + public byte getY() + { + return y; + } + + @Override + public byte getZ() + { + return z; + } + + @Override + public byte getW() + { + return w; + } + + @Override + public Vec4b setX(byte x) + { + this.x = x; + return this; + } + + @Override + public Vec4b setY(byte y) + { + this.y = y; + return this; + } + + @Override + public Vec4b setZ(byte z) + { + this.z = z; + return this; + } + + @Override + public Vec4b setW(byte w) + { + this.w = w; + return this; + } + + @Override + public Vec4b copy() + { + return Vec4b.newMutable(this); + } + + @Override + public Vec4b set(byte x, byte y, byte z, byte w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4b) + { + Vec4b vec = (Vec4b)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4b[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } + +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vecb.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vecb.java new file mode 100644 index 0000000..b4eb060 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vecb.java @@ -0,0 +1,29 @@ +package speiger.src.coreengine.math.vector.bytes; + +import speiger.src.coreengine.math.vector.Vec; +import speiger.src.coreengine.math.vector.doubles.Vecd; +import speiger.src.coreengine.math.vector.floats.Vecf; +import speiger.src.coreengine.math.vector.ints.Veci; +import speiger.src.coreengine.math.vector.longs.Vecl; +import speiger.src.coreengine.math.vector.shorts.Vecs; + +public interface Vecb extends Vec +{ + public Vecb set(byte value); + public Vecb add(byte value); + public Vecb sub(byte value); + public Vecb multiply(byte value); + public Vecb devide(byte value); + public Vecb clamp(byte min, byte max); + public Vecb clamp(byte min, byte max, int filter); + + public long lengthSquared(); + public default double length(){return Math.sqrt(lengthSquared());} + + public byte[] asArray(); + public Vecs asShort(); + public Veci asInt(); + public Vecl asLong(); + public Vecf asFloat(); + public Vecd asDouble(); +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2d.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2d.java new file mode 100644 index 0000000..9d2d5d2 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2d.java @@ -0,0 +1,187 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec2b; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.math.vector.longs.Vec2l; +import speiger.src.coreengine.math.vector.shorts.Vec2s; + +public interface Vec2d extends Vecd +{ + public static final Vec2d ZERO = newVec(); + public static final Vec2d MINUS_ONE = newVec(-1D); + public static final Vec2d ONE = newVec(1D); + + public static Vec2d newMutable(){return new Vec2dMutable();} + public static Vec2d newMutable(double value){return new Vec2dMutable(value);} + public static Vec2d newMutable(double x, double y){return new Vec2dMutable(x, y);} + public static Vec2d newMutable(Vec2d value){return newMutable(value.getX(), value.getY());} + + public static Vec2d newVec(){return new Vec2dImmutable();} + public static Vec2d newVec(double value){return new Vec2dImmutable(value);} + public static Vec2d newVec(double x, double y){return new Vec2dImmutable(x, y);} + public static Vec2d newVec(Vec2d value){return newVec(value.getX(), value.getY());} + + public double getX(); + public double getY(); + public Vec2d setX(double x); + public Vec2d setY(double y); + @Override + public default double[] asArray(){return new double[]{getX(), getY()};} + @Override + public Vec2d copy(); + @Override + public default Vec2d abs(){return set(Math.abs(getX()), Math.abs(getY()));} + @Override + public default Vec2d negate(){return set(0D, 0D);} + @Override + public default Vec2d invert(){return set(-getX(), -getY());}; + public default Vec2d normalize() + { + double l = length(); + return l == 0D ? this : multiply(1D / l); + } + + @Override + public default Vec2d add(double value) {return add(value, value);} + public default Vec2d add(Vec2d value) {return add(value.getX(), value.getY());} + public default Vec2d add(double x, double y) {return set(x + getX(), y + getY());} + + @Override + public default Vec2d sub(double value){return sub(value, value);} + public default Vec2d sub(Vec2d value){return sub(value.getX(), value.getY());} + public default Vec2d sub(double x, double y) {return set(getX() - x, getY() - y);} + + @Override + public default Vec2d multiply(double value){return multiply(value, value);} + public default Vec2d multiply(Vec2d value){return multiply(value.getX(), value.getY());} + public default Vec2d multiply(double x, double y) {return set(x * getX(), y * getY());} + + @Override + public default Vec2d devide(double value){return devide(value, value);} + public default Vec2d devide(Vec2d value){return devide(value.getX(), value.getY());} + public default Vec2d devide(double x, double y){return set(getX() / x, getY() / y);} + + @Override + public default Vec2d set(double value){return set(value, value);}; + public default Vec2d set(Vec2d value){return set(value.getX(), value.getY());} + public Vec2d set(double x, double y); + + public default double distanceTo(Vec2d value){return distanceTo(value.getX(), value.getY());} + public default double distanceTo(double x, double y){return Math.sqrt(distanceToSquared(x, y));} + + public default double distanceToSquared(Vec2d value){return distanceToSquared(value.getX(), value.getY());} + public default double distanceToSquared(double x, double y) + { + double xPos = getX() - x; + double yPos = getY() - y; + return (xPos * xPos) + (yPos * yPos); + } + @Override + public default double lengthSquared() {return (getX() * getX()) + (getY() * getY());} + + public default double dotProduct(Vec2d value){return dotProduct(value.getX(), value.getY());} + public default double dotProduct(double x, double y){return (getX() * x) + (getY() * y);} + + public default Vec2d lerp(Vec2d value, float progress, Vec2d result){return lerp(value.getX(), value.getY(), progress, result);} + public default Vec2d lerp(double x, double y, float progress, Vec2d result){return result.set(MathUtils.lerp(getX(), x, progress), MathUtils.lerp(getY(), y, progress));}; + + public default double angle(Vec2d value){return angle(value.getX(), value.getY());} + public default double angle(double x, double y){return (float)Math.atan2((getX() * y) - (getY() * x), (getX() * x) + (getY() * y));} + + public default double directionAngle(Vec2d value){return directionAngle(value.getX(), value.getY());} + public default double directionAngle(double x, double y){return (float)(-Math.toDegrees(Math.atan2(getX() - x, getY() - y)) % 360D);} + + public default Vec2d reflect(Vec2d value){return reflect(value.getX(), value.getY(), this);} + public default Vec2d reflect(double x, double y){return reflect(x, y, this);}; + public default Vec2d reflect(Vec2d value, Vec2d result){return reflect(value.getX(), value.getY(), result);} + public default Vec2d reflect(double x, double y, Vec2d result) + { + double dot = dotProduct(x, y); + double x2 = (float)((dot + dot) * x); + double y2 = (float)((dot + dot) * y); + return result.set(getX() - x2, getY() - y2); + } + + public default Vec2d rotate(double angle, Vec2d center){return rotate(angle, center.getX(), center.getY());} + public default Vec2d rotate(double angle, double x, double y) + { + double xPos = getX() - x; + double yPos = getY() - y; + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + return set((float)((xPos * cos) + (yPos * sin) + x), (float)(-(xPos * sin) + (yPos * cos) + y)); + } + + public default Vec2d min(Vec2d other) {return min(other, this);} + public default Vec2d min(Vec2d other, Vec2d result){return min(other.getX(), other.getY(), result);} + public default Vec2d min(double x, double y) {return min(x, y, this);} + public default Vec2d min(double x, double y, Vec2d result){return result.set(Math.min(getX(), x), Math.min(getY(), y));} + + public default Vec2d max(Vec2d other) {return max(other, this);} + public default Vec2d max(Vec2d other, Vec2d result){return max(other.getX(), other.getY(), result);} + public default Vec2d max(double x, double y) {return max(x, y, this);} + public default Vec2d max(double x, double y, Vec2d result){return result.set(Math.max(getX(), x), Math.max(getY(), y));} + + public default Vec2d difference(Vec2d other) {return difference(other, this);} + public default Vec2d difference(Vec2d other, Vec2d result){return difference(other.getX(), other.getY(), result);} + public default Vec2d difference(double x, double y) {return difference(x, y, this);} + public default Vec2d difference(double x, double y, Vec2d result){return result.set(getX() - x, getY() - y);} + + @Override + public default Vec2d clamp(double min, double max){return clamp(min, max, ALL);} + public default Vec2d clamp(double min, double max, Vec2d result){return clamp(min, max, result, ALL);} + @Override + public default Vec2d clamp(double min, double max, int filter){return clamp(min, max, this, filter);} + public default Vec2d clamp(double min, double max, Vec2d result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()));} + + @Override + public default Vec2d store(ByteBuffer buffer) + { + buffer.putDouble(getX()).putDouble(getY()); + return this; + } + + @Override + public default Vec2d load(ByteBuffer buffer) + { + return set(buffer.getDouble(), buffer.getDouble()); + } + + @Override + public default Vec2d store(DoubleBuffer buffer) + { + buffer.put(getX()).put(getY()); + return this; + } + + @Override + public default Vec2d load(DoubleBuffer buffer) + { + return set(buffer.get(), buffer.get()); + } + @Override + public default Vec2b asByte(){return isMutable() ? Vec2b.newMutable((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY())) : Vec2b.newVec((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()));} + @Override + public default Vec2s asShort(){return isMutable() ? Vec2s.newMutable((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY())) : Vec2s.newVec((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()));} + @Override + public default Vec2i asInt(){return isMutable() ? Vec2i.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY())) : Vec2i.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()));} + @Override + public default Vec2l asLong() {return isMutable() ? Vec2l.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY())) : Vec2l.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()));} + @Override + public default Vec2f asFloat(){return isMutable() ? Vec2f.newMutable((float)getX(), (float)getY()) : Vec2f.newVec((float)getX(), (float)getY());} + + + @Override + public default Vec2d asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec2d asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec2d copyAsMutable(){return newMutable(this);} + @Override + public default Vec2d copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2dImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2dImmutable.java new file mode 100644 index 0000000..210e961 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2dImmutable.java @@ -0,0 +1,92 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.util.Objects; + +public class Vec2dImmutable implements Vec2d +{ + final double x; + final double y; + + public Vec2dImmutable() + { + x = 0D; + y = 0D; + } + + public Vec2dImmutable(double value) + { + x = value; + y = value; + } + + public Vec2dImmutable(double x, double y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public double getX() + { + return x; + } + + @Override + public double getY() + { + return y; + } + + @Override + public Vec2d setX(double x) + { + return this.x == x ? this : Vec2d.newVec(x, y); + } + + @Override + public Vec2d setY(double y) + { + return this.y == y ? this : Vec2d.newVec(x, y); + } + + @Override + public Vec2d copy() + { + return Vec2d.newVec(this); + } + + @Override + public Vec2d set(double x, double y) + { + return this.x == x && this.y == y ? this : Vec2d.newVec(x, y); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2d) + { + Vec2d vec = (Vec2d)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2d[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2dMutable.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2dMutable.java new file mode 100644 index 0000000..0e2c107 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2dMutable.java @@ -0,0 +1,96 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.util.Objects; + +public class Vec2dMutable implements Vec2d +{ + double x; + double y; + + public Vec2dMutable() + { + x = 0D; + y = 0D; + } + + public Vec2dMutable(double value) + { + x = value; + y = value; + } + + public Vec2dMutable(double x, double y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public double getX() + { + return x; + } + + @Override + public double getY() + { + return y; + } + + @Override + public Vec2d setX(double x) + { + this.x = x; + return this; + } + + @Override + public Vec2d setY(double y) + { + this.y = y; + return this; + } + + @Override + public Vec2d copy() + { + return Vec2d.newMutable(this); + } + + @Override + public Vec2d set(double x, double y) + { + this.x = x; + this.y = y; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2d) + { + Vec2d vec = (Vec2d)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2d[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3d.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3d.java new file mode 100644 index 0000000..95c9652 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3d.java @@ -0,0 +1,241 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec3b; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.ints.Vec3i; +import speiger.src.coreengine.math.vector.longs.Vec3l; +import speiger.src.coreengine.math.vector.shorts.Vec3s; + +public interface Vec3d extends Vecd +{ + public static final Vec3d ZERO = newVec(); + public static final Vec3d MINUS_ONE = newVec(-1D); + public static final Vec3d ONE = newVec(1D); + + public static Vec3d newMutable(){return new Vec3dMutable();} + public static Vec3d newMutable(double value){return new Vec3dMutable(value);} + public static Vec3d newMutable(double x, double y, double z){return new Vec3dMutable(x, y, z);} + public static Vec3d newMutable(Vec3d vec){return newMutable(vec.getX(), vec.getY(), vec.getZ());} + + public static Vec3d newVec(){return new Vec3dImmutable();} + public static Vec3d newVec(double value){return new Vec3dImmutable(value);} + public static Vec3d newVec(double x, double y, double z){return new Vec3dImmutable(x, y, z);} + public static Vec3d newVec(Vec3d vec){return newVec(vec.getX(), vec.getY(), vec.getZ());} + + public double getX(); + public double getY(); + public double getZ(); + public Vec3d setX(double x); + public Vec3d setY(double y); + public Vec3d setZ(double z); + @Override + public default double[] asArray(){return new double[]{getX(), getY(), getZ()};} + + @Override + public Vec3d copy(); + @Override + public default Vec3d abs(){return set(Math.abs(getX()), Math.abs(getY()), Math.abs(getZ()));} + @Override + public default Vec3d negate(){return set(0D, 0D, 0D);} + @Override + public default Vec3d invert(){return set(-getX(), -getY(), -getZ());} + public default Vec3d normalize() + { + double l = length(); + return l == 0D ? this : multiply(1.0D / l); + } + @Override + public default Vec3d add(double value){return add(value, value, value);} + public default Vec3d add(Vec3d value){return add(value.getX(), value.getY(), value.getZ());} + public default Vec3d add(double x, double y, double z){return set(getX() + x, getY() + y, getZ() + z);} + + @Override + public default Vec3d sub(double value){return sub(value, value, value);} + public default Vec3d sub(Vec3d value){return sub(value.getX(), value.getY(), value.getZ());} + public default Vec3d sub(double x, double y, double z){return set(getX() - x, getY() - y, getZ() - z);} + + @Override + public default Vec3d multiply(double value){return multiply(value, value, value);} + public default Vec3d multiply(Vec3d value){return multiply(value.getX(), value.getY(), value.getZ());} + public default Vec3d multiply(double x, double y, double z){return set(getX() * x, getY() * y, getZ() * z);} + + @Override + public default Vec3d devide(double value){return devide(value, value, value);} + public default Vec3d devide(Vec3d value){return devide(value.getX(), value.getY(), value.getZ());} + public default Vec3d devide(double x, double y, double z){return set(getX() / x, getY() / y, getZ() / z);} + + @Override + public default Vec3d set(double value){return set(value, value, value);} + public default Vec3d set(Vec3d value){return set(value.getX(), value.getY(), value.getZ());} + public Vec3d set(double x, double y, double z); + + public default double distanceTo(Vec3d value){return distanceTo(value.getX(), value.getY(), value.getZ());} + public default double distanceTo(double x, double y, double z){return Math.sqrt(distanceToSquared(x, y, z));} + + public default double distanceToSquared(Vec3d value){return distanceToSquared(value.getX(), value.getY(), value.getZ());} + public default double distanceToSquared(double x, double y, double z) + { + double xPos = getX() - x; + double yPos = getY() - y; + double zPos = getZ() - z; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos); + } + @Override + public default double lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ());} + + public default double dotProduct(Vec3d value){return dotProduct(value.getX(), value.getY(), value.getZ());} + public default double dotProduct(double x, double y, double z){return (getX() * x) + (getY() * y) + (getZ() * z);} + + public default Vec3d lerp(Vec3d value, float progress, Vec3d result){return lerp(value.getX(), value.getY(), value.getZ(), progress, result);} + public default Vec3d lerp(double x, double y, double z, float progress, Vec3d result){return result.set(MathUtils.lerp(getX(), x, progress), MathUtils.lerp(getY(), y, progress), MathUtils.lerp(getZ(), z, progress));} + + public default double angle(Vec3d value){return angle(value.getX(), value.getY(), value.getZ());} + public default double angle(double x, double y, double z){return Math.acos(MathUtils.clamp(-1, 1, angleCos(x, y, z)));} + + public default double angleCos(Vec3d value){return angleCos(value.getX(), value.getY(), value.getZ());} + public default double angleCos(double x, double y, double z) + { + double myLength = (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()); + double otherLength = (x * x) + (y * y) + (z * z); + double dot = (getX() * x) + (getY() * y) + (getZ() * z); + return dot / (Math.sqrt(myLength * otherLength)); + } + + public default Vec3d crossProduct(Vec3d value){return crossProduct(value.getX(), value.getY(), value.getZ(), this);} + public default Vec3d crossProduct(Vec3d value, Vec3d result){return crossProduct(value.getX(), value.getY(), value.getZ(), result);} + public default Vec3d crossProduct(double x, double y, double z){return crossProduct(x, y, z, this);} + public default Vec3d crossProduct(double x, double y, double z, Vec3d result){return result.set((getY() * z) - (getZ() * y), (getZ() * x) - (getX() * z), (getX() * y) - (getY() * x));} + + public default Vec3d reflect(Vec3d value){return reflect(value.getX(), value.getY(), value.getZ(), this);} + public default Vec3d reflect(Vec3d value, Vec3d result){return reflect(value.getX(), value.getY(), value.getZ(), result);} + public default Vec3d reflect(double x, double y, double z){return reflect(x, y, z, this);} + public default Vec3d reflect(double x, double y, double z, Vec3d result) + { + double dot = dotProduct(x, y, z) * 2D; + return result.set(getX() - dot * x, getY() - dot * y, getZ() - dot * z); + } + + public default Vec3d rotate(double angle, Vec3d axis){return rotate(angle, axis.getX(), axis.getY(), axis.getZ());} + public default Vec3d rotate(double angle, Vec3d axis, Vec3d result){return rotate(angle, axis.getX(), axis.getY(), axis.getZ(), result);} + public default Vec3d rotate(double angle, double x, double y, double z){return rotate(angle, x, y, z, this);} + public default Vec3d rotate(double angle, double x, double y, double z, Vec3d result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double dot = dotProduct(x, y, z); + double xPos = x * dot * (1D - cos) + (getX() * cos) + (-z * getY() + y * getZ()) * sin; + double yPos = y * dot * (1D - cos) + (getY() * cos) + (z * getX() - x * getZ()) * sin; + double zPos = z * dot * (1D - cos) + (getZ() * cos) + (-y * getX() + x * getY()) * sin; + return result.set(xPos, yPos, zPos); + } + public default Vec3d rotateX(double angle){return rotateX(angle, this);} + public default Vec3d rotateX(double angle, Vec3d result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double y = getY() * cos + getZ() * sin; + double z = getZ() * cos - getY() * sin; + return result.set(getX(), y, z); + } + public default Vec3d rotateY(double angle){return rotateY(angle, this);} + public default Vec3d rotateY(double angle, Vec3d result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double x = getX() * cos + getZ() * sin; + double z = getZ() * cos - getX() * sin; + return result.set(x, getY(), z); + } + public default Vec3d rotateZ(double angle){return rotateZ(angle, this);} + public default Vec3d rotateZ(double angle, Vec3d result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double x = cos * getX() - sin * getY(); + double y = sin * getX() + cos * getY(); + return result.set(x, y, getZ()); + } + + public default Vec3d smoothStep(Vec3d value, float progress, Vec3d result){return smoothStep(value.getX(), value.getY(), value.getZ(), progress, result);} + public default Vec3d smoothStep(double x, double y, double z, float progress, Vec3d result) + { + double t2 = progress * progress; + double t3 = t2 * progress; + double xPos = ((getX() + getX() - x - x) * t3 + (3.0D * x - 3.0D * getX()) * t2 + getX() * progress + getX()); + double yPos = ((getY() + getY() - y - y) * t3 + (3.0D * y - 3.0D * getY()) * t2 + getY() * progress + getY()); + double zPos = ((getZ() + getZ() - z - z) * t3 + (3.0D * z - 3.0D * getZ()) * t2 + getZ() * progress + getZ()); + return result.set(xPos, yPos, zPos); + } + + public default Vec3d min(Vec3d other) {return min(other, this);} + public default Vec3d min(Vec3d other, Vec3d result){return min(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3d min(double x, double y, double z) {return min(x, y, z, this);} + public default Vec3d min(double x, double y, double z, Vec3d result){return result.set(Math.min(getX(), x), Math.min(getY(), y), Math.min(getZ(), z));} + + public default Vec3d max(Vec3d other) {return max(other, this);} + public default Vec3d max(Vec3d other, Vec3d result){return max(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3d max(double x, double y, double z) {return max(x, y, z, this);} + public default Vec3d max(double x, double y, double z, Vec3d result){return result.set(Math.max(getX(), x), Math.max(getY(), y), Math.max(getZ(), z));} + + public default Vec3d difference(Vec3d other) {return difference(other, this);} + public default Vec3d difference(Vec3d other, Vec3d result){return difference(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3d difference(double x, double y, double z) {return difference(x, y, z, this);} + public default Vec3d difference(double x, double y, double z, Vec3d result){return result.set(getX() - x, getY() - y, getZ() - z);} + + @Override + public default Vec3d clamp(double min, double max){return clamp(min, max, ALL);} + public default Vec3d clamp(double min, double max, Vec3d result){return clamp(min, max, result, ALL);} + @Override + public default Vec3d clamp(double min, double max, int filter){return clamp(min, max, this, filter);} + public default Vec3d clamp(double min, double max, Vec3d result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()));} + + @Override + public default Vec3d store(ByteBuffer buffer) + { + buffer.putDouble(getX()).putDouble(getY()).putDouble(getZ()); + return this; + } + + @Override + public default Vec3d load(ByteBuffer buffer) + { + return set(buffer.getDouble(), buffer.getDouble(), buffer.getDouble()); + } + + @Override + public default Vec3d store(DoubleBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()); + return this; + } + + @Override + public default Vec3d load(DoubleBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec3b asByte(){return isMutable() ? Vec3b.newMutable((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()), (byte)MathUtils.floor(getZ())) : Vec3b.newVec((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()), (byte)MathUtils.floor(getZ()));} + @Override + public default Vec3s asShort(){return isMutable() ? Vec3s.newMutable((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()), (short)MathUtils.floor(getZ())) : Vec3s.newVec((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()), (short)MathUtils.floor(getZ()));} + @Override + public default Vec3i asInt(){return isMutable() ? Vec3i.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ())) : Vec3i.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()));} + @Override + public default Vec3l asLong() {return isMutable() ? Vec3l.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ())) : Vec3l.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()));} + @Override + public default Vec3f asFloat(){return isMutable() ? Vec3f.newMutable((float)getX(), (float)getY(), (float)getZ()) : Vec3f.newVec((float)getX(), (float)getY(), (float)getZ());} + + @Override + public default Vec3d asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec3d asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec3d copyAsMutable(){return newMutable(this);} + @Override + public default Vec3d copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3dImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3dImmutable.java new file mode 100644 index 0000000..c99b341 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3dImmutable.java @@ -0,0 +1,108 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.util.Objects; + +public class Vec3dImmutable implements Vec3d +{ + final double x; + final double y; + final double z; + + public Vec3dImmutable() + { + x = 0; + y = 0; + z = 0; + } + + public Vec3dImmutable(double value) + { + x = value; + y = value; + z = value; + } + + public Vec3dImmutable(double x, double y, double z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public double getX() + { + return x; + } + + @Override + public double getY() + { + return y; + } + + @Override + public double getZ() + { + return z; + } + + @Override + public Vec3d setX(double x) + { + return this.x == x ? this : Vec3d.newVec(x, y, z); + } + + @Override + public Vec3d setY(double y) + { + return this.y == y ? this : Vec3d.newVec(x, y, z); + } + + @Override + public Vec3d setZ(double z) + { + return this.z == z ? this : Vec3d.newVec(x, y, z); + } + + @Override + public Vec3d copy() + { + return Vec3d.newVec(this); + } + + @Override + public Vec3d set(double x, double y, double z) + { + return this.x == x && this.y == y && this.z == z ? this : Vec3d.newVec(x, y, z); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3d) + { + Vec3d vec = (Vec3d)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3d[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3dMutable.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3dMutable.java new file mode 100644 index 0000000..34a929b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3dMutable.java @@ -0,0 +1,114 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.util.Objects; + +public class Vec3dMutable implements Vec3d +{ + double x; + double y; + double z; + + public Vec3dMutable() + { + x = 0; + y = 0; + z = 0; + } + + public Vec3dMutable(double value) + { + x = value; + y = value; + z = value; + } + + public Vec3dMutable(double x, double y, double z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public double getX() + { + return x; + } + + @Override + public double getY() + { + return y; + } + + @Override + public double getZ() + { + return z; + } + + @Override + public Vec3d setX(double x) + { + this.x = x; + return this; + } + + @Override + public Vec3d setY(double y) + { + this.y = y; + return this; + } + + @Override + public Vec3d setZ(double z) + { + this.z = z; + return this; + } + + @Override + public Vec3d copy() + { + return Vec3d.newMutable(this); + } + + @Override + public Vec3d set(double x, double y, double z) + { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3d) + { + Vec3d vec = (Vec3d)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3d[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4d.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4d.java new file mode 100644 index 0000000..6025d44 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4d.java @@ -0,0 +1,231 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec4b; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.ints.Vec4i; +import speiger.src.coreengine.math.vector.longs.Vec4l; +import speiger.src.coreengine.math.vector.shorts.Vec4s; + +public interface Vec4d extends Vecd +{ + public static final Vec4d ZERO = newVec(); + public static final Vec4d MINUS_ONE = newVec(-1D); + public static final Vec4d ONE = newVec(1D); + + public static Vec4d newMutable(){return new Vec4dMutable();} + public static Vec4d newMutable(double value){return new Vec4dMutable(value);} + public static Vec4d newMutable(double x, double y, double z, double w){return new Vec4dMutable(x, y, z, w);} + public static Vec4d newMutable(Vec4d vec){return newMutable(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public static Vec4d newVec(){return new Vec4dImmutable();} + public static Vec4d newVec(double value){return new Vec4dImmutable(value);} + public static Vec4d newVec(double x, double y, double z, double w){return new Vec4dImmutable(x, y, z, w);} + public static Vec4d newVec(Vec4d vec){return newVec(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public double getX(); + public double getY(); + public double getZ(); + public double getW(); + public Vec4d setX(double x); + public Vec4d setY(double y); + public Vec4d setZ(double z); + public Vec4d setW(double w); + @Override + public default double[] asArray(){return new double[]{getX(), getY(), getZ(), getW()};} + @Override + public Vec4d copy(); + @Override + public default Vec4d abs(){return set(Math.abs(getX()), Math.abs(getY()), Math.abs(getZ()), Math.abs(getW()));} + @Override + public default Vec4d negate(){return set(0D, 0D, 0D, 0D);} + @Override + public default Vec4d invert(){return set(-getX(), -getY(), -getZ(), -getW());} + public default Vec4d normalize() + { + double l = length(); + return l == 0D ? this : multiply(1.0D / l); + } + + public default Vec4d normalize3D() + { + double value = (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()); + return value == 0D ? this : multiply(1D / value); + } + + @Override + public default Vec4d add(double value){return add(value, value, value, value);} + public default Vec4d add(Vec4d value){return add(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4d add(double x, double y, double z, double w){return set(getX() + x, getY() + y, getZ() + z, getW() + w);} + + @Override + public default Vec4d sub(double value){return sub(value, value, value, value);} + public default Vec4d sub(Vec4d value){return sub(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4d sub(double x, double y, double z, double w){return set(getX() - x, getY() - y, getZ() - z, getW() - w);} + + @Override + public default Vec4d multiply(double value){return multiply(value, value, value, value);} + public default Vec4d multiply(Vec4d value){return multiply(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4d multiply(double x, double y, double z, double w){return set(getX() * x, getY() * y, getZ() * z, getW() * w);} + + @Override + public default Vec4d devide(double value){return devide(value, value, value, value);} + public default Vec4d devide(Vec4d value){return devide(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4d devide(double x, double y, double z, double w){return set(getX() / x, getY() / y, getZ() / z, getW() / w);} + + @Override + public default Vec4d set(double value){return set(value, value, value, value);} + public default Vec4d set(Vec4d value){return set(value.getX(), value.getY(), value.getZ(), value.getW());} + public Vec4d set(double x, double y, double z, double w); + + public default double distanceTo(Vec4d value){return distanceTo(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double distanceTo(double x, double y, double z, double w){return Math.sqrt(distanceTo(x, y, z, w));} + + public default double distanceToSquared(Vec4d value){return distanceToSquared(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double distanceToSquared(double x, double y, double z, double w) + { + double xPos = getX() - x; + double yPos = getY() - y; + double zPos = getZ() - z; + double wPos = getW() - w; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos) + (wPos * wPos); + } + @Override + public default double lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()) + (getW() * getW());} + + public default double dotProduct(Vec4d value){return dotProduct(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double dotProduct(double x, double y, double z, double w){return (getX() * x) + (getY() * y) + (getZ() * z) + (getW() * w);}; + + public default Vec4d lerp(Vec4d value, float progress, Vec4d result){return lerp(value.getX(), value.getY(), value.getZ(), value.getW(), progress, result);} + public default Vec4d lerp(double x, double y, double z, double w, float progress, Vec4d result){return result.set(MathUtils.lerp(getX(), x, progress), MathUtils.lerp(getY(), y, progress), MathUtils.lerp(getZ(), z, progress), MathUtils.lerp(getW(), w, progress));} + + public default double angle(Vec4d value){return angle(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double angle(double x, double y, double z, double w){return Math.acos(MathUtils.clamp(-1D, 1D, angleCos(x, y, z, w)));} + + public default double angleCos(Vec4d value){return angleCos(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double angleCos(double x, double y, double z, double w){return dotProduct(x, y, z, w) / Math.sqrt(lengthSquared() * (x * x) + (y * y) + (z * z) + (w * w));} + + public default Vec4d rotate(double angle, Vec4d axis){return rotate(angle, axis.getX(), axis.getY(), axis.getZ(), axis.getW());} + public default Vec4d rotate(double angle, Vec4d axis, Vec4d result){return rotate(angle, axis.getX(), axis.getY(), axis.getZ(), axis.getW(), result);} + public default Vec4d rotate(double angle, double x, double y, double z, double w){return rotate(angle, x, y, z, w, this);} + public default Vec4d rotate(double angle, double x, double y, double z, double w, Vec4d result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double dot = dotProduct(x, y, z, w); + double xPos = x * dot * (1D - cos) + (getX() * cos) + (-z * getY() + y * getZ()) * sin; + double yPos = y * dot * (1D - cos) + (getY() * cos) + (z * getX() - x * getZ()) * sin; + double zPos = z * dot * (1D - cos) + (getZ() * cos) + (-y * getX() + x * getY()) * sin; + return result.set(xPos, yPos, zPos, getW()); + } + public default Vec4d rotateX(double angle){return rotateX(angle, this);} + public default Vec4d rotateX(double angle, Vec4d result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double y = getY() * cos + getZ() * sin; + double z = getZ() * cos - getY() * sin; + return result.set(getX(), y, z, getW()); + } + public default Vec4d rotateY(double angle){return rotateY(angle, this);} + public default Vec4d rotateY(double angle, Vec4d result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double x = getX() * cos + getZ() * sin; + double z = getZ() * cos - getX() * sin; + return result.set(x, getY(), z, getW()); + } + public default Vec4d rotateZ(double angle){return rotateZ(angle, this);} + public default Vec4d rotateZ(double angle, Vec4d result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double x = cos * getX() - sin * getY(); + double y = sin * getX() + cos * getY(); + return result.set(x, y, getZ(), getW()); + } + + public default Vec4d smoothStep(Vec4d value, float progress, Vec4d result){return smoothStep(value.getX(), value.getY(), value.getZ(), value.getW(), progress, result);} + public default Vec4d smoothStep(double x, double y, double z, double w, float progress, Vec4d result) + { + double t2 = progress * progress; + double t3 = t2 * progress; + double xPos = ((getX() + getX() - x - x) * t3 + (3.0D * x - 3.0D * getX()) * t2 + getX() * progress + getX()); + double yPos = ((getY() + getY() - y - y) * t3 + (3.0D * y - 3.0D * getY()) * t2 + getY() * progress + getY()); + double zPos = ((getZ() + getZ() - z - z) * t3 + (3.0D * z - 3.0D * getZ()) * t2 + getZ() * progress + getZ()); + double wPos = ((getW() + getW() - w - w) * t3 + (3.0D * w - 3.0D * getW()) * t2 + getW() * progress + getW()); + return result.set(xPos, yPos, zPos, wPos); + } + + public default Vec4d min(Vec4d other) {return min(other, this);} + public default Vec4d min(Vec4d other, Vec4d result){return min(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4d min(double x, double y, double z, double w) {return min(x, y, z, w, this);} + public default Vec4d min(double x, double y, double z, double w, Vec4d result){return result.set(Math.min(getX(), x), Math.min(getY(), y), Math.min(getZ(), z), Math.min(getW(), w));} + + public default Vec4d max(Vec4d other) {return max(other, this);} + public default Vec4d max(Vec4d other, Vec4d result){return max(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4d max(double x, double y, double z, double w) {return max(x, y, z, w, this);} + public default Vec4d max(double x, double y, double z, double w, Vec4d result){return result.set(Math.max(getX(), x), Math.max(getY(), y), Math.max(getZ(), z), Math.max(getZ(), z));} + + public default Vec4d difference(Vec4d other) {return difference(other, this);} + public default Vec4d difference(Vec4d other, Vec4d result){return difference(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4d difference(double x, double y, double z, double w) {return difference(x, y, z, w, this);} + public default Vec4d difference(double x, double y, double z, double w, Vec4d result){return result.set(getX() - x, getY() - y, getZ() - z, getW() - w);} + + @Override + public default Vec4d clamp(double min, double max){return clamp(min, max, ALL);} + public default Vec4d clamp(double min, double max, Vec4d result){return clamp(min, max, result, ALL);} + @Override + public default Vec4d clamp(double min, double max, int filter){return clamp(min, max, this, filter);} + public default Vec4d clamp(double min, double max, Vec4d result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()), (filter & W) == 0 ? getW() : MathUtils.clamp(min, max, getW()));} + + @Override + public default Vec4d store(ByteBuffer buffer) + { + buffer.putDouble(getX()).putDouble(getY()).putDouble(getZ()).putDouble(getW()); + return this; + } + + @Override + public default Vec4d load(ByteBuffer buffer) + { + return set(buffer.getDouble(), buffer.getDouble(), buffer.getDouble(), buffer.getDouble()); + } + + @Override + public default Vec4d store(DoubleBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()).put(getW()); + return this; + } + + @Override + public default Vec4d load(DoubleBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec4b asByte(){return isMutable() ? Vec4b.newMutable((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()), (byte)MathUtils.floor(getZ()), (byte)MathUtils.floor(getW())) : Vec4b.newVec((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()), (byte)MathUtils.floor(getZ()), (byte)MathUtils.floor(getW()));} + @Override + public default Vec4s asShort(){return isMutable() ? Vec4s.newMutable((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()), (short)MathUtils.floor(getZ()), (short)MathUtils.floor(getW())) : Vec4s.newVec((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()), (short)MathUtils.floor(getZ()), (short)MathUtils.floor(getW()));} + @Override + public default Vec4i asInt(){return isMutable() ? Vec4i.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()), MathUtils.floor(getW())) : Vec4i.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()), MathUtils.floor(getW()));} + @Override + public default Vec4l asLong() {return isMutable() ? Vec4l.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()), MathUtils.floor(getW())) : Vec4l.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()), MathUtils.floor(getW()));} + @Override + public default Vec4f asFloat(){return isMutable() ? Vec4f.newMutable((float)getX(), (float)getY(), (float)getZ(), (float)getW()) : Vec4f.newVec((float)getX(), (float)getY(), (float)getZ(), (float)getW());} + + @Override + public default Vec4d asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec4d asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec4d copyAsMutable(){return newMutable(this);} + @Override + public default Vec4d copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4dImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4dImmutable.java new file mode 100644 index 0000000..cb6c1ac --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4dImmutable.java @@ -0,0 +1,124 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.util.Objects; + +public class Vec4dImmutable implements Vec4d +{ + final double x; + final double y; + final double z; + final double w; + + public Vec4dImmutable() + { + x = 0; + y = 0; + z = 0; + w = 0; + } + + public Vec4dImmutable(double value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4dImmutable(double x, double y, double z, double w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public double getX() + { + return x; + } + + @Override + public double getY() + { + return y; + } + + @Override + public double getZ() + { + return z; + } + + @Override + public double getW() + { + return w; + } + + @Override + public Vec4d setX(double x) + { + return this.x == x ? this : Vec4d.newVec(x, y, z, w); + } + + @Override + public Vec4d setY(double y) + { + return this.y == y ? this : Vec4d.newVec(x, y, z, w); + } + + @Override + public Vec4d setZ(double z) + { + return this.z == z ? this : Vec4d.newVec(x, y, z, w); + } + + @Override + public Vec4d setW(double w) + { + return this.w == w ? this : Vec4d.newVec(x, y, z, w); + } + + @Override + public Vec4d copy() + { + return Vec4d.newVec(this); + } + + @Override + public Vec4d set(double x, double y, double z, double w) + { + return this.x == x && this.y == y && this.z == z && this.w == w ? this : Vec4d.newVec(x, y, z, w); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4d) + { + Vec4d vec = (Vec4d)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4d[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4dMutable.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4dMutable.java new file mode 100644 index 0000000..c38cfd2 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4dMutable.java @@ -0,0 +1,128 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.util.Objects; + +public class Vec4dMutable implements Vec4d +{ + double x; + double y; + double z; + double w; + + public Vec4dMutable() + { + } + + public Vec4dMutable(double value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4dMutable(double x, double y, double z, double w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public double getX() + { + return x; + } + + @Override + public double getY() + { + return y; + } + + @Override + public double getZ() + { + return z; + } + + @Override + public double getW() + { + return w; + } + + @Override + public Vec4d setX(double x) + { + this.x = x; + return this; + } + + @Override + public Vec4d setY(double y) + { + this.y = y; + return this; + } + + @Override + public Vec4d setZ(double z) + { + this.z = z; + return this; + } + + @Override + public Vec4d setW(double w) + { + this.w = w; + return this; + } + + @Override + public Vec4d copy() + { + return Vec4d.newMutable(this); + } + + @Override + public Vec4d set(double x, double y, double z, double w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4d) + { + Vec4d vec = (Vec4d)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4d[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vecd.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vecd.java new file mode 100644 index 0000000..25d9b86 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vecd.java @@ -0,0 +1,33 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.nio.DoubleBuffer; + +import speiger.src.coreengine.math.vector.Vec; +import speiger.src.coreengine.math.vector.bytes.Vecb; +import speiger.src.coreengine.math.vector.floats.Vecf; +import speiger.src.coreengine.math.vector.ints.Veci; +import speiger.src.coreengine.math.vector.longs.Vecl; +import speiger.src.coreengine.math.vector.shorts.Vecs; + +public interface Vecd extends Vec +{ + public Vecd set(double value); + public Vecd add(double value); + public Vecd sub(double value); + public Vecd multiply(double value); + public Vecd devide(double value); + public Vecd clamp(double min, double max); + public Vecd clamp(double min, double max, int filter); + public double lengthSquared(); + public default double length(){return Math.sqrt(lengthSquared());} + + public Vecd store(DoubleBuffer buffer); + public Vecd load(DoubleBuffer buffer); + public double[] asArray(); + + public Vecb asByte(); + public Vecs asShort(); + public Veci asInt(); + public Vecl asLong(); + public Vecf asFloat(); +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2f.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2f.java new file mode 100644 index 0000000..1d7e1b3 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2f.java @@ -0,0 +1,187 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec2b; +import speiger.src.coreengine.math.vector.doubles.Vec2d; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.math.vector.longs.Vec2l; +import speiger.src.coreengine.math.vector.shorts.Vec2s; + +public interface Vec2f extends Vecf +{ + public static final Vec2f ZERO = newVec(); + public static final Vec2f MINUS_ONE = newVec(-1F); + public static final Vec2f ONE = newVec(1F); + + public static Vec2f newMutable(){return new Vec2fMutable();} + public static Vec2f newMutable(float value){return new Vec2fMutable(value);} + public static Vec2f newMutable(float x, float y){return new Vec2fMutable(x, y);} + public static Vec2f newMutable(Vec2f value){return newMutable(value.getX(), value.getY());} + + public static Vec2f newVec(){return new Vec2fImmutable();} + public static Vec2f newVec(float value){return new Vec2fImmutable(value);} + public static Vec2f newVec(float x, float y){return new Vec2fImmutable(x, y);} + public static Vec2f newVec(Vec2f value){return newVec(value.getX(), value.getY());} + + public float getX(); + public float getY(); + public Vec2f setX(float x); + public Vec2f setY(float y); + @Override + public default float[] asArray(){return new float[]{getX(), getY()};} + @Override + public Vec2f copy(); + @Override + public default Vec2f abs(){return set(Math.abs(getX()), Math.abs(getY()));} + @Override + public default Vec2f negate(){return set(0F, 0F);} + @Override + public default Vec2f invert(){return set(-getX(), -getY());}; + public default Vec2f normalize() + { + float l = length(); + return l == 0F ? this : multiply(1F / l); + } + + @Override + public default Vec2f add(float value) {return add(value, value);} + public default Vec2f add(Vec2f value) {return add(value.getX(), value.getY());} + public default Vec2f add(float x, float y) {return set(x + getX(), y + getY());} + + @Override + public default Vec2f sub(float value){return sub(value, value);} + public default Vec2f sub(Vec2f value){return sub(value.getX(), value.getY());} + public default Vec2f sub(float x, float y) {return set(getX() - x, getY() - y);} + + @Override + public default Vec2f multiply(float value){return multiply(value, value);} + public default Vec2f multiply(Vec2f value){return multiply(value.getX(), value.getY());} + public default Vec2f multiply(float x, float y) {return set(x * getX(), y * getY());} + + @Override + public default Vec2f devide(float value){return devide(value, value);} + public default Vec2f devide(Vec2f value){return devide(value.getX(), value.getY());} + public default Vec2f devide(float x, float y){return set(getX() / x, getY() / y);} + + @Override + public default Vec2f set(float value){return set(value, value);}; + public default Vec2f set(Vec2f value){return set(value.getX(), value.getY());} + public Vec2f set(float x, float y); + + public default double distanceTo(Vec2f value){return distanceTo(value.getX(), value.getY());} + public default double distanceTo(float x, float y){return Math.sqrt(distanceToSquared(x, y));} + + public default double distanceToSquared(Vec2f value){return distanceToSquared(value.getX(), value.getY());} + public default double distanceToSquared(float x, float y) + { + double xPos = getX() - x; + double yPos = getY() - y; + return (xPos * xPos) + (yPos * yPos); + } + @Override + public default float lengthSquared() {return (getX() * getX()) + (getY() * getY());} + + public default double dotProduct(Vec2f value){return dotProduct(value.getX(), value.getY());} + public default double dotProduct(float x, float y){return (getX() * x) + (getY() * y);} + + public default Vec2f lerp(Vec2f value, float progress, Vec2f result){return lerp(value.getX(), value.getY(), progress, result);} + public default Vec2f lerp(float x, float y, float progress, Vec2f result){return result.set(MathUtils.lerp(getX(), x, progress), MathUtils.lerp(getY(), y, progress));}; + + public default float angle(Vec2f value){return angle(value.getX(), value.getY());} + public default float angle(float x, float y){return (float)Math.atan2((getX() * y) - (getY() * x), (getX() * x) + (getY() * y));} + + public default float directionAngle(Vec2f value){return directionAngle(value.getX(), value.getY());} + public default float directionAngle(float x, float y){return (float)(-Math.toDegrees(Math.atan2(getX() - x, getY() - y)) % 360D);} + + public default Vec2f reflect(Vec2f value){return reflect(value.getX(), value.getY(), this);} + public default Vec2f reflect(float x, float y){return reflect(x, y, this);}; + public default Vec2f reflect(Vec2f value, Vec2f result){return reflect(value.getX(), value.getY(), result);} + public default Vec2f reflect(float x, float y, Vec2f result) + { + double dot = dotProduct(x, y); + float x2 = (float)((dot + dot) * x); + float y2 = (float)((dot + dot) * y); + return result.set(getX() - x2, getY() - y2); + } + + public default Vec2f rotate(float angle, Vec2f center){return rotate(angle, center.getX(), center.getY());} + public default Vec2f rotate(float angle, float x, float y) + { + float xPos = getX() - x; + float yPos = getY() - y; + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + return set((float)((xPos * cos) + (yPos * sin) + x), (float)(-(xPos * sin) + (yPos * cos) + y)); + } + + public default Vec2f min(Vec2f other) {return min(other, this);} + public default Vec2f min(Vec2f other, Vec2f result){return min(other.getX(), other.getY(), result);} + public default Vec2f min(float x, float y) {return min(x, y, this);} + public default Vec2f min(float x, float y, Vec2f result){return result.set(Math.min(getX(), x), Math.min(getY(), y));} + + public default Vec2f max(Vec2f other) {return max(other, this);} + public default Vec2f max(Vec2f other, Vec2f result){return max(other.getX(), other.getY(), result);} + public default Vec2f max(float x, float y) {return max(x, y, this);} + public default Vec2f max(float x, float y, Vec2f result){return result.set(Math.max(getX(), x), Math.max(getY(), y));} + + public default Vec2f difference(Vec2f other) {return difference(other, this);} + public default Vec2f difference(Vec2f other, Vec2f result){return difference(other.getX(), other.getY(), result);} + public default Vec2f difference(float x, float y) {return difference(x, y, this);} + public default Vec2f difference(float x, float y, Vec2f result){return result.set(getX() - x, getY() - y);} + + @Override + public default Vec2f clamp(float min, float max){return clamp(min, max, ALL);} + public default Vec2f clamp(float min, float max, Vec2f result){return clamp(min, max, result, ALL);} + @Override + public default Vec2f clamp(float min, float max, int filter){return clamp(min, max, this, filter);} + public default Vec2f clamp(float min, float max, Vec2f result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()));} + + @Override + public default Vec2f store(ByteBuffer buffer) + { + buffer.putFloat(getX()).putFloat(getY()); + return this; + } + + @Override + public default Vec2f load(ByteBuffer buffer) + { + return set(buffer.getFloat(), buffer.getFloat()); + } + + @Override + public default Vec2f store(FloatBuffer buffer) + { + buffer.put(getX()).put(getY()); + return this; + } + + @Override + public default Vec2f load(FloatBuffer buffer) + { + return set(buffer.get(), buffer.get()); + } + @Override + public default Vec2b asByte(){return isMutable() ? Vec2b.newMutable((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY())) : Vec2b.newVec((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()));} + @Override + public default Vec2s asShort(){return isMutable() ? Vec2s.newMutable((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY())) : Vec2s.newVec((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()));} + @Override + public default Vec2i asInt(){return isMutable() ? Vec2i.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY())) : Vec2i.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()));} + @Override + public default Vec2l asLong() {return isMutable() ? Vec2l.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY())) : Vec2l.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()));} + @Override + public default Vec2d asDouble(){return isMutable() ? Vec2d.newMutable(getX(), getY()) : Vec2d.newVec(getX(), getY());} + + + @Override + public default Vec2f asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec2f asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec2f copyAsMutable(){return newMutable(this);} + @Override + public default Vec2f copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2fImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2fImmutable.java new file mode 100644 index 0000000..92dd3e9 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2fImmutable.java @@ -0,0 +1,92 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.util.Objects; + +public class Vec2fImmutable implements Vec2f +{ + final float x; + final float y; + + public Vec2fImmutable() + { + x = 0F; + y = 0F; + } + + public Vec2fImmutable(float value) + { + x = value; + y = value; + } + + public Vec2fImmutable(float x, float y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public float getX() + { + return x; + } + + @Override + public float getY() + { + return y; + } + + @Override + public Vec2f setX(float x) + { + return this.x == x ? this : Vec2f.newVec(x, y); + } + + @Override + public Vec2f setY(float y) + { + return this.y == y ? this : Vec2f.newVec(x, y); + } + + @Override + public Vec2f copy() + { + return Vec2f.newVec(x, y); + } + + @Override + public Vec2f set(float x, float y) + { + return this.x == x && this.y == y ? this : Vec2f.newVec(x, y); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2f) + { + Vec2f vec = (Vec2f)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2f[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2fMutable.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2fMutable.java new file mode 100644 index 0000000..554a2a2 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2fMutable.java @@ -0,0 +1,94 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.util.Objects; + +public class Vec2fMutable implements Vec2f +{ + float x; + float y; + + public Vec2fMutable() + { + } + + public Vec2fMutable(float value) + { + x = value; + y = value; + } + + public Vec2fMutable(float x, float y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public float getX() + { + return x; + } + + @Override + public float getY() + { + return y; + } + + @Override + public Vec2f setX(float x) + { + this.x = x; + return this; + } + + @Override + public Vec2f setY(float y) + { + this.y = y; + return this; + } + + @Override + public Vec2f copy() + { + return Vec2f.newMutable(this); + } + + @Override + public Vec2f set(float x, float y) + { + this.x = x; + this.y = y; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2f) + { + Vec2f vec = (Vec2f)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2f[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3f.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3f.java new file mode 100644 index 0000000..19e96ae --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3f.java @@ -0,0 +1,242 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec3b; +import speiger.src.coreengine.math.vector.doubles.Vec3d; +import speiger.src.coreengine.math.vector.ints.Vec3i; +import speiger.src.coreengine.math.vector.longs.Vec3l; +import speiger.src.coreengine.math.vector.shorts.Vec3s; + +public interface Vec3f extends Vecf +{ + public static final Vec3f ZERO = newVec(); + public static final Vec3f MINUS_ONE = newVec(-1F); + public static final Vec3f ONE = newVec(1F); + + public static Vec3f newMutable(){return new Vec3fMutable();} + public static Vec3f newMutable(float value){return new Vec3fMutable(value);} + public static Vec3f newMutable(float x, float y, float z){return new Vec3fMutable(x, y, z);} + public static Vec3f newMutable(Vec3f vec){return newMutable(vec.getX(), vec.getY(), vec.getZ());} + + public static Vec3f newVec(){return new Vec3fImmutable();} + public static Vec3f newVec(float value){return new Vec3fImmutable(value);} + public static Vec3f newVec(float x, float y, float z){return new Vec3fImmutable(x, y, z);} + public static Vec3f newVec(Vec3f vec){return newVec(vec.getX(), vec.getY(), vec.getZ());} + + public float getX(); + public float getY(); + public float getZ(); + public Vec3f setX(float x); + public Vec3f setY(float y); + public Vec3f setZ(float z); + @Override + public default float[] asArray(){return new float[]{getX(), getY(), getZ()};} + + @Override + public Vec3f copy(); + @Override + public default Vec3f abs(){return set(Math.abs(getX()), Math.abs(getY()), Math.abs(getZ()));} + @Override + public default Vec3f negate(){return set(0F, 0F, 0F);} + @Override + public default Vec3f invert(){return set(-getX(), -getY(), -getZ());} + public default Vec3f normalize() + { + float l = length(); + return l == 0F ? this : multiply(1.0F / l); + } + @Override + public default Vec3f add(float value){return add(value, value, value);} + public default Vec3f add(Vec3f value){return add(value.getX(), value.getY(), value.getZ());} + public default Vec3f add(float x, float y, float z){return set(getX() + x, getY() + y, getZ() + z);} + + @Override + public default Vec3f sub(float value){return sub(value, value, value);} + public default Vec3f sub(Vec3f value){return sub(value.getX(), value.getY(), value.getZ());} + public default Vec3f sub(float x, float y, float z){return set(getX() - x, getY() - y, getZ() - z);} + + @Override + public default Vec3f multiply(float value){return multiply(value, value, value);} + public default Vec3f multiply(Vec3f value){return multiply(value.getX(), value.getY(), value.getZ());} + public default Vec3f multiply(float x, float y, float z){return set(getX() * x, getY() * y, getZ() * z);} + + @Override + public default Vec3f devide(float value){return devide(value, value, value);} + public default Vec3f devide(Vec3f value){return devide(value.getX(), value.getY(), value.getZ());} + public default Vec3f devide(float x, float y, float z){return set(getX() / x, getY() / y, getZ() / z);} + + @Override + public default Vec3f set(float value){return set(value, value, value);} + public default Vec3f set(Vec3f value){return set(value.getX(), value.getY(), value.getZ());} + public Vec3f set(float x, float y, float z); + + public default double distanceTo(Vec3f value){return distanceTo(value.getX(), value.getY(), value.getZ());} + public default double distanceTo(float x, float y, float z){return Math.sqrt(distanceToSquared(x, y, z));} + + public default double distanceToSquared(Vec3f value){return distanceToSquared(value.getX(), value.getY(), value.getZ());} + public default double distanceToSquared(float x, float y, float z) + { + double xPos = getX() - x; + double yPos = getY() - y; + double zPos = getZ() - z; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos); + } + @Override + public default float lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ());} + + public default double dotProduct(Vec3f value){return dotProduct(value.getX(), value.getY(), value.getZ());} + public default double dotProduct(float x, float y, float z){return (getX() * x) + (getY() * y) + (getZ() * z);} + + public default Vec3f lerp(Vec3f value, float progress, Vec3f result){return lerp(value.getX(), value.getY(), value.getZ(), progress, result);} + public default Vec3f lerp(float x, float y, float z, float progress, Vec3f result){return result.set(MathUtils.lerp(getX(), x, progress), MathUtils.lerp(getY(), y, progress), MathUtils.lerp(getZ(), z, progress));} + + public default float angle(Vec3f value){return angle(value.getX(), value.getY(), value.getZ());} + public default float angle(float x, float y, float z){return (float)Math.acos(MathUtils.clamp(-1, 1, angleCos(x, y, z)));} + + public default float angleCos(Vec3f value){return angleCos(value.getX(), value.getY(), value.getZ());} + public default float angleCos(float x, float y, float z) + { + double myLength = (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()); + double otherLength = (x * x) + (y * y) + (z * z); + float dot = (getX() * x) + (getY() * y) + (getZ() * z); + return dot / (float)(Math.sqrt(myLength * otherLength)); + } + + public default Vec3f crossProduct(Vec3f value){return crossProduct(value.getX(), value.getY(), value.getZ(), this);} + public default Vec3f crossProduct(Vec3f value, Vec3f result){return crossProduct(value.getX(), value.getY(), value.getZ(), result);} + public default Vec3f crossProduct(float x, float y, float z){return crossProduct(x, y, z, this);} + public default Vec3f crossProduct(float x, float y, float z, Vec3f result){return result.set((getY() * z) - (getZ() * y), (getZ() * x) - (getX() * z), (getX() * y) - (getY() * x));} + + public default Vec3f reflect(Vec3f value){return reflect(value.getX(), value.getY(), value.getZ(), this);} + public default Vec3f reflect(Vec3f value, Vec3f result){return reflect(value.getX(), value.getY(), value.getZ(), result);} + public default Vec3f reflect(float x, float y, float z){return reflect(x, y, z, this);} + public default Vec3f reflect(float x, float y, float z, Vec3f result) + { + double dot = dotProduct(x, y, z) * 2D; + return result.set(getX() - (float)(dot * x), getY() - (float)(dot * y), getZ() - (float)(dot * z)); + } + + public default Vec3f rotate(float angle, Vec3f axis){return rotate(angle, axis.getX(), axis.getY(), axis.getZ());} + public default Vec3f rotate(float angle, Vec3f axis, Vec3f result){return rotate(angle, axis.getX(), axis.getY(), axis.getZ(), result);} + public default Vec3f rotate(float angle, float x, float y, float z){return rotate(angle, x, y, z, this);} + public default Vec3f rotate(float angle, float x, float y, float z, Vec3f result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double dot = dotProduct(x, y, z); + double xPos = x * dot * (1D - cos) + (getX() * cos) + (-z * getY() + y * getZ()) * sin; + double yPos = y * dot * (1D - cos) + (getY() * cos) + (z * getX() - x * getZ()) * sin; + double zPos = z * dot * (1D - cos) + (getZ() * cos) + (-y * getX() + x * getY()) * sin; + return result.set((float)xPos, (float)yPos, (float)zPos); + } + public default Vec3f rotateX(float angle){return rotateX(angle, this);} + public default Vec3f rotateX(float angle, Vec3f result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double y = getY() * cos + getZ() * sin; + double z = getZ() * cos - getY() * sin; + return result.set(getX(), (float)y, (float)z); + } + public default Vec3f rotateY(float angle){return rotateY(angle, this);} + public default Vec3f rotateY(float angle, Vec3f result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double x = getX() * cos + getZ() * sin; + double z = getZ() * cos - getX() * sin; + return result.set((float)x, getY(), (float)z); + } + public default Vec3f rotateZ(float angle){return rotateZ(angle, this);} + public default Vec3f rotateZ(float angle, Vec3f result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double x = cos * getX() - sin * getY(); + double y = sin * getX() + cos * getY(); + return result.set((float)x, (float)y, getZ()); + } + + public default Vec3f smoothStep(Vec3f value, float progress, Vec3f result){return smoothStep(value.getX(), value.getY(), value.getZ(), progress, result);} + public default Vec3f smoothStep(float x, float y, float z, float progress, Vec3f result) + { + float t2 = progress * progress; + float t3 = t2 * progress; + float xPos = ((getX() + getX() - x - x) * t3 + (3.0F * x - 3.0F * getX()) * t2 + getX() * progress + getX()); + float yPos = ((getY() + getY() - y - y) * t3 + (3.0F * y - 3.0F * getY()) * t2 + getY() * progress + getY()); + float zPos = ((getZ() + getZ() - z - z) * t3 + (3.0F * z - 3.0F * getZ()) * t2 + getZ() * progress + getZ()); + return result.set(xPos, yPos, zPos); + } + + public default Vec3f min(Vec3f other) {return min(other, this);} + public default Vec3f min(Vec3f other, Vec3f result){return min(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3f min(float x, float y, float z) {return min(x, y, z, this);} + public default Vec3f min(float x, float y, float z, Vec3f result){return result.set(Math.min(getX(), x), Math.min(getY(), y), Math.min(getZ(), z));} + + public default Vec3f max(Vec3f other) {return max(other, this);} + public default Vec3f max(Vec3f other, Vec3f result){return max(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3f max(float x, float y, float z) {return max(x, y, z, this);} + public default Vec3f max(float x, float y, float z, Vec3f result){return result.set(Math.max(getX(), x), Math.max(getY(), y), Math.max(getZ(), z));} + + public default Vec3f difference(Vec3f other) {return difference(other, this);} + public default Vec3f difference(Vec3f other, Vec3f result){return difference(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3f difference(float x, float y, float z) {return difference(x, y, z, this);} + public default Vec3f difference(float x, float y, float z, Vec3f result){return result.set(getX() - x, getY() - y, getZ() - z);} + + @Override + public default Vec3f clamp(float min, float max){return clamp(min, max, ALL);} + public default Vec3f clamp(float min, float max, Vec3f result){return clamp(min, max, result, ALL);} + @Override + public default Vec3f clamp(float min, float max, int filter){return clamp(min, max, this, filter);} + public default Vec3f clamp(float min, float max, Vec3f result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()));} + + @Override + public default Vec3f store(ByteBuffer buffer) + { + buffer.putFloat(getX()).putFloat(getY()).putFloat(getZ()); + return this; + } + + @Override + public default Vec3f load(ByteBuffer buffer) + { + return set(buffer.getFloat(), buffer.getFloat(), buffer.getFloat()); + } + + @Override + public default Vec3f store(FloatBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()); + return this; + } + + @Override + public default Vec3f load(FloatBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec3b asByte(){return isMutable() ? Vec3b.newMutable((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()), (byte)MathUtils.floor(getZ())) : Vec3b.newVec((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()), (byte)MathUtils.floor(getZ()));} + @Override + public default Vec3s asShort(){return isMutable() ? Vec3s.newMutable((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()), (short)MathUtils.floor(getZ())) : Vec3s.newVec((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()), (short)MathUtils.floor(getZ()));} + @Override + public default Vec3i asInt(){return isMutable() ? Vec3i.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ())) : Vec3i.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()));} + @Override + public default Vec3l asLong() {return isMutable() ? Vec3l.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ())) : Vec3l.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()));} + @Override + public default Vec3d asDouble(){return isMutable() ? Vec3d.newMutable(getX(), getY(), getZ()) : Vec3d.newVec(getX(), getY(), getZ());} + + + @Override + public default Vec3f asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec3f asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec3f copyAsMutable(){return newMutable(this);} + @Override + public default Vec3f copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3fImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3fImmutable.java new file mode 100644 index 0000000..e0886ad --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3fImmutable.java @@ -0,0 +1,108 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.util.Objects; + +public class Vec3fImmutable implements Vec3f +{ + final float x; + final float y; + final float z; + + public Vec3fImmutable() + { + x = 0F; + y = 0F; + z = 0F; + } + + public Vec3fImmutable(float value) + { + x = value; + y = value; + z = value; + } + + public Vec3fImmutable(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public float getX() + { + return x; + } + + @Override + public float getY() + { + return y; + } + + @Override + public float getZ() + { + return z; + } + + @Override + public Vec3f setX(float x) + { + return this.x == x ? this : Vec3f.newVec(x, y, z); + } + + @Override + public Vec3f setY(float y) + { + return this.y == y ? this : Vec3f.newVec(x, y, z); + } + + @Override + public Vec3f setZ(float z) + { + return this.z == z ? this : Vec3f.newVec(x, y, z); + } + + @Override + public Vec3f copy() + { + return Vec3f.newVec(this); + } + + @Override + public Vec3f set(float x, float y, float z) + { + return this.x == x && this.y == y && this.z == z ? this : Vec3f.newVec(x, y, z); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3f) + { + Vec3f vec = (Vec3f)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3f[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3fMutable.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3fMutable.java new file mode 100644 index 0000000..522a2da --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3fMutable.java @@ -0,0 +1,111 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.util.Objects; + +public class Vec3fMutable implements Vec3f +{ + float x; + float y; + float z; + + public Vec3fMutable() + { + } + + public Vec3fMutable(float value) + { + x = value; + y = value; + z = value; + } + + public Vec3fMutable(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public float getX() + { + return x; + } + + @Override + public float getY() + { + return y; + } + + @Override + public float getZ() + { + return z; + } + + @Override + public Vec3f setX(float x) + { + this.x = x; + return this; + } + + @Override + public Vec3f setY(float y) + { + this.y = y; + return this; + } + + @Override + public Vec3f setZ(float z) + { + this.z = z; + return this; + } + + @Override + public Vec3f copy() + { + return Vec3f.newMutable(this); + } + + @Override + public Vec3f set(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3f) + { + Vec3f vec = (Vec3f)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3f[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4f.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4f.java new file mode 100644 index 0000000..4086764 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4f.java @@ -0,0 +1,232 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec4b; +import speiger.src.coreengine.math.vector.doubles.Vec4d; +import speiger.src.coreengine.math.vector.ints.Vec4i; +import speiger.src.coreengine.math.vector.longs.Vec4l; +import speiger.src.coreengine.math.vector.shorts.Vec4s; + +public interface Vec4f extends Vecf +{ + public static final Vec4f ZERO = newVec(); + public static final Vec4f MINUS_ONE = newVec(-1F); + public static final Vec4f ONE = newVec(1F); + + public static Vec4f newMutable(){return new Vec4fMutable();} + public static Vec4f newMutable(float value){return new Vec4fMutable(value);} + public static Vec4f newMutable(float x, float y, float z, float w){return new Vec4fMutable(x, y, z, w);} + public static Vec4f newMutable(Vec4f vec){return newMutable(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public static Vec4f newVec(){return new Vec4fImmutable();} + public static Vec4f newVec(float value){return new Vec4fImmutable(value);} + public static Vec4f newVec(float x, float y, float z, float w){return new Vec4fImmutable(x, y, z, w);} + public static Vec4f newVec(Vec4f vec){return newVec(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public float getX(); + public float getY(); + public float getZ(); + public float getW(); + public Vec4f setX(float x); + public Vec4f setY(float y); + public Vec4f setZ(float z); + public Vec4f setW(float w); + @Override + public default float[] asArray(){return new float[]{getX(), getY(), getZ(), getW()};} + @Override + public Vec4f copy(); + @Override + public default Vec4f abs(){return set(Math.abs(getX()), Math.abs(getY()), Math.abs(getZ()), Math.abs(getW()));} + @Override + public default Vec4f negate(){return set(0F, 0F, 0F, 0F);} + @Override + public default Vec4f invert(){return set(-getX(), -getY(), -getZ(), -getW());} + public default Vec4f normalize() + { + float l = length(); + return l == 0F ? this : multiply(1.0F / l); + } + + public default Vec4f normalize3D() + { + float value = (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()); + return value == 0F ? this : multiply(1F / value); + } + + @Override + public default Vec4f add(float value){return add(value, value, value, value);} + public default Vec4f add(Vec4f value){return add(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4f add(float x, float y, float z, float w){return set(getX() + x, getY() + y, getZ() + z, getW() + w);} + + @Override + public default Vec4f sub(float value){return sub(value, value, value, value);} + public default Vec4f sub(Vec4f value){return sub(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4f sub(float x, float y, float z, float w){return set(getX() - x, getY() - y, getZ() - z, getW() - w);} + + @Override + public default Vec4f multiply(float value){return multiply(value, value, value, value);} + public default Vec4f multiply(Vec4f value){return multiply(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4f multiply(float x, float y, float z, float w){return set(getX() * x, getY() * y, getZ() * z, getW() * w);} + + @Override + public default Vec4f devide(float value){return devide(value, value, value, value);} + public default Vec4f devide(Vec4f value){return devide(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4f devide(float x, float y, float z, float w){return set(getX() / x, getY() / y, getZ() / z, getW() / w);} + + @Override + public default Vec4f set(float value){return set(value, value, value, value);} + public default Vec4f set(Vec4f value){return set(value.getX(), value.getY(), value.getZ(), value.getW());} + public Vec4f set(float x, float y, float z, float w); + + public default double distanceTo(Vec4f value){return distanceTo(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double distanceTo(float x, float y, float z, float w){return Math.sqrt(distanceTo(x, y, z, w));} + + public default double distanceToSquared(Vec4f value){return distanceToSquared(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double distanceToSquared(float x, float y, float z, float w) + { + double xPos = getX() - x; + double yPos = getY() - y; + double zPos = getZ() - z; + double wPos = getW() - w; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos) + (wPos * wPos); + } + @Override + public default float lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()) + (getW() * getW());} + + public default double dotProduct(Vec4f value){return dotProduct(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double dotProduct(float x, float y, float z, float w){return (getX() * x) + (getY() * y) + (getZ() * z) + (getW() * w);}; + + public default Vec4f lerp(Vec4f value, float progress, Vec4f result){return lerp(value.getX(), value.getY(), value.getZ(), value.getW(), progress, result);} + public default Vec4f lerp(float x, float y, float z, float w, float progress, Vec4f result){return result.set(MathUtils.lerp(getX(), x, progress), MathUtils.lerp(getY(), y, progress), MathUtils.lerp(getZ(), z, progress), MathUtils.lerp(getW(), w, progress));} + + public default float angle(Vec4f value){return angle(value.getX(), value.getY(), value.getZ(), value.getW());} + public default float angle(float x, float y, float z, float w){return (float)Math.acos(MathUtils.clamp(-1F, 1F, angleCos(x, y, z, w)));} + + public default float angleCos(Vec4f value){return angleCos(value.getX(), value.getY(), value.getZ(), value.getW());} + public default float angleCos(float x, float y, float z, float w){return (float)(dotProduct(x, y, z, w) / Math.sqrt(lengthSquared() * (x * x) + (y * y) + (z * z) + (w * w)));} + + public default Vec4f rotate(float angle, Vec4f axis){return rotate(angle, axis.getX(), axis.getY(), axis.getZ(), axis.getW());} + public default Vec4f rotate(float angle, Vec4f axis, Vec4f result){return rotate(angle, axis.getX(), axis.getY(), axis.getZ(), axis.getW(), result);} + public default Vec4f rotate(float angle, float x, float y, float z, float w){return rotate(angle, x, y, z, w, this);} + public default Vec4f rotate(float angle, float x, float y, float z, float w, Vec4f result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double dot = dotProduct(x, y, z, w); + double xPos = x * dot * (1D - cos) + (getX() * cos) + (-z * getY() + y * getZ()) * sin; + double yPos = y * dot * (1D - cos) + (getY() * cos) + (z * getX() - x * getZ()) * sin; + double zPos = z * dot * (1D - cos) + (getZ() * cos) + (-y * getX() + x * getY()) * sin; + return result.set((float)xPos, (float)yPos, (float)zPos, getW()); + } + public default Vec4f rotateX(float angle){return rotateX(angle, this);} + public default Vec4f rotateX(float angle, Vec4f result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double y = getY() * cos + getZ() * sin; + double z = getZ() * cos - getY() * sin; + return result.set(getX(), (float)y, (float)z, getW()); + } + public default Vec4f rotateY(float angle){return rotateY(angle, this);} + public default Vec4f rotateY(float angle, Vec4f result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double x = getX() * cos + getZ() * sin; + double z = getZ() * cos - getX() * sin; + return result.set((float)x, getY(), (float)z, getW()); + } + public default Vec4f rotateZ(float angle){return rotateZ(angle, this);} + public default Vec4f rotateZ(float angle, Vec4f result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double x = cos * getX() - sin * getY(); + double y = sin * getX() + cos * getY(); + return result.set((float)x, (float)y, getZ(), getW()); + } + + public default Vec4f smoothStep(Vec4f value, float progress, Vec4f result){return smoothStep(value.getX(), value.getY(), value.getZ(), value.getW(), progress, result);} + public default Vec4f smoothStep(float x, float y, float z, float w, float progress, Vec4f result) + { + float t2 = progress * progress; + float t3 = t2 * progress; + float xPos = ((getX() + getX() - x - x) * t3 + (3.0F * x - 3.0F * getX()) * t2 + getX() * progress + getX()); + float yPos = ((getY() + getY() - y - y) * t3 + (3.0F * y - 3.0F * getY()) * t2 + getY() * progress + getY()); + float zPos = ((getZ() + getZ() - z - z) * t3 + (3.0F * z - 3.0F * getZ()) * t2 + getZ() * progress + getZ()); + float wPos = ((getW() + getW() - w - w) * t3 + (3.0F * w - 3.0F * getW()) * t2 + getW() * progress + getW()); + return result.set(xPos, yPos, zPos, wPos); + } + + public default Vec4f min(Vec4f other) {return min(other, this);} + public default Vec4f min(Vec4f other, Vec4f result){return min(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4f min(float x, float y, float z, float w) {return min(x, y, z, w, this);} + public default Vec4f min(float x, float y, float z, float w, Vec4f result){return result.set(Math.min(getX(), x), Math.min(getY(), y), Math.min(getZ(), z), Math.min(getW(), w));} + + public default Vec4f max(Vec4f other) {return max(other, this);} + public default Vec4f max(Vec4f other, Vec4f result){return max(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4f max(float x, float y, float z, float w) {return max(x, y, z, w, this);} + public default Vec4f max(float x, float y, float z, float w, Vec4f result){return result.set(Math.max(getX(), x), Math.max(getY(), y), Math.max(getZ(), z), Math.max(getZ(), z));} + + public default Vec4f difference(Vec4f other) {return difference(other, this);} + public default Vec4f difference(Vec4f other, Vec4f result){return difference(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4f difference(float x, float y, float z, float w) {return difference(x, y, z, w, this);} + public default Vec4f difference(float x, float y, float z, float w, Vec4f result){return result.set(getX() - x, getY() - y, getZ() - z, getW() - w);} + + @Override + public default Vec4f clamp(float min, float max){return clamp(min, max, ALL);} + public default Vec4f clamp(float min, float max, Vec4f result){return clamp(min, max, result, ALL);} + @Override + public default Vec4f clamp(float min, float max, int filter){return clamp(min, max, this, filter);} + public default Vec4f clamp(float min, float max, Vec4f result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()), (filter & W) == 0 ? getW() : MathUtils.clamp(min, max, getW()));} + + @Override + public default Vec4f store(ByteBuffer buffer) + { + buffer.putFloat(getX()).putFloat(getY()).putFloat(getZ()).putFloat(getW()); + return this; + } + + @Override + public default Vec4f load(ByteBuffer buffer) + { + return set(buffer.getFloat(), buffer.getFloat(), buffer.getFloat(), buffer.getFloat()); + } + + @Override + public default Vec4f store(FloatBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()).put(getW()); + return this; + } + + @Override + public default Vec4f load(FloatBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec4b asByte(){return isMutable() ? Vec4b.newMutable((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()), (byte)MathUtils.floor(getZ()), (byte)MathUtils.floor(getW())) : Vec4b.newVec((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()), (byte)MathUtils.floor(getZ()), (byte)MathUtils.floor(getW()));} + @Override + public default Vec4s asShort(){return isMutable() ? Vec4s.newMutable((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()), (short)MathUtils.floor(getZ()), (short)MathUtils.floor(getW())) : Vec4s.newVec((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()), (short)MathUtils.floor(getZ()), (short)MathUtils.floor(getW()));} + @Override + public default Vec4i asInt(){return isMutable() ? Vec4i.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()), MathUtils.floor(getW())) : Vec4i.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()), MathUtils.floor(getW()));} + @Override + public default Vec4l asLong() {return isMutable() ? Vec4l.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()), MathUtils.floor(getW())) : Vec4l.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()), MathUtils.floor(getW()));} + @Override + public default Vec4d asDouble(){return isMutable() ? Vec4d.newMutable(getX(), getY(), getZ(), getW()) : Vec4d.newVec(getX(), getY(), getZ(), getW());} + + + @Override + public default Vec4f asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec4f asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec4f copyAsMutable(){return newMutable(this);} + @Override + public default Vec4f copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4fImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4fImmutable.java new file mode 100644 index 0000000..e272831 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4fImmutable.java @@ -0,0 +1,124 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.util.Objects; + +public class Vec4fImmutable implements Vec4f +{ + final float x; + final float y; + final float z; + final float w; + + public Vec4fImmutable() + { + x = 0; + y = 0; + z = 0; + w = 0; + } + + public Vec4fImmutable(float value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4fImmutable(float x, float y, float z, float w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public float getX() + { + return x; + } + + @Override + public float getY() + { + return y; + } + + @Override + public float getZ() + { + return z; + } + + @Override + public float getW() + { + return w; + } + + @Override + public Vec4f setX(float x) + { + return this.x == x ? this : Vec4f.newVec(x, y, z, w); + } + + @Override + public Vec4f setY(float y) + { + return this.y == y ? this : Vec4f.newVec(x, y, z, w); + } + + @Override + public Vec4f setZ(float z) + { + return this.z == z ? this : Vec4f.newVec(x, y, z, w); + } + + @Override + public Vec4f setW(float w) + { + return this.w == w ? this : Vec4f.newVec(x, y, z, w); + } + + @Override + public Vec4f copy() + { + return Vec4f.newVec(this); + } + + @Override + public Vec4f set(float x, float y, float z, float w) + { + return this.x == x && this.y == y && this.z == z && this.w == w ? this : Vec4f.newVec(x, y, z, w); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4f) + { + Vec4f vec = (Vec4f)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4f[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4fMutable.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4fMutable.java new file mode 100644 index 0000000..e1b5215 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4fMutable.java @@ -0,0 +1,132 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.util.Objects; + +public class Vec4fMutable implements Vec4f +{ + float x; + float y; + float z; + float w; + + public Vec4fMutable() + { + x = 0; + y = 0; + z = 0; + w = 0; + } + + public Vec4fMutable(float value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4fMutable(float x, float y, float z, float w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public float getX() + { + return x; + } + + @Override + public float getY() + { + return y; + } + + @Override + public float getZ() + { + return z; + } + + @Override + public float getW() + { + return w; + } + + @Override + public Vec4f setX(float x) + { + this.x = x; + return this; + } + + @Override + public Vec4f setY(float y) + { + this.y = y; + return this; + } + + @Override + public Vec4f setZ(float z) + { + this.z = z; + return this; + } + + @Override + public Vec4f setW(float w) + { + this.w = w; + return this; + } + + @Override + public Vec4f copy() + { + return Vec4f.newMutable(this); + } + + @Override + public Vec4f set(float x, float y, float z, float w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4f) + { + Vec4f vec = (Vec4f)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4f[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vecf.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vecf.java new file mode 100644 index 0000000..3c9c329 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vecf.java @@ -0,0 +1,34 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.nio.FloatBuffer; + +import speiger.src.coreengine.math.vector.Vec; +import speiger.src.coreengine.math.vector.bytes.Vecb; +import speiger.src.coreengine.math.vector.doubles.Vecd; +import speiger.src.coreengine.math.vector.ints.Veci; +import speiger.src.coreengine.math.vector.longs.Vecl; +import speiger.src.coreengine.math.vector.shorts.Vecs; + +public interface Vecf extends Vec +{ + public Vecf set(float value); + public Vecf add(float value); + public Vecf sub(float value); + public Vecf multiply(float value); + public Vecf devide(float value); + public Vecf clamp(float min, float max); + public Vecf clamp(float min, float max, int filter); + + public float lengthSquared(); + public default float length(){return (float)Math.sqrt(lengthSquared());} + + public Vecf store(FloatBuffer buffer); + public Vecf load(FloatBuffer buffer); + public float[] asArray(); + + public Vecb asByte(); + public Vecs asShort(); + public Veci asInt(); + public Vecl asLong(); + public Vecd asDouble(); +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2i.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2i.java new file mode 100644 index 0000000..491bcb0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2i.java @@ -0,0 +1,163 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec2b; +import speiger.src.coreengine.math.vector.doubles.Vec2d; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.longs.Vec2l; +import speiger.src.coreengine.math.vector.shorts.Vec2s; + +public interface Vec2i extends Veci +{ + public static final Vec2i ZERO = newVec(); + public static final Vec2i MINUS_ONE = newVec(-1); + public static final Vec2i ONE = newVec(1); + + public static Vec2i newMutable(){return new Vec2iMutable();} + public static Vec2i newMutable(int value){return new Vec2iMutable(value);} + public static Vec2i newMutable(int x, int y){return new Vec2iMutable(x, y);} + public static Vec2i newMutable(Vec2i value){return newMutable(value.getX(), value.getY());} + + public static Vec2i newVec(){return new Vec2iImmutable();} + public static Vec2i newVec(int value){return new Vec2iImmutable(value);} + public static Vec2i newVec(int x, int y){return new Vec2iImmutable(x, y);} + public static Vec2i newVec(Vec2i value){return newVec(value.getX(), value.getY());} + + public int getX(); + public int getY(); + public Vec2i setX(int x); + public Vec2i setY(int y); + @Override + public default int[] asArray(){return new int[]{getX(), getY()};} + @Override + public Vec2i copy(); + @Override + public default Vec2i abs(){return set(Math.abs(getX()), Math.abs(getY()));} + @Override + public default Vec2i negate(){return set(0, 0);} + @Override + public default Vec2i invert(){return set(-getX(), -getY());}; + + @Override + public default Vec2i add(int value) {return add(value, value);} + public default Vec2i add(Vec2i value) {return add(value.getX(), value.getY());} + public default Vec2i add(int x, int y) {return set(x + getX(), y + getY());} + + @Override + public default Vec2i sub(int value){return sub(value, value);} + public default Vec2i sub(Vec2i value){return sub(value.getX(), value.getY());} + public default Vec2i sub(int x, int y) {return set(getX() - x, getY() - y);} + + @Override + public default Vec2i multiply(int value){return multiply(value, value);} + public default Vec2i multiply(Vec2i value){return multiply(value.getX(), value.getY());} + public default Vec2i multiply(int x, int y) {return set(x * getX(), y * getY());} + + @Override + public default Vec2i devide(int value){return devide(value, value);} + public default Vec2i devide(Vec2i value){return devide(value.getX(), value.getY());} + public default Vec2i devide(int x, int y){return set(getX() / x, getY() / y);} + + @Override + public default Vec2i set(int value){return set(value, value);}; + public default Vec2i set(Vec2i value){return set(value.getX(), value.getY());} + public Vec2i set(int x, int y); + + public default double distanceTo(Vec2i value){return distanceTo(value.getX(), value.getY());} + public default double distanceTo(int x, int y){return Math.sqrt(distanceToSquared(x, y));} + + public default long distanceToSquared(Vec2i value){return distanceToSquared(value.getX(), value.getY());} + public default long distanceToSquared(int x, int y) + { + long xPos = getX() - x; + long yPos = getY() - y; + return (xPos * xPos) + (yPos * yPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY());} + + public default long dotProduct(Vec2i value){return dotProduct(value.getX(), value.getY());} + public default long dotProduct(int x, int y){return (getX() * x) + (getY() * y);} + + public default Vec2i rotate(int angle, Vec2i center){return rotate(angle, center.getX(), center.getY());} + public default Vec2i rotate(int angle, int x, int y) + { + int xPos = getX() - x; + int yPos = getY() - y; + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + return set((int)((xPos * cos) + (yPos * sin) + x), (int)(-(xPos * sin) + (yPos * cos) + y)); + } + + public default Vec2i min(Vec2i other) {return min(other, this);} + public default Vec2i min(Vec2i other, Vec2i result){return min(other.getX(), other.getY(), result);} + public default Vec2i min(int x, int y) {return min(x, y, this);} + public default Vec2i min(int x, int y, Vec2i result){return result.set(Math.min(getX(), x), Math.min(getY(), y));} + + public default Vec2i max(Vec2i other) {return max(other, this);} + public default Vec2i max(Vec2i other, Vec2i result){return max(other.getX(), other.getY(), result);} + public default Vec2i max(int x, int y) {return max(x, y, this);} + public default Vec2i max(int x, int y, Vec2i result){return result.set(Math.max(getX(), x), Math.max(getY(), y));} + + public default Vec2i difference(Vec2i other) {return difference(other, this);} + public default Vec2i difference(Vec2i other, Vec2i result){return difference(other.getX(), other.getY(), result);} + public default Vec2i difference(int x, int y) {return difference(x, y, this);} + public default Vec2i difference(int x, int y, Vec2i result){return result.set(getX() - x, getY() - y);} + + @Override + public default Vec2i clamp(int min, int max){return clamp(min, max, ALL);} + public default Vec2i clamp(int min, int max, Vec2i result){return clamp(min, max, result, ALL);} + @Override + public default Vec2i clamp(int min, int max, int filter){return clamp(min, max, this, filter);} + public default Vec2i clamp(int min, int max, Vec2i result, int filter){return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()));} + + @Override + public default Vec2i store(ByteBuffer buffer) + { + buffer.putInt(getX()).putInt(getY()); + return this; + } + + @Override + public default Vec2i load(ByteBuffer buffer) + { + return set(buffer.getInt(), buffer.getInt()); + } + + @Override + public default Vec2i store(IntBuffer buffer) + { + buffer.put(getX()).put(getY()); + return this; + } + + @Override + public default Vec2i load(IntBuffer buffer) + { + return set(buffer.get(), buffer.get()); + } + + @Override + public default Vec2b asByte(){return isMutable() ? Vec2b.newMutable((byte)getX(), (byte)getY()) : Vec2b.newVec((byte)getX(), (byte)getY());} + @Override + public default Vec2s asShort(){return isMutable() ? Vec2s.newMutable((short)getX(), (short)getY()) : Vec2s.newVec((short)getX(), (short)getY());} + @Override + public default Vec2l asLong(){return isMutable() ? Vec2l.newMutable(getX(), getY()) : Vec2l.newVec(getX(), getY());} + @Override + public default Vec2f asFloat() {return isMutable() ? Vec2f.newMutable(getX(), getY()) : Vec2f.newVec(getX(), getY());} + @Override + public default Vec2d asDouble(){return isMutable() ? Vec2d.newMutable(getX(), getY()) : Vec2d.newVec(getX(), getY());} + + + @Override + public default Vec2i asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec2i asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec2i copyAsMutable(){return newMutable(this);} + @Override + public default Vec2i copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2iImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2iImmutable.java new file mode 100644 index 0000000..1435978 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2iImmutable.java @@ -0,0 +1,92 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.util.Objects; + +public class Vec2iImmutable implements Vec2i +{ + final int x; + final int y; + + public Vec2iImmutable() + { + x = 0; + y = 0; + } + + public Vec2iImmutable(int value) + { + x = value; + y = value; + } + + public Vec2iImmutable(int x, int y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public int getX() + { + return x; + } + + @Override + public int getY() + { + return y; + } + + @Override + public Vec2i setX(int x) + { + return this.x == x ? this : Vec2i.newVec(x, y); + } + + @Override + public Vec2i setY(int y) + { + return this.y == y ? this : Vec2i.newVec(x, y); + } + + @Override + public Vec2i copy() + { + return Vec2i.newVec(this); + } + + @Override + public Vec2i set(int x, int y) + { + return this.x == x && this.y == y ? this : Vec2i.newVec(x, y); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2i) + { + Vec2i vec = (Vec2i)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2i[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2iMutable.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2iMutable.java new file mode 100644 index 0000000..d21e5f7 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2iMutable.java @@ -0,0 +1,94 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.util.Objects; + +public class Vec2iMutable implements Vec2i +{ + int x; + int y; + + public Vec2iMutable() + { + } + + public Vec2iMutable(int value) + { + x = value; + y = value; + } + + public Vec2iMutable(int x, int y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public int getX() + { + return x; + } + + @Override + public int getY() + { + return y; + } + + @Override + public Vec2i setX(int x) + { + this.x = x; + return this; + } + + @Override + public Vec2i setY(int y) + { + this.y = y; + return this; + } + + @Override + public Vec2i copy() + { + return Vec2i.newMutable(this); + } + + @Override + public Vec2i set(int x, int y) + { + this.x = x; + this.y = y; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2i) + { + Vec2i vec = (Vec2i)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2i[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3i.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3i.java new file mode 100644 index 0000000..4b8a3cf --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3i.java @@ -0,0 +1,157 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec3b; +import speiger.src.coreengine.math.vector.doubles.Vec3d; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.longs.Vec3l; +import speiger.src.coreengine.math.vector.shorts.Vec3s; + + +public interface Vec3i extends Veci +{ + public static final Vec3i ZERO = newVec(); + public static final Vec3i MINUS_ONE = newVec(-1); + public static final Vec3i ONE = newVec(1); + + public static Vec3i newMutable(){return new Vec3iMutable();} + public static Vec3i newMutable(int value){return new Vec3iMutable(value);} + public static Vec3i newMutable(int x, int y, int z){return new Vec3iMutable(x, y, z);} + public static Vec3i newMutable(Vec3i vec){return newMutable(vec.getX(), vec.getY(), vec.getZ());} + + public static Vec3i newVec(){return new Vec3iImmutable();} + public static Vec3i newVec(int value){return new Vec3iImmutable(value);} + public static Vec3i newVec(int x, int y, int z){return new Vec3iImmutable(x, y, z);} + public static Vec3i newVec(Vec3i vec){return newVec(vec.getX(), vec.getY(), vec.getZ());} + + public int getX(); + public int getY(); + public int getZ(); + public Vec3i setX(int x); + public Vec3i setY(int y); + public Vec3i setZ(int z); + @Override + public default int[] asArray(){return new int[]{getX(), getY(), getZ()};} + + @Override + public Vec3i copy(); + @Override + public default Vec3i abs(){return set(Math.abs(getX()), Math.abs(getY()), Math.abs(getZ()));} + @Override + public default Vec3i negate(){return set(0, 0, 0);} + @Override + public default Vec3i invert(){return set(-getX(), -getY(), -getZ());} + @Override + public default Vec3i add(int value){return add(value, value, value);} + public default Vec3i add(Vec3i value){return add(value.getX(), value.getY(), value.getZ());} + public default Vec3i add(int x, int y, int z){return set(getX() + x, getY() + y, getZ() + z);} + + @Override + public default Vec3i sub(int value){return sub(value, value, value);} + public default Vec3i sub(Vec3i value){return sub(value.getX(), value.getY(), value.getZ());} + public default Vec3i sub(int x, int y, int z){return set(getX() - x, getY() - y, getZ() - z);} + + @Override + public default Vec3i multiply(int value){return multiply(value, value, value);} + public default Vec3i multiply(Vec3i value){return multiply(value.getX(), value.getY(), value.getZ());} + public default Vec3i multiply(int x, int y, int z){return set(getX() * x, getY() * y, getZ() * z);} + + @Override + public default Vec3i devide(int value){return devide(value, value, value);} + public default Vec3i devide(Vec3i value){return devide(value.getX(), value.getY(), value.getZ());} + public default Vec3i devide(int x, int y, int z){return set(getX() / x, getY() / y, getZ() / z);} + + @Override + public default Vec3i set(int value){return set(value, value, value);} + public default Vec3i set(Vec3i value){return set(value.getX(), value.getY(), value.getZ());} + public Vec3i set(int x, int y, int z); + + public default double distanceTo(Vec3i value){return distanceTo(value.getX(), value.getY(), value.getZ());} + public default double distanceTo(int x, int y, int z){return Math.sqrt(distanceToSquared(x, y, z));} + + public default long distanceToSquared(Vec3i value){return distanceToSquared(value.getX(), value.getY(), value.getZ());} + public default long distanceToSquared(int x, int y, int z) + { + long xPos = getX() - x; + long yPos = getY() - y; + long zPos = getZ() - z; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ());} + + public default long dotProduct(Vec3i value){return dotProduct(value.getX(), value.getY(), value.getZ());} + public default long dotProduct(int x, int y, int z){return (getX() * x) + (getY() * y) + (getZ() * z);} + + public default Vec3i min(Vec3i other) {return min(other, this);} + public default Vec3i min(Vec3i other, Vec3i result){return min(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3i min(int x, int y, int z) {return min(x, y, z, this);} + public default Vec3i min(int x, int y, int z, Vec3i result){return result.set(Math.min(getX(), x), Math.min(getY(), y), Math.min(getZ(), z));} + + public default Vec3i max(Vec3i other) {return max(other, this);} + public default Vec3i max(Vec3i other, Vec3i result){return max(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3i max(int x, int y, int z) {return max(x, y, z, this);} + public default Vec3i max(int x, int y, int z, Vec3i result){return result.set(Math.max(getX(), x), Math.max(getY(), y), Math.max(getZ(), z));} + + public default Vec3i difference(Vec3i other) {return difference(other, this);} + public default Vec3i difference(Vec3i other, Vec3i result){return difference(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3i difference(int x, int y, int z) {return difference(x, y, z, this);} + public default Vec3i difference(int x, int y, int z, Vec3i result){return result.set(getX() - x, getY() - y, getZ() - z);} + + @Override + public default Vec3i clamp(int min, int max){return clamp(min, max, ALL);} + public default Vec3i clamp(int min, int max, Vec3i result){return clamp(min, max, result, ALL);} + @Override + public default Vec3i clamp(int min, int max, int filter){return clamp(min, max, this, filter);} + public default Vec3i clamp(int min, int max, Vec3i result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()));} + + @Override + public default Vec3i store(ByteBuffer buffer) + { + buffer.putInt(getX()).putInt(getY()).putInt(getZ()); + return this; + } + + @Override + public default Vec3i load(ByteBuffer buffer) + { + return set(buffer.getInt(), buffer.getInt(), buffer.getInt()); + } + + @Override + public default Vec3i store(IntBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()); + return this; + } + + @Override + public default Vec3i load(IntBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec3b asByte(){return isMutable() ? Vec3b.newMutable((byte)getX(), (byte)getY(), (byte)getZ()) : Vec3b.newVec((byte)getX(), (byte)getY(), (byte)getZ());} + @Override + public default Vec3s asShort(){return isMutable() ? Vec3s.newMutable((short)getX(), (short)getY(), (short)getZ()) : Vec3s.newVec((short)getX(), (short)getY(), (short)getZ());} + @Override + public default Vec3l asLong(){return isMutable() ? Vec3l.newMutable(getX(), getY(), getZ()) : Vec3l.newVec(getX(), getY(), getZ());} + @Override + public default Vec3f asFloat() {return isMutable() ? Vec3f.newMutable(getX(), getY(), getZ()) : Vec3f.newVec(getX(), getY(), getZ());} + @Override + public default Vec3d asDouble(){return isMutable() ? Vec3d.newMutable(getX(), getY(), getZ()) : Vec3d.newVec(getX(), getY(), getZ());} + + + @Override + public default Vec3i asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec3i asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec3i copyAsMutable(){return newMutable(this);} + @Override + public default Vec3i copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3iImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3iImmutable.java new file mode 100644 index 0000000..09299ff --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3iImmutable.java @@ -0,0 +1,108 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.util.Objects; + +public class Vec3iImmutable implements Vec3i +{ + final int x; + final int y; + final int z; + + public Vec3iImmutable() + { + x = 0; + y = 0; + z = 0; + } + + public Vec3iImmutable(int value) + { + x = value; + y = value; + z = value; + } + + public Vec3iImmutable(int x, int y, int z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public int getX() + { + return x; + } + + @Override + public int getY() + { + return y; + } + + @Override + public int getZ() + { + return z; + } + + @Override + public Vec3i setX(int x) + { + return this.x == x ? this : Vec3i.newVec(x, y, z); + } + + @Override + public Vec3i setY(int y) + { + return this.y == y ? this : Vec3i.newVec(x, y, z); + } + + @Override + public Vec3i setZ(int z) + { + return this.z == z ? this : Vec3i.newVec(x, y, z); + } + + @Override + public Vec3i copy() + { + return Vec3i.newVec(this); + } + + @Override + public Vec3i set(int x, int y, int z) + { + return this.x == x && this.y == y && this.z == z ? this : Vec3i.newVec(x, y, z); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3i) + { + Vec3i vec = (Vec3i)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3i[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3iMutable.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3iMutable.java new file mode 100644 index 0000000..1aebf3d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3iMutable.java @@ -0,0 +1,111 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.util.Objects; + +public class Vec3iMutable implements Vec3i +{ + int x; + int y; + int z; + + public Vec3iMutable() + { + } + + public Vec3iMutable(int value) + { + x = value; + y = value; + z = value; + } + + public Vec3iMutable(int x, int y, int z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public int getX() + { + return x; + } + + @Override + public int getY() + { + return y; + } + + @Override + public int getZ() + { + return z; + } + + @Override + public Vec3i setX(int x) + { + this.x = x; + return this; + } + + @Override + public Vec3i setY(int y) + { + this.y = y; + return this; + } + + @Override + public Vec3i setZ(int z) + { + this.z = z; + return this; + } + + @Override + public Vec3i copy() + { + return Vec3i.newMutable(this); + } + + @Override + public Vec3i set(int x, int y, int z) + { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3i) + { + Vec3i vec = (Vec3i)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3i[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4i.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4i.java new file mode 100644 index 0000000..cd9f345 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4i.java @@ -0,0 +1,158 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec4b; +import speiger.src.coreengine.math.vector.doubles.Vec4d; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.longs.Vec4l; +import speiger.src.coreengine.math.vector.shorts.Vec4s; + +public interface Vec4i extends Veci +{ + public static final Vec4i ZERO = newVec(); + public static final Vec4i MINUS_ONE = newVec(-1); + public static final Vec4i ONE = newVec(1); + + public static Vec4i newMutable(){return new Vec4iMutable();} + public static Vec4i newMutable(int value){return new Vec4iMutable(value);} + public static Vec4i newMutable(int x, int y, int z, int w){return new Vec4iMutable(x, y, z, w);} + public static Vec4i newMutable(Vec4i vec){return newMutable(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public static Vec4i newVec(){return new Vec4iImmutable();} + public static Vec4i newVec(int value){return new Vec4iImmutable(value);} + public static Vec4i newVec(int x, int y, int z, int w){return new Vec4iImmutable(x, y, z, w);} + public static Vec4i newVec(Vec4i vec){return newVec(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public int getX(); + public int getY(); + public int getZ(); + public int getW(); + public Vec4i setX(int x); + public Vec4i setY(int y); + public Vec4i setZ(int z); + public Vec4i setW(int w); + @Override + public default int[] asArray(){return new int[]{getX(), getY(), getZ(), getW()};} + @Override + public Vec4i copy(); + @Override + public default Vec4i abs(){return set(Math.abs(getX()), Math.abs(getY()), Math.abs(getZ()), Math.abs(getW()));} + @Override + public default Vec4i negate(){return set(0, 0, 0, 0);} + @Override + public default Vec4i invert(){return set(-getX(), -getY(), -getZ(), -getW());} + + @Override + public default Vec4i add(int value){return add(value, value, value, value);} + public default Vec4i add(Vec4i value){return add(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4i add(int x, int y, int z, int w){return set(getX() + x, getY() + y, getZ() + z, getW() + w);} + + @Override + public default Vec4i sub(int value){return sub(value, value, value, value);} + public default Vec4i sub(Vec4i value){return sub(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4i sub(int x, int y, int z, int w){return set(getX() - x, getY() - y, getZ() - z, getW() - w);} + + @Override + public default Vec4i multiply(int value){return multiply(value, value, value, value);} + public default Vec4i multiply(Vec4i value){return multiply(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4i multiply(int x, int y, int z, int w){return set(getX() * x, getY() * y, getZ() * z, getW() * w);} + + @Override + public default Vec4i devide(int value){return devide(value, value, value, value);} + public default Vec4i devide(Vec4i value){return devide(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4i devide(int x, int y, int z, int w){return set(getX() / x, getY() / y, getZ() / z, getW() / w);} + + @Override + public default Vec4i set(int value){return set(value, value, value, value);} + public default Vec4i set(Vec4i value){return set(value.getX(), value.getY(), value.getZ(), value.getW());} + public Vec4i set(int x, int y, int z, int w); + + public default double distanceTo(Vec4i value){return distanceTo(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double distanceTo(int x, int y, int z, int w){return Math.sqrt(distanceTo(x, y, z, w));} + + public default long distanceToSquared(Vec4i value){return distanceToSquared(value.getX(), value.getY(), value.getZ(), value.getW());} + public default long distanceToSquared(int x, int y, int z, int w) + { + long xPos = getX() - x; + long yPos = getY() - y; + long zPos = getZ() - z; + long wPos = getW() - w; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos) + (wPos * wPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()) + (getW() * getW());} + + public default long dotProduct(Vec4i value){return dotProduct(value.getX(), value.getY(), value.getZ(), value.getW());} + public default long dotProduct(int x, int y, int z, int w){return (getX() * x) + (getY() * y) + (getZ() * z) + (getW() * w);}; + + public default Vec4i min(Vec4i other) {return min(other, this);} + public default Vec4i min(Vec4i other, Vec4i result){return min(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4i min(int x, int y, int z, int w) {return min(x, y, z, w, this);} + public default Vec4i min(int x, int y, int z, int w, Vec4i result){return result.set(Math.min(getX(), x), Math.min(getY(), y), Math.min(getZ(), z), Math.min(getW(), w));} + + public default Vec4i max(Vec4i other) {return max(other, this);} + public default Vec4i max(Vec4i other, Vec4i result){return max(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4i max(int x, int y, int z, int w) {return max(x, y, z, w, this);} + public default Vec4i max(int x, int y, int z, int w, Vec4i result){return result.set(Math.max(getX(), x), Math.max(getY(), y), Math.max(getZ(), z), Math.max(getZ(), z));} + + public default Vec4i difference(Vec4i other) {return difference(other, this);} + public default Vec4i difference(Vec4i other, Vec4i result){return difference(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4i difference(int x, int y, int z, int w) {return difference(x, y, z, w, this);} + public default Vec4i difference(int x, int y, int z, int w, Vec4i result){return result.set(getX() - x, getY() - y, getZ() - z, getW() - w);} + + @Override + public default Vec4i clamp(int min, int max){return clamp(min, max, ALL);} + public default Vec4i clamp(int min, int max, Vec4i result){return clamp(min, max, result, ALL);} + @Override + public default Vec4i clamp(int min, int max, int filter){return clamp(min, max, this, filter);} + public default Vec4i clamp(int min, int max, Vec4i result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()), (filter & W) == 0 ? getW() : MathUtils.clamp(min, max, getW()));} + + @Override + public default Vec4i store(ByteBuffer buffer) + { + buffer.putInt(getX()).putInt(getY()).putInt(getZ()).putInt(getW()); + return this; + } + + @Override + public default Vec4i load(ByteBuffer buffer) + { + return set(buffer.getInt(), buffer.getInt(), buffer.getInt(), buffer.getInt()); + } + + @Override + public default Vec4i store(IntBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()).put(getW()); + return this; + } + + @Override + public default Vec4i load(IntBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec4b asByte(){return isMutable() ? Vec4b.newMutable((byte)getX(), (byte)getY(), (byte)getZ(), (byte)getW()) : Vec4b.newVec((byte)getX(), (byte)getY(), (byte)getZ(), (byte)getW());} + @Override + public default Vec4s asShort(){return isMutable() ? Vec4s.newMutable((short)getX(), (short)getY(), (short)getZ(), (short)getW()) : Vec4s.newVec((short)getX(), (short)getY(), (short)getZ(), (short)getW());} + @Override + public default Vec4l asLong(){return isMutable() ? Vec4l.newMutable(getX(), getY(), getZ(), getW()) : Vec4l.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4f asFloat() {return isMutable() ? Vec4f.newMutable(getX(), getY(), getZ(), getW()) : Vec4f.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4d asDouble(){return isMutable() ? Vec4d.newMutable(getX(), getY(), getZ(), getW()) : Vec4d.newVec(getX(), getY(), getZ(), getW());} + + @Override + public default Vec4i asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec4i asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec4i copyAsMutable(){return newMutable(this);} + @Override + public default Vec4i copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4iImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4iImmutable.java new file mode 100644 index 0000000..1d03c74 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4iImmutable.java @@ -0,0 +1,124 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.util.Objects; + +public class Vec4iImmutable implements Vec4i +{ + final int x; + final int y; + final int z; + final int w; + + public Vec4iImmutable() + { + x = 0; + y = 0; + z = 0; + w = 0; + } + + public Vec4iImmutable(int value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4iImmutable(int x, int y, int z, int w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public int getX() + { + return x; + } + + @Override + public int getY() + { + return y; + } + + @Override + public int getZ() + { + return z; + } + + @Override + public int getW() + { + return w; + } + + @Override + public Vec4i setX(int x) + { + return this.x == x ? this : Vec4i.newVec(x, y, z, w); + } + + @Override + public Vec4i setY(int y) + { + return this.y == y ? this : Vec4i.newVec(x, y, z, w); + } + + @Override + public Vec4i setZ(int z) + { + return this.z == z ? this : Vec4i.newVec(x, y, z, w); + } + + @Override + public Vec4i setW(int w) + { + return this.w == w ? this : Vec4i.newVec(x, y, z, w); + } + + @Override + public Vec4i copy() + { + return Vec4i.newVec(this); + } + + @Override + public Vec4i set(int x, int y, int z, int w) + { + return this.x == x && this.y == y && this.z == z && this.w == w ? this : Vec4i.newVec(x, y, z, w); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4i) + { + Vec4i vec = (Vec4i)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4i[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4iMutable.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4iMutable.java new file mode 100644 index 0000000..2a8bbd3 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4iMutable.java @@ -0,0 +1,128 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.util.Objects; + +public class Vec4iMutable implements Vec4i +{ + int x; + int y; + int z; + int w; + + public Vec4iMutable() + { + } + + public Vec4iMutable(int value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4iMutable(int x, int y, int z, int w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public int getX() + { + return x; + } + + @Override + public int getY() + { + return y; + } + + @Override + public int getZ() + { + return z; + } + + @Override + public int getW() + { + return w; + } + + @Override + public Vec4i setX(int x) + { + this.x = x; + return this; + } + + @Override + public Vec4i setY(int y) + { + this.y = y; + return this; + } + + @Override + public Vec4i setZ(int z) + { + this.z = z; + return this; + } + + @Override + public Vec4i setW(int w) + { + this.w = w; + return this; + } + + @Override + public Vec4i copy() + { + return Vec4i.newMutable(this); + } + + @Override + public Vec4i set(int x, int y, int z, int w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4i) + { + Vec4i vec = (Vec4i)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4i[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Veci.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Veci.java new file mode 100644 index 0000000..91ce212 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Veci.java @@ -0,0 +1,34 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.nio.IntBuffer; + +import speiger.src.coreengine.math.vector.Vec; +import speiger.src.coreengine.math.vector.bytes.Vecb; +import speiger.src.coreengine.math.vector.doubles.Vecd; +import speiger.src.coreengine.math.vector.floats.Vecf; +import speiger.src.coreengine.math.vector.longs.Vecl; +import speiger.src.coreengine.math.vector.shorts.Vecs; + +public interface Veci extends Vec +{ + public Veci set(int value); + public Veci add(int value); + public Veci sub(int value); + public Veci multiply(int value); + public Veci devide(int value); + public Veci clamp(int min, int max); + public Veci clamp(int min, int max, int filter); + + public long lengthSquared(); + public default double length(){return Math.sqrt(lengthSquared());} + + public Veci store(IntBuffer buffer); + public Veci load(IntBuffer buffer); + public int[] asArray(); + + public Vecb asByte(); + public Vecs asShort(); + public Vecl asLong(); + public Vecf asFloat(); + public Vecd asDouble(); +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2l.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2l.java new file mode 100644 index 0000000..6e0bc6d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2l.java @@ -0,0 +1,163 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.nio.ByteBuffer; +import java.nio.LongBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec2b; +import speiger.src.coreengine.math.vector.doubles.Vec2d; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.math.vector.shorts.Vec2s; + +public interface Vec2l extends Vecl +{ + public static final Vec2l ZERO = newVec(); + public static final Vec2l MINUS_ONE = newVec(-1L); + public static final Vec2l ONE = newVec(1L); + + public static Vec2l newMutable(){return new Vec2lMutable();} + public static Vec2l newMutable(long value){return new Vec2lMutable(value);} + public static Vec2l newMutable(long x, long y){return new Vec2lMutable(x, y);} + public static Vec2l newMutable(Vec2l value){return newMutable(value.getX(), value.getY());} + + public static Vec2l newVec(){return new Vec2lImmutable();} + public static Vec2l newVec(long value){return new Vec2lImmutable(value);} + public static Vec2l newVec(long x, long y){return new Vec2lImmutable(x, y);} + public static Vec2l newVec(Vec2l value){return newVec(value.getX(), value.getY());} + + public long getX(); + public long getY(); + public Vec2l setX(long x); + public Vec2l setY(long y); + @Override + public default long[] asArray(){return new long[]{getX(), getY()};} + @Override + public Vec2l copy(); + @Override + public default Vec2l abs(){return set(Math.abs(getX()), Math.abs(getY()));} + @Override + public default Vec2l negate(){return set(0L, 0L);} + @Override + public default Vec2l invert(){return set(-getX(), -getY());}; + + @Override + public default Vec2l add(long value) {return add(value, value);} + public default Vec2l add(Vec2l value) {return add(value.getX(), value.getY());} + public default Vec2l add(long x, long y) {return set(x + getX(), y + getY());} + + @Override + public default Vec2l sub(long value){return sub(value, value);} + public default Vec2l sub(Vec2l value){return sub(value.getX(), value.getY());} + public default Vec2l sub(long x, long y) {return set(getX() - x, getY() - y);} + + @Override + public default Vec2l multiply(long value){return multiply(value, value);} + public default Vec2l multiply(Vec2l value){return multiply(value.getX(), value.getY());} + public default Vec2l multiply(long x, long y) {return set(x * getX(), y * getY());} + + @Override + public default Vec2l devide(long value){return devide(value, value);} + public default Vec2l devide(Vec2l value){return devide(value.getX(), value.getY());} + public default Vec2l devide(long x, long y){return set(getX() / x, getY() / y);} + + @Override + public default Vec2l set(long value){return set(value, value);}; + public default Vec2l set(Vec2l value){return set(value.getX(), value.getY());} + public Vec2l set(long x, long y); + + public default double distanceTo(Vec2l value){return distanceTo(value.getX(), value.getY());} + public default double distanceTo(long x, long y){return Math.sqrt(distanceToSquared(x, y));} + + public default long distanceToSquared(Vec2l value){return distanceToSquared(value.getX(), value.getY());} + public default long distanceToSquared(long x, long y) + { + long xPos = getX() - x; + long yPos = getY() - y; + return (xPos * xPos) + (yPos * yPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY());} + + public default long dotProduct(Vec2l value){return dotProduct(value.getX(), value.getY());} + public default long dotProduct(long x, long y){return (getX() * x) + (getY() * y);} + + public default Vec2l rotate(long angle, Vec2l center){return rotate(angle, center.getX(), center.getY());} + public default Vec2l rotate(long angle, long x, long y) + { + long xPos = getX() - x; + long yPos = getY() - y; + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + return set((long)((xPos * cos) + (yPos * sin) + x), (long)(-(xPos * sin) + (yPos * cos) + y)); + } + + public default Vec2l min(Vec2l other) {return min(other, this);} + public default Vec2l min(Vec2l other, Vec2l result){return min(other.getX(), other.getY(), result);} + public default Vec2l min(long x, long y) {return min(x, y, this);} + public default Vec2l min(long x, long y, Vec2l result){return result.set(Math.min(getX(), x), Math.min(getY(), y));} + + public default Vec2l max(Vec2l other) {return max(other, this);} + public default Vec2l max(Vec2l other, Vec2l result){return max(other.getX(), other.getY(), result);} + public default Vec2l max(long x, long y) {return max(x, y, this);} + public default Vec2l max(long x, long y, Vec2l result){return result.set(Math.max(getX(), x), Math.max(getY(), y));} + + public default Vec2l difference(Vec2l other) {return difference(other, this);} + public default Vec2l difference(Vec2l other, Vec2l result){return difference(other.getX(), other.getY(), result);} + public default Vec2l difference(long x, long y) {return difference(x, y, this);} + public default Vec2l difference(long x, long y, Vec2l result){return result.set(getX() - x, getY() - y);} + + @Override + public default Vec2l clamp(long min, long max){return clamp(min, max, ALL);} + public default Vec2l clamp(long min, long max, Vec2l result){return clamp(min, max, result, ALL);} + @Override + public default Vec2l clamp(long min, long max, int filter){return clamp(min, max, this, filter);} + public default Vec2l clamp(long min, long max, Vec2l result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()));} + + @Override + public default Vec2l store(ByteBuffer buffer) + { + buffer.putLong(getX()).putLong(getY()); + return this; + } + + @Override + public default Vec2l load(ByteBuffer buffer) + { + return set(buffer.getLong(), buffer.getLong()); + } + + @Override + public default Vec2l store(LongBuffer buffer) + { + buffer.put(getX()).put(getY()); + return this; + } + + @Override + public default Vec2l load(LongBuffer buffer) + { + return set(buffer.get(), buffer.get()); + } + + @Override + public default Vec2b asByte(){return isMutable() ? Vec2b.newMutable((byte)getX(), (byte)getY()) : Vec2b.newVec((byte)getX(), (byte)getY());} + @Override + public default Vec2s asShort(){return isMutable() ? Vec2s.newMutable((short)getX(), (short)getY()) : Vec2s.newVec((short)getX(), (short)getY());} + @Override + public default Vec2i asInt(){return isMutable() ? Vec2i.newMutable((int)getX(), (int)getY()) : Vec2i.newVec((int)getX(), (int)getY());} + @Override + public default Vec2f asFloat() {return isMutable() ? Vec2f.newMutable(getX(), getY()) : Vec2f.newVec(getX(), getY());} + @Override + public default Vec2d asDouble(){return isMutable() ? Vec2d.newMutable(getX(), getY()) : Vec2d.newVec(getX(), getY());} + + + @Override + public default Vec2l asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec2l asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec2l copyAsMutable(){return newMutable(this);} + @Override + public default Vec2l copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2lImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2lImmutable.java new file mode 100644 index 0000000..9e66de8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2lImmutable.java @@ -0,0 +1,92 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.util.Objects; + +public class Vec2lImmutable implements Vec2l +{ + final long x; + final long y; + + public Vec2lImmutable() + { + x = 0; + y = 0; + } + + public Vec2lImmutable(long value) + { + x = value; + y = value; + } + + public Vec2lImmutable(long x, long y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public long getX() + { + return x; + } + + @Override + public long getY() + { + return y; + } + + @Override + public Vec2l setX(long x) + { + return this.x == x ? this : Vec2l.newVec(x, y); + } + + @Override + public Vec2l setY(long y) + { + return this.y == y ? this : Vec2l.newVec(x, y); + } + + @Override + public Vec2l copy() + { + return Vec2l.newVec(this); + } + + @Override + public Vec2l set(long x, long y) + { + return this.x == x && this.y == y ? this : Vec2l.newVec(x, y); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2l) + { + Vec2l vec = (Vec2l)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2l[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2lMutable.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2lMutable.java new file mode 100644 index 0000000..6c5f7c6 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2lMutable.java @@ -0,0 +1,94 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.util.Objects; + +public class Vec2lMutable implements Vec2l +{ + long x; + long y; + + public Vec2lMutable() + { + } + + public Vec2lMutable(long value) + { + x = value; + y = value; + } + + public Vec2lMutable(long x, long y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public long getX() + { + return x; + } + + @Override + public long getY() + { + return y; + } + + @Override + public Vec2l setX(long x) + { + this.x = x; + return this; + } + + @Override + public Vec2l setY(long y) + { + this.y = y; + return this; + } + + @Override + public Vec2l copy() + { + return Vec2l.newMutable(this); + } + + @Override + public Vec2l set(long x, long y) + { + this.x = x; + this.y = y; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2l) + { + Vec2l vec = (Vec2l)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2l[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3l.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3l.java new file mode 100644 index 0000000..d7c1da7 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3l.java @@ -0,0 +1,156 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.nio.ByteBuffer; +import java.nio.LongBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec3b; +import speiger.src.coreengine.math.vector.doubles.Vec3d; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.ints.Vec3i; +import speiger.src.coreengine.math.vector.shorts.Vec3s; + + +public interface Vec3l extends Vecl +{ + public static final Vec3l ZERO = newVec(); + public static final Vec3l MINUS_ONE = newVec(-1L); + public static final Vec3l ONE = newVec(1L); + + public static Vec3l newMutable(){return new Vec3lMutable();} + public static Vec3l newMutable(long value){return new Vec3lMutable(value);} + public static Vec3l newMutable(long x, long y, long z){return new Vec3lMutable(x, y, z);} + public static Vec3l newMutable(Vec3l vec){return newMutable(vec.getX(), vec.getY(), vec.getZ());} + + public static Vec3l newVec(){return new Vec3lImmutable();} + public static Vec3l newVec(long value){return new Vec3lImmutable(value);} + public static Vec3l newVec(long x, long y, long z){return new Vec3lImmutable(x, y, z);} + public static Vec3l newVec(Vec3l vec){return newVec(vec.getX(), vec.getY(), vec.getZ());} + + public long getX(); + public long getY(); + public long getZ(); + public Vec3l setX(long x); + public Vec3l setY(long y); + public Vec3l setZ(long z); + @Override + public default long[] asArray(){return new long[]{getX(), getY(), getZ()};} + + @Override + public Vec3l copy(); + @Override + public default Vec3l abs(){return set(Math.abs(getX()), Math.abs(getY()), Math.abs(getZ()));} + @Override + public default Vec3l negate(){return set(0L, 0L, 0L);} + @Override + public default Vec3l invert(){return set(-getX(), -getY(), -getZ());} + @Override + public default Vec3l add(long value){return add(value, value, value);} + public default Vec3l add(Vec3l value){return add(value.getX(), value.getY(), value.getZ());} + public default Vec3l add(long x, long y, long z){return set(getX() + x, getY() + y, getZ() + z);} + + @Override + public default Vec3l sub(long value){return sub(value, value, value);} + public default Vec3l sub(Vec3l value){return sub(value.getX(), value.getY(), value.getZ());} + public default Vec3l sub(long x, long y, long z){return set(getX() - x, getY() - y, getZ() - z);} + + @Override + public default Vec3l multiply(long value){return multiply(value, value, value);} + public default Vec3l multiply(Vec3l value){return multiply(value.getX(), value.getY(), value.getZ());} + public default Vec3l multiply(long x, long y, long z){return set(getX() * x, getY() * y, getZ() * z);} + + @Override + public default Vec3l devide(long value){return devide(value, value, value);} + public default Vec3l devide(Vec3l value){return devide(value.getX(), value.getY(), value.getZ());} + public default Vec3l devide(long x, long y, long z){return set(getX() / x, getY() / y, getZ() / z);} + + @Override + public default Vec3l set(long value){return set(value, value, value);} + public default Vec3l set(Vec3l value){return set(value.getX(), value.getY(), value.getZ());} + public Vec3l set(long x, long y, long z); + + public default double distanceTo(Vec3l value){return distanceTo(value.getX(), value.getY(), value.getZ());} + public default double distanceTo(long x, long y, long z){return Math.sqrt(distanceToSquared(x, y, z));} + + public default long distanceToSquared(Vec3l value){return distanceToSquared(value.getX(), value.getY(), value.getZ());} + public default long distanceToSquared(long x, long y, long z) + { + long xPos = getX() - x; + long yPos = getY() - y; + long zPos = getZ() - z; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ());} + + public default long dotProduct(Vec3l value){return dotProduct(value.getX(), value.getY(), value.getZ());} + public default long dotProduct(long x, long y, long z){return (getX() * x) + (getY() * y) + (getZ() * z);} + + public default Vec3l min(Vec3l other) {return min(other, this);} + public default Vec3l min(Vec3l other, Vec3l result){return min(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3l min(long x, long y, long z) {return min(x, y, z, this);} + public default Vec3l min(long x, long y, long z, Vec3l result){return result.set(Math.min(getX(), x), Math.min(getY(), y), Math.min(getZ(), z));} + + public default Vec3l max(Vec3l other) {return max(other, this);} + public default Vec3l max(Vec3l other, Vec3l result){return max(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3l max(long x, long y, long z) {return max(x, y, z, this);} + public default Vec3l max(long x, long y, long z, Vec3l result){return result.set(Math.max(getX(), x), Math.max(getY(), y), Math.max(getZ(), z));} + + public default Vec3l difference(Vec3l other) {return difference(other, this);} + public default Vec3l difference(Vec3l other, Vec3l result){return difference(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3l difference(long x, long y, long z) {return difference(x, y, z, this);} + public default Vec3l difference(long x, long y, long z, Vec3l result){return result.set(getX() - x, getY() - y, getZ() - z);} + + @Override + public default Vec3l clamp(long min, long max){return clamp(min, max, ALL);} + public default Vec3l clamp(long min, long max, Vec3l result){return clamp(min, max, result, ALL);} + @Override + public default Vec3l clamp(long min, long max, int filter){return clamp(min, max, this, filter);} + public default Vec3l clamp(long min, long max, Vec3l result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()));} + + @Override + public default Vec3l store(ByteBuffer buffer) + { + buffer.putLong(getX()).putLong(getY()).putLong(getZ()); + return this; + } + + @Override + public default Vec3l load(ByteBuffer buffer) + { + return set(buffer.getLong(), buffer.getLong(), buffer.getLong()); + } + + @Override + public default Vec3l store(LongBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()); + return this; + } + + @Override + public default Vec3l load(LongBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec3b asByte(){return isMutable() ? Vec3b.newMutable((byte)getX(), (byte)getY(), (byte)getZ()) : Vec3b.newVec((byte)getX(), (byte)getY(), (byte)getZ());} + @Override + public default Vec3s asShort(){return isMutable() ? Vec3s.newMutable((short)getX(), (short)getY(), (short)getZ()) : Vec3s.newVec((short)getX(), (short)getY(), (short)getZ());} + @Override + public default Vec3i asInt(){return isMutable() ? Vec3i.newMutable((int)getX(), (int)getY(), (int)getZ()) : Vec3i.newVec((int)getX(), (int)getY(), (int)getZ());} + @Override + public default Vec3f asFloat() {return isMutable() ? Vec3f.newMutable(getX(), getY(), getZ()) : Vec3f.newVec(getX(), getY(), getZ());} + @Override + public default Vec3d asDouble(){return isMutable() ? Vec3d.newMutable(getX(), getY(), getZ()) : Vec3d.newVec(getX(), getY(), getZ());} + + @Override + public default Vec3l asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec3l asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec3l copyAsMutable(){return newMutable(this);} + @Override + public default Vec3l copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3lImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3lImmutable.java new file mode 100644 index 0000000..f66bb26 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3lImmutable.java @@ -0,0 +1,108 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.util.Objects; + +public class Vec3lImmutable implements Vec3l +{ + final long x; + final long y; + final long z; + + public Vec3lImmutable() + { + x = 0; + y = 0; + z = 0; + } + + public Vec3lImmutable(long value) + { + x = value; + y = value; + z = value; + } + + public Vec3lImmutable(long x, long y, long z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public long getX() + { + return x; + } + + @Override + public long getY() + { + return y; + } + + @Override + public long getZ() + { + return z; + } + + @Override + public Vec3l setX(long x) + { + return this.x == x ? this : Vec3l.newVec(x, y, z); + } + + @Override + public Vec3l setY(long y) + { + return this.y == y ? this : Vec3l.newVec(x, y, z); + } + + @Override + public Vec3l setZ(long z) + { + return this.z == z ? this : Vec3l.newVec(x, y, z); + } + + @Override + public Vec3l copy() + { + return Vec3l.newVec(this); + } + + @Override + public Vec3l set(long x, long y, long z) + { + return this.x == x && this.y == y && this.z == z ? this : Vec3l.newVec(x, y, z); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3l) + { + Vec3l vec = (Vec3l)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3l[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3lMutable.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3lMutable.java new file mode 100644 index 0000000..2cbf789 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3lMutable.java @@ -0,0 +1,111 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.util.Objects; + +public class Vec3lMutable implements Vec3l +{ + long x; + long y; + long z; + + public Vec3lMutable() + { + } + + public Vec3lMutable(long value) + { + x = value; + y = value; + z = value; + } + + public Vec3lMutable(long x, long y, long z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public long getX() + { + return x; + } + + @Override + public long getY() + { + return y; + } + + @Override + public long getZ() + { + return z; + } + + @Override + public Vec3l setX(long x) + { + this.x = x; + return this; + } + + @Override + public Vec3l setY(long y) + { + this.y = y; + return this; + } + + @Override + public Vec3l setZ(long z) + { + this.z = z; + return this; + } + + @Override + public Vec3l copy() + { + return Vec3l.newMutable(this); + } + + @Override + public Vec3l set(long x, long y, long z) + { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3l) + { + Vec3l vec = (Vec3l)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3l[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4l.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4l.java new file mode 100644 index 0000000..6c070f5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4l.java @@ -0,0 +1,158 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.nio.ByteBuffer; +import java.nio.LongBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec4b; +import speiger.src.coreengine.math.vector.doubles.Vec4d; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.ints.Vec4i; +import speiger.src.coreengine.math.vector.shorts.Vec4s; + +public interface Vec4l extends Vecl +{ + public static final Vec4l ZERO = newVec(); + public static final Vec4l MINUS_ONE = newVec(-1L); + public static final Vec4l ONE = newVec(1L); + + public static Vec4l newMutable(){return new Vec4lMutable();} + public static Vec4l newMutable(long value){return new Vec4lMutable(value);} + public static Vec4l newMutable(long x, long y, long z, long w){return new Vec4lMutable(x, y, z, w);} + public static Vec4l newMutable(Vec4l vec){return newMutable(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public static Vec4l newVec(){return new Vec4lImmutable();} + public static Vec4l newVec(long value){return new Vec4lImmutable(value);} + public static Vec4l newVec(long x, long y, long z, long w){return new Vec4lImmutable(x, y, z, w);} + public static Vec4l newVec(Vec4l vec){return newVec(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public long getX(); + public long getY(); + public long getZ(); + public long getW(); + public Vec4l setX(long x); + public Vec4l setY(long y); + public Vec4l setZ(long z); + public Vec4l setW(long w); + @Override + public default long[] asArray(){return new long[]{getX(), getY(), getZ(), getW()};} + @Override + public Vec4l copy(); + @Override + public default Vec4l abs(){return set(Math.abs(getX()), Math.abs(getY()), Math.abs(getZ()), Math.abs(getW()));} + @Override + public default Vec4l negate(){return set(0L, 0L, 0L, 0L);} + @Override + public default Vec4l invert(){return set(-getX(), -getY(), -getZ(), -getW());} + + @Override + public default Vec4l add(long value){return add(value, value, value, value);} + public default Vec4l add(Vec4l value){return add(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4l add(long x, long y, long z, long w){return set(getX() + x, getY() + y, getZ() + z, getW() + w);} + + @Override + public default Vec4l sub(long value){return sub(value, value, value, value);} + public default Vec4l sub(Vec4l value){return sub(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4l sub(long x, long y, long z, long w){return set(getX() - x, getY() - y, getZ() - z, getW() - w);} + + @Override + public default Vec4l multiply(long value){return multiply(value, value, value, value);} + public default Vec4l multiply(Vec4l value){return multiply(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4l multiply(long x, long y, long z, long w){return set(getX() * x, getY() * y, getZ() * z, getW() * w);} + + @Override + public default Vec4l devide(long value){return devide(value, value, value, value);} + public default Vec4l devide(Vec4l value){return devide(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4l devide(long x, long y, long z, long w){return set(getX() / x, getY() / y, getZ() / z, getW() / w);} + + @Override + public default Vec4l set(long value){return set(value, value, value, value);} + public default Vec4l set(Vec4l value){return set(value.getX(), value.getY(), value.getZ(), value.getW());} + public Vec4l set(long x, long y, long z, long w); + + public default double distanceTo(Vec4l value){return distanceTo(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double distanceTo(long x, long y, long z, long w){return Math.sqrt(distanceTo(x, y, z, w));} + + public default long distanceToSquared(Vec4l value){return distanceToSquared(value.getX(), value.getY(), value.getZ(), value.getW());} + public default long distanceToSquared(long x, long y, long z, long w) + { + long xPos = getX() - x; + long yPos = getY() - y; + long zPos = getZ() - z; + long wPos = getW() - w; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos) + (wPos * wPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()) + (getW() * getW());} + + public default long dotProduct(Vec4l value){return dotProduct(value.getX(), value.getY(), value.getZ(), value.getW());} + public default long dotProduct(long x, long y, long z, long w){return (getX() * x) + (getY() * y) + (getZ() * z) + (getW() * w);}; + + public default Vec4l min(Vec4l other) {return min(other, this);} + public default Vec4l min(Vec4l other, Vec4l result){return min(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4l min(long x, long y, long z, long w) {return min(x, y, z, w, this);} + public default Vec4l min(long x, long y, long z, long w, Vec4l result){return result.set(Math.min(getX(), x), Math.min(getY(), y), Math.min(getZ(), z), Math.min(getW(), w));} + + public default Vec4l max(Vec4l other) {return max(other, this);} + public default Vec4l max(Vec4l other, Vec4l result){return max(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4l max(long x, long y, long z, long w) {return max(x, y, z, w, this);} + public default Vec4l max(long x, long y, long z, long w, Vec4l result){return result.set(Math.max(getX(), x), Math.max(getY(), y), Math.max(getZ(), z), Math.max(getZ(), z));} + + public default Vec4l difference(Vec4l other) {return difference(other, this);} + public default Vec4l difference(Vec4l other, Vec4l result){return difference(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4l difference(long x, long y, long z, long w) {return difference(x, y, z, w, this);} + public default Vec4l difference(long x, long y, long z, long w, Vec4l result){return result.set(getX() - x, getY() - y, getZ() - z, getW() - w);} + + @Override + public default Vec4l clamp(long min, long max){return clamp(min, max, ALL);} + public default Vec4l clamp(long min, long max, Vec4l result){return clamp(min, max, result, ALL);} + @Override + public default Vec4l clamp(long min, long max, int filter){return clamp(min, max, this, filter);} + public default Vec4l clamp(long min, long max, Vec4l result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()), (filter & W) == 0 ? getW() : MathUtils.clamp(min, max, getW()));} + + @Override + public default Vec4l store(ByteBuffer buffer) + { + buffer.putLong(getX()).putLong(getY()).putLong(getZ()).putLong(getW()); + return this; + } + + @Override + public default Vec4l load(ByteBuffer buffer) + { + return set(buffer.getLong(), buffer.getLong(), buffer.getLong(), buffer.getLong()); + } + + @Override + public default Vec4l store(LongBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()).put(getW()); + return this; + } + + @Override + public default Vec4l load(LongBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec4b asByte(){return isMutable() ? Vec4b.newMutable((byte)getX(), (byte)getY(), (byte)getZ(), (byte)getW()) : Vec4b.newVec((byte)getX(), (byte)getY(), (byte)getZ(), (byte)getW());} + @Override + public default Vec4s asShort(){return isMutable() ? Vec4s.newMutable((short)getX(), (short)getY(), (short)getZ(), (short)getW()) : Vec4s.newVec((short)getX(), (short)getY(), (short)getZ(), (short)getW());} + @Override + public default Vec4i asInt(){return isMutable() ? Vec4i.newMutable((int)getX(), (int)getY(), (int)getZ(), (int)getW()) : Vec4i.newVec((int)getX(), (int)getY(), (int)getZ(), (int)getW());} + @Override + public default Vec4f asFloat() {return isMutable() ? Vec4f.newMutable(getX(), getY(), getZ(), getW()) : Vec4f.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4d asDouble(){return isMutable() ? Vec4d.newMutable(getX(), getY(), getZ(), getW()) : Vec4d.newVec(getX(), getY(), getZ(), getW());} + + @Override + public default Vec4l asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec4l asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec4l copyAsMutable(){return newMutable(this);} + @Override + public default Vec4l copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4lImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4lImmutable.java new file mode 100644 index 0000000..81b5726 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4lImmutable.java @@ -0,0 +1,124 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.util.Objects; + +public class Vec4lImmutable implements Vec4l +{ + final long x; + final long y; + final long z; + final long w; + + public Vec4lImmutable() + { + x = 0; + y = 0; + z = 0; + w = 0; + } + + public Vec4lImmutable(long value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4lImmutable(long x, long y, long z, long w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public long getX() + { + return x; + } + + @Override + public long getY() + { + return y; + } + + @Override + public long getZ() + { + return z; + } + + @Override + public long getW() + { + return w; + } + + @Override + public Vec4l setX(long x) + { + return this.x == x ? this : Vec4l.newVec(x, y, z, w); + } + + @Override + public Vec4l setY(long y) + { + return this.y == y ? this : Vec4l.newVec(x, y, z, w); + } + + @Override + public Vec4l setZ(long z) + { + return this.z == z ? this : Vec4l.newVec(x, y, z, w); + } + + @Override + public Vec4l setW(long w) + { + return this.w == w ? this : Vec4l.newVec(x, y, z, w); + } + + @Override + public Vec4l copy() + { + return Vec4l.newVec(this); + } + + @Override + public Vec4l set(long x, long y, long z, long w) + { + return this.x == x && this.y == y && this.z == z && this.w == w ? this : Vec4l.newVec(x, y, z, w); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4l) + { + Vec4l vec = (Vec4l)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4l[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4lMutable.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4lMutable.java new file mode 100644 index 0000000..5e37dfd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4lMutable.java @@ -0,0 +1,128 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.util.Objects; + +public class Vec4lMutable implements Vec4l +{ + long x; + long y; + long z; + long w; + + public Vec4lMutable() + { + } + + public Vec4lMutable(long value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4lMutable(long x, long y, long z, long w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public long getX() + { + return x; + } + + @Override + public long getY() + { + return y; + } + + @Override + public long getZ() + { + return z; + } + + @Override + public long getW() + { + return w; + } + + @Override + public Vec4l setX(long x) + { + this.x = x; + return this; + } + + @Override + public Vec4l setY(long y) + { + this.y = y; + return this; + } + + @Override + public Vec4l setZ(long z) + { + this.z = z; + return this; + } + + @Override + public Vec4l setW(long w) + { + this.w = w; + return this; + } + + @Override + public Vec4l copy() + { + return Vec4l.newMutable(this); + } + + @Override + public Vec4l set(long x, long y, long z, long w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4l) + { + Vec4l vec = (Vec4l)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4l[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vecl.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vecl.java new file mode 100644 index 0000000..01fd402 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vecl.java @@ -0,0 +1,34 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.nio.LongBuffer; + +import speiger.src.coreengine.math.vector.Vec; +import speiger.src.coreengine.math.vector.bytes.Vecb; +import speiger.src.coreengine.math.vector.doubles.Vecd; +import speiger.src.coreengine.math.vector.floats.Vecf; +import speiger.src.coreengine.math.vector.ints.Veci; +import speiger.src.coreengine.math.vector.shorts.Vecs; + +public interface Vecl extends Vec +{ + public Vecl set(long value); + public Vecl add(long value); + public Vecl sub(long value); + public Vecl multiply(long value); + public Vecl devide(long value); + public Vecl clamp(long min, long max); + public Vecl clamp(long min, long max, int filter); + + public long lengthSquared(); + public default double length(){return Math.sqrt(lengthSquared());} + + public Vecl store(LongBuffer buffer); + public Vecl load(LongBuffer buffer); + public long[] asArray(); + + public Vecb asByte(); + public Vecs asShort(); + public Veci asInt(); + public Vecf asFloat(); + public Vecd asDouble(); +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/matrix/Matrix4f.java b/src/main/java/speiger/src/coreengine/math/vector/matrix/Matrix4f.java new file mode 100644 index 0000000..47f053a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/matrix/Matrix4f.java @@ -0,0 +1,898 @@ +package speiger.src.coreengine.math.vector.matrix; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.Arrays; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.VectorUtil; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.quaternion.Quaternion; + +public class Matrix4f +{ + static final Vec3f X_ROTATION = Vec3f.newVec(1F, 0F, 0F); + static final Vec3f Y_ROTATION = Vec3f.newVec(0F, 1F, 0F); + static final Vec3f Z_ROTATION = Vec3f.newVec(0F, 0F, 1F); + + float[] data = new float[16]; + + public Matrix4f() + { + setIdentity(); + } + + public Matrix4f(float[] data) + { + System.arraycopy(data, 0, this.data, 0, 16); + } + + public Matrix4f(Matrix4f other) + { + this(other.data); + } + + public Matrix4f store(FloatBuffer buffer) + { + buffer.put(data); + return this; + } + + public Matrix4f store(ByteBuffer buffer) + { + for(int i = 0;i < 16;i++) + { + buffer.putFloat(data[i]); + } + return this; + } + + public Matrix4f store(Matrix4f other) + { + System.arraycopy(data, 0, other.data, 0, 16); + return this; + } + + public Matrix4f load(FloatBuffer buffer) + { + buffer.get(data); + return this; + } + + public Matrix4f load(ByteBuffer buffer) + { + for(int i = 0;i < 16;i++) + { + data[i] = buffer.getFloat(); + } + return this; + } + + public Matrix4f load(Matrix4f other) + { + System.arraycopy(other.data, 0, data, 0, 16); + return this; + } + + @Override + public String toString() + { + StringBuilder buf = new StringBuilder().append("\n"); + buf.append("x0=").append(data[0]).append(' ').append("y0=").append(data[4]).append(' ').append("z0=").append(data[8]).append(' ').append("w0=").append(data[12]).append('\n'); + buf.append("x1=").append(data[1]).append(' ').append("y1=").append(data[5]).append(' ').append("z1=").append(data[9]).append(' ').append("w1=").append(data[13]).append('\n'); + buf.append("x2=").append(data[2]).append(' ').append("y2=").append(data[6]).append(' ').append("z2=").append(data[10]).append(' ').append("w2=").append(data[14]).append('\n'); + buf.append("x3=").append(data[3]).append(' ').append("y3=").append(data[7]).append(' ').append("z3=").append(data[11]).append(' ').append("w3=").append(data[15]).append('\n'); + return buf.toString(); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Matrix4f) + { + return Arrays.equals(data, ((Matrix4f)obj).data); + } + return false; + } + + public float[] getData() + { + return data; + } + + public float get(int index) + { + return data[index]; + } + + public float get(int column, int row) + { + return data[(column * 4) + row]; + } + + public Matrix4f set(int column, int row, float value) + { + data[(column * 4) + row] = value; + return this; + } + + public Matrix4f set(int index, float value) + { + data[index] = value; + return this; + } + + public Matrix4f setIdentity() + { + Arrays.fill(data, 0F); + data[0] = 1F; + data[5] = 1F; + data[10] = 1F; + data[15] = 1F; + return this; + } + + public Matrix4f invert() + { + float determinant = determinant(); + if(determinant != 0F) + { + float determinant_inv = 1F / determinant; + float t00 = determinant3x3(data[5], data[6], data[7], data[9], data[10], data[11], data[13], data[14], data[15]); + float t01 = -determinant3x3(data[4], data[6], data[7], data[8], data[10], data[11], data[12], data[14], data[15]); + float t02 = determinant3x3(data[4], data[5], data[7], data[8], data[9], data[11], data[12], data[13], data[15]); + float t03 = -determinant3x3(data[4], data[5], data[6], data[8], data[9], data[10], data[12], data[13], data[14]); + float t10 = -determinant3x3(data[1], data[2], data[3], data[9], data[10], data[11], data[13], data[14], data[15]); + float t11 = determinant3x3(data[0], data[2], data[3], data[8], data[10], data[11], data[12], data[14], data[15]); + float t12 = -determinant3x3(data[0], data[1], data[3], data[8], data[9], data[11], data[12], data[13], data[15]); + float t13 = determinant3x3(data[0], data[1], data[2], data[8], data[9], data[10], data[12], data[13], data[14]); + float t20 = determinant3x3(data[1], data[2], data[3], data[5], data[6], data[7], data[13], data[14], data[15]); + float t21 = -determinant3x3(data[0], data[2], data[3], data[4], data[6], data[7], data[12], data[14], data[15]); + float t22 = determinant3x3(data[0], data[1], data[3], data[4], data[5], data[7], data[12], data[13], data[15]); + float t23 = -determinant3x3(data[0], data[1], data[2], data[4], data[5], data[6], data[12], data[13], data[14]); + float t30 = -determinant3x3(data[1], data[2], data[3], data[5], data[6], data[7], data[9], data[10], data[11]); + float t31 = determinant3x3(data[0], data[2], data[3], data[4], data[6], data[7], data[8], data[10], data[11]); + float t32 = -determinant3x3(data[0], data[1], data[3], data[4], data[5], data[7], data[8], data[9], data[11]); + float t33 = determinant3x3(data[0], data[1], data[2], data[4], data[5], data[6], data[8], data[9], data[10]); + + data[0] = t00 * determinant_inv; + data[5] = t11 * determinant_inv; + data[10] = t22 * determinant_inv; + data[15] = t33 * determinant_inv; + data[1] = t10 * determinant_inv; + data[4] = t01 * determinant_inv; + data[8] = t02 * determinant_inv; + data[2] = t20 * determinant_inv; + data[6] = t21 * determinant_inv; + data[9] = t12 * determinant_inv; + data[3] = t30 * determinant_inv; + data[12] = t03 * determinant_inv; + data[7] = t31 * determinant_inv; + data[13] = t13 * determinant_inv; + data[14] = t23 * determinant_inv; + data[11] = t32 * determinant_inv; + } + return this; + } + + public Matrix4f flip() + { + data[0] = -data[0]; + data[1] = -data[1]; + data[2] = -data[2]; + data[3] = -data[3]; + data[4] = -data[4]; + data[5] = -data[5]; + data[6] = -data[6]; + data[7] = -data[7]; + data[8] = -data[8]; + data[9] = -data[9]; + data[10] = -data[10]; + data[11] = -data[11]; + data[12] = -data[12]; + data[13] = -data[13]; + data[14] = -data[14]; + data[15] = -data[15]; + return this; + } + + public Matrix4f negate() + { + Arrays.fill(data, 0F); + return this; + } + + public Matrix4f add(Matrix4f other) + { + data[0] += other.data[0]; + data[1] += other.data[1]; + data[2] += other.data[2]; + data[3] += other.data[3]; + data[4] += other.data[4]; + data[5] += other.data[5]; + data[6] += other.data[6]; + data[7] += other.data[7]; + data[8] += other.data[8]; + data[9] += other.data[9]; + data[10] += other.data[10]; + data[11] += other.data[11]; + data[12] += other.data[12]; + data[13] += other.data[13]; + data[14] += other.data[14]; + data[15] += other.data[15]; + return this; + } + + public Matrix4f sub(Matrix4f other) + { + data[0] -= other.data[0]; + data[1] -= other.data[1]; + data[2] -= other.data[2]; + data[3] -= other.data[3]; + data[4] -= other.data[4]; + data[5] -= other.data[5]; + data[6] -= other.data[6]; + data[7] -= other.data[7]; + data[8] -= other.data[8]; + data[9] -= other.data[9]; + data[10] -= other.data[10]; + data[11] -= other.data[11]; + data[12] -= other.data[12]; + data[13] -= other.data[13]; + data[14] -= other.data[14]; + data[15] -= other.data[15]; + return this; + } + + public Matrix4f multiply(Matrix4f other) + { + float data0 = data[0] * other.data[0] + data[4] * other.data[1] + data[8] * other.data[2] + data[12] * other.data[3]; + float data1 = data[1] * other.data[0] + data[5] * other.data[1] + data[9] * other.data[2] + data[13] * other.data[3]; + float data2 = data[2] * other.data[0] + data[6] * other.data[1] + data[10] * other.data[2] + data[14] * other.data[3]; + float data3 = data[3] * other.data[0] + data[7] * other.data[1] + data[11] * other.data[2] + data[15] * other.data[3]; + float data4 = data[0] * other.data[4] + data[4] * other.data[5] + data[8] * other.data[6] + data[12] * other.data[7]; + float data5 = data[1] * other.data[4] + data[5] * other.data[5] + data[9] * other.data[6] + data[13] * other.data[7]; + float data6 = data[2] * other.data[4] + data[6] * other.data[5] + data[10] * other.data[6] + data[14] * other.data[7]; + float data7 = data[3] * other.data[4] + data[7] * other.data[5] + data[11] * other.data[6] + data[15] * other.data[7]; + float data8 = data[0] * other.data[8] + data[4] * other.data[9] + data[8] * other.data[10] + data[12] * other.data[11]; + float data9 = data[1] * other.data[8] + data[5] * other.data[9] + data[9] * other.data[10] + data[13] * other.data[11]; + float data10 = data[2] * other.data[8] + data[6] * other.data[9] + data[10] * other.data[10] + data[14] * other.data[11]; + float data11 = data[3] * other.data[8] + data[7] * other.data[9] + data[11] * other.data[10] + data[15] * other.data[11]; + float data12 = data[0] * other.data[12] + data[4] * other.data[13] + data[8] * other.data[14] + data[12] * other.data[15]; + float data13 = data[1] * other.data[12] + data[5] * other.data[13] + data[9] * other.data[14] + data[13] * other.data[15]; + float data14 = data[2] * other.data[12] + data[6] * other.data[13] + data[10] * other.data[14] + data[14] * other.data[15]; + float data15 = data[3] * other.data[12] + data[7] * other.data[13] + data[11] * other.data[14] + data[15] * other.data[15]; + data[0] = data0; + data[1] = data1; + data[2] = data2; + data[3] = data3; + data[4] = data4; + data[5] = data5; + data[6] = data6; + data[7] = data7; + data[8] = data8; + data[9] = data9; + data[10] = data10; + data[11] = data11; + data[12] = data12; + data[13] = data13; + data[14] = data14; + data[15] = data15; + return this; + } + + public Matrix4f transpose() + { + float data0 = data[0]; + float data1 = data[4]; + float data2 = data[8]; + float data3 = data[12]; + float data4 = data[1]; + float data5 = data[5]; + float data6 = data[9]; + float data7 = data[13]; + float data8 = data[2]; + float data9 = data[6]; + float data10 = data[10]; + float data11 = data[14]; + float data12 = data[3]; + float data13 = data[7]; + float data14 = data[11]; + float data15 = data[15]; + + data[0] = data0; + data[1] = data1; + data[2] = data2; + data[3] = data3; + data[4] = data4; + data[5] = data5; + data[6] = data6; + data[7] = data7; + data[8] = data8; + data[9] = data9; + data[10] = data10; + data[11] = data11; + data[12] = data12; + data[13] = data13; + data[14] = data14; + data[15] = data15; + return this; + } + + public Matrix4f transpose3x3() + { + float nm00 = data[0]; + float nm01 = data[4]; + float nm02 = data[8]; + float nm10 = data[1]; + float nm11 = data[5]; + float nm12 = data[9]; + float nm20 = data[2]; + float nm21 = data[6]; + float nm22 = data[10]; + data[0] = nm00; + data[1] = nm01; + data[2] = nm02; + data[4] = nm10; + data[5] = nm11; + data[6] = nm12; + data[8] = nm20; + data[9] = nm21; + data[10] = nm22; + return this; + } + + public Matrix4f decompose(Vec3f position, Quaternion rotation, Vec3f scale) + { + position.set(get(3, 0), get(3, 1), get(3, 2)); + rotation.set(this); + scale.set(get(0, 0), get(1, 1), get(2, 2)); + return this; + } + + public Matrix4f setTranslation(Vec3f vec) + { + data[12] = vec.getX(); + data[13] = vec.getY(); + data[14] = vec.getZ(); + return this; + } + + public Matrix4f translate(Vec2f vec) + { + return translate(vec.getX(), vec.getY()); + } + + public Matrix4f translate(float x, float y) + { + data[12] += data[0] * x + data[4] * y; + data[13] += data[1] * x + data[5] * y; + data[14] += data[2] * x + data[6] * y; + data[15] += data[3] * x + data[7] * y; + return this; + } + + public Matrix4f translate(Vec3f vec) + { + data[12] += data[0] * vec.getX() + data[4] * vec.getY() + data[8] * vec.getZ(); + data[13] += data[1] * vec.getX() + data[5] * vec.getY() + data[9] * vec.getZ(); + data[14] += data[2] * vec.getX() + data[6] * vec.getY() + data[10] * vec.getZ(); + data[15] += data[3] * vec.getX() + data[7] * vec.getY() + data[11] * vec.getZ(); + return this; + } + + public Matrix4f translate(float x, float y, float z) + { + data[12] += data[0] * x + data[4] * y + data[8] * z; + data[13] += data[1] * x + data[5] * y + data[9] * z; + data[14] += data[2] * x + data[6] * y + data[10] * z; + data[15] += data[3] * x + data[7] * y + data[11] * z; + return this; + } + + public Matrix4f rotateX(float angle) + { + return rotate((float)Math.toRadians(angle), X_ROTATION); + } + + public Matrix4f rotateY(float angle) + { + return rotate((float)Math.toRadians(angle), Y_ROTATION); + } + + public Matrix4f rotateZ(float angle) + { + return rotate((float)Math.toRadians(angle), Z_ROTATION); + } + + public Matrix4f rotateRadX(float angle) + { + return rotate(angle, X_ROTATION); + } + + public Matrix4f rotateRadY(float angle) + { + return rotate(angle, Y_ROTATION); + } + + public Matrix4f rotateRadZ(float angle) + { + return rotate(angle, Z_ROTATION); + } + + public Matrix4f rotate(float angle, Vec3f axis) + { + float c = MathUtils.cos(angle); + float s = MathUtils.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 = data[0] * f00 + data[4] * f01 + data[8] * f02; + float t01 = data[1] * f00 + data[5] * f01 + data[9] * f02; + float t02 = data[2] * f00 + data[6] * f01 + data[10] * f02; + float t03 = data[3] * f00 + data[7] * f01 + data[11] * f02; + float t10 = data[0] * f10 + data[4] * f11 + data[8] * f12; + float t11 = data[1] * f10 + data[5] * f11 + data[9] * f12; + float t12 = data[2] * f10 + data[6] * f11 + data[10] * f12; + float t13 = data[3] * f10 + data[7] * f11 + data[11] * f12; + data[8] = data[0] * f20 + data[4] * f21 + data[8] * f22; + data[9] = data[1] * f20 + data[5] * f21 + data[9] * f22; + data[10] = data[2] * f20 + data[6] * f21 + data[10] * f22; + data[11] = data[3] * f20 + data[7] * f21 + data[11] * f22; + data[0] = t00; + data[1] = t01; + data[2] = t02; + data[3] = t03; + data[4] = t10; + data[5] = t11; + data[6] = t12; + data[7] = t13; + return this; + } + + public Matrix4f rotate(Quaternion rotation) + { + float w2 = rotation.getW() * rotation.getW(); + float x2 = rotation.getX() * rotation.getX(); + float y2 = rotation.getY() * rotation.getY(); + float z2 = rotation.getZ() * rotation.getZ(); + float zw = rotation.getZ() * rotation.getW(); + float dzw = zw + zw; + float xy = rotation.getX() * rotation.getY(); + float dxy = xy + xy; + float xz = rotation.getX() * rotation.getZ(); + float dxz = xz + xz; + float yw = rotation.getY() * rotation.getW(); + float dyw = yw + yw; + float yz = rotation.getY() * rotation.getZ(); + float dyz = yz + yz; + float xw = rotation.getX() * rotation.getW(); + float dxw = xw + xw; + float rm00 = w2 + x2 - z2 - y2; + float rm01 = dxy + dzw; + float rm02 = dxz - dyw; + float rm10 = -dzw + dxy; + float rm11 = y2 - z2 + w2 - x2; + float rm12 = dyz + dxw; + float rm20 = dyw + dxz; + float rm21 = dyz - dxw; + float rm22 = z2 - y2 - x2 + w2; + float nm00 = data[0] * rm00 + data[4] * rm01 + data[8] * rm02; + float nm01 = data[1] * rm00 + data[5] * rm01 + data[9] * rm02; + float nm02 = data[2] * rm00 + data[6] * rm01 + data[10] * rm02; + float nm03 = data[3] * rm00 + data[7] * rm01 + data[11] * rm02; + float nm10 = data[0] * rm10 + data[4] * rm11 + data[8] * rm12; + float nm11 = data[1] * rm10 + data[5] * rm11 + data[9] * rm12; + float nm12 = data[2] * rm10 + data[6] * rm11 + data[10] * rm12; + float nm13 = data[3] * rm10 + data[7] * rm11 + data[11] * rm12; + data[8] = data[0] * rm20 + data[4] * rm21 + data[8] * rm22; + data[9] = data[1] * rm20 + data[5] * rm21 + data[9] * rm22; + data[10] = data[2] * rm20 + data[6] * rm21 + data[10] * rm22; + data[11] = data[3] * rm20 + data[7] * rm21 + data[11] * rm22; + data[0] = nm00; + data[1] = nm01; + data[2] = nm02; + data[3] = nm03; + data[4] = nm10; + data[5] = nm11; + data[6] = nm12; + data[7] = nm13; + return this; + } + + public Matrix4f setRotation(Matrix4f source) + { + data[0] = source.data[0]; + data[1] = source.data[1]; + data[2] = source.data[2]; + data[4] = source.data[4]; + data[5] = source.data[5]; + data[6] = source.data[6]; + data[8] = source.data[8]; + data[9] = source.data[9]; + data[10] = source.data[10]; + return this; + } + + public Matrix4f setBillboard(Matrix4f source) + { + data[0] = source.data[0]; + data[1] = source.data[4]; + data[2] = source.data[8]; + data[4] = source.data[1]; + data[5] = source.data[5]; + data[6] = source.data[9]; + data[8] = source.data[2]; + data[9] = source.data[6]; + data[10] = source.data[10]; + return this; + } + + public Matrix4f scale(Vec3f vec) + { + return scale(vec.getX(), vec.getY(), vec.getZ()); + } + + public Matrix4f scale(float value) + { + return scale(value, value, value); + } + + public Matrix4f scale(float x, float y, float z) + { + data[0] *= x; + data[1] *= x; + data[2] *= x; + data[3] *= x; + data[4] *= y; + data[5] *= y; + data[6] *= y; + data[7] *= y; + data[8] *= z; + data[9] *= z; + data[10] *= z; + data[11] *= z; + return this; + } + + public Matrix4f unscale(Vec3f vec) + { + return unscale(vec.getX(), vec.getY(), vec.getZ()); + } + + public Matrix4f unscale(float scale) + { + return unscale(scale, scale, scale); + } + + public Matrix4f unscale(float x, float y, float z) + { + data[0] /= x; + data[1] /= x; + data[2] /= x; + data[3] /= x; + data[4] /= y; + data[5] /= y; + data[6] /= y; + data[7] /= y; + data[8] /= z; + data[9] /= z; + data[10] /= z; + data[11] /= z; + return this; + } + + public Matrix4f setPerspective(float fovy, float aspect, float zNear, float zFar) + { + float h = (float)Math.tan(fovy * 0.5f); + set(0, 0, 1F / (h * aspect)).set(1, 1, 1F / h); + boolean farInf = zFar > 0 && Float.isInfinite(zFar); + boolean nearInf = zNear > 0 && Float.isInfinite(zNear); + if(farInf) + { + float e = 1E-6f; + set(2, 2, e - 1F).set(3, 2, (e - 2F) * zNear); + } + else if(nearInf) + { + float e = 1E-6f; + set(2, 2, 1F - e).set(3, 2, (2F - e) * zFar); + } + else + { + set(2, 2, (zFar + zNear) / (zNear - zFar)).set(3, 2, (zFar + zFar) * zNear / (zNear - zFar)); + } + return set(2, 3, -1.0f); + } + + public Matrix4f getTranslation(Vec3f vec) + { + vec.set(data[12], data[13], data[14]); + return this; + } + + public Matrix4f getScale(Vec3f vec) + { + vec.setX((float)Math.sqrt(data[0] * data[0] + data[1] * data[1] + data[2] * data[2])); + vec.setY((float)Math.sqrt(data[4] * data[4] + data[5] * data[5] + data[6] * data[6])); + vec.setZ((float)Math.sqrt(data[8] * data[8] + data[9] * data[9] + data[10] * data[10])); + return this; + } + + public Vec4f transform(Vec4f input) + { + return transform(input, input); + } + + public Vec4f transform(Vec4f input, Vec4f output) + { + float x = data[0] * input.getX() + data[4] * input.getY() + data[8] * input.getZ() + data[12] * input.getW(); + float y = data[1] * input.getX() + data[5] * input.getY() + data[9] * input.getZ() + data[13] * input.getW(); + float z = data[2] * input.getX() + data[6] * input.getY() + data[10] * input.getZ() + data[14] * input.getW(); + float w = data[3] * input.getX() + data[7] * input.getY() + data[11] * input.getZ() + data[15] * input.getW(); + return output.set(x, y, z, w); + } + + public void transform(Vec4f input, FloatBuffer buffer) + { + buffer.put(data[0] * input.getX() + data[4] * input.getY() + data[8] * input.getZ() + data[12] * input.getW()); + buffer.put(data[1] * input.getX() + data[5] * input.getY() + data[9] * input.getZ() + data[13] * input.getW()); + buffer.put(data[2] * input.getX() + data[6] * input.getY() + data[10] * input.getZ() + data[14] * input.getW()); + buffer.put(data[3] * input.getX() + data[7] * input.getY() + data[11] * input.getZ() + data[15] * input.getW()); + } + + public Vec3f transform(Vec3f input, boolean position) + { + return transform(input, input, position); + } + + public Vec3f transform(Vec3f input, Vec3f output, boolean position) + { + float pos = position ? 1F : 0F; + return output.set(data[0] * input.getX() + data[4] * input.getY() + data[8] * input.getZ() + data[12] * pos, data[1] * input.getX() + data[5] * input.getY() + data[9] * input.getZ() + data[13] * pos, data[2] * input.getX() + data[6] * input.getY() + data[10] * input.getZ() + data[14] * pos); + } + + public void transform(Vec3f input, FloatBuffer buffer, boolean position) + { + float pos = position ? 1F : 0F; + buffer.put(data[0] * input.getX() + data[4] * input.getY() + data[8] * input.getZ() + data[12] * pos); + buffer.put(data[1] * input.getX() + data[5] * input.getY() + data[9] * input.getZ() + data[13] * pos); + buffer.put(data[2] * input.getX() + data[6] * input.getY() + data[10] * input.getZ() + data[14] * pos); + } + + public float determinant() + { + float f = data[0] * ((data[5] * data[10] * data[15] + data[6] * data[11] * data[13] + data[7] * data[9] * data[14]) - data[7] * data[10] * data[13] - data[5] * data[11] * data[14] - data[6] * data[9] * data[15]); + f -= data[1] * ((data[4] * data[10] * data[15] + data[6] * data[11] * data[12] + data[7] * data[8] * data[14]) - data[7] * data[10] * data[12] - data[4] * data[11] * data[14] - data[6] * data[8] * data[15]); + f += data[2] * ((data[4] * data[9] * data[15] + data[5] * data[11] * data[12] + data[7] * data[8] * data[13]) - data[7] * data[9] * data[12] - data[4] * data[11] * data[13] - data[5] * data[8] * data[15]); + f -= data[3] * ((data[4] * data[9] * data[14] + data[5] * data[10] * data[12] + data[6] * data[8] * data[13]) - data[6] * data[9] * data[12] - data[4] * data[10] * data[13] - data[5] * data[8] * data[14]); + return f; + } + + public float determinant3x3(float t00, float t01, float t02, float t10, float t11, float t12, float t20, float t21, float t22) + { + return t00 * (t11 * t22 - t12 * t21) + t01 * (t12 * t20 - t10 * t22) + t02 * (t10 * t21 - t11 * t20); + } + + public Matrix4f setTransform(Vec3f position, Vec3f rotation, float scale) + { + setIdentity(); + translate(position); + rotate((float)Math.toRadians(rotation.getX()), X_ROTATION); + rotate((float)Math.toRadians(rotation.getY()), Y_ROTATION); + rotate((float)Math.toRadians(rotation.getZ()), Z_ROTATION); + scale(scale, scale, scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Vec3f rotation, Vec3f scale) + { + setIdentity(); + translate(position); + rotate((float)Math.toRadians(rotation.getX()), X_ROTATION); + rotate((float)Math.toRadians(rotation.getY()), Y_ROTATION); + rotate((float)Math.toRadians(rotation.getZ()), Z_ROTATION); + scale(scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Vec3f offset, Vec3f rotation, float scale) + { + setIdentity(); + translate(position); + translate(offset); + rotate((float)Math.toRadians(rotation.getX()), X_ROTATION); + rotate((float)Math.toRadians(rotation.getY()), Y_ROTATION); + rotate((float)Math.toRadians(rotation.getZ()), Z_ROTATION); + scale(scale, scale, scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Vec3f offset, Vec3f rotation, Vec3f scale) + { + setIdentity(); + translate(position); + translate(offset); + rotate((float)Math.toRadians(rotation.getX()), X_ROTATION); + rotate((float)Math.toRadians(rotation.getY()), Y_ROTATION); + rotate((float)Math.toRadians(rotation.getZ()), Z_ROTATION); + scale(scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Quaternion rotation, float scale) + { + setIdentity(); + translate(position); + rotate(rotation); + scale(scale, scale, scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Quaternion rotation, Vec3f scale) + { + setIdentity(); + translate(position); + rotate(rotation); + scale(scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Vec3f offset, Quaternion rotation, float scale) + { + setIdentity(); + translate(position); + translate(offset); + rotate(rotation); + scale(scale, scale, scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Vec3f offset, Quaternion rotation, Vec3f scale) + { + setIdentity(); + translate(position); + translate(offset); + rotate(rotation); + scale(scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Matrix4f billRotation, float scale) + { + setIdentity(); + translate(position); + setBillboard(billRotation); + scale(scale, scale, scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Matrix4f billRotation, Vec3f scale) + { + setIdentity(); + translate(position); + setBillboard(billRotation); + transpose3x3(); + scale(scale); + return this; + } + + public Vec3f project(float x, float y, float z, int[] viewport, Vec3f winCoordsDest) + { + float invW = 1F / VectorUtil.fma(data[3], x, VectorUtil.fma(data[7], y, VectorUtil.fma(data[11], z, data[15]))); + float nx = VectorUtil.fma(data[0], x, VectorUtil.fma(data[4], y, VectorUtil.fma(data[8], z, data[12]))) * invW; + float ny = VectorUtil.fma(data[1], x, VectorUtil.fma(data[5], y, VectorUtil.fma(data[9], z, data[13]))) * invW; + float nz = VectorUtil.fma(data[2], x, VectorUtil.fma(data[6], y, VectorUtil.fma(data[10], z, data[14]))) * invW; + return winCoordsDest.set(VectorUtil.fma(VectorUtil.fma(nx, 0.5F, 0.5F), viewport[2], viewport[0]), VectorUtil.fma(VectorUtil.fma(ny, 0.5F, 0.5F), viewport[3], viewport[1]), VectorUtil.fma(0.5F, nz, 0.5F)); + } + + public Vec3f unproject(float winX, float winY, float winZ, int[] viewport, Vec3f dest) { + float a = data[0] * data[5] - data[1] * data[4]; + float b = data[0] * data[6] - data[2] * data[4]; + float c = data[0] * data[7] - data[3] * data[4]; + float d = data[1] * data[6] - data[2] * data[5]; + float e = data[1] * data[7] - data[3] * data[5]; + float f = data[2] * data[7] - data[3] * data[6]; + float g = data[8] * data[13] - data[9] * data[12]; + float h = data[8] * data[14] - data[10] * data[12]; + float i = data[8] * data[15] - data[11] * data[12]; + float j = data[9] * data[14] - data[10] * data[13]; + float k = data[9] * data[15] - data[11] * data[13]; + float l = data[10] * data[15] - data[11] * data[14]; + float det = a * l - b * k + c * j + d * i - e * h + f * g; + det = 1.0f / det; + float im00 = ( data[5] * l - data[6] * k + data[7] * j) * det; + float im01 = (-data[1] * l + data[2] * k - data[3] * j) * det; + float im02 = ( data[13] * f - data[14] * e + data[15] * d) * det; + float im03 = (-data[9] * f + data[10] * e - data[11] * d) * det; + float im10 = (-data[4] * l + data[6] * i - data[7] * h) * det; + float im11 = ( data[0] * l - data[2] * i + data[3] * h) * det; + float im12 = (-data[12] * f + data[14] * c - data[15] * b) * det; + float im13 = ( data[8] * f - data[10] * c + data[11] * b) * det; + float im20 = ( data[4] * k - data[5] * i + data[7] * g) * det; + float im21 = (-data[0] * k + data[1] * i - data[3] * g) * det; + float im22 = ( data[12] * e - data[13] * c + data[15] * a) * det; + float im23 = (-data[8] * e + data[9] * c - data[11] * a) * det; + float im30 = (-data[4] * j + data[5] * h - data[6] * g) * det; + float im31 = ( data[0] * j - data[1] * h + data[2] * g) * det; + float im32 = (-data[12] * d + data[13] * b - data[14] * a) * det; + float im33 = ( data[8] * d - data[9] * b + data[10] * a) * det; + float ndcX = (winX-viewport[0])/viewport[2]*2.0f-1.0f; + float ndcY = (winY-viewport[1])/viewport[3]*2.0f-1.0f; + float ndcZ = winZ+winZ-1.0f; + float invW = 1.0f / (im03 * ndcX + im13 * ndcY + im23 * ndcZ + im33); + return dest.set((im00 * ndcX + im10 * ndcY + im20 * ndcZ + im30) * invW, (im01 * ndcX + im11 * ndcY + im21 * ndcZ + im31) * invW, (im02 * ndcX + im12 * ndcY + im22 * ndcZ + im32) * invW); + } + + public Matrix4f ortho(float x, float y, float width, float height, float zNear, float zFar) + { + return ortho(x, x+width, y+height, y, zNear, zFar, false); + } + + public Matrix4f ortho(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne) + { + float rm00 = 2F / (right - left); + float rm11 = 2F / (top - bottom); + float rm22 = (zZeroToOne ? 1F : 2F) / (zFar - zNear); + float rm30 = (left + right) / (left - right); + float rm31 = (top + bottom) / (bottom - top); + float rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + + data[12] = data[0] * rm30 + data[4] * rm31 + data[8] * rm32 + data[12]; + data[13] = data[1] * rm30 + data[5] * rm31 + data[9] * rm32 + data[13]; + data[14] = data[2] * rm30 + data[6] * rm31 + data[10] * rm32 + data[14]; + data[15] = data[3] * rm30 + data[7] * rm31 + data[11] * rm32 + data[15]; + data[0] = data[0] * rm00; + data[1] = data[1] * rm00; + data[2] = data[2] * rm00; + data[3] = data[3] * rm00; + data[4] = data[4] * rm11; + data[5] = data[5] * rm11; + data[6] = data[6] * rm11; + data[7] = data[7] * rm11; + data[8] = data[8] * rm22; + data[9] = data[9] * rm22; + data[10] = data[10] * rm22; + data[11] = data[11] * rm22; + return this; + } + + public Vec4f storeFrustrumPlane(int plane, Vec4f toStore) + { + switch(plane) + { + case 0: + return toStore.set(data[3] + data[0], data[7] + data[4], data[11] + data[8], data[15] + data[12]).normalize3D(); + case 1: + return toStore.set(data[3] - data[0], data[7] - data[4], data[11] - data[8], data[15] - data[12]).normalize3D(); + case 2: + return toStore.set(data[3] + data[1], data[7] + data[5], data[11] + data[9], data[15] + data[13]).normalize3D(); + case 3: + return toStore.set(data[3] - data[1], data[7] - data[5], data[11] - data[9], data[15] - data[13]).normalize3D(); + case 4: + return toStore.set(data[3] + data[2], data[7] + data[6], data[11] + data[10], data[15] + data[14]).normalize3D(); + case 5: + return toStore.set(data[3] - data[2], data[7] - data[6], data[11] - data[10], data[15] - data[14]).normalize3D(); + } + return toStore; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/quaternion/Quaternion.java b/src/main/java/speiger/src/coreengine/math/vector/quaternion/Quaternion.java new file mode 100644 index 0000000..158d5bc --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/quaternion/Quaternion.java @@ -0,0 +1,215 @@ +package speiger.src.coreengine.math.vector.quaternion; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.matrix.Matrix4f; + +public interface Quaternion +{ + public static final Quaternion ZERO = newQuat(0F, 0F, 0F, 0F); + public static final Quaternion IDENTITY = newQuat(); + + public static Quaternion newQuat(){return new QuaternionImmutable();} + public static Quaternion newQuat(Quaternion source){return newQuat(source.getX(), source.getY(), source.getZ(), source.getW());} + public static Quaternion newQuat(float x, float y, float z, float w){return new QuaternionImmutable(x, y, z, w);} + public static Quaternion newAxisRadQuat(float x, float y, float z, float angle){return ZERO.setAxisRad(x, y, z, angle);} + public static Quaternion newAxisDegQuat(float x, float y, float z, float angle){return ZERO.setAxisDeg(x, y, z, angle);} + + public static Quaternion newMutable(){return new QuaternionMutable();} + public static Quaternion newMutable(Quaternion source){return newMutable(source.getX(), source.getY(), source.getZ(), source.getW());} + public static Quaternion newMutable(float x, float y, float z, float w){return new QuaternionMutable(x, y, z, w);} + public static Quaternion newMutableRadAxis(float x, float y, float z, float angle){return newMutable().setAxisRad(x, y, z, angle);} + public static Quaternion newMutableDegAxis(float x, float y, float z, float angle){return newMutable().setAxisDeg(x, y, z, angle);} + + public static double acos(double v){return v < -1D ? 3.141592653589793D : (v > 1D ? 0D : Math.acos(v));} + + public Quaternion setX(float x); + public Quaternion setY(float y); + public Quaternion setZ(float z); + public Quaternion setW(float w); + public float getX(); + public float getY(); + public float getZ(); + public float getW(); + + public default Quaternion negate(){return set(0F, 0F, 0F, 0F);} + public default Quaternion invert(){return set(-getX(), -getY(), -getZ(), -getW());} + public default Quaternion normalize(){return scale(1.0F / length());} + public default Quaternion conjugate(){return set(-getX(), -getY(), -getZ(), getW());} + public default Quaternion setIdentity(){return set(0F, 0F, 0F, 1F);} + + public default Quaternion multiply(Quaternion other){return set(getX() * other.getW() + getW() * other.getX() + getY() * other.getZ() - getZ() * other.getY(), getY() * other.getW() + getW() * other.getY() + getZ() * other.getX() - getX() * other.getZ(), getZ() * other.getW() + getW() * other.getZ() + getX() * other.getY() - getY() * other.getX(), getW() * other.getW() - getX() * other.getX() - getY() * other.getY() - getZ() * other.getZ());} + public default Quaternion scale(float scale){return set(getX() * scale, getY() * scale, getZ() * scale, getW() * scale);} + public default Quaternion rotateX(float angle) + { + angle = (float)Math.toRadians(angle); + float sin = MathUtils.sin(angle * 0.5D); + float cos = MathUtils.cos(angle * 0.5D); + return set(getW() * sin + getX() * cos, getY() * cos + getZ() * sin, getZ() * cos - getY() * sin, getW() * cos - getX() * sin); + } + public default Quaternion rotateY(float angle) + { + angle = (float)Math.toRadians(angle); + float sin = MathUtils.sin(angle * 0.5D); + float cos = MathUtils.cos(angle * 0.5D); + return set(getX() * cos - getZ() * sin, getW() * sin + getY() * cos, getX() * sin + getZ() * cos, getW() * cos - getY() * sin); + } + public default Quaternion rotateZ(float angle) + { + angle = (float)Math.toRadians(angle); + float sin = MathUtils.sin(angle * 0.5D); + float cos = MathUtils.cos(angle * 0.5D); + return set(getX() * cos + getY() * sin, getY() * cos - getX() * sin, getW() * sin + getZ() * cos, getW() * cos - getZ() * sin); + } + + public default Quaternion set(Vec4f value){return set(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Quaternion set(Quaternion value){return set(value.getX(), value.getY(), value.getZ(), value.getW());} + + public default Quaternion set(Matrix4f matrix) + { + float tr = matrix.get(0, 0) + matrix.get(1, 1) + matrix.get(2, 2); + if(tr >= 0.0D) + { + float s = (float)Math.sqrt(tr + 1.0D); + float w = s * 0.5F; + s = 0.5F / s; + float x = (matrix.get(2, 1) - matrix.get(1, 2)) * s; + float y = (matrix.get(0, 2) - matrix.get(2, 0)) * s; + float z = (matrix.get(1, 0) - matrix.get(0, 1)) * s; + return set(x, y, z, w); + } + else + { + float max = Math.max(Math.max(matrix.get(0, 0), matrix.get(1, 1)), matrix.get(2, 2)); + if(max == matrix.get(0, 0)) + { + float s = (float)Math.sqrt(matrix.get(0, 0) - (matrix.get(1, 1) + matrix.get(2, 2)) + 1.0D); + float x = s * 0.5F; + s = 0.5F / s; + float y = (matrix.get(0, 1) + matrix.get(1, 0)) * s; + float z = (matrix.get(2, 0) + matrix.get(0, 2)) * s; + float w = (matrix.get(2, 1) - matrix.get(1, 2)) * s; + return set(x, y, z, w); + } + else if(max == matrix.get(1, 1)) + { + float s = (float)Math.sqrt(matrix.get(1, 1) - (matrix.get(2, 2) + matrix.get(0, 0)) + 1.0D); + float y = s * 0.5F; + s = 0.5F / s; + float z = (matrix.get(1, 2) + matrix.get(2, 1)) * s; + float x = (matrix.get(0, 1) + matrix.get(1, 0)) * s; + float w = (matrix.get(0, 2) - matrix.get(2, 0)) * s; + return set(x, y, z, w); + } + else + { + float s = (float)Math.sqrt(matrix.get(2, 2) - (matrix.get(0, 0) + matrix.get(1, 1)) + 1.0D); + float z = s * 0.5F; + s = 0.5F / s; + float x = (matrix.get(2, 0) + matrix.get(0, 2)) * s; + float y = (matrix.get(1, 2) + matrix.get(2, 1)) * s; + float w = (matrix.get(1, 0) - matrix.get(0, 1)) * s; + return set(x, y, z, w); + } + } + } + + public default Quaternion setAxisRad(float x, float y, float z, float angle) + { + float sin = MathUtils.sin(angle * 0.5D); + float cos = MathUtils.cos(angle * 0.5D); + return set(x * sin, y * sin, z * sin, cos); + } + public default Quaternion setAxisDeg(float x, float y, float z, float angle) + { + angle = (float)Math.toRadians(angle); + float sin = MathUtils.sin(angle * 0.5D); + float cos = MathUtils.cos(angle * 0.5D); + return set(x * sin, y * sin, z * sin, cos); + } + public Quaternion set(float x, float y, float z, float w); + + public default float dot(Quaternion other){return getX() * other.getX() + getY() * other.getY() + getZ() * other.getZ() + getW() * other.getW();} + public default Quaternion difference(Quaternion other){return difference(other, this);} + public default Quaternion difference(Quaternion other, Quaternion result) + { + float invNorm = 1.0F / (getX() * getX() + getY() * getY() + getZ() * getZ() + getW() * getW()); + float x = -getX() * invNorm; + float y = -getY() * invNorm; + float z = -getZ() * invNorm; + float w = getW() * invNorm; + return set(w * other.getX() + x * other.getW() + y * other.getZ() - z * other.getY(), w * other.getY() - x * other.getZ() + y * other.getW() + z * other.getX(), w * other.getZ() + x * other.getY() - y * other.getX() + z * other.getW(), w * other.getW() - x * other.getX() - y * other.getY() - z * other.getZ()); + } + public default Quaternion lerp(Quaternion other, float progress){return lerp(other, progress, this);} + public default Quaternion lerp(Quaternion other, float progress, Quaternion result) + { + float cosom = getX() * other.getX() + getY() * other.getY() + getZ() * other.getZ() + getW() * other.getW(); + float absCosom = Math.abs(cosom); + float scale1; + float scale0; + if(1.0F - absCosom > 1.0E-006F) + { + float sinSqr = 1.0F - absCosom * absCosom; + float sinom = (float)(1.0D / Math.sqrt(sinSqr)); + float omega = (float)Math.atan2(sinSqr * sinom, absCosom); + scale0 = MathUtils.sin((1.0D - progress) * omega) * sinom; + scale1 = MathUtils.sin(progress * omega) * sinom; + } + else + { + scale0 = 1.0F - progress; + scale1 = progress; + } + scale1 = cosom >= 0.0F ? scale1 : -scale1; + return result.set(scale0 * getX() + scale1 * other.getX(), scale0 * getY() + scale1 * other.getY(), scale0 * getZ() + scale1 * other.getZ(), scale0 * getW() + scale1 * other.getW()); + } + + public default float length(){return (float)Math.sqrt(lengthSquared());} + public default double lengthSquared(){return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()) + (getW() * getW());} + + public default Matrix4f asRotationMatrix(){return new Matrix4f().rotate(this);} + public default Vec4f toAxisDegreeRotation() {return toAxisDegreeRotation(Vec4f.newMutable());} + public default Vec4f toAxisDegreeRotation(Vec4f input) + { + double invSqrt = 1.0D / Math.sqrt(1.0D - getW() * getW()); + return input.set((float)(getX() * invSqrt), (float)(getY() * invSqrt), (float)(getZ() * invSqrt), (float)Math.toDegrees(acos(getW()) * 2F)); + } + public default Vec4f toAxisRotation() {return toAxisRotation(Vec4f.newMutable());} + public default Vec4f toAxisRotation(Vec4f input) + { + double invSqrt = 1.0D / Math.sqrt(1.0D - getW() * getW()); + return input.set((float)(getX() * invSqrt), (float)(getY() * invSqrt), (float)(getZ() * invSqrt), (float)acos(getW()) * 2F); + } + + public default Quaternion store(ByteBuffer buffer) + { + buffer.putFloat(getX()).putFloat(getY()).putFloat(getZ()).putFloat(getW()); + return this; + } + + public default Quaternion load(ByteBuffer buffer) + { + return set(buffer.getFloat(), buffer.getFloat(), buffer.getFloat(), buffer.getFloat()); + } + + public default Quaternion store(FloatBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()).put(getW()); + return this; + } + + public default Quaternion load(FloatBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get(), buffer.get()); + } + + public Quaternion copy(); + public boolean isMutable(); + public default Quaternion asMutable(){return isMutable() ? this : newQuat(this);} + public default Quaternion asImmutable(){return isMutable() ? newMutable(this) : this;} + public default Quaternion copyAsMutable(){return newMutable(this);} + public default Quaternion copyAsImmutable(){return newQuat(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/quaternion/QuaternionImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/quaternion/QuaternionImmutable.java new file mode 100644 index 0000000..f5892ef --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/quaternion/QuaternionImmutable.java @@ -0,0 +1,116 @@ +package speiger.src.coreengine.math.vector.quaternion; + +import java.util.Arrays; + +public class QuaternionImmutable implements Quaternion +{ + final float x; + final float y; + final float z; + final float w; + + public QuaternionImmutable() + { + x = 0F; + y = 0F; + z = 0F; + w = 1F; + } + + public QuaternionImmutable(float x, float y, float z, float w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public Quaternion setX(float x) + { + return this.x == x ? this : set(x, y, z, w); + } + + @Override + public Quaternion setY(float y) + { + return this.y == y ? this : set(x, y, z, w); + } + + @Override + public Quaternion setZ(float z) + { + return this.z == z ? this : set(x, y, z, w); + } + + @Override + public Quaternion setW(float w) + { + return this.w == w ? this : set(x, y, z, w); + } + + @Override + public float getX() + { + return x; + } + + @Override + public float getY() + { + return y; + } + + @Override + public float getZ() + { + return z; + } + + @Override + public float getW() + { + return w; + } + + @Override + public Quaternion set(float x, float y, float z, float w) + { + return this.x == x && this.y == y && this.z == z && this.w == w ? this : Quaternion.newQuat(x, y, z, w); + } + + @Override + public Quaternion copy() + { + return Quaternion.newQuat(this); + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public int hashCode() + { + return Arrays.hashCode(new float[]{x,y,z,w}); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Quaternion) + { + Quaternion other = (Quaternion)obj; + return other.getX() == x && other.getY() == y && other.getZ() == z && other.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Quaternion[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/quaternion/QuaternionMutable.java b/src/main/java/speiger/src/coreengine/math/vector/quaternion/QuaternionMutable.java new file mode 100644 index 0000000..61b8d46 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/quaternion/QuaternionMutable.java @@ -0,0 +1,124 @@ +package speiger.src.coreengine.math.vector.quaternion; + +import java.util.Arrays; + +public class QuaternionMutable implements Quaternion +{ + float x; + float y; + float z; + float w; + + public QuaternionMutable() + { + x = 0F; + y = 0F; + z = 0F; + w = 1F; + } + + public QuaternionMutable(float x, float y, float z, float w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public Quaternion setX(float x) + { + this.x = x; + return this; + } + + @Override + public Quaternion setY(float y) + { + this.y = y; + return this; + } + + @Override + public Quaternion setZ(float z) + { + this.z = z; + return this; + } + + @Override + public Quaternion setW(float w) + { + this.w = w; + return this; + } + + @Override + public float getX() + { + return x; + } + + @Override + public float getY() + { + return y; + } + + @Override + public float getZ() + { + return z; + } + + @Override + public float getW() + { + return w; + } + + @Override + public Quaternion set(float x, float y, float z, float w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + @Override + public Quaternion copy() + { + return Quaternion.newMutable(this); + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public int hashCode() + { + return Arrays.hashCode(new float[]{x,y,z,w}); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Quaternion) + { + Quaternion other = (Quaternion)obj; + return other.getX() == x && other.getY() == y && other.getZ() == z && other.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Quaternion[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2s.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2s.java new file mode 100644 index 0000000..d32573f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2s.java @@ -0,0 +1,162 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec2b; +import speiger.src.coreengine.math.vector.doubles.Vec2d; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.math.vector.longs.Vec2l; + +public interface Vec2s extends Vecs +{ + public static final Vec2s ZERO = newVec(); + public static final Vec2s MINUS_ONE = newVec((short)-1); + public static final Vec2s ONE = newVec((short)1); + + public static Vec2s newMutable(){return new Vec2sMutable();} + public static Vec2s newMutable(short value){return new Vec2sMutable(value);} + public static Vec2s newMutable(short x, short y){return new Vec2sMutable(x, y);} + public static Vec2s newMutable(Vec2s value){return newMutable(value.getX(), value.getY());} + + public static Vec2s newVec(){return new Vec2sImmutable();} + public static Vec2s newVec(short value){return new Vec2sImmutable(value);} + public static Vec2s newVec(short x, short y){return new Vec2sImmutable(x, y);} + public static Vec2s newVec(Vec2s value){return newVec(value.getX(), value.getY());} + + public short getX(); + public short getY(); + public Vec2s setX(short x); + public Vec2s setY(short y); + @Override + public default short[] asArray(){return new short[]{getX(), getY()};} + @Override + public Vec2s copy(); + @Override + public default Vec2s abs(){return set((short)Math.abs(getX()), (short)Math.abs(getY()));} + @Override + public default Vec2s negate(){return set((short)0, (short)0);} + @Override + public default Vec2s invert(){return set((short)(-getX()), (short)(-getY()));}; + + @Override + public default Vec2s add(short value) {return add(value, value);} + public default Vec2s add(Vec2s value) {return add(value.getX(), value.getY());} + public default Vec2s add(short x, short y) {return set((short)(x + getX()), (short)(y + getY()));} + + @Override + public default Vec2s sub(short value){return sub(value, value);} + public default Vec2s sub(Vec2s value){return sub(value.getX(), value.getY());} + public default Vec2s sub(short x, short y) {return set((short)(getX() - x), (short)(getY() - y));} + + @Override + public default Vec2s multiply(short value){return multiply(value, value);} + public default Vec2s multiply(Vec2s value){return multiply(value.getX(), value.getY());} + public default Vec2s multiply(short x, short y) {return set((short)(x * getX()), (short)(y * getY()));} + + @Override + public default Vec2s devide(short value){return devide(value, value);} + public default Vec2s devide(Vec2s value){return devide(value.getX(), value.getY());} + public default Vec2s devide(short x, short y){return set((short)(getX() / x), (short)(getY() / y));} + + @Override + public default Vec2s set(short value){return set(value, value);}; + public default Vec2s set(Vec2s value){return set(value.getX(), value.getY());} + public Vec2s set(short x, short y); + + public default double distanceTo(Vec2s value){return distanceTo(value.getX(), value.getY());} + public default double distanceTo(short x, short y){return Math.sqrt(distanceToSquared(x, y));} + + public default long distanceToSquared(Vec2s value){return distanceToSquared(value.getX(), value.getY());} + public default long distanceToSquared(short x, short y) + { + long xPos = getX() - x; + long yPos = getY() - y; + return (xPos * xPos) + (yPos * yPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY());} + + public default long dotProduct(Vec2s value){return dotProduct(value.getX(), value.getY());} + public default long dotProduct(short x, short y){return (getX() * x) + (getY() * y);} + + public default Vec2s rotate(short angle, Vec2s center){return rotate(angle, center.getX(), center.getY());} + public default Vec2s rotate(short angle, short x, short y) + { + short xPos = (short)(getX() - x); + short yPos = (short)(getY() - y); + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + return set((short)((xPos * cos) + (yPos * sin) + x), (short)(-(xPos * sin) + (yPos * cos) + y)); + } + + public default Vec2s min(Vec2s other) {return min(other, this);} + public default Vec2s min(Vec2s other, Vec2s result){return min(other.getX(), other.getY(), result);} + public default Vec2s min(short x, short y) {return min(x, y, this);} + public default Vec2s min(short x, short y, Vec2s result){return result.set((short)Math.min(getX(), x), (short)Math.min(getY(), y));} + + public default Vec2s max(Vec2s other) {return max(other, this);} + public default Vec2s max(Vec2s other, Vec2s result){return max(other.getX(), other.getY(), result);} + public default Vec2s max(short x, short y) {return max(x, y, this);} + public default Vec2s max(short x, short y, Vec2s result){return result.set((short)Math.max(getX(), x), (short)Math.max(getY(), y));} + + public default Vec2s difference(Vec2s other) {return difference(other, this);} + public default Vec2s difference(Vec2s other, Vec2s result){return difference(other.getX(), other.getY(), result);} + public default Vec2s difference(short x, short y) {return difference(x, y, this);} + public default Vec2s difference(short x, short y, Vec2s result){return result.set((short)(getX() - x), (short)(getY() - y));} + + @Override + public default Vec2s clamp(short min, short max){return clamp(min, max, ALL);} + public default Vec2s clamp(short min, short max, Vec2s result){return clamp(min, max, result, ALL);} + @Override + public default Vec2s clamp(short min, short max, int filter){return clamp(min, max, this, filter);} + public default Vec2s clamp(short min, short max, Vec2s result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()));} + + @Override + public default Vec2s store(ByteBuffer buffer) + { + buffer.putShort(getX()).putShort(getY()); + return this; + } + + @Override + public default Vec2s load(ByteBuffer buffer) + { + return set(buffer.getShort(), buffer.getShort()); + } + + @Override + public default Vec2s store(ShortBuffer buffer) + { + buffer.put(getX()).put(getY()); + return this; + } + + @Override + public default Vec2s load(ShortBuffer buffer) + { + return set(buffer.get(), buffer.get()); + } + + @Override + public default Vec2b asByte(){return isMutable() ? Vec2b.newMutable((byte)getX(), (byte)getY()) : Vec2b.newVec((byte)getX(), (byte)getY());} + @Override + public default Vec2i asInt(){return isMutable() ? Vec2i.newMutable(getX(), getY()) : Vec2i.newVec(getX(), getY());} + @Override + public default Vec2l asLong(){return isMutable() ? Vec2l.newMutable(getX(), getY()) : Vec2l.newVec(getX(), getY());} + @Override + public default Vec2f asFloat() {return isMutable() ? Vec2f.newMutable(getX(), getY()) : Vec2f.newVec(getX(), getY());} + @Override + public default Vec2d asDouble(){return isMutable() ? Vec2d.newMutable(getX(), getY()) : Vec2d.newVec(getX(), getY());} + + @Override + public default Vec2s asMutable(){return isMutable() ? this : newVec(this);} + @Override + public default Vec2s asImmutable(){return isMutable() ? newMutable(this) : this;} + @Override + public default Vec2s copyAsMutable(){return newMutable(this);} + @Override + public default Vec2s copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2sImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2sImmutable.java new file mode 100644 index 0000000..121ade3 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2sImmutable.java @@ -0,0 +1,92 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.util.Objects; + +public class Vec2sImmutable implements Vec2s +{ + final short x; + final short y; + + public Vec2sImmutable() + { + x = 0; + y = 0; + } + + public Vec2sImmutable(short value) + { + x = value; + y = value; + } + + public Vec2sImmutable(short x, short y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public short getX() + { + return x; + } + + @Override + public short getY() + { + return y; + } + + @Override + public Vec2s setX(short x) + { + return this.x == x ? this : Vec2s.newVec(x, y); + } + + @Override + public Vec2s setY(short y) + { + return this.y == y ? this : Vec2s.newVec(x, y); + } + + @Override + public Vec2s copy() + { + return Vec2s.newVec(this); + } + + @Override + public Vec2s set(short x, short y) + { + return this.x == x && this.y == y ? this : Vec2s.newVec(x, y); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2s) + { + Vec2s vec = (Vec2s)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2s[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2sMutable.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2sMutable.java new file mode 100644 index 0000000..ada2c0e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2sMutable.java @@ -0,0 +1,94 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.util.Objects; + +public class Vec2sMutable implements Vec2s +{ + short x; + short y; + + public Vec2sMutable() + { + } + + public Vec2sMutable(short value) + { + x = value; + y = value; + } + + public Vec2sMutable(short x, short y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public short getX() + { + return x; + } + + @Override + public short getY() + { + return y; + } + + @Override + public Vec2s setX(short x) + { + this.x = x; + return this; + } + + @Override + public Vec2s setY(short y) + { + this.y = y; + return this; + } + + @Override + public Vec2s copy() + { + return Vec2s.newMutable(this); + } + + @Override + public Vec2s set(short x, short y) + { + this.x = x; + this.y = y; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2s) + { + Vec2s vec = (Vec2s)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2s[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3s.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3s.java new file mode 100644 index 0000000..b6584cb --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3s.java @@ -0,0 +1,156 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec3b; +import speiger.src.coreengine.math.vector.doubles.Vec3d; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.ints.Vec3i; +import speiger.src.coreengine.math.vector.longs.Vec3l; + +public interface Vec3s extends Vecs +{ + public static final Vec3s ZERO = newVec(); + public static final Vec3s MINUS_ONE = newVec((short)-1); + public static final Vec3s ONE = newVec((short)1); + + public static Vec3s newMutable(){return new Vec3sMutable();} + public static Vec3s newMutable(short value){return new Vec3sMutable(value);} + public static Vec3s newMutable(short x, short y, short z){return new Vec3sMutable(x, y, z);} + public static Vec3s newMutable(Vec3s vec){return newMutable(vec.getX(), vec.getY(), vec.getZ());} + + public static Vec3s newVec(){return new Vec3sImmutable();} + public static Vec3s newVec(short value){return new Vec3sImmutable(value);} + public static Vec3s newVec(short x, short y, short z){return new Vec3sImmutable(x, y, z);} + public static Vec3s newVec(Vec3s vec){return newVec(vec.getX(), vec.getY(), vec.getZ());} + + public short getX(); + public short getY(); + public short getZ(); + public Vec3s setX(short x); + public Vec3s setY(short y); + public Vec3s setZ(short z); + @Override + public default short[] asArray(){return new short[]{getX(), getY(), getZ()};} + + @Override + public Vec3s copy(); + @Override + public default Vec3s abs(){return set((short)Math.abs(getX()), (short)Math.abs(getY()), (short)Math.abs(getZ()));} + @Override + public default Vec3s negate(){return set((short)0, (short)0, (short)0);} + @Override + public default Vec3s invert(){return set((short)-getX(), (short)-getY(), (short)-getZ());} + @Override + public default Vec3s add(short value){return add(value, value, value);} + public default Vec3s add(Vec3s value){return add(value.getX(), value.getY(), value.getZ());} + public default Vec3s add(short x, short y, short z){return set((short)(getX() + x), (short)(getY() + y), (short)(getZ() + z));} + + @Override + public default Vec3s sub(short value){return sub(value, value, value);} + public default Vec3s sub(Vec3s value){return sub(value.getX(), value.getY(), value.getZ());} + public default Vec3s sub(short x, short y, short z){return set((short)(getX() - x), (short)(getY() - y), (short)(getZ() - z));} + + @Override + public default Vec3s multiply(short value){return multiply(value, value, value);} + public default Vec3s multiply(Vec3s value){return multiply(value.getX(), value.getY(), value.getZ());} + public default Vec3s multiply(short x, short y, short z){return set((short)(getX() * x), (short)(getY() * y), (short)(getZ() * z));} + + @Override + public default Vec3s devide(short value){return devide(value, value, value);} + public default Vec3s devide(Vec3s value){return devide(value.getX(), value.getY(), value.getZ());} + public default Vec3s devide(short x, short y, short z){return set((short)(getX() / x), (short)(getY() / y), (short)(getZ() / z));} + + @Override + public default Vec3s set(short value){return set(value, value, value);} + public default Vec3s set(Vec3s value){return set(value.getX(), value.getY(), value.getZ());} + public Vec3s set(short x, short y, short z); + + public default double distanceTo(Vec3s value){return distanceTo(value.getX(), value.getY(), value.getZ());} + public default double distanceTo(short x, short y, short z){return Math.sqrt(distanceToSquared(x, y, z));} + + public default long distanceToSquared(Vec3s value){return distanceToSquared(value.getX(), value.getY(), value.getZ());} + public default long distanceToSquared(short x, short y, short z) + { + long xPos = getX() - x; + long yPos = getY() - y; + long zPos = getZ() - z; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ());} + + public default long dotProduct(Vec3s value){return dotProduct(value.getX(), value.getY(), value.getZ());} + public default long dotProduct(short x, short y, short z){return (getX() * x) + (getY() * y) + (getZ() * z);} + + public default Vec3s min(Vec3s other) {return min(other, this);} + public default Vec3s min(Vec3s other, Vec3s result){return min(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3s min(short x, short y, short z) {return min(x, y, z, this);} + public default Vec3s min(short x, short y, short z, Vec3s result){return result.set((short)Math.min(getX(), x), (short)Math.min(getY(), y), (short)Math.min(getZ(), z));} + + public default Vec3s max(Vec3s other) {return max(other, this);} + public default Vec3s max(Vec3s other, Vec3s result){return max(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3s max(short x, short y, short z) {return max(x, y, z, this);} + public default Vec3s max(short x, short y, short z, Vec3s result){return result.set((short)Math.max(getX(), x), (short)Math.max(getY(), y), (short)Math.max(getZ(), z));} + + public default Vec3s difference(Vec3s other) {return difference(other, this);} + public default Vec3s difference(Vec3s other, Vec3s result){return difference(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3s difference(short x, short y, short z) {return difference(x, y, z, this);} + public default Vec3s difference(short x, short y, short z, Vec3s result){return result.set((short)(getX() - x), (short)(getY() - y), (short)(getZ() - z));} + + @Override + public default Vec3s clamp(short min, short max){return clamp(min, max, ALL);} + public default Vec3s clamp(short min, short max, Vec3s result){return clamp(min, max, result, ALL);} + @Override + public default Vec3s clamp(short min, short max, int filter){return clamp(min, max, this, filter);} + public default Vec3s clamp(short min, short max, Vec3s result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()));} + + @Override + public default Vec3s store(ByteBuffer buffer) + { + buffer.putShort(getX()).putShort(getY()).putShort(getZ()); + return this; + } + + @Override + public default Vec3s load(ByteBuffer buffer) + { + return set(buffer.getShort(), buffer.getShort(), buffer.getShort()); + } + + @Override + public default Vec3s store(ShortBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()); + return this; + } + + @Override + public default Vec3s load(ShortBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec3b asByte(){return isMutable() ? Vec3b.newMutable((byte)getX(), (byte)getY(), (byte)getZ()) : Vec3b.newVec((byte)getX(), (byte)getY(), (byte)getZ());} + @Override + public default Vec3i asInt(){return isMutable() ? Vec3i.newMutable(getX(), getY(), getZ()) : Vec3i.newVec(getX(), getY(), getZ());} + @Override + public default Vec3l asLong(){return isMutable() ? Vec3l.newMutable(getX(), getY(), getZ()) : Vec3l.newVec(getX(), getY(), getZ());} + @Override + public default Vec3f asFloat() {return isMutable() ? Vec3f.newMutable(getX(), getY(), getZ()) : Vec3f.newVec(getX(), getY(), getZ());} + @Override + public default Vec3d asDouble(){return isMutable() ? Vec3d.newMutable(getX(), getY(), getZ()) : Vec3d.newVec(getX(), getY(), getZ());} + + + @Override + public default Vec3s asMutable(){return isMutable() ? this : newVec(this);} + @Override + public default Vec3s asImmutable(){return isMutable() ? newMutable(this) : this;} + @Override + public default Vec3s copyAsMutable(){return newMutable(this);} + @Override + public default Vec3s copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3sImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3sImmutable.java new file mode 100644 index 0000000..416eec8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3sImmutable.java @@ -0,0 +1,108 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.util.Objects; + +public class Vec3sImmutable implements Vec3s +{ + final short x; + final short y; + final short z; + + public Vec3sImmutable() + { + x = 0; + y = 0; + z = 0; + } + + public Vec3sImmutable(short value) + { + x = value; + y = value; + z = value; + } + + public Vec3sImmutable(short x, short y, short z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public short getX() + { + return x; + } + + @Override + public short getY() + { + return y; + } + + @Override + public short getZ() + { + return z; + } + + @Override + public Vec3s setX(short x) + { + return this.x == x ? this : Vec3s.newVec(x, y, z); + } + + @Override + public Vec3s setY(short y) + { + return this.y == y ? this : Vec3s.newVec(x, y, z); + } + + @Override + public Vec3s setZ(short z) + { + return this.z == z ? this : Vec3s.newVec(x, y, z); + } + + @Override + public Vec3s copy() + { + return Vec3s.newVec(this); + } + + @Override + public Vec3s set(short x, short y, short z) + { + return this.x == x && this.y == y && this.z == z ? this : Vec3s.newVec(x, y, z); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3s) + { + Vec3s vec = (Vec3s)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3s[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3sMutable.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3sMutable.java new file mode 100644 index 0000000..5c4c243 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3sMutable.java @@ -0,0 +1,111 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.util.Objects; + +public class Vec3sMutable implements Vec3s +{ + short x; + short y; + short z; + + public Vec3sMutable() + { + } + + public Vec3sMutable(short value) + { + x = value; + y = value; + z = value; + } + + public Vec3sMutable(short x, short y, short z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public short getX() + { + return x; + } + + @Override + public short getY() + { + return y; + } + + @Override + public short getZ() + { + return z; + } + + @Override + public Vec3s setX(short x) + { + this.x = x; + return this; + } + + @Override + public Vec3s setY(short y) + { + this.y = y; + return this; + } + + @Override + public Vec3s setZ(short z) + { + this.z = z; + return this; + } + + @Override + public Vec3s copy() + { + return Vec3s.newMutable(this); + } + + @Override + public Vec3s set(short x, short y, short z) + { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3s) + { + Vec3s vec = (Vec3s)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3s[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4s.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4s.java new file mode 100644 index 0000000..9aa7ebd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4s.java @@ -0,0 +1,159 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec4b; +import speiger.src.coreengine.math.vector.doubles.Vec4d; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.ints.Vec4i; +import speiger.src.coreengine.math.vector.longs.Vec4l; + +public interface Vec4s extends Vecs +{ + public static final Vec4s ZERO = newVec(); + public static final Vec4s MINUS_ONE = newVec((short)-1); + public static final Vec4s ONE = newVec((short)1); + + public static Vec4s newMutable(){return new Vec4sMutable();} + public static Vec4s newMutable(short value){return new Vec4sMutable(value);} + public static Vec4s newMutable(short x, short y, short z, short w){return new Vec4sMutable(x, y, z, w);} + public static Vec4s newMutable(Vec4s vec){return newMutable(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public static Vec4s newVec(){return new Vec4sImmutable();} + public static Vec4s newVec(short value){return new Vec4sImmutable(value);} + public static Vec4s newVec(short x, short y, short z, short w){return new Vec4sImmutable(x, y, z, w);} + public static Vec4s newVec(Vec4s vec){return newVec(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public short getX(); + public short getY(); + public short getZ(); + public short getW(); + public Vec4s setX(short x); + public Vec4s setY(short y); + public Vec4s setZ(short z); + public Vec4s setW(short w); + @Override + public default short[] asArray(){return new short[]{getX(), getY(), getZ(), getW()};} + @Override + public Vec4s copy(); + @Override + public default Vec4s abs(){return set((short)Math.abs(getX()), (short)Math.abs(getY()), (short)Math.abs(getZ()), (short)Math.abs(getW()));} + @Override + public default Vec4s negate(){return set((short)0, (short)0, (short)0, (short)0);} + @Override + public default Vec4s invert(){return set((short)-getX(), (short)-getY(), (short)-getZ(), (short)-getW());} + + @Override + public default Vec4s add(short value){return add(value, value, value, value);} + public default Vec4s add(Vec4s value){return add(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4s add(short x, short y, short z, short w){return set((short)(getX() + x), (short)(getY() + y), (short)(getZ() + z), (short)(getW() + w));} + + @Override + public default Vec4s sub(short value){return sub(value, value, value, value);} + public default Vec4s sub(Vec4s value){return sub(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4s sub(short x, short y, short z, short w){return set((short)(getX() - x), (short)(getY() - y), (short)(getZ() - z), (short)(getW() - w));} + + @Override + public default Vec4s multiply(short value){return multiply(value, value, value, value);} + public default Vec4s multiply(Vec4s value){return multiply(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4s multiply(short x, short y, short z, short w){return set((short)(getX() * x), (short)(getY() * y), (short)(getZ() * z), (short)(getW() * w));} + + @Override + public default Vec4s devide(short value){return devide(value, value, value, value);} + public default Vec4s devide(Vec4s value){return devide(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4s devide(short x, short y, short z, short w){return set((short)(getX() / x), (short)(getY() / y), (short)(getZ() / z), (short)(getW() / w));} + + @Override + public default Vec4s set(short value){return set(value, value, value, value);} + public default Vec4s set(Vec4s value){return set(value.getX(), value.getY(), value.getZ(), value.getW());} + public Vec4s set(short x, short y, short z, short w); + + public default double distanceTo(Vec4s value){return distanceTo(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double distanceTo(short x, short y, short z, short w){return Math.sqrt(distanceTo(x, y, z, w));} + + public default long distanceToSquared(Vec4s value){return distanceToSquared(value.getX(), value.getY(), value.getZ(), value.getW());} + public default long distanceToSquared(short x, short y, short z, short w) + { + long xPos = getX() - x; + long yPos = getY() - y; + long zPos = getZ() - z; + long wPos = getW() - w; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos) + (wPos * wPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()) + (getW() * getW());} + + public default long dotProduct(Vec4s value){return dotProduct(value.getX(), value.getY(), value.getZ(), value.getW());} + public default long dotProduct(short x, short y, short z, short w){return (getX() * x) + (getY() * y) + (getZ() * z) + (getW() * w);}; + + public default Vec4s min(Vec4s other) {return min(other, this);} + public default Vec4s min(Vec4s other, Vec4s result){return min(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4s min(short x, short y, short z, short w) {return min(x, y, z, w, this);} + public default Vec4s min(short x, short y, short z, short w, Vec4s result){return result.set((short)Math.min(getX(), x), (short)Math.min(getY(), y), (short)Math.min(getZ(), z), (short)Math.min(getW(), w));} + + public default Vec4s max(Vec4s other) {return max(other, this);} + public default Vec4s max(Vec4s other, Vec4s result){return max(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4s max(short x, short y, short z, short w) {return max(x, y, z, w, this);} + public default Vec4s max(short x, short y, short z, short w, Vec4s result){return result.set((short)Math.max(getX(), x), (short)Math.max(getY(), y), (short)Math.max(getZ(), z), (short)Math.max(getZ(), z));} + + public default Vec4s difference(Vec4s other) {return difference(other, this);} + public default Vec4s difference(Vec4s other, Vec4s result){return difference(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4s difference(short x, short y, short z, short w) {return difference(x, y, z, w, this);} + public default Vec4s difference(short x, short y, short z, short w, Vec4s result){return result.set((short)(getX() - x), (short)(getY() - y), (short)(getZ() - z), (short)(getW() - w));} + + @Override + public default Vec4s clamp(short min, short max){return clamp(min, max, ALL);} + public default Vec4s clamp(short min, short max, Vec4s result){return clamp(min, max, result, ALL);} + @Override + public default Vec4s clamp(short min, short max, int filter){return clamp(min, max, this, filter);} + public default Vec4s clamp(short min, short max, Vec4s result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()), (filter & W) == 0 ? getW() : MathUtils.clamp(min, max, getW()));} + + @Override + public default Vec4s store(ByteBuffer buffer) + { + buffer.putShort(getX()).putShort(getY()).putShort(getZ()).putShort(getW()); + return this; + } + + @Override + public default Vec4s load(ByteBuffer buffer) + { + return set(buffer.getShort(), buffer.getShort(), buffer.getShort(), buffer.getShort()); + } + + @Override + public default Vec4s store(ShortBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()).put(getW()); + return this; + } + + @Override + public default Vec4s load(ShortBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec4b asByte(){return isMutable() ? Vec4b.newMutable((byte)getX(), (byte)getY(), (byte)getZ(), (byte)getW()) : Vec4b.newVec((byte)getX(), (byte)getY(), (byte)getZ(), (byte)getW());} + @Override + public default Vec4i asInt(){return isMutable() ? Vec4i.newMutable(getX(), getY(), getZ(), getW()) : Vec4i.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4l asLong(){return isMutable() ? Vec4l.newMutable(getX(), getY(), getZ(), getW()) : Vec4l.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4f asFloat() {return isMutable() ? Vec4f.newMutable(getX(), getY(), getZ(), getW()) : Vec4f.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4d asDouble(){return isMutable() ? Vec4d.newMutable(getX(), getY(), getZ(), getW()) : Vec4d.newVec(getX(), getY(), getZ(), getW());} + + + @Override + public default Vec4s asMutable(){return isMutable() ? this : newVec(this);} + @Override + public default Vec4s asImmutable(){return isMutable() ? newMutable(this) : this;} + @Override + public default Vec4s copyAsMutable(){return newMutable(this);} + @Override + public default Vec4s copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4sImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4sImmutable.java new file mode 100644 index 0000000..0c7763a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4sImmutable.java @@ -0,0 +1,124 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.util.Objects; + +public class Vec4sImmutable implements Vec4s +{ + final short x; + final short y; + final short z; + final short w; + + public Vec4sImmutable() + { + x = 0; + y = 0; + z = 0; + w = 0; + } + + public Vec4sImmutable(short value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4sImmutable(short x, short y, short z, short w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public short getX() + { + return x; + } + + @Override + public short getY() + { + return y; + } + + @Override + public short getZ() + { + return z; + } + + @Override + public short getW() + { + return w; + } + + @Override + public Vec4s setX(short x) + { + return this.x == x ? this : Vec4s.newVec(x, y, z, w); + } + + @Override + public Vec4s setY(short y) + { + return this.y == y ? this : Vec4s.newVec(x, y, z, w); + } + + @Override + public Vec4s setZ(short z) + { + return this.z == z ? this : Vec4s.newVec(x, y, z, w); + } + + @Override + public Vec4s setW(short w) + { + return this.w == w ? this : Vec4s.newVec(x, y, z, w); + } + + @Override + public Vec4s copy() + { + return Vec4s.newVec(this); + } + + @Override + public Vec4s set(short x, short y, short z, short w) + { + return this.x == x && this.y == y && this.z == z && this.w == w ? this : Vec4s.newVec(x, y, z, w); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4s) + { + Vec4s vec = (Vec4s)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4s[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4sMutable.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4sMutable.java new file mode 100644 index 0000000..5046117 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4sMutable.java @@ -0,0 +1,128 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.util.Objects; + +public class Vec4sMutable implements Vec4s +{ + short x; + short y; + short z; + short w; + + public Vec4sMutable() + { + } + + public Vec4sMutable(short value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4sMutable(short x, short y, short z, short w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public short getX() + { + return x; + } + + @Override + public short getY() + { + return y; + } + + @Override + public short getZ() + { + return z; + } + + @Override + public short getW() + { + return w; + } + + @Override + public Vec4s setX(short x) + { + this.x = x; + return this; + } + + @Override + public Vec4s setY(short y) + { + this.y = y; + return this; + } + + @Override + public Vec4s setZ(short z) + { + this.z = z; + return this; + } + + @Override + public Vec4s setW(short w) + { + this.w = w; + return this; + } + + @Override + public Vec4s copy() + { + return Vec4s.newMutable(this); + } + + @Override + public Vec4s set(short x, short y, short z, short w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4s) + { + Vec4s vec = (Vec4s)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4s[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vecs.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vecs.java new file mode 100644 index 0000000..2c71c86 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vecs.java @@ -0,0 +1,34 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.nio.ShortBuffer; + +import speiger.src.coreengine.math.vector.Vec; +import speiger.src.coreengine.math.vector.bytes.Vecb; +import speiger.src.coreengine.math.vector.doubles.Vecd; +import speiger.src.coreengine.math.vector.floats.Vecf; +import speiger.src.coreengine.math.vector.ints.Veci; +import speiger.src.coreengine.math.vector.longs.Vecl; + +public interface Vecs extends Vec +{ + public Vecs set(short value); + public Vecs add(short value); + public Vecs sub(short value); + public Vecs multiply(short value); + public Vecs devide(short value); + public Vecs clamp(short min, short max); + public Vecs clamp(short min, short max, int filter); + + public long lengthSquared(); + public default double length(){return Math.sqrt(lengthSquared());} + + public Vecs store(ShortBuffer buffer); + public Vecs load(ShortBuffer buffer); + public short[] asArray(); + + public Vecb asByte(); + public Veci asInt(); + public Vecl asLong(); + public Vecf asFloat(); + public Vecd asDouble(); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/FontLoader.java b/src/main/java/speiger/src/coreengine/rendering/gui/FontLoader.java new file mode 100644 index 0000000..52a4198 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/FontLoader.java @@ -0,0 +1,118 @@ +package speiger.src.coreengine.rendering.gui; + +import java.io.BufferedReader; +import java.util.HashMap; +import java.util.Map; + +import speiger.src.collections.chars.maps.impl.hash.Char2ObjectOpenHashMap; +import speiger.src.collections.chars.maps.interfaces.Char2ObjectMap; +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.IAsset; +import speiger.src.coreengine.rendering.gui.renderer.FontRenderer; +import speiger.src.coreengine.rendering.gui.renderer.IFontRenderer.CharInstance; +import speiger.src.coreengine.rendering.textures.SimpleTexture; +import speiger.src.coreengine.rendering.textures.TextureManager; + +public class FontLoader +{ + public static FontRenderer createFont(AssetLocation location, String name) + { + return createFont(location, name, 0.35F); + } + + public static FontRenderer createFont(AssetLocation location, String name, float scale) + { + try(IAsset asset = TextureManager.INSTANCE.getManager().getAsset(location.subAsset(name+".fnt"))) + { + Char2ObjectMap[] maps = new Char2ObjectMap[]{new Char2ObjectOpenHashMap(), new Char2ObjectOpenHashMap()}; + BufferedReader reader = asset.getStringReader(); + FontInfo info = new FontInfo(convert(reader.readLine().split(", "))); + String value = null; + while((value = getNextValidLine(reader)) != null) + { + Map dataMap = convert(value.split(", ")); + CharInstance instance = createChar(dataMap, info); + instance.scale(scale); + maps[instance.isBold() ? 1 : 0].putIfAbsent(instance.getCharacter(), instance); + } + info.scale(scale); + return new FontRenderer(maps, new SimpleTexture(location.subAsset(name+"-Texture.png")), info.fontHeight, info.lineHeight); + } + catch(Exception e) + { + e.printStackTrace(); + } + return null; + } + + static String getNextValidLine(BufferedReader reader) throws Exception + { + String line = reader.readLine(); + if(line == null) + { + return null; + } + else if(line.isEmpty() || line.startsWith("//")) + { + return getNextValidLine(reader); + } + return line; + } + + static CharInstance createChar(Map data, FontInfo info) + { + char character = (char)data.get("letter").intValue(); + int minX = data.get("minX"); + int minY = data.get("minY"); + int maxX = data.get("maxX"); + int maxY = data.get("maxY"); + return new CharInstance(character, maxX - minX, maxY - minY, info.getTextureU(minX), info.getTextureV(minY), info.getTextureU(maxX), info.getTextureV(maxY), maxX - minX, data.getOrDefault("bold", 0).intValue() == 1); + } + + static Map convert(String[] data) + { + Map map = new HashMap(); + for(String s : data) + { + int index = s.indexOf("="); + if(index != -1) + { + String[] split = s.split("="); + map.put(split[0], Integer.parseInt(split[1])); + } + } + return map; + } + + public static class FontInfo + { + int height; + int width; + float fontHeight; + float lineHeight; + + public FontInfo(Map data) + { + width = data.get("textureWidth"); + height = data.get("textureHeight"); + fontHeight = data.get("fontHeight"); + lineHeight = data.get("base"); + } + + public void scale(float scale) + { + fontHeight *= scale; + lineHeight *= scale; + } + + public float getTextureU(float value) + { + return value / width; + } + + public float getTextureV(float value) + { + return value / height; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/GuiBase.java b/src/main/java/speiger/src/coreengine/rendering/gui/GuiBase.java new file mode 100644 index 0000000..b31667b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/GuiBase.java @@ -0,0 +1,375 @@ +package speiger.src.coreengine.rendering.gui; + +import java.util.function.IntToLongFunction; + +import speiger.src.collections.ints.maps.impl.hash.Int2LongOpenHashMap; +import speiger.src.collections.ints.maps.interfaces.Int2LongMap; +import speiger.src.collections.ints.sets.IntSet; +import speiger.src.coreengine.rendering.gui.base.DebugOverlay; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.base.IKeyComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.renderer.FontRenderer; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.input.events.MouseEvent; +import speiger.src.coreengine.rendering.input.events.MouseEvent.MouseClickEvent; +import speiger.src.coreengine.rendering.input.events.MouseEvent.MouseMoveEvent; +import speiger.src.coreengine.rendering.input.events.MouseEvent.MouseScrollEvent; +import speiger.src.coreengine.rendering.input.window.ScaledResolution; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.utils.profiler.IProfiler; + +public abstract class GuiBase +{ + ButtonMap pressedButtons = new ButtonMap(); + public GuiManager manager; + public int width; + public int height; + boolean focus = false; + public int layers = 0; + int baseLayer = 0; + + GuiBase prevGui; + + public final void setGuiBounds(GuiManager newManager, int screenWidth, int screenHeight) + { + manager = newManager; + width = screenWidth; + height = screenHeight; + } + + public void onInit() + { + + } + + public void onScreenChanged() + { + + } + + public void onFocusLost() + { + + } + + public void onClosed() + { + + } + + public T addComponent(T comp) + { + return addComponent(comp, null); + } + + public T addComponent(int id, T comp) + { + return addComponent(id, comp, null); + } + + public abstract T addComponent(T comp, ComponentConstrains contrains); + + public abstract T addComponent(int id, T comp, ComponentConstrains contrains); + + public T centerComponent(T comp) + { + IGuiBox box = comp.getBox(); + comp.setComponentPosition(Align.CENTER.align(width, box.getWidth()), Align.CENTER.align(height, box.getHeight())); + return comp; + } + + public abstract void addButtonListener(IButtonComponent listener); + + public abstract void addKeyListener(IKeyComponent listener); + + public abstract GuiComponent getComponent(int id); + + public T getCastedComponent(int id) { + GuiComponent component = getComponent(id); + return component == null ? null : component.cast(); + } + + public T getCastedComponent(int id, Class clz) { + GuiComponent component = getComponent(id); + return component == null ? null : component.cast(clz); + } + + public abstract void removeComponent(GuiComponent comp); + + public abstract void removeButtonListener(IButtonComponent listener); + + public abstract void removeKeyListener(IKeyComponent listener); + + protected void addConstrains(GuiComponent comp, ComponentConstrains contrains) + { + comp.constraints = contrains; + if(contrains != null) + { + contrains.setOwner(comp, null); + } + } + + public final void draw(int mouseX, int mouseY, float particalTicks) + { + IProfiler prov = manager.getGPUProfiler(); + prov.start(this instanceof DebugOverlay ? "Debug Overlay" : "Current UI"); + prov.start("Update"); + update(mouseX, mouseY, particalTicks); + prov.next("Render"); + render(mouseX, mouseY, particalTicks); + prov.stop(); + prov.stop(); + } + + public void onFixedUpdate() + { + + } + + protected void update(int mouseX, int mouseY, float particalTicks) + { + + } + + protected void render(int mouseX, int mouseY, float particalTicks) + { + + } + + public boolean isAllowingMovement() + { + return true; + } + + public boolean onKeyTyped(char letter, int keyCode) + { + return false; + } + + public boolean onKeyPressed(int key) + { + return false; + } + + public final void onMouseEvent(MouseEvent evt) + { + if(evt instanceof MouseClickEvent) + { + MouseClickEvent click = (MouseClickEvent)evt; + if(click.press) + { + pressedButtons.add(click.button); + if(onMousePressed(click.button, evt.mouseX, evt.mouseY)) + { + evt.cancel(); + } + } + else + { + pressedButtons.remove(click.button); + if(onMouseReleased(click.button, evt.mouseX, evt.mouseY)) + { + evt.cancel(); + } + } + } + else if(evt instanceof MouseMoveEvent && pressedButtons.size() > 0) + { + if(onMouseDragged(pressedButtons.getButtons(), evt.mouseX, evt.mouseY, pressedButtons)) + { + evt.cancel(); + } + } + else if(evt instanceof MouseScrollEvent) + { + MouseScrollEvent scroll = (MouseScrollEvent)evt; + if(onMouseScroll(evt.mouseX, evt.mouseY, scroll.scrollY)) + { + evt.cancel(); + } + } + } + + public boolean onMousePressed(int button, int mouseX, int mouseY) + { + return false; + } + + public boolean onMouseReleased(int button, int mouseX, int mouseY) + { + return false; + } + + public boolean onMouseDragged(IntSet activeButtons, int mouseX, int mouseY, IntToLongFunction timeMap) + { + return false; + } + + public boolean onMouseScroll(int mouseX, int mouseY, int scrollX) + { + return false; + } + + public boolean isButtonPressed(int mouseButton) + { + return pressedButtons.getButtons().contains(mouseButton); + } + + public long getPressedTime(int mouseButton) + { + return pressedButtons.applyAsLong(mouseButton); + } + + public boolean hasComponentPressed(int mouseButton) + { + return false; + } + + public boolean hasFocus() + { + return focus; + } + + public void requestComponentFocus(GuiComponent comp) + { + } + + public boolean hasComponentInTheWay(GuiComponent comp, int mouseX, int mouseY) + { + return false; + } + + public boolean isComponentInWay(GuiComponent comp, int mouseX, int mouseY) + { + return false; + } + + public boolean isComponentFocused(GuiComponent comp) + { + return false; + } + + public boolean isComponentInFront(GuiComponent comp) + { + return false; + } + + public Window getWindow() + { + return manager.window; + } + + public FontRenderer getFont() + { + return getUIManager().font; + } + + public final long getGlobalClock() + { + return manager.globalClock; + } + + public void closeGui() + { + manager.closeGui(); + } + + public void openGui(GuiBase gui) + { + manager.showGui(gui, false); + } + + public void openGuiWithPrev(GuiBase base) + { + manager.showGui(base, true); + } + + public boolean hasPrevGui() + { + return prevGui != null; + } + + public void setLastGui(GuiBase newLast) + { + prevGui = newLast; + } + + public void openPrevGui() + { + openGui(prevGui); + } + + public void openPrevGui(boolean keepPrev) + { + manager.showGui(prevGui, keepPrev); + } + + public GuiManager getUIManager() + { + return manager; + } + + public UIRenderer getRenderer() + { + return manager.renderer; + } + + public DebugOverlay getOverlay() + { + return manager.getDebug(); + } + + public ScaledResolution getRes() + { + return manager.getRes(); + } + + public final int getBaseLayer() + { + return baseLayer; + } + + public T cast() + { + return (T)this; + } + + public T cast(Class clz) + { + return (T)this; + } + + public static class ButtonMap implements IntToLongFunction + { + Int2LongMap timeMap = new Int2LongOpenHashMap(); + + public void add(int button) + { + timeMap.put(button, System.nanoTime()); + } + + public void remove(int button) + { + timeMap.remove(button); + } + + public int size() + { + return timeMap.size(); + } + + public IntSet getButtons() + { + return timeMap.keySet(); + } + + @Override + public long applyAsLong(int value) + { + return System.nanoTime() - timeMap.get(value); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/GuiComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/GuiComponent.java new file mode 100644 index 0000000..5896268 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/GuiComponent.java @@ -0,0 +1,1115 @@ +package speiger.src.coreengine.rendering.gui; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.function.Consumer; + +import org.lwjgl.glfw.GLFW; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet; +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.math.collision2d.Plane; +import speiger.src.coreengine.math.vector.doubles.Vec2d; +import speiger.src.coreengine.rendering.gui.base.GuiScreenBase; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.base.IKeyComponent; +import speiger.src.coreengine.rendering.gui.components.TextComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; +import speiger.src.coreengine.rendering.gui.helper.animations.Animator; +import speiger.src.coreengine.rendering.gui.helper.box.GuiBox; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constrain; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.input.Keyboard; +import speiger.src.coreengine.rendering.input.bindings.utils.BindingType; +import speiger.src.coreengine.rendering.input.bindings.utils.ModType; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.rendering.utils.Cursor; +import speiger.src.coreengine.rendering.utils.GLUtils; +import speiger.src.coreengine.utils.collections.CollectionUtils; +import speiger.src.coreengine.utils.collections.FlagHolder; + +public abstract class GuiComponent extends FlagHolder +{ + public static final int FLAG_VISIBLE = 1; + public static final int FLAG_ENABLED = 2; + public static final int FLAG_IGNORE_PARENT_BOUNDS = 4; + public static final int FLAG_ALWAYS_CLICKABLE = 8; + public static final int FLAG_RENDER_ORDER = 16; + public static final int FLAG_MANUAL_RENDER = 32; + public static final int FLAG_MASS_CHANGE = 64; + public static final int FLAG_SUPPORT_BINDING = 128; + public static final int FLAG_TEST_SCISSORS = 256; + static final int FLAG_CLOSING = 512; + + public static final int LAST_FLAG = 1 << 19;// This is the last flag and then + // anything behind this is custom + // flags for components + + public static final int LISTENER_USER_ACTION = 0; + public static final int LISTENER_ON_CHANGE = 1; + public static final int LISTENER_CLOSED = 2; + + final IGuiBox box; + GuiBase owner; + GuiComponent parent; + ComponentConstrains constraints = null; + Animator animation = null; + KeyBindAction binding = null; + Set>[] listeners = CollectionUtils.createSets(3, true); + Set children = new ObjectLinkedOpenHashSet<>(); + Set popupChildren = new ObjectLinkedOpenHashSet<>(); + Tooltips tooltips = new Tooltips(); + UUID tooltipId; + float zLevel = 0F; + float totalZ = 0F; + float visiblity = 1F; + float totalVisibility = 1F; + float brightness = 1F; + boolean changed = false; + boolean massRepaint = false; + + public GuiComponent(float x, float y, float width, float height) + { + this(new GuiBox(x, y, width, height)); + } + + public GuiComponent(IGuiBox box) + { + super(FLAG_VISIBLE | FLAG_ENABLED); + this.box = box; + } + + public IGuiBox getBox() + { + return box; + } + + public GuiBase getGui() + { + return owner; + } + + public void calculateActualBounds(float[] area, boolean start) + { + if(start) + { + area[0] = Float.MAX_VALUE; + area[1] = Float.MAX_VALUE; + area[2] = Float.MIN_VALUE; + area[3] = Float.MIN_VALUE; + } + area[0] = Math.min(area[0], box.getMinX()); + area[1] = Math.min(area[1], box.getMinY()); + area[2] = Math.max(area[2], box.getMaxX()); + area[3] = Math.max(area[3], box.getMaxY()); + for(GuiComponent comp : children) + { + if(comp.isVisible()) comp.calculateActualBounds(area, false); + } + } + + public boolean isMouseOver(int mouseX, int mouseY) + { + return (parent == null || isFlagSet(FLAG_IGNORE_PARENT_BOUNDS) || parent.isMouseOver(mouseX, mouseY)) && isOverBox(mouseX, mouseY); + } + + protected boolean isOverBox(int mouseX, int mouseY) + { + return box.isColiding(mouseX, mouseY); + } + + public boolean isHovered(int mouseX, int mouseY) + { + return isParentVisible() && isAnyFlagSet(FLAG_ALWAYS_CLICKABLE | FLAG_ENABLED) && isMouseOver(mouseX, mouseY); + } + + public boolean isTopHovered(int mouseX, int mouseY) + { + if(!isHovered(mouseX, mouseY)) + { + return false; + } + GuiComponent top = getTopComponent(); + return !getGui().hasComponentInTheWay(top, mouseX, mouseY) && top.isChildAtTop(this, mouseX, mouseY); + } + + public boolean isChildAtTop(GuiComponent component, int mouseX, int mouseY) + { + float sourceHeight = component.calculateZLevel(); + if(children.isEmpty()) + { + return true; + } + for(GuiComponent comp : children) + { + if(comp != component && comp.isOverChild(mouseX, mouseY) && (comp.calculateZLevel() > sourceHeight || !comp.isChildAtTop(component, mouseX, mouseY))) + { + return false; + } + } + return true; + } + + public boolean isOverChild(int mouseX, int mouseY) + { + if(children.isEmpty()) + { + return false; + } + for(GuiComponent comp : children) + { + if(comp.isOverChild(mouseX, mouseY) || (comp instanceof IButtonComponent && ((IButtonComponent)comp).isComponentColliding(mouseX, mouseY))) + { + return true; + } + } + return false; + } + + public boolean hasFocus() + { + return getGui().isComponentFocused(getTopComponent()); + } + + public final boolean hasPopups() + { + return popupChildren.size() > 0; + } + + public final void setOwner(GuiBase gui) + { + owner = gui; + for(GuiComponent comp : children) + { + comp.setOwner(gui); + if(comp instanceof IButtonComponent) + { + getGui().addButtonListener((IButtonComponent)comp); + } + if(comp instanceof IKeyComponent) + { + getGui().addKeyListener((IKeyComponent)comp); + } + } + init(); + onComponentChanged(true); + if(binding != null) + { + gui.addKeyListener(binding); + } + } + + public abstract void init(); + + public void onClosed() + { + if(!setFlag(FLAG_CLOSING, true)) + { + return; + } + for(GuiComponent comp : children) + { + comp.onClosed(); + if(comp instanceof IButtonComponent) + { + getGui().removeButtonListener((IButtonComponent)comp); + } + if(comp instanceof IKeyComponent) + { + getGui().removeKeyListener((IKeyComponent)comp); + } + } + children.clear(); + popupChildren.clear(); + box.clearChildren(); + if(binding != null) + { + owner.removeKeyListener(binding); + } + notifyListeners(LISTENER_CLOSED); + clearFlag(FLAG_CLOSING); + } + + public final float getZOffset() + { + return zLevel; + } + + public final float calculateZLevel() + { + return totalZ; + } + + public final float getVisibility() + { + return visiblity; + } + + public final float getTotalVisibility() + { + return totalVisibility; + } + + public final float getBrightness() + { + return brightness; + } + + public final boolean hasConstraints() + { + return constraints != null; + } + + public final boolean isEnabled() + { + return isFlagSet(FLAG_ENABLED); + } + + public final boolean isVisible() + { + return isFlagSet(FLAG_VISIBLE); + } + + public final boolean usesRenderOrder() + { + return isFlagSet(FLAG_RENDER_ORDER); + } + + public final boolean isManualRender() + { + return isFlagSet(FLAG_MANUAL_RENDER); + } + + public boolean isTestingScissors() + { + return isFlagSet(FLAG_TEST_SCISSORS); + } + + public final boolean isParentVisible() + { + return isVisible() && (parent == null || parent.isVisible()); + } + + public final void setMassChanging() + { + setFlag(FLAG_MASS_CHANGE); + } + + public final T setMassChanging(Class clz) + { + setFlag(FLAG_MASS_CHANGE); + return (T)this; + } + + public final GuiComponent finishMassChanging() + { + return finishMassChanging(false); + } + + public final GuiComponent finishMassChanging(boolean quiet) + { + if(isFlagSet(FLAG_MASS_CHANGE)) + { + clearFlag(FLAG_MASS_CHANGE); + if(changed && !quiet) + { + onComponentChanged(massRepaint); + } + } + return this; + } + + public final GuiComponent setEnabled(boolean value) + { + if(!setFlag(FLAG_ENABLED, value)) return this; + for(GuiComponent comp : children) + { + comp.setEnabled(value); + } + return this; + } + + public final GuiComponent setVisible(boolean value) + { + if(!setFlag(FLAG_VISIBLE, value)) return this; + for(GuiComponent comp : children) + { + comp.setVisible(value); + } + return this; + } + + public final GuiComponent setManualRenderer(boolean value) + { + setFlag(FLAG_MANUAL_RENDER, value); + return this; + } + + public GuiComponent setIgnoreBounds(boolean value) + { + setFlag(FLAG_IGNORE_PARENT_BOUNDS, value); + return this; + } + + public GuiComponent setScissorsTest(boolean value) + { + setFlag(FLAG_TEST_SCISSORS, value); + return this; + } + + public final GuiComponent setScale(float value) + { + if(getBox().getBaseScale() != value) + { + getBox().setScale(value); + onComponentChanged(true); + } + return this; + } + + public final GuiComponent setZOffset(float value) + { + zLevel = value; + return this; + } + + public final GuiComponent setVisibilty(float value) + { + if(visiblity == value) return this; + visiblity = value; + updateVisibility(); + return this; + } + + public final GuiComponent setBrightness(float value) + { + brightness = value; + return this; + } + + public final GuiComponent changeVisibility(float value) + { + return value == 1F ? this : setVisibilty(visiblity * value); + } + + protected void updateVisibility() + { + totalVisibility = (parent != null ? parent.totalVisibility : 1F) * visiblity; + for(GuiComponent comp : children) + { + comp.updateVisibility(); + } + } + + public GuiComponent setUserKey(int keyBind){return setUserKey(keyBind, ModType.IGNORE, false);} + public GuiComponent setUserKey(int keyBind, boolean block){return setUserKey(keyBind, ModType.IGNORE, block);} + public GuiComponent setUserKey(int keyBind, int mod){return setUserKey(keyBind, mod, false);} + public GuiComponent setUserKey(int keyBind, int mod, boolean block) + { + if(isFlagNotSet(FLAG_SUPPORT_BINDING)) return this; + if(owner != null) + { + if(binding != null) + { + owner.removeKeyListener(binding); + tooltips.removeTooltip(binding.getTooltip()); + } + binding = new KeyBindAction(keyBind, mod, block); + owner.addKeyListener(binding); + addBindingTooltip(); + return this; + } + binding = new KeyBindAction(keyBind, mod, block); + addBindingTooltip(); + return this; + } + + private void addBindingTooltip() + { + tooltips.addComponent(binding.getTooltip(), new TextComponent(0F, 0F, 200F, 0F, "Key: "+ModType.getMods(binding.mod)+BindingType.KEYBOARD.getName(binding.key)).setLimitedHeight(false).setAlignment(Align.LEFT_TOP, Align.LEFT_TOP).setScale(0.5F)); + } + + protected boolean onUserKey() + { + return false; + } + + public T setRelativeTo(T component) + { + return setRelativeTo(component, Align.CENTER, Align.CENTER); + } + + public T setRelativeTo(T component, Align horizontal, Align vertical) + { + return component.setComponentPosition(box.getMinX() + horizontal.align(box.getWidth(), component.getBox().getWidth()), box.getMinY() + vertical.align(box.getHeight(), component.getBox().getHeight())).cast(); + } + + public T centerComponent(T component) + { + return getGui().centerComponent(component); + } + + public T addChild(T comp) + { + return addChild(comp, null); + } + + public T addChild(T comp, Constrain xPos, Constrain yPos, Constrain width, Constrain height) + { + return addChild(comp, new ComponentConstrains(xPos, yPos, width, height)); + } + + public T addChild(T comp, ComponentConstrains constrains) + { + comp.constraints = constrains; + comp.parent = this; + children.add(comp); + box.addChild(comp.getBox()); + if(constrains != null) + { + constrains.setOwner(comp, this); + constrains.onComponentChanged(); + } + if(owner != null) + { + comp.setOwner(owner); + if(comp instanceof IButtonComponent) + { + owner.addButtonListener((IButtonComponent)comp); + } + if(comp instanceof IKeyComponent) + { + owner.addKeyListener((IKeyComponent)comp); + } + } + return comp; + } + + public T addPopup(T popup) + { + popupChildren.add(popup.addCloseListener(popupChildren::remove)); + getGui().addComponent(popup); + return popup; + } + + protected void addConstrains(GuiComponent comp, ComponentConstrains constrains) + { + comp.constraints = constrains; + if(constrains != null) + { + constrains.setOwner(comp, this); + constrains.onComponentChanged(); + } + } + + public final UUID getTooltipId() + { + return tooltipId; + } + + public Tooltips getTooltips() + { + return tooltips; + } + + public GuiComponent addTooltip(String s, float width) + { + return addTooltip(s, width, 0F); + } + + public GuiComponent addTooltip(String s, float width, float height) + { + return addTooltip(s, width, height, 0.5F); + } + + public GuiComponent addTooltip(String s, float width, float height, float scale) + { + tooltips.addComponent(new TextComponent(0F, 0F, width, height, s).setLimitedHeight(height != 0F).setAlignment(Align.LEFT_TOP, Align.LEFT_TOP).setScale(scale)); + return this; + } + + public GuiComponent addTooltip(GuiComponent comp) + { + tooltips.addComponent(comp); + return this; + } + + public GuiComponent addTooltip(UUID id, GuiComponent comp) + { + tooltips.addComponent(id, comp); + return this; + } + + public boolean containsTooltip(UUID id) + { + return tooltips.contains(id); + } + + public boolean isTooltip() + { + return tooltipId != null; + } + + public GuiComponent removeTooltip(GuiComponent comp) + { + return removeTooltip(comp.getTooltipId()); + } + + public GuiComponent removeTooltip(UUID id) + { + return tooltips.removeTooltip(id); + } + + public List getChildren() + { + return new ObjectArrayList(children); + } + + public GuiComponent getParent() + { + return parent; + } + + public GuiComponent removeChild(GuiComponent comp) + { + comp.onClosed(); + children.remove(comp); + box.removeChild(comp.getBox()); + if(comp instanceof IButtonComponent) + { + owner.removeButtonListener((IButtonComponent)comp); + } + if(comp instanceof IKeyComponent) + { + owner.removeKeyListener((IKeyComponent)comp); + } + return this; + } + + public GuiComponent removeChildren() + { + for(GuiComponent comp : children) + { + comp.onClosed(); + if(comp instanceof IButtonComponent) + { + owner.removeButtonListener((IButtonComponent)comp); + } + if(comp instanceof IKeyComponent) + { + owner.removeKeyListener((IKeyComponent)comp); + } + } + children.clear(); + box.clearChildren(); + return this; + } + + public IGuiBox addBox(IGuiBox box) + { + this.box.addChild(box); + return box; + } + + public GuiComponent removeBox(IGuiBox box) + { + this.box.removeChild(box); + return this; + } + + public final GuiComponent addUserActionListener(Consumer listener) + { + return addListener(listener, GuiComponent.LISTENER_USER_ACTION); + } + + public final GuiComponent addUserActionListener(Runnable listener) + { + return addListener(listener, GuiComponent.LISTENER_USER_ACTION); + } + + public final GuiComponent addChangeListener(Consumer listener) + { + return addListener(listener, GuiComponent.LISTENER_ON_CHANGE); + } + + public final GuiComponent addChangeListener(Runnable listener) + { + return addListener(listener, GuiComponent.LISTENER_ON_CHANGE); + } + + public final GuiComponent addCloseListener(Consumer listener) + { + return addListener(listener, GuiComponent.LISTENER_CLOSED); + } + + public final GuiComponent addCloseListener(Runnable listener) + { + return addListener(listener, GuiComponent.LISTENER_CLOSED); + } + + public final GuiComponent addListener(Runnable runnable, int index) + { + listeners[index].add(T -> runnable.run()); + return this; + } + + public final GuiComponent addListener(Consumer listener, int index) + { + listeners[index].add(listener); + return this; + } + + protected final void notifyListeners(int index) + { + if(listeners[index].size() > 0) + { + for(Consumer comp : listeners[index]) + { + comp.accept(this); + } + } + } + + public final GuiComponent removeUserActionListener(Consumer listener) + { + return removeListener(listener, GuiComponent.LISTENER_USER_ACTION); + } + + public final GuiComponent removeChangeListener(Consumer listener) + { + return removeListener(listener, GuiComponent.LISTENER_ON_CHANGE); + } + + public final GuiComponent removeCloseListener(Consumer listener) + { + return removeListener(listener, GuiComponent.LISTENER_CLOSED); + } + + public final GuiComponent removeListener(Consumer listener, int index) + { + listeners[index].remove(listener); + return this; + } + + public GuiComponent moveComponent(float x, float y) + { + if(x == 0F && y == 0F || constraints != null) return this; + box.move(x, y); + onComponentChanged(false); + return this; + } + + public GuiComponent setComponentPosition(float x, float y) + { + if(box.getBaseX() == x && box.getBaseY() == y || constraints != null) return this; + box.setXY(x, y); + onComponentChanged(false); + return this; + } + + public GuiComponent resizeComponent(float moveX, float moveY) + { + if(moveX == 0F && moveY == 0F || constraints != null) return this; + box.grow(moveX, moveY); + onComponentChanged(true); + return this; + } + + public GuiComponent setComponentBounds(float width, float height) + { + if(box.getBaseWidth() == width && box.getBaseHeight() == height || constraints != null) return this; + box.setBounds(width, height); + onComponentChanged(true); + return this; + } + + public final void onComponentChanged(boolean repaint) + { + if(owner == null) return; + if(isFlagSet(FLAG_MASS_CHANGE)) + { + changed = true; + massRepaint |= repaint; + return; + } + massRepaint = false; + changed = false; + if(constraints != null) + { + constraints.onComponentChanged(); + if(animation != null) animation.applyValues(false); + } + box.onChanged(); + totalZ = 0F; + GuiComponent zComp = this; + while(zComp != null) + { + totalZ += 0.01F + zComp.getZOffset(); + zComp = zComp.parent; + } + notifyListeners(LISTENER_ON_CHANGE); + updateState(); + if(repaint) repaint(); + if(children.isEmpty()) return; + for(GuiComponent comp : children) + { + comp.onComponentChanged(repaint); + } + } + + protected void updateState() + { + + } + + protected void repaint() + { + + } + + public final void onFixedUpdate() + { + if(fixedUpdateSelf()) fixedUpdateChildren(); + } + + public final void onUpdate(int mouseX, int mouseY, float particalTicks) + { + if(animation != null) animation.update(particalTicks); + if(updateSelf(mouseX, mouseY, particalTicks)) updateChildren(mouseX, mouseY, particalTicks); + } + + protected void onPreRender() + { + + } + + public final void onRender(int mouseX, int mouseY, float particalTicks) + { + onPreRender(); + getRenderer().setVisibility(totalVisibility).setBrightness(brightness); + if(renderSelf(mouseX, mouseY, particalTicks)) renderChildren(mouseX, mouseY, particalTicks); + onPostRender(); + getRenderer().resetEffects(); + if(getGui() instanceof GuiScreenBase) + { + ((GuiScreenBase)getGui()).drawBox(this); + } + } + + protected void onPostRender() + { + + } + + protected boolean fixedUpdateSelf() + { + return true; + } + + protected boolean updateSelf(int mouseX, int mouseY, float particalTicks) + { + return true; + } + + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + return true; + } + + public void collectTooltips(int mouseX, int mouseY, float particalTicks, Map collector) + { + if(isParentVisible()) + { + if(isHovered(mouseX, mouseY)) + { + tooltips.merge(collector); + } + if(children.size() > 0) + { + for(GuiComponent entry : children) + { + entry.collectTooltips(mouseX, mouseY, particalTicks, collector); + } + } + } + } + + public void fixedUpdateChildren() + { + for(GuiComponent entry : children) + { + if(entry.isVisible()) + { + entry.onFixedUpdate(); + } + } + } + + public void updateChildren(int mouseX, int mouseY, float particalTicks) + { + for(GuiComponent entry : children) + { + if(entry.isVisible()) + { + entry.onUpdate(mouseX, mouseY, particalTicks); + } + } + } + + public void renderChildren(int mouseX, int mouseY, float particalTicks) + { + for(GuiComponent entry : children) + { + if(!entry.isManualRender() && entry.isVisible() && (!isTestingScissors() || isInScissors(entry.getBox()))) + { + float zOffset = entry.getZOffset() + 0.01F; + getRenderer().push(); + getRenderer().translate(0F, 0F, zOffset); + entry.onPreRender(); + entry.onRender(mouseX, mouseY, particalTicks); + entry.onPostRender(); + getRenderer().translate(0F, 0F, -zOffset); + getRenderer().pop(); + } + } + } + + public Animator getAnimator() + { + if(animation == null) + { + animation = new Animator(this); + } + return animation; + } + + public T cast() + { + return (T)this; + } + + public T cast(Class clz) + { + return (T)this; + } + + public T tryCast(Class clz) + { + return clz.isInstance(this) ? (T)this : null; + } + + public GuiComponent getTopComponent() + { + GuiComponent top = this; + while(top.parent != null) + { + top = top.parent; + } + return top; + } + + protected void requestFocus() + { + getGui().requestComponentFocus(this); + } + + protected boolean isFocused() + { + return getGui().isComponentFocused(this); + } + + protected boolean isFocusedOrChilds() + { + return isFocused() || isChildFocused(); + } + + protected boolean isChildFocused() + { + for(GuiComponent comp : children) + { + if(comp.isFocusedOrChilds()) + { + return true; + } + } + return false; + } + + protected UIRenderer getRenderer() + { + return owner.getRenderer(); + } + + protected Window getWindow() + { + return owner.getWindow(); + } + + protected boolean isSelect(int keyCode) + { + return keyCode == GLFW.GLFW_KEY_A && Keyboard.isCtrlDown() && !Keyboard.isShiftDown() && !Keyboard.isAltDown(); + } + + protected boolean isCopy(int keyCode) + { + return keyCode == GLFW.GLFW_KEY_C && Keyboard.isCtrlDown() && !Keyboard.isShiftDown() && !Keyboard.isAltDown(); + } + + protected boolean isPaste(int keyCode) + { + return keyCode == GLFW.GLFW_KEY_V && Keyboard.isCtrlDown() && !Keyboard.isShiftDown() && !Keyboard.isAltDown(); + } + + protected boolean isCut(int keyCode) + { + return keyCode == GLFW.GLFW_KEY_X && Keyboard.isCtrlDown() && !Keyboard.isShiftDown() && !Keyboard.isAltDown(); + } + + protected final float getBrightness(int mouseX, int mouseY) + { + return isEnabled() ? (isHovered(mouseX, mouseY) ? 0.7F : 1F) : 0.5F; + } + + public final float getActiveBrightness() + { + return isEnabled() ? 1F : 0.5F; + } + + public long getGlobalClock() + { + return getGui().getGlobalClock(); + } + + protected final void bindCursor(AssetLocation location) + { + Cursor.INSTANCE.bindCursor(location, getWindow()); + } + + protected final void clearCursor() + { + Cursor.INSTANCE.clearCursor(getWindow()); + } + + protected final void enableScissors(Plane box) + { + enableScissors(box.getMinX(), box.getMinY(), box.getWidth(), box.getHeight()); + } + + protected final void enableScissors(IGuiBox box) + { + enableScissors((int)box.getMinX(), (int)box.getMinY(), (int)box.getWidth(), (int)box.getHeight()); + } + + protected final void enableScissorsBox(float minX, float minY, float maxX, float maxY) + { + enableScissors((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY)); + } + + protected final void enableScissors(float x, float y, float width, float height) + { + enableScissors((int)x, (int)y, (int)width, (int)height); + } + + protected final void enableScissors(int x, int y, int width, int height) + { + getRenderer().flush(); + int bottom = y + height; + Window window = owner.getWindow(); + Vec2d vec = owner.getUIManager().res.getScaleVec(); + GLUtils.TESTER.enableScissors((int)(x * vec.getX()), (int)(window.getHeight() - bottom * vec.getY()), (int)(width * vec.getX()), (int)(height * vec.getY())); + } + + protected final boolean isInScissors(Plane box) + { + return isInScissors(box.getMinX(), box.getMinY(), box.getWidth(), box.getHeight()); + } + + protected final boolean isInScissors(IGuiBox box) + { + return isInScissors((int)box.getMinX(), (int)box.getMinY(), (int)box.getWidth(), (int)box.getHeight()); + } + + protected final boolean isInScissors(float minX, float minY, float maxX, float maxY) + { + return isInScissors((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY)); + } + + protected final boolean isInScissors(int x, int y, int width, int height) + { + int bottom = y + height; + Window window = owner.getWindow(); + Vec2d vec = owner.getUIManager().res.getScaleVec(); + return GLUtils.TESTER.isInScissors((int)(x * vec.getX()), (int)(window.getHeight() - bottom * vec.getY()), (int)(width * vec.getX()), (int)(height * vec.getY())); + } + + public final void disableScissors() + { + getRenderer().flush(); + GLUtils.TESTER.disableScissors(); + } + + class KeyBindAction implements IKeyComponent + { + int key; + int mod; + boolean block; + UUID tooltip = UUID.randomUUID(); + + public KeyBindAction(int key, int mod, boolean block) + { + this.key = key; + this.mod = mod; + this.block = block; + } + + public UUID getTooltip() + { + return tooltip; + } + + @Override + public boolean isAcceptingInput() + { + return isAnyFlagSet(FLAG_ALWAYS_CLICKABLE | FLAG_ENABLED); + } + + @Override + public boolean isBlockingMovement() + { + return block; + } + + @Override + public boolean isPopup() + { + return isPopupButton(GuiComponent.this) || isPopupButton(getTopComponent()); + } + + @Override + public boolean hasChildPopups() + { + return hasPopups(); + } + + private boolean isPopupButton(GuiComponent comp) + { + return comp instanceof IButtonComponent ? ((IButtonComponent)comp).isPopup() : false; + } + + @Override + public boolean onKeyPressed(int key) + { + if(key == this.key && ModType.isActive(mod)) + { + return onUserKey(); + } + return false; + } + + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/GuiManager.java b/src/main/java/speiger/src/coreengine/rendering/gui/GuiManager.java new file mode 100644 index 0000000..c07bd06 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/GuiManager.java @@ -0,0 +1,275 @@ +package speiger.src.coreengine.rendering.gui; + +import org.lwjgl.opengl.GL11; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.rendering.gui.base.DebugOverlay; +import speiger.src.coreengine.rendering.gui.renderer.FontRenderer; +import speiger.src.coreengine.rendering.gui.renderer.GuiShader; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.input.events.KeyEvent.CharTypeEvent; +import speiger.src.coreengine.rendering.input.events.KeyEvent.KeyPressEvent; +import speiger.src.coreengine.rendering.input.events.MouseEvent; +import speiger.src.coreengine.rendering.input.window.IWindowListener; +import speiger.src.coreengine.rendering.input.window.ScaledResolution; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.rendering.models.UniformBuffer; +import speiger.src.coreengine.rendering.shader.ShaderTracker; +import speiger.src.coreengine.rendering.utils.Cursor; +import speiger.src.coreengine.rendering.utils.GLUtils; +import speiger.src.coreengine.utils.eventbus.EventBus; +import speiger.src.coreengine.utils.profiler.IProfiler; + +public abstract class GuiManager implements IWindowListener +{ + protected GuiBase[] activeGuis = new GuiBase[2]; + protected GuiBase gui; + protected DebugOverlay debug; + protected UIRenderer renderer = new UIRenderer(this); + protected Window window; + protected ScaledResolution res; + protected long globalClock = 0L; + protected boolean isReloading = false; + protected FontRenderer font = FontLoader.createFont(AssetLocation.of("font"), "Roboto-Font"); + protected GuiShader shader = ShaderTracker.INSTANCE.register(GuiShader::create, T -> shader = T); + + public GuiManager(Window window, EventBus bus) + { + this.window = window; + bus.register(MouseEvent.class, this::onMouseEvent); + bus.register(KeyPressEvent.class, (T) -> T.setCanceled(onKeyPressed(T.key))); + bus.register(CharTypeEvent.class, (T) -> T.setCanceled(onCharTyped(T.character, T.codePoint))); + window.addListener(this, false); + debug = createOverlay(); + activeGuis[1] = debug; + res = window.getUIFrame(); + debug.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight()); + debug.onInit(); + } + + public abstract IProfiler getGPUProfiler(); + public abstract IProfiler getCPUProfiler(); + public abstract IProfiler getServerProfiler(); + public abstract UniformBuffer getOrthoMatrixBuffer(); + public abstract DebugOverlay createOverlay(); + + public void showGui(GuiBase base, boolean allowBack) + { + if(allowBack) + { + base.setLastGui(gui); + } + closeGui(); + gui = base; + if(gui != null) + { + gui.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight()); + gui.onInit(); + if(activeGuis[0] != null) + { + activeGuis[1] = activeGuis[0]; + } + activeGuis[0] = base; + } + if(activeGuis[0] != null) + { + activeGuis[0].focus = true; + } + if(activeGuis[1] != null) + { + activeGuis[1].focus = false; + } + } + + public void closeGui() + { + if(gui != null) + { + if(gui == activeGuis[0]) + { + activeGuis[0] = null; + } + if(gui == activeGuis[1]) + { + activeGuis[1] = null; + } + gui.onClosed(); + gui = null; + } + } + + @Override + public void onWindowChanged(Window window) + { + if(gui != null) + { + gui.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight()); + gui.onScreenChanged(); + } + debug.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight()); + debug.onScreenChanged(); + } + + public void onFixedUpdate() + { + globalClock++; + for(int i = 0;i < 2;i++) + { + if(activeGuis[i] != null) + { + activeGuis[i].onFixedUpdate(); + } + } + } + + public void render(float particalTicks) + { + Vec2i offsetMouse = start(); + if(debug.isUpdating() || isReloading) + { + isReloading = false; + debug.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight()); + debug.onScreenChanged(); + if(gui != null) + { + gui.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight()); + gui.onScreenChanged(); + } + } + if(activeGuis[0] != null) + { + activeGuis[0].baseLayer = activeGuis[1] != null ? activeGuis[1].layers : 0; + } + for(int i = 1;i >= 0;i--) + { + if(activeGuis[i] != null && (activeGuis[i] instanceof DebugOverlay || debug.isRendering())) + { + activeGuis[i].draw(offsetMouse.getX(), offsetMouse.getY(), particalTicks); + } + } + stop(); + Cursor.INSTANCE.clearCursor(window); + } + + protected Vec2i start() + { + GLUtils.DEBTH_TEST.push(false); + GLUtils.CULL_FACE.push(false); + GLUtils.BLEND.setFunction(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA).push(true); + renderer.beginFrame(); + return res.getScaledMouse(); + } + + protected void stop() + { + GLUtils.DEBTH_TEST.pop(); + GLUtils.DEBTH_TEST.push(true); + renderer.endFrame(); + GLUtils.BLEND.pop(); + GLUtils.CULL_FACE.pop(); + ShaderTracker.INSTANCE.stopShader(); + } + + public boolean onCharTyped(char character, int codePoint) + { + for(int i = 0;i < 2;i++) + { + if(activeGuis[i] != null && debug.shouldAcceptInputs(activeGuis[i]) && activeGuis[i].onKeyTyped(character, codePoint)) + { + return true; + } + } + return false; + } + + public boolean onKeyPressed(int key) + { + for(int i = 0;i < 2;i++) + { + if(activeGuis[i] != null && debug.shouldAcceptInputs(activeGuis[i]) && activeGuis[i].onKeyPressed(key)) + { + return true; + } + } + return false; + } + + public void onMouseEvent(MouseEvent event) + { + if(!onMouseEventInternal(event)) + { + return; + } + if(activeGuis[0] != null && !event.isCanceled() && debug.shouldAcceptInputs(activeGuis[0])) + { + activeGuis[0].onMouseEvent(event); + } + if(activeGuis[1] != null && !event.isCanceled() && debug.shouldAcceptInputs(activeGuis[1])) + { + activeGuis[1].onMouseEvent(event); + if(event.isCanceled()) + { + GuiBase base = activeGuis[0]; + activeGuis[0] = activeGuis[1]; + activeGuis[1] = base; + if(base != null) + { + base.onFocusLost(); + base.baseLayer = 0; + base.layers = 0; + } + if(activeGuis[0] != null) + { + activeGuis[0].focus = true; + } + if(activeGuis[1] != null) + { + activeGuis[1].focus = false; + } + } + } + event.convertToOrigin(); + } + + protected boolean onMouseEventInternal(MouseEvent event) + { + event.convertToScreenCoords(res); + return true; + } + + public boolean isAllowingMovement() + { + return gui == null || gui.isAllowingMovement(); + } + + public DebugOverlay getDebug() + { + return debug; + } + + public boolean isRenderUIBoxes() + { + return false; + } + + public GuiShader getShader() + { + return shader; + } + + public UIRenderer getRenderer() + { + return renderer; + } + + public FontRenderer getFont() + { + return font; + } + + public ScaledResolution getRes() + { + return res; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/Tooltips.java b/src/main/java/speiger/src/coreengine/rendering/gui/Tooltips.java new file mode 100644 index 0000000..8808a15 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/Tooltips.java @@ -0,0 +1,49 @@ +package speiger.src.coreengine.rendering.gui; + +import java.util.Map; +import java.util.UUID; + +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; + +public class Tooltips +{ + Map components = new Object2ObjectLinkedOpenHashMap<>(); + + public T addComponent(T comp) + { + return addComponent(UUID.randomUUID(), comp); + } + + public T addComponent(UUID id, T comp) + { + if(comp.tooltipId != null) throw new IllegalStateException("A Tooltip can not be added to another container"); + components.put(id, comp); + comp.tooltipId = id; + return comp; + } + + public boolean contains(UUID id) + { + return components.containsKey(id); + } + + public GuiComponent removeTooltip(UUID id) + { + GuiComponent comp = components.remove(id); + if(comp != null) comp.tooltipId = null; + return comp; + } + + public void merge(Map collector) + { + collector.putAll(components); + } + + public void clear() + { + for(GuiComponent comp : components.values()) + { + comp.tooltipId = null; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/UITextures.java b/src/main/java/speiger/src/coreengine/rendering/gui/UITextures.java new file mode 100644 index 0000000..b73b9af --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/UITextures.java @@ -0,0 +1,19 @@ +package speiger.src.coreengine.rendering.gui; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.rendering.textures.ITexture; +import speiger.src.coreengine.rendering.textures.SimpleTexture; + +public class UITextures +{ + public static final AssetLocation TEXTURE_LOCATION = AssetLocation.of("textures"); + +// public static final ITexture OK_SYMBOL = new SimpleTexture(sub("okSymbol.png")); +// public static final ITexture CANCLE_SYMBOL = new SimpleTexture(sub("cancelSymbol.png")); + public static final ITexture COLOR_WHEEL = new SimpleTexture(sub("colorWheel.png")); + + public static AssetLocation sub(String name) + { + return TEXTURE_LOCATION.subAsset(name); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/base/DebugOverlay.java b/src/main/java/speiger/src/coreengine/rendering/gui/base/DebugOverlay.java new file mode 100644 index 0000000..1a410c7 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/base/DebugOverlay.java @@ -0,0 +1,19 @@ +package speiger.src.coreengine.rendering.gui.base; + +import speiger.src.coreengine.rendering.gui.GuiBase; + +public abstract class DebugOverlay extends GuiScreenBase +{ + public abstract boolean toggleFPS(); + public abstract void toggleUI(); + public abstract void toggleUpdate(); + public abstract void toggleDebug(); + public abstract boolean isUpdating(); + public abstract boolean isRendering(); + + public boolean shouldAcceptInputs(GuiBase base) + { + return base instanceof DebugOverlay || isRendering(); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/base/GuiScreenBase.java b/src/main/java/speiger/src/coreengine/rendering/gui/base/GuiScreenBase.java new file mode 100644 index 0000000..20928b5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/base/GuiScreenBase.java @@ -0,0 +1,511 @@ +package speiger.src.coreengine.rendering.gui.base; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.function.IntToLongFunction; + +import speiger.src.collections.ints.maps.impl.hash.Int2ObjectLinkedOpenHashMap; +import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap; +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap; +import speiger.src.collections.ints.maps.interfaces.Int2ObjectSortedMap; +import speiger.src.collections.ints.sets.IntSet; +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2BooleanLinkedOpenHashMap; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2BooleanSortedMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet; +import speiger.src.collections.objects.sets.ObjectSortedSet; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiBase; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.components.TooltipPanel; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.utils.collections.iterators.IterableWrapper; + +public class GuiScreenBase extends GuiBase +{ + Int2ObjectMap getters = new Int2ObjectOpenHashMap<>(); + Set components = new ObjectLinkedOpenHashSet<>(); + Set keyOrder = new ObjectLinkedOpenHashSet<>(); + ObjectSortedSet renderOrder = new ObjectLinkedOpenHashSet<>(); + Object2BooleanSortedMap buttonOrder = new Object2BooleanLinkedOpenHashMap<>(); + Int2ObjectSortedMap selectedButtons = new Int2ObjectLinkedOpenHashMap<>(); + Set draggingButtons = new ObjectLinkedOpenHashSet<>(); + TooltipPanel tooltips = new TooltipPanel(); + int lastMouseX = -1; + int lastMouseY = -1; + long lastTooltipCheck = 0; + boolean drawsTooltip = false; + long delay = 200L; + + @Override + public void onInit() + { + super.onInit(); + addComponent(tooltips); + } + + public void setDelay(long delay, TimeUnit unit) + { + this.delay = Math.max(0, unit.toMillis(delay)); + } + + @Override + public T addComponent(T comp, ComponentConstrains contrains) + { + components.add(comp); + renderOrder.addAndMoveToLast(comp); + addConstrains(comp, contrains); + comp.setOwner(this); + if(comp instanceof IButtonComponent) + { + buttonOrder.put((IButtonComponent)comp, true); + addButtonListener((IButtonComponent)comp); + } + if(comp instanceof IKeyComponent) + { + addKeyListener((IKeyComponent)comp); + } + return comp; + } + + @Override + public T addComponent(int id, T comp, ComponentConstrains contrains) + { + getters.put(id, comp); + return addComponent(comp, contrains); + } + + @Override + public void addButtonListener(IButtonComponent listener) + { + buttonOrder.putAndMoveToFirst(listener, false); + } + + @Override + public void addKeyListener(IKeyComponent listener) + { + keyOrder.add(listener); + } + + @Override + public GuiComponent getComponent(int id) + { + return getters.get(id); + } + + @Override + public void removeComponent(GuiComponent comp) + { + if(comp != null) + { + comp.onClosed(); + } + components.remove(comp); + renderOrder.remove(comp); + getters.values().remove(comp); + if(comp instanceof IButtonComponent) + { + removeButtonListener((IButtonComponent)comp); + } + if(comp instanceof IKeyComponent) + { + removeKeyListener((IKeyComponent)comp); + } + } + + @Override + public void removeButtonListener(IButtonComponent listener) + { + buttonOrder.rem(listener); + } + + @Override + public void removeKeyListener(IKeyComponent listener) + { + keyOrder.remove(listener); + } + + @Override + public void onClosed() + { + super.onClosed(); + for(GuiComponent entry : components) + { + entry.onClosed(); + } + getters.clear(); + renderOrder.clear(); + buttonOrder.clear(); + } + + @Override + public void onScreenChanged() + { + super.onScreenChanged(); + for(GuiComponent entry : components) + { + entry.onComponentChanged(true); + } + } + + @Override + public void onFixedUpdate() + { + for(GuiComponent entry : renderOrder) + { + if(entry.isVisible()) + { + entry.onFixedUpdate(); + } + } + } + + @Override + protected void update(int mouseX, int mouseY, float particalTicks) + { + for(GuiComponent entry : renderOrder) + { + if(entry.isVisible()) + { + entry.onUpdate(mouseX, mouseY, particalTicks); + } + } + } + + @Override + protected void render(int mouseX, int mouseY, float particalTicks) + { + layers = (1 + getBaseLayer()); + UIRenderer render = getRenderer(); + float biggestZ = 0.0F; + float extra = 1.0F; + Object2ObjectMap components = hasFocus() ? new Object2ObjectLinkedOpenHashMap<>() : Object2ObjectMaps.empty(); + for(GuiComponent base : renderOrder) + { + if(base.isVisible() && !base.isManualRender()) + { + float z = base.getZOffset(); + boolean layer = base.usesRenderOrder(); + render.translate(0.0F, 0.0F, layers + z + (layer ? extra : 0.0F)); + base.onRender(mouseX, mouseY, particalTicks); + render.resetTransform(); + biggestZ = Math.max(biggestZ, z); + if(layer) + { + extra += 1.0F; + } + } + if(hasFocus()) + { + base.collectTooltips(mouseX, mouseY, particalTicks, components); + } + } + layers += MathUtils.floor(biggestZ + extra); + if(hasFocus()) + { + if(!drawsTooltip && (lastMouseX != mouseX || lastMouseY != mouseY) || components.isEmpty()) + { + lastTooltipCheck = System.currentTimeMillis(); + lastMouseX = mouseX; + lastMouseY = mouseY; + if(components.isEmpty()) + { + tooltips.updateTooltips(components); + drawsTooltip = false; + } + } + else if(System.currentTimeMillis() - lastTooltipCheck >= delay) + { + drawsTooltip = true; + tooltips.updateTooltips(components); + tooltips.setComponentPosition(mouseX+tooltips.isOutsideScreen(mouseX, width), mouseY); + render.translate(0.0F, 0.0F, layers + 50F); + tooltips.onRender(mouseX, mouseY, particalTicks); + render.resetTransform(); + } + } + render.resetEffects(); + } + + public void drawBox(GuiComponent comp) + { + if(!getUIManager().isRenderUIBoxes()) return; + UIRenderer render = getRenderer(); + render.translate(0F, 0F, 100F); + render.drawFrame(comp.getBox(), ColorObject.RED); + render.translate(0F, 0F, -100F); + } + + @Override + public boolean isAllowingMovement() + { + for(IKeyComponent comp : findKeyPopups()) + { + if(comp.isAcceptingInput() && comp.isBlockingMovement()) + { + return false; + } + } + return true; + } + + @Override + public boolean onKeyTyped(char letter, int keyCode) + { + for(IKeyComponent comp : findKeyPopups()) + { + if(comp.isAcceptingInput() && comp.onKeyTyped(letter, keyCode)) + { + return true; + } + } + return false; + } + + @Override + public boolean onKeyPressed(int key) + { + for(IKeyComponent comp : findKeyPopups()) + { + if(comp.isAcceptingInput() && comp.onKeyPressed(key)) + { + return true; + } + } + return false; + } + + @Override + public boolean onMousePressed(int button, int mouseX, int mouseY) + { + if(buttonOrder.isEmpty()) return false; + List components = new ObjectArrayList<>(); + for(Iterator iter = findPopups();iter.hasNext();) + { + IButtonComponent base = iter.next(); + if(!base.isValidButton(button)) continue; + if(!base.isComponentColliding(mouseX, mouseY)) + { + components.add(base); + continue; + } + if(!base.onClick(button, mouseX, mouseY)) continue; + buttonOrder.getAndMoveToFirst(base); + selectedButtons.put(button, base); + draggingButtons.add(base); + GuiComponent top = getTopComponent(base); + if(base.canMoveIntoForground()) + { + if(buttonOrder.getAndMoveToFirst(base) && base instanceof GuiComponent) + { + requestComponentFocus(((GuiComponent)base).getTopComponent()); + } + else + { + requestComponentFocus(top); + } + } + else if(top instanceof IButtonComponent && ((IButtonComponent)top).canMoveIntoForground()) + { + requestComponentFocus(top); + } + for(int i = 0,m=components.size();i findPopups() + { + List popups = new ObjectArrayList(); + for(IButtonComponent button : buttonOrder.keySet()) + { + if(button.isPopup() && !button.hasChildPopups()) + { + popups.add(button); + continue; + } + GuiComponent comp = getTopComponent(button); + if(comp instanceof IButtonComponent && ((IButtonComponent)comp).isPopup() && !((IButtonComponent)comp).hasChildPopups()) + { + popups.add(button); + } + } + return sortByHeight(popups.isEmpty() ? new ObjectArrayList(buttonOrder.keySet()) : popups); + } + + protected Iterator sortByHeight(List comp) + { + comp.sort(ButtonSorter.SORTER); + return comp.iterator(); + } + + protected Set findKeyPopups() + { + Set components = new ObjectLinkedOpenHashSet(); + for(IKeyComponent key : keyOrder) + { + if(key.isPopup() && !key.hasChildPopups()) + { + components.add(key); + continue; + } + GuiComponent comp = getTopComponent(key); + if((comp instanceof IKeyComponent && ((IKeyComponent)comp).isPopup() && !((IKeyComponent)comp).hasChildPopups()) || (comp instanceof IButtonComponent && ((IButtonComponent)comp).isPopup()) && !((IButtonComponent)comp).hasChildPopups()) + { + components.add(key); + } + } + return components.isEmpty() ? keyOrder : components; + } + + protected GuiComponent getTopComponent(IKeyComponent button) + { + return button instanceof GuiComponent ? ((GuiComponent)button).getTopComponent() : null; + } + + protected GuiComponent getTopComponent(IButtonComponent button) + { + return button instanceof GuiComponent ? ((GuiComponent)button).getTopComponent() : null; + } + + @Override + public void requestComponentFocus(GuiComponent comp) + { + if(!comp.hasPopups()) + { + renderOrder.moveToLast(comp); + } + } + + @Override + public boolean hasComponentInTheWay(GuiComponent comp, int mouseX, int mouseY) + { + for(GuiComponent other : renderOrder) + { + if(other == comp) break; + if(other.usesRenderOrder() && other.isHovered(mouseX, mouseY)) return true; + } + return false; + } + + @Override + public boolean isComponentInWay(GuiComponent comp, int mouseX, int mouseY) + { + for(IButtonComponent entry : buttonOrder.keySet()) + { + if(entry != comp && entry instanceof GuiComponent && ((GuiComponent)entry).isHovered(mouseX, mouseY)) + { + return true; + } + } + return false; + } + + @Override + public boolean isComponentFocused(GuiComponent comp) + { + return hasFocus() && buttonOrder.size() > 0 && ((buttonOrder.size() == 1 && buttonOrder.containsKey(comp)) || buttonOrder.firstKey() == comp); + } + + @Override + public boolean isComponentInFront(GuiComponent comp) + { + if(!hasFocus() || renderOrder.last() == comp || (renderOrder.last() instanceof IButtonComponent && ((IButtonComponent)renderOrder.last()).isPopup())) + { + return true; + } + return false; + } + + @Override + public boolean hasComponentPressed(int mouseButton) + { + return selectedButtons.containsKey(mouseButton); + } + + static final class ButtonSorter implements Comparator + { + public static final ButtonSorter SORTER = new ButtonSorter(); + @Override + public int compare(IButtonComponent o1, IButtonComponent o2) + { + return Float.compare(o2.getComponentZ(), o1.getComponentZ()); + } + + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/base/IButtonComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/base/IButtonComponent.java new file mode 100644 index 0000000..aae28f0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/base/IButtonComponent.java @@ -0,0 +1,52 @@ +package speiger.src.coreengine.rendering.gui.base; + +import speiger.src.coreengine.rendering.gui.GuiComponent; + +public interface IButtonComponent extends IInputComponent +{ + + public default boolean isComponentColliding(int mouseX, int mouseY) + { + return ((GuiComponent)this).isHovered(mouseX, mouseY); + } + + public default float getComponentZ() + { + return this instanceof GuiComponent ? ((GuiComponent)this).calculateZLevel() : 0F; + } + + public default boolean isValidButton(int button) + { + return button == 0; + } + + public default boolean onClick(int button, int mouseX, int mouseY) + { + return true; + } + + public default boolean onDrag(int mouseX, int mouseY) + { + return false; + } + + public default void onRelease(int button, int mouseX, int mouseY) + { + + } + + public default boolean onScroll(int scroll, int mouseX, int mouseY) + { + return false; + } + + public default boolean canMoveIntoForground() + { + return false; + } + + public default void onFocusLost() + { + + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/base/IInputComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/base/IInputComponent.java new file mode 100644 index 0000000..d25081f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/base/IInputComponent.java @@ -0,0 +1,16 @@ +package speiger.src.coreengine.rendering.gui.base; + +import speiger.src.coreengine.rendering.gui.GuiComponent; + +public interface IInputComponent +{ + public default boolean isPopup() + { + return false; + } + + public default boolean hasChildPopups() + { + return this instanceof GuiComponent && ((GuiComponent)this).hasPopups(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/base/IKeyComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/base/IKeyComponent.java new file mode 100644 index 0000000..a583cf5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/base/IKeyComponent.java @@ -0,0 +1,12 @@ +package speiger.src.coreengine.rendering.gui.base; + +public interface IKeyComponent extends IInputComponent +{ + public boolean isAcceptingInput(); + + public default boolean isBlockingMovement() {return false;} + + public boolean onKeyPressed(int key); + + public default boolean onKeyTyped(char letter, int keyCode) {return false;} +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/ButtonComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/ButtonComponent.java new file mode 100644 index 0000000..985c4cd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/ButtonComponent.java @@ -0,0 +1,64 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; + +public class ButtonComponent extends GuiComponent implements IButtonComponent +{ + TextComponent text = new TextComponent(); + ColorObject color; + + public ButtonComponent(String text, ColorObject color) + { + this(0F, 0F, 0F, 0F, text, color); + } + + public ButtonComponent(float x, float y, float width, float height, String text, ColorObject color) + { + super(x, y, width, height); + this.text.setText(text); + this.color = color; + setFlag(FLAG_SUPPORT_BINDING); + } + + @Override + public void init() + { + addChild(text, Constraints.getParentConstrains()); + } + + public TextComponent getText() + { + return text; + } + + public ButtonComponent setColor(ColorObject color) + { + this.color = color; + return this; + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brigthness = getBrightness(mouseX, mouseY); + getRenderer().setBrightness(brigthness).drawQuad(getBox(), color); + text.setBrightness(brigthness); + return true; + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + notifyListeners(LISTENER_USER_ACTION); + } + + @Override + protected boolean onUserKey() + { + notifyListeners(LISTENER_USER_ACTION); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/CheckBoxComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/CheckBoxComponent.java new file mode 100644 index 0000000..2f58c7b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/CheckBoxComponent.java @@ -0,0 +1,123 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.components.misc.ICheckBox; +import speiger.src.coreengine.rendering.gui.helper.UIShapes; +import speiger.src.coreengine.rendering.gui.renderer.buffer.RenderBuffer; + +public class CheckBoxComponent extends GuiComponent implements IButtonComponent, ICheckBox +{ + boolean isChecked = false; + ColorObject color; + RenderBuffer buffer; + + public CheckBoxComponent(ColorObject color) + { + super(0F, 0F, 0F, 0F); + setFlag(FLAG_SUPPORT_BINDING); + this.color = color; + } + + public CheckBoxComponent(ColorObject color, boolean checked) + { + super(0F, 0F, 0F, 0F); + setFlag(FLAG_SUPPORT_BINDING); + this.color = color; + } + + public CheckBoxComponent(float x, float y, float width, float height, ColorObject color) + { + super(x, y, width, height); + setFlag(FLAG_SUPPORT_BINDING); + this.color = color; + } + + public CheckBoxComponent(float x, float y, float width, float height, ColorObject color, boolean checked) + { + super(x, y, width, height); + setFlag(FLAG_SUPPORT_BINDING); + this.color = color; + isChecked = checked; + } + + @Override + public void init() + { + addCloseListener(buffer = getRenderer().createBuffer()); + } + + @Override + public final CheckBoxComponent setChecked(boolean isChecked) + { + this.isChecked = isChecked; + return this; + } + + public final CheckBoxComponent setColor(ColorObject color) + { + if(this.color != color) + { + this.color = color; + onComponentChanged(true); + } + return this; + } + + @Override + public boolean isChecked() + { + return isChecked; + } + + @Override + protected void repaint() + { + float width = getBox().getWidth(); + float height = getBox().getHeight(); + buffer.clear(); + UIShapes.createCross(buffer, width, height, (width / 3.3F), (height / 5F), color); + UIShapes.createCross(buffer, width, height, (width / 5F), (height / 10F), color); + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brightness = getActiveBrightness(); + float centerX = getBox().getCenterX(); + float centerY = getBox().getCenterY(); + getRenderer().setBrightness(brightness).drawQuad(getBox(), color).setBrightness(brightness * 0.8F).drawFrame(getBox(), color).translate(centerX, centerY); + if(isChecked()) + { + getRenderer().setBrightness(brightness * 0.7F).translate(0, 0, 0.001F).drawBuffers(buffer.selectionIterator(1), getBox().getWidth(), getBox().getHeight()).translate(0, 0, -0.001F); + } + if(isTopHovered(mouseX, mouseY)) + { + getRenderer().setBrightness(brightness * 1.3F).translate(0, 0, 0.002F).drawBuffers(buffer.selectionIterator(0), getBox().getWidth(), getBox().getHeight()).translate(0, 0, -0.002F); + } + getRenderer().setBrightness(1F).translate(-centerX, -centerY); + return true; + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + return true; + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + isChecked = !isChecked; + notifyListeners(LISTENER_USER_ACTION); + } + + @Override + protected boolean onUserKey() + { + isChecked = !isChecked; + notifyListeners(LISTENER_USER_ACTION); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/EmptyComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/EmptyComponent.java new file mode 100644 index 0000000..defc200 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/EmptyComponent.java @@ -0,0 +1,23 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.rendering.gui.GuiComponent; + +public class EmptyComponent extends GuiComponent +{ + public EmptyComponent() + { + super(0F, 0F, 0F, 0F); + setFlag(FLAG_SUPPORT_BINDING); + clearFlag(FLAG_ALWAYS_CLICKABLE); + } + + @Override + public void init() {} + + @Override + protected boolean onUserKey() + { + notifyListeners(LISTENER_USER_ACTION); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/GradientSliderComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/GradientSliderComponent.java new file mode 100644 index 0000000..7a806e1 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/GradientSliderComponent.java @@ -0,0 +1,58 @@ +package speiger.src.coreengine.rendering.gui.components; + +import java.util.function.IntFunction; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.misc.Facing; + +public class GradientSliderComponent extends SliderComponent +{ + ColorObject fromColor; + ColorObject toColor; + Facing direction; + + public GradientSliderComponent(float x, float y, float width, float height, int min, int max, int value, ColorObject color, ColorObject fromColor, ColorObject toColor, Facing direction, IntFunction textBuilder) + { + super(x, y, width, height, min, max, value, color, textBuilder); + this.direction = direction; + this.fromColor = fromColor; + this.toColor = toColor; + } + + public GradientSliderComponent(int min, int max, int value, ColorObject color, ColorObject fromColor, ColorObject toColor, Facing direction, IntFunction textBuilder) + { + super(min, max, value, color, textBuilder); + this.direction = direction; + this.fromColor = fromColor; + this.toColor = toColor; + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brightness = getActiveBrightness(); + float minX = getBox().getMinX(5F); + float minY = getBox().getMinY(2F); + float maxX = getBox().getMaxX(-5F); + float maxY = getBox().getMaxY(-2F); + float scale = 0.6F * getBox().getScale(); + if(vertical) + { + float extra = (((float)(value - min) / (float)(max - min)) * (getBox().getMaxY(-3F) - getBox().getMinY(3F))) + getBox().getMinY(3F); + float left = getBox().getMinX(2F); + getRenderer().setBrightness(brightness).drawGradientQuad(minX, minY, maxX, maxY, fromColor, toColor, direction).setBrightness(brightness * 0.7F).drawFrame(minX, minY, maxX, maxY, 0.001F, color); + getRenderer().setBrightness(brightness * 0.5F).translate(left, extra, 0.002F).scale(scale).drawBuffers(buffer, maxX - minX, maxX - minX).setBrightness(brightness).unscale(scale).translate(-left, -extra, -0.002F); + } + else + { + float extra = (((float)(value - min) / (float)(max - min)) * (getBox().getMaxX(-6F) - getBox().getMinX(6F))) + getBox().getMinX(6F); + float top = getBox().getMinY(); + getRenderer().setBrightness(brightness).drawGradientQuad(minX, minY, maxX, maxY, fromColor, toColor, direction).setBrightness(brightness * 0.7F).drawFrame(minX, minY, maxX, maxY, 0.001F, color); + getRenderer().setBrightness(brightness * 0.5F).translate(extra, top, 0.002F).scale(scale).drawBuffers(buffer, maxX - minX, maxX - minX).setBrightness(brightness).unscale(scale).translate(-extra, -top, -0.002F); + } + getRenderer().setBrightness(getBrightness(mouseX, mouseY)); + renderChildren(mouseX, mouseY, particalTicks); + getRenderer().setBrightness(1F); + return false; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/IconButtonComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/IconButtonComponent.java new file mode 100644 index 0000000..8c9a2f0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/IconButtonComponent.java @@ -0,0 +1,66 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.components.icon.IIcon; + +public class IconButtonComponent extends GuiComponent implements IButtonComponent +{ + IIcon icon; + ColorObject hoverColor; + + public IconButtonComponent(ColorObject hoverColor, IIcon icon) + { + super(0F, 0F, 0F, 0F); + this.icon = icon; + this.hoverColor = hoverColor; + setFlag(FLAG_SUPPORT_BINDING); + } + + public IconButtonComponent(float x, float y, float width, float height, ColorObject hoverColor, IIcon icon) + { + super(x, y, width, height); + this.icon = icon; + this.hoverColor = hoverColor; + setFlag(FLAG_SUPPORT_BINDING); + } + + @Override + public void init() + { + + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + if(isTopHovered(mouseX, mouseY) && hoverColor.getAlpha() > 0) + { + getRenderer().drawQuad(getBox(), hoverColor); + } + getRenderer().translate(0F, 0F, 0.001F); + icon.render(getRenderer(), getBox()); + getRenderer().translate(0F, 0F, -0.001F); + return true; + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + return true; + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + notifyListeners(LISTENER_USER_ACTION); + } + + @Override + protected boolean onUserKey() + { + notifyListeners(LISTENER_USER_ACTION); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/IconComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/IconComponent.java new file mode 100644 index 0000000..7e6e143 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/IconComponent.java @@ -0,0 +1,61 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.textures.ITexture; + +public class IconComponent extends GuiComponent +{ + ITexture texture; + ColorObject override = ColorObject.WHITE; + + public IconComponent(ITexture texture) + { + super(0F, 0F, 0F, 0F); + this.texture = texture; + } + + public IconComponent(ITexture texture, ColorObject color) + { + super(0F, 0F, 0F, 0F); + this.texture = texture; + override = color; + } + + public IconComponent(float x, float y, float width, float height, ITexture texture) + { + super(x, y, width, height); + this.texture = texture; + } + + public IconComponent(float x, float y, float width, float height, ITexture texture, ColorObject color) + { + super(x, y, width, height); + this.texture = texture; + override = color; + } + + public IconComponent setTexture(ITexture texture) + { + this.texture = texture; + return this; + } + + public IconComponent setColor(ColorObject color) + { + override = color; + return this; + } + + @Override + public void init() + { + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + getRenderer().setActiveTexture(texture).drawTexturedQuad(getBox(), override, texture.getUMin(), texture.getVMin(), texture.getUMax(), texture.getVMax()); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/LabelComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/LabelComponent.java new file mode 100644 index 0000000..9125ae9 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/LabelComponent.java @@ -0,0 +1,52 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; + +public class LabelComponent extends GuiComponent +{ + TextComponent text = new TextComponent(); + ColorObject color; + + public LabelComponent(String text, ColorObject color) + { + this(0F, 0F, 0F, 0F, text, color); + } + + public LabelComponent(float x, float y, float width, float height, String text, ColorObject color) + { + super(x, y, width, height); + this.text.setText(text); + this.color = color; + } + + public TextComponent getText() + { + return text; + } + + public LabelComponent setColor(ColorObject color) + { + this.color = color; + return this; + } + + public ColorObject getColor() + { + return color; + } + + @Override + public void init() + { + addChild(text, Constraints.getParentConstrains()); + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + getRenderer().drawQuad(getBox(), color); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/ListComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/ListComponent.java new file mode 100644 index 0000000..46c01f9 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/ListComponent.java @@ -0,0 +1,680 @@ +package speiger.src.coreengine.rendering.gui.components; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Function; +import java.util.function.Predicate; + +import speiger.src.collections.ints.collections.IntCollection; +import speiger.src.collections.ints.lists.IntArrayList; +import speiger.src.collections.ints.lists.IntList; +import speiger.src.collections.ints.sets.IntLinkedOpenHashSet; +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.lists.ObjectList; +import speiger.src.collections.objects.utils.ObjectIterators; +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.components.list.IListEntry; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.input.Keyboard; + +public class ListComponent extends GuiComponent + implements Iterable, IButtonComponent +{ + public static final int SELECTION_MODE_DISABLE = 0; + public static final int SELECTION_MODE_SINGLE = 1; + public static final int SELECTION_MODE_MULTI = 2; + public static final int SELECTION_MODE_INTERACT = 3; + + public static final int UPDATE_MODE_DISABLED = 0; + public static final int UPDATE_MODE_VISIBLE = 1; + public static final int UPDATE_MODE_ALL = 2; + + public static final int FLAG_DISABLE_BACKGROUND = 1024; + public static final int FLAG_START_AT_BOTTOM = 2048; + + protected ColorObject color; + protected ColorObject hoverColor = ColorObject.LIGHT_GRAY.copy(); + protected int hoverIndex = -1; + protected int dragIndex = -1; + protected int movement = 0; + protected IButtonComponent customButton; + protected IntLinkedOpenHashSet selectedIndexes = new IntLinkedOpenHashSet(); + protected ObjectList entries = new ObjectArrayList(); + protected int selectionMode = 1; + protected int updateMode = 1; + protected float entryHeight; + protected ScrollBarComponent verticalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY); + protected ScrollBarComponent horizontalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY).setHorizontal(true); + protected Vec2i lastMouse = Vec2i.newMutable(); + + public ListComponent() + { + super(0F, 0F, 0F, 0F); + } + + public ListComponent(ColorObject color, float entryHeight) + { + super(0F, 0F, 0F, 0F); + this.color = color; + this.entryHeight = entryHeight; + } + + public ListComponent(float x, float y, float width, float height, ColorObject color, float entryHeight) + { + super(x, y, width, height); + this.color = color; + this.entryHeight = entryHeight; + } + + @Override + public void init() + { + addChild(horizontalBar, Constraints.getScrollbarConstraints(verticalBar::isInUse, true, 5F)); + addChild(verticalBar, Constraints.getScrollbarConstraints(horizontalBar::isInUse, false, 5F)); + for(int i = 0, m = entries.size();i < m;i++) + { + entries.get(i).init(this, getGui()); + } + updateScrollBar(); + } + + @Override + public void onClosed() + { + for(int i = 0,m=entries.size();i disableBackground() + { + setFlag(FLAG_DISABLE_BACKGROUND); + return this; + } + + public ListComponent setStartAtBottom(boolean value) + { + setFlag(FLAG_START_AT_BOTTOM, value); + return this; + } + + public boolean isStartAtBottom() + { + return isFlagSet(FLAG_START_AT_BOTTOM); + } + + public ListComponent setColor(ColorObject color) + { + this.color = color; + return this; + } + + public ListComponent setHoverColor(ColorObject color) + { + hoverColor = color; + return this; + } + + public ListComponent setEntryHeight(float entryHeight) + { + if(this.entryHeight != entryHeight) + { + this.entryHeight = entryHeight; + onComponentChanged(true); + } + return this; + } + + public ListComponent setSelectionMode(int mode) + { + if(mode < 0 || mode > 3) + { + throw new IllegalStateException("Unknown Mode"); + } + this.selectionMode = mode; + selectedIndexes.clear(); + return this; + } + + public ListComponent setUpdateMode(int mode) + { + if(mode < 0 || mode > 2) + { + throw new IllegalStateException("Unknown Mode"); + } + updateMode = mode; + return this; + } + + public ListComponent setSelectedIndex(int index) + { + switch(selectionMode) + { + case 0: + throw new IllegalStateException("Selection is not allowed"); + case 1: + selectedIndexes.clear(); + case 2: + if(index >= 0 && index < entries.size()) + { + if(selectionMode == SELECTION_MODE_MULTI && Keyboard.isShiftDown()) + { + for(int i = selectedIndexes.lastInt();iindex;i--) + { + selectedIndexes.add(i); + } + } + selectedIndexes.add(index); + } + break; + } + return this; + } + + public boolean isSelected(int index) + { + return selectedIndexes.contains(index); + } + + public T removeSelectedIndex(int index) + { + return selectedIndexes.remove(index) ? entries.get(index) : null; + } + + public boolean hasSelected() + { + return !selectedIndexes.isEmpty(); + } + + public int getSelectedIndex() + { + return selectedIndexes.isEmpty() ? -1 : selectedIndexes.iterator().nextInt(); + } + + public T getSelectedValue() + { + return selectedIndexes.isEmpty() ? null : entries.get(selectedIndexes.iterator().nextInt()); + } + + public void clearSelection() + { + selectedIndexes.clear(); + } + + public IntList getSelection() + { + return new IntArrayList(selectedIndexes); + } + + public List getSelectedValues() + { + List entries = new ObjectArrayList(); + for(int index : selectedIndexes) + { + entries.add(this.entries.get(index)); + } + return entries; + } + + public ListComponent addEntry(T entry) + { + entries.add(entry); + if(getGui() != null) + { + entry.init(this, getGui()); + updateScrollBar(); + } + return this; + } + + public ListComponent addEntries(Collection entries) + { + this.entries.addAll(entries); + if(getGui() != null) + { + for(T entry : entries) + { + entry.init(this, getGui()); + } + updateScrollBar(); + } + return this; + } + + public int size() + { + return entries.size(); + } + + public T get(int index) + { + return entries.get(index); + } + + public boolean removeIf(Predicate filter) + { + if(entries.isEmpty()) return false; + selectedIndexes.clear(); + return entries.removeIf(K -> { + if(filter.test(K)) { + K.onClosed(); + return true; + } + return false; + }); + } + + public List map(Function mapper) + { + List list = new ObjectArrayList<>(entries.size()); + for(int i = 0,m=entries.size();i 0) + { + entries.add(index - 1, entries.remove(index)); + if(isSelected(index)) + { + removeSelectedIndex(index); + setSelectedIndex(index-1); + } + } + } + + public void moveDown(int index) + { + if(index + 1 < entries.size()) + { + entries.add(index + 1, entries.remove(index)); + if(isSelected(index)) + { + removeSelectedIndex(index); + setSelectedIndex(index+1); + } + } + } + + public void move(int fromIndex, int toIndex) + { + entries.add(toIndex, entries.remove(fromIndex)); + } + + public int indexOf(T value) + { + return entries.indexOf(value); + } + + public T remove(int index) + { + if(index < 0 || index >= entries.size()) + { + return null; + } + T value = entries.remove(index); + if(value != null) + { + value.onClosed(); + selectedIndexes.remove(index); + } + return value; + } + + public boolean removeAll(IntCollection values) + { + List result = new ObjectArrayList(); + for(int index : values) + { + if(index < 0 || index >= entries.size()) + { + continue; + } + selectedIndexes.remove(index); + T entry = entries.get(index); + entry.onClosed(); + result.add(entry); + } + if(result.size() > 0) + { + entries.removeAll(result); + return true; + } + return false; + } + + public boolean remove(T value) + { + return remove(indexOf(value)) != null; + } + + public void clear() + { + for(int i = 0,m=entries.size();i iterator() + { + return ObjectIterators.unmodifiable(entries.iterator()); + } + + protected Iterator rangeIterator(int start, int end) + { + return new Iterator() + { + int index = start; + + @Override + public boolean hasNext() + { + return index < end && entries.size() > index; + } + + @Override + public T next() + { + return entries.get(index++); + } + }; + } + + public Iterator visibleIterator() + { + int start = getStartIndex(); + return rangeIterator(start, MathUtils.clamp(0, entries.size() - 1, start + getIndexWidth())); + } + + @Override + protected void repaint() + { + float scale = getBox().getScale(); + for(int i = 0,m=entries.size();i= entries.size() ? -1 : index; + return true; + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brightness = getActiveBrightness(); + UIRenderer render = getRenderer(); + int start = getStartIndex(); + int end = MathUtils.clamp(0, entries.size(), start + getIndexWidth()); + IGuiBox box = getBox(); + float scale = box.getScale(); + float pixelSize = scale * entryHeight; + render.setBrightness(brightness); + if(isFlagNotSet(FLAG_DISABLE_BACKGROUND)) + { + render.drawQuad(box, color); + } + render.push(); + render.translate(box.getMinX(-horizontalBar.getScroll()), box.getMinY(-verticalBar.getScroll())); + float minX = horizontalBar.getScroll() * scale; + float maxX = box.getWidth() - verticalBar.getRequiredSpace() + (horizontalBar.getScroll() * scale); + enableScissorsBox(box.getMinX(), box.getMinY(), box.getMaxX()-verticalBar.getRequiredSpace(), box.getMaxY()-horizontalBar.getRequiredSpace()); + boolean bottom = !verticalBar.isInUse() && isStartAtBottom(); + if(hoverIndex != -1 && selectionMode != SELECTION_MODE_INTERACT && hoverIndex >= start && hoverIndex < end) + { + float offset = bottom ? box.getHeight()-(end-hoverIndex) * pixelSize : hoverIndex * pixelSize; + render.drawQuad(minX, offset, maxX, offset + pixelSize, 0.01F, hoverColor); + } + if(selectedIndexes.size() > 0) + { + render.setBrightness(0.75F * brightness); + for(int index : selectedIndexes) + { + if(index >= start && index < end) + { + float offset = bottom ? box.getHeight()-(end-index) * pixelSize : index * pixelSize; + render.drawQuad(minX, offset, maxX, offset + pixelSize, 0.011F, color); + } + } + } + mouseX -= box.getMinX(); + mouseY -= box.getMinY(); + for(int i = start;i collector) + { + super.collectTooltips(mouseX, mouseY, particalTicks, collector); + int start = getStartIndex(); + int end = MathUtils.clamp(0, entries.size(), start + getIndexWidth()); + IGuiBox box = getBox(); + float scale = box.getScale(); + float pixelSize = scale * entryHeight; + mouseX -= box.getMinX(); + mouseY -= box.getMinY(); + boolean bottom = !verticalBar.isInUse() && isStartAtBottom(); + for(int i = start;i> parts; + int maxSteps; + + public PieComponent(int maxSteps, Supplier> parts) + { + super(0F, 0F, 0F, 0F); + this.maxSteps = maxSteps; + this.parts = parts; + } + + public PieComponent(float x, float y, float width, float height, int maxSteps, Supplier> parts) + { + super(x, y, width, height); + this.maxSteps = maxSteps; + this.parts = parts; + } + + @Override + public void init() + { + addCloseListener(buffer = getRenderer().createBuffer()); + } + + public final PieComponent setAutoUpdate(boolean value) + { + setFlag(FLAG_AUTO_UPDATE, value); + return this; + } + + public final boolean isAutoUpdating() + { + return isFlagSet(FLAG_AUTO_UPDATE); + } + + public int getMaxSteps() + { + return maxSteps; + } + + @Override + protected boolean fixedUpdateSelf() + { + if(isAutoUpdating()) + { + createPie(); + } + return true; + } + + @Override + protected void repaint() + { + createPie(); + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float centerX = getBox().getCenterX(); + float centerY = getBox().getCenterY(); + getRenderer().setBrightness(getActiveBrightness()).translate(centerX, centerY).drawBuffers(buffer, 0F, 0F).translate(-centerX, -centerY).setBrightness(1F); + return true; + } + + protected void createPie() + { + buffer.clear(); + if(parts == null) + { + return; + } + float xSize = getBox().getWidth() / 2F; + float ySize = getBox().getHeight() / 2F; + float extra = getBox().getHeight() / 10F; + float value = -3.17F; + float spaceScale = 0.0495F * (127.0F / maxSteps); + List indexes = parts.get(); + int stepsDone = 0; + Tesselator tes = buffer.start(GL11.GL_TRIANGLES, VertexType.UI); + for(int j = indexes.size()-1;j>=0;j--) + { + IPieIndex pieIndex = indexes.get(j); + int steps = j == 0 ? maxSteps - stepsDone : pieIndex.getSteps(); + ColorObject color = pieIndex.getColor(); + ColorObject darker = color.copy().darker(); + tes.offset(0F, 0F, 0.01F); + for(int i = 0;i name; + IntSupplier valueGenerator; + + public ProgressBarComponent(ColorObject color, int value, int max) + { + this(color, value, max, null); + } + + public ProgressBarComponent(ColorObject color, int value, int max, IntFunction name) + { + this(0F, 0F, 0F, 0F, color, value, max, name); + } + + public ProgressBarComponent(float x, float y, float width, float height, ColorObject color, int value, int max) + { + this(x, y, width, height, color, value, max, null); + } + + public ProgressBarComponent(float x, float y, float width, float height, ColorObject color, int value, int max, IntFunction name) + { + super(x, y, width, height); + this.name = name; + setColor(color); + setMax(max); + setValue(value); + } + + @Override + public void init() + { + addBox(inner); + text.setZOffset(0.1F); + text.setText(stringify()); + addChild(text, Constraints.getParentConstrains()); + } + + public TextComponent getText() + { + return text; + } + + public ProgressBarComponent setColor(ColorObject color) + { + this.color = color; + return this; + } + + public ProgressBarComponent setValueGenerator(IntSupplier valueGenerator) + { + this.valueGenerator = valueGenerator; + return this; + } + + public ProgressBarComponent setValue(int newValue) + { + if(value != newValue) + { + value = newValue; + text.setText(stringify()); + } + return this; + } + + public ProgressBarComponent setMax(int newMax) + { + if(max != newMax) + { + max = newMax; + if(value > max) setValue(max); + } + return this; + } + + protected String stringify() + { + if(name != null) return name.apply(value); + else if(max == 0) return "0%"; + return Integer.toString((int)(((float)value / (float)max) * 100F))+"%"; + } + + protected float getProgress() + { + return max == 0 ? 0F : (float)value / (float)max; + } + + @Override + protected boolean fixedUpdateSelf() + { + if(valueGenerator != null) setValue(valueGenerator.getAsInt()); + return true; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float extra = (inner.getMaxX() - inner.getMinX()) * getProgress(); + UIRenderer render = getRenderer(); + render.setBrightness(getActiveBrightness() * 0.75F).drawQuad(getBox(), color); + render.setBrightness(getActiveBrightness()).drawQuad(inner, 0.01F, color); + render.setBrightness(getActiveBrightness() * 1.25F).drawQuad(inner.getMinX(), inner.getMinY(), inner.getMinX() + extra, inner.getMaxY(), 0.02F, color); + text.setBrightness(getActiveBrightness()); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollBarComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollBarComponent.java new file mode 100644 index 0000000..3650585 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollBarComponent.java @@ -0,0 +1,244 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; + +public class ScrollBarComponent extends GuiComponent implements IButtonComponent +{ + public static final int FLAG_HORIZONTAL = 1024; + public static final int FLAG_INVERTED = 2048; + ColorObject color; + float lastMouse = -1; + int current; + int max; + + public ScrollBarComponent(ColorObject color) + { + super(0F, 0F, 0F, 0F); + this.color = color; + } + + public ScrollBarComponent(ColorObject color, int max) + { + super(0F, 0F, 0F, 0F); + this.color = color; + this.max = max; + } + + public ScrollBarComponent(ColorObject color, int max, int current) + { + super(0F, 0F, 0F, 0F); + this.color = color; + this.max = max; + this.current = current; + } + + public ScrollBarComponent(float x, float y, float width, float height, ColorObject color) + { + super(x, y, width, height); + this.color = color; + } + + public ScrollBarComponent(float x, float y, float width, float height, ColorObject color, int max) + { + super(x, y, width, height); + this.color = color; + this.max = max; + } + + public ScrollBarComponent(float x, float y, float width, float height, ColorObject color, int max, int current) + { + super(x, y, width, height); + this.color = color; + this.max = max; + this.current = current; + } + + @Override + public void init() + { + + } + + public ScrollBarComponent setColor(ColorObject color) + { + this.color = color; + return this; + } + + public ScrollBarComponent setScrollMax(int max) + { + this.max = max; + return addScroll(0); + } + + public ScrollBarComponent addScroll(int add) + { + return setScrollCurrent(current + add); + } + + public ScrollBarComponent setScrollEnd() + { + return setScrollCurrent(max); + } + + public ScrollBarComponent setScrollStart() + { + return setScrollCurrent(0); + } + + public ScrollBarComponent setScrollCurrent(int current) + { + this.current = (int)MathUtils.clamp(0F, Math.max(0, max - getBaseRange()), current); + return this; + } + + protected int getCurrent() + { + return current; + } + + protected void setCurrent(int value) + { + current = value; + } + + public ScrollBarComponent setHorizontal(boolean value) + { + setFlag(FLAG_HORIZONTAL, value); + return this; + } + + public ScrollBarComponent setInverted(boolean value) + { + setFlag(FLAG_INVERTED, value); + return this; + } + + public boolean isHorizontal() + { + return isFlagSet(FLAG_HORIZONTAL); + } + + public int getScroll() + { + return current; + } + + public boolean isAtEnd() + { + return (max - getBaseRange()) <= current; + } + + public boolean isInUse() + { + return (max - getBaseRange()) > 0F; + } + + public float getRequiredSpace() + { + return (max - getBaseRange()) > 0F ? (isHorizontal() ? getBox().getHeight() : getBox().getWidth()) : 0F; + } + + @Override + public boolean isComponentColliding(int mouseX, int mouseY) + { + return isInUse() && isHovered(mouseX, mouseY); + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float baseRange = getBaseRange(); + float extra = max - baseRange; + if(extra <= 0) + { + return true; + } + IGuiBox box = getBox(); + float range = getRange(); + float min = getCurrent() * (baseRange - range) / extra; + float brightness = getActiveBrightness(); + getRenderer().setBrightness(brightness * 0.5F).drawQuad(box, color); + if(isHorizontal()) getRenderer().setBrightness(brightness * 1F).drawQuad(box.getMinX(min), box.getMinY(), box.getMinX(min + range), box.getMaxY(), 0.01F, color); + else getRenderer().setBrightness(brightness * 1F).drawQuad(box.getMinX(), box.getMinY(min), box.getMaxX(), box.getMinY(min + range), 0.01F, color); + getRenderer().setBrightness(1F); + return true; + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + float baseRange = getBaseRange(); + float extra = max - baseRange; + if(extra <= 0F) + { + return false; + } + float range = getRange(); + float pos = (baseRange - range) / extra; + float start = Math.max(0F, getCurrent() * pos); + IGuiBox box = getBox(); + float currentPos = (isHorizontal() ? box.getMinX(start) : box.getMinY(start)) + ((range * box.getScale()) / 2F); + float mouse = isHorizontal() ? mouseX : mouseY; + if(mouse < currentPos) + { + setCurrent((int)Math.max(0F, getCurrent() - (((currentPos - mouse) / pos) / box.getScale()))); + } + else if(mouse > currentPos) + { + setCurrent((int)Math.min(extra, getCurrent() + (((mouse - currentPos) / pos) / box.getScale()))); + } + lastMouse = mouse; + return true; + } + + @Override + public boolean onDrag(int mouseX, int mouseY) + { + if(lastMouse != -1F) + { + float range = getRange(); + IGuiBox box = getBox(); + float mouse = (isHorizontal() ? mouseX - box.getMinX() : mouseY - box.getMinY()) - ((range * box.getScale()) / 2F); + float baseRange = getBaseRange(); + float extra = max - baseRange; + setCurrent((int)MathUtils.clamp(0F, Math.max(0F, extra), (mouse / ((baseRange - range) / extra)) / box.getScale())); + return true; + } + return false; + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + lastMouse = -1F; + } + + @Override + public boolean onScroll(int scroll, int mouseX, int mouseY) + { + float extra = max - getBaseRange(); + if(extra > 0F) + { + setCurrent((int)MathUtils.clamp(0, extra, current - (scroll * (extra / 10F)))); + return true; + } + return false; + } + + protected float getBaseRange() + { + return isHorizontal() ? getBox().getBaseWidth() : getBox().getBaseHeight(); + } + + protected float getRange() + { + float value = getBaseRange(); + return MathUtils.clamp(10F, value, ((value * value) / max)); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollPanelComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollPanelComponent.java new file mode 100644 index 0000000..6df3016 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollPanelComponent.java @@ -0,0 +1,96 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; + +public class ScrollPanelComponent extends GuiComponent +{ + public static final int FLAG_CORNER = 1 << 20; + + ScrollBarComponent horizontalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY).setHorizontal(true); + ScrollBarComponent verticalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY); + protected PanelComponent container = new PanelComponent().setManualRenderer(true).setScissorsTest(true).cast(); + PanelComponent viewPort = new PanelComponent().setManualRenderer(true).cast(); + + public ScrollPanelComponent() + { + super(0F, 0F, 0F, 0F); + } + + public ScrollPanelComponent(float x, float y, float width, float height) + { + super(x, y, width, height); + } + + @Override + public void init() + { + addChild(horizontalBar, Constraints.getScrollbarConstraints(isFlagSet(FLAG_CORNER) ? () -> true : verticalBar::isInUse, true, 5F)); + addChild(verticalBar, Constraints.getScrollbarConstraints(isFlagSet(FLAG_CORNER) ? () -> true : horizontalBar::isInUse, false, 5F)); + viewPort.addChild(container); + container.addChangeListener(this::recalculateSize); + addChild(viewPort, new ComponentConstrains(null, null, Constraints.createConditionalParent(verticalBar::isInUse, 0F, 5F), Constraints.createConditionalParent(horizontalBar::isInUse, 0F, 5F))); + } + + public ScrollPanelComponent setForcedCorner(boolean value) + { + if(setFlag(FLAG_CORNER, value) && getGui() != null) + { + addConstrains(horizontalBar, Constraints.getScrollbarConstraints(isFlagSet(FLAG_CORNER) ? () -> true : verticalBar::isInUse, true, 5F)); + addConstrains(verticalBar, Constraints.getScrollbarConstraints(isFlagSet(FLAG_CORNER) ? () -> true : horizontalBar::isInUse, false, 5F)); + onComponentChanged(false); + } + return this; + } + + public PanelComponent getContainer() + { + return container; + } + + public PanelComponent getViewPort() + { + return viewPort; + } + + protected void recalculateSize(GuiComponent owner) + { + float maxX = 0F; + float maxY = 0F; + for(GuiComponent component : container.getChildren()) + { + IGuiBox box = component.getBox(); + maxX = Math.max(maxX, box.getBaseX() + box.getBaseWidth()); + maxY = Math.max(maxY, box.getBaseY() + box.getBaseHeight()); + } + horizontalBar.setScrollMax((int)maxX); + verticalBar.setScrollMax((int)maxY); + owner.setComponentBounds(maxX, maxY).setComponentPosition(-horizontalBar.getScroll(), -verticalBar.getScroll()); + } + + @Override + protected boolean updateSelf(int mouseX, int mouseY, float particalTicks) + { + super.updateSelf(mouseX, mouseY, particalTicks); + container.setComponentPosition(-horizontalBar.getScroll(), -verticalBar.getScroll()); + return true; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + renderChildren(mouseX, mouseY, particalTicks); + if(container.isVisible()) + { + enableScissors(viewPort.getBox()); + getRenderer().translate(0F, 0F, 0.1F); + container.onRender(mouseX, mouseY, particalTicks); + getRenderer().translate(0F, 0F, -0.1F); + disableScissors(); + } + return false; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollWindowComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollWindowComponent.java new file mode 100644 index 0000000..0a61e5d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollWindowComponent.java @@ -0,0 +1,109 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; + +public class ScrollWindowComponent extends WindowComponent +{ + public static final int FLAG_CORNER = 1 << 26; + ScrollBarComponent horizontalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY).setHorizontal(true); + ScrollBarComponent verticalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY); + protected PanelComponent container = new PanelComponent().setManualRenderer(true).cast(); + PanelComponent viewPort = new PanelComponent().setManualRenderer(true).cast(); + + public ScrollWindowComponent(int flags, String name) + { + super(0F, 0F, 0F, 0F, flags, name); + } + + public ScrollWindowComponent(float x, float y, float width, float height, int flags, String name) + { + super(x, y, width, height, flags, name); + } + + @Override + public void init() + { + super.init(); + addChild(horizontalBar.addChangeListener(minimizedListener), Constraints.getScrollbarConstraints(isFlagSet(FLAG_CORNER) ? () -> true : verticalBar::isInUse, true, 5F)); + addChild(verticalBar.addChangeListener(minimizedListener), Constraints.getVerticalScrollbar(isFlagSet(FLAG_CORNER) ? () -> true : horizontalBar::isInUse, 7.5F, 5F)); + viewPort.addChild(container); + container.addChangeListener(this::recalculateSize).addChangeListener(minimizedListener); + addChild(viewPort.setComponentPosition(0F, 7.5F), new ComponentConstrains(null, null, Constraints.createConditionalParent(verticalBar::isInUse, 0F, 5F), Constraints.createConditionalParent(horizontalBar::isInUse, 7.5F, 12.5F))); + } + + public T addComponent(T comp) + { + return container.addChild(comp); + } + + public T addComponent(T comp, ComponentConstrains constraints) + { + return container.addChild(comp, constraints); + } + + public PanelComponent getContainer() + { + return container; + } + + public PanelComponent getViewPort() + { + return viewPort; + } + + public ScrollWindowComponent setForcedCorner(boolean value) + { + if(setFlag(FLAG_CORNER, value) && getGui() != null) + { + addConstrains(horizontalBar, Constraints.getScrollbarConstraints(isFlagSet(FLAG_CORNER) ? () -> true : verticalBar::isInUse, true, 5F)); + addConstrains(verticalBar, Constraints.getVerticalScrollbar(isFlagSet(FLAG_CORNER) ? () -> true : horizontalBar::isInUse, 7.5F, 5F)); + onComponentChanged(false); + } + return this; + } + + protected void recalculateSize(GuiComponent owner) + { + float maxX = 0F; + float maxY = 0F; + for(GuiComponent component : container.getChildren()) + { + IGuiBox box = component.getBox(); + maxX = Math.max(maxX, box.getBaseX() + box.getBaseWidth()); + maxY = Math.max(maxY, box.getBaseY() + box.getBaseHeight()); + } + horizontalBar.setScrollMax((int)maxX); + verticalBar.setScrollMax((int)maxY); + horizontalBar.onComponentChanged(true); + verticalBar.onComponentChanged(true); + owner.setComponentBounds(maxX, maxY).setComponentPosition(-horizontalBar.getScroll(), -verticalBar.getScroll()); + } + + @Override + protected boolean updateSelf(int mouseX, int mouseY, float particalTicks) + { + super.updateSelf(mouseX, mouseY, particalTicks); + container.setComponentPosition(-horizontalBar.getScroll(), -verticalBar.getScroll()); + return true; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + super.renderSelf(mouseX, mouseY, particalTicks); + renderChildren(mouseX, mouseY, particalTicks); + if(container.isVisible()) + { + enableScissors(viewPort.getBox()); + getRenderer().translate(0F, 0F, 0.1F); + container.onRender(mouseX, mouseY, particalTicks); + getRenderer().translate(0F, 0F, -0.1F); + disableScissors(); + } + return false; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/SelectionComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/SelectionComponent.java new file mode 100644 index 0000000..0c2469f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/SelectionComponent.java @@ -0,0 +1,365 @@ +package speiger.src.coreengine.rendering.gui.components; + +import java.util.Collection; +import java.util.function.Consumer; + +import org.lwjgl.opengl.GL11; + +import speiger.src.collections.ints.collections.IntCollection; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.value.IValue; +import speiger.src.coreengine.math.value.LiniarValue; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.components.list.SelectionEntry; +import speiger.src.coreengine.rendering.gui.helper.Align; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.gui.renderer.buffer.RenderBuffer; +import speiger.src.coreengine.rendering.tesselation.Tesselator; +import speiger.src.coreengine.rendering.tesselation.VertexType; + +public class SelectionComponent extends GuiComponent implements IButtonComponent, Consumer +{ + public static final int FLAG_ANIMATE = 1 << 20; + ListComponent list = new ListComponent().setComponentBounds(0F, 120F).setManualRenderer(true).setIgnoreBounds(true).cast(); + TextComponent text = new TextComponent().setAlignment(Align.LEFT_TOP, Align.CENTER).setTextScale(0.85F).setManualRenderer(true).cast(); + RenderBuffer buffer; + ColorObject color; + boolean isOpen = false; + int selectedIndex = -1; + int defaultIndex = -1; + IValue animation = null; + + public SelectionComponent(ColorObject color) + { + super(0F, 0F, 0F, 0F); + this.color = color; + text.setText("Select Index"); + list.setColor(color); + } + + public SelectionComponent(ColorObject color, Collection collection) + { + super(0F, 0F, 0F, 0F); + this.color = color; + text.setText("Select Index"); + list.setColor(color); + addEntries(collection); + } + + public SelectionComponent(float x, float y, float width, float height, ColorObject color) + { + super(x, y, width, height); + this.color = color; + text.setText("Select Index"); + list.setColor(color); + } + + public SelectionComponent(float x, float y, float width, float height, ColorObject color, Collection collection) + { + super(x, y, width, height); + this.color = color; + text.setText("Select Index"); + list.setColor(color); + addEntries(collection); + } + + @Override + public void init() + { + list.setEntryHeight(getGui().getFont().getFontHeight()).addUserActionListener(this); + addChild(text, Constraints.getParentConstrains(21F, 0F, 10.5F, 0F)); + addChild(list, new ComponentConstrains(new ParentConstrain(), new ParentConstrain().invert(), new ParentConstrain(), null)); + addCloseListener(buffer = getRenderer().createBuffer()); + createArrow(); + } + + public final SelectionComponent setAnimating(boolean value) + { + setFlag(FLAG_ANIMATE, value); + return this; + } + + public final boolean isAnimating() + { + return isFlagSet(FLAG_ANIMATE); + } + + @Override + protected void updateState() + { + list.setVisible(isOpen); + } + + public TextComponent getText() + { + return text; + } + + public ListComponent getList() + { + return list; + } + + public SelectionComponent addEntry(String s) + { + list.addEntry(new SelectionEntry(s)); + return this; + } + + public SelectionComponent addEntries(Collection collection) + { + for(String s : collection) + { + list.addEntry(new SelectionEntry(s)); + } + return this; + } + + public SelectionComponent updateEntry(int index, String newName) + { + list.get(index).setText(newName); + if(index == selectedIndex) + { + updateSelection(); + } + return this; + } + + public SelectionComponent removeEntry(String s) + { + for(int i = 0,m=list.size();i textBuilder; + protected RenderBuffer buffer; + + public SliderComponent(int min, int max, int value, ColorObject color, IntFunction textBuilder) + { + this(0F, 0F, 0F, 0F, min, max, value, color, textBuilder); + } + + public SliderComponent(float x, float y, float width, float height, int min, int max, int value, ColorObject color, IntFunction textBuilder) + { + super(x, y, width, height); + this.min = min; + this.max = max; + this.value = MathUtils.clamp(min, max, value); + this.color = color; + this.textBuilder = textBuilder; + updateText(false); + } + + @Override + public void init() + { + text.setTextScale(0.35F); + addChild(text, Constraints.getParentConstrains()); + createArrow(); + } + + protected void createArrow() + { + if(getGui() == null) + { + return; + } + if(buffer == null) + { + addCloseListener(buffer = getRenderer().createBuffer()); + } + buffer.clear(); + UIShapes.createArrow(buffer, 12F, 12F, color, vertical ? Facing.EAST : Facing.SOUTH); + } + + public TextComponent getText() + { + return text; + } + + public SliderComponent updateText(boolean notifyListeners) + { + if(textBuilder != null) + { + text.setText(textBuilder.apply(value)); + } + if(notifyListeners) + { + notifyListeners(LISTENER_USER_ACTION); + } + return this; + } + + public SliderComponent setColor(ColorObject color) + { + if(this.color != color) + { + this.color = color; + createArrow(); + } + return this; + } + + public SliderComponent setValue(int value) + { + int lastValue = this.value; + this.value = MathUtils.clamp(min, max, value); + if(lastValue != value) + { + updateText(true); + } + return this; + } + + public SliderComponent addValue(int value) + { + return setValue(this.value + value); + } + + public SliderComponent setScrollEffect(int scrollEffect) + { + this.scrollEffect = scrollEffect; + return this; + } + + public SliderComponent setVertical(boolean vertical) + { + if(this.vertical != vertical) + { + this.vertical = vertical; + createArrow(); + } + return this; + } + + public int getValue() + { + return value; + } + + public float getPercentalValue() + { + return ((float)(value - min) / (float)(max - min)); + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brightness = getActiveBrightness(); + float minX = getBox().getMinX(5F); + float minY = getBox().getMinY(2F); + float maxX = getBox().getMaxX(-5F); + float maxY = getBox().getMaxY(-2F); + float scale = 0.6F * getBox().getScale(); + if(vertical) + { + float extra = (((float)(value - min) / (float)(max - min)) * (getBox().getMaxY(-3F) - getBox().getMinY(3F))) + getBox().getMinY(3F); + float left = getBox().getMinX(2F); + getRenderer().setBrightness(brightness).drawQuad(minX, minY, maxX, maxY, color).setBrightness(brightness * 0.7F).drawFrame(minX, minY, maxX, maxY, 0.001F, color); + getRenderer().setBrightness(brightness * 0.5F).translate(left, extra, 0.002F).scale(scale).drawBuffers(buffer, maxX - minX, maxX - minX).setBrightness(brightness).unscale(scale).translate(-left, -extra, -0.002F); + } + else + { + float extra = (((float)(value - min) / (float)(max - min)) * (getBox().getMaxX(-6F) - getBox().getMinX(6F))) + getBox().getMinX(6F); + float top = getBox().getMinY(); + getRenderer().setBrightness(brightness).drawQuad(minX, minY, maxX, maxY, color).setBrightness(brightness * 0.7F).drawFrame(minX, minY, maxX, maxY, 0.001F, color); + getRenderer().setBrightness(brightness * 0.5F).translate(extra, top, 0.002F).scale(scale).drawBuffers(buffer, maxX - minX, maxX - minX).setBrightness(brightness).unscale(scale).translate(-extra, -top, -0.002F); + } + getRenderer().setBrightness(getBrightness(mouseX, mouseY)); + renderChildren(mouseX, mouseY, particalTicks); + getRenderer().setBrightness(1F); + return false; + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + updateScroll(mouseX, mouseY); + return true; + } + + @Override + public boolean onDrag(int mouseX, int mouseY) + { + updateScroll(mouseX, mouseY); + return true; + } + + @Override + public boolean onScroll(int scroll, int mouseX, int mouseY) + { + if(scrollEffect != 0) + { + int newValue = MathUtils.clamp(min, max, value + (scroll * scrollEffect)); + if(newValue != value) + { + value = newValue; + updateText(true); + } + } + return scrollEffect != 0; + } + + public void updateScroll(int mouseX, int mouseY) + { + float pos = vertical ? (mouseY - getBox().getMinY(2F)) / getBox().getHeight(-5F) : (mouseX - getBox().getMinX(5F)) / getBox().getWidth(-11F); + int newValue = min + (int)(MathUtils.clamp(0F, 1F, pos) * (max - min)); + if(newValue != value) + { + value = newValue; + updateText(true); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/TextCheckBoxComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/TextCheckBoxComponent.java new file mode 100644 index 0000000..ad0042c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/TextCheckBoxComponent.java @@ -0,0 +1,56 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.TextConstrain; + +public class TextCheckBoxComponent extends CheckBoxComponent +{ + TextComponent text = new TextComponent().setForcedSingleLine(true).setLimitedBounds(false).setTextScale(0.35F); + + public TextCheckBoxComponent(ColorObject color, String text) + { + super(color); + this.text.setText(text); + } + + public TextCheckBoxComponent(ColorObject color, String text, boolean checked) + { + super(color, checked); + this.text.setText(text); + } + + public TextCheckBoxComponent(float x, float y, float width, float height, ColorObject color, String text) + { + super(x, y, width, height, color); + this.text.setText(text); + } + + public TextCheckBoxComponent(float x, float y, float width, float height, ColorObject color, String text, boolean checked) + { + super(x, y, width, height, color, checked); + this.text.setText(text); + } + + @Override + public void init() + { + super.init(); + addChild(text, new ComponentConstrains(new PixelConstrain(getBox().getWidth() + 1F), new ParentConstrain(), TextConstrain.width(text), new ParentConstrain())); + } + + public TextComponent getText() + { + return text; + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + super.renderSelf(mouseX, mouseY, particalTicks); + text.setBrightness(getBrightness(mouseX, mouseY)); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/TextComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/TextComponent.java new file mode 100644 index 0000000..66c53d4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/TextComponent.java @@ -0,0 +1,447 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.renderer.FontRenderer; +import speiger.src.coreengine.rendering.gui.renderer.buffer.RenderBuffer; +import speiger.src.coreengine.rendering.gui.renderer.lexer.TextMetadata; + +public class TextComponent extends GuiComponent +{ + public static final int FLAG_STRIKE_THROUGH = 1 << 20; + public static final int FLAG_UNDERLINE = 1 << 21; + public static final int FLAG_BOLD = 1 << 22; + public static final int FLAG_FORMATTING = 1 << 23; + public static final int FLAG_FLIPPED = 1 << 24; + public static final int FLAG_LIMITED_WIDTH = 1 << 25; + public static final int FLAG_LIMITED_HEIGHT = 1 << 26; + public static final int FLAG_AUTO_SCALE = 1 << 27; + public static final int FLAG_SINGLE_LINE = 1 << 28; + + FontRenderer font; + String text = ""; + Align vertical = Align.CENTER; + Align horizontal = Align.CENTER; + ColorObject textColor = ColorObject.WHITE.copy(); + ColorObject backGroundColor = null; + float italic = 0F; + float textScale = 1F; + RenderBuffer buffer; + float initialSize; + TextMetadata metadata = new TextMetadata(this); + + public TextComponent() + { + super(0F, 0F, 0F, 0F); + setFlag(FLAG_FORMATTING | FLAG_LIMITED_WIDTH | FLAG_LIMITED_HEIGHT); + } + + public TextComponent(String text) + { + this(0F, 0F, 0F, 0F, text); + } + + public TextComponent(float x, float y, float width, float height) + { + super(x, y, width, height); + setFlag(FLAG_FORMATTING | FLAG_LIMITED_WIDTH | FLAG_LIMITED_HEIGHT); + } + + public TextComponent(float x, float y, float width, float height, String text) + { + super(x, y, width, height); + setFlag(FLAG_FORMATTING | FLAG_LIMITED_WIDTH | FLAG_LIMITED_HEIGHT); + setText(text); + } + + @Override + public void init() + { + addCloseListener(buffer = getRenderer().createBuffer()); + if(font == null) + { + setFont(getGui().getFont()); + } + initialSize = getBox().getWidth(); + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + if(text.isEmpty()) + { + return true; + } + float minX = getBox().getMinX(); + float minY = getBox().getMinY(); + getRenderer().translate(minX, minY).setActiveTexture(font.getTexture()).drawBuffers(buffer, getBox().getWidth(), getBox().getHeight()).translate(-minX, -minY); + return true; + } + + @Override + public void calculateActualBounds(float[] area, boolean start) + { + if(start) + { + area[0] = Float.MAX_VALUE; + area[1] = Float.MAX_VALUE; + area[2] = Float.MIN_VALUE; + area[3] = Float.MIN_VALUE; + } + IGuiBox box = getBox(); + area[0] = Math.min(area[0], box.getMinX() - metadata.getStartX()); + area[1] = Math.min(area[1], box.getMinY() - metadata.getStartY()); + area[2] = Math.max(area[2], (box.getMinX() - metadata.getStartX()) + metadata.getMaxWidth()); + area[3] = Math.max(area[3], (box.getMinY() - metadata.getStartY()) + metadata.getMaxHeight()); + for(GuiComponent comp : getChildren()) + { + if(comp.isVisible()) comp.calculateActualBounds(area, false); + } + } + + public RenderBuffer getBuffer() + { + return buffer; + } + + @Override + protected void repaint() + { + metadata.clear(); + if(font == null) + { + return; + } + font.updateText(this); + } + + public TextComponent setTextScale(float textScale) + { + if(this.textScale != textScale) + { + this.textScale = textScale; + onComponentChanged(true); + } + return this; + } + + public TextMetadata getMetadata() + { + return metadata; + } + + public final float getTextScale() + { + return textScale * getBox().getScale() * (isAutoScaling() ? getBox().getWidth() / initialSize : 1F); + } + + public final float getRawTextScale() + { + return textScale * (isAutoScaling() ? getBox().getWidth() / initialSize : 1F); + } + + public final float getWidth() + { + return getBox().getWidth() / getTextScale(); + } + + public final float getItalic() + { + return italic; + } + + public final boolean isBold() + { + return isFlagSet(FLAG_BOLD); + } + + public final boolean isStrikeThrough() + { + return isFlagSet(FLAG_STRIKE_THROUGH); + } + + public final boolean isUnderlined() + { + return isFlagSet(FLAG_UNDERLINE); + } + + public final boolean isFlipped() + { + return isFlagSet(FLAG_FLIPPED); + } + + public final boolean isRenderingSpecial() + { + return isFlagSet(FLAG_FORMATTING); + } + + public final boolean isWidthLimited() + { + return isFlagSet(FLAG_LIMITED_WIDTH); + } + + public final boolean isHeightLimited() + { + return isFlagSet(FLAG_LIMITED_HEIGHT); + } + + public final boolean isLimitedText() + { + return isFlagSet(FLAG_LIMITED_WIDTH | FLAG_LIMITED_HEIGHT); + } + + public final boolean isAutoScaling() + { + return isFlagSet(FLAG_AUTO_SCALE); + } + + public final boolean isForcedSingleLine() + { + return isFlagSet(FLAG_SINGLE_LINE); + } + + public final ColorObject getTextColor() + { + return textColor; + } + + public final ColorObject getBackgroundColor() + { + return backGroundColor; + } + + public FontRenderer getFont() + { + return font; + } + + public String getText() + { + return text; + } + + public String getText(int min) + { + return text.substring(min); + } + + public String getText(int min, int max) + { + return text.substring(min, max); + } + + public int length() + { + return text.length(); + } + + public Align getVertical() + { + return vertical; + } + + public Align getHorizontal() + { + return horizontal; + } + + public final TextComponent setBold(boolean value) + { + if(setFlag(FLAG_BOLD, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setStrikeThrough(boolean value) + { + if(!setFlag(FLAG_STRIKE_THROUGH, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setUnderlined(boolean value) + { + if(!setFlag(FLAG_UNDERLINE, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setFlipped(boolean value) + { + if(!setFlag(FLAG_FLIPPED, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setSpecialRendering(boolean value) + { + if(!setFlag(FLAG_FORMATTING, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setLimitedBounds(boolean value) + { + if(!setFlag(FLAG_LIMITED_HEIGHT | FLAG_LIMITED_WIDTH, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setLimitedWidth(boolean value) + { + if(!setFlag(FLAG_LIMITED_WIDTH, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setLimitedHeight(boolean value) + { + if(!setFlag(FLAG_LIMITED_HEIGHT, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setAutoScaling(boolean value) + { + if(!setFlag(FLAG_AUTO_SCALE, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setForcedSingleLine(boolean value) + { + if(!setFlag(FLAG_SINGLE_LINE, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setItalic(boolean value) + { + return setItalic(value ? 3F : 0F); + } + + public final TextComponent setItalic(float value) + { + if(italic == value) + { + return this; + } + italic = value; + onComponentChanged(true); + return this; + } + + public TextComponent setAlignment(Align horizontal, Align vertical) + { + if(this.horizontal == horizontal && this.vertical == vertical) + { + return this; + } + this.horizontal = horizontal; + this.vertical = vertical; + onComponentChanged(true); + return this; + } + + public TextComponent setHorizontalAlignment(Align horizontal) + { + if(this.horizontal == horizontal) + { + return this; + } + this.horizontal = horizontal; + onComponentChanged(true); + return this; + } + + public TextComponent setVerticalAlignment(Align vertical) + { + if(this.vertical == vertical) + { + return this; + } + this.vertical = vertical; + onComponentChanged(true); + return this; + } + + public TextComponent setText(String text) + { + if(text == null) + { + text = "null"; + } + if(this.text.equals(text)) + { + return this; + } + this.text = text; + onComponentChanged(true); + return this; + } + + public TextComponent setTextColor(ColorObject color) + { + if(textColor == color) + { + return this; + } + textColor = color; + onComponentChanged(true); + return this; + } + + public TextComponent setBackgroundColor(ColorObject color) + { + if(backGroundColor == color) + { + return this; + } + backGroundColor = color; + onComponentChanged(true); + return this; + } + + public TextComponent setFont(FontRenderer font) + { + if(this.font == font) + { + return this; + } + this.font = font; + onComponentChanged(true); + return this; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/TextFieldComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/TextFieldComponent.java new file mode 100644 index 0000000..9d48d2e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/TextFieldComponent.java @@ -0,0 +1,723 @@ +package speiger.src.coreengine.rendering.gui.components; + +import java.util.function.Predicate; + +import org.lwjgl.glfw.GLFW; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.base.IKeyComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.box.ParentBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; +import speiger.src.coreengine.rendering.gui.helper.constrains.DynamicConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.gui.renderer.lexer.TextMetadata; +import speiger.src.coreengine.rendering.gui.renderer.lexer.Word; +import speiger.src.coreengine.rendering.gui.renderer.lexer.WordType; +import speiger.src.coreengine.rendering.input.Keyboard; +import speiger.src.coreengine.rendering.utils.Cursor; +import speiger.src.coreengine.utils.functions.Functions; + +public class TextFieldComponent extends GuiComponent + implements IButtonComponent, IKeyComponent +{ + public static final int FLAG_FOCUS = 1 << 20; + public static final int FLAG_CAN_LOSE_FOCUS = 1 << 21; + public static final int FLAG_DOUBLE_CLICK = 1 << 22; + public static final int FLAG_INFINITE_WIDTH = 1 << 23; + public static final int FLAG_AUTO_VALIDATE = 1 << 24; + + TextComponent text = new TextComponent().setHorizontalAlignment(Align.LEFT_TOP).setForcedSingleLine(true).setSpecialRendering(false).cast(); + ColorObject color; + int curserPosition = 0; + int selectionPosition = 0; + long lastClickTime = 0; + int maxTextLength = 32; + Predicate validator = Functions.getAlwaysTrue(); + int lineOffset = 0; + boolean direction = false; + int largestPos = 0; + IGuiBox viewPort = new ParentBox(1F); + + public TextFieldComponent(ColorObject color) + { + super(0F, 0F, 0F, 0F); + setFlag(FLAG_CAN_LOSE_FOCUS); + this.color = color; + } + + public TextFieldComponent(float x, float y, float width, float height, ColorObject color) + { + super(x, y, width, height); + setFlag(FLAG_CAN_LOSE_FOCUS); + this.color = color; + } + + @Override + public void init() + { + addBox(viewPort); + addChild(text, createConstraints()); + if(text.getText().length() > 0 && isFlagNotSet(FLAG_INFINITE_WIDTH)) + { + String s = text.getText(); + float scale = text.getTextScale(); + float width = text.getBox().getWidth(); + while(text.getFont().getTextLength(s) * scale > width && s.length() > 0) + { + s = s.substring(0, s.length() - 1); + } + text.setText(s); + } + } + + private ComponentConstrains createConstraints() + { + return isFlagNotSet(FLAG_INFINITE_WIDTH) ? Constraints.getParentConstrains(1F) : new ComponentConstrains(new DynamicConstrain(this::getOffset), new ParentConstrain(1F), new ParentConstrain(1F), new ParentConstrain(1F)); + } + + public TextFieldComponent setValidator(Predicate validator) + { + this.validator = validator; + return this; + } + + public TextFieldComponent setMaxTextLength(int charLimit) + { + maxTextLength = charLimit; + return this; + } + + public TextFieldComponent setCanLoseFocus(boolean value) + { + setFlag(FLAG_CAN_LOSE_FOCUS, value); + return this; + } + + public TextFieldComponent setFocused(boolean value) + { + setFlag(FLAG_FOCUS, value); + return this; + } + + public TextFieldComponent setAutoUpdating(boolean value) + { + setFlag(FLAG_AUTO_VALIDATE, value); + return this; + } + + public TextFieldComponent setColor(ColorObject color) + { + this.color = color; + return this; + } + + public final TextFieldComponent setInfiniteText(boolean value) + { + if(setFlag(FLAG_INFINITE_WIDTH, value)) + { + text.setLimitedBounds(!value); + if(getGui() != null) + { + addConstrains(text, createConstraints()); + } + } + return this; + } + + public TextComponent getRawText() + { + return text; + } + + public String getText() + { + return text.getText(); + } + + public String getSelectedText() + { + return text.getText(Math.min(getCurserPosition(), getSelectionPosition()), Math.max(getCurserPosition(), getSelectionPosition())); + } + + public TextFieldComponent setText(String s) + { + if(s == null) + { + return this; + } + if(validator.test(s)) + { + if(s.length() > maxTextLength) + { + s = s.substring(0, maxTextLength); + } + if(isFlagNotSet(FLAG_INFINITE_WIDTH) && getGui() != null) + { + float scale = text.getTextScale(); + float width = text.getBox().getWidth(); + while(text.getFont().getTextLength(s) * scale > width) + { + s = s.substring(0, s.length() - 1); + } + } + text.setText(s); + } + return this; + } + + @Override + protected boolean updateSelf(int mouseX, int mouseY, float particalTicks) + { + if(text.isTopHovered(mouseX, mouseY)) + { + bindCursor(Cursor.CURSOR_IBEAM); + } + return true; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brightness = getActiveBrightness(); + UIRenderer render = getRenderer(); + render.setBrightness(brightness * 0.7F).drawQuad(getBox(), color).setBrightness(brightness); + IGuiBox box = text.getBox(); + render.drawQuad(viewPort, 0.001F, color); + if(isFlagSet(FLAG_INFINITE_WIDTH)) + { + enableScissorsBox(getBox().getMinX(1F), getBox().getMinY(1F), getBox().getMaxX(-1F), getBox().getMaxY(-1F)); + } + if(isFlagSet(FLAG_FOCUS) && (getGlobalClock() / 15) % 2L == 0) + { + TextMetadata data = text.getMetadata(); + if(hasSelectedText()) + { + float extra = text.getHorizontal().align(box.getWidth(), data.getMaxWidth()); + render.drawQuad(box.getMinX() + extra + data.getWidth(Math.min(getCurserPosition(), getSelectionPosition())), box.getMinY(), box.getMinX() + extra + data.getWidth(Math.max(getCurserPosition(), getSelectionPosition())), box.getMaxY(), 0.02F, text.getTextColor()); + } + else + { + float width = data.getWidth(curserPosition) + text.getHorizontal().align(box.getWidth(), data.getMaxWidth()); + render.drawQuad(box.getMinX() + width, box.getMinY(), box.getMinX() + width + text.getTextScale(), box.getMaxY(), 0.02F, text.getTextColor()); + } + } + if(isFlagSet(FLAG_INFINITE_WIDTH)) + { + renderChildren(mouseX, mouseY, particalTicks); + disableScissors(); + return false; + } + return true; + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + setFocused(true); + if(hasSelectedText() && isFlagNotSet(FLAG_DOUBLE_CLICK)) + { + setSelectionPosition(-1); + setCurserPosition(getMousePosition(mouseX)); + return true; + } + int pos = getMousePosition(mouseX); + if(pos == getCurserPosition() || isFlagSet(FLAG_DOUBLE_CLICK)) + { + if(System.currentTimeMillis() - lastClickTime < 500L) + { + if(isFlagSet(FLAG_DOUBLE_CLICK)) + { + clearFlag(FLAG_DOUBLE_CLICK); + setSelectionPosition(0); + setCurserPosition(text.length()); + } + else + { + setFlag(FLAG_DOUBLE_CLICK); + handleDoubleClick(pos); + } + } + else + { + clearFlag(FLAG_DOUBLE_CLICK); + } + lastClickTime = System.currentTimeMillis(); + return true; + } + clearFlag(FLAG_DOUBLE_CLICK); + setCurserPosition(pos); + lastClickTime = System.currentTimeMillis(); + return true; + } + + @Override + public boolean onDrag(int mouseX, int mouseY) + { + if(!hasSelectedText()) + { + setSelectionPosition(getCurserPosition()); + } + setCurserPosition(getMousePosition(mouseX)); + return true; + } + + @Override + public boolean isAcceptingInput() + { + return isFlagSet(FLAG_FOCUS); + } + + @Override + public boolean isBlockingMovement() + { + return true; + } + + @Override + public boolean onKeyPressed(int key) + { + if(isFlagNotSet(FLAG_FOCUS)) + { + return false; + } + if(key == GLFW.GLFW_KEY_ENTER) + { + notifyListeners(LISTENER_USER_ACTION); + if(isFlagSet(FLAG_CAN_LOSE_FOCUS)) + { + setFocused(false); + return true; + } + } + if(key == GLFW.GLFW_KEY_BACKSPACE) + { + if(Keyboard.isCtrlDown()) + { + Word word = text.getMetadata().getWord(getCurserPosition()); + if(word == null) + { + return true; + } + if(getCurserPosition() == word.getStartIndex()) + { + if(word.getPrev() != null) + { + deleteAtCurser(word.getPrev().getStartIndex() - word.getStartIndex()); + } + } + else + { + setCurserPosition(word.getStartIndex()); + deleteAtCurser(word.getEndIndex() - word.getStartIndex()); + } + return true; + } + else if(deleteAtCurser(-1)) + { + return true; + } + } + else if(key == GLFW.GLFW_KEY_DELETE) + { + if(Keyboard.isCtrlDown()) + { + Word word = text.getMetadata().getWord(getCurserPosition()); + if(word == null) + { + return true; + } + if(getCurserPosition() == word.getEndIndex()) + { + if(word.getNext() != null) + { + deleteAtCurser(word.getNext().getEndIndex() - word.getEndIndex()); + } + } + else + { + setCurserPosition(word.getStartIndex()); + deleteAtCurser(word.getEndIndex() - word.getStartIndex()); + } + return true; + } + else if(deleteAtCurser(1)) + { + return true; + } + } + else if(key == GLFW.GLFW_KEY_LEFT) + { + if(getCurserPosition() >= 0) + { + if(Keyboard.isShiftDown() && getSelectionPosition() == -1) + { + setSelectionPosition(getCurserPosition()); + } + else if(!Keyboard.isShiftDown() && getSelectionPosition() != -1) + { + setSelectionPosition(-1); + } + if(Keyboard.isCtrlDown()) + { + Word word = text.getMetadata().getWord(getCurserPosition()); + if(word.getStartIndex() == getCurserPosition()) + { + if(word.getPrev() != null) + { + setCurserPosition(word.getPrev().getStartIndex()); + } + } + else + { + setCurserPosition(word.getStartIndex()); + } + } + else + { + setCurserPosition(getCurserPosition() - 1); + } + return true; + } + } + else if(key == GLFW.GLFW_KEY_RIGHT) + { + if(getCurserPosition() < text.length()) + { + if(Keyboard.isShiftDown() && getSelectionPosition() == -1) + { + setSelectionPosition(getCurserPosition()); + } + else if(!Keyboard.isShiftDown() && getSelectionPosition() != -1) + { + setSelectionPosition(-1); + } + if(Keyboard.isCtrlDown()) + { + Word word = text.getMetadata().getWord(getCurserPosition()); + if(word.getEndIndex() == getCurserPosition()) + { + if(word.getNext() != null) + { + setCurserPosition(word.getNext().getEndIndex()); + } + } + else + { + setCurserPosition(word.getEndIndex()); + } + } + else + { + setCurserPosition(getCurserPosition() + 1); + } + return true; + } + else + { + if(!Keyboard.isShiftDown() && hasSelectedText()) + { + setSelectionPosition(-1); + } + return true; + } + } + else if(key == GLFW.GLFW_KEY_END) + { + if(Keyboard.isShiftDown()) + { + if(!hasSelectedText()) + { + setSelectionPosition(getCurserPosition()); + } + } + else + { + setSelectionPosition(-1); + } + setCurserToEnd(); + return true; + } + else if(key == GLFW.GLFW_KEY_HOME) + { + if(Keyboard.isShiftDown()) + { + if(getSelectionPosition() == -1) + { + setSelectionPosition(getCurserPosition()); + } + } + else + { + setSelectionPosition(-1); + } + setCurserPosition(0); + return true; + } + else if(key == GLFW.GLFW_KEY_TAB) + { + writeText(Character.toString('\t')); + return true; + } + else if(isSelect(key)) + { + setSelectionPosition(0); + setCurserToEnd(); + return true; + } + else if(isCopy(key)) + { + if(hasSelectedText()) + { + getWindow().setClipboardString(getSelectedText()); + } + return true; + } + else if(isPaste(key)) + { + String text = getWindow().getClipboardString(); + if(text != null) + { + writeText(text); + } + return true; + } + else if(isCut(key)) + { + if(hasSelectedText()) + { + getWindow().setClipboardString(getSelectedText()); + writeText(""); + } + return true; + } + else if(Keyboard.isPrintableKey(key)) + { + return true; + } + return false; + } + + @Override + public boolean onKeyTyped(char letter, int keyCode) + { + return isFlagSet(FLAG_FOCUS) && text.getFont().isCharValid(letter) && writeText(Character.toString(letter)); + } + + public boolean deleteAtCurser(int amount) + { + if(text.length() > 0) + { + if(hasSelectedText()) + { + int startPos = Math.min(getCurserPosition(), getSelectionPosition()); + writeText(""); + setCurserPosition(startPos); + } + else + { + int startPos = Math.min(getCurserPosition(), getCurserPosition() + amount); + int endPos = Math.max(getCurserPosition(), getCurserPosition() + amount); + StringBuilder builder = new StringBuilder(); + if(startPos >= 0) + { + builder.append(text.getText(0, startPos)); + } + if(endPos < text.length()) + { + builder.append(text.getText(endPos)); + } + String s = builder.toString(); + if(validator.test(s)) + { + setText(s); + setCurserPosition(Math.min(startPos, text.length())); + if(isFlagSet(FLAG_AUTO_VALIDATE)) notifyListeners(LISTENER_USER_ACTION); + } + } + return true; + } + return false; + } + + public boolean writeText(String toWrite) + { + toWrite = text.getFont().clearInvalidLetters(toWrite); + StringBuilder builder = new StringBuilder(); + int startPos = hasSelectedText() ? Math.min(getSelectionPosition(), getCurserPosition()) : getCurserPosition(); + int endPos = hasSelectedText() ? Math.max(getSelectionPosition(), getCurserPosition()) : getCurserPosition(); + int room = maxTextLength - text.length() - (startPos - endPos); + if(text.length() > 0) builder.append(text.getText(0, startPos)); + int moved = 0; + if(room < toWrite.length()) + { + builder.append(toWrite.substring(0, room)); + moved = room; + } + else + { + builder.append(toWrite); + moved = toWrite.length(); + } + if(text.length() > 0 && endPos < text.length()) builder.append(text.getText(endPos)); + String s = builder.toString(); + if(validator.test(s)) + { + setText(s); + setCurserPosition(Math.min(endPos + moved, text.length())); + setSelectionPosition(-1); + if(isFlagSet(FLAG_AUTO_VALIDATE)) notifyListeners(LISTENER_USER_ACTION); + return true; + } + return false; + } + + @Override + public void onFocusLost() + { + if(isFlagSet(FLAG_FOCUS)) + { + notifyListeners(LISTENER_USER_ACTION); + } + setFocused(false); + } + + protected void handleDoubleClick(int position) + { + Word word = text.getMetadata().getWord(position); + if(word == null) + { + return; + } + if(!word.isSpecial()) + { + setSelectionPosition(word.getStartIndex()); + setCurserPosition(word.getEndIndex()); + return; + } + WordType type = word.getType(); + if(type.isStartWord()) + { + Word other = type.findEndWord(word); + if(other != null) + { + setSelectionPosition(word.getStartIndex()); + setCurserPosition(other.getEndIndex()); + } + } + else if(type.isEndWord()) + { + Word other = type.findStartWord(word); + if(other != null) + { + setSelectionPosition(other.getStartIndex()); + setCurserPosition(word.getEndIndex()); + } + } + else if(type.isDualWord()) + { + if(word.getStartIndex() == position && word.getPrev() != null) + { + Word other = type.findStartWord(word); + if(other != null) + { + setSelectionPosition(other.getStartIndex()); + setCurserPosition(word.getEndIndex()); + } + } + else + { + Word other = type.findEndWord(word); + if(other != null) + { + setSelectionPosition(word.getStartIndex()); + setCurserPosition(other.getEndIndex()); + } + } + } + else + { + setSelectionPosition(word.getStartIndex()); + setCurserPosition(word.getEndIndex()); + } + } + + protected int getMousePosition(int mouseX) + { + return text.getMetadata().getIndex(mouseX - (text.getBox().getMinX()+text.getHorizontal().align(text.getBox().getWidth(), text.getMetadata().getMaxWidth()))); + } + + public void setCurserToEnd() + { + setCurserPosition(text.length()); + } + + public void setCurserPosition(int curserPosition) + { + this.curserPosition = Math.max(0, curserPosition); + if(isFlagSet(FLAG_INFINITE_WIDTH) && text.getFont() != null) + { + int lastLine = lineOffset; + if(lineOffset > text.length()) + { + lineOffset = text.length(); + } + + String s = text.getFont().trimStringToWidth(text.getText(lineOffset), text.getWidth()); + int k = s.length() + lineOffset; + if(curserPosition > k) + { + lineOffset += curserPosition - k; + direction = true; + largestPos = curserPosition; + } + else if(curserPosition < largestPos) + { + int diff = largestPos - curserPosition; + lineOffset -= diff; + largestPos -= diff; + if(lineOffset <= 0) direction = false; + } + lineOffset = MathUtils.clamp(0, text.length(), lineOffset); + if(lastLine != lineOffset) + { + text.onComponentChanged(false); + } + } + } + + public int getCurserPosition() + { + return curserPosition; + } + + public void setSelectionPosition(int selectionPosition) + { + this.selectionPosition = selectionPosition; + } + + public int getSelectionPosition() + { + return selectionPosition; + } + + public boolean hasSelectedText() + { + return selectionPosition != -1; + } + + protected float getOffset() + { + if(direction && text.getFont() != null) + { + return -(text.getMetadata().getWidth(largestPos)) + text.getBox().getWidth() - 1F; + } + return 1F - (text.getMetadata().getWidth(lineOffset)); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/TextPanelComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/TextPanelComponent.java new file mode 100644 index 0000000..95e8a47 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/TextPanelComponent.java @@ -0,0 +1,688 @@ +package speiger.src.coreengine.rendering.gui.components; + +import java.util.function.Predicate; + +import org.lwjgl.glfw.GLFW; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.base.IKeyComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.gui.renderer.lexer.Line; +import speiger.src.coreengine.rendering.gui.renderer.lexer.TextMetadata; +import speiger.src.coreengine.rendering.gui.renderer.lexer.Word; +import speiger.src.coreengine.rendering.gui.renderer.lexer.WordType; +import speiger.src.coreengine.rendering.input.Keyboard; +import speiger.src.coreengine.rendering.utils.Cursor; +import speiger.src.coreengine.utils.functions.Functions; + +public class TextPanelComponent extends GuiComponent implements IButtonComponent, IKeyComponent +{ + public static final int FLAG_FOCUS = 1024; + public static final int FLAG_CAN_LOSE_FOCUS = 2048; + + TextComponent text = new TextComponent().setAlignment(Align.LEFT_TOP, Align.LEFT_TOP).setForcedSingleLine(true).setSpecialRendering(false).cast(); + ColorObject color; + int curserPosition = 0; + Vec2i curserPos = Vec2i.newMutable(); + int selectionPosition = 0; + Vec2i selectionPos = Vec2i.newMutable(); + long lastClickTime = 0; + int maxTextLength = 64; + int clickCount = 0; + Predicate validator = Functions.getAlwaysTrue(); + + public TextPanelComponent(ColorObject color) + { + super(0F, 0F, 0F, 0F); + this.color = color; + } + + public TextPanelComponent(float x, float y, float width, float height, ColorObject color) + { + super(x, y, width, height); + this.color = color; + } + + @Override + public void init() + { + addChild(text, Constraints.getParentConstrains(1F)); + } + + public TextPanelComponent setValidator(Predicate validator) + { + this.validator = validator; + return this; + } + + public TextPanelComponent setMaxTextLength(int charLimit) + { + maxTextLength = charLimit; + return this; + } + + public TextPanelComponent setColor(ColorObject color) + { + this.color = color; + return this; + } + + public TextComponent getRawText() + { + return text; + } + + public String getText() + { + return text.getText(); + } + + public String getSelectedText() + { + return text.getText(Math.min(getCurserPosition(), getSelectionPosition()), Math.max(getCurserPosition(), getSelectionPosition())); + } + + public TextPanelComponent setText(String s) + { + if(s == null) + { + return this; + } + if(validator.test(s)) + { + if(s.length() > maxTextLength) + { + s = s.substring(0, maxTextLength); + } + float height = text.getBox().getHeight(); + float scale = text.getTextScale(); + while(s.length() > 0 && text.getFont().getTextHeight(s) * scale > height) + { + s = s.substring(0, s.length() - 1); + } + text.setText(s); + } + return this; + } + + @Override + protected boolean updateSelf(int mouseX, int mouseY, float particalTicks) + { + if(text.isMouseOver(mouseX, mouseY) && !text.isOverChild(mouseX, mouseY)) + { + bindCursor(Cursor.CURSOR_IBEAM); + } + return true; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brightness = getActiveBrightness(); + UIRenderer render = getRenderer(); + render.setBrightness(brightness * 0.7F).drawQuad(getBox(), color).setBrightness(brightness); + IGuiBox box = text.getBox(); + render.drawQuad(box, 0.001F, color); + if(isFlagSet(FLAG_FOCUS) && (getGlobalClock() / 15) % 2L == 0) + { + TextMetadata data = text.getMetadata(); + float height = text.getFont().getFontHeight() * text.getTextScale(); + if(hasSelectedText()) + { + if(selectionPos.getY() == curserPos.getY()) + { + render.drawQuad(box.getMinX() + data.getWidth(Math.min(curserPos.getX(), selectionPos.getX()), curserPos.getY()), box.getMinY() + (height * curserPos.getY()), box.getMinX() + data.getWidth(Math.max(curserPos.getX(), selectionPos.getX()), curserPos.getY()), box.getMinY() + (height * (curserPos.getY()+1)), 0.02F, text.getTextColor()); + } + else + { + Vec2i min = selectionPos.getY() < curserPos.getY() ? selectionPos : curserPos; + Vec2i max = selectionPos.getY() > curserPos.getY() ? selectionPos : curserPos; + for(int i = min.getY(),m=max.getY();i<=m;i++) + { + if(i == min.getY()) + { + render.drawQuad(box.getMinX() + data.getWidth(min.getX()), box.getMinY() + (height * i), box.getMinX() + data.getLineWidth(i), box.getMinY() + (height * (i+1)), 0.02F, text.getTextColor()); + } + else if(i == max.getY()) + { + render.drawQuad(box.getMinX(), box.getMinY() + (height * i), box.getMinX() + data.getWidth(max.getX()), box.getMinY() + (height * (i+1)), 0.02F, text.getTextColor()); + } + else + { + render.drawQuad(box.getMinX(), box.getMinY() + (height * i), box.getMinX() + data.getLineWidth(i), box.getMinY() + (height * (i+1)), 0.02F, text.getTextColor()); + } + } + } + return true; + } + float width = data.getWidth(curserPos.getX(), curserPos.getY()); + render.drawQuad(box.getMinX()+width, box.getMinY() + (height * curserPos.getY()), box.getMinX()+width+text.getTextScale(), box.getMinY() + (height * (curserPos.getY()+1)), 0.02F, text.getTextColor()); + } + return true; + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + setFlag(FLAG_FOCUS); + if(hasSelectedText() && clickCount <= 0) + { + setSelectionPosition(-1); + setCurserPosition(getMousePosition(mouseX, mouseY)); + return true; + } + int pos = getMousePosition(mouseX, mouseY); + if(pos == getCurserPosition()) + { + if(System.currentTimeMillis() - lastClickTime < 500L) + { + if(clickCount == 1) + { + Line line = text.getMetadata().getLine(curserPos.getY()); + setSelectionPosition(line.getStart()); + setCurserPosition(line.getEnd()); + clickCount = 2; + } + else if(clickCount == 2) + { + clickCount = 3; + setSelectionPosition(0); + setCurserToEnd(); + } + else + { + clickCount = 1; + handleDoubleClick(pos); + } + } + else + { + clickCount = 0; + } + lastClickTime = System.currentTimeMillis(); + return true; + } + lastClickTime = System.currentTimeMillis(); + setCurserPosition(pos); + setSelectionPosition(-1); + clickCount = 0; + return false; + } + + @Override + public boolean onDrag(int mouseX, int mouseY) + { + if(!hasSelectedText()) + { + setSelectionPosition(getCurserPosition()); + } + setCurserPosition(getMousePosition(mouseX, mouseY)); + return true; + } + + @Override + public void onFocusLost() + { + clearFlag(FLAG_FOCUS); + } + + @Override + public boolean isAcceptingInput() + { + return isFlagSet(FLAG_FOCUS); + } + + @Override + public boolean isBlockingMovement() + { + return true; + } + + @Override + public boolean onKeyPressed(int key) + { + if(isFlagNotSet(FLAG_FOCUS)) + { + return false; + } + if(key == GLFW.GLFW_KEY_BACKSPACE) + { + if(Keyboard.isCtrlDown()) + { + Word word = text.getMetadata().getWord(getCurserPosition()); + if(word == null) + { + return true; + } + if(getCurserPosition() == word.getStartIndex()) + { + if(word.getPrev() != null) + { + deleteAtCurser(word.getPrev().getStartIndex() - word.getStartIndex()); + } + } + else + { + setCurserPosition(word.getStartIndex()); + deleteAtCurser(word.getEndIndex() - word.getStartIndex()); + } + return true; + } + else if(deleteAtCurser(-1)) + { + return true; + } + } + else if(key == GLFW.GLFW_KEY_DELETE) + { + if(Keyboard.isCtrlDown()) + { + Word word = text.getMetadata().getWord(getCurserPosition()); + if(word == null) + { + return true; + } + if(getCurserPosition() == word.getEndIndex()) + { + if(word.getNext() != null) + { + deleteAtCurser(word.getNext().getEndIndex() - word.getEndIndex()); + } + } + else + { + setCurserPosition(word.getStartIndex()); + deleteAtCurser(word.getEndIndex() - word.getStartIndex()); + } + return true; + } + else if(deleteAtCurser(1)) + { + return true; + } + } + else if(key == GLFW.GLFW_KEY_LEFT) + { + if(getCurserPosition() >= 0) + { + if(Keyboard.isShiftDown() && getSelectionPosition() == -1) + { + setSelectionPosition(getCurserPosition()); + } + else if(!Keyboard.isShiftDown() && getSelectionPosition() != -1) + { + setSelectionPosition(-1); + } + if(Keyboard.isCtrlDown()) + { + Word word = text.getMetadata().getWord(getCurserPosition()); + if(word == null) + { + return true; + } + if(word.getStartIndex() == getCurserPosition()) + { + if(word.getPrev() != null) + { + setCurserPosition(word.getPrev().getStartIndex()); + } + } + else + { + setCurserPosition(word.getStartIndex()); + } + } + else + { + setCurserPosition(getCurserPosition()-1); + } + return true; + } + } + else if(key == GLFW.GLFW_KEY_RIGHT) + { + if(getCurserPosition() < text.length()) + { + if(Keyboard.isShiftDown() && getSelectionPosition() == -1) + { + setSelectionPosition(getCurserPosition()); + } + else if(!Keyboard.isShiftDown() && getSelectionPosition() != -1) + { + setSelectionPosition(-1); + } + if(Keyboard.isCtrlDown()) + { + Word word = text.getMetadata().getWord(getCurserPosition()); + if(word == null) + { + return true; + } + if(word.getEndIndex() == getCurserPosition()) + { + if(word.getNext() != null) + { + setCurserPosition(word.getNext().getEndIndex()); + } + } + else + { + setCurserPosition(word.getEndIndex()); + } + } + else + { + setCurserPosition(getCurserPosition()+1); + } + return true; + } + else + { + if(!Keyboard.isShiftDown() && hasSelectedText()) + { + setSelectionPosition(-1); + } + return true; + } + } + else if(key == GLFW.GLFW_KEY_UP) + { + if(Keyboard.isShiftDown() && !hasSelectedText()) + { + setSelectionPosition(getCurserPosition()); + } + else if(!Keyboard.isShiftDown() && hasSelectedText()) + { + setSelectionPosition(-1); + } + setCurserPosition(text.getMetadata().moveDown(curserPos)); + } + else if(key == GLFW.GLFW_KEY_DOWN) + { + if(Keyboard.isShiftDown() && !hasSelectedText()) + { + setSelectionPosition(getCurserPosition()); + } + else if(!Keyboard.isShiftDown() && hasSelectedText()) + { + setSelectionPosition(-1); + } + setCurserPosition(text.getMetadata().moveUp(curserPos)); + } + else if(key == GLFW.GLFW_KEY_END) + { + if(Keyboard.isShiftDown()) + { + if(!hasSelectedText()) + { + setSelectionPosition(getCurserPosition()); + } + } + else + { + setSelectionPosition(-1); + } + setCurserToEnd(); + return true; + } + else if(key == GLFW.GLFW_KEY_HOME) + { + if(Keyboard.isShiftDown()) + { + if(getSelectionPosition() == -1) + { + setSelectionPosition(getCurserPosition()); + } + } + else + { + setSelectionPosition(-1); + } + setCurserPosition(0); + return true; + } + else if(key == GLFW.GLFW_KEY_TAB) + { + writeText(Character.toString('\t')); + return true; + } + else if(key == GLFW.GLFW_KEY_ENTER) + { + writeText(Character.toString('\n')); + return true; + } + else if(isSelect(key)) + { + setSelectionPosition(0); + setCurserToEnd(); + return true; + } + else if(isCopy(key)) + { + if(hasSelectedText()) + { + getWindow().setClipboardString(getSelectedText()); + } + return true; + } + else if(isPaste(key)) + { + String text = getWindow().getClipboardString(); + if(text != null) + { + writeText(text); + } + return true; + } + else if(isCut(key)) + { + if(hasSelectedText()) + { + getWindow().setClipboardString(getSelectedText()); + writeText(""); + } + return true; + } + else if(Keyboard.isPrintableKey(key)) + { + return true; + } + return false; + } + + @Override + public boolean onKeyTyped(char letter, int keyCode) + { + return isFlagSet(FLAG_FOCUS) && text.getFont().isCharValid(letter) && writeText(Character.toString(letter)); + } + + public boolean deleteAtCurser(int amount) + { + if(text.length() > 0) + { + if(hasSelectedText()) + { + int startPos = Math.min(getCurserPosition(), getSelectionPosition()); + writeText(""); + setCurserPosition(startPos); + } + else + { + int startPos = Math.min(getCurserPosition(), getCurserPosition() + amount); + int endPos = Math.max(getCurserPosition(), getCurserPosition() + amount); + StringBuilder builder = new StringBuilder(); + if(startPos >= 0) + { + builder.append(text.getText(0, startPos)); + } + if(endPos < text.length()) + { + builder.append(text.getText(endPos)); + } + String s = builder.toString(); + if (validator.test(s)) + { + setText(s); + setCurserPosition(Math.min(startPos, text.length())); + notifyListeners(LISTENER_USER_ACTION); + } + } + return true; + } + return false; + } + + public boolean writeText(String toWrite) + { + toWrite = text.getFont().clearInvalidLetters(toWrite); + StringBuilder builder = new StringBuilder(); + int startPos = hasSelectedText() ? Math.min(getSelectionPosition(), getCurserPosition()) : getCurserPosition(); + int endPos = hasSelectedText() ? Math.max(getSelectionPosition(), getCurserPosition()) : getCurserPosition(); + int room = maxTextLength - text.length() - (startPos - endPos); + if(text.length() > 0) + { + builder.append(text.getText(0, startPos)); + } + int moved = 0; + if(room < toWrite.length()) + { + builder.append(toWrite.substring(0, room)); + moved = room; + } + else + { + builder.append(toWrite); + moved = toWrite.length(); + } + if(text.length() > 0 && endPos < text.length()) + { + builder.append(text.getText(endPos)); + } + String s = builder.toString(); + if(validator.test(s)) + { + setText(s); + setCurserPosition(Math.min(endPos + moved, text.length())); + setSelectionPosition(-1); + notifyListeners(LISTENER_USER_ACTION); + return true; + } + return false; + } + + protected void handleDoubleClick(int position) + { + Word word = text.getMetadata().getWord(position); + if(word == null) + { + return; + } + if(!word.isSpecial()) + { + setSelectionPosition(word.getStartIndex()); + setCurserPosition(word.getEndIndex()); + return; + } + WordType type = word.getType(); + if(type.isStartWord()) + { + Word other = type.findEndWord(word); + if(other != null) + { + setSelectionPosition(word.getStartIndex()); + setCurserPosition(other.getEndIndex()); + } + } + else if(type.isEndWord()) + { + Word other = type.findStartWord(word); + if(other != null) + { + setSelectionPosition(other.getStartIndex()); + setCurserPosition(word.getEndIndex()); + } + } + else if(type.isDualWord()) + { + if(word.getStartIndex() == position && word.getPrev() != null) + { + Word other = type.findStartWord(word); + if(other != null) + { + setSelectionPosition(other.getStartIndex()); + setCurserPosition(word.getEndIndex()); + } + } + else + { + Word other = type.findEndWord(word); + if(other != null) + { + setSelectionPosition(word.getStartIndex()); + setCurserPosition(other.getEndIndex()); + } + } + } + else + { + setSelectionPosition(word.getStartIndex()); + setCurserPosition(word.getEndIndex()); + } + } + + public int getMousePosition(int mouseX, int mouseY) + { + return text.getMetadata().getIndex(mouseX - text.getBox().getMinX(), mouseY - text.getBox().getMinY()); + } + + public void setCurserToEnd() + { + setCurserPosition(curserPosition); + } + + public void setCurserPosition(int curserPosition) + { + if(this.curserPosition == curserPosition) + { + return; + } + this.curserPosition = curserPosition; + text.getMetadata().convert(curserPosition, curserPos); + } + + public int getCurserPosition() + { + return curserPosition; + } + + public void setSelectionPosition(int selectionPosition) + { + if(this.selectionPosition == selectionPosition) + { + return; + } + this.selectionPosition = selectionPosition; + if(selectionPosition == -1) + { + selectionPos.set(Vec2i.MINUS_ONE); + return; + } + text.getMetadata().convert(selectionPosition, selectionPos); + } + + public int getSelectionPosition() + { + return selectionPosition; + } + + public boolean hasSelectedText() + { + return selectionPosition != -1; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/TooltipPanel.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/TooltipPanel.java new file mode 100644 index 0000000..fc4b8d8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/TooltipPanel.java @@ -0,0 +1,79 @@ +package speiger.src.coreengine.rendering.gui.components; + +import java.util.List; +import java.util.UUID; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.box.ParentBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.MenuConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; + +public class TooltipPanel extends PanelComponent +{ + List helperList = new ObjectArrayList<>(); + List indexedComponents = new ObjectArrayList<>(); + Object2ObjectMap renderedTooltips = new Object2ObjectLinkedOpenHashMap<>(); + ColorObject color = ColorObject.BLACK; + ColorObject borderColor = ColorObject.DARK_GRAY; + IGuiBox box = new ParentBox(1.2F); + + @Override + public void init() + { + super.init(); + addBox(box); + } + + public void updateTooltips(Object2ObjectMap newTooltips) + { + helperList.addAll(renderedTooltips.keySet()); + helperList.removeAll(newTooltips.keySet()); + if(helperList.size() > 0) + { + for(int i = 0;i entry : Object2ObjectMaps.fastIterable(newTooltips)) + { + renderedTooltips.put(entry.getKey(), entry.getValue()); + indexedComponents.add(entry.getValue()); + addChild(entry.getValue(), new ComponentConstrains(new ParentConstrain(3.2F), new MenuConstrain<>(indexedComponents, () -> 1F), null, null)); + } + float[] data = new float[]{ + Float.MAX_VALUE, + Float.MAX_VALUE, + Float.MIN_VALUE, + Float.MIN_VALUE + }; + for(GuiComponent child : indexedComponents) + { + if(child.isVisible()) child.calculateActualBounds(data, false); + } + setComponentBounds(data[2]-data[0] + 6.4F, data[3]-data[1] + 2.5F); + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + getRenderer().drawQuad(getBox(), -0.01F, color).drawQuad(box, 0F, borderColor); + return true; + } + + public float isOutsideScreen(int mouseX, int width) + { + return mouseX + getBox().getWidth() > width ? -(getBox().getWidth() + 5F) : 10F; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/TreeComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/TreeComponent.java new file mode 100644 index 0000000..530d9f3 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/TreeComponent.java @@ -0,0 +1,676 @@ +package speiger.src.coreengine.rendering.gui.components; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.sets.ObjectOpenHashSet; +import speiger.src.collections.objects.sets.ObjectSet; +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.misc.Facing; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.components.tree.ITreeEntry; +import speiger.src.coreengine.rendering.gui.helper.UIShapes; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.gui.renderer.buffer.RenderBuffer; +import speiger.src.coreengine.rendering.input.Keyboard; + +public class TreeComponent extends GuiComponent implements IButtonComponent +{ + public static final int SELECTION_MODE_DISABLE = 0; + public static final int SELECTION_MODE_SINGLE = 1; + public static final int SELECTION_MODE_MULTI = 2; + public static final int SELECTION_MODE_INTERACT = 3; + + public static final int UPDATE_MODE_DISABLED = 0; + public static final int UPDATE_MODE_VISIBLE = 1; + public static final int UPDATE_MODE_OPEN = 2; + public static final int UPDATE_MODE_ALL = 3; + + public static final int FLAG_NO_BACKGROUND = 1 << 20; + + ScrollBarComponent verticalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY); + ScrollBarComponent horizontalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY).setHorizontal(true); + + ColorObject color; + ColorObject selectedColor; + ColorObject hoverColor = ColorObject.LIGHT_GRAY; + int hoverIndex = -1; + int dragIndex = -1; + int movement; + Vec2i lastMouse = Vec2i.newMutable(); + IButtonComponent customButton; + protected ObjectSet openNodes = new ObjectOpenHashSet(); + protected ObjectSet selectedNodes = new ObjectOpenHashSet(); + protected int selectionMode = 1; + protected int updateMode = 1; + T node; + float entryHeight; + + boolean listChange = true; + List visibleNodes = new ObjectArrayList(); + RenderBuffer buffer; + + public TreeComponent(ColorObject color, float entryHeight) + { + super(0F, 0F, 0F, 0F); + this.entryHeight = entryHeight; + this.color = color; + this.selectedColor = color; + } + + public TreeComponent(ColorObject color, float entryHeight, T entry) + { + super(0F, 0F, 0F, 0F); + this.entryHeight = entryHeight; + getNodes(entry, openNodes, false); + this.color = color; + this.selectedColor = color; + node = entry; + if(entry != null) + { + entry.calculateDebth(); + } + } + + public TreeComponent(float x, float y, float width, float height, ColorObject color, float entryHeight, T entry) + { + super(x, y, width, height); + this.entryHeight = entryHeight; + getNodes(entry, openNodes, false); + this.color = color; + node = entry; + if(entry != null) + { + entry.calculateDebth(); + } + } + + @Override + public void init() + { + addCloseListener(buffer = getRenderer().createBuffer()); + addChild(horizontalBar, Constraints.getScrollbarConstraints(verticalBar::isInUse, true, 5F)); + addChild(verticalBar, Constraints.getScrollbarConstraints(horizontalBar::isInUse, false, 5F)); + List entries = new ObjectArrayList(); + getNodes(node, entries, false); + for(int i = 0, m = entries.size();i < m;i++) + { + entries.get(i).init(this, getGui()); + } + updateScrollBar(); + createArrow(); + } + + public TreeComponent setColor(ColorObject color) + { + if(this.color != color) + { + this.color = color; + onComponentChanged(true); + } + return this; + } + + public TreeComponent setHoverColor(ColorObject color) + { + hoverColor = color; + return this; + } + + public TreeComponent setSelectionColor(ColorObject color) + { + selectedColor = color; + return this; + } + + public TreeComponent setEntryHeight(float entryHeight) + { + if(this.entryHeight != entryHeight) + { + this.entryHeight = entryHeight; + onComponentChanged(true); + } + return this; + } + + public TreeComponent setSelectionMode(int mode) + { + if(mode < 0 || mode > 3) + { + throw new IllegalStateException("Unknown Mode"); + } + this.selectionMode = mode; + selectedNodes.clear(); + return this; + } + + public TreeComponent setUpdateMode(int mode) + { + if(mode < 0 || mode > 3) + { + throw new IllegalStateException("Unknown Mode"); + } + updateMode = mode; + return this; + } + + public TreeComponent disableBackground(boolean value) + { + setFlag(FLAG_NO_BACKGROUND, value); + return this; + } + + @Override + protected void repaint() + { + float scale = getBox().getScale(); + List entries = new ObjectArrayList(); + getNodes(node, entries, false); + for(int i = 0,m=entries.size();i setTree(T entry) + { + if(node == entry) + { + return this; + } + node = entry; + if(entry != null) + { + entry.calculateDebth(); + if(getGui() != null) + { + List entries = new ObjectArrayList(); + getNodes(entry, entries, false); + for(int i = 0,m=entries.size();i entries = new ObjectArrayList(); + getNodes(node, entries, false); + for(int i = 0,m=entries.size();i 0; + } + + public T getSelectedNode() + { + return selectedNodes.isEmpty() ? null : selectedNodes.iterator().next(); + } + + public List getSelectedNodes() + { + return new ObjectArrayList(this.selectedNodes); + } + + public List getOpenNodes() + { + return new ObjectArrayList(this.visibleNodes); + } + + public List getAllNodes() + { + List list = new ObjectArrayList(); + getNodes(node, list, false); + return list; + } + + public void openAll() + { + if(node != null) + { + openNode(node, true); + } + } + + public void closeAll() + { + if(node != null) + { + closeNode(node, true); + } + } + + public void openNode(T entry, boolean childrenIncluded) + { + openNodes.add(entry); + if(childrenIncluded) + { + getNodes(entry, openNodes, false); + } + listChange = true; + updateScrollBar(); + } + + public void toggleNode(T entry, boolean childrenIncluded) + { + if(isOpen(entry)) + { + closeNode(entry, childrenIncluded); + return; + } + openNode(entry, childrenIncluded); + } + + public boolean isOpen(T entry) + { + return openNodes.contains(entry); + } + + public void closeNode(T entry, boolean childrenIncluded) + { + openNodes.remove(entry); + if(childrenIncluded) + { + Set entries = new ObjectOpenHashSet(); + getNodes(entry, entries, false); + openNodes.removeAll(entries); + } + listChange = true; + updateScrollBar(); + } + + protected void createArrow() + { + float pixelSize = (entryHeight * 0.5F); + UIShapes.createArrow(buffer, pixelSize, pixelSize, color, Facing.EAST); + UIShapes.createArrow(buffer, pixelSize, pixelSize, color, Facing.SOUTH); + } + + protected void updateScrollBar() + { + if(listChange) + { + listChange = false; + visibleNodes.clear(); + getNodes(node, visibleNodes, true); + customButton = null; + } + float width = 0F; + float pixelSize = getBox().getScale() * entryHeight; + for(int i = 0,m=visibleNodes.size();i entries = new ObjectArrayList(); + getNodes(node, entries, false); + for(int i = 0,m=entries.size();i= visibleNodes.size() ? -1 : index; + return true; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brightness = getActiveBrightness(); + UIRenderer render = getRenderer(); + int start = getStartIndex(); + int end = MathUtils.clamp(0, visibleNodes.size(), start + getIndexWidth()); + IGuiBox box = getBox(); + float scale = box.getScale(); + float minX = horizontalBar.getScroll() * scale; + float minY = verticalBar.getScroll() * scale; + float maxX = box.getWidth() - verticalBar.getRequiredSpace() + (horizontalBar.getScroll() * scale); + float pixelSize = scale * entryHeight; + float offsetSize = pixelSize * 0.8F; + if(isFlagNotSet(FLAG_NO_BACKGROUND)) + { + render.setBrightness(brightness).drawQuad(box, color); + } + render.push(); + render.translate(box.getMinX(-horizontalBar.getScroll()), box.getMinY(-verticalBar.getScroll())); + enableScissorsBox(box.getMinX(), box.getMinY(), box.getMaxX()-verticalBar.getRequiredSpace(), box.getMaxY()-horizontalBar.getRequiredSpace()); + boolean skip = false; + if(hoverIndex != -1 && hoverIndex >= start && hoverIndex < end) + { + T node = visibleNodes.get(hoverIndex); + float xOffset = (offsetSize + (node.getDebth() * offsetSize * 0.6F)) * 0.8F; + if(!node.isLeaf() && mouseX - box.getMinX() >= xOffset - (pixelSize * 0.6F) && mouseX - box.getMinX() <= xOffset) + { + skip = true; + } + if(!skip) + { + int extraX = (int)(minX - getBox().getMinX() - xOffset); + int extraY = (int)(minY - hoverIndex * pixelSize - getBox().getMinY()); + if(node instanceof IButtonComponent && ((IButtonComponent)node).isComponentColliding(mouseX + extraX, mouseY + extraY)) + { + skip = true; + } + } + float otherMax = node.getHighlightWidth() >= 0F ? xOffset + node.getHighlightWidth() : maxX; + if(mouseX - box.getMinX() >= otherMax) skip = true; + if(!skip && selectionMode != SELECTION_MODE_INTERACT) + { + float offset = pixelSize * hoverIndex; + render.drawQuad(minX, offset, Math.min(otherMax, maxX), offset + pixelSize, 0.01F, hoverColor); + } + } + mouseX -= box.getMinX(); + mouseY -= box.getMinY(); + if(selectedNodes.size() > 0) + { + render.setBrightness(0.75F * brightness); + for(int i = start;i collector) + { + super.collectTooltips(mouseX, mouseY, particalTicks, collector); + int start = getStartIndex(); + int end = MathUtils.clamp(0, visibleNodes.size(), start + getIndexWidth()); + mouseX -= getBox().getMinX(); + mouseY -= getBox().getMinY(); + float scale = getBox().getScale(); + float minX = horizontalBar.getScroll() * scale; + float minY = verticalBar.getScroll() * scale; + float pixelSize = scale * entryHeight; + float offsetSize = pixelSize * 0.8F; + for(int i = start;i= offsetX - (entryHeight * scale * 0.6F) && mouseX - getBox().getMinX() <= offsetX) + { + toggleNode(visibleNodes.get(hoverIndex), Keyboard.isShiftDown()); + return true; + } + if(visibleNodes.get(hoverIndex) instanceof IButtonComponent) + { + IButtonComponent comp = (IButtonComponent)visibleNodes.get(hoverIndex); + int extraX = (int)((horizontalBar.getScroll() * scale - getBox().getMinX()) - offsetX); + int extraY = (int)(verticalBar.getScroll() * scale - (hoverIndex * scale * entryHeight) - getBox().getMinY()); + if(comp.isComponentColliding(mouseX + extraX, mouseY + extraY) && comp.onClick(button, mouseX + extraX, mouseY + extraY)) + { + customButton = comp; + return true; + } + } + } + dragIndex = hoverIndex; + if(selectionMode == SELECTION_MODE_INTERACT) + { + dragIndex = -1; + } + lastMouse.set(mouseX, mouseY); + return true; + } + + @Override + public boolean onDrag(int mouseX, int mouseY) + { + if(horizontalBar.onDrag(mouseX, mouseY) || verticalBar.onDrag(mouseX, mouseY)) + { + return true; + } + if(customButton != null) + { + float scale = getBox().getScale(); + float offsetSize = entryHeight * scale * 0.8F; + int extraX = (int)((horizontalBar.getScroll() * scale - getBox().getMinX()) - ((offsetSize + (visibleNodes.get(hoverIndex).getDebth() * offsetSize * 0.6F)) * 0.8F)); + int extraY = (int)(verticalBar.getScroll() * scale - (hoverIndex * scale * entryHeight) - getBox().getMinY()); + return customButton.onDrag(mouseX + extraX, mouseY + extraY); + } + horizontalBar.addScroll(lastMouse.getX() - mouseX); + verticalBar.addScroll(lastMouse.getY() - mouseY); + movement += Math.abs(lastMouse.getX() - mouseX) + Math.abs(lastMouse.getY() - mouseY); + lastMouse.set(mouseX, mouseY); + return true; + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + horizontalBar.onRelease(button, mouseX, mouseY); + verticalBar.onRelease(button, mouseX, mouseY); + if(customButton != null && (hoverIndex >= 0 && hoverIndex < visibleNodes.size() && customButton == visibleNodes.get(hoverIndex))) + { + float scale = getBox().getScale(); + float offsetSize = entryHeight * scale * 0.8F; + int extraX = (int)((horizontalBar.getScroll() * scale - getBox().getMinX()) - ((offsetSize + (visibleNodes.get(hoverIndex).getDebth() * offsetSize * 0.6F)) * 0.8F)); + int extraY = (int)(verticalBar.getScroll() * scale - (hoverIndex * scale * entryHeight) - getBox().getMinY()); + customButton.onRelease(button, mouseX + extraX, mouseY + extraY); + customButton = null; + } + else if(dragIndex != -1 && dragIndex == hoverIndex && movement < 2) + { + if(isNodeSelected(visibleNodes.get(hoverIndex))) + { + removeSelectedNode(visibleNodes.get(hoverIndex)); + } + else + { + addSelectedNode(visibleNodes.get(hoverIndex)); + } + notifyListeners(LISTENER_USER_ACTION); + dragIndex = -1; + } + movement = 0; + } + + @Override + public boolean onScroll(int scroll, int mouseX, int mouseY) + { + if((horizontalBar.isComponentColliding(mouseX, mouseY) && horizontalBar.onScroll(scroll, mouseX, mouseY)) || (verticalBar.isComponentColliding(mouseX, mouseY) && verticalBar.onScroll(scroll, mouseX, mouseY))) + { + movement = 100; + return true; + } + if(hoverIndex != -1 && visibleNodes.get(hoverIndex) instanceof IButtonComponent) + { + IButtonComponent comp = (IButtonComponent)visibleNodes.get(hoverIndex); + float scale = getBox().getScale(); + float offsetSize = entryHeight * scale * 0.8F; + int extraX = (int)((horizontalBar.getScroll() * scale - getBox().getMinX()) - ((offsetSize + (visibleNodes.get(hoverIndex).getDebth() * offsetSize * 0.6F)) * 0.8F)); + int extraY = (int)(verticalBar.getScroll() - (hoverIndex * getBox().getScale() * entryHeight) - getBox().getMinY()); + if(comp.isComponentColliding(mouseX + extraX, mouseY + extraY) && comp.onScroll(scroll, mouseX + extraX, mouseY + extraY)) + { + return true; + } + } + if(verticalBar.isInUse()) + { + verticalBar.addScroll(-(int)(scroll * 5F * getBox().getScale())); + return true; + } + return false; + } + + public int getStartIndex() + { + return MathUtils.clamp(0, visibleNodes.size(), MathUtils.floor(verticalBar.getScroll() / entryHeight)); + } + + public int getIndexWidth() + { + return MathUtils.clamp(0, visibleNodes.size(), MathUtils.ceil((getBox().getBaseHeight() - (horizontalBar.getRequiredSpace() / getBox().getScale())) / entryHeight) + 1); + } + + protected void getNodes(T entry, Collection collection, boolean openOnly) + { + if(entry != null) + { + collection.add(entry); + if(!entry.isLeaf() && (!openOnly || openNodes.contains(entry))) + { + for(int i = 0,m=entry.getChildCount();i +{ + public static final Vec2f DEFAULT_MINIMUM_BOUNDS = Vec2f.newVec(75F, 7.5F); + public static final int FLAG_MINIMIZED = 1 << 20; + public static final int FLAG_RESIZEABLE = 1 << 21; + public static final int FLAG_MOVEABLE = 1 << 22; + public static final int FLAG_RESIZEABLE_HORIZONTAL = 1 << 23; + public static final int FLAG_RESIZEABLE_VERTICAL = 1 << 24; + public static final int FLAG_RESIZE_INVERT = 1 << 25; + + public static final int WINDOW_FLAG_CLOSEABLE = 1; + public static final int WINDOW_FLAG_MINIMIZEABLE = 2; + + public static final int WINDOW_FLAGS = WINDOW_FLAG_CLOSEABLE | WINDOW_FLAG_MINIMIZEABLE; + + public static final int DEFAULT_FLAGS = WINDOW_FLAGS | FLAG_RESIZEABLE | FLAG_MOVEABLE; + public static final int FIXED_SIZE_WINDOW = WINDOW_FLAGS | FLAG_MOVEABLE; + public static final int FIXED_SIZE_POPUP = WINDOW_FLAG_CLOSEABLE | FLAG_MOVEABLE; + public static final int UNCLOSEABLE_WINDOW = WINDOW_FLAG_MINIMIZEABLE | FLAG_RESIZEABLE | FLAG_MOVEABLE; + public static final int SUB_WINDOW = WINDOW_FLAG_MINIMIZEABLE | FLAG_RESIZEABLE | FLAG_RESIZE_INVERT; + + final int flags; + FacingList facing = null; + String name; + ColorObject color = ColorObject.WINDOW_DEFAULT_BACKGROUND; + Vec2f lastSize = Vec2f.newMutable(); + Vec2i lastClick = Vec2i.newMutable(); + IValue animation = null; + protected final Consumer closeListener = new ConsumerConverter(0, this); + protected final Consumer minimizedListener = new ConsumerConverter(2, this); + + public WindowComponent(float x, float y, float width, float height, int flags, String name) + { + super(x, y, width, height); + this.name = name; + this.flags = flags & WINDOW_FLAGS; + setFlag(flags &= ~(WINDOW_FLAGS)); + setFlag(FLAG_RESIZEABLE_HORIZONTAL | FLAG_RESIZEABLE_VERTICAL); + lastSize.set(getBox().getBaseWidth(), getBox().getBaseHeight()); + } + + @Override + public void init() + { + super.init(); + LabelComponent label = new LabelComponent(name, ColorObject.DARK_GRAY); + label.getText().setTextScale(0.4F).setHorizontalAlignment(Align.LEFT_TOP).setForcedSingleLine(true); + addChild(label, new ComponentConstrains(null, null, new ParentConstrain(), new PixelConstrain(7.5F))); + float offset = 9F; + if((flags & WINDOW_FLAG_CLOSEABLE) != 0) + { + addChild(new IconButtonComponent(0F, 0F, 7.5F, 7.5F, ColorObject.RED, new CrossIcon(ColorObject.WHITE).setPadding(2.5F, 2F)).addUserActionListener(new ConsumerConverter(0, this)).setZOffset(0.001F), new ComponentConstrains(new PixelConstrain(offset).setInverted(), null, null, null)); + offset += 7.5F; + } + if((flags & WINDOW_FLAG_MINIMIZEABLE) != 0) + { + addChild(new IconButtonComponent(0F, 0F, 7.5F, 7.5F, ColorObject.GRAY, new LineIcon(ColorObject.WHITE, 0.7F, 0.25F)).addUserActionListener(new ConsumerConverter(1, this)).setZOffset(0.001F), new ComponentConstrains(new PixelConstrain(offset).setInverted(), null, null, null)); + } + if(canMoveIntoForground()) + { + setFlag(FLAG_RENDER_ORDER); + } + } + + protected void updateMinizedState(boolean value) + { + if(setFlag(FLAG_MINIMIZED, value)) + { + onComponentChanged(false); + } + } + + public final WindowComponent setMinimized(boolean value) + { + if((flags & WINDOW_FLAG_MINIMIZEABLE) != 0 && setFlag(FLAG_MINIMIZED, value)) + { + if(value) + { + Vec2f last = InternalThreadPools.VEC2F.get().set(lastSize); + setComponentBounds(last.getX(), isFlagSet(FLAG_MINIMIZED) ? getMinimizedY() : last.getY()); + lastSize.set(last); + InternalThreadPools.VEC2F.accept(last.negate()); + } + else setComponentBounds(lastSize.getX(), lastSize.getY()); + onComponentChanged(false); + } + return this; + } + + public final boolean isMinimized() + { + return isFlagSet(FLAG_MINIMIZED); + } + + public Vec2f getMinimumBounds() + { + return DEFAULT_MINIMUM_BOUNDS; + } + + public float getMinimizedY() + { + return 7.5F; + } + + public WindowComponent setColor(ColorObject color) + { + this.color = color; + return this; + } + + @Override + public void calculateActualBounds(float[] area, boolean start) + { + if(animation != null) + { + float scale = getBox().getScale(); + area[0] = Math.min(area[0], getBox().getMinX()); + area[1] = Math.min(area[1], getBox().getMinY()); + area[2] = Math.max(area[2], lastSize.getX() * scale); + area[3] = Math.max(area[3], animation.get(getMinimizedY(), lastSize.getY()) * scale); + return; + } + super.calculateActualBounds(area, start); + } + + @Override + protected void updateState() + { + if(animation == null) + { + if(isFlagSet(FLAG_MINIMIZED)) + { + lastSize.setX(getBox().getBaseWidth()); + return; + } + lastSize.set(getBox().getBaseWidth(), getBox().getBaseHeight()); + } + } + + @Override + protected boolean updateSelf(int mouseX, int mouseY, float particalTicks) + { + if(animation != null) + { + animation.update(particalTicks); + if(animation.isDone()) + { + if(animation.get() < 0.1F) + { + updateMinizedState(true); + } + Vec2f last = InternalThreadPools.VEC2F.get().set(lastSize); + setComponentBounds(last.getX(), isFlagSet(FLAG_MINIMIZED) ? getMinimizedY() : last.getY()); + lastSize.set(last); + InternalThreadPools.VEC2F.accept(last.negate()); + animation = null; + } + notifyListeners(LISTENER_ON_CHANGE); + } + else if(isFlagSet(FLAG_RESIZEABLE) && !isOverChild(mouseX, mouseY) && !getGui().hasComponentInTheWay(getTopComponent(), mouseX, mouseY)) + { + FacingList list = getBox().isColiding(mouseX, mouseY) ? getBox().getColidingBorder(mouseX, mouseY, 2F) : null; + if(list != null) + { + if(isFlagNotSet(FLAG_RESIZEABLE_HORIZONTAL)) list = list.remove(FacingList.HORIZONTAL); + if(isFlagNotSet(FLAG_RESIZEABLE_VERTICAL)) list = list.remove(FacingList.VERTICAL); + bindCursor(list.containsAny(FacingList.VERTICAL) && isFlagNotSet(FLAG_MINIMIZED) ? Cursor.CURSOR_VRESIZE : (list.containsAny(FacingList.HORIZONTAL) ? Cursor.CURSOR_HRESIZE : null)); + } + } + if(isPopup() && !hasChildPopups() && !getGui().isComponentInFront(this)) + { + requestFocus(); + } + return true; + } + + @Override + protected void onPreRender() + { + if(animation != null) + { + float scale = getBox().getScale(); + enableScissors(getBox().getMinX(), getBox().getMinY(), lastSize.getX() * scale, animation.get(getMinimizedY(), lastSize.getY()) * scale); + } + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + getRenderer().drawQuad(getBox(), color); + return true; + } + + @Override + protected void onPostRender() + { + if(animation != null) + { + disableScissors(); + } + } + + @Override + public void accept(GuiComponent value, int index) + { + switch(index) + { + case 0: + getGui().removeComponent(this); + break; + case 1: + if(animation != null || (flags & WINDOW_FLAG_MINIMIZEABLE) == 0) + { + break; + } + animation = (isMinimized() ? new LiniarValue(1F, 0F, 1F) : new LiniarValue(1F, 1F, 0F)).setSmooth(); + if(isMinimized()) + { + setComponentBounds(lastSize.getX(), lastSize.getY()); + } + updateMinizedState(false); + break; + case 2: + value.setVisible(!isMinimized()); + break; + } + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + if(isOverChild(mouseX, mouseY) || getGui().hasComponentInTheWay(getTopComponent(), mouseX, mouseY)) + { + return false; + } + facing = getBox().getColidingBorder(mouseX, mouseY, 2F); + lastClick.set(mouseX, mouseY); + if(facing != null) + { + if(isFlagNotSet(FLAG_RESIZEABLE_HORIZONTAL)) facing = facing.remove(FacingList.HORIZONTAL); + if(isFlagNotSet(FLAG_RESIZEABLE_VERTICAL)) facing = facing.remove(FacingList.VERTICAL); + } + return true; + } + + @Override + public boolean onDrag(int mouseX, int mouseY) + { + if(facing != null) + { + if(facing.isEmpty() && isFlagSet(FLAG_MOVEABLE)) + { + moveComponent(mouseX - lastClick.getX(), mouseY - lastClick.getY()); + } + else if(!facing.isEmpty() && isFlagSet(FLAG_RESIZEABLE)) + { + float scale = getBox().getScale(); + float xChange = (mouseX - lastClick.getX()) * (facing.containsAny(FacingList.HORIZONTAL) ? 1F : 0F); + float yChange = (mouseY - lastClick.getY()) * (facing.containsAny(FacingList.VERTICAL) ? 1F : 0F); + if(isFlagSet(FLAG_RESIZE_INVERT)) + { + xChange *= -1F; + yChange *= -1F; + } + setMassChanging(); + if(facing.contains(Facing.NORTH) && isFlagNotSet(FLAG_MINIMIZED)) + { + resizeComponent(0F, -(yChange / scale)); + moveComponent(0F, yChange); + } + else if(facing.contains(Facing.SOUTH) && isFlagNotSet(FLAG_MINIMIZED)) + { + resizeComponent(0F, yChange / scale); + } + if(facing.contains(Facing.WEST)) + { + resizeComponent(-(xChange / scale) - 1F, 0F); + moveComponent(xChange, 0F); + } + else if(facing.contains(Facing.EAST)) + { + resizeComponent(xChange / scale, 0F); + } + ensureMinimumBounds(); + finishMassChanging(); + if(xChange > 0F || yChange > 0F) + { + onComponentChanged(true); + } + } + lastClick.set(mouseX, mouseY); + return true; + } + return false; + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + facing = null; + } + + @Override + public boolean canMoveIntoForground() + { + return true; + } + + @Override + public void onFocusLost() + { + facing = null; + } + + protected void ensureMinimumBounds() + { + IGuiBox box = getBox(); + Vec2f bounds = getMinimumBounds(); + if(box.getBaseWidth() < bounds.getX()) + { + box.setWidth(bounds.getX()); + onComponentChanged(true); + } + if(box.getBaseHeight() < bounds.getY()) + { + box.setHeight(bounds.getY()); + onComponentChanged(true); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/ArrowIcon.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/ArrowIcon.java new file mode 100644 index 0000000..0edcb5e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/ArrowIcon.java @@ -0,0 +1,40 @@ +package speiger.src.coreengine.rendering.gui.components.icon; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.misc.Facing; +import speiger.src.coreengine.rendering.gui.helper.UIShapes; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; + +public class ArrowIcon implements IIcon +{ + ColorObject color; + Facing direction; + float scale = 1F; + + public ArrowIcon(ColorObject color, Facing direction) + { + this.color = color; + this.direction = direction; + } + + public ArrowIcon setDirection(Facing direction) + { + this.direction = direction; + return this; + } + + public ArrowIcon setScale(float scale) + { + this.scale = scale; + return this; + } + + @Override + public void render(UIRenderer render, float minX, float minY, float maxX, float maxY, float scale) + { + render.push().translate(minX + ((maxX - minX) * 0.5F), minY + ((maxY - minY) * 0.5F)); + UIShapes.createArrow(render, (maxX - minX) * this.scale, (maxY - minY) * this.scale, color, direction); + render.pop(); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/CrossIcon.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/CrossIcon.java new file mode 100644 index 0000000..adae352 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/CrossIcon.java @@ -0,0 +1,33 @@ +package speiger.src.coreengine.rendering.gui.components.icon; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.helper.UIShapes; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; + +public class CrossIcon implements IIcon +{ + ColorObject color; + float paddingX = 4F; + float paddingY = 2F; + + public CrossIcon(ColorObject color) + { + this.color = color; + } + + public CrossIcon setPadding(float x, float y) + { + paddingX = x; + paddingY = y; + return this; + } + + @Override + public void render(UIRenderer render, float minX, float minY, float maxX, float maxY, float scale) + { + render.push().translate(minX + ((maxX - minX) / 2F), minY + ((maxY - minY) / 2F)); + UIShapes.createCross(render, maxX - minX, maxY - minY, paddingX * scale, paddingY * scale, color); + render.pop(); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/IIcon.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/IIcon.java new file mode 100644 index 0000000..8b8f577 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/IIcon.java @@ -0,0 +1,10 @@ +package speiger.src.coreengine.rendering.gui.components.icon; + +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; + +public interface IIcon +{ + public default void render(UIRenderer render, IGuiBox box) {render(render, box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), box.getScale());}; + public void render(UIRenderer render, float minX, float minY, float maxX, float maxY, float scale); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/LineIcon.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/LineIcon.java new file mode 100644 index 0000000..511f4c5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/LineIcon.java @@ -0,0 +1,28 @@ +package speiger.src.coreengine.rendering.gui.components.icon; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; + +public class LineIcon implements IIcon +{ + ColorObject color; + float height; + float padding; + float thickness = 1F; + + public LineIcon(ColorObject color, float height, float padding) + { + this.color = color; + this.height = height; + this.padding = padding; + } + + @Override + public void render(UIRenderer render, float minX, float minY, float maxX, float maxY, float scale) + { + float y = minY + ((maxY - minY) * height); + float x = (maxX - minX) * padding; + render.drawQuad(minX + x, y - (0.5F * scale), maxX - x, y, color); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/TexturedIcon.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/TexturedIcon.java new file mode 100644 index 0000000..15b4830 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/TexturedIcon.java @@ -0,0 +1,53 @@ +package speiger.src.coreengine.rendering.gui.components.icon; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.textures.ITexture; + +public class TexturedIcon implements IIcon +{ + ColorObject color = ColorObject.WHITE; + ITexture texture; + boolean forceBounds = false; + + public TexturedIcon(ITexture texture) + { + this.texture = texture; + } + + public TexturedIcon(ColorObject color, ITexture texture) + { + this.color = color; + this.texture = texture; + } + + public void setTexture(ITexture newTexture) + { + if(texture != null) + { + texture.deleteTexture(); + texture = null; + } + texture = newTexture; + } + + public TexturedIcon forceBounds(boolean value) + { + forceBounds = value; + return this; + } + + @Override + public void render(UIRenderer render, float minX, float minY, float maxX, float maxY, float scale) + { + if(forceBounds) + { + float width = texture.getWidth(); + float height = texture.getHeight(); + float ratio = height / width; + maxY = Math.min(minY + ((maxY - minY) * ratio), maxY); + } + render.setActiveTexture(texture).drawTexturedQuad(minX, minY, maxX, maxY, color, texture.getUMin(), texture.getVMin(), texture.getUMax(), texture.getVMax()); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/layouts/FlowLayout.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/layouts/FlowLayout.java new file mode 100644 index 0000000..dd6e2a4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/layouts/FlowLayout.java @@ -0,0 +1,55 @@ +package speiger.src.coreengine.rendering.gui.components.layouts; + +import java.util.List; +import java.util.function.Consumer; + +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; + +public class FlowLayout implements Consumer +{ + IGuiBox box; + List components; + Vec2f padding; + + public FlowLayout(IGuiBox box, Vec2f padding, List components) + { + this.padding = padding; + this.box = box; + this.components = components; + } + + @Override + public void accept(GuiComponent t) + { + float minX = box.getRelativeX(); + float minY = box.getRelativeY(); + float maxWidth = box.getWidth(); + int xInserted = 0; + int yInserted = 0; + float widthUsed = 0F; + float heightOffset = 0F; + float maxHeight = 0F; + for(int i = 0,m=components.size();i= maxWidth && xInserted > 0) + { + xInserted = 0; + maxHeight += heightOffset; + heightOffset = 0F; + widthUsed = 0F; + yInserted++; + } + component.setComponentPosition(minX + widthUsed + padding.getX(), minY + maxHeight + (padding.getY() * yInserted)); + heightOffset = Math.max(heightOffset, bounds[3] - bounds[1]); + widthUsed += width + padding.getX(); + xInserted++; + } + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/layouts/VerticalLayout.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/layouts/VerticalLayout.java new file mode 100644 index 0000000..adeab1a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/layouts/VerticalLayout.java @@ -0,0 +1,84 @@ +package speiger.src.coreengine.rendering.gui.components.layouts; + +import java.util.List; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; + +public class VerticalLayout implements Consumer +{ + GuiComponent parent; + List components = new ObjectArrayList<>(); + IGuiBox box; + float padding; + boolean changing = false; + + public VerticalLayout(List components, IGuiBox box, float padding) + { + this.components.addAll(components); + for(int i = 0;i T addComponent(T gui) + { + components.add(gui); + gui.addChangeListener(this); + gui.addCloseListener(this::removeComponent); + accept(null); + return gui; + } + + public void removeComponent(GuiComponent gui) + { + if(components.remove(gui)) + { + gui.removeChangeListener(this); + gui.removeCloseListener(this::removeComponent); + accept(null); + } + } + + @Override + public void accept(GuiComponent t) + { + if(changing) return; + changing = true; + float minX = box.getBaseX(); + float minY = box.getBaseY(); + float currentY = 0F; + for(int i = 0;i components = new ObjectArrayList<>(); + protected float maxWidth = -1F; + protected float lastScale = 1F; + protected T addComponent(T comp) + { + components.add(comp); + maxWidth = -1F; + return comp; + } + + @Override + public float getWidth() + { + if(maxWidth < 0F) + { + float[] value = new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MIN_VALUE, Float.MIN_VALUE}; + for(int i = 0,m=components.size();i collector) + { + for(int i = 0;i collector) + { + + } + + @Override + public boolean isComponentColliding(int mouseX, int mouseY) + { + return checkBox.isComponentColliding(mouseX, mouseY); + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + return checkBox.onClick(button, mouseX, mouseY); + } + + @Override + public boolean onDrag(int mouseX, int mouseY) + { + return checkBox.onDrag(mouseX, mouseY); + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + checkBox.onRelease(button, mouseX, mouseY); + } + + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/list/FileEntry.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/list/FileEntry.java new file mode 100644 index 0000000..fc1b563 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/list/FileEntry.java @@ -0,0 +1,20 @@ + +package speiger.src.coreengine.rendering.gui.components.list; + +import java.io.File; + +public class FileEntry extends SelectionEntry +{ + File file; + + public FileEntry(File file) + { + this.file = file; + setText(file.getName()); + } + + public File getFile() + { + return file; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/list/IListEntry.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/list/IListEntry.java new file mode 100644 index 0000000..49f452d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/list/IListEntry.java @@ -0,0 +1,26 @@ +package speiger.src.coreengine.rendering.gui.components.list; + +import java.util.Map; +import java.util.UUID; + +import speiger.src.coreengine.rendering.gui.GuiBase; +import speiger.src.coreengine.rendering.gui.GuiComponent; + +public interface IListEntry +{ + public float getWidth(); + + public default float getHighlightWidth() { return -1F; } + + public void init(GuiComponent comp, GuiBase owner); + + public void updateState(GuiComponent comp, float scale); + + public void onClosed(); + + public void onFixedUpdate(); + + public void onRender(GuiComponent comp, boolean enabled, int mouseX, int mouseY, float particalTicks); + + public void collectTooltips(GuiComponent comp, int mouseX, int mouseY, float particalTicks, Map collector); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/list/SelectionEntry.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/list/SelectionEntry.java new file mode 100644 index 0000000..316d38a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/list/SelectionEntry.java @@ -0,0 +1,81 @@ +package speiger.src.coreengine.rendering.gui.components.list; + +import java.util.Map; +import java.util.UUID; + +import speiger.src.coreengine.rendering.gui.GuiBase; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.components.TextComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; + +public class SelectionEntry implements IListEntry +{ + TextComponent text = new TextComponent().setLimitedBounds(false).setAlignment(Align.LEFT_TOP, Align.LEFT_TOP); + + public SelectionEntry(){} + + public SelectionEntry(String s) + { + setText(s); + } + + public SelectionEntry setText(String s) + { + text.setText(s); + return this; + } + + public SelectionEntry setTextScale(float scale) + { + text.setTextScale(scale); + return this; + } + + @Override + public float getWidth() + { + return text.getMetadata().getMaxWidth(); + } + + @Override + public void init(GuiComponent comp, GuiBase owner) + { + text.setOwner(owner); + text.setScale(comp.getBox().getScale()); + } + + @Override + public void updateState(GuiComponent comp, float scale) + { + text.setScale(scale); + } + + @Override + public void onClosed() + { + text.onClosed(); + } + + @Override + public void onFixedUpdate() + { + + } + + @Override + public void onRender(GuiComponent comp, boolean enabled, int mouseX, int mouseY, float particalTicks) + { + text.onRender(mouseX, mouseY, particalTicks); + } + + @Override + public void collectTooltips(GuiComponent comp, int mouseX, int mouseY, float particalTicks, Map collector) + { + text.collectTooltips(mouseX, mouseY, particalTicks, collector); + } + + public String getText() + { + return text.getText(); + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/list/TextEntry.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/list/TextEntry.java new file mode 100644 index 0000000..b5c861c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/list/TextEntry.java @@ -0,0 +1,25 @@ +package speiger.src.coreengine.rendering.gui.components.list; + +import speiger.src.coreengine.rendering.gui.components.TextComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; + +public class TextEntry extends BaseListEntry +{ + TextComponent text = addComponent(new TextComponent().setLimitedBounds(false).setAlignment(Align.LEFT_TOP, Align.LEFT_TOP).setSpecialRendering(true).setComponentPosition(0F, 0F).cast()); + + public TextEntry(String s) + { + text.setText(s); + } + + public TextEntry setScale(float scale) + { + text.setTextScale(scale); + return this; + } + + public String getText() + { + return text.getText(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuBarComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuBarComponent.java new file mode 100644 index 0000000..e0772b9 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuBarComponent.java @@ -0,0 +1,103 @@ +package speiger.src.coreengine.rendering.gui.components.menu; + +import java.util.List; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.MenuConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; + +public class MenuBarComponent extends GuiComponent +{ + ColorObject color; + List menuItems = new ObjectArrayList(); + float scale = 1F; + + public MenuBarComponent(ColorObject color) + { + super(0F, 0F, 0F, 0F); + this.color = color; + } + + public MenuBarComponent(float x, float y, float width, float height, ColorObject color) + { + super(x, y, width, height); + this.color = color; + } + + @Override + public void init() + { + } + + public MenuBarComponent setTextScale(float scale) + { + if(this.scale != scale) + { + this.scale = scale; + for(int i = 0,m=menuItems.size();i(menuItems), new PixelConstrain(), item.createWidthConstriain(), new ParentConstrain())); + item.setZOffset(0.3F); + item.setTextScale(scale); + item.onComponentChanged(false); + return item; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + getRenderer().drawQuad(getBox(), color); + return true; + } + + protected void closeMenus(GuiComponent owner) + { + for(int i = 0,m=menuItems.size();i +{ + public static final int FLAG_CHECKED = 1 << 21; + RenderBuffer buffer; + + public MenuCheckBoxComponent(String name) + { + super(name); + setFlag(FLAG_KEEP_MENU_OPEN | FLAG_SUPPORT_BINDING); + } + + public MenuCheckBoxComponent(String name, boolean checked) + { + super(name); + setFlag(FLAG_CHECKED, checked); + setFlag(FLAG_KEEP_MENU_OPEN | FLAG_SUPPORT_BINDING); + } + + public MenuCheckBoxComponent(float x, float y, float width, float height, String name) + { + super(x, y, width, height, name); + setFlag(FLAG_KEEP_MENU_OPEN | FLAG_SUPPORT_BINDING); + } + + public MenuCheckBoxComponent(float x, float y, float width, float height, String name, boolean checked) + { + super(x, y, width, height, name); + setFlag(FLAG_CHECKED, checked); + setFlag(FLAG_KEEP_MENU_OPEN | FLAG_SUPPORT_BINDING); + } + + @Override + public void init() + { + addChild(text, new ComponentConstrains(new PixelConstrain(getBox().getHeight() + 1F), new ParentConstrain(), new DynamicConstrain(() -> text.getMetadata().getMaxWidth() + 0.5F), new ParentConstrain())); + addCloseListener(buffer = getRenderer().createBuffer()); + } + + @Override + protected void repaint() + { + float width = getBox().getHeight(); + float height = getBox().getHeight(); + buffer.clear(); + UIShapes.createCross(buffer, width, height, (width / 3.3F), (height / 5F), color); + UIShapes.createCross(buffer, width, height, (width / 5F), (height / 10F), color); + } + + @Override + protected Constrain createWidthConstriain() + { + return new DynamicConstrain(() -> text.getMetadata().getMaxWidth() + getBox().getHeight() + 3F); + } + + @Override + public final boolean isChecked() + { + return isFlagSet(FLAG_CHECKED); + } + + @Override + public final MenuCheckBoxComponent setChecked(boolean value) + { + setFlag(FLAG_CHECKED, value); + return this; + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brightness = getActiveBrightness(); + float height = getBox().getHeight(); + float centerX = getBox().getMinX(height * 0.5F); + float centerY = getBox().getMinY(height * 0.5F); + getRenderer().setBrightness(brightness * 0.85F).drawQuad(getBox().getMinX(0.2F), getBox().getMinY(), getBox().getMinX(height + 0.2F), getBox().getMinY(height), color).setBrightness(brightness * 0.75F).drawFrame(getBox().getMinX(0.2F), getBox().getMinY(), getBox().getMinX(height + 0.2F), getBox().getMinY(height), color).translate(centerX, centerY); + if(isChecked()) + { + getRenderer().setBrightness(brightness * 0.7F).translate(0, 0, 0.001F).drawBuffers(buffer.selectionIterator(1), getBox().getWidth(), getBox().getHeight()).translate(0, 0, -0.001F); + } + if(isHovered(mouseX, mouseY)) + { + getRenderer().setBrightness(brightness * 1.3F).translate(0, 0, 0.002F).drawBuffers(buffer.selectionIterator(0), getBox().getWidth(), getBox().getHeight()).translate(0, 0, -0.002F); + } + float maxX = Math.min(boxWidth, getBox().getMaxX()); + getRenderer().setBrightness(1F).translate(-centerX, -centerY).drawQuad(getBox().getMinX(height), getBox().getMinY(), getBox().getMinX(maxX), getBox().getMaxY(), color).setBrightness(brightness * 0.75F).drawFrame(getBox().getMinX(height), getBox().getMinY(), getBox().getMinX(maxX), getBox().getMaxY(), color); + return true; + } + + protected boolean isCheckBoxHovered(int mouseX, int mouseY) + { + float height = getBox().getHeight(); + return getBox().getMinX() <= mouseX && getBox().getMinX(height) >= mouseX && getBox().getMinY() <= mouseY && getBox().getMinY(height) >= mouseY && getGui().hasComponentInTheWay(this, mouseX, mouseY); + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + return true; + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + setFlag(FLAG_CHECKED, !isFlagSet(FLAG_CHECKED)); + notifyListeners(LISTENER_USER_ACTION); + } + + @Override + protected boolean onUserKey() + { + setFlag(FLAG_CHECKED, !isFlagSet(FLAG_CHECKED)); + notifyListeners(LISTENER_USER_ACTION); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuComponent.java new file mode 100644 index 0000000..74f07e0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuComponent.java @@ -0,0 +1,319 @@ +package speiger.src.coreengine.rendering.gui.components.menu; + +import java.util.List; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.DynamicConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.MenuConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; + +public class MenuComponent extends MenuItemComponent implements Consumer +{ + static final int FLAG_OPEN = 1 << 21; + static final int FLAG_SUB_MENU = 1 << 22; + List components = new ObjectArrayList(); + float defaultScale = 1F; + float width = 0F; + float entryHeight = 10F; + float textScale = 1F; + long timeNotHovered = -1; + MenuComponent ownerMenu; + + public MenuComponent(String name) + { + super(name); + clearFlag(FLAG_SUPPORT_BINDING); + } + + public MenuComponent(float x, float y, float width, float height, String name) + { + super(x, y, width, height, name); + clearFlag(FLAG_SUPPORT_BINDING); + } + + @Override + public boolean canMoveIntoForground() + { + return true; + } + + public MenuComponent setHeight(float height) + { + entryHeight = height; + return this; + } + + protected MenuComponent setParent(MenuComponent owner) + { + ownerMenu = owner; + return this; + } + + @Override + protected void setMenuColor(ColorObject color) + { + super.setMenuColor(color); + for(int i = 0,m=components.size();i").setIsSubMenu(true).setParent(this)).cast(); + } + + public MenuItemComponent addText(String name) + { + return addMenuItem(new MenuTextComponent(name)).cast(); + } + + public MenuItemComponent addMenuItem(String name) + { + return addMenuItem(new MenuItemComponent(name)); + } + + public MenuCheckBoxComponent addCheckBox(String name) + { + return addMenuItem(new MenuCheckBoxComponent(name)).cast(); + } + + public MenuCheckBoxComponent addCheckBox(String name, boolean value) + { + return addMenuItem(new MenuCheckBoxComponent(name, value)).cast(); + } + + public MenuItemComponent addMenuItem(MenuItemComponent comp) + { + components.add(comp); + comp.setScale(defaultScale); + comp.setMenuColor(color); + addChild(comp.setIgnoreBounds(true).addUserActionListener(this), createConstrains(comp)).onComponentChanged(false); + comp.setVisible(isFlagSet(FLAG_OPEN)); + comp.setTextScale(textScale); + return comp; + } + + protected ComponentConstrains createConstrains(MenuItemComponent comp) + { + Constrain constraint = isFlagSet(FLAG_SUB_MENU) ? new DynamicConstrain(() -> Math.max(getBox().getBaseWidth(), boxWidth / getBox().getScale())) : new PixelConstrain(); + return new ComponentConstrains(constraint, new MenuConstrain(components, isFlagSet(FLAG_SUB_MENU) ? MenuConstrain.DEFAULT : () -> getBox().getBaseHeight() + 0.1F).setPadding(0.01F), comp.createWidthConstriain(), new PixelConstrain(entryHeight)); + } + + @Override + public GuiComponent removeChild(GuiComponent comp) + { + super.removeChild(comp); + if(components.remove(comp)) + { + onComponentChanged(false); + } + return this; + } + + @Override + public GuiComponent removeChildren() + { + components.clear(); + return super.removeChildren(); + } + + public void removeElements() + { + for(MenuItemComponent comp : components) + { + super.removeChild(comp); + } + components.clear(); + } + + @Override + protected boolean updateSelf(int mouseX, int mouseY, float particalTicks) + { + if(isSubMenu() && isVisible()) + { + if(isFlagSet(FLAG_OPEN)) + { + if(isMouseOver(mouseX, mouseY) || isOverChild(mouseX, mouseY)) + { + timeNotHovered = -1; + return true; + } + if(timeNotHovered == -1) + { + timeNotHovered = getGlobalClock(); + } + if(getGlobalClock() - timeNotHovered > 10) + { + setOpen(false); + } + return true; + } + timeNotHovered = -1; + if(isMouseOver(mouseX, mouseY) && !ownerMenu.hasOtherMenuOpen(this)) + { + setOpen(true); + } + } + return true; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brigthness = isFlagSet(FLAG_OPEN) ? 0.7F : getBrightness(mouseX, mouseY); + float minX = getBox().getMinX(0.2F); + float minY = getBox().getMinY(0.2F); + float maxX = Math.max(getBox().getMaxX(), minX + boxWidth); + float maxY = getBox().getMaxY(-0.2F); + getRenderer().setBrightness(brigthness).drawQuad(minX, minY, maxX, maxY, color).setBrightness(brigthness * 0.5F).drawFrame(minX, minY, maxX, maxY, color); + text.setBrightness(brigthness); + return true; + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + if(!isSubMenu()) + { + setOpen(!isFlagSet(FLAG_OPEN)).notifyListeners(LISTENER_USER_ACTION); + } + return true; + } + + protected MenuComponent setIsSubMenu(boolean value) + { + if(setFlag(FLAG_SUB_MENU, value)) + { + setFlag(FLAG_KEEP_MENU_OPEN, value); + for(int i = 0,m=components.size();i= mouseX && getBox().getMinY() <= mouseY && getBox().getMaxY() >= mouseY; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brigthness = getBrightness(mouseX, mouseY); + float minX = getBox().getMinX(0.2F); + float minY = getBox().getMinY(0.2F); + float maxX = Math.max(getBox().getMaxX(), minX + boxWidth); + float maxY = getBox().getMaxY(-0.2F); + getRenderer().setBrightness(brigthness).drawQuad(minX, minY, maxX, maxY, color).setBrightness(brigthness * 0.5F).drawFrame(minX, minY, maxX, maxY, color); + text.setBrightness(brigthness); + return true; + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + return true; + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + notifyListeners(LISTENER_USER_ACTION); + } + + @Override + protected boolean onUserKey() + { + notifyListeners(LISTENER_USER_ACTION); + return true; + } + + @Override + public String toString() + { + return "Menu Item: "+getText().getText(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuTextComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuTextComponent.java new file mode 100644 index 0000000..7acdb07 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuTextComponent.java @@ -0,0 +1,39 @@ +package speiger.src.coreengine.rendering.gui.components.menu; + +public class MenuTextComponent extends MenuItemComponent +{ + + public MenuTextComponent(String name) + { + super(name); + clearFlag(FLAG_SUPPORT_BINDING); + } + + public MenuTextComponent(float x, float y, float width, float height, String name) + { + super(x, y, width, height, name); + } + + @Override + public boolean isValidButton(int button) + { + return false; + } + + @Override + public boolean isComponentColliding(int mouseX, int mouseY) + { + return false; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float minX = getBox().getMinX(0.2F); + float minY = getBox().getMinY(0.2F); + float maxX = Math.max(getBox().getMaxX(), minX + boxWidth); + float maxY = getBox().getMaxY(-0.2F); + getRenderer().drawQuad(minX, minY, maxX, maxY, color).setBrightness(0.5F).drawFrame(minX, minY, maxX, maxY, color); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/misc/CheckBoxGroup.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/misc/CheckBoxGroup.java new file mode 100644 index 0000000..2bf1323 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/misc/CheckBoxGroup.java @@ -0,0 +1,118 @@ +package speiger.src.coreengine.rendering.gui.components.misc; + +import java.util.List; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.lists.ObjectList; +import speiger.src.collections.objects.utils.ObjectLists; +import speiger.src.coreengine.rendering.gui.GuiComponent; + +public class CheckBoxGroup> implements Consumer +{ + ObjectList checkboxes = new ObjectArrayList(); + int selectedIndex = -1; + Runnable listener; + + public CheckBoxGroup setListener(Runnable listener) + { + this.listener = listener; + return this; + } + + public T add(T box) + { + checkboxes.add(box); + if(box.isChecked()) + { + if(selectedIndex == -1) + { + selectedIndex = checkboxes.indexOf(box); + } + else + { + box.setChecked(false); + } + } + box.addUserActionListener(this); + return box; + } + + public boolean remove(T box) + { + boolean result = checkboxes.remove(box); + if(result) + { + if(box.isChecked()) + { + selectedIndex = -1; + } + box.removeChangeListener(this); + } + return result; + } + + public T getSelected() + { + return selectedIndex == -1 ? null : checkboxes.get(selectedIndex); + } + + public boolean hasSelected() + { + return selectedIndex != -1; + } + + public int getSelectedIndex() + { + return selectedIndex; + } + + public List getCheckBoxes() + { + return ObjectLists.unmodifiable(checkboxes); + } + + public void setChecked(int selected) + { + if(selectedIndex != -1) + { + checkboxes.get(selectedIndex).setChecked(false); + } + selectedIndex = selected; + if(selectedIndex != -1) + { + checkboxes.get(selectedIndex).setChecked(true); + } + } + + @Override + public void accept(GuiComponent t) + { + ICheckBox box = t.tryCast(ICheckBox.class); + if(box == null) + { + return; + } + int index = checkboxes.indexOf(t); + if(index == -1) + { + return; + } + if(box.isChecked()) + { + if(selectedIndex != -1) + { + checkboxes.get(selectedIndex).setChecked(false); + } + selectedIndex = index; + } + else + { + selectedIndex = -1; + } + if(listener != null) + { + listener.run(); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/misc/ICheckBox.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/misc/ICheckBox.java new file mode 100644 index 0000000..4b8cf84 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/misc/ICheckBox.java @@ -0,0 +1,10 @@ +package speiger.src.coreengine.rendering.gui.components.misc; + +import speiger.src.coreengine.rendering.gui.GuiComponent; + +public interface ICheckBox +{ + public boolean isChecked(); + + public T setChecked(boolean value); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/special/ConsoleComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/special/ConsoleComponent.java new file mode 100644 index 0000000..0421182 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/special/ConsoleComponent.java @@ -0,0 +1,223 @@ +package speiger.src.coreengine.rendering.gui.components.special; + +import java.util.List; +import java.util.function.Consumer; + +import org.lwjgl.glfw.GLFW; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IKeyComponent; +import speiger.src.coreengine.rendering.gui.components.ListComponent; +import speiger.src.coreengine.rendering.gui.components.ScrollBarComponent; +import speiger.src.coreengine.rendering.gui.components.TextFieldComponent; +import speiger.src.coreengine.rendering.gui.components.list.TextEntry; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.RelativeConstrain; +import speiger.src.coreengine.rendering.gui.renderer.IFontRenderer; + +public class ConsoleComponent extends GuiComponent implements IKeyComponent +{ + TextFieldComponent chat = new TextFieldComponent(ColorObject.DARK_GRAY).setCanLoseFocus(false).setInfiniteText(true).setMaxTextLength(Integer.MAX_VALUE).setFocused(true); + ListComponent list = new ListComponent<>(ColorObject.DARK_GRAY.copy().setAlpha(120), 8F); + float lastWidth = 0F; + List messages = new ObjectArrayList<>(); + Consumer listener; + int messageIterator = 0; + boolean started = false; + + public ConsoleComponent() + { + this(0F, 0F, 0F, 0F); + } + + public ConsoleComponent(float x, float y, float width, float height) + { + super(x, y, width, height); + } + + public ConsoleComponent setListener(Consumer listener) + { + this.listener = listener; + return this; + } + + @Override + public void init() + { + chat.getRawText().setTextScale(0.45F); + chat.addUserActionListener(this::onEnter); + list.setSelectionMode(ListComponent.SELECTION_MODE_DISABLE); + list.setStartAtBottom(true); + addChild(chat, new ComponentConstrains(new ParentConstrain(), new ParentConstrain(12F).invert(), new ParentConstrain(), new PixelConstrain(12F))); + addChild(list, new ComponentConstrains(new ParentConstrain(), new ParentConstrain(100).invert(), new RelativeConstrain(0.8F), new PixelConstrain(88))); + } + + @Override + protected void updateState() + { + float width = list.getBox().getWidth(); + if(lastWidth != width) + { + lastWidth = width; + updateMessages(); + } + } + + protected void updateMessages() + { + list.clear(); + for(int i = 0,m=messages.size();i 0) + { + messageIterator--; + chat.setText(messages.get((messages.size()-1 - messageIterator)).getMessage()).setCurserToEnd(); + } + else if(started) + { + chat.setText("").setCurserToEnd(); + started = false; + } + return true; + } + return false; + } + + public void addMessage(String text) + { + addMessage(text, 0); + } + + public void addMessage(String text, int messageId) + { + addMessage(text, messageId, true); + } + + private void addMessage(String text, int messageId, boolean add) + { + if(listener != null && text.startsWith("/")) + { + listener.accept(text.substring(1)); + return; + } + ScrollBarComponent scroll = list.getVerticalBar(); + boolean atEnd = scroll.isAtEnd(); + float width = (list.getBox().getWidth() -5F) / 0.45F; + for(String s : getGui().getFont().splitLines(text, width, IFontRenderer.SPECIAL)) + { + list.addEntry(new MessageEntry(s, messageId)); + } + if(atEnd) scroll.setScrollEnd(); + if(add) + { + messages.add(new ConsoleMessage(messageId, text)); + messageIterator = 0; + } + } + + public void clearMessages() + { + messages.clear(); + list.clear(); + messageIterator = 0; + } + + public void removeMessage(int messageId) + { + messages.removeIf(T -> T.getMessageId() == messageId); + list.removeIf(T -> T.getMessageId() == messageId); + } + + private static class ConsoleMessage + { + int messageId; + String message; + + public ConsoleMessage(int messageId, String message) + { + this.messageId = messageId; + this.message = message; + } + + public String getMessage() + { + return message; + } + + public int getMessageId() + { + return messageId; + } + } + + private static class MessageEntry extends TextEntry + { + int messageId; + + public MessageEntry(String s, int messageId) + { + super(s); + this.messageId = messageId; + setScale(0.45F); + } + + public int getMessageId() + { + return messageId; + } + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/tree/BaseTreeEntry.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/tree/BaseTreeEntry.java new file mode 100644 index 0000000..df155f7 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/tree/BaseTreeEntry.java @@ -0,0 +1,142 @@ +package speiger.src.coreengine.rendering.gui.components.tree; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.rendering.gui.GuiBase; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.components.list.BaseListEntry; + +public abstract class BaseTreeEntry extends BaseListEntry implements ITreeEntry +{ + protected List children = null; + protected ITreeEntry parent = null; + protected int debth; + protected boolean init = false; + + @Override + public Iterator iterator() + { + return isLeaf() ? Collections.emptyIterator() : children.iterator(); + } + + @Override + public boolean isInit() + { + return init; + } + + @Override + public void init(GuiComponent comp, GuiBase owner) + { + super.init(comp, owner); + init = true; + } + + @Override + public boolean isLeaf() + { + return children == null; + } + + @Override + public void addChild(ITreeEntry child) + { + if(isLeaf()) + { + children = new ObjectArrayList(); + } + children.add(child); + child.setParent(this); + calculateDebth(); + onChange(); + } + + @Override + public void removeChild(ITreeEntry child) + { + if(isLeaf()) + { + return; + } + if(children.remove(child)) + { + child.setParent(null); + if(children.isEmpty()) + { + children = null; + } + onChange(); + } + } + + protected void onChange() + { + + } + + @Override + public void onClosed() + { + super.onClosed(); + if(!isLeaf()) + { + for(int i = 0;i +{ + public boolean isInit(); + + public boolean isLeaf(); + + public void addChild(ITreeEntry child); + + public void removeChild(ITreeEntry child); + + public int getChildCount(); + + public ITreeEntry getChild(int index); + + public int indexOf(ITreeEntry child); + + public ITreeEntry getParent(); + + public void setParent(ITreeEntry parent); + + public int getDebth(); + + public void calculateDebth(); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/tree/ProfilerTreeEntry.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/tree/ProfilerTreeEntry.java new file mode 100644 index 0000000..ebb9fe9 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/tree/ProfilerTreeEntry.java @@ -0,0 +1,129 @@ +package speiger.src.coreengine.rendering.gui.components.tree; + +import java.util.Map; +import java.util.UUID; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiBase; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.components.TextComponent; +import speiger.src.coreengine.rendering.gui.components.window.debug.PieProfilerWindowComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; +import speiger.src.coreengine.rendering.input.Keyboard; +import speiger.src.coreengine.utils.profiler.IProfiler.IProfilerEntry; + +public class ProfilerTreeEntry extends BaseTreeEntry +{ + TextComponent text = new TextComponent().setLimitedBounds(false).setAlignment(Align.LEFT_TOP, Align.LEFT_TOP).setSpecialRendering(true).setTextScale(0.4F).setComponentPosition(0F, 1F).cast(); + IProfilerEntry entry; + + public ProfilerTreeEntry(IProfilerEntry entry) + { + this.entry = entry; + } + + @Override + public float getWidth() + { + return text.getMetadata().getMaxWidth(); + } + + @Override + public void init(GuiComponent comp, GuiBase owner) + { + super.init(comp, owner); + text.setOwner(owner); + } + + @Override + public void updateState(GuiComponent comp, float scale) + { + text.setScale(scale); + } + + @Override + public void onClosed() + { + text.onClosed(); + } + + @Override + public void onFixedUpdate() + { + if(text.length() > 0 && (Keyboard.isAltDown() || text.getGui().getGlobalClock() % 2 != 0)) + { + return; + } + StringBuilder builder = new StringBuilder(); + builder.append(entry.getName()).append(": "); + builder.append(getColor(entry.getMinTime())).append(getTime(entry.getMinTime())).append("§").append(" / "); + builder.append(getColor(entry.getNanoTime())).append(getTime(entry.getNanoTime())).append("§").append(" / "); + builder.append(getColor(entry.getMaxTime())).append(getTime(entry.getMaxTime())).append("§"); + text.setText(builder.toString()); + } + + @Override + public void onRender(GuiComponent comp, boolean enabled, int mouseX, int mouseY, float particalTicks) + { + text.setEnabled(enabled).onRender(mouseX, mouseY, particalTicks); + } + + @Override + public void collectTooltips(GuiComponent comp, int mouseX, int mouseY, float particalTicks, Map collector) + { + + } + + @Override + public int hashCode() + { + return entry.getPathName().hashCode(); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof ProfilerTreeEntry) + { + return ((ProfilerTreeEntry)obj).entry == entry; + } + return false; + } + + public String getTime(long time) + { + if(time >= 2000000L) + { + return PieProfilerWindowComponent.PERCENT_FORMAT.format(time / 1000000.0D) + "ms"; + } + if(time >= 2000L) + { + return PieProfilerWindowComponent.PERCENT_FORMAT.format(time / 1000.0D) + "µs"; + } + return PieProfilerWindowComponent.PERCENT_FORMAT.format(time) + "ns"; + } + + public String getColor(long time) + { + time /= 1000000L; + if(time < 1) + { + return "§"; + } + if(time >= 12) + { + return "§"; + } + if(time >= 5) + { + return "§"; + } + return "§"; + } + + @Override + public String toString() + { + return entry.getPathName(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/window/color/ColorPickerWindowComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/color/ColorPickerWindowComponent.java new file mode 100644 index 0000000..bec8c76 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/color/ColorPickerWindowComponent.java @@ -0,0 +1,154 @@ +package speiger.src.coreengine.rendering.gui.components.window.color; + +import java.awt.Color; +import java.util.function.Consumer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.misc.Facing; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.rendering.gui.UITextures; +import speiger.src.coreengine.rendering.gui.components.ButtonComponent; +import speiger.src.coreengine.rendering.gui.components.GradientSliderComponent; +import speiger.src.coreengine.rendering.gui.components.SliderComponent; +import speiger.src.coreengine.rendering.gui.components.TextFieldComponent; +import speiger.src.coreengine.rendering.gui.components.WindowComponent; +import speiger.src.coreengine.rendering.gui.components.icon.IIcon; +import speiger.src.coreengine.rendering.gui.components.icon.TexturedIcon; +import speiger.src.coreengine.rendering.gui.helper.box.GuiBox; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.RelativeConstrain; +import speiger.src.coreengine.utils.functions.Functions; +import speiger.src.coreengine.utils.helpers.InternalThreadPools; + +public class ColorPickerWindowComponent extends WindowComponent +{ + Consumer listener; + IIcon wheelIcon = new TexturedIcon(UITextures.COLOR_WHEEL); + IGuiBox wheelBox = new GuiBox(15, 10, 70, 70); + IGuiBox selectedBox = new GuiBox(5F, 94F, 50F, 10F); + ColorObject[] colors = new ColorObject[]{ColorObject.rgb(0), ColorObject.rgb(0), ColorObject.rgb(0), ColorObject.rgb(0), ColorObject.rgb(0)}; + SliderComponent brightness = new GradientSliderComponent(95, 13, 17, 76, 0, 100, 0, ColorObject.GRAY, colors[2], colors[3], Facing.NORTH, null).setScrollEffect(-1).setVertical(true); + SliderComponent saturation = new GradientSliderComponent(110, 13, 17, 76, 0, 100, 0, ColorObject.GRAY, colors[0], colors[1], Facing.NORTH, null).setScrollEffect(-1).setVertical(true); + TextFieldComponent code = new TextFieldComponent(60, 94, 104, 20, ColorObject.DARK_GRAY).setValidator(Functions.NUMBERS_ONLY).setMaxTextLength(8); + float[] hsv = new float[3]; + IGuiBox[] colorBoxes; + ColorPool pool; + + public ColorPickerWindowComponent(float x, float y, ColorPool pool, ColorObject defaultColor, Consumer listener, String name) + { + super(x, y, 125, 140, FIXED_SIZE_POPUP, name); + this.pool = pool; + this.listener = listener; + hsv = defaultColor.toHue(); + colorBoxes = new IGuiBox[Math.min(18, pool.size())]; + for(int i = 0,m=colorBoxes.length;i= 9 ? 12 : 0), 10, 10)); + } + } + + @Override + public void init() + { + super.init(); + addBox(wheelBox); + addBox(selectedBox); + addChild(brightness.addChangeListener(minimizedListener).addUserActionListener(T -> setColor(hsv[0], hsv[1], T.cast(SliderComponent.class).getValue() * 0.01F))); + addChild(saturation.addChangeListener(minimizedListener).addUserActionListener(T -> setColor(hsv[0], T.cast(SliderComponent.class).getValue() * 0.01F, hsv[2]))); + addChild(code.setScale(0.5F).addChangeListener(minimizedListener).addUserActionListener(T -> onTyped())); + addChild(new ButtonComponent(0F, 0F, 0F, 20F, "Select", ColorObject.GREEN).setScale(0.4F).addUserActionListener(T -> apply()), new ComponentConstrains(null, new ParentConstrain(8F).invert(), new RelativeConstrain(0.5F / 0.4F), null)); + addChild(new ButtonComponent(0F, 0F, 0F, 20F, "Cancel", ColorObject.RED).setScale(0.4F).addUserActionListener(T -> T.getGui().removeComponent(this)), new ComponentConstrains(new RelativeConstrain(0.5F), new ParentConstrain(8F).invert(), new RelativeConstrain(0.5F / 0.4F), null)); + setColor(hsv[0], hsv[1], hsv[2]); + } + + @Override + public boolean isPopup() + { + return true; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + super.renderSelf(mouseX, mouseY, particalTicks); + wheelIcon.render(getRenderer().translate(0F, 0F, 0.01F).setBrightness(hsv[1]), wheelBox); + getRenderer().setBrightness(1F); + ColorObject color = new ColorObject(0); + for(int i = 0,m=colorBoxes.length;i> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF, new float[3]); + setColor(hsv[0], hsv[1], hsv[2]); + } + catch(Exception e){} + } + + public void apply() + { + pool.addColor(colors[4].getRGB()); + listener.accept(colors[4]); + getGui().removeComponent(this); + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + return wheelBox.isColiding(mouseX, mouseY) ? onChanged(mouseX, mouseY) : super.onClick(button, mouseX, mouseY); + } + + @Override + public boolean onDrag(int mouseX, int mouseY) + { + return super.onDrag(mouseX, mouseY) || (wheelBox.isColiding(mouseX, mouseY) ? onChanged(mouseX, mouseY) : false); + } + + private boolean onChanged(int mouseX, int mouseY) + { + Vec2f pos = InternalThreadPools.VEC2F.get().set(mouseX, mouseY); + float radius = (float)pos.distanceTo(wheelBox.getCenterX(), wheelBox.getCenterY()); + if(radius > 38) + { + InternalThreadPools.VEC2F.accept(pos); + return false; + } + float hue = (pos.directionAngle(wheelBox.getCenterX(), wheelBox.getCenterY()) + 180F) % 360F; + setColor(hue / 360F, hsv[1], radius / 33F); + InternalThreadPools.VEC2F.accept(pos); + return true; + } + + protected void setColor(float hue, float saturation, float brightness) + { + hsv[0] = MathUtils.clamp(0F, 1F, hue); + hsv[1] = MathUtils.clamp(0F, 1F, saturation); + hsv[2] = MathUtils.clamp(0F, 1F, brightness); + colors[0].setRGB(hsv[0], 0F, 1F, 255); + colors[1].setRGB(hsv[0], 1F, 1F, 255); + colors[2].setRGB(hsv[0], 1F, 0F, 255); + colors[3].setRGB(hsv[0], 1F, 1F, 255); + colors[4].setRGB(hsv[0], hsv[1], hsv[2], 255); + code.setText(colors[4].getHTMLCode(false)); + this.brightness.setValue((int)(hsv[2] * 100F)); + this.saturation.setValue((int)(hsv[1] * 100F)); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/window/color/ColorPool.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/color/ColorPool.java new file mode 100644 index 0000000..d71ac38 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/color/ColorPool.java @@ -0,0 +1,102 @@ +package speiger.src.coreengine.rendering.gui.components.window.color; + +import java.util.BitSet; + +import speiger.src.coreengine.math.ArrayUtil; +import speiger.src.coreengine.math.misc.ColorObject; + +public class ColorPool +{ + int[] colors; + BitSet locked; + + public ColorPool(int size) + { + colors = ArrayUtil.fill(new int[size], ColorObject.WHITE.getRGB()); + locked = new BitSet(size); + } + + public void addColor(int color) + { + int index = colors.length; + boolean lock = false; + for(int i = 0,m=colors.length;i0;i--) + { + if(isLocked(i) && (index == colors.length || i != (index - 1))) + { + continue; + } + colors[i] = colors[i-1]; + locked.set(i, locked.get(i-1)); + locked.clear(i-1); + } + colors[0] = color; + locked.set(0, lock); + } + + public void removeColor(int index) + { + colors[index] = ColorObject.WHITE.getRGB(); + } + + public int getColor(int index) + { + return colors[index]; + } + + public void toggleLock(int index) + { + locked.set(index, !locked.get(index)); + } + + public void lockIndex(int index) + { + locked.set(index); + } + + public void unlockIndex(int index) + { + locked.clear(index); + } + + public boolean isLocked(int index) + { + return locked.get(index); + } + + public int[] getColors() + { + return colors.clone(); + } + + public long[] getLocked() + { + return locked.toLongArray(); + } + + public void setData(int[] array, long[] locked) + { + System.arraycopy(array, 0, colors, 0, Math.min(array.length, colors.length)); + this.locked = BitSet.valueOf(locked); + } + + public void clear() + { + ArrayUtil.fill(colors, ColorObject.WHITE.getRGB()); + locked.clear(); + } + + public int size() + { + return colors.length; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/window/debug/PieProfilerWindowComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/debug/PieProfilerWindowComponent.java new file mode 100644 index 0000000..bd6f0e0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/debug/PieProfilerWindowComponent.java @@ -0,0 +1,306 @@ +package speiger.src.coreengine.rendering.gui.components.window.debug; + +import java.text.DecimalFormat; +import java.util.List; +import java.util.function.Supplier; + +import org.lwjgl.glfw.GLFW; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.utils.ObjectLists; +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.rendering.gui.GuiManager; +import speiger.src.coreengine.rendering.gui.base.IKeyComponent; +import speiger.src.coreengine.rendering.gui.components.ButtonComponent; +import speiger.src.coreengine.rendering.gui.components.PieComponent; +import speiger.src.coreengine.rendering.gui.components.PieComponent.IPieIndex; +import speiger.src.coreengine.rendering.gui.components.PieComponent.PieIndex; +import speiger.src.coreengine.rendering.gui.components.TextComponent; +import speiger.src.coreengine.rendering.gui.components.WindowComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.DynamicConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.RelativeConstrain; +import speiger.src.coreengine.rendering.input.Keyboard; +import speiger.src.coreengine.utils.profiler.IProfiler; +import speiger.src.coreengine.utils.profiler.IProfiler.IProfilerEntry; +import speiger.src.coreengine.utils.profiler.IProfiler.ProfilerData; + +public class PieProfilerWindowComponent extends WindowComponent + implements Supplier>, IKeyComponent +{ + public static final DecimalFormat PERCENT_FORMAT = new DecimalFormat("##0.00"); + + PieComponent pie = new PieComponent(127, this); + ButtonComponent[] buttons = new ButtonComponent[3]; + TextComponent[] extraFeatures = new TextComponent[3]; + List entries = new ObjectArrayList(); + int textInUse = 0; + IProfiler profiler; + IProfilerEntry currentEntry; + String entryName; + List lastValues = null; + + public PieProfilerWindowComponent(float x, float y, float width, float height, String name) + { + super(x, y, width, height, DEFAULT_FLAGS, name); + } + + @Override + public boolean canMoveIntoForground() + { + return true; + } + + @Override + public void init() + { + super.init(); + addChild(pie.setAutoUpdate(true).setComponentPosition(0F, 5F).addChangeListener(minimizedListener), new ComponentConstrains(null, null, new ParentConstrain(), new DynamicConstrain(this::calculatePieHeight))); + buttons[0] = createButton(0, "Client"); + buttons[1] = createButton(1, "GPU"); + buttons[2] = createButton(2, "Server"); + extraFeatures[0] = new TextComponent(0F, 0F, 18F, 5.5F).setTextScale(0.3F).setText("[0] Back").setAlignment(Align.LEFT_TOP, Align.CENTER); + extraFeatures[0].addChangeListener(T -> T.setVisible(!isMinimized() && currentEntry != null && currentEntry.getParent() != null)); + addChild(extraFeatures[0], new ComponentConstrains(null, new DynamicConstrain(() -> pie.getBox().getBaseHeight() + (-5.5F)), new PixelConstrain(38).setInverted(), null)); + + extraFeatures[1] = new TextComponent(0F, 0F, 0F, 7F).setTextScale(0.4F).setText("Client Thread"); + addChild(extraFeatures[1].addChangeListener(minimizedListener), new ComponentConstrains(null, new PixelConstrain(8F), new ParentConstrain(), null)); + extraFeatures[2] = new TextComponent(0F, 0F, 0F, 6F).setTextScale(0.33F).setText("Client"); + addChild(extraFeatures[2].addChangeListener(minimizedListener), new ComponentConstrains(null, new PixelConstrain(15F), new ParentConstrain(), null)); + + } + + @Override + public void onClosed() + { + super.onClosed(); + if(profiler != null) + { + profiler.disable(); + profiler = null; + } + } + + public PieProfilerWindowComponent setProfiler(IProfiler profiler, String root) + { + if(this.profiler != null) + { + buttons[getProfilerIndex(this.profiler)].setEnabled(true); + this.profiler.disable(); + } + this.profiler = profiler; + if(profiler != null) + { + profiler.enable(); + setCurrentEntry(root); + buttons[getProfilerIndex(this.profiler)].setEnabled(false); + extraFeatures[1].setText(profiler.getName()); + } + return this; + } + + public PieProfilerWindowComponent setCurrentEntry(String name) + { + entryName = name; + currentEntry = profiler.getEntry(name); + lastValues = null; + extraFeatures[2].setText(currentEntry == null ? "Unknown" : currentEntry.getName()); + return this; + } + + @Override + protected boolean updateSelf(int mouseX, int mouseY, float particalTicks) + { + for(int i = 0,m=entries.size();i= GLFW.GLFW_KEY_1 && key <= GLFW.GLFW_KEY_9) + { + key -= GLFW.GLFW_KEY_1; + if(key < textInUse) + { + String s = entries.get(key)[0].getText(); + s = s.substring(s.indexOf("] ")+2); + if(!s.equalsIgnoreCase("Nameless")) + { + setCurrentEntry(currentEntry.getPathName() + "/" + s); + return true; + } + } + return false; + } + return false; + } + + @Override + public Vec2f getMinimumBounds() + { + return Vec2f.newVec(80F, 80F + (textInUse * 5.5F)); + } + + @Override + public List get() + { + if(lastValues != null) + { + List entries = new ObjectArrayList(); + for(int i = 0, m = lastValues.size();i < m;i++) + { + entries.add(new PieIndex(MathUtils.floor(lastValues.get(i).getEffect() * 1.28D), lastValues.get(i).getColor())); + } + return entries; + } + return ObjectLists.singleton(new PieIndex(pie.getMaxSteps(), ColorObject.LIGHT_BLUE)); + } + + protected float calculatePieHeight() + { + return getBox().getBaseHeight() - ((textInUse * 5.5F) + 9F); + } + + public IProfiler getProfiler(int index) + { + GuiManager manager = getGui().getUIManager(); + switch(index) + { + case 1: + return manager.getGPUProfiler(); + case 2: + return manager.getServerProfiler(); + default: + return manager.getCPUProfiler(); + } + } + + public int getProfilerIndex(IProfiler prof) + { + GuiManager manager = getGui().getUIManager(); + return manager.getGPUProfiler() == prof ? 1 : manager.getServerProfiler() == prof ? 2 : 0; + } + + public static String getRoot(int index) + { + return index < 2 ? "Client" : "Server"; + } + + protected TextComponent createComponent(int column) + { + final int index = entries.size(); + String text = column == 0 ? "[" + (entries.size() + 1) + "] Unknown" : PERCENT_FORMAT.format(0D) + "%"; + TextComponent comp = new TextComponent(0F, 0F, 18F, 5.5F).setTextScale(0.3F).setText(text).setAlignment(column == 0 ? Align.LEFT_TOP : Align.RIGHT_BOTTOM, Align.CENTER); + comp.addChangeListener(T -> T.setVisible(!isMinimized() && index < textInUse)); + Constrain xPos = column == 0 ? null : (column == 1 ? new PixelConstrain(38F).setInverted() : new PixelConstrain(19F).setInverted()); + addChild(comp, new ComponentConstrains(xPos, new DynamicConstrain(() -> pie.getBox().getBaseHeight() + (index * 5.5F)), column == 0 ? new PixelConstrain(38).setInverted() : null, null)); + return comp; + } + + protected ButtonComponent createButton(int index, String name) + { + ButtonComponent button = new ButtonComponent(name, ColorObject.GRAY); + button.getText().setTextScale(0.3F); + button.addChangeListener(minimizedListener).addUserActionListener(T -> setProfiler(getProfiler(index), getRoot(index))); + addChild(button, new ComponentConstrains(new RelativeConstrain(index * 0.3333F), new PixelConstrain(8F).setInverted(), new RelativeConstrain(0.3333F), new PixelConstrain(7F))); + return button; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/window/debug/TreeProfilerWindowComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/debug/TreeProfilerWindowComponent.java new file mode 100644 index 0000000..4c4c450 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/debug/TreeProfilerWindowComponent.java @@ -0,0 +1,184 @@ +package speiger.src.coreengine.rendering.gui.components.window.debug; + +import java.util.function.ObjIntConsumer; + +import speiger.src.collections.ints.queues.IntArrayFIFOQueue; +import speiger.src.collections.ints.queues.IntPriorityQueue; +import speiger.src.collections.ints.utils.IntPriorityQueues; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.rendering.gui.GuiManager; +import speiger.src.coreengine.rendering.gui.components.ButtonComponent; +import speiger.src.coreengine.rendering.gui.components.TreeComponent; +import speiger.src.coreengine.rendering.gui.components.WindowComponent; +import speiger.src.coreengine.rendering.gui.components.tree.ProfilerTreeEntry; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.RelativeConstrain; +import speiger.src.coreengine.utils.profiler.IProfiler; +import speiger.src.coreengine.utils.profiler.IProfiler.IProfilerEntry; + +public class TreeProfilerWindowComponent extends WindowComponent +{ + IntPriorityQueue todoList = IntPriorityQueues.synchronize(new IntArrayFIFOQueue()); + TreeComponent tree = new TreeComponent(ColorObject.GRAY, 9F).disableBackground(true).setSelectionMode(TreeComponent.SELECTION_MODE_INTERACT); + ButtonComponent[] buttons = new ButtonComponent[3]; + ObjIntConsumer listener = (T, V) -> todoList.enqueue(V); + IProfiler profiler; + String root; + + public TreeProfilerWindowComponent(float x, float y, float width, float height, String name) + { + super(x, y, width, height, DEFAULT_FLAGS, name); + } + + @Override + public boolean canMoveIntoForground() + { + return true; + } + + @Override + public void init() + { + super.init(); + addChild(tree.addChangeListener(minimizedListener), new ComponentConstrains(new PixelConstrain(2F), new PixelConstrain(getMinimizedY()), new ParentConstrain(1.5F), new PixelConstrain(getMinimizedY() + 8.5F).setInverted())); + buttons[0] = createButton(0, "Client"); + buttons[1] = createButton(1, "GPU"); + buttons[2] = createButton(2, "Server"); + } + + @Override + protected boolean fixedUpdateSelf() + { + while(!todoList.isEmpty()) + { + int index = todoList.dequeue(); + switch(index) + { + case 0: + addEntries(profiler.getEntry(root)); + break; + case 1: + tree.setTree(null); + break; + case 2: + if(tree.getTree() == null) + { + addEntries(profiler.getEntry(root)); + break; + } + updateChildren(profiler.getEntry(root), tree.getTree()); + tree.onTreeChanged(); + break; + } + } + return true; + } + + public TreeProfilerWindowComponent setProfiler(IProfiler profiler, String root) + { + if(this.profiler != null) + { + buttons[getProfilerIndex(this.profiler)].setEnabled(true); + this.profiler.removeListener(listener); + this.profiler.disable(); + tree.setTree(null); + } + this.profiler = profiler; + this.root = root; + if(this.profiler != null) + { + buttons[getProfilerIndex(this.profiler)].setEnabled(false); + this.profiler.enable(); + this.profiler.addListener(listener); + addEntries(this.profiler.getEntry(this.root)); + } + return this; + } + + protected void addEntries(IProfilerEntry entry) + { + if(entry == null) + { + return; + } + ProfilerTreeEntry child = new ProfilerTreeEntry(entry); + for(int i = 0,m=entry.getChildCount();i setProfiler(getProfiler(index), PieProfilerWindowComponent.getRoot(index))); + addChild(button, new ComponentConstrains(new RelativeConstrain(index * 0.3333F), new PixelConstrain(8F).setInverted(), new RelativeConstrain(0.3333F), new PixelConstrain(7F))); + return button; + } + + @Override + public Vec2f getMinimumBounds() + { + return Vec2f.newVec(100F, 50F); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/ChoiceComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/ChoiceComponent.java new file mode 100644 index 0000000..71781b3 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/ChoiceComponent.java @@ -0,0 +1,60 @@ +package speiger.src.coreengine.rendering.gui.components.window.misc; + +import speiger.src.collections.booleans.functions.BooleanConsumer; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.components.ButtonComponent; +import speiger.src.coreengine.rendering.gui.components.TextComponent; +import speiger.src.coreengine.rendering.gui.components.WindowComponent; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.RelativeConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.TextConstrain; + +public class ChoiceComponent extends WindowComponent +{ + TextComponent message = new TextComponent().setLimitedHeight(false).setTextScale(0.5F); + ButtonComponent yesButton = new ButtonComponent("Yes", ColorObject.GRAY); + ButtonComponent noButton = new ButtonComponent("No", ColorObject.GRAY); + BooleanConsumer listener; + + public ChoiceComponent(float width, String windowTitle, String message, BooleanConsumer listener) + { + this(0F, 0F, width, windowTitle, message, listener); + } + + public ChoiceComponent(float x, float y, float width, String windowTitle, String message, BooleanConsumer listener) + { + super(x, y, width, 25F, FIXED_SIZE_POPUP, windowTitle); + this.message.setText(message); + this.listener = listener; + } + + public static ChoiceComponent createChoice(float width, String windowTitle, String message, BooleanConsumer listener) + { + return new ChoiceComponent(width, windowTitle, message, listener); + } + + public static ChoiceComponent createChoice(String windowTitle, String message, BooleanConsumer listener) + { + return new ChoiceComponent(150, windowTitle, message, listener); + } + + @Override + public void init() + { + super.init(); + yesButton.getText().setTextScale(0.5F); + noButton.getText().setTextScale(0.5F); + addChild(yesButton.addChangeListener(minimizedListener).addUserActionListener(closeListener).addUserActionListener(T -> listener.accept(true)), new ComponentConstrains(new RelativeConstrain(0F), new ParentConstrain(10F).invert(), new RelativeConstrain(0.5F), new PixelConstrain(10F))); + addChild(noButton.addChangeListener(minimizedListener).addUserActionListener(closeListener).addUserActionListener(T -> listener.accept(false)), new ComponentConstrains(new RelativeConstrain(0.5F), new ParentConstrain(10F).invert(), new RelativeConstrain(0.5F), new PixelConstrain(10F))); + addChild(message, new ComponentConstrains(new PixelConstrain(10F), new PixelConstrain(11F), new ParentConstrain(10F), TextConstrain.height(message))); + getBox().setHeight(25F + message.getMetadata().getMaxHeight()); + } + + @Override + public boolean isPopup() + { + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/MessageComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/MessageComponent.java new file mode 100644 index 0000000..ab7794d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/MessageComponent.java @@ -0,0 +1,74 @@ +package speiger.src.coreengine.rendering.gui.components.window.misc; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.components.ButtonComponent; +import speiger.src.coreengine.rendering.gui.components.TextComponent; +import speiger.src.coreengine.rendering.gui.components.WindowComponent; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.TextConstrain; + +public class MessageComponent extends WindowComponent +{ + TextComponent message = new TextComponent().setLimitedHeight(false).setTextScale(0.5F); + ButtonComponent resultButton = new ButtonComponent("", ColorObject.GRAY); + + public MessageComponent(float width, String windowTitle, String confirmButton, String message) + { + this(0F, 0F, width, windowTitle, confirmButton, message); + } + + public MessageComponent(float x, float y, float width, String windowTitle, String confirmButton, String message) + { + super(x, y, width, 25F, FIXED_SIZE_POPUP, windowTitle); + resultButton.getText().setText(confirmButton); + this.message.setText(message); + } + + public static MessageComponent createInfo(String message) + { + return new MessageComponent(150, "Info", "Ok", message); + } + + public static MessageComponent createInfo(float width, String message) + { + return new MessageComponent(width, "Info", "Ok", message); + } + + public static MessageComponent createWarn(String message) + { + return new MessageComponent(150, "Warn", "Ok", message); + } + + public static MessageComponent createWarn(float width, String message) + { + return new MessageComponent(width, "Warn", "Ok", message); + } + + public static MessageComponent createError(String message) + { + return new MessageComponent(150, "Error!", "Ok", message); + } + + public static MessageComponent createError(float width, String message) + { + return new MessageComponent(width, "Error!", "Ok", message); + } + + @Override + public void init() + { + super.init(); + resultButton.getText().setTextScale(0.5F); + addChild(resultButton.addChangeListener(minimizedListener).addUserActionListener(closeListener), new ComponentConstrains(new ParentConstrain(), new ParentConstrain(10F).invert(), new ParentConstrain(), new PixelConstrain(10F))); + addChild(message, new ComponentConstrains(new PixelConstrain(10F), new PixelConstrain(11F), new ParentConstrain(10F), TextConstrain.height(message))); + getBox().setHeight(25F + message.getMetadata().getMaxHeight()); + } + + @Override + public boolean isPopup() + { + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/TextInputComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/TextInputComponent.java new file mode 100644 index 0000000..9f53d54 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/TextInputComponent.java @@ -0,0 +1,61 @@ +package speiger.src.coreengine.rendering.gui.components.window.misc; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.components.ButtonComponent; +import speiger.src.coreengine.rendering.gui.components.TextComponent; +import speiger.src.coreengine.rendering.gui.components.TextFieldComponent; +import speiger.src.coreengine.rendering.gui.components.WindowComponent; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.RelativeConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.TextConstrain; + +public class TextInputComponent extends WindowComponent +{ + TextComponent message = new TextComponent().setLimitedHeight(false).setTextScale(0.5F); + TextFieldComponent input = new TextFieldComponent(ColorObject.GRAY); + ButtonComponent confirm = new ButtonComponent("Confirm", ColorObject.DARK_GREEN); + ButtonComponent cancel = new ButtonComponent("Cancel", ColorObject.RED); + + public TextInputComponent(float width, String name, String message) + { + this(0F, 0F, width, name, message); + } + + public TextInputComponent(float x, float y, float width, String name, String message) + { + super(x, y, width, 35F, FIXED_SIZE_POPUP, name); + this.message.setText(message); + } + + public TextFieldComponent getInput() + { + return input; + } + + public String getText() + { + return input.getText(); + } + + @Override + public void init() + { + super.init(); + confirm.getText().setTextScale(0.5F); + cancel.getText().setTextScale(0.5F); + input.getRawText().setTextScale(0.5F); + addChild(confirm.addChangeListener(minimizedListener).addUserActionListener((T) -> notifyListeners(LISTENER_USER_ACTION)).addUserActionListener(closeListener), new ComponentConstrains(new RelativeConstrain(0F), new ParentConstrain(10F).invert(), new RelativeConstrain(0.5F), new PixelConstrain(10F))); + addChild(cancel.addChangeListener(minimizedListener).addUserActionListener(closeListener), new ComponentConstrains(new RelativeConstrain(0.5F), new ParentConstrain(10F).invert(), new RelativeConstrain(0.5F), new PixelConstrain(10F))); + addChild(message, new ComponentConstrains(new PixelConstrain(10F), new PixelConstrain(11F), new ParentConstrain(10F), TextConstrain.height(message))); + addChild(input, new ComponentConstrains(new PixelConstrain(10F), TextConstrain.height(message).setPadding(15F), new ParentConstrain(10F), new PixelConstrain(12F))); + getBox().setHeight(45F + message.getMetadata().getMaxHeight()); + } + + @Override + public boolean isPopup() + { + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/Align.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/Align.java new file mode 100644 index 0000000..aa6865b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/Align.java @@ -0,0 +1,19 @@ +package speiger.src.coreengine.rendering.gui.helper; + +public enum Align +{ + LEFT_TOP, + CENTER, + RIGHT_BOTTOM; + + public float align(float containerSize, float objectSize) + { + switch(this) + { + case LEFT_TOP: return 0F; + case CENTER: return (containerSize / 2F) - (objectSize / 2F); + case RIGHT_BOTTOM: return containerSize - objectSize; + } + return 0F; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/TextFilter.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/TextFilter.java new file mode 100644 index 0000000..3d59c8d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/TextFilter.java @@ -0,0 +1,24 @@ +package speiger.src.coreengine.rendering.gui.helper; + +import java.util.function.Predicate; + +public class TextFilter +{ + public static final Predicate INTEGER_ONLY = T -> { + try { + if(T == null || T.isEmpty()); + else Integer.parseInt(T); + return true; + } + catch(Exception e) { return false; } + }; + + public static final Predicate FLOAT_ONLY = T -> { + try { + if(T == null || T.isEmpty()); + else Float.parseFloat(T); + return true; + } + catch(Exception e) { return false; } + }; +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/UIShapes.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/UIShapes.java new file mode 100644 index 0000000..3ac05a8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/UIShapes.java @@ -0,0 +1,175 @@ +package speiger.src.coreengine.rendering.gui.helper; + +import org.lwjgl.opengl.GL11; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.misc.Facing; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.gui.renderer.buffer.RenderBuffer; +import speiger.src.coreengine.rendering.tesselation.Tesselator; +import speiger.src.coreengine.rendering.tesselation.VertexType; + +public class UIShapes +{ + public static void createCross(RenderBuffer buffer, float width, float height, float paddingA, float paddingB, ColorObject color) + { + createCross(buffer.start(GL11.GL_TRIANGLES, VertexType.UI), width, height, paddingA, paddingB, color); + buffer.finishShape(0); + } + + public static void createCross(Tesselator tes, float width, float height, float paddingA, float paddingB, ColorObject color) + { + tes.setOffset(-(width * 0.5F), -(height * 0.5F), 0F); + tes.pos(paddingB, paddingA, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(paddingA, paddingB, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width - paddingB, height - paddingA, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width - paddingA, height - paddingB, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(paddingB, paddingA, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width - paddingB, height - paddingA, 0F).tex(0F, 0F).color4f(color).endVertex(); + + tes.pos(width - paddingB, paddingA, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width - paddingA, paddingB, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(paddingB, height - paddingA, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(paddingA, height - paddingB, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width - paddingB, paddingA, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(paddingB, height - paddingA, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.setOffset(0F, 0F, 0F); + } + + public static void createCross(UIRenderer renderer, float width, float height, float paddingA, float paddingB, ColorObject color) + { + renderer.translate(-(width * 0.5F), -(height * 0.5F)); + renderer.startCustomShape(GL11.GL_TRIANGLES, false); + renderer.pos(paddingB, paddingA, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(paddingA, paddingB, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width - paddingB, height - paddingA, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width - paddingA, height - paddingB, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(paddingB, paddingA, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width - paddingB, height - paddingA, 0F).tex(0F, 0F).color(color).endVertex(); + + renderer.pos(width - paddingB, paddingA, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width - paddingA, paddingB, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(paddingB, height - paddingA, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(paddingA, height - paddingB, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width - paddingB, paddingA, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(paddingB, height - paddingA, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.translate((width * 0.5F), (height * 0.5F)); + } + + public static void createArrow(RenderBuffer buffer, float width, float height, ColorObject color, Facing dir) + { + createArrow(buffer.start(GL11.GL_TRIANGLES, VertexType.UI), width, height, color, dir); + buffer.finishShape(0); + } + + public static void createArrow(Tesselator tes, float width, float height, ColorObject color, Facing dir) + { + float halfWidth = width * 0.5F; + float halfHeight = height * 0.5F; + tes.setOffset(-halfWidth, -halfHeight, 0F); + switch(dir) + { + case NORTH: + tes.pos(0F, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + break; + case SOUTH: + tes.pos(0F, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + break; + case EAST: + tes.pos(0F, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + break; + case WEST: + tes.pos(halfWidth, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + break; + } + tes.setOffset(0F, 0F, 0F); + } + + public static void createArrow(UIRenderer renderer, float width, float height, ColorObject color, Facing dir) + { + float halfWidth = width * 0.5F; + float halfHeight = height * 0.5F; + renderer.translate(-halfWidth, -halfHeight, 0F); + renderer.startCustomShape(GL11.GL_TRIANGLES, false); + switch(dir) + { + case NORTH: + renderer.pos(0F, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, height, 0F).tex(0F, 0F).color(color).endVertex(); + break; + case SOUTH: + renderer.pos(0F, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, height, 0F).tex(0F, 0F).color(color).endVertex(); + break; + case EAST: + renderer.pos(0F, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + break; + case WEST: + renderer.pos(halfWidth, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + break; + } + renderer.translate(halfWidth, halfHeight, 0F); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/Animation.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/Animation.java new file mode 100644 index 0000000..5198d4a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/Animation.java @@ -0,0 +1,46 @@ +package speiger.src.coreengine.rendering.gui.helper.animations; + +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; +import speiger.src.coreengine.math.value.IValue; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.animations.transitions.ITransition; + +public class Animation +{ + Object2ObjectMap transitions = new Object2ObjectLinkedOpenHashMap(); + AnimationListener listener; + float duration = 0F; + + public Animation add(AnimationTarget target, ITransition transition) + { + transitions.put(target, transition); + duration += transition.getDuration(); + return this; + } + + public Animation addListener(AnimationListener listener) + { + this.listener = listener; + return this; + } + + public AnimationInstance createInstance(GuiComponent comp, AnimationInstance old, float delay, boolean reverse) + { + Object2ObjectMap values = new Object2ObjectLinkedOpenHashMap(); + for(Object2ObjectMap.Entry entry : Object2ObjectMaps.fastIterable(transitions)) + { + AnimationTarget target = entry.getKey(); + float base = target.getBase(comp); + values.put(target, entry.getValue().initValue(base, old != null ? old.getCurrentProgress(target) : (reverse ? entry.getValue().getHiddenValue() : base), reverse, delay, duration)); + } + return new AnimationInstance(values, duration + delay, reverse, listener); + } + + @FunctionalInterface + public static interface AnimationListener + { + public void onAnimationFinished(Animation instance, GuiComponent owner, boolean reverse); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/AnimationInstance.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/AnimationInstance.java new file mode 100644 index 0000000..cc4cec2 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/AnimationInstance.java @@ -0,0 +1,53 @@ +package speiger.src.coreengine.rendering.gui.helper.animations; + +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; +import speiger.src.coreengine.math.value.ConstantValue; +import speiger.src.coreengine.math.value.IValue; +import speiger.src.coreengine.rendering.gui.helper.animations.Animation.AnimationListener; + +public class AnimationInstance +{ + Object2ObjectMap values; + float totalProgress; + boolean reverse; + AnimationListener listener; + float progress = 0F; + + public AnimationInstance(Object2ObjectMap values, float totalProgress, boolean reverse, AnimationListener listener) + { + this.values = values; + this.totalProgress = totalProgress; + this.reverse = reverse; + this.listener = listener; + } + + public void update(Animator animator, float particalTime) + { + progress += particalTime; + for(Object2ObjectMap.Entry entry : Object2ObjectMaps.fastIterable(values)) + { + entry.getKey().apply(animator, entry.getValue().update(particalTime)); + } + } + + public float getCurrentProgress(AnimationTarget target) + { + return values.getOrDefault(target, ConstantValue.ZERO).get(); + } + + public boolean isDone() + { + return reverse && progress >= totalProgress; + } + + public AnimationListener getListener() + { + return listener; + } + + public boolean isReverse() + { + return reverse; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/AnimationTarget.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/AnimationTarget.java new file mode 100644 index 0000000..554d627 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/AnimationTarget.java @@ -0,0 +1,52 @@ +package speiger.src.coreengine.rendering.gui.helper.animations; + +import speiger.src.coreengine.rendering.gui.GuiComponent; + +public enum AnimationTarget +{ + XPOS, + YPOS, + WIDTH, + HEIGHT, + SCALE, + VISIBILITY; + + public void apply(Animator animator, float amount) + { + switch(this) + { + case XPOS: + animator.modifyX(amount); + break; + case YPOS: + animator.modifyY(amount); + break; + case HEIGHT: + animator.modifyHeight(amount); + break; + case WIDTH: + animator.modifyWidth(amount); + break; + case SCALE: + animator.modifyScale(amount); + break; + case VISIBILITY: + animator.modifyVisibility(amount); + break; + } + } + + public float getBase(GuiComponent comp) + { + switch(this) + { + case XPOS: return 0; + case YPOS: return 0; + case WIDTH: return 0; + case HEIGHT: return 0; + case SCALE: return 1; + case VISIBILITY: return 1; + } + return 0F; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/Animator.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/Animator.java new file mode 100644 index 0000000..2eabce8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/Animator.java @@ -0,0 +1,178 @@ +package speiger.src.coreengine.rendering.gui.helper.animations; + +import java.util.Iterator; +import java.util.List; +import java.util.function.BiConsumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.animations.Animation.AnimationListener; + +public class Animator +{ + GuiComponent owner; + List> listeners = new ObjectArrayList>(); + float xMod = 0F; + float yMod = 0F; + float widthMod = 0F; + float heightMod = 0F; + float scaleMod = 1F; + float visibilityMod = 1F; + + + boolean changed = false; + boolean redraw = false; + + Object2ObjectMap animations = new Object2ObjectLinkedOpenHashMap(); + + public Animator(GuiComponent owner) + { + this.owner = owner; + } + + public void addAnimation(Animation animation, boolean reverse, float delay) + { + AnimationInstance old = animations.remove(animation); + animations.put(animation, animation.createInstance(owner, old, delay, reverse)); + } + + public boolean isPlayingAnimation(Animation animation) + { + return animations.containsKey(animation); + } + + public void update(float particalTicks) + { + if(listeners.size() > 0) + { + for(int i = 0,m=listeners.size();i> iter = Object2ObjectMaps.fastIterator(animations);iter.hasNext();) + { + Object2ObjectMap.Entry animation = iter.next(); + AnimationInstance instance = animation.getValue(); + instance.update(this, particalTicks); + if(instance.isDone()) + { + AnimationListener listener = instance.getListener(); + if(listener != null) + { + listener.onAnimationFinished(animation.getKey(), owner, instance.isReverse()); + } + iter.remove(); + } + } + applyValues(true); + if(changed) + { + owner.onComponentChanged(redraw); + } + owner.finishMassChanging(); + } + + private void resetValues() + { + if(!changed) + { + return; + } + if(!owner.hasConstraints()) + { + owner.changeVisibility(1F / visibilityMod).getBox().move(-xMod, -yMod).grow(-widthMod, -heightMod).scale(1F / scaleMod); + } + else + { + owner.changeVisibility(1F / visibilityMod).getBox().scale(1F / scaleMod); + } + xMod = 0F; + yMod = 0F; + widthMod = 0F; + heightMod = 0F; + scaleMod = 1F; + visibilityMod = 1F; + changed = false; + redraw = false; + } + + public void applyValues(boolean internal) + { + if(!changed || (internal && owner.hasConstraints())) + { + return; + } + owner.changeVisibility(visibilityMod).getBox().scale(scaleMod).move(xMod, yMod).grow(widthMod, heightMod); + } + + + public void modifyX(float value) + { + if(value == 0F) + { + return; + } + xMod += value; + changed = true; + } + + public void modifyY(float value) + { + if(value == 0F) + { + return; + } + yMod += value; + changed = true; + } + + public void modifyWidth(float value) + { + if(value == 0F) + { + return; + } + widthMod += value; + changed = true; + redraw = true; + } + + public void modifyHeight(float value) + { + if(value == 0F) + { + return; + } + heightMod += value; + changed = true; + redraw = true; + } + + public void modifyScale(float value) + { + if(value == 1F) + { + return; + } + scaleMod *= value; + changed = true; + redraw = true; + } + + public void modifyVisibility(float value) + { + visibilityMod *= value; + changed = true; + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/transitions/ITransition.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/transitions/ITransition.java new file mode 100644 index 0000000..e8edc48 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/transitions/ITransition.java @@ -0,0 +1,10 @@ +package speiger.src.coreengine.rendering.gui.helper.animations.transitions; + +import speiger.src.coreengine.math.value.IValue; + +public interface ITransition +{ + public IValue initValue(float baseValue, float currentValue, boolean reverse, float delay, float totalDuration); + public float getDuration(); + public float getHiddenValue(); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/transitions/LiniarTransition.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/transitions/LiniarTransition.java new file mode 100644 index 0000000..214bab6 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/transitions/LiniarTransition.java @@ -0,0 +1,45 @@ +package speiger.src.coreengine.rendering.gui.helper.animations.transitions; + +import speiger.src.coreengine.math.value.IValue; +import speiger.src.coreengine.math.value.LiniarValue; + +public class LiniarTransition implements ITransition +{ + float offsetValue; + float duration; + float selfDelay; + + public LiniarTransition(float offset, float duration) + { + offsetValue = offset; + this.duration = duration; + selfDelay = 0; + } + + public LiniarTransition(float offset, float duration, float selfDelay) { + offsetValue = offset; + this.duration = duration; + this.selfDelay = selfDelay; + } + + @Override + public IValue initValue(float baseValue, float currentValue, boolean reverse, float delay, float totalDuration) + { + float target = reverse ? baseValue : offsetValue; + float selfDelay = reverse ? totalDuration - (this.selfDelay + duration) : this.selfDelay; + return new LiniarValue(delay + selfDelay, duration, currentValue, target); + } + + @Override + public float getHiddenValue() + { + return offsetValue; + } + + @Override + public float getDuration() + { + return duration + selfDelay; + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/GuiBox.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/GuiBox.java new file mode 100644 index 0000000..db50bca --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/GuiBox.java @@ -0,0 +1,316 @@ +package speiger.src.coreengine.rendering.gui.helper.box; + +import java.util.List; + +import speiger.src.collections.objects.lists.ObjectArrayList; + +public class GuiBox implements IGuiBox +{ + List children = new ObjectArrayList(); + IGuiBox parent; + + float minX; + float minY; + float maxX; + float maxY; + + float width; + float height; + float scale = 1F; + + float baseX; + float baseY; + float baseWidth; + float baseHeight; + float baseScale = 1F; + + + protected GuiBox() + { + } + + public GuiBox(float x, float y, float width, float height) + { + set(x, y, width, height); + onChanged(); + } + + public static IGuiBox createSimpleBox(float x, float y, float width, float height) + { + return new GuiBox(x, y, width, height); + } + + public static IGuiBox createParentBox(IGuiBox box) + { + return new GuiBox(box.getMinX(), box.getMinY(), box.getWidth(), box.getHeight()); + } + + public static IGuiBox createParentBox(IGuiBox box, float padding) + { + return new GuiBox(box.getMinX() + padding, box.getMinY() + padding, box.getWidth() - (padding * 2F), box.getHeight() - (padding * 2F)); + } + + @Override + public IGuiBox addChild(IGuiBox box) + { + children.add(box); + box.setParent(this); + return this; + } + + @Override + public IGuiBox removeChild(IGuiBox box) + { + if(children.remove(box)) + { + box.setParent(null); + } + return this; + } + + @Override + public IGuiBox clearChildren() + { + for(int i = 0,m=children.size();i= x && getMinY() <= y && getMaxY() >= y; + } + + public default FacingList getColidingBorder(float x, float y, float margin) + { + margin *= getScale(); + float minX = getMinX(); + float maxX = getMaxX(); + float minY = getMinY(); + float maxY = getMaxY(); + FacingList list = FacingList.EMPTY; + if(y <= minY + margin && y >= minY) list = list.add(Facing.NORTH); + if(x >= maxX - margin && x <= maxX) list = list.add(Facing.EAST); + if(y >= maxY - margin && y <= maxY) list = list.add(Facing.SOUTH); + if(x <= minX + margin && x >= minX) list = list.add(Facing.WEST); + return list; + } + + public default boolean isIntersecting(IGuiBox box){return isIntersecting(box.getMinX(), box.getMaxX(), box.getMinY(), box.getMaxY());} + public default boolean isIntersecting(float minX, float maxX, float minY, float maxY) + { + float xMin = getMinX(); + float yMin = getMinY(); + float xMax = getMaxX(); + float yMax = getMaxY(); + return ((minX >= xMin && minX <= xMax) || (maxX >= xMin && maxX <= xMax)) && ((minY >= yMin && minY <= yMax) || (maxY >= yMin && maxY <= yMax)); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/IScreenBox.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/IScreenBox.java new file mode 100644 index 0000000..c8ad501 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/IScreenBox.java @@ -0,0 +1,22 @@ +package speiger.src.coreengine.rendering.gui.helper.box; + +public interface IScreenBox +{ + public float getBaseScale(); + + public float getBaseWidth(); + + public default float getSquaredBaseWidth() + { + float value = getBaseWidth(); + return value * value; + } + + public float getBaseHeight(); + + public default float getSquaredBaseHeight() + { + float value = getBaseWidth(); + return value * value; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/ParentBox.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/ParentBox.java new file mode 100644 index 0000000..d19a2d7 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/ParentBox.java @@ -0,0 +1,276 @@ +package speiger.src.coreengine.rendering.gui.helper.box; + +import java.util.List; + +import speiger.src.collections.objects.lists.ObjectArrayList; + +public class ParentBox implements IGuiBox +{ + List children = new ObjectArrayList(); + IGuiBox parent; + + float minX; + float minY; + float maxX; + float maxY; + + public ParentBox(float value) + { + minX = value; + minY = value; + maxX = -value; + maxY = -value; + } + + public ParentBox(float paddingTop, float paddingLeft, float paddingBottom, float paddingRight) + { + minX = paddingLeft; + minY = paddingTop; + maxX = paddingRight; + maxY = paddingBottom; + } + + @Override + public IGuiBox addChild(IGuiBox box) + { + children.add(box); + box.setParent(this); + return this; + } + + @Override + public IGuiBox removeChild(IGuiBox box) + { + if(children.remove(box)) + { + box.setParent(null); + } + return this; + } + + @Override + public IGuiBox clearChildren() + { + for(int i = 0,m=children.size();i extends Constrain +{ + public static final FloatSupplier DEFAULT = () -> 0F; + List components; + FloatSupplier offset = DEFAULT; + float padding = 0F; + + public MenuConstrain(List components) + { + this.components = components; + } + + public MenuConstrain(List components, FloatSupplier offset) + { + this.components = components; + this.offset = offset; + } + + public MenuConstrain setPadding(float padding) + { + this.padding = padding; + return this; + } + + @Override + public void apply() + { + boolean vertical = target == ConstrainTarget.Y_POS; + if(!target.isPosition()) + { + float max = 0F; + for(int i = 0;i[] chars; + final ITexture texture; + final float height; + final float baseLine; + final float space; + + public FontRenderer(Char2ObjectMap[] chars, ITexture texture, float height, float baseLine) + { + this.chars = chars; + this.texture = texture; + this.height = height; + this.baseLine = baseLine; + space = chars[0].get(' ').getXAdvance(); + } + + @Override + public CharInstance getInstance(char letter, boolean isBold) + { + return chars[isBold ? 1 : 0].get(letter); + } + + @Override + public float getFontHeight() + { + return height; + } + + @Override + public float getBaseLine() + { + return baseLine; + } + + @Override + public ITexture getTexture() + { + return texture; + } + + public List renderText(String text, float x, float y, float z) + { + if(text.isEmpty()) return ObjectLists.empty(); + List drawCalls = new ObjectArrayList<>(); + TextContext context = new TextContext(1F, 0); + List lines = lexer.evaluateLines(text, context, Float.MAX_VALUE); + if(lines.isEmpty()) return ObjectLists.empty(); + WordContext effects = context.getEffect(); + ColorObject textColor = new ColorObject(effects.color); + float yOffset = 0F; + IVertexBuilder builder = new TranslatedVertexBuilder(bufferBuilder, x, y, z, 1F); + bufferBuilder.begin(GL11.GL_TRIANGLES, VertexType.IN_WORLD_UI); + for(int i = 0,m=lines.size();i 0) + { + drawCalls.add(bufferBuilder.getDrawCall(texture.getTextureID())); + } + bufferBuilder.setOffset(0F, 0F, 0F); + return drawCalls; + } + + @Override + public void updateText(TextComponent component) + { + RenderBuffer buffer = component.getBuffer(); + buffer.clear(); + if(component.getText().isEmpty()) + { + return; + } + TextContext context = new TextContext(component); + float boxWidth = component.getBox().getWidth(); + float boxHeight = component.getBox().getHeight(); + List lines = lexer.evaluateLines(component.getText(), context, component.isWidthLimited() ? boxWidth : Float.MAX_VALUE); + if(lines.isEmpty()) + { + return; + } + bufferBuilder.begin(GL11.GL_TRIANGLES, VertexType.UI); + int maxLanes = component.isHeightLimited() ? Math.min((int)(boxHeight / (height * context.getScale())), lines.size()) : lines.size(); + float maxHeight = maxLanes * height * context.getScale(); + float maxWidth = 0F; + float yOffset = component.getVertical().align(boxHeight, maxHeight); + float startX = component.getHorizontal().align(boxWidth, lines.get(0).getWidth()); + component.getMetadata().setStart(startX, yOffset); + WordContext effects = context.getEffect(); + while(effects.isNext(0)) + { + effects = effects.next(); + } + ColorObject textColor = new ColorObject(effects.color); + Float strikeThrough = effects.strike_through ? startX : null; + Float underline = effects.underline ? startX : -1F; + if(component.getBackgroundColor() != null) + { + Tesselator tes = buffer.start(GL11.GL_TRIANGLES, VertexType.UI).setOffset(0F, 0F, -0.001F); + addBackground(tes, lines, maxLanes, yOffset, component.getHorizontal(), boxWidth, component.getBackgroundColor(), false); + tes.setOffset(0F, 0F, 0F); + buffer.finishShape(0); + } + for(int i = 0, index = 0;i < maxLanes;i++) + { + float xOffset = component.getHorizontal().align(boxWidth, lines.get(i).getWidth()); + maxWidth = Math.max(maxWidth, lines.get(i).getWidth()); + strikeThrough = effects.strike_through ? xOffset : null; + underline = effects.underline ? xOffset : null; + for(CharInstance letter : lines.get(i).letterIterator()) + { + xOffset += renderChar(letter, xOffset, yOffset, context.getScale(), effects.italic, effects.flipped, textColor, bufferBuilder, false); + if(effects.isNext(++index)) + { + WordContext next = effects.next(); + Float newStrike = getValue(effects.strike_through, next.strike_through, xOffset, strikeThrough); + if(newStrike == null && strikeThrough != null) + { + addStrikeThrough(strikeThrough, xOffset - strikeThrough, yOffset, textColor, lineBuffer); + } + strikeThrough = newStrike; + Float newLine = getValue(effects.underline, next.underline, xOffset, strikeThrough); + if(newLine == null && underline != null) + { + addUnderline(underline, xOffset - underline, yOffset, textColor, lineBuffer, false); + } + textColor.setRGB(next.color); + underline = newLine; + effects = next; + } + } + if(strikeThrough != null) + { + addStrikeThrough(strikeThrough, xOffset - strikeThrough, yOffset, textColor, lineBuffer); + } + if(underline != null) + { + addUnderline(underline, xOffset - underline, yOffset, textColor, lineBuffer, false); + } + yOffset += height * context.getScale(); + component.getMetadata().addLine(lines.get(i)); + } + maxWidth /= 2; + buffer.finishShape(texture.getTextureID(), bufferBuilder); + if(lineBuffer.hasData()) + { + Tesselator tes = buffer.start(GL11.GL_TRIANGLES, VertexType.UI).offset(0F, 0F, 0.001F); + lineBuffer.merge(tes); + tes.setOffset(0F, 0F, 0F); + buffer.finishShape(0); + } + } + + protected float renderChar(CharInstance instance, float x, float y, float scale, float italic, boolean flipped, ColorObject color, IVertexBuilder buffer, boolean flipPos) + { + switch(instance.getCharacter()) + { + case TAB: + return space * 4 * scale; + case SPACE: + return space * scale; + } + if(instance.getXAdvance() <= 0F) + { + return 0F; + } + if(flipPos) flipped = !flipped; + float minX = x; + float minY = y; + float maxX = x + (instance.getWidth() * scale); + float maxY = flipPos ? y - (instance.getHeight() * scale) : y + (instance.getHeight() * scale); + float minV = flipped ? instance.getMaxV() : instance.getMinV(); + float maxV = flipped ? instance.getMinV() : instance.getMaxV(); + buffer.pos(minX - italic, maxY, 0F).tex(instance.getMinU(), maxV).color4f(color).endVertex(); + buffer.pos(minX + italic, minY, 0F).tex(instance.getMinU(), minV).color4f(color).endVertex(); + buffer.pos(maxX - italic, maxY, 0F).tex(instance.getMaxU(), maxV).color4f(color).endVertex(); + buffer.pos(maxX - italic, maxY, 0F).tex(instance.getMaxU(), maxV).color4f(color).endVertex(); + buffer.pos(minX + italic, minY, 0F).tex(instance.getMinU(), minV).color4f(color).endVertex(); + buffer.pos(maxX + italic, minY, 0F).tex(instance.getMaxU(), minV).color4f(color).endVertex(); + return instance.getXAdvance() * scale; + } + + protected void addBackground(IVertexBuilder tes, List lines, int maxLines, float yPos, Align align, float width, ColorObject color, boolean flipPos) + { + for(int i = 0;i < maxLines;i++) + { + float lineWidth = lines.get(i).getWidth(); + float xOffset = align.align(width, lineWidth); + float maxY = flipPos ? yPos - height : yPos + height; + tes.pos(xOffset, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(xOffset, yPos, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(xOffset + lineWidth, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(xOffset + lineWidth, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(xOffset, yPos, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(xOffset + lineWidth, yPos, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + yPos = maxY; + } + } + + protected void addUnderline(float xStart, float width, float yStart, ColorObject color, IVertexBuilder buffer, boolean flipPos) + { + float minY = yStart + baseLine + 0.5F; + float maxY = yStart + baseLine + 1.5F; + if(flipPos) + { + minY = yStart - baseLine - 0.5F; + maxY = yStart - baseLine - 1.5F; + } + buffer.pos(xStart, maxY, 0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart, minY, 0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart + width, maxY, 0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart + width, maxY, 0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart, minY, 0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart + width, minY, 0F).tex(0F, 0F).color4f(color).endVertex(); + } + + protected void addStrikeThrough(float xStart, float width, float yStart, ColorObject color, IVertexBuilder buffer) + { + float minY = yStart + height / 2.0F; + float maxY = yStart + height / 2.0F + 1.4F; + buffer.pos(xStart, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart, minY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart + width, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart + width, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart, minY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart + width, minY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + } + + protected Float getValue(boolean oldValue, boolean newValue, Float position, Float oldPosition) + { + if((position == null && oldPosition == null) || (position != null && oldPosition != null)) return oldPosition; + return newValue ? position : null; + } + + @Override + public String trimStringToWidth(String text, float limit, boolean reverse) + { + StringBuilder builder = new StringBuilder(); + float width = 0F; + int start = reverse ? text.length() - 1 : 0; + int direction = reverse ? -1 : 1; + for(int i = start;i >= 0 && i < text.length() && width < limit;i += direction) + { + char letter = text.charAt(i); + width += getCharLength(letter); + if(width > limit) break; + if(reverse) builder.insert(0, letter); + else builder.append(letter); + } + return builder.toString(); + } + + @Override + public float getCharLength(char letter, boolean bold) + { + switch(letter) + { + case SPACE: + return space; + case TAB: + return space * 4; + default: + CharInstance instance = getInstance(letter, bold); + return instance == null ? 0F : instance.getXAdvance(); + } + } + + @Override + public float getTextLength(String text, int flags) + { + float result = 0.0F; + float current = 0.0F; + boolean bold = (flags & BOLD) != 0; + for(int i = 0;i < text.length();i++) + { + char character = text.charAt(i); + if(LINE_SEPERATOR == character) + { + result = Math.max(result, current += space); + current = 0.0F; + continue; + } + if(character == '§' && (flags & SPECIAL) != 0 && i + 1 < text.length() && text.charAt(i + 1) == '<') + { + String search = TextUtil.searchUntil(text, i + 2, '>', INVALID_SEARCH); + if(search.length() > 0) + { + for(String entry : search.toLowerCase(Locale.ROOT).split(",")) + { + bold = TextUtil.findFlag(entry, "bold", bold); + } + i += search.length() + 2; + continue; + } + } + current += getCharLength(character, bold); + } + return Math.max(result, current); + } + + @Override + public float[] getTextLengths(String text, int flags) + { + FloatList results = new FloatArrayList(); + results.add(0F); + float current = 0F; + int chars = 0; + boolean bold = (flags & BOLD) != 0; + float max = 0F; + for(int i = 0;i < text.length();i++) + { + char character = text.charAt(i); + if(LINE_SEPERATOR == character) + { + results.add(current); + max = Math.max(max, current); + current = 0F; + chars = 0; + continue; + } + chars++; + if(character == '§' && (flags & SPECIAL) != 0 && i + 1 < text.length() && text.charAt(i + 1) == '<') + { + String result = TextUtil.searchUntil(text, i + 2, '>', INVALID_SEARCH); + if(result.length() > 0) + { + for(String entry : result.toLowerCase(Locale.ROOT).split(",")) + { + bold = TextUtil.findFlag(entry, "bold", bold); + } + i += result.length() + 2; + continue; + } + } + current += getCharLength(character, bold); + } + if(chars++ > 0) + { + results.add(current); + max = Math.max(max, current); + } + results.set(0, max); + return results.toFloatArray(); + } + + @Override + public float getTextHeight(String text, int flags) + { + return getTextLengths(text, flags).length - 1 * height; + } + + @Override + public boolean isCharValid(char letter) + { + return chars[0].containsKey(letter); + } + + @Override + public String[] splitLines(String text, float maxWidth, int flags) + { + TextContext context = new TextContext(1F, ((flags & IFontRenderer.SPECIAL) != 0 ? 16 : 0) | ((flags & IFontRenderer.BOLD) != 0 ? 1 : 0)); + List lines = lexer.evaluateLines(text, context, maxWidth); + String[] array = new String[lines.size()]; + if(context.allowsSpecial()) + { + int index = 0; + StringBuilder builder = new StringBuilder(); + WordContext wordContext = context.getEffect(); + while(wordContext.isNext(0)) + { + builder.append(wordContext.getString(false)); + wordContext = wordContext.next(); + } + for(int i = 0,m=lines.size();i recylcePool = new SimplePool<>(100, Matrix4f::new); + Stack transformStack = new ObjectArrayList<>(); + Matrix4f transform = new Matrix4f(); + boolean isFastTransform = true; + SimplePool fastPool = new SimplePool<>(100, () -> Vec4f.newMutable(0F, 0F, 0F, 1F)); + Stack fastStack = new ObjectArrayList<>(); + Vec4f fastTransform = Vec4f.newMutable(0F, 0F, 0F, 1F); + + SimplePool callPool = new SimplePool<>(100, GLCall::new); + ObjectPriorityDequeue activeCalls = new ObjectArrayFIFOQueue<>(); + + Tesselator bufferHelper = new Tesselator(655340); + FloatBuffer renderBuffer = MemoryUtil.memAllocFloat(655340); + Tesselator renderer = new Tesselator(renderBuffer); + ITexture currentTexture; + boolean drawing = false; + boolean useTexture = false; + Vec4f currentFrame = Vec4f.newMutable(); + float roundedNess = 0F; + + GuiModel texturedModel = GuiModel.createTextureModel(Short.MAX_VALUE * 2); + + Align horizontal = Align.CENTER; + Align vertical = Align.CENTER; + + Vec3f helper = Vec3f.newMutable(); + Vec4f transformHelper = Vec4f.newMutable(); + Vec4f alignHelper = Vec4f.newMutable(); + Vec3f alignTranslation = Vec3f.newMutable(0F); + Vec3f alignScale = Vec3f.newMutable(1F); + + GuiManager manager; + + public UIRenderer(GuiManager manager) + { + this.manager = manager; + TextureManager.INSTANCE.getReloader().addReloadableResource(this); + } + + public RenderBuffer createBuffer() + { + return new RenderBuffer(bufferHelper); + } + + public UIRenderer push() + { + if(isFastTransform) + { + fastStack.push(fastPool.get().set(fastTransform)); + return this; + } + transformStack.push(recylcePool.get().load(transform)); + return this; + } + + public UIRenderer pop() + { + if(isFastTransform) + { + Vec4f pop = fastStack.pop(); + fastTransform.set(pop); + fastPool.accept(pop); + return this; + } + Matrix4f pop = transformStack.pop(); + transform.load(pop); + pop.getScale(alignScale); + recylcePool.accept(pop); + return this; + } + + public UIRenderer setVisibility(float visibility) + { + sanityCheck("Visibilty Change"); + renderer.setVisibilty(visibility); + return this; + } + + public UIRenderer setBrightness(float brightness) + { + sanityCheck("Brightness change"); + renderer.setBrightness(brightness); + return this; + } + + public UIRenderer resetEffects() + { + renderer.setBrightness(1F).setVisibilty(1F); + return this; + } + + public UIRenderer setActiveTexture(ITexture texture) + { + if(getTextureId(currentTexture) != getTextureId(texture)) + { + addDrawCall(); + currentTexture = texture; + } + return this; + } + + public UIRenderer setRoundness(IGuiBox box, float roundedNess) + { + if(roundedNess <= 0) + { + if(this.roundedNess > 0F) addDrawCall(); + this.roundedNess = roundedNess; + return this; + } + alignHelper.set(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY()); + if(!currentFrame.equals(alignHelper) || this.roundedNess != roundedNess) + { + addDrawCall(); + currentFrame.set(alignHelper); + this.roundedNess = roundedNess; + } + return this; + } + + protected void applyEffects(UniformVec2f vec) + { + vec.storeData(renderer.getBrightness(), renderer.getVisibility()); + } + + public UIRenderer translate(float x, float y) + { + return translate(x, y, 0F); + } + + public UIRenderer translate(Vec2f offset) + { + return translate(offset.getX(), offset.getY(), 0F); + } + + public UIRenderer translate(float x, float y, float z) + { + if(isFastTransform) + { + fastTransform.add(x, y, z, 0F); + return this; + } + transform.translate(helper.set(x, y, z)); + return this; + } + + public UIRenderer translate(Vec3f offset) + { + if(isFastTransform) + { + fastTransform.add(offset.getX(), offset.getY(), offset.getZ(), 0F); + return this; + } + transform.translate(offset); + return this; + } + + public UIRenderer rotate(Quaternion quad) + { + if(isFastTransform) return this; + transform.rotate(quad); + return this; + } + + public UIRenderer rotateX(float angle) + { + if(isFastTransform) return this; + transform.rotateX((float)Math.toRadians(angle)); + return this; + } + + public UIRenderer rotateY(float angle) + { + if(isFastTransform) return this; + transform.rotateY((float)Math.toRadians(angle)); + return this; + } + + public UIRenderer rotateZ(float angle) + { + if(isFastTransform) return this; + transform.rotateZ((float)Math.toRadians(angle)); + return this; + } + + public UIRenderer scale(float x, float y, float z) + { + if(isFastTransform) return this; + transform.scale(helper.set(x, y, z)); + alignScale.multiply(x, y, z); + return this; + } + + public UIRenderer scale(float scale) + { + if(isFastTransform) + { + fastTransform.multiply(1F, 1F, 1F, scale); + return this; + } + transform.scale(helper.set(scale, scale, scale)); + alignScale.multiply(scale); + return this; + } + + public UIRenderer unscale(float x, float y, float z) + { + if(isFastTransform) return this; + transform.scale(helper.set(1.0F / x, 1.0F / y, 1.0F / z)); + alignScale.multiply(helper); + return this; + } + + public UIRenderer unscale(float scale) + { + scale = 1.0F / scale; + if(isFastTransform) + { + fastTransform.multiply(1F, 1F, 1F, scale); + return this; + } + transform.scale(helper.set(scale, scale, scale)).getScale(alignScale); + alignScale.multiply(scale); + return this; + } + + public UIRenderer resetTransform() + { + if(isFastTransform) + { + fastTransform.set(0F, 0F, 0F, 1F); + return this; + } + transform.setIdentity(); + alignScale.set(Vec3f.ONE); + return this; + } + + public float getCurrentZ() + { + return isFastTransform ? fastTransform.getZ() : transform.get(3, 2); + } + + public ITexture getActiveTexture() + { + return currentTexture; + } + + protected Matrix4f getCurrentMatrix() + { + return transform; + } + + public Matrix4f createCurrentMatrix(Matrix4f matrix) + { + return isFastTransform ? matrix.setIdentity().translate(fastTransform.getX(), fastTransform.getY(), fastTransform.getZ()) : matrix.load(transform); + } + + public UIRenderer setFastTransform(boolean fast) + { + isFastTransform = fast; + return this; + } + + public UIRenderer beginFrame() + { + if(drawing) + { + throw new RuntimeException("Already Drawing!"); + } + drawing = true; + fastTransform.set(0F, 0F, 0F, 1F); + roundedNess = 0F; + currentFrame.negate(); + transform.setIdentity(); + alignScale.set(Vec3f.ONE); + transformStack.clear(); + fastStack.clear(); + return this; + } + + public UIRenderer flush() + { + if(!renderer.isDrawing()) + { + return this; + } + renderer.finishData(); + int count = renderer.getVertexCount(); + if(count <= 0) + { + return this; + } + if(count > (activeCalls.isEmpty() ? 0 : activeCalls.last().getCount())) + { + addDrawCall(); + } + texturedModel.storeData(renderBuffer); + GuiShader shader = manager.getShader(); + shader.bind(manager.getOrthoMatrixBuffer()); + texturedModel.bindArray(); + int last = 0; + while(!activeCalls.isEmpty()) + { + GLCall call = activeCalls.dequeue(); + shader.texture.storeTexture(call.getTexture()); + shader.useTexture.storeData(call.getTexture() > 0 ? 1F : 0F); + shader.roundeness.storeData(call.getRoundedNess()); + if(call.getRoundedNess() > 0F) shader.bounds.storeData(call.getFrame()); + GLUtils.drawArrays(call.getType(), last, call.getCount() - last); + last = call.getCount(); + callPool.accept(call); + } + texturedModel.unbindArray(); + shader.stopShader(); + renderer.resetVertexes(); + return this; + } + + public UIRenderer endFrame() + { + if(!drawing) + { + throw new RuntimeException("Not Drawing!"); + } + drawing = false; + isFastTransform = true; + flush(); + fastTransform.set(0F, 0F, 0F, 1F); + transform.setIdentity(); + renderer.resetEffects(); + useTexture = false; + while(!transformStack.isEmpty()) + { + recylcePool.accept(transformStack.pop()); + } + while(!fastStack.isEmpty()) + { + fastPool.accept(fastStack.pop()); + } + return this; + } + + @Override + public void reload() + { + texturedModel.remove(); + MemoryUtil.memFree(renderBuffer); + texturedModel = GuiModel.createTextureModel(Short.MAX_VALUE * 2); + renderBuffer = MemoryUtil.memAllocFloat(655340); + renderer = new Tesselator(renderBuffer); + } + + @Override + public void destroy() + { + texturedModel.remove(); + MemoryUtil.memFree(renderBuffer); + TextureManager.INSTANCE.getReloader().removeReloadableResource(this); + } + + public UIRenderer drawBuffers(Iterable drawCalls, float width, float height) + { + sanityCheck("Buffer Drawing"); + applyAlignment(0, 0, width, height, 0, alignHelper); + for(DrawCall call : drawCalls) + { + ensureDrawing(call.getGLType(), call.getTextureId() > 0); + FloatBuffer buffer = ByteBuffer.wrap(call.getData()).order(ByteOrder.nativeOrder()).asFloatBuffer(); + for(int i = 0,m=buffer.remaining();i+9<=m;i+=9) + { + pos(transformHelper.set(buffer.get(i)+alignHelper.getX(), buffer.get(i+1)+alignHelper.getY(), buffer.get(i+2), 1F)) + .tex(buffer.get(i+3), buffer.get(i+4)) + .color4f(buffer.get(i+5), buffer.get(i+6), buffer.get(i+7), buffer.get(i+8)).endVertex(); + } + } + alignTranslation.set(0F, 0F, 0F); + return this; + } + + public UIRenderer drawQuad(IGuiBox box, ColorObject color) + { + return drawQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), color); + } + + public UIRenderer drawQuad(IGuiBox box, float zLevel, ColorObject color) + { + return drawQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), zLevel, color); + } + + public UIRenderer drawQuad(float minX, float minY, float maxX, float maxY, ColorObject color) + { + return drawQuad(minX, minY, maxX, maxY, 0.0F, color); + } + + public UIRenderer drawQuad(float minX, float minY, float maxX, float maxY, float zLevel, ColorObject color) + { + ensureDrawing(GL11.GL_TRIANGLES, false); + applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + alignTranslation.set(0F, 0F, 0F); + return this; + } + + public UIRenderer drawTexturedQuad(IGuiBox box, ColorObject color, float minU, float minV, float maxU, float maxV) + { + return drawTexturedQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), color, minU, minV, maxU, maxV); + } + + public UIRenderer drawTexturedQuad(IGuiBox box, float zLevel, ColorObject color, float minU, float minV, float maxU, float maxV) + { + return drawTexturedQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), zLevel, color, minU, minV, maxU, maxV); + } + + public UIRenderer drawTexturedQuad(float minX, float minY, float maxX, float maxY, ColorObject color, float minU, float minV, float maxU, float maxV) + { + return drawTexturedQuad(minX, minY, maxX, maxY, 0F, color, minU, minV, maxU, maxV); + } + + public UIRenderer drawTexturedQuad(float minX, float minY, float maxX, float maxY, float zLevel, ColorObject color, float minU, float minV, float maxU, float maxV) + { + ensureDrawing(GL11.GL_TRIANGLES, true); + applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(minU, maxV).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(minU, minV).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(maxU, maxV).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(maxU, maxV).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(minU, minV).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(maxU, minV).color4f(color).endVertex(); + alignTranslation.set(0F, 0F, 0F); + return this; + } + + public UIRenderer drawGradientQuad(IGuiBox box, ColorObject from, ColorObject to, Facing direction) + { + return drawGradientQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), from, to, direction); + } + + public UIRenderer drawGradientQuad(float minX, float minY, float maxX, float maxY, ColorObject from, ColorObject to, Facing direction) + { + return drawGradientQuad(minX, minY, maxX, maxY, 0F, from, to, direction); + } + + public UIRenderer drawGradientQuad(float minX, float minY, float maxX, float maxY, float zLevel, ColorObject start, ColorObject end, Facing direction) + { + if(!direction.isPositive()) + { + ColorObject wrapper = start; + start = end; + end = wrapper; + } + ensureDrawing(GL11.GL_TRIANGLES, false); + applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? start : end).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? start : start).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? end : end).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? end : end).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? start : start).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? end : start).endVertex(); + alignTranslation.set(0F, 0F, 0F); + return this; + } + + public UIRenderer drawFrame(IGuiBox box, ColorObject color) + { + return drawFrame(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), color); + } + + public UIRenderer drawFrame(IGuiBox box, float zLevel, ColorObject color) + { + return drawFrame(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), zLevel, color); + } + + public UIRenderer drawFrame(float minX, float minY, float maxX, float maxY, ColorObject color) + { + return drawFrame(minX, minY, maxX, maxY, 0F, color); + } + + public UIRenderer drawFrame(float minX, float minY, float maxX, float maxY, float zLevel, ColorObject color) + { + ensureDrawing(GL11.GL_LINES, false); + applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + alignTranslation.set(0F, 0F, 0F); + return this; + } + + public UIRenderer drawLine(float minX, float minY, float maxX, float maxY, ColorObject color) + { + return drawLine(minX, minY, maxX, maxY, 0F, color); + } + + public UIRenderer drawLine(float minX, float minY, float maxX, float maxY, float zLevel, ColorObject color) + { + ensureDrawing(GL11.GL_LINES, false); + applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + alignTranslation.set(0F, 0F, 0F); + return this; + } + + public UIRenderer startCustomShape(int shapeType, boolean needsTexture) + { + sanityCheck("Custom Shape"); + ensureDrawing(shapeType, needsTexture); + return this; + } + + public UIRenderer pos(float x, float y, float z) + { + pos(transformHelper.set(x, y, z, 1F)); + return this; + } + + public UIRenderer pos(Vec3f pos) + { + return pos(pos.getX(), pos.getY(), pos.getZ()); + } + + public UIRenderer color(float r, float g, float b) + { + renderer.color4f(r, g, b); + return this; + } + + public UIRenderer color(float r, float g, float b, float a) + { + renderer.color4f(r, g, b, a); + return this; + } + + public UIRenderer color(ColorObject color) + { + renderer.color4f(color); + return this; + } + + public UIRenderer tex(float u, float v) + { + renderer.tex(u, v); + return this; + } + + public UIRenderer tex(Vec2f tex) + { + return tex(tex.getX(), tex.getY()); + } + + public UIRenderer endVertex() + { + renderer.endVertex(); + return this; + } + + protected void sanityCheck(String task) + { + if(drawing) + { + return; + } + throw new RuntimeException(task); + } + + protected Tesselator pos(Vec4f pos) + { + if(isFastTransform) + { + return renderer.pos(fastTransform(pos.getX(), fastTransform.getX(), alignTranslation.getX()), fastTransform(pos.getY(), fastTransform.getY(), alignTranslation.getY()), fastTransform(pos.getZ(), fastTransform.getZ(), alignTranslation.getZ())); + } + float x = (transform.get(0) * pos.getX() + transform.get(4) * pos.getY() + transform.get(8) * pos.getZ() + transform.get(12)) + (alignTranslation.getX() * alignScale.getX()); + float y = (transform.get(1) * pos.getX() + transform.get(5) * pos.getY() + transform.get(9) * pos.getZ() + transform.get(13)) + (alignTranslation.getY() * alignScale.getY()); + float z = (transform.get(2) * pos.getX() + transform.get(6) * pos.getY() + transform.get(10) * pos.getZ() + transform.get(14)) + (alignTranslation.getZ() * alignScale.getZ()); + return renderer.pos(x, y, z); + } + + private float fastTransform(float pos, float offset, float align) + { + return ((pos + align) * fastTransform.getW()) + offset; + } + + protected void applyAlignment(float minX, float minY, float maxX, float maxY, float zLevel, Vec4f result) + { + applyAlignment(minX, minY, maxX, maxY, zLevel, vertical, horizontal, renderer, result); + } + + protected void applyAlignment(float minX, float minY, float maxX, float maxY, float zLevel, Align vertical, Align horizontal, Tesselator tes, Vec4f result) + { + float width = maxX - minX; + float height = maxY - minY; + float xOffset = 0F; + float yOffset = 0F; + switch(vertical) + { + case LEFT_TOP: + yOffset = 0; + result.setY(0F).setW(height); + break; + case CENTER: + yOffset = height / 2F; + result.setY(-yOffset).setW(yOffset); + break; + case RIGHT_BOTTOM: + yOffset = maxY; + result.setY(-height).setW(0F); + break; + } + switch(horizontal) + { + case LEFT_TOP: + xOffset = 0; + result.setX(0F).setZ(width); + break; + case CENTER: + xOffset = width / 2F; + result.setX(-xOffset).setZ(xOffset); + break; + case RIGHT_BOTTOM: + xOffset = maxX; + result.setX(-width).setZ(0F); + break; + } + alignTranslation.set(minX + xOffset, minY + yOffset, zLevel); + } + + protected void ensureDrawing(int glState, boolean textureNeeded) + { + if(!renderer.isDrawing()) + { + renderer.begin(glState, VertexType.UI); + } + if(glState != renderer.getGLDrawType() || useTexture != textureNeeded) + { + addDrawCall(); + renderer.changeGLType(glState); + useTexture = textureNeeded; + } + } + + protected void addDrawCall() + { + if(getDrawCall() >= renderer.getVertexCount()) + { + return; + } + GLCall call = callPool.get(); + call.setType(renderer.getGLDrawType()); + call.setCount(renderer.getVertexCount()); + call.setTexture(getTextureId(useTexture ? currentTexture : null)); + call.setFrame(currentFrame); + call.setRounded(roundedNess); + activeCalls.enqueue(call); + } + + private int getDrawCall() + { + return activeCalls.isEmpty() ? 0 : activeCalls.last().getCount(); + } + + private int getTextureId(ITexture texture) + { + return texture == null ? 0 : texture.getTextureID(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/DelayedRenderBuffer.java b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/DelayedRenderBuffer.java new file mode 100644 index 0000000..494f15c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/DelayedRenderBuffer.java @@ -0,0 +1,140 @@ +package speiger.src.coreengine.rendering.gui.renderer.buffer; + +import java.util.function.BiConsumer; + +import speiger.src.collections.floats.lists.FloatArrayList; +import speiger.src.collections.floats.lists.FloatList; +import speiger.src.coreengine.rendering.tesselation.IVertexBuilder; +import speiger.src.coreengine.rendering.tesselation.Tesselator; + +public class DelayedRenderBuffer implements IVertexBuilder +{ + protected FloatList buffer = new FloatArrayList(); + + @Override + public DelayedRenderBuffer pos(float x, float y) + { + buffer.add(x); + buffer.add(y); + return this; + } + + @Override + public DelayedRenderBuffer pos(float x, float y, float z) + { + buffer.add(x); + buffer.add(y); + buffer.add(z); + return this; + } + + @Override + public DelayedRenderBuffer color4f(float r, float g, float b, float a) + { + buffer.add(r); + buffer.add(g); + buffer.add(b); + buffer.add(a); + return this; + } + + @Override + public DelayedRenderBuffer tex(float u, float v) + { + buffer.add(u); + buffer.add(v); + return this; + } + + @Override + public IVertexBuilder color3f(float r, float g, float b) + { + buffer.add(r); + buffer.add(g); + buffer.add(b); + return this; + } + + @Override + public IVertexBuilder normal(float x, float y, float z) + { + buffer.add(x); + buffer.add(y); + buffer.add(z); + return this; + } + + @Override + public IVertexBuilder custom(float x) + { + buffer.add(x); + return this; + } + + @Override + public IVertexBuilder custom(float x, float y) + { + buffer.add(x); + buffer.add(y); + return this; + } + + @Override + public IVertexBuilder custom(float x, float y, float z) + { + buffer.add(x); + buffer.add(y); + buffer.add(z); + return this; + } + + @Override + public IVertexBuilder custom(float x, float y, float z, float w) + { + buffer.add(x); + buffer.add(y); + buffer.add(z); + buffer.add(w); + return this; + } + + @Override + public IVertexBuilder endVertex() + { + return this; + } + + public void merge(Tesselator builder, BiConsumer merge) + { + merge.accept(buffer, builder); + buffer.clear(); + } + + protected void defaultMerge(FloatList buffer, Tesselator builder) + { + if(builder.hasTexture()) + { + for(int i = 0;i 0; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/RenderBuffer.java b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/RenderBuffer.java new file mode 100644 index 0000000..2e8757d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/RenderBuffer.java @@ -0,0 +1,80 @@ +package speiger.src.coreengine.rendering.gui.renderer.buffer; + +import java.util.Iterator; +import java.util.List; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.models.DrawCall; +import speiger.src.coreengine.rendering.tesselation.Tesselator; +import speiger.src.coreengine.rendering.tesselation.VertexList; +import speiger.src.coreengine.utils.collections.iterators.IndexIterator; +import speiger.src.coreengine.utils.collections.iterators.IterableWrapper; + +public class RenderBuffer implements Consumer, Iterable +{ + List drawCalls = new ObjectArrayList(); + Tesselator tesselator; + + public RenderBuffer(Tesselator tesselator) + { + this.tesselator = tesselator; + } + + @Override + public void accept(GuiComponent t) + { + clear(); + } + + public T get(int index, Class clz) + { + return (T)drawCalls.get(index); + } + + public void addDrawCall(DrawCall call) + { + drawCalls.add(call); + } + + public void clear() + { + drawCalls.clear(); + } + + public Tesselator start(int shape, VertexList format) + { + return tesselator.begin(shape, format); + } + + public void finishShape(int texture, Tesselator tesselator) + { + if(!tesselator.isDrawing()) + { + throw new IllegalStateException("Tesselator wasnt filled"); + } + tesselator.finishData(); + if(tesselator.getVertexCount() <= 0) + { + return; + } + drawCalls.add(tesselator.getDrawCall(texture)); + } + + public void finishShape(int texture) + { + finishShape(texture, tesselator); + } + + @Override + public Iterator iterator() + { + return drawCalls.iterator(); + } + + public Iterable selectionIterator(int...indexes) + { + return IterableWrapper.wrap(new IndexIterator(drawCalls, indexes)); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/TranslatedVertexBuilder.java b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/TranslatedVertexBuilder.java new file mode 100644 index 0000000..9933a38 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/TranslatedVertexBuilder.java @@ -0,0 +1,98 @@ +package speiger.src.coreengine.rendering.gui.renderer.buffer; + +import speiger.src.coreengine.rendering.tesselation.IVertexBuilder; + +public class TranslatedVertexBuilder implements IVertexBuilder +{ + IVertexBuilder builder; + float xOff; + float yOff; + float zOff; + float wOff; + + public TranslatedVertexBuilder(IVertexBuilder builder, float xOff, float yOff, float zOff, float wOff) + { + this.builder = builder; + this.xOff = xOff; + this.yOff = yOff; + this.zOff = zOff; + this.wOff = wOff; + } + + @Override + public IVertexBuilder pos(float x, float y) + { + builder.pos(x, y); + return this; + } + + @Override + public IVertexBuilder pos(float x, float y, float z) + { + builder.pos(x, y, z); + return this; + } + + @Override + public IVertexBuilder color3f(float r, float g, float b) + { + builder.color3f(r, g, b); + return this; + } + + @Override + public IVertexBuilder color4f(float r, float g, float b, float a) + { + builder.color4f(r, g, b, a); + return this; + } + + @Override + public IVertexBuilder tex(float u, float v) + { + builder.tex(u, v); + return this; + } + + @Override + public IVertexBuilder normal(float x, float y, float z) + { + builder.normal(x, y, z); + return this; + } + + @Override + public IVertexBuilder custom(float x) + { + builder.custom(x); + return this; + } + + @Override + public IVertexBuilder custom(float x, float y) + { + builder.custom(x, y); + return this; + } + + @Override + public IVertexBuilder custom(float x, float y, float z) + { + builder.custom(x, y, z); + return this; + } + + @Override + public IVertexBuilder custom(float x, float y, float z, float w) + { + builder.custom(x, y, z, w); + return this; + } + + @Override + public IVertexBuilder endVertex() + { + builder.custom(xOff, yOff, zOff, wOff).endVertex(); + return this; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/Line.java b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/Line.java new file mode 100644 index 0000000..63df5c3 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/Line.java @@ -0,0 +1,142 @@ +package speiger.src.coreengine.rendering.gui.renderer.lexer; + +import java.util.List; + +import speiger.src.collections.objects.collections.ObjectIterator; +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.utils.ObjectIterators; +import speiger.src.coreengine.rendering.gui.renderer.IFontRenderer.CharInstance; +import speiger.src.coreengine.utils.collections.iterators.IterableWrapper; + +public class Line +{ + float totalWidth; + float width; + int totalLetters = 0; + String result = null; + List words = new ObjectArrayList(); + + public Line(float totalWidth) + { + this.totalWidth = totalWidth; + } + + public boolean attemptAddingWord(Word word) + { + if(word.getWidth() + width > totalWidth) + { + return false; + } + width += word.getWidth(); + words.add(word); + totalLetters += word.size(); + return true; + } + + public void addWord(Word word) + { + width += word.getWidth(); + words.add(word); + totalLetters += word.size(); + } + + protected void finishLine() + { + StringBuilder builder = new StringBuilder(); + for(Word word : words) + { + builder.append(word.getLetters()); + } + result = builder.toString(); + } + + public int getIndex(float width, boolean total) + { + if(width <= 0F) return (total && !words.isEmpty()) ? words.get(0).getStartIndex() : 0; + if(width >= this.width) return ((total && !words.isEmpty()) ? words.get(0).getStartIndex() : 0) + totalLetters; + int letters = (total && !words.isEmpty()) ? words.get(0).getStartIndex() : 0; + for(int i = 0,m=words.size();i width) + { + return letters + word.getIndex(width); + } + width -= word.getWidth(); + letters += word.size(); + } + return letters; + } + + public String getText() + { + return result; + } + + public boolean isEmpty() + { + return words.isEmpty(); + } + + public int size() + { + return totalLetters; + } + + public int getStart() + { + return words.isEmpty() ? 0 : words.get(0).getStartIndex(); + } + + public int getEnd() + { + return words.isEmpty() ? 0 : words.get(words.size() - 1).getEndIndex(); + } + + public Word getWord(int letterIndex) + { + for(int i = 0;i= letterIndex) + { + return word; + } + letterIndex -= word.size(); + } + return null; + } + + public float getWidth() + { + return width; + } + + public float getWidth(int index) + { + if(index <= 0) return 0F; + else if(index >= totalLetters) return width; + float width = 0F; + for(int i = 0,m=words.size();i index) + { + return width + word.getWidth(index); + } + width += word.getWidth(); + index -= word.size(); + } + return 0; + } + + public Iterable letterIterator() + { + ObjectIterator[] arrays = new ObjectIterator[words.size()]; + for(int i = 0,m=words.size();i changes = new ObjectArrayList(); + + public TextContext(float scale) + { + this(scale, 0); + } + + public TextContext(float scale, int flags) + { + this.scale = scale; + originalFlags = this.flags = flags; + originalColor = Color.WHITE; + originalItalic = 0F; + changes.add(new WordContext(0, flags, italic, color.hashCode())); + } + + public TextContext(TextComponent component) + { + scale = component.getTextScale(); + originalFlags = createFlags(component); + originalColor = component.getTextColor().toColor(); + originalItalic = component.getItalic(); + flags = originalFlags; + color.setRGB(originalColor.getRGB()); + italic = originalItalic; + changes.add(new WordContext(0, flags, italic, color.hashCode())); + } + + public void reset() + { + flags = originalFlags; + color.setRGB(originalColor.getRGB()); + italic = originalItalic; + } + + public boolean allowsSpecial() + { + return (originalFlags & SPECIAL) != 0; + } + + public boolean isBold() + { + return (flags & BOLD) != 0; + } + + public boolean isSingle() + { + return (originalFlags & SINGLE) != 0; + } + + public float getScale() + { + return scale; + } + + public void apply(String[] flagString, int index) + { + for(int i = 0,m=flagString.length;i 0.0F ? 0.0F : 3.0F); + } + WordContext context = new WordContext(index, flags, italic, color.hashCode()); + changes.get(changes.size() - 1).setNext(context); + changes.add(context); + } + + void setFlag(int flag, boolean value) + { + flags = (value ? flags | flag : flags & ~(flag)); + } + + static int createFlags(TextComponent component) + { + int flags = 0; + flags |= component.isBold() ? BOLD : 0; + flags |= component.isFlipped() ? FLIPPED : 0; + flags |= component.isStrikeThrough() ? STRIKE_THROUGH : 0; + flags |= component.isUnderlined() ? UNDERLINE : 0; + flags |= component.isRenderingSpecial() ? SPECIAL : 0; + flags |= component.isForcedSingleLine() ? SINGLE : 0; + return flags; + } + + public static class WordContext + { + static final DecimalFormat ITALIC_FORMAT = new DecimalFormat("0.##"); + WordContext prev; + WordContext next; + public final int charIndex; + public final boolean bold; + public final boolean flipped; + public final boolean strike_through; + public final boolean underline; + public final float italic; + public final int color; + + public WordContext(int index, int flags, float italic, int color) + { + charIndex = index; + bold = (flags & BOLD) != 0; + flipped = (flags & FLIPPED) != 0; + strike_through = (flags & STRIKE_THROUGH) != 0; + underline = (flags & UNDERLINE) != 0; + this.italic = italic; + this.color = color; + } + + void setNext(WordContext next) + { + this.next = next; + this.next.prev = this; + } + + public boolean isNext(int index) + { + return next != null && next.charIndex <= index; + } + + public WordContext next() + { + return next; + } + + public String getString(boolean restart) + { + if(prev == null || restart) + { + if(anyEnabled()) + { + StringJoiner joiner = new StringJoiner(",", "§<", ">"); + if(bold) joiner.add("bold=true"); + if(flipped) joiner.add("flipped=true"); + if(underline) joiner.add("underline=true"); + if(strike_through) joiner.add("strike=true"); + if(color != -1) joiner.add("color="+ColorObject.rgb(color).getHexCode(true)); + if(italic > 0F) joiner.add("italic="+ITALIC_FORMAT.format(italic)); + return joiner.toString(); + } + return ""; + } + if(bold != prev.bold || flipped != prev.flipped || underline != prev.underline || strike_through != prev.strike_through || color != prev.color || Float.compare(italic, prev.italic) != 0) + { + StringJoiner joiner = new StringJoiner(",", "§<", ">"); + if(bold != prev.bold) joiner.add("bold="+bold); + if(flipped != prev.flipped) joiner.add("flipped="+flipped); + if(underline != prev.underline) joiner.add("underline="+underline); + if(strike_through != prev.strike_through) joiner.add("strike="+strike_through); + if(color != prev.color) joiner.add("color="+ColorObject.rgb(color).getHexCode(true)); + if(Float.compare(italic, prev.italic) != 0) joiner.add("italic="+ITALIC_FORMAT.format(italic)); + return joiner.toString(); + } + return ""; + } + + protected boolean anyEnabled() + { + return bold || flipped || underline || strike_through || color != -1 || italic > 0F; + } + } + + public WordContext getEffect() + { + return changes.get(0); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/TextLexer.java b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/TextLexer.java new file mode 100644 index 0000000..0eafe3f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/TextLexer.java @@ -0,0 +1,128 @@ +package speiger.src.coreengine.rendering.gui.renderer.lexer; + +import java.util.List; +import java.util.Locale; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.utils.ObjectLists; +import speiger.src.coreengine.rendering.gui.renderer.FontRenderer; +import speiger.src.coreengine.rendering.gui.renderer.IFontRenderer.CharInstance; +import speiger.src.coreengine.utils.helpers.TextUtil; + +public class TextLexer +{ + FontRenderer render; + List helper = new ObjectArrayList(); + + public TextLexer(FontRenderer render) + { + this.render = render; + } + + public void getWords(String text, TextContext context, List words) + { + text = render.clearInvalidLetters(text); + Word current = new Word(context.getScale()); + for(int i = 0,j=0,m=text.length();i', FontRenderer.INVALID_SEARCH); + if(search.length() > 0) + { + context.apply(search.toLowerCase(Locale.ROOT).split(","), j--); + i += search.length()+2; + continue; + } + } + WordType type = WordType.getByLetter(letter); + if(current.getType() != type || type != null && type.isNewLine()) + { + if(!current.isEmpty()) + { + words.add(current); + current = new Word(current); + } + current.setSpecail(type); + } + current.addLetter(render.getInstance(letter, context.isBold())); + } + if(!current.isEmpty()) + { + words.add(current); + } + } + + public List evaluateLines(String text, TextContext context, float maxWidth) + { + if(text == null || text.isEmpty() || maxWidth < 10F * context.getScale()) + { + return ObjectLists.empty(); + } + List lines = new ObjectArrayList(); + getWords(text, context, helper); + Line line = new Line(maxWidth); + for(int i = 0;i maxLength) + { + helper.add(index++, newWord); + newWord = new Word(newWord); + } + newWord.addLetter(letter); + } + if(newWord.size() > 0) + { + helper.add(index++, newWord); + if(word.getNext() != null) + { + word.getNext().setPrev(newWord); + } + } + else if(word.getNext() != null) + { + word.getNext().setPrev(newWord.getPrev()); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/TextMetadata.java b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/TextMetadata.java new file mode 100644 index 0000000..71d1cbd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/TextMetadata.java @@ -0,0 +1,158 @@ +package speiger.src.coreengine.rendering.gui.renderer.lexer; + +import java.util.List; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.rendering.gui.components.TextComponent; + +public class TextMetadata +{ + TextComponent owner; + List lines = new ObjectArrayList(); + float startX; + float startY; + float maxHeight; + float maxWidth = 0F; + float scale; + + public TextMetadata(TextComponent owner) + { + this.owner = owner; + } + + public void clear() + { + scale = owner.getTextScale(); + lines.clear(); + maxWidth = 0F; + maxHeight = 0F; + startX = 0F; + startY = 0F; + } + + public void addLine(Line line) + { + lines.add(line); + maxWidth = Math.max(maxWidth, line.getWidth()); + maxHeight = lines.size() * owner.getFont().getFontHeight() * scale; + } + + public void setStart(float x, float y) + { + startX = x; + startY = y; + } + + public float getStartX() + { + return startX; + } + + public float getStartY() + { + return startY; + } + + public float getMaxHeight() + { + return maxHeight; + } + + public float getMaxWidth() + { + return maxWidth; + } + + public float getWidth(int letterIndex) + { + return lines.isEmpty() ? 0F : lines.get(0).getWidth(letterIndex); + } + + public float getWidth(int posX, int posY) + { + return lines.size() > posY ? lines.get(posY).getWidth(posX) : 0F; + } + + public float getLineWidth(int line) + { + return lines.size() > line ? lines.get(line).getWidth() : 0F; + } + + public Word getWord(int letterIndex) + { + return lines.isEmpty() ? null : lines.get(0).getWord(letterIndex); + } + + public Line getLine(int line) + { + return lines.size() > line ? lines.get(line) : null; + } + + public int moveUp(Vec2i pos) + { + if(pos.getY() + 1 < lines.size()) + { + float width = lines.get(pos.getY()).getWidth(pos.getX()); + return lines.get(pos.getY() + 1).getIndex(width, true); + } + return lines.isEmpty() ? 0 : lines.get(lines.size() - 1).getEnd(); + } + + public int moveDown(Vec2i pos) + { + if(pos.getY() > 0 && pos.getY() - 1 < lines.size()) + { + float width = lines.get(pos.getY()).getWidth(pos.getX()); + return lines.get(pos.getY() - 1).getIndex(width, true); + } + return lines.isEmpty() ? 0 : lines.get(0).getStart(); + } + + public int getIndex(float width) + { + return lines.size() > 0 ? lines.get(0).getIndex(width, false) : 0; + } + + public int getIndex(float width, float height) + { + return lines.isEmpty() ? 0 : lines.get(MathUtils.clamp(0, lines.size() - 1, (int)(height / (owner.getFont().getFontHeight() * scale)))).getIndex(width, true); + } + + public void getIndex(float width, float height, Vec2i result) + { + if(lines.isEmpty()) + { + result.negate(); + return; + } + int index = MathUtils.clamp(0, lines.size() - 1, (int)(height / (owner.getFont().getFontHeight() * scale))); + result.set(lines.get(index).getIndex(width, false), index); + } + + public void convert(int index, Vec2i result) + { + result.set(Vec2i.ZERO); + for(int i = 0,m=lines.size();i= index) + { + result.add(index, i); + return; + } + index -= line.size(); + } + } + + public int convert(Vec2i input) + { + int index = input.getX(); + for(int i = 0,m=input.getY();i letters = new ObjectArrayList(); + int startIndex; + Word prev; + Word next; + WordType special = null; + float width; + WordContext context; + + public Word(float scale) + { + this.scale = scale; + } + + public Word(Word parent) + { + prev = parent; + if(parent == null) + { + scale = 1F; + return; + } + parent.next = this; + startIndex = parent.getEndIndex(); + scale = parent.scale; + } + + void setPrev(Word parent) + { + prev = parent; + parent.next = this; + startIndex = parent.getEndIndex(); + } + + public void addLetter(CharInstance letter) + { + if(letter == null) + { + return; + } + letters.add(letter); + width += letter.getXAdvance() * scale; + } + + public Word getNext() + { + return next; + } + + public Word getPrev() + { + return prev; + } + + public int getStartIndex() + { + return startIndex; + } + + public int getEndIndex() + { + return startIndex + letters.size(); + } + + public boolean isEmpty() + { + return letters.isEmpty(); + } + + public int size() + { + return letters.size(); + } + + public String getLetters() + { + StringBuilder builder = new StringBuilder(); + for(int i = 0,m=letters.size();i= letters.size()) return width; + float result = 0F; + for(int i = 0;i instanceIterator() + { + return IterableWrapper.wrap(letters.iterator()); + } + + ObjectIterator objectInstanceIterator() + { + return letters.iterator(); + } + + public int getIndex(float width) + { + for(int i = 0,m=letters.size();i', 1), + MORE_THEN('>', '<', 2), + RIGHT_BRACKET('[', ']', 1), + LEFT_BRACKET(']', '[', 2), + SPACE(' ', 3), + LINE_SEPERATOR('\n', 3); + + static final int START_WORD = 1; + static final int END_WORD = 2; + static final int DUAL_WORD = 3; + static final Char2ObjectMap MAPPER; + + char letter; + Character opposite; + int flag; + + private WordType(char letter, char opposite, int flag) + { + this.letter = letter; + this.opposite = Character.valueOf(opposite); + this.flag = flag; + } + + private WordType(char letter, int flag) + { + this.letter = letter; + this.flag = flag; + } + + public char getLetter() + { + return letter; + } + + public boolean isStartWord() + { + return flag == START_WORD; + } + + public boolean isEndWord() + { + return flag == END_WORD; + } + + public boolean isDualWord() + { + return flag == DUAL_WORD; + } + + public boolean isSpace() + { + return this == SPACE; + } + + public boolean isNewLine() + { + return this == LINE_SEPERATOR; + } + + public boolean isOpposite(WordType letter) + { + return (opposite == null && letter == this) || (opposite != null && opposite.charValue() == letter.letter); + } + + public Word findStartWord(Word word) + { + Word currentWord = word; + while(currentWord.getPrev() != null) + { + currentWord = currentWord.getPrev(); + if(isOpposite(currentWord.getType())) + { + return currentWord; + } + } + return null; + } + + public Word findEndWord(Word word) + { + Word currentWord = word; + while(currentWord.getNext() != null) + { + currentWord = currentWord.getNext(); + if(isOpposite(currentWord.getType())) + { + return currentWord; + } + } + return null; + } + + public static WordType getByLetter(char letter) + { + return MAPPER.get(letter); + } + + static + { + MAPPER = new Char2ObjectOpenHashMap(); + for(WordType type : WordType.values()) + { + MAPPER.put(type.letter, type); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/DropListener.java b/src/main/java/speiger/src/coreengine/rendering/input/DropListener.java new file mode 100644 index 0000000..6b1dc0b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/DropListener.java @@ -0,0 +1,38 @@ +package speiger.src.coreengine.rendering.input; + +import java.io.File; + +import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWDropCallback; + +import speiger.src.coreengine.rendering.input.events.FileDropEvent; +import speiger.src.coreengine.rendering.input.window.ScaledResolution; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.utils.eventbus.EventBus; + +public class DropListener +{ + EventBus bus; + ScaledResolution resolution; + + public DropListener(Window window, EventBus bus) + { + window.addCallback(T -> GLFW.glfwSetDropCallback(T, this::onFileDropped)); + resolution = window.getUIFrame(); + this.bus = bus; + } + + public void onFileDropped(long windowId, int count, long names) + { + int x = Mouse.getX(); + int y = Mouse.getY(); + for(int i = 0;i filter; + + public void init(EventBus bus, Window window) + { + this.bus = bus; + window.addCallback(T -> GLFW.glfwSetKeyCallback(T, this::onKeyTyped)); + window.addCallback(T -> GLFW.glfwSetCharCallback(T, this::onCharTyped)); + } + + public static void setFilter(BiPredicate filter) + { + INSTANCE.filter = filter; + } + + public static void clearFilter() + { + setFilter(null); + } + + protected void onKeyTyped(long window, int key, int scancode, int action, int mods) + { + if(key > 350 || key < 0) + { + return; + } + if(action >= 1) + { + pressedKeys[key] = true; + onKeyPressed(key); + } + else + { + pressedKeys[key] = false; + InputBinding.BINDINGS.updatePressing(BindingType.KEYBOARD, key, false); + } + } + + public void onKeyPressed(int key) + { + if(filter == null || filter.test(key, this)) + { + KeyPressEvent event = new KeyPressEvent(key); + bus.post(event); + if(event.isCanceled()) + { + return; + } + InputBinding.BINDINGS.updatePressing(BindingType.KEYBOARD, key, true); + } + } + + public void onCharTyped(long windowId, int codepoint) + { + bus.post(new CharTypeEvent((char)codepoint, codepoint)); + } + + public static boolean isCtrlDown() + { + return isKeyPressed(GLFW.GLFW_KEY_LEFT_CONTROL) || isKeyPressed(GLFW.GLFW_KEY_RIGHT_CONTROL); + } + + public static boolean isShiftDown() + { + return isKeyPressed(GLFW.GLFW_KEY_LEFT_SHIFT) || isKeyPressed(GLFW.GLFW_KEY_RIGHT_SHIFT); + } + + public static boolean isAltDown() + { + return isKeyPressed(GLFW.GLFW_KEY_LEFT_ALT) || isKeyPressed(GLFW.GLFW_KEY_RIGHT_ALT); + } + + public static boolean isPrintableKey(int key) + { + return key <= 162; + } + + public static boolean isKeyPressed(int key) + { + return INSTANCE.isPressed(key); + } + + public boolean isPressed(int key) + { + return pressedKeys[key]; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/Mouse.java b/src/main/java/speiger/src/coreengine/rendering/input/Mouse.java new file mode 100644 index 0000000..576c61e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/Mouse.java @@ -0,0 +1,127 @@ +package speiger.src.coreengine.rendering.input; + +import org.lwjgl.glfw.GLFW; + +import speiger.src.collections.ints.sets.IntLinkedOpenHashSet; +import speiger.src.collections.ints.sets.IntSet; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.rendering.input.bindings.InputBinding; +import speiger.src.coreengine.rendering.input.bindings.utils.BindingType; +import speiger.src.coreengine.rendering.input.camera.Camera; +import speiger.src.coreengine.rendering.input.events.MouseEvent; +import speiger.src.coreengine.rendering.input.events.MouseEvent.MouseClickEvent; +import speiger.src.coreengine.rendering.input.events.MouseEvent.MouseMoveEvent; +import speiger.src.coreengine.rendering.input.events.MouseEvent.MouseScrollEvent; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.utils.eventbus.EventBus; + +public class Mouse +{ + public static final Mouse INSTANCE = new Mouse(); + + MouseRay ray; + EventBus bus; + IntSet pressedButton = new IntLinkedOpenHashSet(); + Vec2i position = Vec2i.newMutable(); + Vec2i movement = Vec2i.newMutable(); + Vec2i scroll = Vec2i.newMutable(); + boolean active = true; + + public void init(EventBus bus, Window window, Camera cam) + { + this.bus = bus; + ray = new MouseRay(cam, window); + window.addCallback(T -> GLFW.glfwSetCursorPosCallback(T, Mouse.this::onDrag)); + window.addCallback(T -> GLFW.glfwSetMouseButtonCallback(T, Mouse.this::onClick)); + window.addCallback(T -> GLFW.glfwSetScrollCallback(T, Mouse.this::onScroll)); + window.addCallback(T -> GLFW.glfwSetCursorEnterCallback(T, (K, V) -> active = V)); + } + + protected void onDrag(long window, double xpos, double ypos) + { + int xChange = (int)xpos - position.getX(); + int yChange = (int)ypos - position.getY(); + position.set((int)xpos, (int)ypos); + pushEvent(new MouseMoveEvent(position.getX(), position.getY(), xChange, yChange)); + } + + protected void onScroll(long window, double xScroll, double yScroll) + { + pushEvent(new MouseScrollEvent(position.getX(), position.getY(), (int)xScroll, (int)yScroll)); + } + + protected void onClick(long window, int button, int action, int mods) + { + if(action == 1) pressedButton.add(button); + else pressedButton.remove(button); + pushEvent(new MouseClickEvent(position.getX(), position.getY(), button, action == 1)); + } + + public static void update() + { + INSTANCE.scroll.negate(); + INSTANCE.movement.negate(); + INSTANCE.ray.update(); + } + + public static boolean isButtonPressed(int button) + { + return INSTANCE.pressedButton.contains(button); + } + + public static int getX() + { + return INSTANCE.position.getX(); + } + + public static int getY() + { + return INSTANCE.position.getY(); + } + + public static Vec2i getMovement() + { + return INSTANCE.movement; + } + + public static boolean isActive() + { + return INSTANCE.active; + } + + public static MouseRay getRayCast() + { + return INSTANCE.ray; + } + + public Vec2i getScroll() + { + return scroll; + } + + public void pushEvent(MouseEvent event) + { + if(active) + { + bus.post(event); + if(!event.isCanceled() || event.isForced()) + { + if(event instanceof MouseMoveEvent) + { + MouseMoveEvent move = (MouseMoveEvent)event; + movement.add(move.xMove, move.yMove); + } + else if(event instanceof MouseClickEvent) + { + MouseClickEvent click = (MouseClickEvent)event; + InputBinding.BINDINGS.updatePressing(BindingType.MOUSE, click.button, click.press); + } + else if(event instanceof MouseScrollEvent) + { + MouseScrollEvent scroll = (MouseScrollEvent)event; + this.scroll.add(scroll.scrollX, scroll.scrollY); + } + } + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/MouseRay.java b/src/main/java/speiger/src/coreengine/rendering/input/MouseRay.java new file mode 100644 index 0000000..b854f81 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/MouseRay.java @@ -0,0 +1,76 @@ +package speiger.src.coreengine.rendering.input; + +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.math.vector.matrix.Matrix4f; +import speiger.src.coreengine.rendering.input.camera.Camera; +import speiger.src.coreengine.rendering.input.raycast.Ray; +import speiger.src.coreengine.rendering.input.window.IWindowListener; +import speiger.src.coreengine.rendering.input.window.Window; + +public class MouseRay implements IWindowListener +{ + final static Vec2i INVALID = Vec2i.MINUS_ONE; + final static Vec3f INVALID_3D = Vec3f.MINUS_ONE; + Camera cam; + Matrix4f windowMatrix; + Vec2i windowBounds = Vec2i.newMutable(); + Vec3f direction = Vec3f.newVec(); + Vec4f helper = Vec4f.newMutable(); + + public MouseRay(Camera cam, Window window) + { + this.cam = cam; + window.addListener(this, true); + } + + @Override + public void onWindowChanged(Window window) + { + windowMatrix = window.getInvertedProjectionMatrix(); + windowBounds.set(window.getWidth(), window.getHeight()); + } + + public void update() + { + if(Mouse.isActive()) + { + direction = calculateMouseRay(); + } + else + { + direction = Vec3f.ZERO; + } + } + + public Vec3f getDirection() + { + return direction; + } + + public Ray getRayCast(float maxReach) + { + return new Ray(cam.getPosition(), cam.getPosition().asImmutable().add(direction.getX() * maxReach, direction.getY() * maxReach, direction.getZ() * maxReach)); + } + +// private int[] createViewPort() +// { +// return GLUtils.VIEW_PORT.getCurrent().asArray(); +// } +// +// private Vec3f calculateWindowRay() +// { +// int[] viewPort = createViewPort(); +// Matrix4f mat = GameManager.INSTANCE.getProjectionBuffer().getProViewMatrix(); +// return mat.unproject(Mouse.getX(), viewPort[3] - Mouse.getY(), 1F, viewPort, Vec3f.newMutable()).sub(mat.unproject(Mouse.getX(), viewPort[3] - Mouse.getY(), 0F, viewPort, Vec3f.newMutable())).normalize(); +// } + + private Vec3f calculateMouseRay() + { + helper.set(2F * Mouse.getX() / windowBounds.getX() - 1.0f, 1F - 2F * Mouse.getY() / windowBounds.getY(), -1F, 1F); + windowMatrix.transform(helper).setZ(-1F).setW(0F); + cam.getInvertedViewMatrix().transform(helper); + return Vec3f.newVec(helper.getX(), helper.getY(), helper.getZ()).normalize(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/bindings/Axis.java b/src/main/java/speiger/src/coreengine/rendering/input/bindings/Axis.java new file mode 100644 index 0000000..9486cf5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/bindings/Axis.java @@ -0,0 +1,173 @@ +package speiger.src.coreengine.rendering.input.bindings; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.lwjgl.glfw.GLFW; + +import speiger.src.coreengine.rendering.input.Mouse; +import speiger.src.coreengine.rendering.input.bindings.utils.BindingType; + +public class Axis +{ + static final Map AXIS_MAP = new LinkedHashMap(); + public static final IAxisSource MAIN_SCROLL_WEEL = new ScrollAxis(false); + public static final IAxisSource SECOND_SCROLL_WEEL = new ScrollAxis(true); + public static final Axis XMOVEMENT = new Axis("XMovement", BindingType.KEYBOARD, GLFW.GLFW_KEY_S, GLFW.GLFW_KEY_W); + public static final Axis STRAVE = new Axis("Strave", BindingType.KEYBOARD, GLFW.GLFW_KEY_D, GLFW.GLFW_KEY_A); + public static final Axis YAW = new Axis("Yaw", BindingType.KEYBOARD, GLFW.GLFW_KEY_E, GLFW.GLFW_KEY_Q); + public static final Axis PITCH = new Axis("Pitch", BindingType.KEYBOARD, GLFW.GLFW_KEY_X, GLFW.GLFW_KEY_C); + public static final Axis ZOOM = new Axis("Zoom", MAIN_SCROLL_WEEL); + + + String name; + IAxisSource source; + + public Axis(String name) + { + this(name, null); + } + + public Axis(String name, BindingType type, int pos, int neg) + { + this(name, new KeySource(type, neg, pos)); + } + + public Axis(String id, IAxisSource provider) + { + name = id; + source = provider; + AXIS_MAP.put(id, this); + } + + public void setSource(BindingType type, int pos, int neg) + { + setSource(new KeySource(type, neg, pos)); + } + + public void setSource(IAxisSource provider) + { + source = provider; + } + + public static Axis getAxis(String name) + { + return AXIS_MAP.get(name); + } + + public IAxisSource getSource() + { + return source; + } + + public float getValue() + { + if(source != null) + { + return source.getValue(); + } + return 0F; + } + + public String getName() + { + return name; + } + + public static interface IAxisSource + { + public float getValue(); + + public boolean isButton(); + + public String getButtonName(boolean first); + + public String getAxisName(); + + public boolean isAlive(); + } + + public static class ScrollAxis implements IAxisSource + { + boolean first; + + public ScrollAxis(boolean data) + { + first = data; + } + + @Override + public float getValue() + { + return first ? Mouse.INSTANCE.getScroll().getX() : Mouse.INSTANCE.getScroll().getY(); + } + + @Override + public boolean isAlive() + { + return true; + } + + @Override + public boolean isButton() + { + return false; + } + + @Override + public String getButtonName(boolean first) + { + return "Not Present"; + } + + @Override + public String getAxisName() + { + return "ScrollWeel "+(first ? 0 : 1); + } + } + + public static class KeySource implements IAxisSource + { + BindingType type; + int neg; + int pos; + + public KeySource(BindingType type, int neg, int pos) + { + this.type = type; + this.neg = neg; + this.pos = pos; + } + + @Override + public float getValue() + { + return (type.isPressed(pos) ? 1F : 0F) - (type.isPressed(neg) ? 1F : 0F); + } + + @Override + public boolean isButton() + { + return true; + } + + @Override + public boolean isAlive() + { + return true; + } + + @Override + public String getButtonName(boolean first) + { + return type.getName(first ? pos : neg); + } + + @Override + public String getAxisName() + { + return "Not Present"; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/bindings/InputBinding.java b/src/main/java/speiger/src/coreengine/rendering/input/bindings/InputBinding.java new file mode 100644 index 0000000..a9aab3b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/bindings/InputBinding.java @@ -0,0 +1,170 @@ +package speiger.src.coreengine.rendering.input.bindings; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; + +import speiger.src.coreengine.rendering.input.bindings.utils.BindingRegistry; +import speiger.src.coreengine.rendering.input.bindings.utils.BindingType; +import speiger.src.coreengine.rendering.input.bindings.utils.ModType; + +public class InputBinding +{ + public static final BindingRegistry BINDINGS = new BindingRegistry(); +// public static final InputBinding SCREEN_SHOT = new InputBinding("misc", "Screenshot", GLFW.GLFW_KEY_F12, ModType.NONE); +// public static final InputBinding GIF_RECORDING = new InputBinding("misc", "GifRecording", GLFW.GLFW_KEY_F12, ModType.SHIFT); +// public static final InputBinding RELOAD_RENDERING = new InputBinding("misc", "Reload Rendering", GLFW.GLFW_KEY_F8); +// public static final InputBinding TOGGLE_UI = new InputBinding("UI", "ToggleUI", GLFW.GLFW_KEY_F11); +// public static final InputBinding TOGGLE_DEBUG = new InputBinding("UI", "ToggleDebug", GLFW.GLFW_KEY_F10, ModType.NONE); +// public static final InputBinding TOGGLE_RELOAD = new InputBinding("UI", "ToggleUIReload", GLFW.GLFW_KEY_F10, ModType.SHIFT); +// public static final InputBinding LAST_UI = new InputBinding("UI", "OpenLastUI", GLFW.GLFW_KEY_F9); + public static final InputBinding MOUSE_ROTATION = new InputBinding("Camera", "Mouse Rotation", 2, BindingType.MOUSE, ModType.IGNORE); + public static final InputBinding MOUSE_MOVEMENT = new InputBinding("Camera", "Mouse Movement", 1, BindingType.MOUSE, ModType.IGNORE); +// public static final InputBinding TOGGLE_GRID = new InputBinding("misc", "Toggle Terrain Grid", GLFW.GLFW_KEY_F2, ModType.NONE); +// public static final InputBinding TOGGLE_LOG_RAYCAST = new InputBinding("misc", "Toggle Log Raycast", GLFW.GLFW_KEY_SCROLL_LOCK); +// public static final InputBinding TOGGLE_FRUSTUM = new InputBinding("debug", "Toggle Frustum", GLFW.GLFW_KEY_PAUSE); + + String category; + String bindingName; + BindingType binding; + int mod; + int currentKey; + + BindingType defaultBinding; + int defaultMod; + int defaultKey; + + boolean pressed; + int timePressed; + List> listeners = new ArrayList>(); + + public InputBinding(String category, String name, int key) + { + this(category, name, key, BindingType.KEYBOARD, ModType.IGNORE); + } + + public InputBinding(String category, String name, int key, BindingType type) + { + this(category, name, key, type, ModType.IGNORE); + } + + public InputBinding(String category, String name, int key, int mod) + { + this(category, name, key, BindingType.KEYBOARD, mod); + } + + public InputBinding(String theCategory, String name, int key, BindingType type, int modType) + { + category = theCategory; + bindingName = name; + defaultKey = key; + currentKey = key; + defaultBinding = type; + binding = type; + defaultMod = modType; + mod = modType; + BINDINGS.registerBinding(this); + } + + public String getBindingName() + { + return bindingName; + } + + public int getKey() + { + return currentKey; + } + + public BindingType getInputType() + { + return binding; + } + + public int getMod() + { + return mod; + } + + public void setKey(int id) + { + BINDINGS.removeBinding(this); + currentKey = id; + BINDINGS.registerBinding(this); + } + + public void setMod(int type) + { + mod = type; + } + + public void setBinding(BindingType type) + { + BINDINGS.removeBinding(this); + binding = type; + BINDINGS.registerBinding(this); + } + + public void addListener(Consumer listener) + { + listeners.add(listener); + } + + public void reset() + { + BINDINGS.removeBinding(this); + currentKey = defaultKey; + mod = defaultMod; + binding = defaultBinding; + BINDINGS.registerBinding(this); + } + + public void setPressed(boolean newPressed) + { + pressed = newPressed && ModType.isActive(mod); + timePressed = pressed ? timePressed + 1 : 0; + if(pressed && listeners.size() > 0) + { + for(int i = 0,m=listeners.size();i>> handlers = new EnumMap>>(BindingType.class); + + public void registerBinding(InputBinding binding) + { + Int2ObjectMap> bindings = handlers.get(binding.getInputType()); + if(bindings == null) + { + bindings = new Int2ObjectLinkedOpenHashMap>(); + handlers.put(binding.getInputType(), bindings); + } + Set entries = bindings.get(binding.getKey()); + if(entries == null) + { + entries = new ObjectLinkedOpenHashSet(); + bindings.put(binding.getKey(), entries); + } + entries.add(binding); + } + + public void removeBinding(InputBinding binding) + { + Int2ObjectMap> bindings = handlers.get(binding.getInputType()); + if(bindings == null) + { + return; + } + Set entries = bindings.get(binding.getKey()); + if(entries.remove(binding) && entries.isEmpty()) + { + bindings.remove(binding.getKey()); + if(bindings.isEmpty()) + { + handlers.remove(binding.getInputType()); + } + } + } + + public void updatePressing(BindingType type, int key, boolean pressed) + { + Int2ObjectMap> bindings = handlers.get(type); + if(bindings == null) + { + return; + } + for(InputBinding binding : bindings.getOrDefault(key, Collections.emptySet())) + { + binding.setPressed(pressed); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/bindings/utils/BindingType.java b/src/main/java/speiger/src/coreengine/rendering/input/bindings/utils/BindingType.java new file mode 100644 index 0000000..7a9a0b7 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/bindings/utils/BindingType.java @@ -0,0 +1,59 @@ +package speiger.src.coreengine.rendering.input.bindings.utils; + +import org.lwjgl.glfw.GLFW; + +import speiger.src.coreengine.rendering.input.Keyboard; +import speiger.src.coreengine.rendering.input.Mouse; + +public enum BindingType +{ + KEYBOARD + { + @Override + public String getName(int key) + { + if(key >= GLFW.GLFW_KEY_F1 && key <= GLFW.GLFW_KEY_F25) return "F"+(key - GLFW.GLFW_KEY_F1+1); + else if(key == GLFW.GLFW_KEY_DELETE) return "Delete"; + String s = GLFW.glfwGetKeyName(key, GLFW.glfwGetKeyScancode(key)); + return s == null ? "Unkonwn" : s.toUpperCase(); + } + + @Override + public boolean isPressed(int key) + { + return Keyboard.isKeyPressed(key); + } + }, + MOUSE + { + @Override + public String getName(int key) + { + return "Button: "+key; + } + + @Override + public boolean isPressed(int key) + { + return Mouse.isButtonPressed(key); + } + }, + NONE + { + @Override + public String getName(int key) + { + return "None"; + } + + @Override + public boolean isPressed(int key) + { + return false; + } + }; + + public abstract String getName(int key); + + public abstract boolean isPressed(int key); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/bindings/utils/ModType.java b/src/main/java/speiger/src/coreengine/rendering/input/bindings/utils/ModType.java new file mode 100644 index 0000000..a5fc46f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/bindings/utils/ModType.java @@ -0,0 +1,36 @@ +package speiger.src.coreengine.rendering.input.bindings.utils; + +import java.util.StringJoiner; + +import speiger.src.coreengine.rendering.input.Keyboard; + +public final class ModType +{ + public static final int NONE = 0; + public static final int CTRL = 1; + public static final int SHIFT = 2; + public static final int ALT = 4; + public static final int IGNORE = 8; + + public static boolean isActive(int flags) + { + boolean ignore = (flags & IGNORE) != 0; + return valid(Keyboard.isCtrlDown(), (flags & CTRL) != 0, ignore) && valid(Keyboard.isShiftDown(), (flags & SHIFT) != 0, ignore) && valid(Keyboard.isAltDown(), (flags & ALT) != 0, ignore); + } + + public static String getMods(int flags) + { + flags &= ~IGNORE; + if(flags == 0) return ""; + StringJoiner builder = new StringJoiner(" + ", "", " + "); + if((flags & CTRL) != 0) builder.add("Ctrl"); + if((flags & SHIFT) != 0) builder.add("Shift"); + if((flags & ALT) != 0) builder.add("Alt"); + return builder.toString(); + } + + static boolean valid(boolean binding, boolean flags, boolean ignore) + { + return (flags == binding) || (!flags && ignore); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/camera/Camera.java b/src/main/java/speiger/src/coreengine/rendering/input/camera/Camera.java new file mode 100644 index 0000000..7716655 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/camera/Camera.java @@ -0,0 +1,206 @@ +package speiger.src.coreengine.rendering.input.camera; + +import java.text.DecimalFormat; +import java.util.List; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.math.vector.ints.Vec3i; +import speiger.src.coreengine.math.vector.matrix.Matrix4f; +import speiger.src.coreengine.rendering.input.window.Window; + +public class Camera +{ + static final Vec3f X_ROTATION = Vec3f.newVec(1, 0, 0); + static final Vec3f Y_ROTATION = Vec3f.newVec(0, 1, 0); + + Vec3f position = Vec3f.newMutable(0, 0, 0); + Vec3f lastPosition = Vec3f.newMutable(); + Vec2f rotation = Vec2f.newMutable(0); + Vec2f lastRotation = Vec2f.newMutable(); + boolean moved = false; + boolean rotated = false; + + Matrix4f viewMatrix = createViewMatrix(); + Matrix4f invertedViewMatrix = new Matrix4f(viewMatrix).invert(); + + ICameraController controller; + Frustrum frustrum; + boolean changed = false; + List> listeners = new ObjectArrayList>(); + + public Camera(Window window) + { + frustrum = new Frustrum(this); + window.addListener(frustrum, false); + setController(controller); + } + + public void setController(ICameraController cont) + { + cont = cont == null ? DefaultCameraController.INSTANCE : cont; + if(controller != null) + { + controller.onRemoved(this); + } + controller = cont; + cont.onAdded(this); + } + + public void addListener(Consumer listner) + { + listeners.add(listner); + } + + public void onInput() + { + controller.onInputUpdate(this); + } + + public void update(float particalTicks) + { + controller.updateCamera(this, particalTicks); + moved = !lastPosition.equals(position); + rotated = !rotation.equals(lastRotation); + if(rotated || moved) + { + viewMatrix = createViewMatrix(); + invertedViewMatrix.load(viewMatrix).invert(); + lastPosition.set(position); + lastRotation.set(rotation); + frustrum.update(); + changed = true; + for(Consumer listener : listeners) + { + listener.accept(this); + } + } + else + { + changed = false; + } + } + + public Matrix4f createViewMatrix() + { + Matrix4f viewMatrix = new Matrix4f(); + viewMatrix.rotate((float)Math.toRadians(rotation.getX()), X_ROTATION); + viewMatrix.rotate((float)Math.toRadians(rotation.getY()), Y_ROTATION); + viewMatrix.translate(position.copy().invert()); + return viewMatrix; + } + + public boolean isChanged() + { + return changed; + } + + public boolean wasMoved() + { + return moved; + } + + public boolean wasRotated() + { + return rotated; + } + + public Matrix4f getViewMatrix() + { + return viewMatrix; + } + + public Matrix4f getInvertedViewMatrix() + { + return invertedViewMatrix; + } + + public Frustrum getFrustrum() + { + return frustrum; + } + + public ICameraController getController() + { + return controller; + } + + public T getController(Class clz) + { + return (T)controller; + } + + public Vec3f getPosition() + { + return position; + } + + public Vec3f getLookDirection() + { + float f = MathUtils.cos(-rotation.getY() * 0.017453292F - (float)Math.PI); + float f1 = MathUtils.sin(-rotation.getY() * 0.017453292F - (float)Math.PI); + float f2 = -MathUtils.cos(-rotation.getX() * 0.017453292F); + float f3 = MathUtils.sin(-rotation.getX() * 0.017453292F); + return Vec3f.newVec(f1 * f2, f3, f * f2); + } + + public Camera setCameraChanged() + { + changed = true; + return this; + } + + public void setPosition(Vec3f vec, boolean update) + { + position.set(vec); + if(update) + { + controller.onPositionUpdate(this); + } + } + + public Vec2i getActualCenter(){return controller.getCenter(this);} + public Vec2i getSolidPosition(){return Vec2i.newVec((int)position.getX(), (int)position.getZ());} + + public void rotate(Vec2i vec){rotate(vec.getY(), vec.getX());} + public void rotate(Vec2f vec){rotate(vec.getY(), vec.getX());} + public void rotate(float x, float y){rotation.add(x, y);} + + public void move(Vec3f vec){move(vec.getX(), vec.getY(), vec.getZ());} + public void move(Vec3i vec){move(vec.getX(), vec.getY(), vec.getZ());} + public void move(float x, float y, float z){position.add(x, y, z);} + + public void moveRotated(float x, float y, float z) + { + if(z != 0) + { + position.add(MathUtils.sin(Math.toRadians(getYaw())) * -1.0f * z, 0, MathUtils.cos(Math.toRadians(getYaw())) * z); + } + if(x != 0) + { + position.add(MathUtils.sin(Math.toRadians(getYaw() - 90)) * -1.0f * x, 0, MathUtils.cos(Math.toRadians(getYaw() - 90)) * x); + } + if(y != 0) + { + position.add(0F, y, 0F); + } + } + + public Vec2f getRotation(){return rotation;} + public float getYaw(){return rotation.getY();} + public void setYaw(float value){rotation.setY(value);} + + public float pitch(){return rotation.getX();} + public void setPitch(float value){rotation.setX(value);} + + @Override + public String toString() + { + DecimalFormat format = new DecimalFormat("###,###"); + return "Camera[x="+format.format(position.getX())+", y="+format.format(position.getY())+", z="+format.format(position.getZ())+", Yaw="+format.format(getYaw())+", Pitch="+format.format(pitch())+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/camera/DefaultCameraController.java b/src/main/java/speiger/src/coreengine/rendering/input/camera/DefaultCameraController.java new file mode 100644 index 0000000..8f8049c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/camera/DefaultCameraController.java @@ -0,0 +1,18 @@ +package speiger.src.coreengine.rendering.input.camera; + +import speiger.src.coreengine.math.vector.ints.Vec2i; + +public enum DefaultCameraController implements ICameraController +{ + INSTANCE; + + @Override + public Vec2i getCenter(Camera cam){return cam.getSolidPosition();} + @Override + public void onInputUpdate(Camera camera){} + @Override + public void updateCamera(Camera camera, float particalTicks){} + @Override + public void onPositionUpdate(Camera camera){} + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/camera/Frustrum.java b/src/main/java/speiger/src/coreengine/rendering/input/camera/Frustrum.java new file mode 100644 index 0000000..79a4012 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/camera/Frustrum.java @@ -0,0 +1,127 @@ +package speiger.src.coreengine.rendering.input.camera; + +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.matrix.Matrix4f; +import speiger.src.coreengine.rendering.input.window.IWindowListener; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.utils.helpers.InternalThreadPools; + +public class Frustrum implements IWindowListener +{ + public static boolean FREEZE_FRUSTUM = false; + Camera cam; + Matrix4f projectMatrix = new Matrix4f(); + Vec4f[] frustrumPlanes = new Vec4f[6]; + + public Frustrum(Camera camera) + { + cam = camera; + projectMatrix = new Matrix4f(); + for(int i = 0;i<6;i++) + { + frustrumPlanes[i] = Vec4f.newMutable(); + } + } + + @Override + public void onWindowChanged(Window window) + { + projectMatrix.load(window.getProjectionMatrix()); + update(); + } + + public void update() + { + if(FREEZE_FRUSTUM) + { + return; + } + Matrix4f checkMatrix = InternalThreadPools.MATRIX.get().load(projectMatrix).multiply(cam.getViewMatrix()); + for(int i = 0;i<6;i++) + { + checkMatrix.storeFrustrumPlane(i, frustrumPlanes[i]); + } + InternalThreadPools.MATRIX.accept(checkMatrix); + } + + public boolean isPointInFrustrum(Vec3f vec) + { + return isPointInFrustrum(vec.getX(), vec.getY(), vec.getZ()); + } + + public boolean isPointInFrustrum(float x, float y, float z) + { + return isSphereInFrustrum(x, y, z, 0); + } + + public boolean isSphereInFrustrum(Vec3f vec, float radius) + { + return isSphereInFrustrum(vec.getX(), vec.getY(), vec.getZ(), radius); + } + + public boolean isSphereInFrustrum(float x, float y, float z, float radius) + { + for(int i = 0;i<6;i++) + { + if(getDistance(i, x, y, z) < -radius) + { + return false; + } + } + return true; + } + + public boolean isCubeInFrustrum(Vec3f min, Vec3f max) + { + return isCubeInFrustrum(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ()); + } + + public boolean isCubeInFrustrum(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) + { + for(int i = 0;i<6;i++) + { + if(isOutsideOfPlane(i, minX, minY, minZ, maxX, maxY, maxZ)) + { + return false; + } + } + return true; + } + + public boolean isPlaneInFrustrum(Vec2f min, Vec2f max) + { + return isPlaneInFrustrum(min.getX(), min.getY(), max.getX(), min.getY()); + } + + public boolean isPlaneInFrustrum(float minX, float minZ, float maxX, float maxZ) + { + for(int i = 0;i<6;i++) + { + if(isOutsideOfPlane(i, minX, minZ, maxX, maxZ)) + { + return false; + } + } + return true; + } + + private float getDistance(int index, float x, float y, float z) + { + Vec4f vec = frustrumPlanes[index]; + return vec.getX() * x + vec.getY() * y + vec.getZ() * z + vec.getW(); + } + + private boolean isOutsideOfPlane(int index, float minX, float minZ, float maxX, float maxZ) + { + Vec4f vec = frustrumPlanes[index]; + return vec.getX() * (vec.getX() < 0 ? minX : maxX) + vec.getZ() * (vec.getZ() < 0 ? minZ : maxZ) >= -vec.getW(); + } + + private boolean isOutsideOfPlane(int index, float minX, float minY, float minZ, float maxX, float maxY, float maxZ) + { + Vec4f vec = frustrumPlanes[index]; + return vec.getX() * (vec.getX() < 0 ? minX : maxX) + vec.getY() * (vec.getY() < 0 ? minY : maxY) + vec.getZ() * (vec.getZ() < 0 ? minZ : maxZ) < -vec.getW(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/camera/ICameraController.java b/src/main/java/speiger/src/coreengine/rendering/input/camera/ICameraController.java new file mode 100644 index 0000000..d17d7df --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/camera/ICameraController.java @@ -0,0 +1,14 @@ +package speiger.src.coreengine.rendering.input.camera; + +import speiger.src.coreengine.math.vector.ints.Vec2i; + +public interface ICameraController +{ + public Vec2i getCenter(Camera cam); + public void onInputUpdate(Camera camera); + public void updateCamera(Camera camera, float particalTicks); + public void onPositionUpdate(Camera camera); + public default void onAdded(Camera cam){}; + public default void onRemoved(Camera cam){}; + public default float getZoom(){return 0F;} +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/input/events/FileDropEvent.java b/src/main/java/speiger/src/coreengine/rendering/input/events/FileDropEvent.java new file mode 100644 index 0000000..3b5f028 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/events/FileDropEvent.java @@ -0,0 +1,42 @@ +package speiger.src.coreengine.rendering.input.events; + +import java.io.File; + +import speiger.src.coreengine.rendering.input.window.ScaledResolution; + +public class FileDropEvent extends MouseEvent +{ + final File file; + final String fileName; + final String fileExtension; + + public FileDropEvent(File file, int x, int y, ScaledResolution res) + { + super(x, y); + this.file = file; + fileName = file.getName(); + fileExtension = fileName.substring(fileName.lastIndexOf(".")+1); + convertToScreenCoords(res); + } + + public String getFileExtension() + { + return fileExtension; + } + + public String getFileName() + { + return fileName; + } + + public File getFile() + { + return file; + } + + @Override + public boolean isCancelable() + { + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/events/KeyEvent.java b/src/main/java/speiger/src/coreengine/rendering/input/events/KeyEvent.java new file mode 100644 index 0000000..8434429 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/events/KeyEvent.java @@ -0,0 +1,34 @@ +package speiger.src.coreengine.rendering.input.events; + +import speiger.src.coreengine.utils.eventbus.Event; + +public abstract class KeyEvent extends Event +{ + @Override + public boolean isCancelable() + { + return true; + } + + public static class KeyPressEvent extends KeyEvent + { + public final int key; + + public KeyPressEvent(int key) + { + this.key = key; + } + } + + public static class CharTypeEvent extends KeyEvent + { + public final char character; + public final int codePoint; + + public CharTypeEvent(char character, int codePoint) + { + this.character = character; + this.codePoint = codePoint; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/events/MouseEvent.java b/src/main/java/speiger/src/coreengine/rendering/input/events/MouseEvent.java new file mode 100644 index 0000000..c519297 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/events/MouseEvent.java @@ -0,0 +1,88 @@ +package speiger.src.coreengine.rendering.input.events; + +import speiger.src.coreengine.rendering.input.window.ScaledResolution; +import speiger.src.coreengine.utils.eventbus.Event; + +public class MouseEvent extends Event +{ + public int mouseX; + public int mouseY; + public final int originX; + public final int originY; + + public MouseEvent(int x, int y) + { + mouseX = x; + mouseY = y; + originX = x; + originY = y; + } + + public void convertToOrigin() + { + mouseX = originX; + mouseY = originY; + } + + public void convertToScreenCoords(ScaledResolution res) + { + res.getScaledMouseEvent(this); + } + + @Override + public boolean isCancelable() + { + return true; + } + + public boolean isForced() + { + return false; + } + + public static class MouseClickEvent extends MouseEvent + { + public final int button; + public final boolean press; + + public MouseClickEvent(int x, int y, int button, boolean press) + { + super(x, y); + this.button = button; + this.press = press; + } + + //Forced so Buttons are always released. Maybe gets disabled. Not sure yet + @Override + public boolean isForced() + { + return !press; + } + } + + public static class MouseMoveEvent extends MouseEvent + { + public final int xMove; + public final int yMove; + + public MouseMoveEvent(int x, int y, int xMove, int yMove) + { + super(x, y); + this.xMove = xMove; + this.yMove = yMove; + } + } + + public static class MouseScrollEvent extends MouseEvent + { + public final int scrollX; + public final int scrollY; + + public MouseScrollEvent(int x, int y, int scrollX, int scrollY) + { + super(x, y); + this.scrollX = scrollX; + this.scrollY = scrollY; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/raycast/Ray.java b/src/main/java/speiger/src/coreengine/rendering/input/raycast/Ray.java new file mode 100644 index 0000000..d520b66 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/raycast/Ray.java @@ -0,0 +1,35 @@ +package speiger.src.coreengine.rendering.input.raycast; + +import speiger.src.coreengine.math.vector.floats.Vec3f; + +public class Ray +{ + protected Vec3f start; + protected Vec3f end; + + public Ray(Vec3f start, Vec3f end) + { + this.start = start.asImmutable(); + this.end = end.asImmutable(); + } + + public Vec3f getStart() + { + return start; + } + + public Vec3f getEnd() + { + return end; + } + + public Vec3f interpolate(float progress) + { + return start.lerp(end, progress, Vec3f.ZERO); + } + + public Vec3f distance() + { + return end.asMutable().sub(start); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/raycast/RayCollisions.java b/src/main/java/speiger/src/coreengine/rendering/input/raycast/RayCollisions.java new file mode 100644 index 0000000..15bb8d4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/raycast/RayCollisions.java @@ -0,0 +1,44 @@ +package speiger.src.coreengine.rendering.input.raycast; + +import speiger.src.coreengine.math.vector.floats.Vec3f; + +public class RayCollisions +{ + public static RayResult intersectQuad(Ray ray, Vec3f first, Vec3f second, Vec3f third, Vec3f fourth) + { + RayResult result = intersectTriangle(ray, first, second, third); + return result != null ? result : intersectTriangle(ray, first, fourth, third); + } + + public static RayResult intersectTriangle(Ray ray, Vec3f first, Vec3f second, Vec3f third) + { + first = first.asImmutable(); + second = second.asImmutable(); + third = third.asImmutable(); + RayResult result = intersectPlane(ray, first, second, third); + if(result == null) return null; + Vec3f hit = result.getHit(); + Vec3f cross = second.sub(first).crossProduct(third.sub(first), Vec3f.ZERO); + if(cross.dotProduct(second.sub(first).crossProduct(hit.sub(first))) > 0) + { + if(cross.dotProduct(third.sub(second).crossProduct(hit.sub(second))) > 0) + { + if(cross.dotProduct(first.sub(third).crossProduct(hit.sub(third))) > 0) + { + return result; + } + } + } + return null; + } + + static RayResult intersectPlane(Ray ray, Vec3f first, Vec3f second, Vec3f third) + { + Vec3f cross = second.sub(first).crossProduct(third.sub(first), Vec3f.ZERO); + Vec3f distance = ray.distance(); + double hitDot = distance.dotProduct(cross); + if(Math.abs(hitDot) < 0.0000000001D) return null; + double step = first.sub(ray.getStart()).dotProduct(cross) / hitDot; + return new RayResult(ray, ray.interpolate((float)step), step); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/raycast/RayResult.java b/src/main/java/speiger/src/coreengine/rendering/input/raycast/RayResult.java new file mode 100644 index 0000000..fad7d23 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/raycast/RayResult.java @@ -0,0 +1,38 @@ +package speiger.src.coreengine.rendering.input.raycast; + +import speiger.src.coreengine.math.vector.floats.Vec3f; + +public class RayResult +{ + Ray ray; + Vec3f hit; + double distance; + public Object subData; + + public RayResult(Ray ray, Vec3f hit, double distance) + { + this.ray = ray; + this.hit = hit; + this.distance = distance; + } + + public double getDistance() + { + return distance; + } + + public Vec3f getHit() + { + return hit; + } + + public Ray getRay() + { + return ray; + } + + public T getSubData(Class clz) + { + return subData != null && clz.isInstance(subData) ? clz.cast(subData) : null; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/window/IWindowListener.java b/src/main/java/speiger/src/coreengine/rendering/input/window/IWindowListener.java new file mode 100644 index 0000000..26f6913 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/window/IWindowListener.java @@ -0,0 +1,7 @@ +package speiger.src.coreengine.rendering.input.window; + + +public interface IWindowListener +{ + public void onWindowChanged(Window window); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/window/Monitor.java b/src/main/java/speiger/src/coreengine/rendering/input/window/Monitor.java new file mode 100644 index 0000000..87cb36e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/window/Monitor.java @@ -0,0 +1,110 @@ +package speiger.src.coreengine.rendering.input.window; + +import java.util.List; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWVidMode; + +import speiger.src.collections.longs.maps.impl.hash.Long2ObjectOpenHashMap; +import speiger.src.collections.longs.maps.interfaces.Long2ObjectMap; +import speiger.src.collections.objects.lists.ObjectArrayList; + +public class Monitor +{ + final long monitor; + List modes = new ObjectArrayList(); + VideoMode defaultMode; + int xOffset; + int yOffset; + + public Monitor(long monitor) + { + this.monitor = monitor; + GLFWVidMode.Buffer buffer = GLFW.glfwGetVideoModes(monitor); + for(int i = buffer.limit() - 1;i >= 0;--i) + { + buffer.position(i); + VideoMode videomode = new VideoMode(this, buffer); + if(videomode.getRedBits() >= 8 && videomode.getGreenBits() >= 8 && videomode.getBlueBits() >= 8) + { + modes.add(videomode); + } + } + defaultMode = new VideoMode(this, GLFW.glfwGetVideoMode(monitor)); + int[] xPos = new int[1]; + int[] yPos = new int[1]; + GLFW.glfwGetMonitorPos(monitor, xPos, yPos); + xOffset = xPos[0]; + yOffset = yPos[0]; + } + + public String getMonitorName() + { + return GLFW.glfwGetMonitorName(monitor); + } + + public long getMonitorId() + { + return monitor; + } + + public int size() + { + return modes.size(); + } + + public VideoMode getMode(int index) + { + return modes.get(index); + } + + public boolean contains(VideoMode mode) + { + return modes.indexOf(mode) != -1; + } + + public List getVideoMods() + { + return new ObjectArrayList(modes); + } + + public VideoMode getDefaultMode() + { + return defaultMode; + } + + public int getXOffset() + { + return xOffset; + } + + public int getYOffset() + { + return yOffset; + } + + public long getInBounds(Window window) + { + if(xOffset <= window.getXPos() + window.getActualWidth() && xOffset + defaultMode.getWidth() >= window.getXPos() && yOffset <= window.getYPos() + window.getActualHeight() && yOffset + defaultMode.getHeight() >= window.getYPos()) + { + int xArea = Math.abs(window.getActualWidth() * defaultMode.getWidth()); + int yArea = Math.abs(window.getActualHeight() * defaultMode.getHeight()); + int areaI = (Math.min(window.getXPos() + window.getActualWidth(), xOffset + defaultMode.getWidth()) - Math.max(window.getXPos(), xOffset)) * (Math.min(window.getYPos() + window.getActualHeight(), yOffset + defaultMode.getHeight()) - Math.max(window.getYPos(), yOffset)); + return (xArea + yArea) - areaI; + } + return 0L; + } + + public static Long2ObjectMap createMonitors() + { + Long2ObjectMap monitors = new Long2ObjectOpenHashMap(); + PointerBuffer buffer = GLFW.glfwGetMonitors(); + for(int i = 0,m=buffer.limit();i callbacks = new ObjectArrayList(); + List listeners = new ObjectArrayList(); + GLCapabilities capabilities; + + public Window() + { + GLFW.glfwDefaultWindowHints(); + GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, 0); // 0 = False, 1 = true + GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, 1); + GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4); + GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 0); + GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE);//GLFW.GLFW_OPENGL_COMPAT_PROFILE); + GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, 1); + } + + protected void initWindowListeners() + { + addCallback(T -> GLFW.glfwSetFramebufferSizeCallback(T, this::onResize)); + addCallback(T -> GLFW.glfwSetWindowMaximizeCallback(T, (K, V) -> flags.setFlag(FLAG_WINDOW_CHANGE))); + addCallback(T -> GLFW.glfwSetWindowPosCallback(T, (W, X, Y) -> setPosition(X, Y))); + addCallback(T -> GLFW.glfwSetWindowFocusCallback(T, (K, V) -> flags.setFlag(FLAG_FOCUS, V))); + } + + Window createWindow(WindowStats stat) + { + owner = stat.provider; + title = stat.name; + width = stat.width; + height = stat.height; + aaLevel = stat.antiAlis; + videoMode = stat.videoMode; + flags.setFlag(FLAG_BORDERLESS, stat.borderless); + flags.setFlag(FLAG_FULL_SCREEN, stat.fullScreen); + flags.setFlag(FLAG_FLOATING, stat.floating); + flags.setFlag(FLAG_VSYNC, stat.vsync); + flags.setFlag(FLAG_CPU_FPS_CAP, stat.fpsCap); + GLFW.glfwWindowHint(GLFW.GLFW_SAMPLES, aaLevel); + GLFW.glfwWindowHint(GLFW.GLFW_DECORATED, flags.isFlagNotSet(FLAG_FULL_SCREEN) && flags.isFlagSet(FLAG_BORDERLESS) ? 0 : 1); + GLFW.glfwWindowHint(GLFW.GLFW_FLOATING, flags.isFlagNotSet(FLAG_FULL_SCREEN) && flags.isFlagSet(FLAG_FLOATING) ? 1 : 0); + if(videoMode == null || videoMode.getMonitor() == null) + { + throw new IllegalStateException("Monitor or Video Mode is missing: "+videoMode); + } + Monitor monitor = videoMode.getMonitor(); + windowId = GLFW.glfwCreateWindow(flags.isFlagSet(FLAG_FULL_SCREEN) ? videoMode.getWidth() : width, flags.isFlagSet(FLAG_FULL_SCREEN) ? videoMode.getHeight() : height, title, flags.isFlagSet(FLAG_FULL_SCREEN) ? monitor.getMonitorId() : 0L, stat.parent == null ? 0 : stat.parent.windowId); + if(windowId == 0) + { + throw new IllegalStateException("Window Couldn't be Created"); + } + initWindowListeners(); + GLFW.glfwMakeContextCurrent(windowId); + if(capabilities == null) + { + capabilities = GL.createCapabilities(); + } + if(flags.isFlagNotSet(FLAG_FULL_SCREEN)) + { + xPos = stat.center ? (monitor.getXOffset() + (videoMode.getWidth() / 2) - (width / 2)) : monitor.getXOffset() + xPos; + yPos = stat.center ? (monitor.getYOffset() + (videoMode.getHeight() / 2) - (width / 2)) : monitor.getYOffset() + yPos; + GLFW.glfwSetWindowPos(windowId, xPos, yPos); + } + else + { + xPos = monitor.getXOffset() + (videoMode.getWidth() / 2) - (width / 2); + yPos = monitor.getYOffset() + (videoMode.getHeight() / 2) - (width / 2); + tempWidth = width; + tempHeight = height; + } + GLFW.glfwSwapInterval(flags.isFlagSet(FLAG_VSYNC) ? 1 : 0); + fetchWindowBounds(); + updateViewPort(); + return this; + } + + protected void onResize(long window, int newWidth, int newHeight) + { + if(flags.isFlagNotSet(FLAG_FULL_SCREEN | FLAG_MAXIMIZED) && window == windowId && (width != newWidth || height != newHeight)) + { + width = newWidth; + height = newHeight; + flags.setFlag(FLAG_WINDOW_CHANGE); + } + } + + protected void updateCallbacks() + { + for(int i = 0,m=callbacks.size();i provider) + { + if(provider == null) + { + return null; + } + WindowCallback callback = new WindowCallback(provider); + callbacks.add(callback); + if(windowId != 0) + { + callback.reload(windowId); + } + return callback; + } + + public void removeCallback(WindowCallback call) + { + if(callbacks.remove(call)) + { + call.destroy(); + } + } + + private void createProjectionMatrix() + { +// float aspectRatio = (float)width / (float)height; +// float y_scale = (float)((1f / Math.tan(Math.toRadians(fieldOfView / 2f))) * aspectRatio); +// float x_scale = y_scale / aspectRatio; +// float frustum_length = far_plane - near_plane; +// projection.setIdentity(); +// projection.set(0, x_scale); +// projection.set(5, y_scale); +// projection.set(10, -((far_plane + near_plane) / frustum_length)); +// projection.set(11, -1); +// projection.set(14, -((2 * near_plane * far_plane) / frustum_length)); +// projection.set(15, 0); +// invertedProjection.load(projection).invert(); +// + projection.setIdentity().setPerspective((float)Math.toRadians(fieldOfView), (float)width / (float)height, near_plane, far_plane); + invertedProjection.load(projection).invert(); + } + + protected void fetchWindowBounds() + { + int[] width = new int[1]; + int[] height = new int[1]; + GLFW.glfwGetWindowSize(windowId, width, height); + this.width = width[0]; + this.height = height[0]; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/window/WindowCallback.java b/src/main/java/speiger/src/coreengine/rendering/input/window/WindowCallback.java new file mode 100644 index 0000000..8365d8c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/window/WindowCallback.java @@ -0,0 +1,30 @@ +package speiger.src.coreengine.rendering.input.window; + +import java.util.function.LongFunction; + +import org.lwjgl.system.Callback; + +public class WindowCallback +{ + Callback callback; + LongFunction provider; + + public WindowCallback(LongFunction provider) + { + this.provider = provider; + } + + public void reload(long windowID) + { + callback = provider.apply(windowID); + } + + public void destroy() + { + if(callback != null) + { + callback.free(); + callback = null; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/window/WindowProvider.java b/src/main/java/speiger/src/coreengine/rendering/input/window/WindowProvider.java new file mode 100644 index 0000000..ce72431 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/window/WindowProvider.java @@ -0,0 +1,228 @@ +package speiger.src.coreengine.rendering.input.window; + +import java.util.List; + +import org.lwjgl.glfw.GLFW; +import org.lwjgl.system.Callback; + +import speiger.src.collections.longs.maps.interfaces.Long2ObjectMap; +import speiger.src.collections.objects.lists.ObjectArrayList; + +public class WindowProvider +{ + Long2ObjectMap monitors; + List callbacks = new ObjectArrayList<>(); + List activeWindows = new ObjectArrayList<>(); + + public void init() + { + monitors = Monitor.createMonitors(); + callbacks.add(GLFW.glfwSetMonitorCallback(this::onMonitorChange)); + } + + protected void onMonitorChange(long id, int state) + { + switch(state) + { + case GLFW.GLFW_CONNECTED: + monitors.put(id, new Monitor(id)); + break; + case GLFW.GLFW_DISCONNECTED: + monitors.remove(state); + break; + } + } + + public WindowStats createBuilder() + { + return new WindowStats(this); + } + + public Monitor getMonitors(long id) + { + return monitors.get(id); + } + + public List getActiveWindows() + { + return activeWindows; + } + + public Monitor getMonitorFromWindow(Window window) + { + Monitor monitor = null; + long overlap = Long.MIN_VALUE; + for(Monitor entry : monitors.values()) + { + long thisOverlap = entry.getInBounds(window); + if(thisOverlap > overlap) + { + monitor = entry; + overlap = thisOverlap; + } + } + return monitor == null ? monitors.get(GLFW.glfwGetPrimaryMonitor()) : monitor; + } + + public List getMonitors() + { + return new ObjectArrayList(monitors.values()); + } + + public void destroy() + { + for(int i = 0,m=callbacks.size();i 0) + { + this.width = width; + } + return this; + } + + public WindowStats setHeight(int height) + { + if(height > 0) + { + this.height = height; + } + return this; + } + + public WindowStats setAntiAlis(int antiAlis) + { + if(antiAlis >= 0) + { + this.antiAlis = antiAlis; + } + return this; + } + + public WindowStats setSubWindow(Window parent) + { + this.parent = parent; + return this; + } + + public WindowStats setFullScreen(boolean fullScreen) + { + this.fullScreen = fullScreen; + if(fullScreen) + { + borderless = false; + floating = false; + } + return this; + } + + public WindowStats setBorderless(boolean borderless) + { + this.borderless = borderless; + if(borderless) + { + fullScreen = false; + } + return this; + } + + public WindowStats setFloating(boolean floating) + { + this.floating = floating; + if(floating) + { + fullScreen = false; + } + return this; + } + + public WindowStats setCentered(boolean center) + { + this.center = center; + return this; + } + + public WindowStats setMonitor(VideoMode videoMode) + { + if(videoMode != null && videoMode.getMonitor() != null) + { + this.videoMode = videoMode; + } + return this; + } + + public WindowStats setVsync(boolean value) + { + vsync = value; + return this; + } + + public WindowStats setFPSCap(boolean cap) + { + fpsCap = cap; + return this; + } + + public Window build() + { + Window window = new Window().createWindow(this); + provider.activeWindows.add(window); + return window; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/models/BufferAttribute.java b/src/main/java/speiger/src/coreengine/rendering/models/BufferAttribute.java new file mode 100644 index 0000000..33e2d7d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/models/BufferAttribute.java @@ -0,0 +1,86 @@ +package speiger.src.coreengine.rendering.models; + +import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL33; + +public class BufferAttribute +{ + public final int index; + public final int size; + public final GLDataType type; + public final boolean normal; + public final int instancedAmount; + + public BufferAttribute(int index, int size) + { + this(index, size, GLDataType.FLOAT, false, 0); + } + + public BufferAttribute(int index, int size, GLDataType type) + { + this(index, size, type, false, 0); + } + + public BufferAttribute(int index, int size, GLDataType type, boolean normal) + { + this(index, size, type, normal, 0); + } + + public BufferAttribute(int index, int size, GLDataType type, int instancedAmount) + { + this(index, size, type, false, instancedAmount); + } + + public BufferAttribute(int index, int size, GLDataType type, boolean normal, int instancedAmount) + { + this.index = index; + this.size = size; + this.type = type; + this.normal = normal; + this.instancedAmount = instancedAmount; + } + + public static BufferAttribute[] createArrayAttribute(int startIndex, int width, int size, GLDataType type) + { + return createArrayAttribute(startIndex, width, size, type, false, 0); + } + + public static BufferAttribute[] createArrayAttribute(int startIndex, int width, int size, GLDataType type, boolean normal) + { + return createArrayAttribute(startIndex, width, size, type, normal, 0); + } + + public static BufferAttribute[] createArrayAttribute(int startIndex, int width, int size, GLDataType type, int instanceAmount) + { + return createArrayAttribute(startIndex, width, size, type, false, instanceAmount); + } + + public static BufferAttribute[] createArrayAttribute(int startIndex, int width, int size, GLDataType type, boolean normal, int instancedAmount) + { + BufferAttribute[] attributes = new BufferAttribute[width]; + for(int i = 0;i 0) + { + GL33.glVertexAttribDivisor(index, instancedAmount); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/models/DrawCall.java b/src/main/java/speiger/src/coreengine/rendering/models/DrawCall.java new file mode 100644 index 0000000..9ce94bd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/models/DrawCall.java @@ -0,0 +1,88 @@ +package speiger.src.coreengine.rendering.models; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.List; + +public class DrawCall +{ + final int glType; + final int textureId; + final byte[] data; + final int vertexCount; + + public DrawCall(int glType, int textureId, float[] data, int vertexCount) + { + this.glType = glType; + this.textureId = textureId; + this.vertexCount = vertexCount; + this.data = new byte[data.length*4]; + ByteBuffer.wrap(this.data).order(ByteOrder.nativeOrder()).asFloatBuffer().put(data); + } + + public DrawCall(int glType, int textureId, byte[] data, int vertexCount) + { + this.glType = glType; + this.textureId = textureId; + this.data = data; + this.vertexCount = vertexCount; + } + + public byte[] getData() + { + return data; + } + + public int getGLType() + { + return glType; + } + + public int getTextureId() + { + return textureId; + } + + public int getVertexCount() + { + return vertexCount; + } + + public static byte[] merge(List drawCalls) + { + int total = 0; + for(int i = 0,m=drawCalls.size();i currentCapacity) + { + if(currentCapacity == limit) + { + throw new RuntimeException("THIS SHOULD NEVER HAPPEN!: "+currentCapacity+":"+limit); + } + int old = currentCapacity; + currentCapacity = Math.min(Math.max(currentCapacity + (currentCapacity / 2), capacity), limit); + buffer.bind().grow(old * bytesPerIndex, currentCapacity * bytesPerIndex, "Growable Vertex Buffer").unbind(); + } + else if(capacity < currentCapacity / 2 && currentCapacity != 10) + { + int old = currentCapacity; + currentCapacity = Math.max(10, capacity); + buffer.bind().shrink(old * bytesPerIndex, currentCapacity * bytesPerIndex, "Growable Vertex Buffer").unbind(); + } + return buffer; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/models/MultiVertexBuffer.java b/src/main/java/speiger/src/coreengine/rendering/models/MultiVertexBuffer.java new file mode 100644 index 0000000..6bbcda1 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/models/MultiVertexBuffer.java @@ -0,0 +1,99 @@ +package speiger.src.coreengine.rendering.models; + +public class MultiVertexBuffer +{ + VertexBuffer buffer; + final int bytesPerIndex; + final int limit; + final int startSize; + final float growRate; + final int padding; + final int[][] offsets; + int currentCapacity; + + public MultiVertexBuffer(VertexBuffer buffer, int bytesPerIndex, int limit, int startSize, int padding, float growRate, int[][] offsets) + { + super(); + this.buffer = buffer; + this.bytesPerIndex = bytesPerIndex; + this.limit = limit; + this.startSize = startSize; + this.padding = padding; + this.growRate = growRate; + this.offsets = offsets; + currentCapacity = startSize; + buffer.bind().allocateMemory(startSize * bytesPerIndex).unbind(); + } + + public boolean ensureSize(int index, int capacity) + { + int[] offset = offsets[index]; + int room = offset[2] - offset[0]; + if(capacity >= room) + { + int newCap = Math.max((int)(room * growRate), capacity+1); + offset[2] = offset[0] + newCap; + grow(index+1, newCap - room); + return true; + } +// else if(capacity < room * growRate && capacity != padding) +// { +// int newSize = Math.max((int)(room * growRate), padding); +// offset[2] = offset[0] + newSize; +// shrink(index+1, room - newSize); +// return true; +// } + return false; + } + + protected void grow(int index, int extra) + { + if(index == offsets.length) + { + int newCap = offsets[offsets.length-1][2]+extra; + if(newCap > currentCapacity) + { + if(currentCapacity == limit) + { + throw new RuntimeException("THIS SHOULD NEVER HAPPEN!: "+currentCapacity+":"+limit); + } + int old = currentCapacity; + currentCapacity = Math.min(Math.max(currentCapacity + (currentCapacity / 2), newCap), limit); + buffer.bind().grow(old * bytesPerIndex, currentCapacity * bytesPerIndex, "Multi Vertex Buffer").unbind(); + } + return; + } + int startBytes = offsets[index][0]*bytesPerIndex; + int endBytes = offsets[offsets.length-1][2]*bytesPerIndex; + for(int i = index;i attributes = new ArrayList(); + VertexBuffer indeciesBuffer; + List buffers = new ArrayList(); + + public VertexArray() + { + arrayID = GL30.glGenVertexArrays(); + } + + public abstract int getVertexCount(); + + protected void bind() + { + GL30.glBindVertexArray(arrayID); + } + + public void bindArray() + { + bind(); + for(BufferAttribute attr : attributes) + { + attr.bind(); + } + } + + protected BufferAttribute getAttribute(int index) + { + return attributes.get(index); + } + + protected void unbind() + { + GL30.glBindVertexArray(0); + } + + public void unbindArray() + { + for(BufferAttribute attr : attributes) + { + attr.unbind(); + } + unbind(); + } + + public int getArrayID() + { + return arrayID; + } + + public boolean isValid() + { + return arrayID != -1; + } + + public boolean isInvalid() + { + return arrayID == -1; + } + + public void remove() + { + if(arrayID == -1) + { + return; + } + GL30.glDeleteVertexArrays(arrayID); + if(indeciesBuffer != null) + { + indeciesBuffer.delete(); + } + for(VertexBuffer buffer : buffers) + { + buffer.delete(); + } + arrayID = -1; + } + + public VertexBuffer getIndeciesBuffer() + { + return indeciesBuffer; + } + + public VertexBuffer getBuffer(int index) + { + return buffers.get(index); + } + + public void createIndenciesBuffer(int[] indencies) + { + indeciesBuffer = new VertexBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER); + indeciesBuffer.bind(); + indeciesBuffer.setBufferData(indencies); + } + + public void updateIndenciesBuffer(int[] indencies) + { + indeciesBuffer.bind(); + indeciesBuffer.setBufferData(indencies); + } + + public VertexBuffer createSimpleBuffer(int index, int size, GLDataType type) + { + VertexBuffer buffer = new VertexBuffer(GL15.GL_ARRAY_BUFFER); + buffers.add(buffer); + buffer.bind(); + BufferAttribute attribute = new BufferAttribute(index, size, type); + attributes.add(attribute); + finishAttributes(attribute); + return buffer; + } + + public VertexBuffer createBuffer(BufferAttribute...array) + { + VertexBuffer buffer = new VertexBuffer(GL15.GL_ARRAY_BUFFER); + buffers.add(buffer); + attributes.addAll(Arrays.asList(array)); + buffer.bind(); + finishAttributes(array); + return buffer; + } + + public void removeBuffer(int attr) + { + for(int i = 0;i supplier) + { + FloatBuffer buffer = MemoryUtil.memAllocFloat(neededFloats); + supplier.accept(buffer); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining() * 4L); + GL15.glBufferData(bufferType, buffer, bufferState); + MemoryUtil.memFree(buffer); + return this; + } + + public VertexBuffer setBufferDataInt(int neededInts, Consumer supplier) + { + IntBuffer buffer = MemoryUtil.memAllocInt(neededInts); + supplier.accept(buffer); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining() * 4L); + GL15.glBufferData(bufferType, buffer, bufferState); + MemoryUtil.memFree(buffer); + return this; + } + + public VertexBuffer setBufferDataByte(int neededBytes, Consumer supplier) + { + ByteBuffer buffer = MemoryUtil.memAlloc(neededBytes); + supplier.accept(buffer); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining()); + GL15.glBufferData(bufferType, buffer, bufferState); + MemoryUtil.memFree(buffer); + return this; + } + + public VertexBuffer fillExternalBuffer(FloatBuffer buffer, int floatOffset) + { + GL15.glBufferSubData(bufferType, floatOffset, buffer); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining() * 4); + return this; + } + + public VertexBuffer fillExternalBuffer(IntBuffer buffer, int intOffset) + { + GL15.glBufferSubData(bufferType, intOffset, buffer); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining() * 4); + return this; + } + + public VertexBuffer fillExternalBuffer(ByteBuffer buffer, int byteOffset) + { + GL15.glBufferSubData(bufferType, byteOffset, buffer); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining()); + return this; + } + + public VertexBuffer fillBuffer(int byteOffset, float[] data) + { + ByteBuffer buffer = GL15.glMapBuffer(bufferType, GL15.GL_WRITE_ONLY); + buffer.position(byteOffset); + buffer.asFloatBuffer().put(data); + GL15.glUnmapBuffer(bufferType); + AllocationTracker.INSTANCE.addGPUBytes(data.length * 4L); + return this; + } + + public VertexBuffer fillBuffer(int byteOffset, int[] data) + { + ByteBuffer buffer = GL15.glMapBuffer(bufferType, GL15.GL_WRITE_ONLY); + buffer.position(byteOffset); + buffer.asIntBuffer().put(data); + GL15.glUnmapBuffer(bufferType); + AllocationTracker.INSTANCE.addGPUBytes(data.length * 4L); + return this; + } + + public VertexBuffer fillBuffer(int byteOffset, byte[] data) + { + ByteBuffer buffer = GL15.glMapBuffer(bufferType, GL15.GL_WRITE_ONLY); + buffer.position(byteOffset); + buffer.put(data); + GL15.glUnmapBuffer(bufferType); + AllocationTracker.INSTANCE.addGPUBytes(data.length); + return this; + } + + public VertexBuffer fillBuffer(List> data) + { + return fillBuffer(0, data); + } + + public VertexBuffer fillBuffer(int byteOffset, List> data) + { + if(data.isEmpty()) + { + return this; + } + long totalData = 0; + ByteBuffer buffer = GL15.glMapBuffer(bufferType, GL15.GL_WRITE_ONLY); + for(int i = 0,m=data.size();i entry = data.get(i); + buffer.position(entry.getIntKey()+byteOffset); + buffer.put(entry.getValue()); + totalData += entry.getValue().length; + } + buffer.flip(); + if(!GL15.glUnmapBuffer(bufferType)) + { + GameLog.info("Memory Corruption?"); + } + AllocationTracker.INSTANCE.addGPUBytes(totalData); + return this; + } + + public VertexBuffer fillBufferFloat(int byteOffset, int neededFloats, Consumer supplier) + { + FloatBuffer buffer = MemoryUtil.memAllocFloat(neededFloats); + supplier.accept(buffer); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining() * 4L); + GL15.glBufferSubData(bufferType, byteOffset, buffer); + MemoryUtil.memFree(buffer); + return this; + } + + public VertexBuffer fillBufferInt(int byteOffset, int neededInts, Consumer supplier) + { + IntBuffer buffer = MemoryUtil.memAllocInt(neededInts); + supplier.accept(buffer); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining() * 4L); + GL15.glBufferSubData(bufferType, byteOffset, buffer); + MemoryUtil.memFree(buffer); + return this; + } + + public VertexBuffer fillBufferByte(int byteOffset, int neededBytes, Consumer supplier) + { + ByteBuffer buffer = MemoryUtil.memAlloc(neededBytes); + supplier.accept(buffer); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining()); + GL15.glBufferSubData(bufferType, byteOffset, buffer); + MemoryUtil.memFree(buffer); + return this; + } + + public VertexBuffer readBufferData(FloatBuffer buffer, int floatsToRead) + { + GL15.nglGetBufferSubData(bufferType, 0, floatsToRead << 2, MemoryUtil.memAddress(buffer)); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(floatsToRead * 4L); + return this; + } + + public VertexBuffer readBufferData(IntBuffer buffer, int intsToRead) + { + GL15.nglGetBufferSubData(bufferType, 0, intsToRead << 2, MemoryUtil.memAddress(buffer)); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(intsToRead * 4L); + return this; + } + + public VertexBuffer readBufferData(ByteBuffer buffer, int bytesToRead) + { + GL15.nglGetBufferSubData(bufferType, 0, bytesToRead, MemoryUtil.memAddress(buffer)); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(bytesToRead); + return this; + } + + public VertexBuffer grow(int oldByteSize, int newByteSize, String source) + { + ByteBuffer buffer = GL15.glMapBuffer(bufferType, GL15.GL_READ_ONLY); + if(oldByteSize != buffer.remaining()) + { + GameLog.info("Grow: [Expected="+oldByteSize+", Present="+buffer.remaining()+", source="+source+"]"); + } + ByteBuffer newCap = MemoryUtil.memAlloc(newByteSize); + MemoryUtil.memCopy(MemoryUtil.memAddress(buffer), MemoryUtil.memAddress(newCap), oldByteSize); + GL15.glUnmapBuffer(bufferType); + newCap.position(newByteSize); + newCap.flip(); + GL15.glBufferData(bufferType, newCap, bufferState); + MemoryUtil.memFree(newCap); + AllocationTracker.INSTANCE.addGPUBytes(oldByteSize + newByteSize); + return this; + } + + public VertexBuffer shrink(int oldByteSize, int newByteSize, String source) + { + ByteBuffer buffer = GL15.glMapBuffer(bufferType, GL15.GL_READ_WRITE); + if(oldByteSize != buffer.remaining()) + { + GameLog.info("Shrink: [Expected="+oldByteSize+", Present="+buffer.remaining()+", source="+source+"]"); + } + ByteBuffer newBuffer = MemoryUtil.memAlloc(newByteSize); + MemoryUtil.memCopy(MemoryUtil.memAddress(buffer), MemoryUtil.memAddress(newBuffer), newByteSize); + newBuffer.position(newByteSize); + newBuffer.flip(); + GL15.glUnmapBuffer(bufferType); + GL15.glBufferData(bufferType, newBuffer, bufferState); + MemoryUtil.memFree(newBuffer); + AllocationTracker.INSTANCE.addGPUBytes(oldByteSize + newByteSize); + return this; + } + + public VertexBuffer shiftData(int startBytes, int bytesLength, int byteShift, String source) + { + ByteBuffer buffer = GL15.glMapBuffer(bufferType, GL15.GL_READ_WRITE); + int min = Math.min(startBytes, startBytes + byteShift); + int max = Math.max(startBytes + bytesLength, startBytes + bytesLength + byteShift); + if(min < 0 || max > buffer.remaining()) + { + GameLog.info("Shift Aborted due to out of range [Expected Min="+min+", Max="+max+", Source="+source+"]"); + return this; + } + MemoryUtil.memCopy(MemoryUtil.memAddress(buffer, startBytes), MemoryUtil.memAddress(buffer, startBytes+byteShift), bytesLength); + GL15.glUnmapBuffer(bufferType); + AllocationTracker.INSTANCE.addGPUBytes(bytesLength + byteShift); + return this; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/models/frameBuffer/BufferAttachment.java b/src/main/java/speiger/src/coreengine/rendering/models/frameBuffer/BufferAttachment.java new file mode 100644 index 0000000..d0b32e5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/models/frameBuffer/BufferAttachment.java @@ -0,0 +1,82 @@ +package speiger.src.coreengine.rendering.models.frameBuffer; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL30; + +public class BufferAttachment implements IFrameAttachment +{ + int id; + int format; + int type; + int samples; + + public BufferAttachment(int format) + { + this.format = format; + } + + public static BufferAttachment createRGBA() + { + return new BufferAttachment(GL11.GL_RGBA); + } + + public static BufferAttachment createRGB() + { + return new BufferAttachment(GL11.GL_RGB); + } + + public static BufferAttachment createDepth() + { + return new BufferAttachment(GL11.GL_DEPTH_COMPONENT); + } + + public static BufferAttachment createDepthStencil() + { + return new BufferAttachment(GL30.GL_DEPTH24_STENCIL8); + } + + @Override + public int getId() + { + return id; + } + + public void setBufferId(int id) + { + if(this.id != id) + { + this.id = id; + if(type != 0 && id != 0) + { + GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, type, GL30.GL_RENDERBUFFER, id); + GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, 0); + } + } + } + + @Override + public void init(int type, int width, int height, int samples) + { + + this.type = type; + this.samples = samples; + if(id == 0) + { + id = GL30.glGenRenderbuffers(); + } + GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, id); + GL30.glRenderbufferStorageMultisample(GL30.GL_RENDERBUFFER, samples, format, width, height); + GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, type, GL30.GL_RENDERBUFFER, id); + GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, 0); + } + + @Override + public void delete() + { + if(id != 0) + { + GL30.glDeleteRenderbuffers(id); + } + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/models/frameBuffer/FrameBuffer.java b/src/main/java/speiger/src/coreengine/rendering/models/frameBuffer/FrameBuffer.java new file mode 100644 index 0000000..a65bf20 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/models/frameBuffer/FrameBuffer.java @@ -0,0 +1,221 @@ +package speiger.src.coreengine.rendering.models.frameBuffer; + +import java.util.List; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL30; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.rendering.utils.GLUtils; + +public class FrameBuffer +{ + int id; + int width; + int height; + int samples; + boolean finished = false; + List colors = new ObjectArrayList(); + IFrameAttachment depth; + IFrameAttachment stencil; + IFrameAttachment stencil_depth; + + public FrameBuffer(int width, int height, int samples) + { + this.width = width; + this.height = height; + this.samples = samples; + } + + public FrameBuffer addColorAttachment(IFrameAttachment attachment) + { + if(colors.size() > 32) + { + throw new IllegalStateException("Color Attachments are full"); + } + colors.add(attachment); + return this; + } + + public FrameBuffer setColorAttachment(int index, IFrameAttachment attachment) + { + if(colors.size() <= index) + { + throw new IllegalStateException("No Attachment Found!"); + } + colors.set(index, attachment); + return this; + } + + public FrameBuffer setDepthAttachment(IFrameAttachment attachment) + { + depth = attachment; + return this; + } + + public FrameBuffer finished() + { + if(finished) + { + return this; + } + finished = true; + id = GL30.glGenFramebuffers(); + GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, id); + for(int i = 0,m=colors.size();i +{ + Camera camera; + Window window; + UniformBuffer projectionMatrixBuffer; + UniformBuffer proViewMatrixBuffer; + UniformBuffer orthoViewMatrixBuffer; + final Matrix4f proViewMatrix = new Matrix4f(); + final Matrix4f orthoViewMatrix = new Matrix4f(); + + public ProjectionBuffer(Camera camera, Window window) + { + this.camera = camera; + this.window = window; + projectionMatrixBuffer = new UniformBuffer(true); + proViewMatrixBuffer = new UniformBuffer(true); + orthoViewMatrixBuffer = new UniformBuffer(true); + window.addListener(this, true); + camera.addListener(this); + } + + @Override + public void accept(Camera t) + { + proViewMatrix.load(window.getProjectionMatrix()).multiply(camera.getViewMatrix()); + proViewMatrixBuffer.bind().setBufferData(proViewMatrix.getData()).unbind(); + } + + @Override + public void onWindowChanged(Window window) + { + ScaledResolution res = window.getUIFrame(); + orthoViewMatrix.setIdentity().ortho(0, 0, res.getScaledWidth(), res.getScaledHeight(), 1000, -1000); + projectionMatrixBuffer.bind().setBufferData(window.getProjectionMatrix().getData()).unbind(); + orthoViewMatrixBuffer.bind().setBufferData(orthoViewMatrix.getData()).unbind(); + accept(camera); + } + + public UniformBuffer getProjectionMatrixBuffer() + { + return projectionMatrixBuffer; + } + + public UniformBuffer getProViewMatrixBuffer() + { + return proViewMatrixBuffer; + } + + public UniformBuffer getOrthoViewMatrixBuffer() + { + return orthoViewMatrixBuffer; + } + + public Matrix4f getProViewMatrix() + { + return proViewMatrix; + } + + public Matrix4f getOrthoViewMatrix() + { + return orthoViewMatrix; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/ShaderEntry.java b/src/main/java/speiger/src/coreengine/rendering/shader/ShaderEntry.java new file mode 100644 index 0000000..443adc2 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/ShaderEntry.java @@ -0,0 +1,23 @@ +package speiger.src.coreengine.rendering.shader; + +import java.util.List; +import java.util.function.Consumer; + +public class ShaderEntry implements Consumer +{ + List shaders; + int index; + + public ShaderEntry(List shaders, int index) + { + this.shaders = shaders; + this.index = index; + } + + @Override + public void accept(T t) + { + shaders.set(index, t); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/ShaderProgram.java b/src/main/java/speiger/src/coreengine/rendering/shader/ShaderProgram.java new file mode 100644 index 0000000..2e30627 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/ShaderProgram.java @@ -0,0 +1,129 @@ +package speiger.src.coreengine.rendering.shader; + +import java.util.List; + +import org.lwjgl.opengl.GL20; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.IAsset; +import speiger.src.coreengine.rendering.shader.ShaderTracker.ReloadReference; +import speiger.src.coreengine.rendering.shader.uniforms.Uniform; +import speiger.src.coreengine.utils.collections.iterators.IterableWrapper; +import speiger.src.coreengine.utils.io.GameLog; + +public abstract class ShaderProgram +{ + protected List localUniforms = new ObjectArrayList(); + ReloadReference reference; + private int shaderID; + + protected ShaderProgram(AssetLocation vertex, AssetLocation fragment, String[] attributes) + { + int vertexID = loadShader(vertex, GL20.GL_VERTEX_SHADER); + int fragmentID = loadShader(fragment, GL20.GL_FRAGMENT_SHADER); + if(vertexID == -1 || fragmentID == -1) + { + shaderID = 0; + return; + } + shaderID = GL20.glCreateProgram(); + GL20.glAttachShader(shaderID, vertexID); + GL20.glAttachShader(shaderID, fragmentID); + for(int i = 0;i < attributes.length;i++) + { + GL20.glBindAttribLocation(shaderID, i, attributes[i]); + } + GL20.glLinkProgram(shaderID); + GL20.glDetachShader(shaderID, vertexID); + GL20.glDetachShader(shaderID, fragmentID); + GL20.glDeleteShader(vertexID); + GL20.glDeleteShader(fragmentID); + } + + public void addUniforms(Uniform... uniforms) + { + for(Uniform uni : uniforms) + { + uni.loadUniform(this); + localUniforms.add(uni); + } + GL20.glValidateProgram(shaderID); + } + + void setReference(ReloadReference ref) + { + reference = ref; + } + + public abstract ShaderProgram init(); + + public boolean isShaderValid() + { + return shaderID != 0; + } + + public int getShaderID() + { + return shaderID; + } + + public void startShader() + { + ShaderTracker.INSTANCE.startNextShader(this); + } + + public void stopShader() + { + ShaderTracker.INSTANCE.stopShader(this); + } + + public boolean isShaderRunning() + { + return ShaderTracker.INSTANCE.activeShader == getShaderID(); + } + + public void removeShader() + { + if(shaderID == 0) + { + return; + } + stopShader(); + GL20.glDeleteProgram(shaderID); + shaderID = 0; + reference.removeReference(); + reference = null; + } + + private int loadShader(AssetLocation location, int type) + { + StringBuilder shaderSource = new StringBuilder(); + try(IAsset asset = ShaderTracker.INSTANCE.getAssets().getAsset(location)) + { + for(String s : IterableWrapper.wrap(asset.getStringReader())) + { + if(s.startsWith("//")) + { + continue; + } + shaderSource.append(s).append("//\n"); + } + } + catch(Exception e) + { + e.printStackTrace(); + return -1; + } + int shaderID = GL20.glCreateShader(type); + GL20.glShaderSource(shaderID, shaderSource); + GL20.glCompileShader(shaderID); + if(GL20.glGetShaderi(shaderID, GL20.GL_COMPILE_STATUS) == 0) + { + GameLog.warn("Could not compile shader " + location); + GameLog.error(GL20.glGetShaderInfoLog(shaderID, 500)); + return -1; + } + return shaderID; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/ShaderTracker.java b/src/main/java/speiger/src/coreengine/rendering/shader/ShaderTracker.java new file mode 100644 index 0000000..eeb8d6d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/ShaderTracker.java @@ -0,0 +1,142 @@ +package speiger.src.coreengine.rendering.shader; + +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.lwjgl.opengl.GL20; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.assets.AssetManager; +import speiger.src.coreengine.assets.reloader.IReloadableResource; + +public class ShaderTracker implements IReloadableResource +{ + public static final ShaderTracker INSTANCE = new ShaderTracker(); + List> programs = new ObjectArrayList>(); + int activeShader = 0; + boolean reloading = false; + AssetManager assets; + + public void init(AssetManager assets) + { + this.assets = assets; + } + + public T register(Supplier creator, Consumer reloader) + { + T result = creator.get(); + result.init(); + ReloadReference reference = new ReloadReference<>(result, creator, reloader); + programs.add(reference); + return result; + } + + protected AssetManager getAssets() + { + return assets; + } + + void startNextShader(ShaderProgram shader) + { + if(shader.getShaderID() == activeShader) + { + return; + } + activeShader = shader.getShaderID(); + GL20.glUseProgram(activeShader); + } + + void stopShader(ShaderProgram shader) + { + if(activeShader == 0) + { + return; + } + activeShader = 0; + GL20.glUseProgram(activeShader); + } + + public void stopShader() + { + if(activeShader == 0) + { + return; + } + activeShader = 0; + GL20.glUseProgram(activeShader); + } + + public boolean isShaderActive() + { + return activeShader > 0; + } + + @Override + public void reload() + { + for(int i = 0,m = programs.size();i + { + T program; + Supplier creator; + Consumer listener; + + public ReloadReference(T value, Supplier creator, Consumer listener) + { + this.program = value; + this.creator = creator; + this.listener = listener; + program.setReference(this); + } + + private void destroy() + { + if(program == null || reloading) + { + return; + } + program.removeShader(); + program = null; + listener.accept(null); + } + + public void removeReference() + { + destroy(); + if(reloading) + { + return; + } + programs.remove(this); + } + + public void reload() + { + reloading = true; + program.removeShader(); + program = creator.get(); + program.init(); + program.setReference(this); + listener.accept(program); + reloading = false; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/Uniform.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/Uniform.java new file mode 100644 index 0000000..24721c7 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/Uniform.java @@ -0,0 +1,38 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.rendering.shader.ShaderProgram; +import speiger.src.coreengine.utils.io.GameLog; + +public abstract class Uniform +{ + protected String name; + protected int position; + protected boolean optional = false; + + public Uniform(String id) + { + name = id; + } + + public int getPosition() + { + return position; + } + + public T setOptional() + { + optional = true; + return (T)this; + } + + public void loadUniform(ShaderProgram prog) + { + position = GL20.glGetUniformLocation(prog.getShaderID(), name); + if(position == -1 && !optional) + { + GameLog.error("Couldn't find Shader Uniform: "+name+", Shader: "+"("+prog.getClass().getSimpleName()+".java:0) ("+prog.getShaderID()+")"); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformArrayBase.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformArrayBase.java new file mode 100644 index 0000000..d54f6e4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformArrayBase.java @@ -0,0 +1,61 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import java.util.Objects; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.rendering.shader.ShaderProgram; + +public class UniformArrayBase extends Uniform +{ + protected int[] positions; + protected boolean[] first; + protected Object[] lastValues; + protected int limit; + + public UniformArrayBase(String id, int length) + { + super(id); + limit = length; + positions = new int[length]; + first = new boolean[length]; + lastValues = new Object[length]; + } + + public int getPosition(int index) + { + return positions[index]; + } + + protected boolean hasChanged(int index, T value) + { + if(!first[index] || !Objects.equals(lastValues[index], value)) + { + first[index] = true; + lastValues[index] = setValue((T)lastValues[index], value); + return true; + } + return false; + } + + protected T setValue(T oldValue, T newValue) + { + return newValue; + } + + @Override + public void loadUniform(ShaderProgram prog) + { + for(int i = 0;i extends Uniform +{ + T lastValue; + boolean first = true; + + public UniformBase(String id) + { + super(id); + } + + protected boolean hasChanged(T value) + { + if(first || !Objects.equals(lastValue, value)) + { + first = false; + lastValue = setValue(lastValue, value); + return true; + } + return false; + } + + protected T setValue(T oldValue, T newValue) + { + return newValue; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformBoolean.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformBoolean.java new file mode 100644 index 0000000..3111fe0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformBoolean.java @@ -0,0 +1,26 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class UniformBoolean extends Uniform +{ + boolean last = false; + boolean first = true; + public UniformBoolean(String id) + { + super(id); + } + + public void storeData(boolean value) + { + if(last != value || first) + { + first = false; + last = value; + GL20.glUniform1f(getPosition(), value ? 1F : 0F); + AllocationTracker.INSTANCE.addGPUBytes(1L); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformBufferBlock.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformBufferBlock.java new file mode 100644 index 0000000..24cb4d6 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformBufferBlock.java @@ -0,0 +1,37 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import org.lwjgl.opengl.GL30; +import org.lwjgl.opengl.GL31; + +import speiger.src.coreengine.rendering.models.UniformBuffer; +import speiger.src.coreengine.rendering.shader.ShaderProgram; +import speiger.src.coreengine.utils.io.GameLog; + +public class UniformBufferBlock extends Uniform +{ + ShaderProgram prog; + int slot; + + public UniformBufferBlock(String id, int slot) + { + super(id); + this.slot = slot; + } + + @Override + public void loadUniform(ShaderProgram prog) + { + this.prog = prog; + position = GL31.glGetUniformBlockIndex(prog.getShaderID(), name); + if(position == -1) + { + GameLog.error("Couldn't find Shader Uniform Block: "+name+", Shader ID: "+prog.getShaderID()); + } + } + + public void bindBuffer(UniformBuffer buffer) + { + GL30.glBindBufferBase(GL31.GL_UNIFORM_BUFFER, slot, buffer.getBufferID()); + GL31.glUniformBlockBinding(prog.getShaderID(), position, slot); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformFloat.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformFloat.java new file mode 100644 index 0000000..7d1442e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformFloat.java @@ -0,0 +1,27 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class UniformFloat extends Uniform +{ + float lastValue = 0F; + boolean first = true; + + public UniformFloat(String id) + { + super(id); + } + + public void storeData(float value) + { + if(Float.floatToIntBits(lastValue) != Float.floatToIntBits(value) || first) + { + lastValue = value; + first = false; + GL20.glUniform1f(getPosition(), value); + AllocationTracker.INSTANCE.addGPUBytes(4L); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformInt.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformInt.java new file mode 100644 index 0000000..29b5058 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformInt.java @@ -0,0 +1,22 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class UniformInt extends UniformBase +{ + public UniformInt(String id) + { + super(id); + } + + public void storeData(Integer data) + { + if(hasChanged(data)) + { + GL20.glUniform1i(getPosition(), data); + AllocationTracker.INSTANCE.addGPUBytes(4L); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformMatrix.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformMatrix.java new file mode 100644 index 0000000..5161654 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformMatrix.java @@ -0,0 +1,33 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.math.vector.matrix.Matrix4f; +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class UniformMatrix extends UniformBase +{ + public UniformMatrix(String id) + { + super(id); + } + + public void storeData(Matrix4f data) + { + if(hasChanged(data)) + { + GL20.glUniformMatrix4fv(getPosition(), false, data.getData()); + AllocationTracker.INSTANCE.addGPUBytes(64L); + } + } + + @Override + protected Matrix4f setValue(Matrix4f oldValue, Matrix4f newValue) + { + if(oldValue != null) + { + return oldValue.load(newValue); + } + return new Matrix4f(newValue); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformTexture.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformTexture.java new file mode 100644 index 0000000..1e82a50 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformTexture.java @@ -0,0 +1,44 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import speiger.src.coreengine.rendering.textures.ITexture; +import speiger.src.coreengine.rendering.textures.TextureManager; + +public class UniformTexture extends UniformInt +{ + int bank; + + public UniformTexture(String id, int bank) + { + super(id); + this.bank = bank; + } + + public void storeTexture(int id) + { + if(id == 0) + { + disable(); + return; + } + TextureManager.bindTexture(id, bank); + if(hasChanged(id)) + { + storeData(id); + } + } + + public void storeTexture(ITexture textureID) + { + if(textureID == null) + { + disable(); + return; + } + storeTexture(textureID.getTextureID()); + } + + public void disable() + { + TextureManager.unbindTexture(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec2f.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec2f.java new file mode 100644 index 0000000..e074b41 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec2f.java @@ -0,0 +1,41 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class UniformVec2f extends UniformBase +{ + static final Vec2f STORED = Vec2f.newMutable(); + public UniformVec2f(String id) + { + super(id); + } + + @Override + protected Vec2f setValue(Vec2f oldValue, Vec2f newValue) + { + if(oldValue != null) + { + oldValue.set(newValue.getX(), newValue.getY()); + return oldValue; + } + return Vec2f.newMutable(newValue); + } + + public void storeData(Vec2f data) + { + if(hasChanged(data)) + { + GL20.glUniform2f(getPosition(), data.getX(), data.getY()); + AllocationTracker.INSTANCE.addGPUBytes(8L); + } + } + + public void storeData(float x, float y) + { + STORED.set(x, y); + storeData(STORED); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec3f.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec3f.java new file mode 100644 index 0000000..05e070f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec3f.java @@ -0,0 +1,46 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class UniformVec3f extends UniformBase +{ + static final Vec3f STORAGE = Vec3f.newMutable(); + + public UniformVec3f(String id) + { + super(id); + } + + public void storeData(Vec3f data) + { + if(hasChanged(data)) + { + GL20.glUniform3f(getPosition(), data.getX(), data.getY(), data.getZ()); + AllocationTracker.INSTANCE.addGPUBytes(12L); + } + } + + public void storeData(ColorObject color) + { + storeData(STORAGE.set(color.getRedFloat(), color.getGreenFloat(), color.getBlueFloat())); + } + + public void storeData(float x, float y, float z) + { + storeData(STORAGE.set(x, y, z)); + } + + @Override + protected Vec3f setValue(Vec3f oldValue, Vec3f newValue) + { + if(oldValue != null) + { + return oldValue.set(newValue); + } + return Vec3f.newMutable(newValue); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec3fArray.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec3fArray.java new file mode 100644 index 0000000..e35e83f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec3fArray.java @@ -0,0 +1,54 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import java.util.Collection; +import java.util.Iterator; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class UniformVec3fArray extends UniformArrayBase +{ + static final Vec3f STORAGE = Vec3f.newMutable(); + + public UniformVec3fArray(String id, int length) + { + super(id, length); + } + + public void store(int index, Vec3f data) + { + if(hasChanged(index, data)) + { + GL20.glUniform3f(getPosition(index), data.getX(), data.getY(), data.getZ()); + AllocationTracker.INSTANCE.addGPUBytes(12L); + } + } + + public void store(int index, float x, float y, float z) + { + STORAGE.set(x, y, z); + store(index, STORAGE); + } + + public void store(Collection store) + { + Iterator values = store.iterator(); + for(int i = 0;i +{ + static final Vec4f STORAGE = Vec4f.newMutable(); + + public UniformVec4f(String id) + { + super(id); + } + + public void storeData(Vec4f data) + { + if(hasChanged(data)) + { + GL20.glUniform4f(getPosition(), data.getX(), data.getY(), data.getZ(), data.getW()); + AllocationTracker.INSTANCE.addGPUBytes(16L); + } + } + + public void storeData(ColorObject color) + { + STORAGE.set(color.getRedFloat(), color.getGreenFloat(), color.getBlueFloat(), color.getAlphaFloat()); + storeData(STORAGE); + } + + public void storeData(float x, float y, float z, float w) + { + STORAGE.set(x, y, z, w); + storeData(STORAGE); + } + + @Override + protected Vec4f setValue(Vec4f oldValue, Vec4f newValue) + { + if(oldValue != null) + { + oldValue.set(newValue); + return oldValue; + } + return Vec4f.newMutable(newValue); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec4fArray.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec4fArray.java new file mode 100644 index 0000000..ff02380 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec4fArray.java @@ -0,0 +1,54 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import java.util.Collection; +import java.util.Iterator; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class UniformVec4fArray extends UniformArrayBase +{ + static final Vec4f STORAGE = Vec4f.newMutable(); + + public UniformVec4fArray(String id, int length) + { + super(id, length); + } + + public void store(int index, Vec4f data) + { + if(hasChanged(index, data)) + { + GL20.glUniform4f(getPosition(index), data.getX(), data.getY(), data.getZ(), data.getW()); + AllocationTracker.INSTANCE.addGPUBytes(16L); + } + } + + public void store(int index, float x, float y, float z, float w) + { + STORAGE.set(x, y, z, w); + store(index, STORAGE); + } + + public void store(Collection store) + { + Iterator values = store.iterator(); + for(int i = 0;i +{ + List elements = new ArrayList(); + Set types = EnumSet.noneOf(ElementUsage.class); + IntList offsets = new IntArrayList(); + int totalOffset; + + public VertexList() + { + } + + public VertexList(VertexList vertexList) + { + elements.addAll(vertexList.elements); + offsets.addAll(vertexList.offsets); + totalOffset = vertexList.totalOffset; + } + + public void addElement(VertexElement element) + { + elements.add(element); + offsets.add(totalOffset); + totalOffset += element.getSize(); + types.add(element.getUsage()); + } + + public int getOffsets() + { + return totalOffset; + } + + public int getElementCount() + { + return elements.size(); + } + + public VertexElement getElement(int index) + { + return elements.get(index); + } + + public int getOffset(int index) + { + return offsets.getInt(index); + } + + @Override + public Iterator iterator() + { + return new ReadOnlyIterator(elements.iterator()); + } + + public boolean hasType(ElementUsage type) + { + return types.contains(type); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/tesselation/VertexType.java b/src/main/java/speiger/src/coreengine/rendering/tesselation/VertexType.java new file mode 100644 index 0000000..e2c2cb8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/tesselation/VertexType.java @@ -0,0 +1,47 @@ +package speiger.src.coreengine.rendering.tesselation; + +import speiger.src.coreengine.rendering.tesselation.VertexElement.ElementUsage; + +public class VertexType +{ + public static final VertexElement POSITION_2F = new VertexElement(2, ElementUsage.POS); + public static final VertexElement POSITION_3F = new VertexElement(3, ElementUsage.POS); + public static final VertexElement TEXTURE_2F = new VertexElement(2, ElementUsage.UV); + public static final VertexElement COLOR_3F = new VertexElement(3, ElementUsage.COLOR); + public static final VertexElement COLOR_4F = new VertexElement(4, ElementUsage.COLOR); + public static final VertexElement NORMAL_3F = new VertexElement(3, ElementUsage.NORMAL); + public static final VertexElement CUSTOM_4F = new VertexElement(4, ElementUsage.CUSTOM); + + public static final VertexList POS_COLOR_3F = new VertexList(); + public static final VertexList POS_COLOR_4F = new VertexList(); + public static final VertexList POS_COLOR_2F = new VertexList(); + public static final VertexList POS_TEX = new VertexList(); + public static final VertexList TERRAIN = new VertexList(); + + public static final VertexList UI = new VertexList(); + public static final VertexList IN_WORLD_UI = new VertexList(); + + static + { + UI.addElement(POSITION_3F); + UI.addElement(TEXTURE_2F); + UI.addElement(COLOR_4F); + + IN_WORLD_UI.addElement(POSITION_3F); + IN_WORLD_UI.addElement(TEXTURE_2F); + IN_WORLD_UI.addElement(COLOR_4F); + IN_WORLD_UI.addElement(CUSTOM_4F); + + TERRAIN.addElement(POSITION_3F); + TERRAIN.addElement(COLOR_3F); + TERRAIN.addElement(NORMAL_3F); + POS_COLOR_2F.addElement(POSITION_2F); + POS_COLOR_2F.addElement(COLOR_4F); + POS_COLOR_3F.addElement(POSITION_3F); + POS_COLOR_3F.addElement(COLOR_3F); + POS_COLOR_4F.addElement(POSITION_3F); + POS_COLOR_4F.addElement(COLOR_4F); + POS_TEX.addElement(POSITION_3F); + POS_TEX.addElement(TEXTURE_2F); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/textures/AbstractTexture.java b/src/main/java/speiger/src/coreengine/rendering/textures/AbstractTexture.java new file mode 100644 index 0000000..4b3a4ff --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/textures/AbstractTexture.java @@ -0,0 +1,69 @@ +package speiger.src.coreengine.rendering.textures; + +public abstract class AbstractTexture implements ITexture +{ + int textureID; + + public AbstractTexture() + { + TextureManager.INSTANCE.addTexture(this); + } + + public void setTextureID(int textureID) + { + this.textureID = textureID; + } + + @Override + public int getTextureID() + { + return textureID; + } + + @Override + public void bindTexture() + { + TextureManager.bindTexture(textureID); + } + + @Override + public void destroy() + { + deleteTexture(); + } + + @Override + public void deleteTexture() + { + if(textureID == -1) + { + return; + } + TextureManager.INSTANCE.removeTexture(this); + textureID = -1; + } + + @Override + public float getUMin() + { + return 0F; + } + + @Override + public float getVMin() + { + return 0F; + } + + @Override + public float getUMax() + { + return 1F; + } + + @Override + public float getVMax() + { + return 1F; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/textures/DirectTexture.java b/src/main/java/speiger/src/coreengine/rendering/textures/DirectTexture.java new file mode 100644 index 0000000..055a377 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/textures/DirectTexture.java @@ -0,0 +1,99 @@ +package speiger.src.coreengine.rendering.textures; + +import java.awt.image.BufferedImage; +import java.nio.ByteBuffer; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL30; +import org.lwjgl.system.MemoryUtil; + +public class DirectTexture extends AbstractTexture +{ + BufferedImage image; + + public DirectTexture(BufferedImage image) + { + this.image = image; + loadTexture(); + } + + public void loadTexture() + { + int width = image.getWidth(); + int height = image.getHeight(); + int[] pixelData = new int[width * height]; + image.getRGB(0, 0, width, height, pixelData, 0, width); + ByteBuffer buffer = MemoryUtil.memAlloc(pixelData.length * 4); + for(int y = 0;y> 16) & 0xFF)); + buffer.put((byte)((pixel >> 8) & 0xFF)); + buffer.put((byte)(pixel & 0xFF)); + buffer.put((byte)((pixel >> 24) & 0xFF)); + } + } + + buffer.flip(); + setTextureID(GL11.glGenTextures()); + bindTexture(); + GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); + GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL13.GL_CLAMP_TO_BORDER); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL13.GL_CLAMP_TO_BORDER); + GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer); + GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D); + MemoryUtil.memFree(buffer); + } + + public void updateTexture() + { + int width = image.getWidth(); + int height = image.getHeight(); + int[] pixelData = new int[width * height]; + image.getRGB(0, 0, width, height, pixelData, 0, width); + ByteBuffer buffer = MemoryUtil.memAlloc(pixelData.length * 4); + for(int y = 0;y> 16) & 0xFF)); + buffer.put((byte)((pixel >> 8) & 0xFF)); + buffer.put((byte)(pixel & 0xFF)); + buffer.put((byte)((pixel >> 24) & 0xFF)); + } + } + buffer.flip(); + GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer); + GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D); + MemoryUtil.memFree(buffer); + } + + public BufferedImage getImage() + { + return image; + } + + @Override + public int getWidth() + { + return image.getWidth(); + } + + @Override + public int getHeight() + { + return image.getHeight(); + } + + @Override + public void reload() + { + TextureManager.INSTANCE.removeTexture(textureID); + loadTexture(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/textures/DynamicTexture.java b/src/main/java/speiger/src/coreengine/rendering/textures/DynamicTexture.java new file mode 100644 index 0000000..6e9ab86 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/textures/DynamicTexture.java @@ -0,0 +1,153 @@ +package speiger.src.coreengine.rendering.textures; + +import java.nio.ByteBuffer; +import java.util.Iterator; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; + +import speiger.src.collections.ints.collections.IntIterator; +import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap; +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap; +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry; +import speiger.src.collections.ints.sets.IntOpenHashSet; +import speiger.src.collections.ints.sets.IntSet; +import speiger.src.collections.ints.utils.maps.Int2ObjectMaps; +import speiger.src.collections.utils.ITrimmable; +import speiger.src.coreengine.math.BitUtil; +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class DynamicTexture extends AbstractTexture +{ + Int2ObjectMap dirtyChunks = new Int2ObjectOpenHashMap(); + int[] data; + int width; + int height; + boolean first = true; + + public DynamicTexture(int width, int height) + { + this.width = width; + this.height = height; + setTextureID(GL11.glGenTextures()); + data = new int[width * height]; + } + + public int[] getTextureData() + { + return data; + } + + public void markDirty(int x, int z) + { + int chunkIndex = BitUtil.toInt(x >> 4, z >> 4); + IntSet set = dirtyChunks.get(chunkIndex); + if(set == null) + { + set = new IntOpenHashSet(); + dirtyChunks.put(chunkIndex, set); + } + set.add(BitUtil.toInt(x, z)); + } + + public void setData(int x, int z, int value) + { + data[(z * width) + x] = value; + markDirty(x, z); + } + + public boolean hasTasks() + { + return dirtyChunks.size() > 0 || first; + } + + public void updateData() + { + if(first) + { + ByteBuffer buffer = MemoryUtil.memAlloc(data.length * 4); + for(int i = 0;i> 16) & 0xFF)); + buffer.put((byte)((pixel >> 8) & 0xFF)); + buffer.put((byte)(pixel & 0xFF)); + buffer.put((byte)((pixel >> 24) & 0xFF)); + } + buffer.flip(); + bindTexture(); + GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); + GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining()); + GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer); + first = false; + MemoryUtil.memFree(buffer); + dirtyChunks.clear(); + } + else + { + bindTexture(); + Thread thread = Thread.currentThread(); + for(Iterator> iter = Int2ObjectMaps.fastIterator(dirtyChunks);iter.hasNext() && !thread.isInterrupted();iter.remove()) + { + Entry values = iter.next(); + int key = values.getIntKey(); + int chunkX = BitUtil.toFirstShort(key) * 16; + int chunkZ = BitUtil.toSecondShort(key) * 16; + int width = 0; + int height = 0; + for(IntIterator subIt = values.getValue().iterator();subIt.hasNext();) + { + int index = subIt.nextInt(); + width = Math.max(width, BitUtil.toFirstShort(index) - chunkX + 1); + height = Math.max(height, BitUtil.toSecondShort(index) - chunkZ + 1); + } + try(MemoryStack stack = MemoryStack.stackPush()) + { + ByteBuffer buffer = stack.malloc(width * height * 4); + for(int y = 0;y> 16) & 0xFF)); + buffer.put((byte)((pixel >> 8) & 0xFF)); + buffer.put((byte)(pixel & 0xFF)); + buffer.put((byte)((pixel >> 24) & 0xFF)); + } + } + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining()); + GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, chunkX, chunkZ, width, height, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer); + } + } + } + if(dirtyChunks.isEmpty()) + { + ((ITrimmable)dirtyChunks).trim(); + } + } + + @Override + public void reload() + { + TextureManager.INSTANCE.removeTexture(getTextureID()); + setTextureID(GL11.glGenTextures()); + dirtyChunks.clear(); + first = true; + updateData(); + } + + @Override + public int getWidth() + { + return width; + } + + @Override + public int getHeight() + { + return height; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/textures/ITexture.java b/src/main/java/speiger/src/coreengine/rendering/textures/ITexture.java new file mode 100644 index 0000000..1ce92f2 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/textures/ITexture.java @@ -0,0 +1,22 @@ +package speiger.src.coreengine.rendering.textures; + +import speiger.src.coreengine.assets.reloader.IReloadableResource; + +public interface ITexture extends IReloadableResource +{ + public int getTextureID(); + + public void bindTexture(); + public void deleteTexture(); + + public int getWidth(); + public int getHeight(); + + public float getUMin(); + public float getVMin(); + public float getUMax(); + public float getVMax(); + + public default float getInterpolatedU(float u){return getUMin() + ((getUMax() - getUMin()) * u);} + public default float getINterpolatedV(float v){return getUMin() + ((getUMax() - getUMin()) * v);} +} diff --git a/src/main/java/speiger/src/coreengine/rendering/textures/SimpleTexture.java b/src/main/java/speiger/src/coreengine/rendering/textures/SimpleTexture.java new file mode 100644 index 0000000..a06247e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/textures/SimpleTexture.java @@ -0,0 +1,84 @@ +package speiger.src.coreengine.rendering.textures; + +import java.awt.image.BufferedImage; +import java.nio.ByteBuffer; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL30; +import org.lwjgl.system.MemoryUtil; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.IAsset; + +public class SimpleTexture extends AbstractTexture +{ + AssetLocation location; + int width; + int height; + + public SimpleTexture(AssetLocation location) + { + super(); + this.location = location; + loadTexture(); + } + + public void loadTexture() + { + try(IAsset asset = TextureManager.INSTANCE.getManager().getAsset(location)) + { + BufferedImage image = asset.getTexture(); + width = image.getWidth(); + height = image.getHeight(); + int[] pixelData = new int[width * height]; + image.getRGB(0, 0, width, height, pixelData, 0, width); + ByteBuffer buffer = MemoryUtil.memAlloc(pixelData.length * 4); + for(int y = 0;y> 16) & 0xFF)); + buffer.put((byte)((pixel >> 8) & 0xFF)); + buffer.put((byte)(pixel & 0xFF)); + buffer.put((byte)((pixel >> 24) & 0xFF)); + } + } + + buffer.flip(); + setTextureID(GL11.glGenTextures()); + bindTexture(); + GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); + GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL13.GL_CLAMP_TO_BORDER); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL13.GL_CLAMP_TO_BORDER); + GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer); + GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D); + MemoryUtil.memFree(buffer); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + + @Override + public void reload() + { + TextureManager.INSTANCE.removeTexture(textureID); + loadTexture(); + } + + @Override + public int getWidth() + { + return width; + } + + @Override + public int getHeight() + { + return height; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/textures/TextureManager.java b/src/main/java/speiger/src/coreengine/rendering/textures/TextureManager.java new file mode 100644 index 0000000..b101bd2 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/textures/TextureManager.java @@ -0,0 +1,144 @@ +package speiger.src.coreengine.rendering.textures; + +import java.awt.image.BufferedImage; +import java.nio.ByteBuffer; + +import org.lwjgl.glfw.GLFWImage; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL13; +import org.lwjgl.system.MemoryUtil; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.AssetManager; +import speiger.src.coreengine.assets.IAsset; +import speiger.src.coreengine.assets.reloader.IReloadableResource; +import speiger.src.coreengine.assets.reloader.ResourceReloader; +import speiger.src.coreengine.rendering.utils.GLUtils; +import speiger.src.coreengine.rendering.utils.states.GLState; +import speiger.src.coreengine.utils.io.GameLog; +import speiger.src.coreengine.utils.io.GameLog.LogLevel; + +public class TextureManager implements IReloadableResource +{ + public static final TextureManager INSTANCE = new TextureManager(); + public static final GLState TEXTURE = GLUtils.addState(new GLState(GL11.GL_TEXTURE_2D, false)); + AssetManager manager; + ResourceReloader reloader = new ResourceReloader(); + int[] textureBanks = new int[GL13.GL_TEXTURE31 - GL13.GL_TEXTURE0]; + int activeBank = 0; + + public void init(AssetManager manager) + { + this.manager = manager; + } + + public ResourceReloader getReloader() + { + return reloader; + } + + public void addTexture(ITexture texture) + { + if(texture != null) + { + reloader.addReloadableResource(texture); + } + } + + public void removeTexture(ITexture texture) + { + if(texture != null) + { + GL11.glDeleteTextures(texture.getTextureID()); + reloader.removeReloadableResource(texture); + } + } + + public void removeTexture(int id) + { + GL11.glDeleteTextures(id); + } + + public static void bindTexture(int texture) + { + INSTANCE.setActiveTexture(texture); + } + + public static void bindTexture(int texture, int bank) + { + INSTANCE.setActiveTexture(texture, bank); + } + + public static void unbindTexture() + { + INSTANCE.setActiveTexture(0); + } + + public void setActiveTexture(int texture) + { + if(textureBanks[activeBank] != texture) + { + textureBanks[activeBank] = texture; + GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture); + } + } + + public void setActiveTexture(int texture, int bank) + { + if(activeBank != bank) + { + activeBank = bank; + GL13.glActiveTexture(GL13.GL_TEXTURE0 + activeBank); + } + bindTexture(texture); + } + + @Override + public void reload() + { + reloader.reloadResources(); + } + + @Override + public void destroy() + { + reloader.deleteResources(); + } + + public AssetManager getManager() + { + return manager; + } + + public static GLFWImage create(AssetLocation location) + { + try(IAsset asset = INSTANCE.manager.getAsset(location)) + { + BufferedImage image = asset.getTexture(); + int width = image.getWidth(); + int height = image.getHeight(); + int[] pixelData = new int[width * height]; + image.getRGB(0, 0, width, height, pixelData, 0, width); + ByteBuffer buffer = MemoryUtil.memAlloc(pixelData.length * 4); + for(int y = 0;y> 16) & 0xFF)); + buffer.put((byte)((pixel >> 8) & 0xFF)); + buffer.put((byte)(pixel & 0xFF)); + buffer.put((byte)((pixel >> 24) & 0xFF)); + } + } + GLFWImage glfw = GLFWImage.malloc(); + glfw.set(width, height, buffer); + return glfw; + } + catch(Exception e) + { + GameLog.error("Couldn't Load Texture ["+location.toString()+"]", e, LogLevel.ERROR); + return null; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/textures/WrappedTexture.java b/src/main/java/speiger/src/coreengine/rendering/textures/WrappedTexture.java new file mode 100644 index 0000000..fa61518 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/textures/WrappedTexture.java @@ -0,0 +1,69 @@ +package speiger.src.coreengine.rendering.textures; + +public class WrappedTexture implements ITexture +{ + int textureId; + + public WrappedTexture(int textureId) + { + this.textureId = textureId; + } + + @Override + public void reload() + { + } + + @Override + public int getTextureID() + { + return textureId; + } + + @Override + public void bindTexture() + { + TextureManager.bindTexture(textureId); + } + + @Override + public void deleteTexture() + { + } + + @Override + public int getWidth() + { + return 0; + } + + @Override + public int getHeight() + { + return 0; + } + + @Override + public float getUMin() + { + return 0F; + } + + @Override + public float getVMin() + { + return 0F; + } + + @Override + public float getUMax() + { + return 1F; + } + + @Override + public float getVMax() + { + return 1F; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/AllocationTracker.java b/src/main/java/speiger/src/coreengine/rendering/utils/AllocationTracker.java new file mode 100644 index 0000000..0ed3b84 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/AllocationTracker.java @@ -0,0 +1,62 @@ +package speiger.src.coreengine.rendering.utils; + +import speiger.src.coreengine.utils.counters.averager.Averager; + +public class AllocationTracker +{ + public static final AllocationTracker INSTANCE = new AllocationTracker(); + + long lastTime = 0; + long lastMemory = 0L; + long addedCPUMemory = 0L; + long addedGPUMemory = 0L; + Averager cpuAllocation = new Averager(20); + Averager gpuAllocation = new Averager(20); + + public void update() + { + long newTime = System.currentTimeMillis(); + if(newTime - lastTime < 1000) + { + updateAllocation(); + return; + } + lastTime = newTime; + updateAllocation(); + cpuAllocation.addEntry(addedCPUMemory); + addedCPUMemory = 0L; + gpuAllocation.addEntry(addedGPUMemory); + addedGPUMemory = 0L; + } + + public void addGPUBytes(long amount) + { + addedGPUMemory += amount; + } + + private void updateAllocation() + { + long memory = getMemory(); + if(memory > lastMemory) + { + addedCPUMemory += (memory - lastMemory); + } + lastMemory = memory; + } + + private long getMemory() + { + Runtime runtime = Runtime.getRuntime(); + return runtime.totalMemory() - runtime.freeMemory(); + } + + public long getCPUAllocatedBytes() + { + return cpuAllocation.getAverage(); + } + + public long getGPUAllocationBytes() + { + return gpuAllocation.getAverage(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/Cursor.java b/src/main/java/speiger/src/coreengine/rendering/utils/Cursor.java new file mode 100644 index 0000000..959a636 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/Cursor.java @@ -0,0 +1,113 @@ +package speiger.src.coreengine.rendering.utils; + +import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWImage; + +import speiger.src.collections.objects.maps.impl.hash.Object2LongOpenHashMap; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2LongMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap.Entry; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.reloader.IReloadableResource; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.rendering.textures.TextureManager; + +public final class Cursor implements IReloadableResource +{ + public static final Cursor INSTANCE = new Cursor(); + public static final AssetLocation CURSOR_ARROW = AssetLocation.of("Arrow"); + public static final AssetLocation CURSOR_IBEAM = AssetLocation.of("I-Beam"); + public static final AssetLocation CURSOR_CROSSHAIR = AssetLocation.of("Crosshair"); + public static final AssetLocation CURSOR_HAND = AssetLocation.of("Hand"); + public static final AssetLocation CURSOR_HRESIZE = AssetLocation.of("H-Resize"); + public static final AssetLocation CURSOR_VRESIZE = AssetLocation.of("V-Resize"); + static final AssetLocation[] LOCATIONS = new AssetLocation[]{CURSOR_ARROW, CURSOR_IBEAM, CURSOR_CROSSHAIR, CURSOR_HAND, CURSOR_HRESIZE, CURSOR_VRESIZE}; + + long currentCursor = 0; + boolean ignore = false; + Object2ObjectMap customCursors = new Object2ObjectLinkedOpenHashMap(); + Object2LongMap cursors = new Object2LongOpenHashMap(); + + Cursor() + { + } + + public void createCursor(AssetLocation id, AssetLocation texture) + { + GLFWImage image = TextureManager.create(texture); + if(image != null) + { + customCursors.put(id, texture); + cursors.put(id, GLFW.glfwCreateCursor(image, 0, 0)); + image.close(); + } + } + + public void bindCursor(AssetLocation id, Window window) + { + long cursor = cursors.getOrDefault(id, 0L); + if(cursor != currentCursor) + { + currentCursor = cursor; + GLFW.glfwSetCursor(window.getId(), cursor); + } + if(cursor != 0) + { + ignore = true; + } + } + + public void clearCursor(Window window) + { + if(ignore) + { + ignore = false; + return; + } + if(currentCursor != 0) + { + currentCursor = 0; + GLFW.glfwSetCursor(window.getId(), 0); + } + } + + @Override + public void reload() + { + for(long value : cursors.values()) + { + GLFW.glfwDestroyCursor(value); + } + cursors.clear(); + loadCursors(); + } + + @Override + public void destroy() + { + for(long value : cursors.values()) + { + GLFW.glfwDestroyCursor(value); + } + cursors.clear(); + } + + private void loadCursors() + { + for(int i = 0,m = LOCATIONS.length;i entry : Object2ObjectMaps.fastIterable(customCursors)) + { + GLFWImage image = TextureManager.create(entry.getValue()); + if(image != null) + { + cursors.put(entry.getKey(), GLFW.glfwCreateCursor(image, 0, 0)); + image.close(); + } + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/GLStamper.java b/src/main/java/speiger/src/coreengine/rendering/utils/GLStamper.java new file mode 100644 index 0000000..5a89a32 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/GLStamper.java @@ -0,0 +1,151 @@ +package speiger.src.coreengine.rendering.utils; + +import java.util.ArrayList; +import java.util.Map; +import java.util.Set; + +import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL33; + +import speiger.src.collections.ints.collections.IntStack; +import speiger.src.collections.ints.lists.IntArrayList; +import speiger.src.collections.ints.lists.IntList; +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap; +import speiger.src.collections.objects.sets.ObjectOpenHashSet; +import speiger.src.collections.utils.Stack; + +public class GLStamper +{ + public static GLStamper INSTANCE = new GLStamper(); + + Map> stamps = new Object2ObjectOpenHashMap>(); + Stack unusedStamps = new ObjectArrayList(); + IntStack queryIDs = new IntArrayList(); + + + public GLStamp createStamp(String ownerID) + { + Set list = stamps.get(ownerID); + if(list == null) + { + list = new ObjectOpenHashSet(); + stamps.put(ownerID, list); + } + GLStamp stamp = unusedStamps.isEmpty() ? new GLStamp(this) : unusedStamps.pop(); + stamp.setOwner(ownerID); + list.add(stamp); + return stamp; + } + + public void removeOwner(String ownerID) + { + Set set = stamps.remove(ownerID); + if(set != null) + { + for(GLStamp stamp : set) + { + stamp.release(); + } + } + } + + public void cleanup() + { + if(!stamps.isEmpty()) + { + for(String s : new ArrayList(stamps.keySet())) + { + removeOwner(s); + } + } + if(queryIDs.isEmpty()) return; + GL15.glDeleteQueries(((IntList)queryIDs).toIntArray()); + ((IntList)queryIDs).clear(); + } + + protected void release(GLStamp stamp) + { + Set set = stamps.get(stamp.getOwner()); + if(set != null) + { + set.remove(set); + } + unusedStamps.push(stamp); + } + + protected int getQueryID() + { + int id = queryIDs.isEmpty() ? GL15.glGenQueries() : queryIDs.pop(); + GL33.glQueryCounter(id, GL33.GL_TIMESTAMP); + return id; + } + + protected void releaseQueryID(int id) + { + if(id != -1) + { + queryIDs.push(id); + } + } + + public static class GLStamp + { + GLStamper stamp; + String owner; + int startID = -1; + int endID = -1; + + private GLStamp(GLStamper owner) + { + stamp = owner; + } + + void setOwner(String owner) + { + this.owner = owner; + } + + String getOwner() + { + return owner; + } + + public GLStamp start() + { + if(startID == -1) + { + startID = stamp.getQueryID(); + } + return this; + } + + public GLStamp stop() + { + if(endID == -1) + { + endID = stamp.getQueryID(); + } + return this; + } + + public void release() + { + stamp.releaseQueryID(startID); + stamp.releaseQueryID(endID); + stamp.release(this); + startID = -1; + endID = -1; + } + + public boolean isFinished() + { + return GL15.glGetQueryObjectui(endID, GL15.GL_QUERY_RESULT_AVAILABLE) == 1; + } + + public long getResult() + { + return GL33.glGetQueryObjectui64(endID, GL15.GL_QUERY_RESULT) - GL33.glGetQueryObjectui64(startID, GL15.GL_QUERY_RESULT); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/GLUtils.java b/src/main/java/speiger/src/coreengine/rendering/utils/GLUtils.java new file mode 100644 index 0000000..2f1a280 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/GLUtils.java @@ -0,0 +1,131 @@ +package speiger.src.coreengine.rendering.utils; + +import java.nio.IntBuffer; +import java.util.List; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL31; +import org.lwjgl.opengl.GL44; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.rendering.utils.states.BlendState; +import speiger.src.coreengine.rendering.utils.states.CullState; +import speiger.src.coreengine.rendering.utils.states.GLProvoking; +import speiger.src.coreengine.rendering.utils.states.GLState; +import speiger.src.coreengine.rendering.utils.states.GLWireFrame; +import speiger.src.coreengine.rendering.utils.states.IGLState; +import speiger.src.coreengine.rendering.utils.states.LineSize; +import speiger.src.coreengine.rendering.utils.states.PointSize; +import speiger.src.coreengine.utils.counters.averager.Counter; + +public class GLUtils +{ + static final List ALL_STATES = new ObjectArrayList<>(); + public static final GLState MULTI_SAMPLING = addState(new GLState(GL13.GL_MULTISAMPLE, false)); + public static final GLState WIRE_FRAME = addState(new GLWireFrame()); + public static final GLState PROVOKING_VERTEX = addState(new GLProvoking()); + public static final GLState DEBTH_TEST = addState(new GLState(GL11.GL_DEPTH_TEST, false)); + public static final CullState CULL_FACE = addState(new CullState(GL11.GL_BACK)); + public static final BlendState BLEND = addState(new BlendState()); + public static final GLState CLIP_PLANE0 = addState(new GLState(GL11.GL_CLIP_PLANE0, false)); + public static final PointSize POINT_SIZE = addState(new PointSize()); + public static final LineSize LINE_SIZE = addState(new LineSize()); + public static final ScissorsManager TESTER = new ScissorsManager(100); + public static final Counter[] COUNTERS = Counter.createCounters(4); + public static final ViewPortStack VIEW_PORT = new ViewPortStack(); + + public static T addState(T state) + { + ALL_STATES.add(state); + return state; + } + + public static void onFrameEnded() + { + for(int i = 0,m=COUNTERS.length;i= instances.length) + { + GameLog.warn("Scissors test would go out of Bounds!"); + return; + } + inUse++; + if(inUse == 0) + { + instances[inUse].set(x, y, x + width, y + height); + GL11.glEnable(GL11.GL_SCISSOR_TEST); + GL11.glScissor(x, y, width, height); + return; + } + Plane oldBox = instances[inUse - 1]; + int maxX = x + width; + int maxY = y + height; + if (x < oldBox.getMinX()) x = oldBox.getMinX(); + if (maxX > oldBox.getMaxX()) maxX = oldBox.getMaxX(); + if (y < oldBox.getMinY()) y = oldBox.getMinY(); + if (maxY > oldBox.getMaxY()) maxY = oldBox.getMaxY(); + Plane box = instances[inUse]; + box.set(x, y, maxX, maxY); + GL11.glScissor(box.getMinX(), box.getMinY(), box.getWidth(), box.getHeight()); + } + + public boolean isInScissors(int x, int y, int width, int height) + { + if(inUse == -1) + { + return true; + } + return instances[inUse].isIntersecting(x, y, x + width, y + height); + } + + public void disableScissors() + { + if(inUse < 0) + { + GameLog.warn("Scissors test would go into negative"); + return; + } + inUse--; + if(inUse >= 0) + { + Plane box = instances[inUse]; + GL11.glScissor(box.getMinX(), box.getMinY(), box.getWidth(), box.getHeight()); + } + else + { + GL11.glDisable(GL11.GL_SCISSOR_TEST); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/ScreenshotHandler.java b/src/main/java/speiger/src/coreengine/rendering/utils/ScreenshotHandler.java new file mode 100644 index 0000000..321447d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/ScreenshotHandler.java @@ -0,0 +1,248 @@ +package speiger.src.coreengine.rendering.utils; + +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.io.File; +import java.nio.ByteBuffer; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +import javax.imageio.ImageIO; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.system.MemoryUtil; + +import speiger.src.coreengine.rendering.input.window.IWindowListener; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.utils.helpers.FileUtils; +import speiger.src.coreengine.utils.io.GifWriter; + +public class ScreenshotHandler implements IWindowListener +{ + public static final DateTimeFormatter FILE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss"); + public static final DateTimeFormatter FOLDER_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM"); + static final ThreadPoolExecutor FACTORY = (ThreadPoolExecutor)Executors.newFixedThreadPool(1); + + File folder; + int width; + int height; + ByteBuffer buffer; + GifRecorder recorder; + Supplier imageProvider = this::getData; + + public ScreenshotHandler(File helper) + { + FACTORY.setKeepAliveTime(1, TimeUnit.SECONDS); + FACTORY.allowCoreThreadTimeOut(true); + folder = new File(helper, "Screenshots"); + } + + @Override + public void onWindowChanged(Window window) + { + width = window.getWidth(); + height = window.getHeight(); + if(buffer != null) + { + MemoryUtil.memFree(buffer); + buffer = null; + } + buffer = MemoryUtil.memAlloc(width * height * 4); + } + + public void screenShot() + { + ZonedDateTime time = ZonedDateTime.now(); + File subFolder = new File(folder, time.format(FOLDER_FORMAT)); + FileUtils.ensureFolder(subFolder); + try + { + ImageIO.write(getScreenShot(), "png", new File(subFolder, "Screenshot_"+time.format(FILE_FORMAT)+".png")); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + + public boolean isRecording() + { + return recorder != null && !recorder.isFinished(); + } + + public void toggleRecording() + { + if(isRecording()) + { + stopRecording(); + } + else + { + startRecording(); + } + } + + //Undefined time recording + public void startRecording() + { + record("Capture_"+ZonedDateTime.now().format(FILE_FORMAT)+".gif", 15, Integer.MAX_VALUE); + } + + public void record(String fileName, int fps, int time) + { + if(recorder == null) + { + File subFolder = new File(folder, ZonedDateTime.now().format(FOLDER_FORMAT)); + FileUtils.ensureFolder(subFolder); + recorder = new GifRecorder(new File(subFolder, fileName), fps, time); + } + } + + public void stopRecording() + { + if(recorder != null) + { + recorder.finishPic(); + recorder = null; + } + } + + public void update() + { + if(recorder != null) + { + recorder.tick(imageProvider); + if(recorder.isFinished()) + { + recorder.finishPic(); + recorder = null; + } + } + } + + private BufferedImage getData() + { + buffer.clear(); + GL11.glReadBuffer(GL11.GL_FRONT); + GL11.glReadPixels(0, 0, width, height, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer); + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + int[] data = ((DataBufferInt)image.getRaster().getDataBuffer()).getData(); + for(int x = 0;x < width;x++) + { + for(int y = 0;y < height;y++) + { + int i = (x + (width * y)) * 4; + int r = buffer.get(i) & 0xFF; + int g = buffer.get(i + 1) & 0xFF; + int b = buffer.get(i + 2) & 0xFF; + data[x + (width * (height - (y + 1)))] = (0xFF << 24) | (r << 16) | (g << 8) | b; + } + } + return image; + } + + public BufferedImage getScreenShot() + { + buffer.clear(); + GL11.glReadBuffer(GL11.GL_FRONT); + GL11.glReadPixels(0, 0, width, height, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer); + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + int[] data = ((DataBufferInt)image.getRaster().getDataBuffer()).getData(); + for(int x = 0;x < width;x++) + { + for(int y = 0;y < height;y++) + { + int i = (x + (width * y)) * 4; + int r = buffer.get(i) & 0xFF; + int g = buffer.get(i + 1) & 0xFF; + int b = buffer.get(i + 2) & 0xFF; + data[x + (width * (height - (y + 1)))] = (0xFF << 24) | (r << 16) | (g << 8) | b; + } + } + return image; + } + + public void cleanup() + { + if(buffer != null) + { + MemoryUtil.memFree(buffer); + buffer = null; + } + } + + public static class GifRecorder + { + GifWriter writer; + long timeBetweenFrames; + long lastTime = -1L; + long freeTime; + int totalPics; + + public GifRecorder(File file, int fps, int time) + { + int ms = 1000 / fps; + writer = GifWriter.create(file, BufferedImage.TYPE_INT_ARGB, ms); + timeBetweenFrames = ms * 1000000L; + freeTime = ms * 1000000L; + totalPics = fps * time; + } + + public void tick(Supplier supply) + { + if(lastTime == -1L) + { + totalPics--; + insert(supply.get()); + lastTime = System.nanoTime(); + return; + } + long time = System.nanoTime(); + long happend = time - lastTime; + lastTime = time; + freeTime -= happend; + if(freeTime <= 0) + { + freeTime += timeBetweenFrames; + insert(supply.get()); + totalPics--; + } + } + + private void insert(final BufferedImage newBuffer) + { + FACTORY.execute(new Runnable(){ + BufferedImage buffer = newBuffer; + @Override + public void run() + { + if(writer != null) + { + writer.insertPicture(buffer); + } + } + }); + } + + public boolean isFinished() + { + return totalPics <= 0; + } + + public void finishPic() + { + FACTORY.execute(new Runnable(){ + @Override + public void run() + { + writer.close(); + writer = null; + } + }); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/ViewPortStack.java b/src/main/java/speiger/src/coreengine/rendering/utils/ViewPortStack.java new file mode 100644 index 0000000..2c48611 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/ViewPortStack.java @@ -0,0 +1,51 @@ +package speiger.src.coreengine.rendering.utils; + +import org.lwjgl.opengl.GL11; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.utils.Stack; +import speiger.src.coreengine.math.vector.ints.Vec4i; +import speiger.src.coreengine.utils.collections.pools.SimplePool; + +public class ViewPortStack +{ + SimplePool pool = new SimplePool<>(100, Vec4i::newMutable); + Stack viewPort = new ObjectArrayList(); + Vec4i defaultPort = Vec4i.newMutable(); + int inUse = 0; + + public void setDefault(int x, int y, int w, int h) + { + if(defaultPort.getX() != x || defaultPort.getY() != y || defaultPort.getZ() != w || defaultPort.getW() != h) + { + defaultPort.set(x, y, w, h); + if(viewPort.isEmpty()) + { + GL11.glViewport(x, y, w, h); + } + } + } + + public void push(int x, int y, int w, int h) + { + Vec4i current = getCurrent(); + if(current.getX() != x || current.getY() != y || current.getZ() != w || current.getW() != h) + { + viewPort.push(pool.get().set(x, y, w, h)); + GL11.glViewport(x, y, w, h); + } + } + + public void pop() + { + if(viewPort.isEmpty()) return; + pool.accept(viewPort.pop()); + Vec4i current = getCurrent(); + GL11.glViewport(current.getX(), current.getY(), current.getZ(), current.getW()); + } + + public Vec4i getCurrent() + { + return viewPort.isEmpty() ? defaultPort : viewPort.top(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/BlendState.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/BlendState.java new file mode 100644 index 0000000..98e82b5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/BlendState.java @@ -0,0 +1,37 @@ +package speiger.src.coreengine.rendering.utils.states; + +import org.lwjgl.opengl.GL11; + +public class BlendState extends GLState +{ + int src; + int func; + + public BlendState() + { + super(GL11.GL_BLEND, false); + } + + @Override + public BlendState push(boolean newValue) + { + super.push(newValue); + return this; + } + + public BlendState setFunction(int src, int func) + { + if(this.src != src && this.func != func) + { + this.src = src; + this.func = func; + GL11.glBlendFunc(src, func); + } + return this; + } + + public void setDefault() + { + setFunction(GL11.GL_ONE, GL11.GL_ONE); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/CullState.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/CullState.java new file mode 100644 index 0000000..4736126 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/CullState.java @@ -0,0 +1,33 @@ +package speiger.src.coreengine.rendering.utils.states; + +import org.lwjgl.opengl.GL11; + +public class CullState extends GLState +{ + int type; + + public CullState(int defaultState) + { + super(GL11.GL_CULL_FACE, false); + type = defaultState; + } + + @Override + public CullState push(boolean newValue) + { + super.push(newValue); + return this; + } + + public void setCullType(int type) + { + if(this.type != type) + { + this.type = type; + if(lastState) + { + GL11.glCullFace(type); + } + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/FloatState.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/FloatState.java new file mode 100644 index 0000000..8c20b71 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/FloatState.java @@ -0,0 +1,58 @@ +package speiger.src.coreengine.rendering.utils.states; + +import speiger.src.collections.floats.lists.FloatArrayList; +import speiger.src.collections.floats.lists.FloatList; + +public abstract class FloatState implements IGLState +{ + protected final float defaultValue; + protected FloatList states = new FloatArrayList(); + + public FloatState(float defaultValue) + { + this.defaultValue = defaultValue; + } + + public void push(float newValue) + { + if((states.isEmpty() && equalsNot(newValue, defaultValue)) || (states.size() > 0 && equalsNot(states.getFloat(states.size()-1), newValue))) + { + setValue(newValue); + } + states.add(newValue); + } + + public void pop() + { + if(states.isEmpty()) throw new IllegalStateException("State is already reset"); + if(states.size() == 1 && equalsNot(defaultValue, states.getFloat(0))) + { + setValue(defaultValue); + } + else if(states.size() > 1 && equalsNot(states.getFloat(states.size()-2), states.getFloat(states.size()-1))) + { + setValue(states.getFloat(states.size()-2)); + } + states.removeFloat(states.size()-1); + } + + protected abstract void setValue(float value); + + protected abstract String getName(); + + protected boolean equalsNot(float key, float value) + { + return Float.floatToIntBits(key) != Float.floatToIntBits(value); + } + + @Override + public void cleanup() + { + if(states.size() > 1) + { + float value = states.getFloat(states.size()-1); + states.clear(); + states.add(value); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/GLProvoking.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/GLProvoking.java new file mode 100644 index 0000000..445da01 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/GLProvoking.java @@ -0,0 +1,17 @@ +package speiger.src.coreengine.rendering.utils.states; + +import org.lwjgl.opengl.GL32; + +public class GLProvoking extends GLState +{ + public GLProvoking() + { + super(-1, false); + } + + @Override + protected void setValue(boolean value) + { + GL32.glProvokingVertex(value ? GL32.GL_FIRST_VERTEX_CONVENTION : GL32.GL_LAST_VERTEX_CONVENTION); + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/GLState.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/GLState.java new file mode 100644 index 0000000..28f4f7c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/GLState.java @@ -0,0 +1,65 @@ +package speiger.src.coreengine.rendering.utils.states; + +import java.util.BitSet; + +import org.lwjgl.opengl.GL11; + +public class GLState implements IGLState +{ + final int id; + protected final boolean defaultValue; + protected final BitSet memory = new BitSet(); + protected int index = -1; + protected boolean lastState; + + public GLState(int id, boolean defaultValue) + { + this.id = id; + this.defaultValue = defaultValue; + } + + public GLState push(boolean newValue) + { + if((index < 0 && newValue != defaultValue) || (index >= 0 && memory.get(index) != newValue)) + { + setValue(newValue); + } + memory.set(++index, newValue); + return this; + } + + public void pop() + { + if(index < 0) throw new IllegalStateException("State is already reset"); + if(index == 0 && defaultValue != memory.get(0)) + { + setValue(defaultValue); + } + else if(index > 0 && memory.get(index-1) != memory.get(index)) + { + setValue(memory.get(index-1)); + } + index--; + } + + public boolean isEnabled() + { + return index < 0 ? defaultValue : memory.get(index); + } + + protected void setValue(boolean value) + { + if(value) GL11.glEnable(id); + else GL11.glDisable(id); + } + + @Override + public void cleanup() + { + if(index > 0) + { + memory.set(0, memory.get(index)); + index = 0; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/GLWireFrame.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/GLWireFrame.java new file mode 100644 index 0000000..59b42ed --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/GLWireFrame.java @@ -0,0 +1,17 @@ +package speiger.src.coreengine.rendering.utils.states; + +import org.lwjgl.opengl.GL11; + +public class GLWireFrame extends GLState +{ + public GLWireFrame() + { + super(-1, false); + } + + @Override + protected void setValue(boolean value) + { + GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, value ? GL11.GL_LINE : GL11.GL_FILL); + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/IGLState.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/IGLState.java new file mode 100644 index 0000000..be57ac4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/IGLState.java @@ -0,0 +1,6 @@ +package speiger.src.coreengine.rendering.utils.states; + +public interface IGLState +{ + public void cleanup(); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/LineSize.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/LineSize.java new file mode 100644 index 0000000..bb36f6a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/LineSize.java @@ -0,0 +1,25 @@ +package speiger.src.coreengine.rendering.utils.states; + +import org.lwjgl.opengl.GL11; + +public class LineSize extends FloatState +{ + float value = 1F; + + public LineSize() + { + super(1F); + } + + @Override + protected String getName() + { + return "GL_Line_Width"; + } + + @Override + protected void setValue(float value) + { + GL11.glLineWidth(value); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/PointSize.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/PointSize.java new file mode 100644 index 0000000..bf1a1b6 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/PointSize.java @@ -0,0 +1,23 @@ +package speiger.src.coreengine.rendering.utils.states; + +import org.lwjgl.opengl.GL11; + +public class PointSize extends FloatState +{ + public PointSize() + { + super(1F); + } + + @Override + protected String getName() + { + return "GL_Point_Size"; + } + + @Override + protected void setValue(float value) + { + GL11.glPointSize(value); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/CollectionUtils.java b/src/main/java/speiger/src/coreengine/utils/collections/CollectionUtils.java new file mode 100644 index 0000000..c5b6a1c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/CollectionUtils.java @@ -0,0 +1,54 @@ +package speiger.src.coreengine.utils.collections; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import speiger.src.collections.ints.maps.impl.hash.Int2ObjectLinkedOpenHashMap; +import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap; +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap; +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet; +import speiger.src.collections.objects.sets.ObjectOpenHashSet; + +public class CollectionUtils +{ + public static > K createSubCollection(List list, int[] indecies, K result) + { + for(int i = 0;i Set[] createSets(int size, boolean linked) + { + Set[] sets = new Set[size]; + for(int i = 0;i() : new ObjectOpenHashSet(); + } + return sets; + } + + public static List[] createList(int size) + { + List[] list = new List[size]; + for(int i = 0;i(); + } + return list; + } + + public static Int2ObjectMap[] createInt2ObjectMap(int size, boolean linked) + { + Int2ObjectMap[] maps = new Int2ObjectMap[size]; + for(int i = 0;i() : new Int2ObjectOpenHashMap(); + } + return maps; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/FlagHolder.java b/src/main/java/speiger/src/coreengine/utils/collections/FlagHolder.java new file mode 100644 index 0000000..0e323ff --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/FlagHolder.java @@ -0,0 +1,60 @@ +package speiger.src.coreengine.utils.collections; + +public class FlagHolder +{ + int flags; + + public FlagHolder() + { + } + + public FlagHolder(int initFlags) + { + flags = initFlags; + } + + public void setFlag(int flag) + { + flags |= flag; + } + + public boolean setFlag(int flag, boolean value) + { + if(isFlagSet(flag) == value) + { + return false; + } + flags = (value ? flags | flag : flags & ~(flag)); + return true; + } + + public int getFlags() + { + return flags; + } + + public void flipFlag(int flag) + { + flags ^= flag; + } + + public void clearFlag(int flag) + { + flags &= ~flag; + } + + public boolean isFlagSet(int flag) + { + return (flags & flag) == flag; + } + + public boolean isAnyFlagSet(int flag) + { + return (flags & flag) != 0; + } + + public boolean isFlagNotSet(int flag) + { + return (flags & flag) == 0; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/collection/BitArray.java b/src/main/java/speiger/src/coreengine/utils/collections/collection/BitArray.java new file mode 100644 index 0000000..1c69365 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/collection/BitArray.java @@ -0,0 +1,111 @@ +package speiger.src.coreengine.utils.collections.collection; + +public class BitArray +{ + long[] data; + int size; + int bits; + long maxValue; + + public BitArray(int size, int bits) + { + this.size = size; + this.bits = bits; + maxValue = (1L << bits) - 1L; + data = new long[arraySize(size * bits)]; + } + + public BitArray(int size, int bits, long[] data) + { + this.size = size; + this.bits = bits; + maxValue = (1L << bits) - 1L; + int arraySize = arraySize(size * bits); + if(data == null || data.length != arraySize) + { + this.data = new long[arraySize]; + if(data.length < arraySize) + { + System.arraycopy(data, 0, this.data, 0, Math.min(data.length, arraySize)); + } + } + else + { + this.data = data; + } + } + + private int arraySize(int entries) + { + int left = entries % 64; + return left == 0 ? entries / 64 : (entries / 64) + 1; + } + + public void set(int index, long value) + { + if(index < 0 || index >= size) throw new IllegalStateException("Out ouf Bounds"); + + int arrayIndex = (index * bits) / 64; + int bitIndex = (index * bits) % 64; + data[arrayIndex] = data[arrayIndex] & ~(maxValue << bitIndex) | (value & maxValue) << bitIndex; + int left = 64 - bitIndex; + if(left < bits) + { + int leftover = bits - left; + data[arrayIndex+1] = data[arrayIndex+1] >>> leftover << leftover | (value & maxValue) >> left; + } + } + + public void clear(int index) + { + set(index, 0L); + } + + public long get(int index) + { + if(index < 0 || index >= size) throw new IllegalStateException("Out ouf Bounds"); + int arrayIndex = (index * bits) / 64; + int bitIndex = (index * bits) % 64; + return (64 - bitIndex < bits ? (data[arrayIndex] >>> bitIndex | data[arrayIndex+1] << (64 - bitIndex)) : (data[arrayIndex] >>> bitIndex)) & maxValue; + } + + public int getInt(int index) + { + return (int)get(index); + } + + public int swapInt(int index, int newValue) + { + int value = getInt(index); + if(newValue != value) + { + set(index, newValue); + } + return value; + } + + public long swap(int index, long newValue) + { + long value = get(index); + if(newValue != value) + { + set(index, newValue); + } + return value; + } + + public int size() + { + return size; + } + + public int bits() + { + return bits; + } + + public long[] getData() + { + return data; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/collection/FixedBitSet.java b/src/main/java/speiger/src/coreengine/utils/collections/collection/FixedBitSet.java new file mode 100644 index 0000000..3d05fb9 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/collection/FixedBitSet.java @@ -0,0 +1,290 @@ +package speiger.src.coreengine.utils.collections.collection; + +import speiger.src.collections.ints.collections.IntCollection; +import speiger.src.coreengine.math.MathUtils; + +public class FixedBitSet +{ + final long[] data; + final int capacity; + int storedBits = -1; + int wordsInUse = 0; + + public FixedBitSet(int maxBits) + { + capacity = maxBits; + data = new long[MathUtils.ceil(maxBits / 8D)]; + } + + public FixedBitSet(long[] data) + { + this(data.length * 8, data); + } + + public FixedBitSet(int maxBits, long[] data) + { + this.data = data; + capacity = maxBits; + wordsInUse = data.length - 1; + validateWords(); + } + + public void set(boolean value) + { + if(value) + { + set(); + return; + } + clear(); + } + + public void set(int index, boolean value) + { + if(value) + { + set(index); + return; + } + clear(index); + } + + public void set(int fromIndex, int toIndex, boolean value) + { + if(value) + { + set(fromIndex, toIndex); + return; + } + clear(fromIndex, toIndex); + } + + public boolean ifSet(int index, boolean value) + { + return value ? ifSet(index) : ifClear(index); + } + + public void clear() + { + for(int i = 0;i < wordsInUse;i++) + { + data[wordsInUse] = 0; + } + wordsInUse = 0; + storedBits = 0; + } + + public void clear(int index) + { + if(index >= 0 && index < capacity) + { + data[index >> 6] &= ~(1L << index); + validateWords(); + } + } + + public void clear(int fromIndex, int toIndex) + { + if(fromIndex < 0 || toIndex < 0 || fromIndex >= capacity || toIndex >= capacity || fromIndex >= toIndex) + { + return; + } + int startWord = fromIndex >> 6; + if(startWord >= wordsInUse) + { + return; + } + int endWord = (toIndex - 1) >> 6; + if(endWord >= wordsInUse) + { + toIndex = length(); + endWord = wordsInUse - 1; + } + long startIndex = -1 << fromIndex; + long endIndex = -1 >>> -toIndex; + if(startWord == endWord) + { + data[startWord] &= ~(startIndex & endIndex); + } + else + { + data[startWord] &= ~startIndex; + for(int i = startWord + 1;i < endWord;i++) + { + data[i] = 0; + } + data[endWord] &= ~endIndex; + } + validateWords(); + } + + public boolean ifClear(int index) + { + if(index >= 0 && index < capacity) + { + long bit = 1L << index; + int wordIndex = index >> 6; + if((data[wordIndex] & bit) != 0) + { + data[wordIndex] &= ~bit; + validateWords(); + return true; + } + } + return false; + } + + public void set() + { + for(int i = 0;i < data.length;i++) + { + data[i] = -1; + } + wordsInUse = data.length - 1; + storedBits = capacity; + } + + public void set(int index) + { + if(index >= 0 && index < capacity) + { + data[index >> 6] |= 1L << index; + updateWords(index >> 6); + } + } + + public void set(int fromIndex, int toIndex) + { + if(fromIndex < 0 || toIndex < 0 || fromIndex >= capacity || toIndex >= capacity || fromIndex >= toIndex) + { + return; + } + int startWord = fromIndex >> 6; + int endWord = (toIndex - 1) >> 6; + long startIndex = -1 << fromIndex; + long endIndex = -1 >>> -toIndex; + if(startWord == endWord) + { + data[startWord] |= (startIndex & endIndex); + } + else + { + data[startWord] |= startIndex; + for(int i = startWord + 1;i < endWord;i++) + { + data[i] = -1; + } + data[endWord] |= endIndex; + } + updateWords(endWord); + } + + public boolean ifSet(int index) + { + if(index >= 0 && index < capacity) + { + long bit = 1L << index; + int wordIndex = index >> 6; + if((data[wordIndex] & bit) == 0) + { + data[wordIndex] |= bit; + updateWords(wordIndex); + return true; + } + } + return false; + } + + public boolean get(int index) + { + return (data[index >> 6] & (1L << index)) != 0; + } + + protected void validateWords() + { + for(;wordsInUse >= 0;wordsInUse--) + { + if(data[wordsInUse] == 0) + { + wordsInUse--; + } + } + wordsInUse++; + storedBits = -1; + } + + protected void updateWords(int check) + { + if(wordsInUse < check) + { + wordsInUse = check; + } + storedBits = -1; + } + + public boolean isEmpty() + { + return wordsInUse == 0; + } + + public boolean isFull() + { + return cardinality() == capacity; + } + + public int cardinality() + { + if(storedBits != -1) + { + return storedBits; + } + int sum = 0; + for(int i = 0;i < wordsInUse;i++) + { + sum += Long.bitCount(data[i]); + } + storedBits = sum; + return sum; + } + + public int length() + { + return wordsInUse == 0 ? 0 : 64 * (wordsInUse - 1) + (64 - Long.numberOfLeadingZeros(data[wordsInUse - 1])); + } + + public long[] getData() + { + return data.clone(); + } + + public FixedBitSet trim() + { + if(wordsInUse == 0) + { + return new FixedBitSet(1); + } + FixedBitSet set = new FixedBitSet(length()); + System.arraycopy(data, 0, set.data, 0, set.data.length); + set.wordsInUse = wordsInUse; + return set; + } + + public void pourIndexes(IntCollection target) + { + if(wordsInUse == 0) + { + return; + } + for(int i = 0;i < wordsInUse;i++) + { + long word = data[i]; + int wordBits = i << 6; + while(word != 0) + { + long t = word & -word; + target.add(wordBits + Long.bitCount(t - 1)); + word ^= t; + } + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/collection/Lazy.java b/src/main/java/speiger/src/coreengine/utils/collections/collection/Lazy.java new file mode 100644 index 0000000..7644b2b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/collection/Lazy.java @@ -0,0 +1,40 @@ +package speiger.src.coreengine.utils.collections.collection; + +import java.util.function.Supplier; + +public class Lazy +{ + Supplier provider; + T value; + + public Lazy(Supplier provider) + { + this.provider = provider; + } + + public T get() + { + if(value == null) + { + value = provider.get(); + } + return value; + } + + public void clearValue() + { + value = null; + } + + public boolean hasValue() + { + return value != null; + } + + public T reload() + { + T oldValue = value; + value = provider.get(); + return oldValue; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/collection/SaveState.java b/src/main/java/speiger/src/coreengine/utils/collections/collection/SaveState.java new file mode 100644 index 0000000..7503bcd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/collection/SaveState.java @@ -0,0 +1,77 @@ +package speiger.src.coreengine.utils.collections.collection; + +import java.util.Collection; +import java.util.List; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.utils.ObjectLists; + +public class SaveState +{ + ObjectArrayList entries = new ObjectArrayList(); + ObjectArrayList reverted = new ObjectArrayList(); + boolean lock = false; + + public void push(T entry) + { + if(lock) return; + entries.push(entry); + reverted.clear(); + } + + @SuppressWarnings("deprecation") + public void load(Collection toLoad, boolean applied) + { + (applied ? entries : reverted).addAll(toLoad); + } + + public T undo() + { + if(lock) throw new IllegalStateException("Save State Locked"); + if(entries.isEmpty()) + { + throw new IllegalStateException("No State Left"); + } + T value = entries.pop(); + reverted.push(value); + return value; + } + + public T redo() + { + if(lock) throw new IllegalStateException("Save State Locked"); + if(reverted.isEmpty()) + { + throw new IllegalStateException("Nothing to Undo"); + } + T value = reverted.pop(); + entries.push(value); + return value; + } + + public int index() + { + return entries.size(); + } + + public int max() + { + return entries.size() + reverted.size(); + } + + public List getEntries() + { + return ObjectLists.unmodifiable(entries); + } + + public List getReverted() + { + return ObjectLists.unmodifiable(reverted); + } + + public void clear() + { + reverted.clear(); + entries.clear(); + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/collections/iterators/EnumIterator.java b/src/main/java/speiger/src/coreengine/utils/collections/iterators/EnumIterator.java new file mode 100644 index 0000000..6f1fa41 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/iterators/EnumIterator.java @@ -0,0 +1,27 @@ +package speiger.src.coreengine.utils.collections.iterators; + +import java.util.Enumeration; +import java.util.Iterator; + +public class EnumIterator implements Iterator +{ + Enumeration value; + + public EnumIterator(Enumeration value) + { + this.value = value; + } + + @Override + public boolean hasNext() + { + return value.hasMoreElements(); + } + + @Override + public T next() + { + return value.nextElement(); + } + +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/iterators/IndexIterator.java b/src/main/java/speiger/src/coreengine/utils/collections/iterators/IndexIterator.java new file mode 100644 index 0000000..279b23d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/iterators/IndexIterator.java @@ -0,0 +1,31 @@ +package speiger.src.coreengine.utils.collections.iterators; + +import java.util.Iterator; +import java.util.List; + +public class IndexIterator implements Iterator +{ + List list; + int[] indexes; + int index = 0; + + public IndexIterator(List list, int...indexes) + { + this.list = list; + this.indexes = indexes; + } + + @Override + public boolean hasNext() + { + return index < indexes.length; + } + + @Override + public T next() + { + return list.get(indexes[index++]); + } + + +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/iterators/IterableWrapper.java b/src/main/java/speiger/src/coreengine/utils/collections/iterators/IterableWrapper.java new file mode 100644 index 0000000..f83fb63 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/iterators/IterableWrapper.java @@ -0,0 +1,55 @@ +package speiger.src.coreengine.utils.collections.iterators; + +import java.io.BufferedReader; +import java.io.Reader; +import java.util.Enumeration; +import java.util.Iterator; + +import speiger.src.collections.objects.collections.ObjectBidirectionalIterator; + +public class IterableWrapper implements Iterable +{ + Iterator iter; + + protected IterableWrapper(Iterator value) + { + iter = value; + } + + public IterableWrapper asReadOnly() + { + iter = new ReadOnlyIterator(iter); + return this; + } + + @Override + public Iterator iterator() + { + return iter; + } + + public static IterableWrapper reverseWrap(ObjectBidirectionalIterator value) + { + return new IterableWrapper(new ReverseIterator(value)); + } + + public static IterableWrapper wrap(Iterator value) + { + return new IterableWrapper(value); + } + + public static IterableWrapper wrap(Enumeration value) + { + return new IterableWrapper(new EnumIterator(value)); + } + + public static IterableWrapper wrap(Reader reader) + { + return new IterableWrapper(new ReaderIterator(reader)); + } + + public static IterableWrapper wrap(BufferedReader reader) + { + return new IterableWrapper(new ReaderIterator(reader)); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReadOnlyIterator.java b/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReadOnlyIterator.java new file mode 100644 index 0000000..79485fd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReadOnlyIterator.java @@ -0,0 +1,26 @@ +package speiger.src.coreengine.utils.collections.iterators; + +import java.util.Iterator; + +public class ReadOnlyIterator implements Iterator +{ + Iterator data; + + public ReadOnlyIterator(Iterator iter) + { + data = iter; + } + + @Override + public boolean hasNext() + { + return data.hasNext(); + } + + @Override + public T next() + { + return data.next(); + } + +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReaderIterator.java b/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReaderIterator.java new file mode 100644 index 0000000..20f4f09 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReaderIterator.java @@ -0,0 +1,57 @@ +package speiger.src.coreengine.utils.collections.iterators; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.Iterator; + +public class ReaderIterator implements Iterator +{ + BufferedReader reader; + String value = null; + + public ReaderIterator(Reader reader) + { + this(new BufferedReader(reader)); + } + + public ReaderIterator(InputStream stream) + { + this(new InputStreamReader(stream)); + } + + public ReaderIterator(BufferedReader reader) + { + this.reader = reader; + } + + @Override + public boolean hasNext() + { + if(value != null) + { + return true; + } + try + { + value = reader.readLine(); + } + catch(IOException e) + { + e.printStackTrace(); + return false; + } + return value != null; + } + + @Override + public String next() + { + String result = value; + value = null; + return result; + } + +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReverseIterator.java b/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReverseIterator.java new file mode 100644 index 0000000..0d78bfe --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReverseIterator.java @@ -0,0 +1,27 @@ +package speiger.src.coreengine.utils.collections.iterators; + +import java.util.Iterator; + +import speiger.src.collections.objects.collections.ObjectBidirectionalIterator; + +public class ReverseIterator implements Iterator +{ + ObjectBidirectionalIterator iter; + + public ReverseIterator(ObjectBidirectionalIterator iter) + { + this.iter = iter; + } + + @Override + public boolean hasNext() + { + return iter.hasPrevious(); + } + + @Override + public T next() + { + return iter.previous(); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/DataSlot.java b/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/DataSlot.java new file mode 100644 index 0000000..e43ff7e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/DataSlot.java @@ -0,0 +1,64 @@ +package speiger.src.coreengine.utils.collections.managers.dynamic; + +public class DataSlot implements Comparable +{ + DataSlot previous; + DataSlot next; + int dataIndex = -1; + int byteOffset; + byte[] value; + + public DataSlot set(DataSlot previous, int dataIndex, byte[] value) + { + this.previous = previous; + byteOffset = previous == null ? 0 : previous.getNextByteOffset(); + this.dataIndex = dataIndex; + this.value = value; + if(this.previous != null) this.previous.next = this; + return this; + } + + public DataSlot setData(int dataIndex, byte[] value) + { + this.dataIndex = dataIndex; + this.value = value; + return this; + } + + @Override + public int compareTo(DataSlot o) + { + return Integer.compare(byteOffset, o.byteOffset); + } + + public int getNextByteOffset() + { + return byteOffset + (value == null ? 0 : value.length); + } + + public int getUsedBytes() + { + return next == null ? (value == null ? 0 : value.length) : next.byteOffset - byteOffset; + } + + public int getFreeBytes() + { + return next == null ? Integer.MAX_VALUE : next.byteOffset - byteOffset; + } + + public void clear() + { + dataIndex = -1; + value = null; + if(previous != null) + { + previous.next = null; + previous = null; + } + if(next != null) + { + next.previous = null; + next = null; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/DynamicDataManager.java b/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/DynamicDataManager.java new file mode 100644 index 0000000..195f178 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/DynamicDataManager.java @@ -0,0 +1,216 @@ +package speiger.src.coreengine.utils.collections.managers.dynamic; + +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import speiger.src.collections.ints.collections.IntIterator; +import speiger.src.collections.ints.maps.abstracts.AbstractInt2IntMap.BasicEntry; +import speiger.src.collections.ints.maps.abstracts.AbstractInt2ObjectMap; +import speiger.src.collections.ints.maps.interfaces.Int2IntMap; +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap; +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry; +import speiger.src.collections.ints.sets.IntAVLTreeSet; +import speiger.src.collections.ints.sets.IntSet; +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.sets.ObjectAVLTreeSet; +import speiger.src.collections.objects.utils.ObjectLists; +import speiger.src.coreengine.utils.collections.pools.ThreadPool; + +public class DynamicDataManager +{ + static final ThreadPool SLOTS = new ThreadPool<>(1000, DataSlot::new, DataSlot::clear); + final IDynamicDataHandler manager; + Int2ObjectMap slots = Int2ObjectMap.createLinkedMap(); + Set emptySlots = new ObjectAVLTreeSet<>(); + IntSet changedSlots = new IntAVLTreeSet(); + DataSlot lastSlot = null; + + public DynamicDataManager(IDynamicDataHandler manager) + { + this.manager = manager; + } + + public boolean add(int dataIndex, T value) + { + if(slots.containsKey(dataIndex)) return false; + return add(dataIndex, manager.toBytes(value)); + } + + protected boolean add(int dataIndex, byte[] data) + { + if(!emptySlots.isEmpty()) + { + for(Iterator iter = emptySlots.iterator();iter.hasNext();) + { + DataSlot slot = iter.next(); + if(slot.getFreeBytes() >= data.length) + { + slot.setData(dataIndex, data); + slots.put(dataIndex, slot); + changedSlots.add(dataIndex); + return true; + } + } + } + DataSlot next = SLOTS.get(); + next.set(lastSlot, dataIndex, data); + slots.put(dataIndex, next); + changedSlots.add(dataIndex); + lastSlot = next; + return true; + } + + public boolean contains(int dataIndex) + { + return slots.containsKey(dataIndex); + } + + public boolean update(int dataIndex, T value) + { + DataSlot slot = slots.get(dataIndex); + if(slot != null) + { + byte[] data = manager.toBytes(value); + if(slot.getFreeBytes() >= data.length) + { + slot.setData(dataIndex, data); + changedSlots.add(dataIndex); + return true; + } + else + { + slot.setData(-1, null); + slots.remove(dataIndex); + add(dataIndex, data); + emptySlots.add(slot); + return true; + } + } + return false; + } + + public Iterator iterator(int byteOffset) + { + return new Iterator() { + BasicEntry entry = new BasicEntry(); + DataSlot current = findNextSlot(lastSlot); + @Override + public boolean hasNext() + { + return current != null; + } + + DataSlot findNextSlot(DataSlot input) + { + for(input = input.previous; input != null && input.value == null;input = input.previous); + return input; + } + + @Override + public Int2IntMap.Entry next() + { + if(!hasNext()) throw new IllegalStateException(); + entry.set(current.dataIndex, current.byteOffset / byteOffset); + current = findNextSlot(current); + return entry; + } + }; + } + + public boolean remove(int dataIndex) + { + DataSlot slot = slots.remove(dataIndex); + if(slot != null) + { + slot.setData(-1, null); + emptySlots.add(slot); + return true; + } + return false; + } + + protected void clearEmptySlots() + { + while(lastSlot != null && lastSlot.value == null) + { + DataSlot prev = lastSlot; + emptySlots.remove(prev); + lastSlot = prev.previous; + SLOTS.accept(prev); + } + } + + public void clear() + { + slots.clear(); + emptySlots.clear(); + changedSlots.clear(); + while(lastSlot != null) + { + DataSlot prev = lastSlot; + lastSlot = prev.previous; + SLOTS.accept(prev); + } + } + + public void update() + { + int last = getEndSize(); + clearEmptySlots(); + if(changedSlots.isEmpty()) + { + if(last != getEndSize()) + { + manager.uploadBytes(ObjectLists.empty(), getEndSize()); + } + return; + } + List> list = new ObjectArrayList<>(); + while(!changedSlots.isEmpty()) + { + DataSlot start = null; + DataSlot current = null; + int bytesAllocated = 0; + for(IntIterator iter = changedSlots.iterator();iter.hasNext();) + { + DataSlot slot = slots.get(iter.nextInt()); + iter.remove(); + if(slot == null) break; + if(current == null || current.next == slot) + { + current = slot; + if(start == null) start = slot; + bytesAllocated += slot.getUsedBytes(); + continue; + } + break; + } + if(bytesAllocated <= 0) continue; + byte[] data = new byte[bytesAllocated]; + int byteOffset = start.byteOffset; + bytesAllocated = 0; + while(start.byteOffset <= current.byteOffset); + { + System.arraycopy(start.value, 0, data, bytesAllocated, start.value.length); + bytesAllocated += start.getUsedBytes(); + start = start.next; + } + list.add(new AbstractInt2ObjectMap.BasicEntry<>(byteOffset, data)); + } + if(!list.isEmpty()) + { + manager.uploadBytes(list, getEndSize()); + } + } + + private int getEndSize() + { + return lastSlot == null ? 0 : lastSlot.getNextByteOffset(); + } + + public int size() + { + return slots.size(); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/IDynamicDataHandler.java b/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/IDynamicDataHandler.java new file mode 100644 index 0000000..fa34aeb --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/IDynamicDataHandler.java @@ -0,0 +1,11 @@ +package speiger.src.coreengine.utils.collections.managers.dynamic; + +import java.util.List; + +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry; + +public interface IDynamicDataHandler +{ + public byte[] toBytes(T entry); + public void uploadBytes(List> list, int newArraySize); +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/FixedDataManager.java b/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/FixedDataManager.java new file mode 100644 index 0000000..5f69bea --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/FixedDataManager.java @@ -0,0 +1,358 @@ +package speiger.src.coreengine.utils.collections.managers.fixed; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.List; +import java.util.concurrent.Callable; + +import speiger.src.collections.ints.collections.IntCollection; +import speiger.src.collections.ints.collections.IntIterator; +import speiger.src.collections.ints.functions.IntComparator; +import speiger.src.collections.ints.lists.IntArrayList; +import speiger.src.collections.ints.lists.IntList; +import speiger.src.collections.ints.maps.abstracts.AbstractInt2ObjectMap.BasicEntry; +import speiger.src.collections.ints.maps.impl.hash.Int2IntOpenHashMap; +import speiger.src.collections.ints.maps.interfaces.Int2IntMap; +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry; +import speiger.src.collections.ints.sets.IntAVLTreeSet; +import speiger.src.collections.ints.sets.IntLinkedOpenHashSet; +import speiger.src.collections.ints.sets.IntSet; +import speiger.src.collections.ints.sets.IntSortedSet; +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.utils.ObjectArrays; +import speiger.src.collections.objects.utils.ObjectLists; +import speiger.src.coreengine.utils.collections.pools.ThreadPool; + +public class FixedDataManager +{ + static final ThreadPool SLOTS = new ThreadPool<>(1000, FixedSlot::new); + + final int bytesPerEntry; + final IFixedDataHandler manager; + Int2IntMap slots = new Int2IntOpenHashMap(); + IntArrayList slotData = new IntArrayList(); + IntSortedSet freeSlots = new IntAVLTreeSet(); + IntSet changes = new IntLinkedOpenHashSet(); + + public FixedDataManager(int bytesPerEntry, IFixedDataHandler manager) + { + this.bytesPerEntry = bytesPerEntry; + this.manager = manager; + slots.setDefaultReturnValue(-1); + } + + public boolean add(int dataIndex) + { + if(dataIndex == -1) + { + throw new IllegalStateException("-1 isnt allowed to be a dataIndex"); + } + if(slots.containsKey(dataIndex)) + { + return false; + } + if(freeSlots.isEmpty()) + { + slots.put(dataIndex, slotData.size()); + slotData.add(dataIndex); + changes.add(dataIndex); + return true; + } + int slot = freeSlots.pollFirstInt(); + slots.put(dataIndex, slot); + slotData.set(slot, dataIndex); + changes.add(dataIndex); + return true; + } + + public void addAll(IntCollection indexes) + { + for(int index : indexes) + { + add(index); + } + } + + public boolean contains(int dataIndex) + { + return slots.containsKey(dataIndex); + } + + public boolean update(int dataIndex) + { + if(slots.containsKey(dataIndex)) + { + changes.add(dataIndex); + return true; + } + return false; + } + + public void updateAll(IntCollection indexes) + { + for(int index : indexes) + { + if(slots.containsKey(index)) + { + changes.add(index); + } + } + } + + public boolean remove(int dataIndex) + { + int slot = slots.remove(dataIndex); + if(slot >= 0 && slot < slotData.size()) + { + slotData.set(slot, -1); + freeSlots.add(slot); + changes.remove(dataIndex); + return true; + } + return false; + } + + public void removeAll(IntCollection indexes) + { + for(int index : indexes) + { + remove(index); + } + } + + public void clear() + { + changes.clear(); + slots.clear(); + freeSlots.clear(); + slotData.clear(); + } + + public void reload(boolean now) + { + if(slots.isEmpty()) + { + return; + } + changes.addAll(slots.keySet()); + if(now) + { + updateNow(); + } + } + + public int size() + { + return slots.size(); + } + + public IntIterator valueIterator() + { + return slotData.iterator(); + } + + public void copy(FixedDataManager manager) + { + IntList list = manager.slotData; + for(int i = 0,m=list.size();i call = update(); + if(call != null) + { + call.call().run(); + } + } + catch(Exception e){} + } + + public Callable update() + { + int lastSize = slotData.size(); + clearEmptySlots(); + if(changes.isEmpty()) + { + if(slotData.size() != lastSize) + { + return new ShrinkTask(new FinishingTask(manager, ObjectLists.empty(), size())); + } + return null; + } + FixedSlot[] slots = SLOTS.get(new FixedSlot[changes.size()]); + int index = 0; + for(int dataIndex : changes) + { + slots[index++].set(dataIndex, this.slots.get(dataIndex)); + } + changes.clear(); + return new ProcessTask(slots, size(), bytesPerEntry, manager); + } + + protected void clearEmptySlots() + { + if(freeSlots.isEmpty()) + { + return; + } + if(slots.isEmpty()) + { + freeSlots.clear(); + slotData.clear(); + changes.clear(); + return; + } + int size = freeSlots.size(); + while(size > 0) + { + size -= ensureData(); + if(slotData.isEmpty() || freeSlots.isEmpty()) + { + break; + } + int index = freeSlots.pollFirstInt(); + int dataIndex = slotData.pop(); + slotData.set(index, dataIndex); + slots.put(dataIndex, index); + changes.add(dataIndex); + } + } + + protected int ensureData() + { + int cleared = 0; + while(slotData.top() == -1) + { + freeSlots.remove(slotData.size() - 1); + slotData.pop(); + cleared++; + } + return cleared; + } + + static class ProcessTask implements Callable + { + FixedSlot[] slots; + int size; + int bytes; + IFixedDataHandler handler; + + public ProcessTask(FixedSlot[] slots, int size, int bytes, IFixedDataHandler handler) + { + this.slots = slots; + this.size = size; + this.bytes = bytes; + this.handler = handler; + } + + @Override + public Runnable call() + { + ObjectArrays.quickSort(slots); + List fixed = new ObjectArrayList(); + List> data = new ObjectArrayList>(); + FixedSlot first = null; + FixedSlot last = null; + for(int i = 0,m=slots.length;i(first.index * bytes, buffer.array())); + i--; + first = last = null; + } + if(first != null) + { + ByteBuffer buffer = ByteBuffer.allocate(fixed.size() * bytes).order(ByteOrder.nativeOrder()); + handler.createData(buffer, fixed.size(), fixed); + fixed.clear(); + data.add(new BasicEntry(first.index * bytes, buffer.array())); + } + SLOTS.accept(slots); + return new FinishingTask(handler, data, size); + } + } + + static class ShrinkTask implements Callable + { + Runnable run; + + public ShrinkTask(Runnable run) + { + this.run = run; + } + + @Override + public Runnable call() throws Exception + { + return run; + } + } + + static class FinishingTask implements Runnable + { + IFixedDataHandler handler; + List> data; + int size; + + public FinishingTask(IFixedDataHandler handler, List> data, int size) + { + this.handler = handler; + this.data = data; + this.size = size; + } + + @Override + public void run() + { + handler.updateData(data, size); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/FixedSlot.java b/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/FixedSlot.java new file mode 100644 index 0000000..1f80994 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/FixedSlot.java @@ -0,0 +1,40 @@ +package speiger.src.coreengine.utils.collections.managers.fixed; + +public class FixedSlot implements Comparable +{ + int index; + int data; + + public void set(int data, int index) + { + this.data = data; + this.index = index; + } + + public boolean isNext(FixedSlot next) + { + return next.index - index == 1; + } + + @Override + public String toString() + { + return "DataSlot[Index="+index+", Data="+data+"]"; + } + + @Override + public int compareTo(FixedSlot o) + { + return Integer.compareUnsigned(index, o.index); + } + + public int getData() + { + return data; + } + + public int getIndex() + { + return index; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/IFixedDataHandler.java b/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/IFixedDataHandler.java new file mode 100644 index 0000000..110abf0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/IFixedDataHandler.java @@ -0,0 +1,13 @@ +package speiger.src.coreengine.utils.collections.managers.fixed; + +import java.nio.ByteBuffer; +import java.util.List; + +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry; + +public interface IFixedDataHandler +{ + public void createData(ByteBuffer data, int size, List slots); + + public void updateData(List> list, int newSize); +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/collections/pools/FixedPool.java b/src/main/java/speiger/src/coreengine/utils/collections/pools/FixedPool.java new file mode 100644 index 0000000..4924f65 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/pools/FixedPool.java @@ -0,0 +1,96 @@ +package speiger.src.coreengine.utils.collections.pools; + +import java.util.Collection; +import java.util.Iterator; +import java.util.function.Consumer; + +import speiger.src.coreengine.utils.functions.Functions; + +public class FixedPool implements IPool +{ + T[] stack; + int stored; + Consumer acceptor; + + public FixedPool(T[] stack) + { + this(stack, 0); + } + + public FixedPool(T[] stack, int stored) + { + this(stack, stored, Functions.getVoidConsumer()); + } + + public FixedPool(T[] stack, Consumer acceptor) + { + this(stack, 0, acceptor); + } + + public FixedPool(T[] stack, int stored, Consumer acceptor) + { + this.stack = stack; + this.stored = stored; + this.acceptor = acceptor; + } + + @Override + public T get() + { + if(stored <= 0) + { + throw new IllegalStateException("Pool is empty"); + } + T result = stack[stored]; + stack[stored--] = null; + return result; + } + + @Override + public T[] get(T[] input) + { + if(stored < input.length) + { + throw new IllegalStateException("Pool is to Small"); + } + for(int i = 0,m=input.length;i= stack.length) + { + throw new IllegalStateException("Pool is Full"); + } + acceptor.accept(t); + stack[stored++] = t; + } + + @Override + public void accept(T[] values) + { + for(int i = 0,m=Math.min(values.length, stack.length - stored);i values) + { + Iterator iter = values.iterator(); + for(int i = 0,m=Math.min(values.size(), stack.length - stored);i extends Consumer, Supplier +{ + public T[] get(T[] input); + + public void accept(T[] values); + + public void accept(Collection values); +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/pools/SimplePool.java b/src/main/java/speiger/src/coreengine/utils/collections/pools/SimplePool.java new file mode 100644 index 0000000..3199419 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/pools/SimplePool.java @@ -0,0 +1,79 @@ +package speiger.src.coreengine.utils.collections.pools; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.utils.functions.Functions; + +public class SimplePool implements IPool +{ + int cap; + List stack; + Supplier creator; + Consumer acceptor; + + public SimplePool(int size, Supplier creator) + { + this(size, creator, Functions.getVoidConsumer()); + } + + public SimplePool(int size, Supplier creator, Consumer acceptor) + { + cap = size; + stack = new ObjectArrayList(size); + this.creator = creator; + this.acceptor = acceptor; + } + + @Override + public void accept(T t) + { + if(stack.size() < cap) + { + stack.add(t); + acceptor.accept(t); + } + } + + @Override + public T get() + { + return stack.isEmpty() ? creator.get() : stack.remove(stack.size() - 1); + } + + @Override + public T[] get(T[] input) + { + for(int i = 0,m=input.length;i values) + { + Iterator iter = values.iterator(); + for(int i = 0,m=Math.min(values.size(), cap - stack.size());i implements IPool +{ + ThreadLocal> stack = ThreadLocal.withInitial(ObjectArrayList::new); + int cap; + Supplier creator; + Consumer acceptor; + + public ThreadLocalPool(int size, Supplier creator) + { + this(size, creator, Functions.getVoidConsumer()); + } + + public ThreadLocalPool(int size, Supplier creator, Consumer acceptor) + { + cap = size; + this.creator = creator; + this.acceptor = acceptor; + } + + @Override + public void accept(T t) + { + List list = stack.get(); + if(list.size() >= cap) return; + acceptor.accept(t); + list.add(t); + } + + @Override + public T get() + { + List list = stack.get(); + return list.isEmpty() ? creator.get() : list.remove(list.size() - 1); + } + + @Override + public T[] get(T[] input) + { + List list = stack.get(); + for(int i = 0,m=input.length;i list = stack.get(); + for(int i = 0,m=Math.min(array.length, cap - list.size());i values) + { + List list = stack.get(); + Iterator iter = values.iterator(); + for(int i = 0,m=Math.min(values.size(), cap - list.size());i implements IPool +{ + int cap; + List stack; + Supplier creator; + Consumer acceptor; + + public ThreadPool(int size, Supplier creator) + { + this(size, creator, Functions.getVoidConsumer()); + } + + public ThreadPool(int size, Supplier creator, Consumer acceptor) + { + cap = size; + stack = new ObjectArrayList(size); + this.creator = creator; + this.acceptor = acceptor; + } + + @Override + public synchronized void accept(T t) + { + if(stack.size() < cap) + { + stack.add(t); + acceptor.accept(t); + } + } + + @Override + public synchronized void accept(T[] array) + { + for(int i = 0,m=Math.min(array.length, cap - stack.size());i array) + { + Iterator iter = array.iterator(); + for(int i = 0,m=Math.min(array.size(), cap - stack.size());i void resolveDependencies(Object2ObjectMap map, Set values) + { + Map> results = new Object2ObjectLinkedOpenHashMap>(); + for(Object2ObjectMap.Entry entry : Object2ObjectMaps.fastIterable(map)) + { + results.put(entry.getKey(), new Entry(entry.getKey(), entry.getValue())); + } + for(Entry entry : results.values()) + { + Entry newer = results.get(entry.value); + if(newer != null) + { + newer.addChild(entry); + } + } + for(Entry entry : results.values()) + { + if(entry.isLoop()) + { + entry.clearLoop(values); + } + } + if(!results.entrySet().removeIf(T -> T.getValue().parent != null)) + { + return; + } + map.clear(); + for(Entry root : results.values()) + { + root.addToMap(map); + } + } + + static class Entry + { + T key; + T value; + Entry parent = null; + List> children = new ObjectArrayList>(); + + public Entry(T key, T value) + { + this.key = key; + this.value = value; + } + + public void addChild(Entry child) + { + if(child != null) + { + children.add(child); + child.parent = this; + } + } + + public void addToMap(Map map) + { + addToMap(value, map); + } + + protected void addToMap(T newValue, Map map) + { + map.put(key, newValue); + for(int i = 0,m=children.size();i current = parent; + while(current.parent != null) + { + current = current.parent; + if(current == parent) + { + return true; + } + } + return false; + } + + protected void clearLoop(Set keys) + { + if(keys.contains(value)) + { + parent = null; + return; + } + parent.clearLoop(keys); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/FlatRegistry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/FlatRegistry.java new file mode 100644 index 0000000..ddd768b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/FlatRegistry.java @@ -0,0 +1,60 @@ +package speiger.src.coreengine.utils.collections.registries.flat; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.utils.collections.registries.simple.SimpleRegistryWithDefault; + +public class FlatRegistry> extends SimpleRegistryWithDefault +{ + int usedCapacity = 0; + IFlatRegistryEntry[] flatIds; + + public FlatRegistry(int capacity, AssetLocation defaultValue) + { + super(capacity, defaultValue); + } + + @Override + public T register(T value) + { + int meta = value.getMetadata() + 1; + if(meta >= capacity - usedCapacity) + { + throw new RuntimeException("Registry Is Overflowed"); + } + T result = super.register(value); + usedCapacity += meta; + return result; + } + + public void bake() + { + freeze(); + flatIds = new IFlatRegistryEntry[usedCapacity]; + int index = 0; + for(T entry : this) + { + entry.setFlatId(index); + int meta = entry.getMetadata(); + for(int i = 0;i<=meta;i++) + { + flatIds[index+i] = entry; + } + index+=meta + 1; + } + } + + public T getFromFlatId(int id) + { + return (T)flatIds[id]; + } + + public int getMetadata(int id) + { + return id - flatIds[id].getFlatId(); + } + + public int flatSize() + { + return flatIds.length; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/FlatRegistryEntry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/FlatRegistryEntry.java new file mode 100644 index 0000000..19155c0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/FlatRegistryEntry.java @@ -0,0 +1,20 @@ +package speiger.src.coreengine.utils.collections.registries.flat; + +import speiger.src.coreengine.utils.collections.registries.simple.RegistryEntry; + +public abstract class FlatRegistryEntry> extends RegistryEntry implements IFlatRegistryEntry +{ + int flatId; + + @Override + public void setFlatId(int id) + { + this.flatId = id; + } + + @Override + public int getFlatId() + { + return flatId; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/IFlatRegistryEntry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/IFlatRegistryEntry.java new file mode 100644 index 0000000..49a625a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/IFlatRegistryEntry.java @@ -0,0 +1,11 @@ +package speiger.src.coreengine.utils.collections.registries.flat; + +import speiger.src.coreengine.utils.collections.registries.simple.IRegistryEntry; + +public interface IFlatRegistryEntry> extends IRegistryEntry +{ + public int getMetadata(); + + public void setFlatId(int id); + public int getFlatId(); +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/mutable/IMutableRegistry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/mutable/IMutableRegistry.java new file mode 100644 index 0000000..7130dfb --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/mutable/IMutableRegistry.java @@ -0,0 +1,12 @@ +package speiger.src.coreengine.utils.collections.registries.mutable; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.utils.collections.registries.simple.IRegistry; +import speiger.src.coreengine.utils.collections.registries.simple.IRegistryEntry; + +public interface IMutableRegistry> extends IRegistry +{ + public default boolean remove(T entry){return remove(getId(entry));} + public default boolean remove(AssetLocation name){return remove(getId(name));} + public boolean remove(int id); +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/mutable/MutableRegistry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/mutable/MutableRegistry.java new file mode 100644 index 0000000..9961bf3 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/mutable/MutableRegistry.java @@ -0,0 +1,127 @@ +package speiger.src.coreengine.utils.collections.registries.mutable; + +import java.util.BitSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.lists.ObjectList; +import speiger.src.collections.objects.maps.impl.hash.Object2IntLinkedOpenHashMap; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2IntMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.utils.ObjectCollections; +import speiger.src.collections.objects.utils.ObjectSets; +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.utils.collections.iterators.ReadOnlyIterator; +import speiger.src.coreengine.utils.collections.registries.simple.IRegistryEntry; + +public class MutableRegistry> implements IMutableRegistry +{ + protected Object2IntMap nameToId = new Object2IntLinkedOpenHashMap(); + protected Object2ObjectMap nameToValue = new Object2ObjectLinkedOpenHashMap(); + protected ObjectList idsToName = new ObjectArrayList(); + protected BitSet freeIds; + protected int capacity; + protected int stored; + + public MutableRegistry(int capacity) + { + this.capacity = capacity; + freeIds = new BitSet(capacity + 1); + nameToId.setDefaultReturnValue(-1); + } + + @Override + public T register(T value) + { + if(value.getName() == null) + { + throw new IllegalStateException(value+", is Missing a name"); + } + AssetLocation name = value.getName(); + if(nameToValue.get(name) != null) + { + throw new IllegalStateException("Name is already registered [Name="+name+", Tile="+nameToValue.get(name)+"]"); + } + int freeBit = freeIds.nextClearBit(0); + if(capacity > 0 && freeBit >= capacity) + { + throw new IllegalStateException("Registry is full"); + } + while(idsToName.size() <= freeBit) + { + idsToName.add(null); + } + freeIds.set(freeBit); + idsToName.set(freeBit, name); + nameToValue.put(name, value); + nameToId.put(name, freeBit); + value.setId(this, freeBit); + stored++; + return value; + } + + @Override + public boolean remove(int id) + { + if(freeIds.get(id)) + { + freeIds.clear(id); + AssetLocation name = idsToName.get(id); + idsToName.set(id, null); + nameToId.rem(name); + nameToValue.remove(name).setId(null, -1); + stored--; + return true; + } + return false; + } + + @Override + public int getId(AssetLocation name) + { + return nameToId.getInt(name); + } + + @Override + public AssetLocation getName(int id) + { + return idsToName.get(id); + } + + @Override + public T getValue(AssetLocation name) + { + return nameToValue.get(name); + } + + public boolean isEmpty() + { + return stored <= 0; + } + + public int size() + { + return stored; + } + + @Override + public Set keySet() + { + return ObjectSets.unmodifiable(nameToId.keySet()); + } + + @Override + public Collection values() + { + return ObjectCollections.unmodifiable(nameToValue.values()); + } + + @Override + public Iterator iterator() + { + return new ReadOnlyIterator(nameToValue.values().iterator()); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/IRegistry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/IRegistry.java new file mode 100644 index 0000000..39ae2fe --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/IRegistry.java @@ -0,0 +1,23 @@ +package speiger.src.coreengine.utils.collections.registries.simple; + +import java.util.Collection; +import java.util.Set; + +import speiger.src.coreengine.assets.AssetLocation; + +public interface IRegistry> extends Iterable +{ + public T register(T value); + + public default int getId(T value){return getId(value.getName());} + public int getId(AssetLocation name); + public AssetLocation getName(int id); + public T getValue(AssetLocation name); + public default T getValue(int id){return getValue(getName(id));} + + public default boolean contains(AssetLocation name){return getValue(name) != null;} + public default boolean contains(T value){return contains(value.getName());} + + public Set keySet(); + public Collection values(); +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/IRegistryEntry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/IRegistryEntry.java new file mode 100644 index 0000000..40a47b4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/IRegistryEntry.java @@ -0,0 +1,13 @@ +package speiger.src.coreengine.utils.collections.registries.simple; + +import speiger.src.coreengine.assets.AssetLocation; + +public interface IRegistryEntry> +{ + public AssetLocation getName(); + + public default T setName(String domain, String name) { return setName(AssetLocation.of(domain, name));} + public T setName(AssetLocation name); + + public void setId(IRegistry registry, int registryId); +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/RegistryEntry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/RegistryEntry.java new file mode 100644 index 0000000..2ffaeed --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/RegistryEntry.java @@ -0,0 +1,33 @@ +package speiger.src.coreengine.utils.collections.registries.simple; + +import speiger.src.coreengine.assets.AssetLocation; + +public abstract class RegistryEntry> implements IRegistryEntry +{ + AssetLocation location; + int id; + + @Override + public AssetLocation getName() + { + return location; + } + + @Override + public T setName(AssetLocation name) + { + location = name; + return (T)this; + } + + @Override + public void setId(IRegistry registry, int registryId) + { + this.id = registryId; + } + + public int getRegistryId() + { + return id; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/SimpleRegistry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/SimpleRegistry.java new file mode 100644 index 0000000..ef4d99f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/SimpleRegistry.java @@ -0,0 +1,133 @@ +package speiger.src.coreengine.utils.collections.registries.simple; + +import java.util.BitSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.lists.ObjectList; +import speiger.src.collections.objects.maps.impl.hash.Object2IntLinkedOpenHashMap; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2IntMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.utils.ObjectCollections; +import speiger.src.collections.objects.utils.ObjectSets; +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.utils.collections.iterators.ReadOnlyIterator; + +public class SimpleRegistry> implements IRegistry +{ + protected Object2IntMap nameToId = new Object2IntLinkedOpenHashMap(); + protected Object2ObjectMap nameToValue = new Object2ObjectLinkedOpenHashMap(); + protected ObjectList idsToName = new ObjectArrayList(); + protected BitSet freeIds; + protected int capacity; + protected int stored; + + boolean frozen = false; + Object2IntMap missing = new Object2IntLinkedOpenHashMap(); + Object2ObjectMap remaps = new Object2ObjectLinkedOpenHashMap(); + + public SimpleRegistry(int capacity) + { + this.capacity = capacity; + freeIds = new BitSet(capacity + 1); + nameToId.setDefaultReturnValue(-1); + } + + @Override + public T register(T value) + { + if(frozen) + { + throw new RuntimeException("Registry is Frozen"); + } + if(value.getName() == null) + { + throw new IllegalStateException(value+", is Missing a name"); + } + AssetLocation name = value.getName(); + if(nameToValue.get(name) != null) + { + throw new IllegalStateException("Name is already registered [Name="+name+", Tile="+nameToValue.get(name)+"]"); + } + int freeBit = freeIds.nextClearBit(0); + if(capacity >= 0 && freeBit >= capacity) + { + throw new IllegalStateException("Registry is full"); + } + while(idsToName.size() <= freeBit) + { + idsToName.add(null); + } + freeIds.set(freeBit); + idsToName.set(freeBit, name); + nameToValue.put(name, value); + nameToId.put(name, freeBit); + value.setId(this, freeBit); + if(missing.size() > 0) + { + missing.remove(name); + } + stored++; + return value; + } + + @Override + public int getId(AssetLocation name) + { + return nameToId.getInt(remap(name)); + } + + @Override + public AssetLocation getName(int id) + { + return idsToName.get(id); + } + + @Override + public T getValue(AssetLocation name) + { + return nameToValue.get(remap(name)); + } + + public boolean isEmpty() + { + return stored <= 0; + } + + public int size() + { + return stored; + } + + @Override + public Set keySet() + { + return ObjectSets.unmodifiable(nameToId.keySet()); + } + + @Override + public Collection values() + { + return ObjectCollections.unmodifiable(nameToValue.values()); + } + + @Override + public Iterator iterator() + { + return new ReadOnlyIterator(nameToValue.values().iterator()); + } + + protected AssetLocation remap(AssetLocation location) + { + AssetLocation mapped = remaps.get(location); + return mapped == null ? location : mapped; + } + + public final void freeze() + { + frozen = true; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/SimpleRegistryWithDefault.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/SimpleRegistryWithDefault.java new file mode 100644 index 0000000..2c425c4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/SimpleRegistryWithDefault.java @@ -0,0 +1,32 @@ +package speiger.src.coreengine.utils.collections.registries.simple; + +import speiger.src.coreengine.assets.AssetLocation; + +public class SimpleRegistryWithDefault> extends SimpleRegistry +{ + AssetLocation defaultKey; + T defaultValue; + public SimpleRegistryWithDefault(int capacity, AssetLocation defaultValue) + { + super(capacity); + this.defaultKey = defaultValue; + } + + @Override + public T register(T value) + { + T result = super.register(value); + if(defaultKey != null && result.getName().matches(defaultKey)) + { + defaultValue = result; + } + return result; + } + + @Override + public T getValue(AssetLocation name) + { + T value = super.getValue(name); + return value == null ? defaultValue : value; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/counters/averager/Averager.java b/src/main/java/speiger/src/coreengine/utils/counters/averager/Averager.java new file mode 100644 index 0000000..5f81cdf --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/averager/Averager.java @@ -0,0 +1,59 @@ +package speiger.src.coreengine.utils.counters.averager; + +import speiger.src.collections.longs.queues.LongArrayFIFOQueue; +import speiger.src.collections.longs.queues.LongPriorityQueue; + +public class Averager +{ + LongPriorityQueue values; + long totalValue; + int totalCount; + + public Averager() + { + this(200); + } + + public Averager(int totalCount) + { + this.totalCount = totalCount; + values = new LongArrayFIFOQueue(totalCount + 1); + } + + public void addEntry(long time) + { + totalValue += time; + if(values.size() >= totalCount) + { + totalValue -= values.dequeue(); + } + values.enqueue(time); + } + + public void clear() + { + values.clear(); + totalValue = 0L; + } + + public long getAverage() + { + return (values.isEmpty() ? 0 : totalValue / values.size()); + } + + public boolean isEmpty() + { + return values.isEmpty(); + } + + public long getTotal() + { + return totalValue; + } + + @Override + public String toString() + { + return "Stamper[total="+totalValue+", average="+getAverage()+", count="+values.size()+", max="+totalCount+"]"; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/counters/averager/Counter.java b/src/main/java/speiger/src/coreengine/utils/counters/averager/Counter.java new file mode 100644 index 0000000..ed7282c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/averager/Counter.java @@ -0,0 +1,33 @@ +package speiger.src.coreengine.utils.counters.averager; + +public class Counter +{ + int value; + int lastValue; + + public void add(int value) + { + this.value += value; + } + + public void onFinished() + { + lastValue = value; + value = 0; + } + + public int get() + { + return lastValue; + } + + public static Counter[] createCounters(int count) + { + Counter[] counters = new Counter[count]; + for(int i = 0;i 10000000L) + { + for(int i = 0;i < slots.length;i++) + { + slots[i] *= 0.9F; + } + total *= 0.9F; + } + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/counters/averager/TimeAverager.java b/src/main/java/speiger/src/coreengine/utils/counters/averager/TimeAverager.java new file mode 100644 index 0000000..d79bf61 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/averager/TimeAverager.java @@ -0,0 +1,63 @@ +package speiger.src.coreengine.utils.counters.averager; + +import java.text.DecimalFormat; + +public class TimeAverager extends Averager +{ + private static final TimeAverager GLOBAL_TIME_STAMP = new TimeAverager(); + private static final DecimalFormat FORMAT = new DecimalFormat("###,###"); + + long start = 0L; + + public TimeAverager() + { + } + + public TimeAverager(int count) + { + super(count); + } + + public void stop() + { + addEntry(System.nanoTime() - start); + } + + public void start() + { + start = System.nanoTime(); + } + + @Override + public String toString() + { + long time = getAverage(); + return "Time Stamp: "+FORMAT.format(time / 1000000L)+"ms, ("+FORMAT.format(time / 1000L)+"qs, "+FORMAT.format(time)+"ns)"; + } + + public static void startStamp() + { + GLOBAL_TIME_STAMP.start(); + } + + public static void stopStamp() + { + GLOBAL_TIME_STAMP.stop(); + } + + public static long getAverageTime() + { + return GLOBAL_TIME_STAMP.getAverage(); + } + + public static String print() + { + return GLOBAL_TIME_STAMP.toString(); + } + + public static void reset() + { + GLOBAL_TIME_STAMP.values.clear(); + GLOBAL_TIME_STAMP.totalValue = 0L; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/counters/timers/CountdownSync.java b/src/main/java/speiger/src/coreengine/utils/counters/timers/CountdownSync.java new file mode 100644 index 0000000..10a1663 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/timers/CountdownSync.java @@ -0,0 +1,37 @@ +package speiger.src.coreengine.utils.counters.timers; + +import speiger.src.coreengine.utils.counters.averager.RunningAvg; + +public class CountdownSync +{ + boolean initialised = false; + RunningAvg sleepDurations = new RunningAvg(10); + RunningAvg yieldDurations = new RunningAvg(10); + + public void sync(long expectedNanoTime) + { + if(!initialised) + { + initialised = true; + sleepDurations.init(1000000); + yieldDurations.init((int)(-(System.nanoTime() - System.nanoTime()) * 1.333)); + } + long nextFrame = System.nanoTime() + expectedNanoTime; + try + { + for(long t0 = System.nanoTime(), t1;(nextFrame - t0) > sleepDurations.avg();t0 = t1) + { + Thread.sleep(1); + sleepDurations.add((t1 = System.nanoTime()) - t0); + } + sleepDurations.dampenForLowResTicker(); + + for(long t0 = System.nanoTime(), t1;(nextFrame - t0) > yieldDurations.avg();t0 = t1) + { + Thread.yield(); + yieldDurations.add((t1 = System.nanoTime()) - t0); + } + } + catch(InterruptedException e) {} + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/counters/timers/FPSTimer.java b/src/main/java/speiger/src/coreengine/utils/counters/timers/FPSTimer.java new file mode 100644 index 0000000..074c0f5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/timers/FPSTimer.java @@ -0,0 +1,89 @@ +package speiger.src.coreengine.utils.counters.timers; + +import speiger.src.coreengine.utils.counters.averager.TimeAverager; + +public class FPSTimer +{ + long lastTime; + long lastDelta; + long delta_storage; + long time_storage; + float particalTicks; + + TimerTarget fps = new TimerTarget(60); + TimerTarget ups = new TimerTarget(20); + + TimeAverager mainUsage = new TimeAverager(); + + public void init() + { + lastTime = System.nanoTime(); + } + + public long getDelta() + { + mainUsage.start(); + long time = System.nanoTime(); + lastDelta = time; + return getDelta(time); + } + + protected long getDelta(long time) + { + long delta = time - lastTime; + lastTime = time; + delta_storage += delta; + time_storage += delta; + float value = (delta_storage / 1000000000F); + particalTicks = value - ((int)value); + return delta_storage; + } + + public long consumeDelta() + { + long time = System.nanoTime(); + delta_storage -= Math.max(ups.getTargetNS(), time - lastDelta); + float value = (delta_storage / 1000000000F); + particalTicks = value - ((int)value); + lastDelta = time; + return delta_storage; + } + + public boolean update() + { + mainUsage.stop(); + if(time_storage >= 1000000000L) + { + fps.update(); + ups.update(); + time_storage -= 1000000000L; + return true; + } + return false; + } + + public TimerTarget getFPS() + { + return fps; + } + + public TimerTarget getUPS() + { + return ups; + } + + public long getLastTime() + { + return lastTime; + } + + public float getParticalTime() + { + return particalTicks; + } + + public long getUsage() + { + return mainUsage.getAverage(); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/counters/timers/FrameSyncer.java b/src/main/java/speiger/src/coreengine/utils/counters/timers/FrameSyncer.java new file mode 100644 index 0000000..9e9589c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/timers/FrameSyncer.java @@ -0,0 +1,51 @@ +package speiger.src.coreengine.utils.counters.timers; + +import speiger.src.coreengine.utils.counters.averager.RunningAvg; + +/** + * Found this code on the Internet here: http://forum.lwjgl.org/index.php?topic=6582.0 + * + * @author Riven + * @author kappaOne + * + * @author Speiger (Adjusted original code to work with my system) + */ +public class FrameSyncer +{ + long nextFrame = 0; + boolean initialised = false; + RunningAvg sleepDurations = new RunningAvg(10); + RunningAvg yieldDurations = new RunningAvg(10); + + public void sync(int fps) + { + if(fps <= 0) + { + return; + } + if(!initialised) + { + initialised = true; + sleepDurations.init(1000000); + yieldDurations.init((int)(-(System.nanoTime() - System.nanoTime()) * 1.333)); + nextFrame = System.nanoTime(); + } + try + { + for(long t0 = System.nanoTime(), t1;(nextFrame - t0) > sleepDurations.avg();t0 = t1) + { + Thread.sleep(1);// sleep time + sleepDurations.add((t1 = System.nanoTime()) - t0); // update average + } + sleepDurations.dampenForLowResTicker(); + + for(long t0 = System.nanoTime(), t1;(nextFrame - t0) > yieldDurations.avg();t0 = t1) + { + Thread.yield();// yield time + yieldDurations.add((t1 = System.nanoTime()) - t0); // update average + } + } + catch(InterruptedException e) {} + nextFrame = Math.max(nextFrame + 1000000000L / fps, System.nanoTime()); + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/counters/timers/Stopwatch.java b/src/main/java/speiger/src/coreengine/utils/counters/timers/Stopwatch.java new file mode 100644 index 0000000..6add107 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/timers/Stopwatch.java @@ -0,0 +1,59 @@ +package speiger.src.coreengine.utils.counters.timers; + +import java.util.concurrent.TimeUnit; + +public class Stopwatch +{ + long start; + long elapsedTime; + boolean running; + + public Stopwatch start() + { + if(running) + { + throw new IllegalStateException("Stopwatch is started"); + } + start = System.nanoTime(); + running = true; + return this; + } + + public Stopwatch stop() + { + if(!running) + { + throw new IllegalStateException("Stopwatch is stopped"); + } + running = false; + elapsedTime += System.nanoTime() - start; + return this; + } + + public Stopwatch reset() + { + running = false; + elapsedTime = 0L; + return this; + } + + public long getTime() + { + return running ? (System.nanoTime() - start) + elapsedTime : elapsedTime; + } + + public long getTime(TimeUnit unit) + { + return unit.convert(getTime(), TimeUnit.NANOSECONDS); + } + + public boolean isTimeLeft(TimeUnit unit, long time) + { + return getTime(unit) < time; + } + + public boolean isTimeLeft(long time) + { + return getTime() < time; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/counters/timers/Timer.java b/src/main/java/speiger/src/coreengine/utils/counters/timers/Timer.java new file mode 100644 index 0000000..cd620af --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/timers/Timer.java @@ -0,0 +1,58 @@ +package speiger.src.coreengine.utils.counters.timers; + +import speiger.src.coreengine.utils.counters.averager.TimeAverager; + +public class Timer +{ + long lastTime; + long trackerTime; + long deltaLeft; + TimeAverager timer = new TimeAverager(); + TimerTarget target = new TimerTarget(20); + + public void init() + { + lastTime = System.nanoTime(); + trackerTime = lastTime; + } + + public long getDelta() + { + long time = System.nanoTime(); + deltaLeft += (time - lastTime); + lastTime = time; + timer.start(); + return deltaLeft; + } + + public long consumeDelta() + { + long time = System.nanoTime(); + deltaLeft -= Math.max(target.getTargetNS(), time - lastTime); + lastTime = time; + return deltaLeft; + } + + public boolean update() + { + timer.stop(); + long newTime = System.nanoTime(); + if(newTime - trackerTime >= 1000000000) + { + trackerTime = newTime; + target.update(); + return true; + } + return false; + } + + public TimerTarget getTarget() + { + return target; + } + + public long getUsage() + { + return timer.getAverage(); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/counters/timers/TimerTarget.java b/src/main/java/speiger/src/coreengine/utils/counters/timers/TimerTarget.java new file mode 100644 index 0000000..354349b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/timers/TimerTarget.java @@ -0,0 +1,46 @@ +package speiger.src.coreengine.utils.counters.timers; + +import speiger.src.coreengine.utils.counters.averager.Counter; + +public class TimerTarget +{ + int targetTicks; + long targetNS; + Counter counter = new Counter(); + + public TimerTarget(int defaultValue) + { + setTarget(defaultValue); + } + + public void setTarget(int targetTicks) + { + this.targetTicks = targetTicks; + targetNS = 1000000000L / targetTicks; + } + + public int getTargetTicks() + { + return targetTicks; + } + + public long getTargetNS() + { + return targetNS; + } + + public void tick() + { + counter.add(1); + } + + public void update() + { + counter.onFinished(); + } + + public int getTicks() + { + return counter.get(); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/eventbus/Event.java b/src/main/java/speiger/src/coreengine/utils/eventbus/Event.java new file mode 100644 index 0000000..b1ccf81 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/eventbus/Event.java @@ -0,0 +1,29 @@ +package speiger.src.coreengine.utils.eventbus; + +public class Event +{ + boolean canceled = false; + + public void setCanceled(boolean canceled) + { + if(isCancelable()) + { + this.canceled = canceled; + } + } + + public final void cancel() + { + setCanceled(true); + } + + public final boolean isCanceled() + { + return canceled; + } + + public boolean isCancelable() + { + return false; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/eventbus/EventBus.java b/src/main/java/speiger/src/coreengine/utils/eventbus/EventBus.java new file mode 100644 index 0000000..2dcc831 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/eventbus/EventBus.java @@ -0,0 +1,231 @@ +package speiger.src.coreengine.utils.eventbus; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.utils.ObjectLists; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; + +public class EventBus +{ + private static final Lookup LOOKUP = MethodHandles.lookup(); + Object2ObjectMap, Listeners> listeners = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap, Listeners>()); + Map> instances = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap>()); + + public void register(Class event, Consumer listener) + { + register(event, EventPriority.MEDIUM, listener); + } + + public void register(Class event, EventPriority priority, Consumer listener) + { + if(instances.containsKey(listener)) + { + return; + } + getListeners(event).addListener(priority, (Consumer)listener); + instances.put(listener, ObjectLists.singleton(new EventListener(event, (Consumer)listener))); + } + + public void register(Object obj) + { + register(obj, false); + } + + public void register(Object obj, boolean superClasses) + { + if(instances.containsKey(obj)) + { + return; + } + final List list = new ObjectArrayList(); + try + { + register(obj.getClass().getDeclaredAnnotation(SubscribeEvent.class), obj, list); + findMethods(obj.getClass(), superClasses, new Consumer() { + @Override + public void accept(Method t) + { + try + { + t.setAccessible(true); + SubscribeEvent data = t.getAnnotation(SubscribeEvent.class); + if(data == null) + { + return; + } + Consumer listener = new MethodListener(LOOKUP.unreflect(t).bindTo(obj)); + getListeners(data.event()).addListener(data.priority(), listener); + list.add(new EventListener(data.event(), listener)); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + }); + findFields(obj.getClass(), superClasses, new Consumer(){ + @Override + public void accept(Field t) + { + try + { + t.setAccessible(true); + register(t.getAnnotation(SubscribeEvent.class), t.get(obj), list); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + }); + } + catch(Exception e) + { + e.printStackTrace(); + } + if(list.isEmpty()) + { + return; + } + instances.put(obj, list); + } + + private void register(SubscribeEvent data, Object obj, List listeners) + { + if(data == null || !(obj instanceof Consumer)) + { + return; + } + getListeners(data.event()).addListener(data.priority(), (Consumer)obj); + listeners.add(new EventListener(data.event(), (Consumer)obj)); + } + + private void findFields(Class clz, boolean superClasses, Consumer fields) + { + do + { + for(Field field : clz.getDeclaredFields()) + { + fields.accept(field); + } + clz = clz.getSuperclass(); + } + while(clz != Object.class && superClasses); + } + + private void findMethods(Class clz, boolean superClasses, Consumer methods) + { + do + { + for(Method field : clz.getDeclaredMethods()) + { + methods.accept(field); + } + clz = clz.getSuperclass(); + } + while(clz != Object.class && superClasses); + } + + public void unregister(Object obj) + { + List instance = instances.remove(obj); + if(instance == null) + { + return; + } + for(EventListener entry : instance) + { + getListeners(entry.getEvent()).removeListeners(entry.getListener()); + } + } + + public void post(Event event) + { + Consumer[] listeners = getListeners(event.getClass()).getListeners(); + if(listeners.length <= 0) + { + return; + } + int index = 0; + try + { + for(;index event) + { + Listeners entry = listeners.get(event); + if(entry == null) + { + if(event == Event.class) entry = new Listeners(); + else entry = new Listeners(getListeners((Class)event.getSuperclass())); + listeners.put(event, entry); + } + return entry; + } + + + public static class EventListener + { + Class event; + Consumer listener; + + public EventListener(Class event, Consumer listener) + { + this.event = event; + this.listener = listener; + } + + public Class getEvent() + { + return event; + } + + public Consumer getListener() + { + return listener; + } + } + + public static class MethodListener implements Consumer + { + MethodHandle handle; + + public MethodListener(MethodHandle handle) + { + this.handle = handle; + } + + @Override + public void accept(Event t) + { + try + { + handle.invoke(t); + } + catch(Throwable e) + { + e.printStackTrace(); + } + } + + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/eventbus/EventPriority.java b/src/main/java/speiger/src/coreengine/utils/eventbus/EventPriority.java new file mode 100644 index 0000000..5000e21 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/eventbus/EventPriority.java @@ -0,0 +1,27 @@ +package speiger.src.coreengine.utils.eventbus; + +public enum EventPriority +{ + HIGH(0), + MEDIUM(1), + LOW(2); + + int priority; + + static final EventPriority[] PRIORITIES = new EventPriority[]{HIGH, MEDIUM, LOW}; + + private EventPriority(int id) + { + priority = id; + } + + public int getPriority() + { + return priority; + } + + public static EventPriority[] getPriorities() + { + return PRIORITIES; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/eventbus/Listeners.java b/src/main/java/speiger/src/coreengine/utils/eventbus/Listeners.java new file mode 100644 index 0000000..defda3e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/eventbus/Listeners.java @@ -0,0 +1,103 @@ +package speiger.src.coreengine.utils.eventbus; + +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet; + +public class Listeners +{ + Set>[] unsortedListeners = new Set[EventPriority.values().length]; + Consumer[] listeners = new Consumer[0]; + boolean rebuild = true; + Listeners parent; + List childs = null; + + public Listeners() + { + for(int i = 0;i>(); + } + } + + public Listeners(Listeners parent) + { + this(); + this.parent = parent; + this.parent.addChild(this); + } + + public void addChild(Listeners listener) + { + if(childs == null) + { + childs = new ObjectArrayList(); + } + childs.add(listener); + } + + public void addListener(EventPriority priority, Consumer listener) + { + if(unsortedListeners[priority.getPriority()].add(listener)) + { + markDirty(); + } + } + + public void removeListeners(Consumer listener) + { + boolean found = false; + for(int i = 0,m=unsortedListeners.length;i> events) + { + events.addAll(unsortedListeners[entry.getPriority()]); + if(parent != null) + { + parent.getListeners(entry, events); + } + } + + public Consumer[] getListeners() + { + if(rebuild) rebuildListeners(); + return listeners; + } + + private void markDirty() + { + rebuild = true; + if(childs != null) + { + for(int i = 0,m=childs.size();i> result = new ObjectArrayList>(); + for(EventPriority entry : EventPriority.getPriorities()) + { + getListeners(entry, result); + } + listeners = result.toArray(new Consumer[result.size()]); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/eventbus/SubscribeEvent.java b/src/main/java/speiger/src/coreengine/utils/eventbus/SubscribeEvent.java new file mode 100644 index 0000000..6c2d58f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/eventbus/SubscribeEvent.java @@ -0,0 +1,18 @@ +package speiger.src.coreengine.utils.eventbus; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target({TYPE, FIELD, METHOD}) +public @interface SubscribeEvent +{ + public EventPriority priority() default EventPriority.MEDIUM; + + public Class event(); +} diff --git a/src/main/java/speiger/src/coreengine/utils/functions/BiIntConsumer.java b/src/main/java/speiger/src/coreengine/utils/functions/BiIntConsumer.java new file mode 100644 index 0000000..41687ef --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/functions/BiIntConsumer.java @@ -0,0 +1,7 @@ +package speiger.src.coreengine.utils.functions; + +@FunctionalInterface +public interface BiIntConsumer +{ + public void accpet(int k, int v); +} diff --git a/src/main/java/speiger/src/coreengine/utils/functions/ConsumerConverter.java b/src/main/java/speiger/src/coreengine/utils/functions/ConsumerConverter.java new file mode 100644 index 0000000..44036bd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/functions/ConsumerConverter.java @@ -0,0 +1,23 @@ +package speiger.src.coreengine.utils.functions; + +import java.util.function.Consumer; +import java.util.function.ObjIntConsumer; + +public class ConsumerConverter implements Consumer +{ + int value; + ObjIntConsumer receiver; + + public ConsumerConverter(int value, ObjIntConsumer listener) + { + this.value = value; + receiver = listener; + } + + @Override + public void accept(T t) + { + receiver.accept(t, value); + } + +} diff --git a/src/main/java/speiger/src/coreengine/utils/functions/FloatSupplier.java b/src/main/java/speiger/src/coreengine/utils/functions/FloatSupplier.java new file mode 100644 index 0000000..4d404f8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/functions/FloatSupplier.java @@ -0,0 +1,7 @@ +package speiger.src.coreengine.utils.functions; + +@FunctionalInterface +public interface FloatSupplier +{ + public float getAsFloat(); +} diff --git a/src/main/java/speiger/src/coreengine/utils/functions/Functions.java b/src/main/java/speiger/src/coreengine/utils/functions/Functions.java new file mode 100644 index 0000000..9b57eb7 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/functions/Functions.java @@ -0,0 +1,32 @@ +package speiger.src.coreengine.utils.functions; + +import java.util.function.Consumer; +import java.util.function.Predicate; + +public class Functions +{ + static final Predicate ALWAYS_TRUE = (T) -> true; + static final Consumer EMPTY_CONSUMER = (T) -> {}; + public static final Predicate NUMBERS_ONLY = T -> { + if(T.isEmpty() || T.equals("#") || T.equals("0x")) + { + return true; + } + try + { + Integer.decode(T); + return true; + } + catch(Exception e){return false;} + }; + + public static Predicate getAlwaysTrue() + { + return (Predicate)ALWAYS_TRUE; + } + + public static Consumer getVoidConsumer() + { + return (Consumer)EMPTY_CONSUMER; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/functions/ObjFloatFunction.java b/src/main/java/speiger/src/coreengine/utils/functions/ObjFloatFunction.java new file mode 100644 index 0000000..0bb6a3d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/functions/ObjFloatFunction.java @@ -0,0 +1,7 @@ +package speiger.src.coreengine.utils.functions; + +@FunctionalInterface +public interface ObjFloatFunction +{ + public float apply(T v); +} diff --git a/src/main/java/speiger/src/coreengine/utils/functions/TriConsumer.java b/src/main/java/speiger/src/coreengine/utils/functions/TriConsumer.java new file mode 100644 index 0000000..1cd3118 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/functions/TriConsumer.java @@ -0,0 +1,6 @@ +package speiger.src.coreengine.utils.functions; + +public interface TriConsumer +{ + public void accept(A a, B b, C c); +} diff --git a/src/main/java/speiger/src/coreengine/utils/helpers/FileUtils.java b/src/main/java/speiger/src/coreengine/utils/helpers/FileUtils.java new file mode 100644 index 0000000..edba7b0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/helpers/FileUtils.java @@ -0,0 +1,48 @@ +package speiger.src.coreengine.utils.helpers; + +import java.io.File; + +public class FileUtils +{ + public static void ensureFolder(File file) + { + if(file.exists()) + { + if(!file.isDirectory()) + { + file.delete(); + file.mkdirs(); + } + return; + } + file.mkdirs(); + } + + public static String convertBytes(long bytes) + { + return (bytes >> 30) > 5 ? (bytes >> 30)+"GB" : ((bytes >> 20) > 5 ? (bytes >> 20)+"MB" : ((bytes >> 10) >= 5 ? (bytes >> 10)+"KB" : bytes+"B")); + } + + public static File getBase() + { + try + { + File file = new File(FileUtils.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + if(file.getName().endsWith(".jar")) return file; + } + catch(Exception e) + { + } + return new File("."); + } + + public static String buildVersion(String fileName, String prefix, boolean forced_DEV, String devString) + { + if(forced_DEV) + { + return devString; + } + int index = fileName.indexOf(prefix); + return index == -1 || fileName.length() - 4 == prefix.length() ? "Unknown Version" : fileName.substring(index + prefix.length(), fileName.length() - 4); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/helpers/InternalThreadPools.java b/src/main/java/speiger/src/coreengine/utils/helpers/InternalThreadPools.java new file mode 100644 index 0000000..f5b8504 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/helpers/InternalThreadPools.java @@ -0,0 +1,11 @@ +package speiger.src.coreengine.utils.helpers; + +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.matrix.Matrix4f; +import speiger.src.coreengine.utils.collections.pools.ThreadPool; + +public class InternalThreadPools +{ + public static final ThreadPool MATRIX = new ThreadPool(100, Matrix4f::new); + public static final ThreadPool VEC2F = new ThreadPool(100, Vec2f::newMutable); +} diff --git a/src/main/java/speiger/src/coreengine/utils/helpers/JsonUtil.java b/src/main/java/speiger/src/coreengine/utils/helpers/JsonUtil.java new file mode 100644 index 0000000..39c33e1 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/helpers/JsonUtil.java @@ -0,0 +1,125 @@ +package speiger.src.coreengine.utils.helpers; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +public class JsonUtil +{ + public static JsonObject loadFile(Path path) + { + try(BufferedReader reader = Files.newBufferedReader(path)) + { + return JsonParser.parseReader(reader).getAsJsonObject(); + } + catch(Exception e) + { + e.printStackTrace(); + return new JsonObject(); + } + } + + public static JsonObject loadFile(File file) + { + try(Reader reader = new BufferedReader(new FileReader(file))) + { + return JsonParser.parseReader(reader).getAsJsonObject(); + } + catch(Exception e) + { + e.printStackTrace(); + return new JsonObject(); + } + } + + public static JsonObject loadFile(InputStream stream) + { + try(Reader reader = new BufferedReader(new InputStreamReader(stream))) + { + return JsonParser.parseReader(reader).getAsJsonObject(); + } + catch(Exception e) + { + e.printStackTrace(); + return new JsonObject(); + } + } + + public static boolean getOrDefault(JsonObject obj, String name, boolean defaultValue) + { + JsonElement el = obj.get(name); + return el == null ? defaultValue : el.getAsBoolean(); + } + + public static byte getOrDefault(JsonObject obj, String name, byte defaultValue) + { + JsonElement el = obj.get(name); + return el == null ? defaultValue : el.getAsByte(); + } + + public static short getOrDefault(JsonObject obj, String name, short defaultValue) + { + JsonElement el = obj.get(name); + return el == null ? defaultValue : el.getAsShort(); + } + + public static int getOrDefault(JsonObject obj, String name, int defaultValue) + { + JsonElement el = obj.get(name); + return el == null ? defaultValue : el.getAsInt(); + } + + public static long getOrDefault(JsonObject obj, String name, long defaultValue) + { + JsonElement el = obj.get(name); + return el == null ? defaultValue : el.getAsLong(); + } + + public static float getOrDefault(JsonObject obj, String name, float defaultValue) + { + JsonElement el = obj.get(name); + return el == null ? defaultValue : el.getAsFloat(); + } + + public static double getOrDefault(JsonObject obj, String name, double defaultValue) + { + JsonElement el = obj.get(name); + return el == null ? defaultValue : el.getAsDouble(); + } + + public static String getOrDefault(JsonObject obj, String name, String defaultValue) + { + JsonElement el = obj.get(name); + return el == null ? defaultValue : el.getAsString(); + } + + public static JsonArray toArray(byte[] values) + { + JsonArray array = new JsonArray(); + for(int i = 0,m=values.length;i", ""); + } + + public static String makeColor(Integer color) + { + return "§"; + } + + public static ColorObject getColorFromText(String s) + { + s = s == null ? "" : s; + return ColorObject.rgb((s.hashCode() & 11184810) + 4473924); + } + + public static String searchUntil(String s, int startIndex, char endChar, String invalidChars) + { + boolean found = false; + invalidChars = invalidChars.toLowerCase(Locale.ROOT); + StringBuilder builder = new StringBuilder(); + while(startIndex < s.length()) + { + char character = s.charAt(startIndex); + if(character == endChar) + { + found = true; + break; + } + if(invalidChars.contains(Character.toString(character).toLowerCase(Locale.ROOT))) + { + break; + } + builder.append(character); + startIndex++; + } + if(!found) + { + return ""; + } + return builder.toString(); + } + + public static boolean findFlag(String value, String search, boolean original) + { + if(value.startsWith(search)) + { + int equals = value.indexOf("="); + return equals == -1 ? !original : (Boolean.parseBoolean(value.substring(equals + 1))); + } + return original; + } + + public static ColorObject findColor(String value, String search, ColorObject current, ColorObject original) + { + if(value.startsWith(search)) + { + try + { + int equals = value.indexOf("="); + return equals == -1 ? original : ColorObject.rgb(Integer.decode(value.substring(equals + 1)).intValue()); + } + catch(Exception e) + { + GameLog.error("Color Parsing failed", e, LogLevel.WARNING); + } + } + return current; + } + + public static float findFloat(String value, String search, float current, float toggleValue) + { + if(value.startsWith(search)) + { + int equals = value.indexOf("="); + return equals == -1 ? toggleValue : (Float.parseFloat(value.substring(equals + 1))); + } + return current; + } + + public static String convertTime(long time, String key, DecimalFormat format) + { + return key + (time >= 1000000 ? format.format(time /= 1000000)+"ms" : (time >= 1000 ? format.format(time /= 1000)+"qs" : format.format(time)+"ns")); + } + + public static String repeate(String s, int amount) + { + StringBuilder builder = new StringBuilder(); + for(int i = 0;i BUFFER_PROVIDER = ThreadLocal.withInitial(() -> new StringBuilder(320)); + public static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss"); + public static boolean STACK_TRACE = true; + public static boolean FULL_STACK_TRACE = false; + + public static void trace(String name) + { + StringBuilder builder = BUFFER_PROVIDER.get(); + new Exception(name).printStackTrace(GAME_LOG_STREAM); + String result = builder.toString(); + builder.setLength(0); + log(LogLevel.INFO, result); + } + + public static void test(Object...values) + { + StringBuilder builder = new StringBuilder(); + builder.append("Test: "); + for(Object obj : values) + { + builder.append(obj).append(" : "); + } + log(LogLevel.INFO, builder.substring(0, builder.length() - 3)); + } + + public static void info(String name, Object...values) + { + StringBuilder builder = new StringBuilder(); + builder.append(name).append(": "); + for(Object obj : values) + { + builder.append(obj).append(" : "); + } + log(LogLevel.INFO, builder.substring(0, builder.length() - 3)); + } + + public static void info(Object obj) + { + log(LogLevel.INFO, obj == null ? "Null" : obj.toString()); + } + + public static void info(String text) + { + log(LogLevel.INFO, text); + } + + public static void warn(Object obj) + { + log(LogLevel.WARNING, obj == null ? "Null" : obj.toString()); + } + + public static void warn(String text) + { + log(LogLevel.WARNING, text); + } + + public static void error(Object obj) + { + log(LogLevel.ERROR, obj == null ? "Null" : obj.toString()); + } + + public static void error(String text) + { + log(LogLevel.ERROR, text); + } + + public static void error(String message, Throwable error, LogLevel level) + { + StringBuilder buffer = BUFFER_PROVIDER.get(); + buffer.append("[").append(ZonedDateTime.now().format(TIME_FORMAT)).append("]").append(level.getLevelName()); + if(STACK_TRACE) + { + buffer.append("[ ").append(getTrace(4)).append("]"); + } + buffer.append(" ").append(message); + System.out.println(buffer.toString()); + buffer.setLength(0); + error.printStackTrace(GAME_LOG_STREAM); + buffer.setLength(buffer.length() - 2); + System.err.println(buffer.toString()); + buffer.setLength(0); + } + + public static void log(LogLevel level, String info) + { + StringBuilder buffer = BUFFER_PROVIDER.get(); + buffer.append("[").append(ZonedDateTime.now().format(TIME_FORMAT)).append("]").append(getThreadName()).append(level.getLevelName()); + if(STACK_TRACE) + { + buffer.append("[ ").append(getTrace(4)).append("]"); + } + buffer.append(" ").append(info); + (level == LogLevel.ERROR ? System.err : System.out).println(buffer.toString()); + buffer.setLength(0); + if(FULL_STACK_TRACE) + { + printStackTrace(buffer); + System.out.flush(); + System.err.flush(); + } + } + + private static String getThreadName() + { + return "["+Thread.currentThread().getName()+"]"; + } + + private static void printStackTrace(StringBuilder buffer) + { + new Exception("Stack trace").printStackTrace(GAME_LOG_STREAM); + System.err.println(buffer.toString()); + buffer.setLength(0); + } + + private static String getTrace(int index) + { + return createStackTrace(Thread.currentThread().getStackTrace()[index]); + } + + private static String createStackTrace(StackTraceElement trace) + { + if(trace.isNativeMethod()) + { + return "(Native Methode)"; + } + else if(trace.getFileName() != null) + { + if(trace.getLineNumber() >= 0) + { + return "(" + trace.getFileName() + ":" + trace.getLineNumber() + ")"; + } + return "("+trace.getFileName()+":0)"; + } + return "(Unknown Source)"; + } + + public static enum LogLevel + { + INFO("[Info]"), + WARNING("[Warning]"), + ERROR("[Error]"); + + String levelName; + + private LogLevel(String level) + { + levelName = level; + } + + public String getLevelName() + { + return levelName; + } + } + + public static class GameLogWriter extends PrintStream + { + public GameLogWriter(PrintStream parent) + { + super(parent); + } + + @Override + public void println(String x) + { + GameLog.BUFFER_PROVIDER.get().append(x).append(LINE_SEPERATOR); + } + + @Override + public void println(Object x) + { + GameLog.BUFFER_PROVIDER.get().append(String.valueOf(x)).append(LINE_SEPERATOR); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/GifWriter.java b/src/main/java/speiger/src/coreengine/utils/io/GifWriter.java new file mode 100644 index 0000000..ce3811c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/GifWriter.java @@ -0,0 +1,126 @@ +package speiger.src.coreengine.utils.io; + +import java.awt.image.RenderedImage; +import java.io.File; +import java.util.function.BiConsumer; + +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.metadata.IIOMetadataNode; +import javax.imageio.stream.FileImageOutputStream; +import javax.imageio.stream.ImageOutputStream; + +import org.w3c.dom.NodeList; + +public class GifWriter +{ + ImageOutputStream stream; + ImageWriter writer; + BiConsumer inserter; + + public static GifWriter create(File file, int image, int delay) + { + try + { + return new GifWriter(file, image, delay); + } + catch(Exception e) + { + e.printStackTrace(); + return null; + } + } + + public static GifWriter create(ImageOutputStream stream, int image, int delay) + { + try + { + return new GifWriter(stream, image, delay); + } + catch(Exception e) + { + e.printStackTrace(); + return null; + } + } + + protected GifWriter(File file, int imageType, int delay) throws Exception + { + this(new FileImageOutputStream(file), imageType, delay); + } + + protected GifWriter(ImageOutputStream out, int imageType, int delay) throws Exception + { + stream = out; + writer = ImageIO.getImageWritersBySuffix("gif").next(); + final ImageWriteParam parameter = writer.getDefaultWriteParam(); + final IIOMetadata metadata = writer.getDefaultImageMetadata(ImageTypeSpecifier.createFromBufferedImageType(imageType), parameter); + IIOMetadataNode root = (IIOMetadataNode)metadata.getAsTree(metadata.getNativeMetadataFormatName()); + IIOMetadataNode graphics = getOrCreateNode(root, "GraphicControlExtension"); + graphics.setAttribute("delayTime", ""+Float.valueOf(((delay / 1000F) * 15F)).intValue()); + graphics.setAttribute("transparentColorFlag", "false"); + graphics.setAttribute("transparentColorIndex", "0"); + graphics.setAttribute("disposalMethod", "none"); + graphics.setAttribute("userInputFlag", "false"); + + IIOMetadataNode application = new IIOMetadataNode("ApplicationExtension"); + application.setAttribute("applicationID", "NETSCAPE"); + application.setAttribute("authenticationCode", "2.0"); + application.setUserObject(new byte[] {1, 0, 0}); + getOrCreateNode(root, "ApplicationExtensions").appendChild(application); + metadata.setFromTree(metadata.getNativeMetadataFormatName(), root); + writer.setOutput(out); + writer.prepareWriteSequence(null); + inserter = new BiConsumer(){ + ImageWriteParam param = parameter; + IIOMetadata meta = metadata; + @Override + public void accept(ImageWriter writer, RenderedImage image) + { + try + { + writer.writeToSequence(new IIOImage(image, null, meta), param); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + }; + } + + public void insertPicture(RenderedImage image) + { + inserter.accept(writer, image); + } + + public void close() + { + try + { + writer.endWriteSequence(); + stream.close(); + inserter = null; + } + catch(Exception e) + { + e.printStackTrace(); + } + } + + private IIOMetadataNode getOrCreateNode(IIOMetadataNode parent, String name) + { + NodeList list = parent.getElementsByTagName(name); + if(list.getLength() > 0) + { + return (IIOMetadataNode)list.item(0); + } + IIOMetadataNode node = new IIOMetadataNode(name); + parent.appendChild(node); + return node; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/DataTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/DataTag.java new file mode 100644 index 0000000..2fe0665 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/DataTag.java @@ -0,0 +1,91 @@ +package speiger.src.coreengine.utils.io.dataTag; + +import java.io.IOException; +import java.util.function.Consumer; + +import speiger.src.coreengine.utils.io.dataTag.array.ByteArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.DoubleArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.FloatArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.IntArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.LongArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.MediumArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.ShortArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.StringArrayTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.dataTag.simple.ByteTag; +import speiger.src.coreengine.utils.io.dataTag.simple.DoubleTag; +import speiger.src.coreengine.utils.io.dataTag.simple.FloatTag; +import speiger.src.coreengine.utils.io.dataTag.simple.IntTag; +import speiger.src.coreengine.utils.io.dataTag.simple.LongTag; +import speiger.src.coreengine.utils.io.dataTag.simple.MediumTag; +import speiger.src.coreengine.utils.io.dataTag.simple.ShortTag; +import speiger.src.coreengine.utils.io.dataTag.simple.StringTag; +import speiger.src.coreengine.utils.io.dataTag.special.ListTag; +import speiger.src.coreengine.utils.io.dataTag.special.MapTag; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public interface DataTag +{ + String KEY_HIGHLIGHT = "§"; + String STRING_HIGHLIGHT = "§"; + String NUMBER_HIGHLIGHT = "§"; + String TYPE_HIGHLIGHT = "§"; + String RESET_HIGHLIGHT = "§"; + + public int getId(); + public String getTypeName(); + + public long countBytes(long input); + public default void addKeys(Consumer keys){}; + + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException; + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException; + + public DataTag copy(); + + public default T cast(){return (T)this;} + public default T cast(Class clz){return (T)this;} + + public default String prettyPrint(){return prettyPrint(" ", 0);} + public String prettyPrint(String indent, int depth); + + + public static DataTag create(byte id) + { + switch(id) + { + case 1: return new ByteTag(); + case 2: return new ShortTag(); + case 3: return new MediumTag(); + case 4: return new IntTag(); + case 5: return new LongTag(); + case 6: return new FloatTag(); + case 7: return new DoubleTag(); + case 8: return new StringTag(); + case 9: return new ByteArrayTag(); + case 10: return new ShortArrayTag(); + case 11: return new MediumArrayTag(); + case 12: return new IntArrayTag(); + case 13: return new LongArrayTag(); + case 14: return new FloatArrayTag(); + case 15: return new DoubleArrayTag(); + case 16: return new StringArrayTag(); + case 17: return new MapTag(); + case 18: return new ListTag(); + } + return null; + } + + static String repeate(String s, int amount) + { + StringBuilder builder = new StringBuilder(); + for(int i = 0;i {}; + + public static DataTagLimiter limit(int byteLimit) + { + return new Instance(byteLimit); + } + + public void countBytes(int amount) throws IOException; + + public static class Instance implements DataTagLimiter + { + final int max; + int read = 0; + + public Instance(int max) + { + this.max = max; + } + + @Override + public void countBytes(int amount) throws IOException + { + read += amount; + if(read > amount) + { + throw new IllegalStateException("To many Bytes Read"); + } + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/compression/TagCompressor.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/compression/TagCompressor.java new file mode 100644 index 0000000..50a4c29 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/compression/TagCompressor.java @@ -0,0 +1,122 @@ +package speiger.src.coreengine.utils.io.dataTag.compression; + +import java.io.Flushable; +import java.io.IOException; +import java.io.OutputStream; +import java.util.function.Consumer; +import java.util.zip.GZIPOutputStream; + +import speiger.src.collections.objects.maps.impl.hash.Object2IntLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2IntMap; +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class TagCompressor implements Flushable, AutoCloseable +{ + public static final int VERSION = 1; + + protected ExtendedDataOutput output; + + public TagCompressor(ExtendedDataOutput output) + { + this.output = output; + } + + public static TagCompressor create(ExtendedDataOutput output, boolean lexed) + { + return lexed ? new LexedTagCompressor(output) : new TagCompressor(output); + } + + public static TagCompressor create(OutputStream output, boolean lexed) throws IOException + { + return create(ExtendedDataOutput.createBuffered(new GZIPOutputStream(output)), lexed); + } + + public static TagCompressor createAndSign(ExtendedDataOutput output, boolean lexed) throws IOException + { + TagCompressor compressor = create(output, lexed); + compressor.writeIdentifier(); + return compressor; + } + + public static TagCompressor createDefaultSign(OutputStream output, boolean lexed) throws IOException + { + TagCompressor compressor = create(ExtendedDataOutput.createBuffered(new GZIPOutputStream(output)), lexed); + compressor.writeIdentifier(); + return compressor; + } + + public void writeIdentifier() throws IOException + { + output.writeInt(VERSION << 1 | 0); + } + + public void write(DataTag tag) throws IOException + { + output.writeByte(tag.getId()); + tag.write(output, this); + } + + public void writeKey(String name) throws IOException + { + output.writeString(name); + } + + @Override + public void flush() throws IOException + { + output.flush(); + } + + @Override + public void close() throws Exception + { + if(output != null) + { + output.flush(); + output.close(); + output = null; + } + } + + public static class LexedTagCompressor extends TagCompressor implements Consumer + { + Object2IntMap indexes = new Object2IntLinkedOpenHashMap(); + + public LexedTagCompressor(ExtendedDataOutput output) + { + super(output); + } + + @Override + public void writeIdentifier() throws IOException + { + output.writeInt(VERSION << 1 | 1); + } + + @Override + public void write(DataTag tag) throws IOException + { + indexes.clear(); + tag.addKeys(this); + output.writeVarInt(indexes.size()); + for(String s : indexes.keySet()) + { + output.writeString(s); + } + super.write(tag); + } + + @Override + public void writeKey(String name) throws IOException + { + output.writeVarInt(indexes.getInt(name)); + } + + @Override + public void accept(String t) + { + indexes.putIfAbsent(t, indexes.size()); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/compression/TagDecompressor.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/compression/TagDecompressor.java new file mode 100644 index 0000000..725ba72 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/compression/TagDecompressor.java @@ -0,0 +1,107 @@ +package speiger.src.coreengine.utils.io.dataTag.compression; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.zip.GZIPInputStream; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; + +public class TagDecompressor implements AutoCloseable +{ + public static final int VERSION = 1; + public static boolean LOG = false; + + protected ExtendedDataInput input; + protected DataTagLimiter limit; + + public TagDecompressor(ExtendedDataInput input, DataTagLimiter limit) + { + this.input = input; + this.limit = limit; + } + + public static TagDecompressor create(ExtendedDataInput input, DataTagLimiter limit, boolean lexed) + { + return lexed ? new LexedTagDeCompressor(input, limit) : new TagDecompressor(input, limit); + } + + public static TagDecompressor createDefault(InputStream input, DataTagLimiter limit, boolean lexed) throws IOException + { + return create(ExtendedDataInput.createBuffered(new GZIPInputStream(input)), limit, lexed); + } + + public static TagDecompressor createFromSign(ExtendedDataInput input, DataTagLimiter limit) throws IOException + { + int sign = input.readInt(); + int version = sign >> 1; + if(version > VERSION) throw new IllegalStateException("Version ["+version+"] is to new"); + else if(version < VERSION && LOG) System.out.println("Loading Outdated Version ["+version+"] this might cause problems"); + return (sign & 1) != 0 ? new LexedTagDeCompressor(input, limit) : new TagDecompressor(input, limit); + } + + public static TagDecompressor createDefaultSign(InputStream input, DataTagLimiter limit) throws IOException + { + return createFromSign(ExtendedDataInput.createBuffered(new GZIPInputStream(input)), limit); + } + + @Override + public void close() throws Exception + { + input.close(); + } + + public DataTag read() throws IOException + { + return readSubTag(input.readByte(), "Init", 0); + } + + public String readKey() throws IOException + { + return input.readString(); + } + + public DataTag readSubTag(byte type, String key, int depth) throws IOException + { + try + { + DataTag tag = DataTag.create(type); + tag.read(input, this, depth, limit); + return tag; + } + catch(IOException e) + { + throw new IllegalStateException("Crash during Reading a Tag. [Key="+key+", Type="+type+"]", e); + } + } + + public static class LexedTagDeCompressor extends TagDecompressor + { + List keys = new ObjectArrayList<>(); + + public LexedTagDeCompressor(ExtendedDataInput input, DataTagLimiter limit) + { + super(input, limit); + } + + @Override + public DataTag read() throws IOException + { + keys.clear(); + int size = input.readVarInt(); + for(int i = 0;i, IOEntry> entries; + SerializationContext context = new SerializationContext(); + + DataTagIO(Map, IOEntry> entries) + { + this.entries = entries; + } + + public T fromData(DataTag tag, Class type) throws IOException + { + IOEntry entry = entries.get(type); + return entry == null || entry.deserializer == null ? null : (T)entry.deserializer.deserialize(tag, context); + } + + public List fromData(DataTag tag, Class type, List list) throws IOException + { + IOEntry entry = entries.get(type); + if(entry != null && entry.deserializer != null) + { + if(tag instanceof ListTag) + { + for(DataTag subTag : tag.cast(ListTag.class)) + { + list.add((T)entry.deserializer.deserialize(subTag, context)); + } + } + else + { + list.add((T)entry.deserializer.deserialize(tag, context)); + } + } + return list; + } + + public DataTag toData(Object obj) + { + return toData(obj, obj.getClass()); + } + + public DataTag toData(Object obj, Class type) + { + IOEntry entry = (IOEntry)entries.get(type); + return entry == null || entry.serializer == null ? null : entry.serializer.serialize(obj, context); + } + + public DataTag toData(List list, Class type) + { + IOEntry entry = (IOEntry)entries.get(type); + if(entry == null || entry.serializer == null) + { + return null; + } + ListTag tags = new ListTag(); + for(int i = 0;i + { + IDataSerializer serializer; + IDataDeserializer deserializer; + + public IOEntry(IDataSerializer serializer, IDataDeserializer deserializer) + { + this.serializer = serializer; + this.deserializer = deserializer; + } + + } + + class SerializationContext implements IDataDeserializationContext, IDataSerializationContext + { + @Override + public DataTag serialize(Object obj) + { + return toData(obj); + } + + @Override + public DataTag serialize(Object obj, Class clz) + { + return toData(obj, clz); + } + + @Override + public T deserialize(DataTag tag, Class type) throws IOException + { + return fromData(tag, type); + } + + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/DataTagIOBuilder.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/DataTagIOBuilder.java new file mode 100644 index 0000000..996b2b6 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/DataTagIOBuilder.java @@ -0,0 +1,37 @@ +package speiger.src.coreengine.utils.io.dataTag.parsing; + +import java.util.Map; + +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap; +import speiger.src.coreengine.utils.io.dataTag.parsing.DataTagIO.IOEntry; +import speiger.src.coreengine.utils.io.dataTag.parsing.deserialize.IDataDeserializer; +import speiger.src.coreengine.utils.io.dataTag.parsing.serialize.IDataSerializer; + +public class DataTagIOBuilder +{ + Map, IOEntry> map = new Object2ObjectOpenHashMap<>(); + + public DataTagIO build() + { + return new DataTagIO(map); + } + + public static DataTagIOBuilder copyOf(DataTagIO io) + { + DataTagIOBuilder builder = new DataTagIOBuilder(); + builder.map.putAll(io.entries); + return builder; + } + + public DataTagIOBuilder add(Class type, Object obj) + { + IDataSerializer serializer = obj instanceof IDataSerializer ? (IDataSerializer)obj : null; + IDataDeserializer deserializer = obj instanceof IDataDeserializer ? (IDataDeserializer)obj : null; + if(serializer == null || deserializer == null) + { + return this; + } + map.put(type, new IOEntry<>(serializer, deserializer)); + return this; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/DataTagParser.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/DataTagParser.java new file mode 100644 index 0000000..3d67d35 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/DataTagParser.java @@ -0,0 +1,330 @@ +package speiger.src.coreengine.utils.io.dataTag.parsing; + +import java.io.IOException; +import java.util.function.Consumer; +import java.util.regex.Pattern; + +import speiger.src.collections.bytes.lists.ByteArrayList; +import speiger.src.collections.bytes.lists.ByteList; +import speiger.src.collections.doubles.lists.DoubleArrayList; +import speiger.src.collections.doubles.lists.DoubleList; +import speiger.src.collections.floats.lists.FloatArrayList; +import speiger.src.collections.floats.lists.FloatList; +import speiger.src.collections.ints.lists.IntArrayList; +import speiger.src.collections.ints.lists.IntList; +import speiger.src.collections.longs.lists.LongArrayList; +import speiger.src.collections.longs.lists.LongList; +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.lists.ObjectList; +import speiger.src.collections.shorts.lists.ShortArrayList; +import speiger.src.collections.shorts.lists.ShortList; +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.array.ByteArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.DoubleArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.FloatArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.IntArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.LongArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.MediumArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.ShortArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.StringArrayTag; +import speiger.src.coreengine.utils.io.dataTag.simple.ByteTag; +import speiger.src.coreengine.utils.io.dataTag.simple.DoubleTag; +import speiger.src.coreengine.utils.io.dataTag.simple.FloatTag; +import speiger.src.coreengine.utils.io.dataTag.simple.IntTag; +import speiger.src.coreengine.utils.io.dataTag.simple.LongTag; +import speiger.src.coreengine.utils.io.dataTag.simple.MediumTag; +import speiger.src.coreengine.utils.io.dataTag.simple.PrimitiveTag; +import speiger.src.coreengine.utils.io.dataTag.simple.ShortTag; +import speiger.src.coreengine.utils.io.dataTag.simple.StringTag; +import speiger.src.coreengine.utils.io.dataTag.special.ListTag; +import speiger.src.coreengine.utils.io.dataTag.special.MapTag; +import speiger.src.coreengine.utils.io.streams.StringReader; + +public class DataTagParser +{ + private static final Pattern BYTE_TYPE = Pattern.compile("[-+]?(?:[0-9]+)b", 2); + private static final Pattern SHORT_TYPE = Pattern.compile("[-+]?(?:[0-9]+)s", 2); + private static final Pattern MEDIUM_TYPE = Pattern.compile("[-+]?(?:[0-9]+)m", 2); + private static final Pattern INT_TYPE = Pattern.compile("[-+]?(?:[0-9]+)i", 2); + private static final Pattern INT_TYPE_DEFAULT = Pattern.compile("[-+]?(?:[0-9]+)"); + private static final Pattern LONG_TYPE = Pattern.compile("[-+]?(?:[0-9]+)l", 2); + private static final Pattern FLOAT_TYPE = Pattern.compile("[-+]?(?:[0-9]+[.]?|[0-9]*[.][0-9]+)(?:e[-+]?[0-9]+)?f", 2); + private static final Pattern DOUBLE_TYPE = Pattern.compile("[-+]?(?:[0-9]+[.]?|[0-9]*[.][0-9]+)(?:e[-+]?[0-9]+)?d", 2); + private static final Pattern DOUBLE_TYPE_DEFAULT = Pattern.compile("[-+]?(?:[0-9]+[.]|[0-9]*[.][0-9]+)(?:e[-+]?[0-9]+)?", 2); + StringReader reader; + + public DataTagParser(String s) + { + reader = new StringReader(s.replaceAll("\\§\\<(.*?)\\>", "")); + } + + public DataTag readTagSave() + { + try + { + return readTag(); + } + catch(Exception e) + { + return null; + } + } + + public DataTag readTag() throws IOException + { + reader.skipEmpty(); + if(!reader.canRead()) + { + throw new IOException("Nothing to parse found"); + } + else + { + char c0 = reader.peek(); + if(c0 == '{') + { + return readMap(); + } + else + { + return c0 == '[' ? readList() : readTagType(); + } + } + } + + public MapTag readMap() throws IOException + { + requirePresent('{'); + reader.skipEmpty(); + MapTag map = new MapTag(); + while(reader.canRead() && reader.peek() != '}') + { + reader.skipEmpty(); + int last = reader.getIndex(); + String s = reader.readString(); + if(s.isEmpty()) + { + throw new IOException("Expected Key at " + last); + } + requirePresent(':'); + map.setTag(s, readTag()); + reader.skipEmpty(); + if(!reader.isPresent(',')) + { + break; + } + reader.skipEmpty(); + } + requirePresent('}'); + return map; + } + + protected DataTag readList() throws IOException + { + return reader.canRead(3) && !StringReader.isQuoteCharacter(reader.peek(1)) && reader.peek(2) == ';' ? readArrayTag() : readListTag(); + } + + protected DataTag readTagType() throws IOException + { + reader.skipEmpty(); + int last = reader.getIndex(); + if(StringReader.isQuoteCharacter(reader.peek())) + { + return new StringTag(reader.readQuotedText()); + } + else + { + String s = reader.readString(); + if(s.isEmpty()) + { + throw new IOException("Expected Value at " + last); + } + else + { + return tag(s); + } + } + } + + public ListTag readListTag() throws IOException + { + requirePresent('['); + reader.skipEmpty(); + if(!reader.canRead()) + { + throw new IOException("Expected List Data"); + } + else + { + ListTag list = new ListTag(); + while(reader.peek() != ']') + { + list.add(readTag()); + if(!reader.isPresent(',')) + { + break; + } + reader.skipEmpty(); + if(!reader.canRead()) + { + throw new IOException("Expected Another Element"); + } + } + + requirePresent(']'); + return list; + } + } + + protected DataTag readArrayTag() throws IOException + { + requirePresent('['); + char type = reader.pull(); + reader.skip(); + reader.skipEmpty(); + if(!reader.canRead()) + { + throw new IOException("Out of Data!"); + } + else if(type == 'B') + { + ByteList list = new ByteArrayList(); + readArray(T -> list.add(T.asByte()), 1, PrimitiveTag.class); + return new ByteArrayTag(list.toByteArray()); + } + else if(type == 'S') + { + ShortList list = new ShortArrayList(); + readArray(T -> list.add(T.asShort()), 2, PrimitiveTag.class); + return new ShortArrayTag(list.toShortArray()); + } + else if(type == 'M') + { + IntList list = new IntArrayList(); + readArray(T -> list.add(T.asInt()), 3, PrimitiveTag.class); + return new MediumArrayTag(list.toIntArray()); + } + else if(type == 'I') + { + IntList list = new IntArrayList(); + readArray(T -> list.add(T.asInt()), 4, PrimitiveTag.class); + return new IntArrayTag(list.toIntArray()); + } + else if(type == 'L') + { + LongList list = new LongArrayList(); + readArray(T -> list.add(T.asLong()), 5, PrimitiveTag.class); + return new LongArrayTag(list.toLongArray()); + } + else if(type == 'F') + { + FloatList list = new FloatArrayList(); + readArray(T -> list.add(T.asFloat()), 6, PrimitiveTag.class); + return new FloatArrayTag(list.toFloatArray()); + } + else if(type == 'D') + { + DoubleList list = new DoubleArrayList(); + readArray(T -> list.add(T.asDouble()), 7, PrimitiveTag.class); + return new DoubleArrayTag(list.toDoubleArray()); + } + else if(type == 'A') + { + ObjectList list = new ObjectArrayList(); + readArray(T -> list.add(T.get()), 8, StringTag.class); + return new StringArrayTag(list.toArray(new String[list.size()])); + } + else + { + throw new IOException("Invalid Data Type: [Received=" + type + ", Valid=[B, S, M, I, L, F, D, A]"); + } + } + + protected void readArray(Consumer result, int type, Class clz) throws IOException + { + while(true) + { + if(reader.peek() != ']') + { + DataTag tag = readTag(); + if(tag.getId() != type) + { + throw new IOException("Mixing of ArrayTypes [Expected=" + DataTag.create((byte)type).getTypeName() + ", Present=" + tag.getTypeName() + "]"); + } + result.accept(tag.cast()); + reader.skipEmpty(); + if(reader.isPresent(',')) + { + reader.skipEmpty(); + if(!reader.canRead()) + { + throw new IOException("Expected Another Element"); + } + continue; + } + } + requirePresent(']'); + return; + } + } + + protected DataTag tag(String s) + { + try + { + if(FLOAT_TYPE.matcher(s).matches()) + { + return new FloatTag(Float.parseFloat(s.substring(0, s.length() - 1))); + } + if(BYTE_TYPE.matcher(s).matches()) + { + return new ByteTag(Byte.parseByte(s.substring(0, s.length() - 1))); + } + if(LONG_TYPE.matcher(s).matches()) + { + return new LongTag(Long.parseLong(s.substring(0, s.length() - 1))); + } + if(SHORT_TYPE.matcher(s).matches()) + { + return new ShortTag(Short.parseShort(s.substring(0, s.length() - 1))); + } + if(MEDIUM_TYPE.matcher(s).matches()) + { + return new MediumTag(Integer.parseInt(s.substring(0, s.length() - 1))); + } + if(INT_TYPE.matcher(s).matches()) + { + return new IntTag(Integer.parseInt(s.substring(0, s.length() - 1))); + } + if(DOUBLE_TYPE.matcher(s).matches()) + { + return new DoubleTag(Double.parseDouble(s.substring(0, s.length() - 1))); + } + if(DOUBLE_TYPE_DEFAULT.matcher(s).matches()) + { + return new DoubleTag(Double.parseDouble(s)); + } + if(INT_TYPE_DEFAULT.matcher(s).matches()) + { + return new IntTag(Integer.parseInt(s)); + } + if("true".equalsIgnoreCase(s)) + { + return new ByteTag((byte)1); + } + if("false".equalsIgnoreCase(s)) + { + return new ByteTag((byte)0); + } + } + catch(NumberFormatException e) + { + } + return new StringTag(s); + } + + protected void requirePresent(char value) + { + reader.skipEmpty(); + reader.requirePresent(value); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/deserialize/IDataDeserializationContext.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/deserialize/IDataDeserializationContext.java new file mode 100644 index 0000000..df2befc --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/deserialize/IDataDeserializationContext.java @@ -0,0 +1,10 @@ +package speiger.src.coreengine.utils.io.dataTag.parsing.deserialize; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; + +public interface IDataDeserializationContext +{ + public T deserialize(DataTag tag, Class type) throws IOException; +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/deserialize/IDataDeserializer.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/deserialize/IDataDeserializer.java new file mode 100644 index 0000000..458b0c9 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/deserialize/IDataDeserializer.java @@ -0,0 +1,10 @@ +package speiger.src.coreengine.utils.io.dataTag.parsing.deserialize; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; + +public interface IDataDeserializer +{ + public T deserialize(DataTag tag, IDataDeserializationContext context) throws IOException; +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/serialize/IDataSerializationContext.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/serialize/IDataSerializationContext.java new file mode 100644 index 0000000..13b8cda --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/serialize/IDataSerializationContext.java @@ -0,0 +1,10 @@ +package speiger.src.coreengine.utils.io.dataTag.parsing.serialize; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; + +public interface IDataSerializationContext +{ + public DataTag serialize(Object obj); + + public DataTag serialize(Object obj, Class clz); +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/serialize/IDataSerializer.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/serialize/IDataSerializer.java new file mode 100644 index 0000000..0ea1bcb --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/serialize/IDataSerializer.java @@ -0,0 +1,8 @@ +package speiger.src.coreengine.utils.io.dataTag.parsing.serialize; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; + +public interface IDataSerializer +{ + public DataTag serialize(T entry, IDataSerializationContext context); +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/ByteTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/ByteTag.java new file mode 100644 index 0000000..e8dfe3b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/ByteTag.java @@ -0,0 +1,120 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class ByteTag implements PrimitiveTag +{ + byte value; + + public ByteTag() + { + } + + public ByteTag(byte value) + { + this.value = value; + } + + @Override + public int getId() + { + return 1; + } + + @Override + public String getTypeName() + { + return "Byte"; + } + + @Override + public long countBytes(long input) + { + return input + 1L; + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeByte(value); + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + value = input.readByte(); + limiter.countBytes(1); + } + + @Override + public ByteTag copy() + { + return new ByteTag(value); + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof ByteTag && ((ByteTag)obj).value == value; + } + + @Override + public int hashCode() + { + return value; + } + + @Override + public String toString() + { + return value+"b"; + } + + @Override + public String prettyPrint(String indent, int depth) + { + return new StringBuilder().append(NUMBER_HIGHLIGHT).append(value).append(TYPE_HIGHLIGHT).append("b").append(RESET_HIGHLIGHT).toString(); + } + + @Override + public byte asByte() + { + return value; + } + + @Override + public short asShort() + { + return value; + } + + @Override + public int asInt() + { + return value; + } + + @Override + public long asLong() + { + return value; + } + + @Override + public float asFloat() + { + return value; + } + + @Override + public double asDouble() + { + return value; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/DoubleTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/DoubleTag.java new file mode 100644 index 0000000..8b2b797 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/DoubleTag.java @@ -0,0 +1,121 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class DoubleTag implements PrimitiveTag +{ + double value; + + public DoubleTag() + { + } + + public DoubleTag(double value) + { + this.value = value; + } + + @Override + public int getId() + { + return 7; + } + + @Override + public String getTypeName() + { + return "Double"; + } + + @Override + public long countBytes(long input) + { + return input + 8L; + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeDouble(value); + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + value = input.readDouble(); + limiter.countBytes(8); + } + + @Override + public DataTag copy() + { + return new DoubleTag(value); + } + + @Override + public int hashCode() + { + return Double.hashCode(value); + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof DoubleTag && ((DoubleTag)obj).value == value; + } + + @Override + public String toString() + { + return value+"d"; + } + + @Override + public String prettyPrint(String indent, int depth) + { + return new StringBuilder().append(NUMBER_HIGHLIGHT).append(value).append(TYPE_HIGHLIGHT).append("d").append(RESET_HIGHLIGHT).toString(); + } + + @Override + public byte asByte() + { + return (byte)value; + } + + @Override + public short asShort() + { + return (short)value; + } + + @Override + public int asInt() + { + return (int)value; + } + + @Override + public long asLong() + { + return (long)value; + } + + @Override + public float asFloat() + { + return (float)value; + } + + @Override + public double asDouble() + { + return value; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/FloatTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/FloatTag.java new file mode 100644 index 0000000..18c5ab5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/FloatTag.java @@ -0,0 +1,121 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class FloatTag implements PrimitiveTag +{ + float value; + + public FloatTag() + { + } + + public FloatTag(float value) + { + this.value = value; + } + + @Override + public int getId() + { + return 6; + } + + @Override + public String getTypeName() + { + return "Float"; + } + + @Override + public long countBytes(long input) + { + return input + 4L; + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeFloat(value); + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + value = input.readFloat(); + limiter.countBytes(4); + } + + @Override + public DataTag copy() + { + return new FloatTag(value); + } + + @Override + public int hashCode() + { + return Float.hashCode(value); + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof FloatTag && ((FloatTag)obj).value == value; + } + + @Override + public String toString() + { + return value+"f"; + } + + @Override + public String prettyPrint(String indent, int depth) + { + return new StringBuilder().append(NUMBER_HIGHLIGHT).append(value).append(TYPE_HIGHLIGHT).append("f").append(RESET_HIGHLIGHT).toString(); + } + + @Override + public byte asByte() + { + return (byte)value; + } + + @Override + public short asShort() + { + return (short)value; + } + + @Override + public int asInt() + { + return (int)value; + } + + @Override + public long asLong() + { + return (long)value; + } + + @Override + public float asFloat() + { + return value; + } + + @Override + public double asDouble() + { + return value; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/IntTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/IntTag.java new file mode 100644 index 0000000..59dacd0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/IntTag.java @@ -0,0 +1,121 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class IntTag implements PrimitiveTag +{ + int value; + + public IntTag() + { + } + + public IntTag(int value) + { + this.value = value; + } + + @Override + public int getId() + { + return 4; + } + + @Override + public String getTypeName() + { + return "Integer"; + } + + @Override + public long countBytes(long input) + { + return input + 4L; + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeInt(value); + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + value = input.readInt(); + limiter.countBytes(4); + } + + @Override + public DataTag copy() + { + return new IntTag(value); + } + + @Override + public int hashCode() + { + return value; + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof IntTag && ((IntTag)obj).value == value; + } + + @Override + public String toString() + { + return value+""; + } + + @Override + public String prettyPrint(String indent, int depth) + { + return new StringBuilder().append(NUMBER_HIGHLIGHT).append(value).append(RESET_HIGHLIGHT).toString(); + } + + @Override + public byte asByte() + { + return (byte)value; + } + + @Override + public short asShort() + { + return (short)value; + } + + @Override + public int asInt() + { + return value; + } + + @Override + public long asLong() + { + return value; + } + + @Override + public float asFloat() + { + return value; + } + + @Override + public double asDouble() + { + return value; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/LongTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/LongTag.java new file mode 100644 index 0000000..0b59bbf --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/LongTag.java @@ -0,0 +1,121 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class LongTag implements PrimitiveTag +{ + long value; + + public LongTag() + { + } + + public LongTag(long value) + { + this.value = value; + } + + @Override + public int getId() + { + return 5; + } + + @Override + public String getTypeName() + { + return "Long"; + } + + @Override + public long countBytes(long input) + { + return input + 8L; + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeLong(value); + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + value = input.readLong(); + limiter.countBytes(8); + } + + @Override + public DataTag copy() + { + return new LongTag(value); + } + + @Override + public int hashCode() + { + return Long.hashCode(value); + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof LongTag && ((LongTag)obj).value == value; + } + + @Override + public String toString() + { + return value+"l"; + } + + @Override + public String prettyPrint(String indent, int depth) + { + return new StringBuilder().append(NUMBER_HIGHLIGHT).append(value).append(TYPE_HIGHLIGHT).append("l").append(RESET_HIGHLIGHT).toString(); + } + + @Override + public byte asByte() + { + return (byte)value; + } + + @Override + public short asShort() + { + return (short)value; + } + + @Override + public int asInt() + { + return (int)value; + } + + @Override + public long asLong() + { + return value; + } + + @Override + public float asFloat() + { + return value; + } + + @Override + public double asDouble() + { + return value; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/MediumTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/MediumTag.java new file mode 100644 index 0000000..9817831 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/MediumTag.java @@ -0,0 +1,121 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class MediumTag implements PrimitiveTag +{ + int value; + + public MediumTag() + { + } + + public MediumTag(int value) + { + this.value = value; + } + + @Override + public int getId() + { + return 3; + } + + @Override + public String getTypeName() + { + return "Medium"; + } + + @Override + public long countBytes(long input) + { + return input + 3L; + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeMedium(value); + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + value = input.readMedium(); + limiter.countBytes(3); + } + + @Override + public DataTag copy() + { + return new MediumTag(value); + } + + @Override + public int hashCode() + { + return value; + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof MediumTag && ((MediumTag)obj).value == value; + } + + @Override + public String toString() + { + return value+"m"; + } + + @Override + public String prettyPrint(String indent, int depth) + { + return new StringBuilder().append(NUMBER_HIGHLIGHT).append(value).append(TYPE_HIGHLIGHT).append("m").append(RESET_HIGHLIGHT).toString(); + } + + @Override + public byte asByte() + { + return (byte)value; + } + + @Override + public short asShort() + { + return (short)value; + } + + @Override + public int asInt() + { + return value; + } + + @Override + public long asLong() + { + return value; + } + + @Override + public float asFloat() + { + return value; + } + + @Override + public double asDouble() + { + return value; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/PrimitiveTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/PrimitiveTag.java new file mode 100644 index 0000000..0d3d58f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/PrimitiveTag.java @@ -0,0 +1,18 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; + +public interface PrimitiveTag extends DataTag +{ + public byte asByte(); + + public short asShort(); + + public int asInt(); + + public long asLong(); + + public float asFloat(); + + public double asDouble(); +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/ShortTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/ShortTag.java new file mode 100644 index 0000000..205077c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/ShortTag.java @@ -0,0 +1,121 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class ShortTag implements PrimitiveTag +{ + short value; + + public ShortTag() + { + } + + public ShortTag(short value) + { + this.value = value; + } + + @Override + public int getId() + { + return 2; + } + + @Override + public String getTypeName() + { + return "Short"; + } + + @Override + public long countBytes(long input) + { + return input + 2L; + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeShort(value); + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + value = input.readShort(); + limiter.countBytes(2); + } + + @Override + public ShortTag copy() + { + return new ShortTag(value); + } + + @Override + public int hashCode() + { + return value; + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof ShortTag && ((ShortTag)obj).value == value; + } + + @Override + public String toString() + { + return value+"s"; + } + + @Override + public String prettyPrint(String indent, int depth) + { + return new StringBuilder().append(NUMBER_HIGHLIGHT).append(value).append(TYPE_HIGHLIGHT).append("s").append(RESET_HIGHLIGHT).toString(); + } + + @Override + public byte asByte() + { + return (byte)value; + } + + @Override + public short asShort() + { + return value; + } + + @Override + public int asInt() + { + return value; + } + + @Override + public long asLong() + { + return value; + } + + @Override + public float asFloat() + { + return value; + } + + @Override + public double asDouble() + { + return value; + } + +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/StringTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/StringTag.java new file mode 100644 index 0000000..520291d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/StringTag.java @@ -0,0 +1,125 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class StringTag implements DataTag +{ + String value; + + public StringTag() + { + this(""); + } + + public StringTag(String value) + { + this.value = value == null ? "" : value; + } + + @Override + public int getId() + { + return 8; + } + + @Override + public String getTypeName() + { + return "String"; + } + + @Override + public long countBytes(long input) + { + return input + 4L + (value.length() * 2L); + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeString(value); + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + value = input.readString(); + limiter.countBytes((value.length() * 2) + 4); + } + + @Override + public StringTag copy() + { + return new StringTag(value); + } + + public String get() + { + return value; + } + + @Override + public int hashCode() + { + return value.hashCode(); + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof StringTag && ((StringTag)obj).value.equals(value); + } + + @Override + public String toString() + { + return quoteAndEscape(value); + } + + @Override + public String prettyPrint(String indent, int depth) + { + StringBuilder builder = quoteAndEscapeBuilder(value).insert(1, STRING_HIGHLIGHT); + return builder.insert(builder.length() - 1, RESET_HIGHLIGHT).toString(); + } + + public static String quoteAndEscape(String value) + { + return quoteAndEscapeBuilder(value).toString(); + } + + public static StringBuilder quoteAndEscapeBuilder(String value) + { + StringBuilder builder = new StringBuilder(" "); + char c0 = 0; + for(int i = 0,m=value.length();i < m;++i) + { + char c1 = value.charAt(i); + if(c1 == '\\') + { + builder.append('\\'); + } + else if(c1 == '"' || c1 == '\'') + { + if(c0 == 0) + { + c0 = (char)(c1 == '"' ? 39 : 34); + } + if(c0 == c1) + { + builder.append('\\'); + } + } + builder.append(c1); + } + builder.setCharAt(0, c0 == 0 ? '"' : c0); + return builder.append(c0 == 0 ? '"' : c0); + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/IListTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/IListTag.java new file mode 100644 index 0000000..1deb524 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/IListTag.java @@ -0,0 +1,67 @@ +package speiger.src.coreengine.utils.io.dataTag.special; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.array.ByteArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.DoubleArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.FloatArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.IntArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.LongArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.MediumArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.ShortArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.StringArrayTag; +import speiger.src.coreengine.utils.io.dataTag.simple.ByteTag; +import speiger.src.coreengine.utils.io.dataTag.simple.DoubleTag; +import speiger.src.coreengine.utils.io.dataTag.simple.FloatTag; +import speiger.src.coreengine.utils.io.dataTag.simple.IntTag; +import speiger.src.coreengine.utils.io.dataTag.simple.LongTag; +import speiger.src.coreengine.utils.io.dataTag.simple.MediumTag; +import speiger.src.coreengine.utils.io.dataTag.simple.PrimitiveTag; +import speiger.src.coreengine.utils.io.dataTag.simple.ShortTag; +import speiger.src.coreengine.utils.io.dataTag.simple.StringTag; + +public interface IListTag extends DataTag +{ + public void add(DataTag tag); + public default void addList(ListTag list) { if(!list.isEmpty()) add(list); } + public default void addMap(MapTag map) { if(!map.isEmpty()) add(map); } + public DataTag get(int index); + + public default void addByte(byte value) {add(new ByteTag(value));} + public default void addShort(short value) {add(new ShortTag(value));} + public default void addMedium(int value) {add(new MediumTag(value));} + public default void addInt(int value) {add(new IntTag(value));} + public default void addLong(long value) {add(new LongTag(value));} + public default void addFloat(float value) {add(new FloatTag(value));} + public default void addDouble(double value) {add(new DoubleTag(value));} + public default void addString(String value) {add(new StringTag(value));} + + public default void addByteArray(byte[] value) {add(new ByteArrayTag(value));} + public default void addShortArray(short[] value) {add(new ShortArrayTag(value));} + public default void addMediumArray(int[] value) {add(new MediumArrayTag(value));} + public default void addIntArray(int[] value) {add(new IntArrayTag(value));} + public default void addLongArray(long[] value) {add(new LongArrayTag(value));} + public default void addFloatArray(float[] value) {add(new FloatArrayTag(value));} + public default void addDoubleArray(double[] value) {add(new DoubleArrayTag(value));} + public default void addStringArray(String[] value) {add(new StringArrayTag(value));} + + public default byte getByte(int index) {return ((PrimitiveTag)get(index)).asByte();} + public default short getShort(int index) {return ((PrimitiveTag)get(index)).asShort();} + public default int getMedium(int index) {return ((PrimitiveTag)get(index)).asInt();} + public default int getInt(int index) {return ((PrimitiveTag)get(index)).asInt();} + public default long getLong(int index) {return ((PrimitiveTag)get(index)).asLong();} + public default float getFloat(int index) {return ((PrimitiveTag)get(index)).asFloat();} + public default double getDouble(int index) {return ((PrimitiveTag)get(index)).asDouble();} + public default String getString(int index) {return ((StringTag)get(index)).get();} + + public default byte[] getByteArray(int index) {return ((ByteArrayTag)get(index)).get();} + public default short[] getShortArray(int index) {return ((ShortArrayTag)get(index)).get();} + public default int[] getMediumArray(int index) {return ((MediumArrayTag)get(index)).get();} + public default int[] getIntArray(int index) {return ((IntArrayTag)get(index)).get();} + public default long[] getLongArray(int index) {return ((LongArrayTag)get(index)).get();} + public default float[] getFloatArray(int index) {return ((FloatArrayTag)get(index)).get();} + public default double[] getDoubleArray(int index) {return ((DoubleArrayTag)get(index)).get();} + public default String[] getStringArray(int index) {return ((StringArrayTag)get(index)).get();} + + public default ListTag getList(int index){return (ListTag)get(index);} + public default MapTag getMap(int index){return (MapTag)get(index);} +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/IMapTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/IMapTag.java new file mode 100644 index 0000000..d99536f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/IMapTag.java @@ -0,0 +1,249 @@ +package speiger.src.coreengine.utils.io.dataTag.special; + +import java.util.UUID; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.array.ByteArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.DoubleArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.FloatArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.IntArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.LongArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.MediumArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.ShortArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.StringArrayTag; +import speiger.src.coreengine.utils.io.dataTag.simple.ByteTag; +import speiger.src.coreengine.utils.io.dataTag.simple.DoubleTag; +import speiger.src.coreengine.utils.io.dataTag.simple.FloatTag; +import speiger.src.coreengine.utils.io.dataTag.simple.IntTag; +import speiger.src.coreengine.utils.io.dataTag.simple.LongTag; +import speiger.src.coreengine.utils.io.dataTag.simple.MediumTag; +import speiger.src.coreengine.utils.io.dataTag.simple.PrimitiveTag; +import speiger.src.coreengine.utils.io.dataTag.simple.ShortTag; +import speiger.src.coreengine.utils.io.dataTag.simple.StringTag; + +public interface IMapTag extends DataTag +{ + public void setTag(String id, DataTag tag); + public default void setList(String id, ListTag tag) { if(!tag.isEmpty()) setTag(id, tag); } + public default void setMap(String id, MapTag tag) { if(!tag.isEmpty()) setTag(id, tag); } + + public default void setBoolean(String id, boolean value) {setByte(id, (byte)(value ? 1 : 0), (byte)0);} + public default void setByte(String id, byte value) {setByte(id, value, (byte)0);} + public default void setByte(String id, byte value, byte defaultValue) + { + if(value != defaultValue) + { + setTag(id, new ByteTag(value)); + } + } + + public default void setShort(String id, short value) {setShort(id, value, (short)0);} + public default void setShort(String id, short value, short defaultValue) + { + if(value != defaultValue) + { + setTag(id, new ShortTag(value)); + } + } + + public default void setMedium(String id, int value) {setMedium(id, value, 0);} + public default void setMedium(String id, int value, int defaultValue) + { + if(value != defaultValue) + { + setTag(id, new MediumTag(value)); + } + } + + public default void setInt(String id, int value) {setInt(id, value, 0);} + public default void setInt(String id, int value, int defaultValue) + { + if(value != defaultValue) + { + setTag(id, new IntTag(value)); + } + } + + public default void setLong(String id, long value) {setLong(id, value, 0L);} + public default void setLong(String id, long value, long defaultValue) + { + if(value != defaultValue) + { + setTag(id, new LongTag(value)); + } + } + + public default void setFloat(String id, float value) {setFloat(id, value, 0F);} + public default void setFloat(String id, float value, float defaultValue) + { + if(value != defaultValue) + { + setTag(id, new FloatTag(value)); + } + } + + public default void setDouble(String id, double value) {setDouble(id, value, 0D);} + public default void setDouble(String id, double value, double defaultValue) + { + if(value != defaultValue) + { + setTag(id, new DoubleTag(value)); + } + } + + public default void setString(String id, String value) {setString(id, value, "");} + public default void setString(String id, String value, String defaultValue) + { + if(!value.equals(defaultValue)) + { + setTag(id, new StringTag(value)); + } + } + + public default void setByteArray(String id, byte[] data) {if(data.length > 0){setTag(id, new ByteArrayTag(data));}} + public default void setShortArray(String id, short[] data) {if(data.length > 0){setTag(id, new ShortArrayTag(data));}} + public default void setMediumArray(String id, int[] data) {if(data.length > 0){setTag(id, new MediumArrayTag(data));}} + public default void setIntArray(String id, int[] data) {if(data.length > 0){setTag(id, new IntArrayTag(data));}} + public default void setLongArray(String id, long[] data) {if(data.length > 0){setTag(id, new LongArrayTag(data));}} + public default void setFloatArray(String id, float[] data) {if(data.length > 0){setTag(id, new FloatArrayTag(data));}} + public default void setDoubleArray(String id, double[] data) {if(data.length > 0){setTag(id, new DoubleArrayTag(data));}} + public default void setStringArray(String id, String[] data) {if(data.length > 0){setTag(id, new StringArrayTag(data));}} + + public default void setUUID(String id, UUID data) { if(data != null) setLongArray(id, new long[]{data.getMostSignificantBits(), data.getLeastSignificantBits()});} + + public DataTag get(String id); + + public default boolean getBoolean(String id) { return getByte(id, (byte)0) != 0;} + public default byte getByte(String id) {return getByte(id, (byte)0);} + public default byte getByte(String id, byte defaultValue) + { + DataTag tag = get(id); + return isPrimitve(tag) ? ((PrimitiveTag)tag).asByte() : defaultValue; + } + + public default short getShort(String id) {return getShort(id, (short)0);} + public default short getShort(String id, short defaultValue) + { + DataTag tag = get(id); + return isPrimitve(tag) ? ((PrimitiveTag)tag).asShort() : defaultValue; + } + + public default int getMedium(String id) {return getMedium(id, 0);} + public default int getMedium(String id, int defaultValue) + { + DataTag tag = get(id); + return isPrimitve(tag) ? ((PrimitiveTag)tag).asInt() : defaultValue; + } + + public default int getInt(String id) {return getInt(id, 0);} + public default int getInt(String id, int defaultValue) + { + DataTag tag = get(id); + return isPrimitve(tag) ? ((PrimitiveTag)tag).asInt() : defaultValue; + } + + public default long getLong(String id) {return getLong(id, 0L);} + public default long getLong(String id, long defaultValue) + { + DataTag tag = get(id); + return isPrimitve(tag) ? ((PrimitiveTag)tag).asLong() : defaultValue; + } + + public default float getFloat(String id) {return getFloat(id, 0F);} + public default float getFloat(String id, float defaultValue) + { + DataTag tag = get(id); + return isPrimitve(tag) ? ((PrimitiveTag)tag).asFloat() : defaultValue; + } + + public default double getDouble(String id) {return getDouble(id, 0D);} + public default double getDouble(String id, double defaultValue) + { + DataTag tag = get(id); + return isPrimitve(tag) ? ((PrimitiveTag)tag).asByte() : defaultValue; + } + + public default String getString(String id) {return getString(id, "");} + public default String getString(String id, String defaultValue) + { + DataTag tag = get(id); + return isType(tag, 8) ? ((StringTag)tag).get() : defaultValue; + } + + public default byte[] getByteArray(String id) + { + DataTag tag = get(id); + return isType(tag, 9) ? ((ByteArrayTag)tag).get() : new byte[0]; + } + + public default short[] getShortArray(String id) + { + DataTag tag = get(id); + return isType(tag, 10) ? ((ShortArrayTag)tag).get() : new short[0]; + } + + public default int[] getMediumArray(String id) + { + DataTag tag = get(id); + return isType(tag, 11) ? ((MediumArrayTag)tag).get() : new int[0]; + } + + public default int[] getIntArray(String id) + { + DataTag tag = get(id); + return isType(tag, 12) ? ((IntArrayTag)tag).get() : new int[0]; + } + + public default long[] getLongArray(String id) + { + DataTag tag = get(id); + return isType(tag, 13) ? ((LongArrayTag)tag).get() : new long[0]; + } + + public default float[] getFloatArray(String id) + { + DataTag tag = get(id); + return isType(tag, 14) ? ((FloatArrayTag)tag).get() : new float[0]; + } + + public default double[] getDoubleArray(String id) + { + DataTag tag = get(id); + return isType(tag, 15) ? ((DoubleArrayTag)tag).get() : new double[0]; + } + + public default String[] getStringArray(String id) + { + DataTag tag = get(id); + return isType(tag, 16) ? ((StringArrayTag)tag).get() : new String[0]; + } + + public default UUID getUUID(String id) + { + long[] data = getLongArray(id); + return data == null || data.length != 2 ? null : new UUID(data[0], data[1]); + } + + public default MapTag getMap(String id) + { + DataTag tag = get(id); + return isType(tag, 17) ? (MapTag)tag : new MapTag(); + } + + public default ListTag getList(String id) + { + DataTag tag = get(id); + return isType(tag, 18) ? (ListTag)tag : new ListTag(); + } + + public default boolean isPrimitve(DataTag tag) + { + int id = tag == null ? 0 : tag.getId(); + return id >= 1 && id <= 7; + } + + public default boolean isType(DataTag tag, int type) + { + return (tag == null ? 0 : tag.getId()) == type; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/ListTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/ListTag.java new file mode 100644 index 0000000..7e356d4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/ListTag.java @@ -0,0 +1,257 @@ +package speiger.src.coreengine.utils.io.dataTag.special; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.StringJoiner; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class ListTag implements IListTag, Iterable +{ + List list = new ObjectArrayList(); + + @Override + public int getId() + { + return 18; + } + + @Override + public String getTypeName() + { + return "List"; + } + + @Override + public void addKeys(Consumer keys) + { + for(int i = 0,m=list.size();i= 512) + { + throw new IllegalStateException("Depth Limit Exceeded"); + } + limiter.countBytes(6); + int size = input.readVarInt(); + if(size <= 0) + { + return; + } + byte type = input.readByte(); + for(int i = 0;i= 1 && list.get(0).getId() < 8) + { + StringJoiner joiner = new StringJoiner(",", "[", "]"); + for(int i = 0,m=list.size();i= list.size()) + { + throw new ArrayIndexOutOfBoundsException(index); + } + else if(list.get(0).getId() != tag.getId() || tag.getId() == 0) + { + throw new IllegalStateException("Tag Type didn't match. [Inserted="+tag.getTypeName()+", Expected="+list.get(0).getTypeName()+"]"); + } + return list.set(index, tag); + } + + public DataTag remove(int index) + { + return list.remove(index); + } + + @Override + public DataTag get(int index) + { + return list.get(index); + } + + public boolean isEmpty() + { + return list.isEmpty(); + } + + public int size() + { + return list.size(); + } + + public ListTag subList(int from, int to) + { + ListTag list = new ListTag(); + for(int i = 0,m=to-from;i iterator() + { + return new Iterator(){ + int index = 0; + @Override + public boolean hasNext() + { + return index < list.size(); + } + @Override + public DataTag next() + { + return list.get(index++); + } + }; + } + + public Iterable typeIterator(Class clz) + { + return () -> new Iterator(){ + int index = 0; + @Override + public boolean hasNext() + { + return index < list.size(); + } + @Override + public S next() + { + return list.get(index++).cast(); + } + }; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/MapTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/MapTag.java new file mode 100644 index 0000000..b626b0b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/MapTag.java @@ -0,0 +1,210 @@ +package speiger.src.coreengine.utils.io.dataTag.special; + +import java.io.IOException; +import java.util.List; +import java.util.StringJoiner; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap.Entry; +import speiger.src.collections.objects.sets.ObjectSet; +import speiger.src.collections.objects.utils.ObjectSets; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.dataTag.simple.PrimitiveTag; +import speiger.src.coreengine.utils.io.dataTag.simple.StringTag; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class MapTag implements IMapTag +{ + Object2ObjectMap tags = new Object2ObjectLinkedOpenHashMap(); + + public MapTag() + { + } + + @Override + public int getId() + { + return 17; + } + + @Override + public String getTypeName() + { + return "Map"; + } + + @Override + public long countBytes(long input) + { + input += 4; + for(Entry entry : Object2ObjectMaps.fastIterable(tags)) + { + input += 1; + input = entry.getValue().countBytes(input); + } + return input; + } + + @Override + public void addKeys(Consumer keys) + { + for(Entry entry : Object2ObjectMaps.fastIterable(tags)) + { + keys.accept(entry.getKey()); + entry.getValue().addKeys(keys); + } + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeVarInt(tags.size()); + for(Entry entry : Object2ObjectMaps.fastIterable(tags)) + { + output.writeByte(entry.getValue().getId()); + compressor.writeKey(entry.getKey()); + entry.getValue().write(output, compressor); + } + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + if(depthLimit >= 512) + { + throw new IllegalStateException("Depth Limit Exceeded"); + } + int size = input.readVarInt(); + limiter.countBytes(4 + size); + for(int i = 0;i < size;i++) + { + byte type = input.readByte(); + String id = decompressor.readKey(); + limiter.countBytes(4 + (id.length() * 2)); + tags.put(id, decompressor.readSubTag(type, id, depthLimit + 1)); + } + } + + @Override + public MapTag copy() + { + MapTag tag = new MapTag(); + for(Entry entry : Object2ObjectMaps.fastIterable(tags)) + { + tag.tags.put(entry.getKey(), entry.getValue().copy()); + } + return tag; + } + + @Override + public void setTag(String id, DataTag tag) + { + tags.put(id, tag); + } + + public boolean contains(String id) + { + return tags.containsKey(id); + } + + public boolean contains(String id, int tagType) + { + DataTag tag = tags.get(id); + return tag != null && ((tagType == 99 && tag instanceof PrimitiveTag) || tag.getId() == tagType); + } + + @Override + public DataTag get(String id) + { + return tags.get(id); + } + + public DataTag remove(String id) + { + return tags.remove(id); + } + + public boolean isEmpty() + { + return tags.isEmpty(); + } + + public int size() + { + return tags.size(); + } + + public ObjectSet keySet() + { + return ObjectSets.unmodifiable(tags.keySet()); + } + + public ObjectSet> entrySet() + { + return ObjectSets.unmodifiable(tags.object2ObjectEntrySet()); + } + + @Override + public int hashCode() + { + return tags.hashCode(); + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof MapTag && ((MapTag)obj).tags.equals(tags); + } + + @Override + public String toString() + { + StringJoiner joiner = new StringJoiner(",", "{", "}"); + List keys = new ObjectArrayList<>(tags.keySet()); + keys.sort(null); + for(int i = 0, m = keys.size();i < m;i++) + { + joiner.add(StringTag.quoteAndEscapeBuilder(keys.get(i)).append(":").append(tags.get(keys.get(i))).toString()); + } + return joiner.toString(); + } + + @Override + public String prettyPrint(String indent, int depth) + { + if(tags.isEmpty()) + { + return "{}"; + } + List keys = new ObjectArrayList<>(tags.keySet()); + keys.sort(null); + StringBuilder builder = new StringBuilder().append("{"); + String s = DataTag.repeate(indent, depth+1); + if(!indent.isEmpty()) + { + builder.append('\n'); + } + for(int i = 0,m=keys.size();i> implements ISetting +{ + protected final String id; + List> listeners = new ObjectArrayList>(); + + public BaseSetting(String id) + { + this.id = id; + } + + @Override + public T addListener(Consumer listener) + { + listeners.add(listener); + return (T)this; + } + + protected void notifyListeners() + { + if(listeners.isEmpty()) + { + return; + } + T value = (T)this; + for(int i = 0,m=listeners.size();i +{ + final boolean defaultValue; + boolean value; + + public BooleanSetting(String id, boolean defaultValue) + { + super(id); + this.defaultValue = defaultValue; + value = defaultValue; + } + + @Override + public boolean isDefault() + { + return value == defaultValue; + } + + @Override + public void setDefault() + { + if(value != defaultValue) + { + value = defaultValue; + notifyListeners(); + } + } + + public BooleanSetting setValue(boolean value) + { + if(this.value != value) + { + this.value = value; + notifyListeners(); + } + return this; + } + + public boolean getDefaultValue() + { + return defaultValue; + } + + public boolean getValue() + { + return value; + } + + @Override + public void write(JsonObject object) + { + object.addProperty(id, value); + } + + @Override + public void read(JsonObject object) + { + value = JsonUtil.getOrDefault(object, id, defaultValue); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/settings/ISetting.java b/src/main/java/speiger/src/coreengine/utils/io/settings/ISetting.java new file mode 100644 index 0000000..c50b313 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/settings/ISetting.java @@ -0,0 +1,16 @@ +package speiger.src.coreengine.utils.io.settings; + +import java.util.function.Consumer; + +import com.google.gson.JsonObject; + +public interface ISetting> +{ + public T addListener(Consumer listener); + + public boolean isDefault(); + public void setDefault(); + + public void write(JsonObject object); + public void read(JsonObject object); +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/settings/IntSetting.java b/src/main/java/speiger/src/coreengine/utils/io/settings/IntSetting.java new file mode 100644 index 0000000..a7259b5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/settings/IntSetting.java @@ -0,0 +1,66 @@ +package speiger.src.coreengine.utils.io.settings; + +import com.google.gson.JsonObject; + +import speiger.src.coreengine.utils.helpers.JsonUtil; + +public class IntSetting extends BaseSetting +{ + final int defaultValue; + int value; + + public IntSetting(String id, int defaultValue) + { + super(id); + this.defaultValue = defaultValue; + value = defaultValue; + } + + @Override + public boolean isDefault() + { + return value == defaultValue; + } + + @Override + public void setDefault() + { + if(value != defaultValue) + { + value = defaultValue; + notifyListeners(); + } + } + + public int getValue() + { + return value; + } + + public int getDefaultValue() + { + return defaultValue; + } + + public IntSetting setValue(int value) + { + if(this.value != value) + { + this.value = value; + notifyListeners(); + } + return this; + } + + @Override + public void write(JsonObject object) + { + object.addProperty(id, value); + } + + @Override + public void read(JsonObject object) + { + value = JsonUtil.getOrDefault(object, id, defaultValue); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/settings/Settings.java b/src/main/java/speiger/src/coreengine/utils/io/settings/Settings.java new file mode 100644 index 0000000..90b44b0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/settings/Settings.java @@ -0,0 +1,50 @@ +package speiger.src.coreengine.utils.io.settings; + +import java.io.File; +import java.io.FileWriter; + +import com.google.gson.JsonObject; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; + +import speiger.src.coreengine.utils.helpers.JsonUtil; + +public class Settings +{ + File file; + ISetting[] settings; + + public Settings(File path, ISetting[] settings) + { + file = path; + this.settings = settings; + } + + public void load() + { + JsonObject obj = JsonUtil.loadFile(file); + for(int i = 0,m=settings.length;i 5) + { + throw new RuntimeException("VarInt too big"); + } + } + while((temp & 128) == 128); + return data; + } + + public default long readVarLong() throws IOException + { + long data = 0L; + int index = 0; + byte temp; + do + { + temp = readByte(); + data |= (long)(temp & 127) << index++ * 7; + if(index > 10) + { + throw new RuntimeException("VarLong too big"); + } + } + while((temp & 128) == 128); + return data; + } + + public default byte[] readBytes() throws IOException + { + byte[] data = new byte[readVarInt()]; + readFully(data); + return data; + } + + public default UUID readUUID() throws IOException + { + return new UUID(readLong(), readLong()); + } + + public default > T readEnumValue(Class enumType) throws IOException + { + return enumType.getEnumConstants()[readVarInt()]; + } + + public default String readString() throws IOException + { + return new String(readBytes(), StandardCharsets.UTF_8); + } + + public static class ExtendedDataInputStream extends DataInputStream + implements ExtendedDataInput + { + public ExtendedDataInputStream(InputStream in) + { + super(in); + } + } + + public static class WrappedExtendedDataInput implements ExtendedDataInput + { + DataInput input; + + public WrappedExtendedDataInput(DataInput input) + { + this.input = input; + } + + @Override + public void close() throws Exception + { + if(input instanceof AutoCloseable) + { + ((AutoCloseable)input).close(); + } + } + + @Override + public void readFully(byte[] b) throws IOException + { + input.readFully(b); + } + + @Override + public void readFully(byte[] b, int off, int len) throws IOException + { + input.readFully(b, off, len); + } + + @Override + public int skipBytes(int n) throws IOException + { + return input.skipBytes(n); + } + + @Override + public boolean readBoolean() throws IOException + { + return input.readBoolean(); + } + + @Override + public byte readByte() throws IOException + { + return input.readByte(); + } + + @Override + public int readUnsignedByte() throws IOException + { + return input.readUnsignedByte(); + } + + @Override + public short readShort() throws IOException + { + return input.readShort(); + } + + @Override + public int readUnsignedShort() throws IOException + { + return input.readUnsignedShort(); + } + + @Override + public char readChar() throws IOException + { + return input.readChar(); + } + + @Override + public int readInt() throws IOException + { + return input.readInt(); + } + + @Override + public long readLong() throws IOException + { + return input.readLong(); + } + + @Override + public float readFloat() throws IOException + { + return input.readFloat(); + } + + @Override + public double readDouble() throws IOException + { + return input.readDouble(); + } + + @Override + public String readLine() throws IOException + { + return input.readLine(); + } + + @Override + public String readUTF() throws IOException + { + return input.readUTF(); + } + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/io/streams/ExtendedDataOutput.java b/src/main/java/speiger/src/coreengine/utils/io/streams/ExtendedDataOutput.java new file mode 100644 index 0000000..27b6caf --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/streams/ExtendedDataOutput.java @@ -0,0 +1,100 @@ +package speiger.src.coreengine.utils.io.streams; + +import java.io.BufferedOutputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.Flushable; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +public interface ExtendedDataOutput extends DataOutput, Flushable, AutoCloseable +{ + public static ExtendedDataOutput createBuffered(OutputStream stream) + { + return new ExtendedDataOutputStream(new BufferedOutputStream(stream)); + } + + public static ExtendedDataOutput createBuffered(OutputStream stream, int size) + { + return new ExtendedDataOutputStream(new BufferedOutputStream(stream, size)); + } + + public static ExtendedDataOutput create(OutputStream stream) + { + return new ExtendedDataOutputStream(stream); + } + + public default void writeMedium(int v) throws IOException + { + write((v >>> 16) & 0xFF); + write((v >>> 8) & 0xFF); + write((v >>> 0) & 0xFF); + } + + public default void writeVarInt(int v) throws IOException + { + while((v & -128) != 0) + { + writeByte(v & 127 | 128); + v >>>= 7; + } + writeByte(v); + } + + public default void writeVarLong(long v) throws IOException + { + while((v & -128L) != 0L) + { + writeByte((int)(v & 127L) | 128); + v >>>= 7; + } + writeByte((int)v); + } + + public default void writeBytes(byte[] v) throws IOException + { + writeBytes(v, 0, v.length); + } + + public default void writeBytes(byte[] v, int offset, int length) throws IOException + { + writeVarInt(length); + write(v, offset, length); + } + + public default void writeUUID(UUID id) throws IOException + { + writeLong(id.getMostSignificantBits()); + writeLong(id.getLeastSignificantBits()); + } + + public default void writeEnum(Enum v) throws IOException + { + writeVarInt(v.ordinal()); + } + + public default void writeString(String s) throws IOException + { + writeString(s, 65535); + } + + public default void writeString(String s, int maxBytes) throws IOException + { + byte[] data = s.getBytes(StandardCharsets.UTF_8); + if(data.length > maxBytes) + { + throw new IOException("Encoded String was to big. ("+data.length+", Limit: "+maxBytes+", Diff: "+(data.length - maxBytes)+")"); + } + writeBytes(data); + } + + public static class ExtendedDataOutputStream extends DataOutputStream implements ExtendedDataOutput + { + public ExtendedDataOutputStream(OutputStream out) + { + super(out); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/streams/StringReader.java b/src/main/java/speiger/src/coreengine/utils/io/streams/StringReader.java new file mode 100644 index 0000000..8430869 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/streams/StringReader.java @@ -0,0 +1,172 @@ +package speiger.src.coreengine.utils.io.streams; + +public class StringReader +{ + String text; + int index; + + public StringReader(String text) + { + this.text = text; + if(text == null) + { + throw new IllegalStateException("String is null"); + } + } + + public int getIndex() + { + return index; + } + + public void setIndex(int index) + { + this.index = index; + } + + public boolean canRead() + { + return canRead(1); + } + + public boolean canRead(int amount) + { + return index + amount <= text.length(); + } + + public char peek() + { + return text.charAt(index); + } + + public char peek(int offset) + { + return text.charAt(index + offset); + } + + public char pull() + { + return text.charAt(index++); + } + + public void skip() + { + index++; + } + + public void skip(int amount) + { + index += amount; + } + + public void skipEmpty() + { + for(;canRead() && Character.isWhitespace(peek());skip()) + ; + } + + public void requirePresent(char value) + { + if(!canRead() || peek() != value) + { + throw new IllegalStateException("Char not present"); + } + skip(); + } + + public boolean isPresent(char value) + { + if(!canRead() || peek() != value) + { + return false; + } + skip(); + return true; + } + + public static boolean isQuoteCharacter(char c) + { + return c == '"' || c == '\''; + } + + public static boolean isValidTextCharacter(char c) + { + return c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '_' || c == '-' || c == '.' || c == '+'; + } + + public String readText() + { + int start = index; + while(canRead() && isValidTextCharacter(peek())) + { + skip(); + } + return text.substring(start, index); + } + + public String readQuotedText() + { + if(!canRead()) + { + return ""; + } + final char next = peek(); + if(isQuoteCharacter(next)) + { + skip(); + return readUntil(next); + } + throw new IllegalStateException("No Quote found"); + } + + public String readString() + { + if(!canRead()) + { + return ""; + } + final char next = peek(); + if(isQuoteCharacter(next)) + { + skip(); + return readUntil(next); + } + return readText(); + } + + public String readUntil(char end) + { + StringBuilder builder = new StringBuilder(); + boolean skip = false; + while(canRead()) + { + char c = pull(); + if(skip) + { + if(c == end || c == '\\') + { + builder.append(c); + skip = false; + } + else + { + setIndex(getIndex() - 1); + throw new IllegalStateException("Skipping non Skippable Letter"); + } + } + else if(c == '\\') + { + skip = true; + } + else if(c == end) + { + return builder.toString(); + } + else + { + builder.append(c); + } + } + throw new ArrayIndexOutOfBoundsException("Ran out of characters"); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/profiler/EmptyProfiler.java b/src/main/java/speiger/src/coreengine/utils/profiler/EmptyProfiler.java new file mode 100644 index 0000000..a1110d8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/profiler/EmptyProfiler.java @@ -0,0 +1,36 @@ +package speiger.src.coreengine.utils.profiler; + +import java.util.function.ObjIntConsumer; + +public final class EmptyProfiler implements IProfiler +{ + public static final IProfiler INSTANCE = new EmptyProfiler(); + + @Override + public String getName(){return "Empty";} + @Override + public IProfilerEntry getEntry(String name){return null;} + @Override + public long getTicksRan(){return 0;} + @Override + public void enable(){} + @Override + public void disable(){} + @Override + public boolean isEnabled(){return false;} + @Override + public void addListener(ObjIntConsumer listener){} + @Override + public void removeListener(ObjIntConsumer listener){} + @Override + public IProfiler start(String name){return this;} + @Override + public IProfiler start(Class clz){return this;} + @Override + public IProfiler stop(){return this;} + @Override + public IProfiler next(String name){return this;} + @Override + public void onFrameEnded(boolean count){} + +} diff --git a/src/main/java/speiger/src/coreengine/utils/profiler/GPUProfiler.java b/src/main/java/speiger/src/coreengine/utils/profiler/GPUProfiler.java new file mode 100644 index 0000000..9a3fcaf --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/profiler/GPUProfiler.java @@ -0,0 +1,173 @@ +package speiger.src.coreengine.utils.profiler; + +import java.util.Map; +import java.util.Set; +import java.util.function.ObjIntConsumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet; +import speiger.src.collections.utils.Stack; +import speiger.src.coreengine.rendering.utils.GLStamper; + +public class GPUProfiler implements IProfiler +{ + IProfiler cpuSide; + String name; + Map cache = new Object2ObjectLinkedOpenHashMap<>(); + Stack activeProfilers = new ObjectArrayList<>(); + Set> listeners = new ObjectLinkedOpenHashSet<>(); + long ticksRan = 0; + int enableRequest = 0; + + public GPUProfiler(String name, IProfiler cpu) + { + this.name = name; + cpuSide = cpu; + } + + @Override + public String getName() + { + return name; + } + + @Override + public IProfilerEntry getEntry(String name) + { + return cache.get(name); + } + + @Override + public long getTicksRan() + { + return ticksRan; + } + + @Override + public void enable() + { + enableRequest++; + if(enableRequest == 1) + { + sendState(0); + } + } + + @Override + public void disable() + { + enableRequest = Math.max(0, enableRequest-1); + if(enableRequest > 0) + { + return; + } + GLStamper.INSTANCE.removeOwner(name); + cache.clear(); + while(!activeProfilers.isEmpty()) + { + activeProfilers.pop(); + } + sendState(1); + } + + @Override + public boolean isEnabled() + { + return enableRequest > 0; + } + + @Override + public void addListener(ObjIntConsumer listener) + { + listeners.add(listener); + if(enableRequest > 0) + { + listener.accept(this, 0); + } + } + + @Override + public void removeListener(ObjIntConsumer listener) + { + listeners.remove(listener); + } + + @Override + public IProfiler start(String name) + { + cpuSide.start(name); + if(enableRequest == 0) + { + return this; + } + String entry = (activeProfilers.isEmpty() ? "" : activeProfilers.top().getPathName()+"/") + name; + GPUProfilerEntry active = cache.get(entry); + if(active == null) + { + active = new GPUProfilerEntry(name, this); + if(!activeProfilers.isEmpty()) + { + activeProfilers.top().addChild(active); + } + cache.put(entry, active); + sendState(2); + } + activeProfilers.push(active); + active.start(); + return this; + } + + @Override + public IProfiler start(Class clz) + { + cpuSide.start(clz); + return enableRequest == 0 ? this : start(clz.getSimpleName()); + } + + @Override + public IProfiler stop() + { + cpuSide.stop(); + if(enableRequest == 0 || activeProfilers.isEmpty()) + { + return this; + } + activeProfilers.pop().stop(); + return this; + } + + @Override + public IProfiler next(String name) + { + return stop().start(name); + } + + private void sendState(int state) + { + if(listeners.isEmpty()) + { + return; + } + for(ObjIntConsumer entry : listeners) + { + entry.accept(this, state); + } + } + + @Override + public void onFrameEnded(boolean end) + { + cpuSide.onFrameEnded(end); + if(enableRequest == 0) + { + return; + } + ticksRan++; + for(GPUProfilerEntry entry : cache.values()) + { + entry.onFrameFinished(end); + } + } + +} diff --git a/src/main/java/speiger/src/coreengine/utils/profiler/GPUProfilerEntry.java b/src/main/java/speiger/src/coreengine/utils/profiler/GPUProfilerEntry.java new file mode 100644 index 0000000..4aa513b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/profiler/GPUProfilerEntry.java @@ -0,0 +1,151 @@ +package speiger.src.coreengine.utils.profiler; + +import java.util.ArrayList; +import java.util.List; + +import speiger.src.collections.longs.queues.LongArrayFIFOQueue; +import speiger.src.collections.longs.queues.LongPriorityQueue; +import speiger.src.collections.objects.queues.ObjectArrayFIFOQueue; +import speiger.src.collections.objects.queues.ObjectPriorityDequeue; +import speiger.src.coreengine.rendering.utils.GLStamper; +import speiger.src.coreengine.rendering.utils.GLStamper.GLStamp; +import speiger.src.coreengine.utils.profiler.IProfiler.IProfilerEntry; + +public class GPUProfilerEntry implements IProfilerEntry +{ + String name; + String pathName; + IProfiler owner; + IProfilerEntry parent; + List children = new ArrayList(); + int debth = 0; + int totalTicks = 0; + + long maxTime = Long.MIN_VALUE; + long minTime = Long.MAX_VALUE; + long average = 0L; + LongPriorityQueue entries = new LongArrayFIFOQueue(); + long startTime = 0L; + long currentTime = 0L; + + ObjectPriorityDequeue stamps = new ObjectArrayFIFOQueue(); + + public GPUProfilerEntry(String name, IProfiler owner) + { + this.name = name; + pathName = name; + this.owner = owner; + } + + public void addChild(GPUProfilerEntry child) + { + if(child != null) + { + children.add(child); + child.setParent(this); + } + } + + private void setParent(GPUProfilerEntry parent) + { + this.parent = parent; + debth = parent.debth + 1; + pathName = parent.pathName+"/"+name; + } + + public void start() + { + stamps.enqueue(GLStamper.INSTANCE.createStamp(owner.getName()).start()); + } + + public void stop() + { + stamps.last().stop(); + } + + public void onFrameFinished(boolean end) + { + while(!stamps.isEmpty() && stamps.first().isFinished()) + { + GLStamp stamp = stamps.dequeue(); + currentTime += stamp.getResult(); + stamp.release(); + } + average += currentTime; + entries.enqueue(currentTime); + if(entries.size() > 20) + { + average -= entries.dequeue(); + } + currentTime = average / entries.size(); + minTime = Math.min(minTime, currentTime); + maxTime = Math.max(maxTime, currentTime); + currentTime = 0; + if(end && totalTicks++ >= 10) + { + minTime = Long.MAX_VALUE; + maxTime = Long.MIN_VALUE; + totalTicks = 0; + } + } + + @Override + public String getName() + { + return name; + } + + @Override + public String getPathName() + { + return pathName; + } + + @Override + public int getDebth() + { + return debth; + } + + @Override + public IProfiler getOwner() + { + return owner; + } + + @Override + public IProfilerEntry getParent() + { + return parent; + } + + @Override + public int getChildCount() + { + return children.size(); + } + + @Override + public IProfilerEntry getChild(int index) + { + return children.get(index); + } + + @Override + public long getMinTime() + { + return minTime; + } + + @Override + public long getNanoTime() + { + return entries.isEmpty() ? 0L : average / entries.size(); + } + + @Override + public long getMaxTime() + { + return maxTime; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/profiler/IProfiler.java b/src/main/java/speiger/src/coreengine/utils/profiler/IProfiler.java new file mode 100644 index 0000000..0d1142a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/profiler/IProfiler.java @@ -0,0 +1,164 @@ +package speiger.src.coreengine.utils.profiler; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.ObjIntConsumer; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.utils.helpers.TextUtil; + +public interface IProfiler +{ + + public String getName(); + + public IProfilerEntry getEntry(String name); + + public long getTicksRan(); + + public void enable(); + + public void disable(); + + public default void setState(boolean enabled) + { + if(enabled) + { + enable(); + return; + } + disable(); + } + + public boolean isEnabled(); + + public void addListener(ObjIntConsumer listener); + + public void removeListener(ObjIntConsumer listener); + + public IProfiler start(String name); + + public IProfiler start(Class clz); + + public IProfiler stop(); + + public IProfiler next(String name); + + public void onFrameEnded(boolean count); + + public static interface IProfilerEntry + { + public String getName(); + + public default String getPathName() + { + return getName(); + } + + public long getMinTime(); + + public long getNanoTime(); + + public long getMaxTime(); + + public int getDebth(); + + public int getChildCount(); + + public IProfilerEntry getChild(int index); + + public IProfilerEntry getParent(); + + public IProfiler getOwner(); + + default long getTotalTime() + { + IProfilerEntry entry = this; + while(entry.getParent() != null) + { + entry = entry.getParent(); + } + return entry.getNanoTime(); + } + + public default List getData() + { + List list = new ArrayList(); + long time = getNanoTime(); + long totalTime = getTotalTime(); + long used = 0L; + for(int i = 0;i + { + String name; + ColorObject color; + long nanoTime; + double effect; + double totalEffect; + + public ProfilerData(String name, long time, double effect, double totalEffect) + { + this.name = name; + color = TextUtil.getColorFromText(name); + nanoTime = time; + this.effect = effect; + this.totalEffect = totalEffect; + } + + @Override + public int compareTo(ProfilerData o) + { + if(o.nanoTime > nanoTime) + { + return 1; + } + return o.nanoTime < nanoTime ? -1 : name.compareTo(o.name); + } + + public String getName() + { + return name; + } + + public ColorObject getColor() + { + return color; + } + + public long getNanoTime() + { + return nanoTime; + } + + public double getEffect() + { + return effect; + } + + public double getTotalEffect() + { + return totalEffect; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/profiler/Profiler.java b/src/main/java/speiger/src/coreengine/utils/profiler/Profiler.java new file mode 100644 index 0000000..c39cf6e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/profiler/Profiler.java @@ -0,0 +1,165 @@ +package speiger.src.coreengine.utils.profiler; + +import java.util.Map; +import java.util.Set; +import java.util.function.ObjIntConsumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet; +import speiger.src.collections.utils.Stack; + +public class Profiler implements IProfiler +{ + String name; + Map cache = new Object2ObjectLinkedOpenHashMap(); + Stack activeProfilers = new ObjectArrayList(); + Set> listeners = new ObjectLinkedOpenHashSet<>(); + long ticksRan = 0; + int enableRequest = 0; + + public Profiler(String profilerName) + { + name = profilerName; + } + + @Override + public String getName() + { + return name; + } + + @Override + public IProfilerEntry getEntry(String name) + { + return cache.get(name); + } + + @Override + public long getTicksRan() + { + return ticksRan; + } + + @Override + public void enable() + { + enableRequest++; + if(enableRequest == 1) + { + sendState(0); + } + } + + @Override + public void disable() + { + enableRequest = Math.max(0, enableRequest-1); + if(enableRequest > 0) + { + return; + } + cache.clear(); + while(!activeProfilers.isEmpty()) + { + activeProfilers.pop(); + } + sendState(1); + } + + @Override + public boolean isEnabled() + { + return enableRequest > 0; + } + + @Override + public void addListener(ObjIntConsumer listener) + { + listeners.add(listener); + if(enableRequest > 0) + { + listener.accept(this, 0); + } + } + + @Override + public void removeListener(ObjIntConsumer listener) + { + listeners.remove(listener); + } + + @Override + public IProfiler start(String name) + { + if(enableRequest == 0) + { + return this; + } + String entry = (activeProfilers.isEmpty() ? "" : activeProfilers.top().getPathName()+"/") + name; + ProfilerEntry active = cache.get(entry); + if(active == null) + { + active = new ProfilerEntry(name, this); + if(!activeProfilers.isEmpty()) + { + activeProfilers.top().addChild(active); + } + cache.put(entry, active); + sendState(2); + } + activeProfilers.push(active); + active.start(); + return this; + } + + @Override + public IProfiler start(Class clz) + { + return enableRequest == 0 ? this : start(clz.getSimpleName()); + } + + @Override + public IProfiler stop() + { + if(enableRequest == 0 || activeProfilers.isEmpty()) + { + return this; + } + activeProfilers.pop().stop(); + return this; + } + + @Override + public IProfiler next(String name) + { + return stop().start(name); + } + + private void sendState(int state) + { + if(listeners.isEmpty()) + { + return; + } + for(ObjIntConsumer entry : listeners) + { + entry.accept(this, state); + } + } + + @Override + public void onFrameEnded(boolean end) + { + if(enableRequest == 0) + { + return; + } + ticksRan++; + for(ProfilerEntry entry : cache.values()) + { + entry.onFrameFinished(end); + } + } + +} diff --git a/src/main/java/speiger/src/coreengine/utils/profiler/ProfilerEntry.java b/src/main/java/speiger/src/coreengine/utils/profiler/ProfilerEntry.java new file mode 100644 index 0000000..1ff5e32 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/profiler/ProfilerEntry.java @@ -0,0 +1,152 @@ +package speiger.src.coreengine.utils.profiler; + +import java.util.ArrayList; +import java.util.List; + +import speiger.src.collections.longs.queues.LongArrayFIFOQueue; +import speiger.src.collections.longs.queues.LongPriorityQueue; +import speiger.src.coreengine.utils.profiler.IProfiler.IProfilerEntry; + +public class ProfilerEntry implements IProfilerEntry +{ + String name; + String pathName; + IProfiler owner; + IProfilerEntry parent; + List children = new ArrayList(); + int debth = 0; + int totalTicks = 0; + + long maxTime = Long.MIN_VALUE; + long minTime = Long.MAX_VALUE; + long average = 0L; + LongPriorityQueue entries = new LongArrayFIFOQueue(); + long startTime = 0L; + long currentTime = 0L; + boolean didTick = false; + + public ProfilerEntry(String name, IProfiler owner) + { + this.name = name; + pathName = name; + this.owner = owner; + } + + public void addChild(ProfilerEntry child) + { + if(child != null) + { + children.add(child); + child.setParent(this); + } + } + + private void setParent(ProfilerEntry parent) + { + this.parent = parent; + debth = parent.debth + 1; + pathName = parent.pathName+"/"+name; + } + + public void start() + { + startTime = System.nanoTime(); + } + + public void stop() + { + currentTime += (System.nanoTime() - startTime); + didTick = true; + } + + public void onFrameFinished(boolean end) + { + if(!didTick) + { + if(end && totalTicks++ >= 10) + { + minTime = 0; + maxTime = 0; + totalTicks = 0; + } + return; + } + didTick = false; + average += currentTime; + entries.enqueue(currentTime); + if(entries.size() > 20) + { + average -= entries.dequeue(); + } + currentTime = average / entries.size(); + minTime = Math.min(minTime, currentTime); + maxTime = Math.max(maxTime, currentTime); + currentTime = 0; + if(end && totalTicks++ >= 10) + { + minTime = Long.MAX_VALUE; + maxTime = Long.MIN_VALUE; + totalTicks = 0; + } + } + + @Override + public String getName() + { + return name; + } + + @Override + public String getPathName() + { + return pathName; + } + + @Override + public long getMinTime() + { + return minTime; + } + + @Override + public long getNanoTime() + { + return entries.isEmpty() ? 0L : average / entries.size(); + } + + @Override + public long getMaxTime() + { + return maxTime; + } + + @Override + public int getDebth() + { + return debth; + } + + @Override + public int getChildCount() + { + return children.size(); + } + + @Override + public IProfilerEntry getChild(int index) + { + return children.get(index); + } + + @Override + public IProfilerEntry getParent() + { + return parent; + } + + @Override + public IProfiler getOwner() + { + return owner; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/tasks/ArrayRunCall.java b/src/main/java/speiger/src/coreengine/utils/tasks/ArrayRunCall.java new file mode 100644 index 0000000..0a478fa --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/tasks/ArrayRunCall.java @@ -0,0 +1,24 @@ +package speiger.src.coreengine.utils.tasks; + +import java.util.concurrent.Callable; + +public class ArrayRunCall implements Callable +{ + Callable[] calls; + + public ArrayRunCall(Callable... calls) + { + this.calls = calls; + } + + @Override + public Runnable call() throws Exception + { + Runnable[] array = new Runnable[calls.length]; + for(int i = 0,m=array.length;i implements Runnable +{ + Callable call; + Consumer listener; + boolean canceled = false; + + public ListenableTask(Callable call, Consumer listener) + { + this.call = call; + this.listener = listener; + } + + @Override + public void run() + { + if(!canceled) + { + try + { + T value = call.call(); + if(value == null) + { + return; + } + listener.accept(value); + } + catch(Exception e){} + } + } + + public void cancel() + { + canceled = true; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/tasks/MainThreadTaskProcessor.java b/src/main/java/speiger/src/coreengine/utils/tasks/MainThreadTaskProcessor.java new file mode 100644 index 0000000..61a269a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/tasks/MainThreadTaskProcessor.java @@ -0,0 +1,146 @@ +package speiger.src.coreengine.utils.tasks; + +import speiger.src.collections.objects.queues.ObjectArrayFIFOQueue; +import speiger.src.collections.objects.queues.ObjectPriorityDequeue; +import speiger.src.collections.objects.utils.ObjectPriorityQueues; +import speiger.src.coreengine.utils.counters.timers.CountdownSync; + +public class MainThreadTaskProcessor +{ + ObjectPriorityDequeue tasks = ObjectPriorityQueues.synchronize(new ObjectArrayFIFOQueue()); + Watchdog watch; + Thread watchThread; + boolean running = false; + long timeout; + + public MainThreadTaskProcessor(long timeout, String name) + { + this.timeout = timeout; + watch = new Watchdog(Thread.currentThread()); + watchThread = new Thread(watch, name+"-Thread-Watchdog"); + watchThread.start(); + } + + public void setTimeout(long timeout) + { + this.timeout = timeout; + } + + public void addTask(ITask task) + { + tasks.enqueue(task); + } + + public void finishAllTasks() + { + if(tasks.isEmpty()) + { + return; + } + running = true; + while(!tasks.isEmpty()) + { + try + { + tasks.dequeue().execute(); + } + catch(InterruptedException e) + { + } + catch(Exception e) + { + e.printStackTrace(); + } + } + running = false; + } + + public void update() + { + if(tasks.isEmpty()) + { + return; + } + watch.unlock(); + running = true; + boolean interrupted = false; + while(!tasks.isEmpty() && !(interrupted |= Thread.interrupted())) + { + ITask task = tasks.dequeue(); + try + { + task.execute(); + } + catch(InterruptedException e) + { + interrupted = true; + } + catch(Exception e) + { + e.printStackTrace(); + } + if(!task.isFinished()) + { + tasks.enqueue(task); + } + } + running = false; + if(!interrupted) + { + watchThread.interrupt(); + } + } + + public void kill() + { + if(watchThread != null) + { + return; + } + watch.alive = false; + watchThread.interrupt(); + watchThread = null; + } + + class Watchdog implements Runnable + { + Thread owner; + boolean alive = true; + CountdownSync timer = new CountdownSync(); + Object lock = new Object(); + + public Watchdog(Thread thread) + { + owner = thread; + } + + @Override + public void run() + { + while(alive) + { + try + { + synchronized(lock) + { + lock.wait(); + } + timer.sync(timeout); + if(running) + { + owner.interrupt(); + } + } + catch(InterruptedException e) {} + } + } + + public void unlock() + { + synchronized(lock) + { + lock.notify(); + } + } + } +}