├── .github
├── ISSUE_TEMPLATE
│ ├── api-bug-report.md
│ ├── buildscript-error.md
│ ├── feature_request.md
│ └── library-crash.md
└── workflows
│ └── publish.yml
├── .gitignore
├── LICENSE
├── README.md
├── build.gradle
├── coreLib
├── build.gradle
└── src
│ ├── main
│ └── java
│ │ └── dev
│ │ └── kosmx
│ │ └── playerAnim
│ │ ├── api
│ │ ├── AnimUtils.java
│ │ ├── IPlayable.java
│ │ ├── IPlayer.java
│ │ ├── TransformType.java
│ │ ├── firstPerson
│ │ │ ├── FirstPersonConfiguration.java
│ │ │ └── FirstPersonMode.java
│ │ ├── layered
│ │ │ ├── AnimationContainer.java
│ │ │ ├── AnimationStack.java
│ │ │ ├── IActualAnimation.java
│ │ │ ├── IAnimation.java
│ │ │ ├── KeyframeAnimationPlayer.java
│ │ │ ├── ModifierLayer.java
│ │ │ ├── PlayerAnimationFrame.java
│ │ │ └── modifier
│ │ │ │ ├── AbstractFadeModifier.java
│ │ │ │ ├── AbstractModifier.java
│ │ │ │ ├── AdjustmentModifier.java
│ │ │ │ ├── FirstPersonModifier.java
│ │ │ │ ├── MirrorModifier.java
│ │ │ │ └── SpeedModifier.java
│ │ └── package-info.java
│ │ └── core
│ │ ├── data
│ │ ├── AnimationBinary.java
│ │ ├── AnimationFormat.java
│ │ ├── KeyframeAnimation.java
│ │ ├── gson
│ │ │ ├── AnimationJson.java
│ │ │ ├── AnimationSerializing.java
│ │ │ └── GeckoLibSerializer.java
│ │ ├── opennbs
│ │ │ ├── NBS.java
│ │ │ ├── NBSFileUtils.java
│ │ │ ├── SoundPlayer.java
│ │ │ ├── format
│ │ │ │ ├── CustomInstrument.java
│ │ │ │ ├── Header.java
│ │ │ │ ├── Layer.java
│ │ │ │ └── package-info.java
│ │ │ ├── network
│ │ │ │ └── NBSPacket.java
│ │ │ └── package-info.java
│ │ └── quarktool
│ │ │ ├── InverseEase.java
│ │ │ ├── Move.java
│ │ │ ├── PartMap.java
│ │ │ ├── Pause.java
│ │ │ ├── Pauseable.java
│ │ │ ├── Playable.java
│ │ │ ├── QuarkParsingError.java
│ │ │ ├── QuarkReader.java
│ │ │ ├── Repeat.java
│ │ │ ├── Reset.java
│ │ │ ├── Section.java
│ │ │ └── Yoyo.java
│ │ ├── impl
│ │ ├── AnimationProcessor.java
│ │ └── event
│ │ │ ├── Event.java
│ │ │ └── EventResult.java
│ │ └── util
│ │ ├── Ease.java
│ │ ├── Easing.java
│ │ ├── MathHelper.java
│ │ ├── NetworkHelper.java
│ │ ├── Pair.java
│ │ ├── SetableSupplier.java
│ │ ├── UUIDMap.java
│ │ ├── Vec3d.java
│ │ ├── Vec3f.java
│ │ └── Vector3.java
│ └── test
│ └── java
│ └── dev
│ └── kosmx
│ └── playerAnim
│ └── core
│ └── data
│ ├── KeyframeAnimationTest.java
│ ├── LayerTest.java
│ └── StringTest.java
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── icon.png
├── minecraft
├── build.gradle
├── common
│ ├── build.gradle
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── dev
│ │ │ └── kosmx
│ │ │ └── playerAnim
│ │ │ ├── impl
│ │ │ ├── Helper.java
│ │ │ ├── IAnimatedPlayer.java
│ │ │ ├── IMutableModel.java
│ │ │ ├── IPlayerModel.java
│ │ │ ├── IUpperPartHelper.java
│ │ │ ├── animation
│ │ │ │ ├── AnimationApplier.java
│ │ │ │ ├── BendHelper.java
│ │ │ │ └── IBendHelper.java
│ │ │ ├── compat
│ │ │ │ └── skinLayers
│ │ │ │ │ └── SkinLayersTransformer.java
│ │ │ └── mixin
│ │ │ │ └── MixinConfig.java
│ │ │ ├── minecraftApi
│ │ │ ├── PlayerAnimationAccess.java
│ │ │ ├── PlayerAnimationFactory.java
│ │ │ ├── PlayerAnimationRegistry.java
│ │ │ ├── codec
│ │ │ │ ├── AbstractGsonCodec.java
│ │ │ │ ├── AnimationCodec.java
│ │ │ │ ├── AnimationCodecs.java
│ │ │ │ ├── AnimationDecoder.java
│ │ │ │ ├── AnimationEncoder.java
│ │ │ │ ├── EmotecraftGsonCodec.java
│ │ │ │ └── LegacyGeckoJsonCodec.java
│ │ │ └── layers
│ │ │ │ └── LeftHandedHelperModifier.java
│ │ │ └── mixin
│ │ │ ├── ArmorFeatureRendererMixin.java
│ │ │ ├── BipedEntityModelMixin.java
│ │ │ ├── CapeLayerMixin.java
│ │ │ ├── ElytraLayerMixin.java
│ │ │ ├── FeatureRendererMixin.java
│ │ │ ├── HeldItemMixin.java
│ │ │ ├── LivingEntityRenderRedirect_bendOnly.java
│ │ │ ├── ModelPartMixin.java
│ │ │ ├── PlayerEntityMixin.java
│ │ │ ├── PlayerModelAccessor.java
│ │ │ ├── PlayerModelMixin.java
│ │ │ ├── PlayerRendererMixin.java
│ │ │ └── firstPerson
│ │ │ ├── CameraAccessor.java
│ │ │ ├── EntityRenderDispatcherMixin.java
│ │ │ ├── ItemInHandRendererMixin.java
│ │ │ ├── LevelRendererMixin.java
│ │ │ └── LivingEntityRendererMixin.java
│ │ └── resources
│ │ ├── architectury.common.json
│ │ ├── playerAnimator-common.mixins.json
│ │ └── playerAnimator.accesswidener
├── fabric
│ ├── build.gradle
│ └── src
│ │ ├── main
│ │ ├── java
│ │ │ └── dev
│ │ │ │ └── kosmx
│ │ │ │ └── playerAnim
│ │ │ │ ├── fabric
│ │ │ │ └── client
│ │ │ │ │ └── FabricClientInitializer.java
│ │ │ │ └── impl
│ │ │ │ └── fabric
│ │ │ │ └── HelperImpl.java
│ │ └── resources
│ │ │ ├── assets
│ │ │ └── player-animator
│ │ │ │ └── lang
│ │ │ │ └── tt_ru.json
│ │ │ └── fabric.mod.json
│ │ ├── test.md
│ │ └── testmod
│ │ ├── java
│ │ └── dev
│ │ │ └── kosmx
│ │ │ └── animatorTestmod
│ │ │ ├── PlayerAnimTestmod.java
│ │ │ └── mixin
│ │ │ ├── ClientPlayerMixin.java
│ │ │ └── MinecraftClientMixin.java
│ │ └── resources
│ │ ├── assets
│ │ └── testmod
│ │ │ └── player_animations
│ │ │ ├── note.md
│ │ │ ├── player.animation.json
│ │ │ ├── two_handed_slash_horizontal_right.json
│ │ │ └── two_handed_slash_vertical_right.json
│ │ ├── fabric.mod.json
│ │ └── testmod.mixins.json
├── forge
│ ├── build.gradle
│ ├── gradle.properties
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── dev
│ │ │ └── kosmx
│ │ │ └── playerAnim
│ │ │ ├── forge
│ │ │ └── ForgeClientEvent.java
│ │ │ └── impl
│ │ │ └── neoforge
│ │ │ └── HelperImpl.java
│ │ └── resources
│ │ └── META-INF
│ │ └── neoforge.mods.toml
└── gradle.properties
└── settings.gradle
/.github/ISSUE_TEMPLATE/api-bug-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: API bug report
3 | about: Issue about the API (not the buildscript)
4 | title: "[API]"
5 | labels: bug
6 | assignees: KosmX
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Setup config (minecraft, modloader, libraries):**
27 | - Minecraft version: [e.g. 1.19.3]
28 | - Mod loader: [e.g. Fabric 0.14.11]
29 | - PlayerAnimator Library version [e.g. 0.4.0+1.19.3]
30 | - MC library version (e.g. Fabric API version)
31 |
32 | **Other mods (If you use other mods, please specify! it might be an incompatibility):**
33 |
34 | *Optional:*
35 | ** Used library methods, ideas (If you're a developer, what function is not working as expected)**
36 | Add any other context about the problem here.
37 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/buildscript-error.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Buildscript error
3 | about: Something is wrong with buildscript
4 | title: ''
5 | labels: build
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **Buildscript content or link to content (If you don't share your code, I cannot debug it):**
14 | https://github.com/KosmX/fabricPlayerAnimatorExample/blob/1.19/build.gradle
15 |
16 | **To Reproduce**
17 | Steps to reproduce the behavior:
18 | 1. Run `gradle runClient`
19 | 2. See error
20 |
21 | **Expected behavior**
22 | A clear and concise description of what you expected to happen.
23 |
24 | **Environment (please complete the following information):**
25 | - OS: [e.g. Windows 11, Archlinux, Ubuntu 22.04]
26 | - IDE [e.g. IntelliJ, Eclipse, vim+bash]
27 | - Gradle version [e.g. 7.5.1]
28 | - JDK version [e.g. 17.0.5 - Adoptium]
29 | - gradle plugin [e.g. Fabric loom, ForgeGradle] (if I don't see it in your project)
30 | - Minecraft, Loader: (Fabric 0.14.11+1.19.2)
31 |
32 | **Additional context**
33 | Add any other context about the problem here.
34 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/library-crash.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Library crash
3 | about: User crash report
4 | title: ''
5 | labels: bug, library
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Screenshots**
21 | If applicable, add screenshots to help explain your problem.
22 |
23 | **Crash report (please copy the whole crash report!)**
24 | ```
25 | Minecraft 1.19.3
26 | ...
27 | ```
28 |
29 | **Logs (if you have logs from that run, please share it):**
30 | ```
31 | ...
32 | ```
33 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 |
2 | name: Publish
3 |
4 | on:
5 | workflow_dispatch:
6 | inputs:
7 | type:
8 | description: 'release type'
9 | required: false
10 | type: choice
11 | options:
12 | - alpha
13 | - beta
14 | - release
15 | changelog:
16 | description: 'changelog'
17 | required: false
18 | default: ''
19 | upload:
20 | required: true
21 | type: choice
22 | options:
23 | - true
24 | - false
25 |
26 |
27 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
28 | jobs:
29 | # This workflow contains a single job called "build"
30 | publish:
31 | # The type of runner that the job will run on
32 | runs-on: ubuntu-latest
33 |
34 | # Steps represent a sequence of tasks that will be executed as part of the job
35 | steps:
36 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
37 | - name: Check for valid input
38 | run: |
39 | if ! ( [ "${{github.event.inputs.type}}" = "alpha" ] || [ "${{github.event.inputs.type}}" = "beta" ] || [ "${{github.event.inputs.type}}" = "release" ] )
40 | then
41 | return -1
42 | fi
43 | - uses: actions/checkout@v3
44 | - uses: actions/setup-java@v3
45 | with:
46 | distribution: 'microsoft'
47 | java-version: '21'
48 |
49 | # Runs a single command using the runners shell
50 | - name: validate gradle wrapper
51 | uses: gradle/wrapper-validation-action@v1
52 | - name: make gradle wrapper executable
53 | if: ${{ runner.os != 'Windows' }}
54 | run: chmod +x ./gradlew
55 | - name: Publish package
56 | run: ./gradlew publish --no-daemon
57 | env:
58 | KOSMX_MAVEN_USER: ${{ secrets.MAVEN_USER }}
59 | KOSMX_MAVEN_TOKEN: ${{ secrets.MAVEN_PASS }}
60 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
61 | MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
62 | CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }}
63 | CHANGELOG: ${{github.event.inputs.changelog}}
64 | RELEASE_TYPE: ${{github.event.inputs.type}}
65 | UPLOAD_TO_PORTAL: ${{github.event.inputs.upload}}
66 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | *.ipr
3 | run/
4 | *.iws
5 | out/
6 | *.iml
7 | .gradle/
8 | output/
9 | bin/
10 | libs/
11 |
12 | .classpath
13 | .project
14 | .idea/
15 | classes/
16 | .metadata
17 | .vscode
18 | .settings
19 | *.launch
20 | .architectury-transformer
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 KosmX
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "architectury-plugin" version "3.4-SNAPSHOT"
3 | id "dev.architectury.loom" version "1.7-SNAPSHOT" apply false
4 |
5 | //Publishing
6 | id 'com.matthewprenger.cursegradle' version '1.4.0' apply false
7 | id "com.modrinth.minotaur" version "2.7.4" apply false
8 | id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
9 | }
10 |
11 | architectury {
12 | minecraft = rootProject.minecraft_version
13 | }
14 |
15 | project.mod_version = mod_version + "+" + minecraft_version
16 |
17 | allprojects {
18 | apply plugin: "java"
19 | apply plugin: "architectury-plugin"
20 | apply plugin: "maven-publish"
21 |
22 | group = rootProject.maven_group
23 | archivesBaseName = rootProject.archives_base_name
24 | version = rootProject.mod_version
25 |
26 | repositories {
27 | maven { url "https://maven.neoforged.net/releases" }
28 | mavenCentral()
29 | maven {
30 | name 'KosmX\'s maven'
31 | url 'https://maven.kosmx.dev/'
32 | }
33 | exclusiveContent {
34 | forRepository {
35 | maven {
36 | name = "Modrinth"
37 | url = "https://api.modrinth.com/maven"
38 | }
39 | }
40 | filter {
41 | includeGroup "maven.modrinth"
42 | }
43 | }
44 | }
45 |
46 | dependencies {
47 | compileOnly 'com.google.code.findbugs:jsr305:3.0.2'
48 | }
49 |
50 | tasks.withType(JavaCompile) {
51 | options.encoding = "UTF-8"
52 | options.release = 21
53 | }
54 |
55 | java {
56 | withSourcesJar()
57 | }
58 | }
59 |
60 | ext.ENV = System.getenv()
61 |
62 | ext.cfType = ENV.RELEASE_TYPE ? ENV.RELEASE_TYPE : "alpha"
63 | ext.changes = ENV.CHANGELOG ? ENV.CHANGELOG.replaceAll("\\\\n", "\n") : ""
64 | ext.ENV = System.getenv()
65 | boolean upload = ENV.UPLOAD_TO_PORTAL ? ENV.UPLOAD_TO_PORTAL == "true" : false
66 |
67 | ext.keysExists = ENV.KOSMX_MAVEN_USER != null || project.getGradle().getStartParameter().isDryRun()
68 |
69 | if(keysExists) {
70 | project.ext.keys = new Properties()
71 | if (project.getGradle().getStartParameter().isDryRun()) {
72 | println("Dry run, loading publish scripts")
73 | //All of these are fake, don't waste your time with it. (Copied from API docs and random generated)
74 | project.ext.keys.modrinth_token = "gho_pJ9dGXVKpfzZp4PUHSxYEq9hjk0h288Gwj4S"
75 | project.ext.keys.curseforge_key = "00000000-0000-0000-0000-000000000000"
76 | project.ext.keys.kosmx_maven = "V2h5IGRpZCB5b3UgZGVjb2RlIGl0PyAg"
77 | project.ext.keys.kosmx_maven_user = "username"
78 | } else {
79 | println("Keys loaded, loading publish scripts")
80 | project.ext.keys.modrinth_token = ENV.MODRINTH_TOKEN
81 | project.ext.keys.curseforge_key = ENV.CURSEFORGE_TOKEN
82 | project.ext.keys.kosmx_maven = ENV.KOSMX_MAVEN_TOKEN
83 | project.ext.keys.kosmx_maven_user = ENV.KOSMX_MAVEN_USER
84 | }
85 |
86 | publish {
87 | if (upload) {
88 | finalizedBy(':minecraft:publishMod')
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/coreLib/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java-library'
3 | id 'maven-publish'
4 | //id "io.freefair.lombok" version "6.5.0.2" This breaks javadoc (???) I'll use annotationProcessor
5 | }
6 |
7 | dependencies {
8 |
9 | implementation 'com.google.code.gson:gson:2.8.9'
10 |
11 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
12 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
13 |
14 |
15 | compileOnly 'org.projectlombok:lombok:1.18.32'
16 | annotationProcessor 'org.projectlombok:lombok:1.18.32'
17 |
18 | testCompileOnly 'org.projectlombok:lombok:1.18.32'
19 | testAnnotationProcessor 'org.projectlombok:lombok:1.18.32'
20 | compileOnly 'org.jetbrains:annotations:23.0.0'
21 | }
22 |
23 | compileJava {
24 | options.release.set 17
25 | }
26 |
27 |
28 | test {
29 | useJUnitPlatform()
30 | }
31 |
32 | java {
33 | withJavadocJar()
34 | withSourcesJar()
35 | }
36 |
37 | publishing {
38 | publications {
39 | mavenJava(MavenPublication) {
40 | artifactId = 'anim-core'
41 |
42 | from components.java
43 |
44 | pom{
45 | name = "playerAnimatorCORE"
46 | description = "Minecraft animation api"
47 | url = 'https://github.com/KosmX/emotes'
48 | developers {
49 | developer {
50 | id = 'kosmx'
51 | name = 'KosmX'
52 | email = 'kosmx.mc@gmail.com'
53 | }
54 | }
55 | }
56 | }
57 | }
58 | repositories {
59 |
60 | if (project.keysExists) {
61 | maven {
62 | url = 'https://maven.kosmx.dev/'
63 | credentials {
64 | username = project.keys.kosmx_maven_user
65 | password = project.keys.kosmx_maven
66 | }
67 | }
68 | maven {
69 | name = "GitHubPackages"
70 | url = "https://maven.pkg.github.com/kosmx/minecraftPlayerAnimator"
71 | credentials {
72 | username = System.getenv("GITHUB_ACTOR")
73 | password = System.getenv("GITHUB_TOKEN")
74 | }
75 | }
76 | } else {
77 | mavenLocal()
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/api/AnimUtils.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.api;
2 |
3 | import dev.kosmx.playerAnim.api.layered.AnimationStack;
4 |
5 | public abstract class AnimUtils {
6 |
7 | /**
8 | * Get the animation stack for a player entity on the client.
9 | *
10 | * Or you can use {@code ((IPlayer) player).getAnimationStack();}
11 | *
12 | * @param player The ClientPlayer object
13 | * @return The players' animation stack
14 | */
15 | public static AnimationStack getPlayerAnimLayer(Object player) throws IllegalArgumentException {
16 | if (player instanceof IPlayer) {
17 | return ((IPlayer) player).getAnimationStack();
18 | } else throw new IllegalArgumentException(player + " is not a player or library mixins failed");
19 | }
20 |
21 | /**
22 | * Unused, use proper first person library instead
23 | * will be removed after next release (1.1)
24 | */
25 | @Deprecated
26 | public static boolean disableFirstPersonAnim = true;
27 | }
28 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/api/IPlayable.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.api;
2 |
3 | import dev.kosmx.playerAnim.api.layered.IActualAnimation;
4 | import org.jetbrains.annotations.NotNull;
5 |
6 | import java.util.UUID;
7 | import java.util.function.Supplier;
8 |
9 | /**
10 | * Animation that can be stored in animation registry.
11 | * It has a function to create a player
12 | */
13 | public interface IPlayable extends Supplier {
14 |
15 | @NotNull
16 | IActualAnimation> playAnimation();
17 |
18 | @NotNull String getName();
19 | }
20 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/api/IPlayer.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.api;
2 |
3 | import dev.kosmx.playerAnim.api.layered.AnimationStack;
4 |
5 | public interface IPlayer {
6 | AnimationStack getAnimationStack();
7 | }
8 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/api/TransformType.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.api;
2 |
3 | public enum TransformType {
4 | /**
5 | * The part is shifted in 3d space into the direction
6 | */
7 | POSITION,
8 | /**
9 | * The part is rotated in 3D space using Euler angles
10 | */
11 | ROTATION,
12 | /**
13 | * Bend the part, the vector should look like this: {bend planes rotation 0-2π, bend value, not defined}
14 | */
15 | BEND,
16 | /**
17 | * The part is scaled in 3d space
18 | */
19 | SCALE
20 | }
21 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/api/firstPerson/FirstPersonConfiguration.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.api.firstPerson;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 | import lombok.experimental.Accessors;
7 |
8 | @Data
9 | @NoArgsConstructor
10 | @AllArgsConstructor
11 | @Accessors(chain = true)
12 | public class FirstPersonConfiguration {
13 | boolean showRightArm = false;
14 | boolean showLeftArm = false;
15 | boolean showRightItem = true;
16 | boolean showLeftItem = true;
17 | boolean showArmor = false;
18 |
19 | public FirstPersonConfiguration(boolean showRightArm, boolean showLeftArm, boolean showRightItem, boolean showLeftItem) {
20 | this.showRightArm = showRightArm;
21 | this.showLeftArm = showLeftArm;
22 | this.showRightItem = showRightItem;
23 | this.showLeftItem = showLeftItem;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/api/firstPerson/FirstPersonMode.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.api.firstPerson;
2 |
3 | import lombok.Getter;
4 | import org.jetbrains.annotations.ApiStatus;
5 |
6 | public enum FirstPersonMode {
7 |
8 | /**
9 | * The animation does not decide first person mode, this way, the animation will be transparent in first person mode.
10 | */
11 | NONE(false),
12 | /**
13 | * Use the vanilla renderer, most of the time broken, if you use this, please check your animation
14 | */
15 | VANILLA(true),
16 |
17 | /**
18 | * Use the 3rd person player model (only arms/items/shoulder armor) to render accurate first-person perspective.
19 | * Note that armor rendering is disabled in the default FirstPersonConfiguration. {@link FirstPersonConfiguration#showShoulder}
20 | */
21 | THIRD_PERSON_MODEL(true),
22 |
23 | /**
24 | * First person animation is DISABLED, vanilla idle will be active.
25 | */
26 | DISABLED(false),
27 |
28 | ;
29 | @Getter
30 | private final boolean enabled;
31 |
32 |
33 | FirstPersonMode(boolean enabled) {
34 | this.enabled = enabled;
35 | }
36 |
37 |
38 |
39 | private static final ThreadLocal firstPersonPass = ThreadLocal.withInitial(() -> false);
40 |
41 |
42 | /**
43 | * @return is the current render pass a first person pass
44 | */
45 | public static boolean isFirstPersonPass() {
46 | return firstPersonPass.get();
47 | }
48 |
49 | @ApiStatus.Internal
50 | public static void setFirstPersonPass(boolean newValue) {
51 | firstPersonPass.set(newValue);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/api/layered/AnimationContainer.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.api.layered;
2 |
3 | import dev.kosmx.playerAnim.api.TransformType;
4 | import dev.kosmx.playerAnim.api.firstPerson.FirstPersonConfiguration;
5 | import dev.kosmx.playerAnim.api.firstPerson.FirstPersonMode;
6 | import dev.kosmx.playerAnim.core.util.Vec3f;
7 | import org.jetbrains.annotations.NotNull;
8 | import org.jetbrains.annotations.Nullable;
9 |
10 | /**
11 | * A container to make swapping animation object easier
12 | * It will clone the behaviour of the held animation
13 | *
14 | * you can put endless AnimationContainer into each other
15 | * @param Nullable animation
16 | */
17 | public class AnimationContainer implements IAnimation {
18 | @Nullable
19 | protected T anim;
20 |
21 | public AnimationContainer(@Nullable T anim) {
22 | this.anim = anim;
23 | }
24 |
25 | public AnimationContainer() {
26 | this.anim = null;
27 | }
28 |
29 | public void setAnim(@Nullable T newAnim) {
30 | this.anim = newAnim;
31 | }
32 |
33 | public @Nullable T getAnim() {
34 | return this.anim;
35 | }
36 |
37 | @Override
38 | public boolean isActive() {
39 | return anim != null && anim.isActive();
40 | }
41 |
42 | @Override
43 | public void tick() {
44 | if (anim != null) anim.tick();
45 | }
46 |
47 | @Override
48 | public @NotNull Vec3f get3DTransform(@NotNull String modelName, @NotNull TransformType type, float tickDelta, @NotNull Vec3f value0) {
49 | return anim == null ? value0 : anim.get3DTransform(modelName, type, tickDelta, value0);
50 | }
51 |
52 | @Override
53 | public void setupAnim(float tickDelta) {
54 | if (this.anim != null) this.anim.setupAnim(tickDelta);
55 | }
56 |
57 | @Override
58 | public @NotNull FirstPersonMode getFirstPersonMode(float tickDelta) {
59 | return anim != null ? anim.getFirstPersonMode(tickDelta) : FirstPersonMode.NONE;
60 | }
61 |
62 | // Override candidate
63 | @Override
64 | public @NotNull FirstPersonConfiguration getFirstPersonConfiguration(float tickDelta) {
65 | return anim != null ? anim.getFirstPersonConfiguration(tickDelta) : IAnimation.super.getFirstPersonConfiguration(tickDelta);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/api/layered/AnimationStack.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.api.layered;
2 |
3 | import dev.kosmx.playerAnim.api.TransformType;
4 | import dev.kosmx.playerAnim.api.firstPerson.FirstPersonConfiguration;
5 | import dev.kosmx.playerAnim.api.firstPerson.FirstPersonMode;
6 | import dev.kosmx.playerAnim.core.util.Pair;
7 | import dev.kosmx.playerAnim.core.util.Vec3f;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.ArrayList;
11 |
12 | /**
13 | * Player animation stack, can contain multiple active or passive layers, will always be evaluated from the lowest index.
14 | * Highest index = it can override everything else
15 | */
16 | public class AnimationStack implements IAnimation {
17 |
18 | private final ArrayList> layers = new ArrayList<>();
19 |
20 | /**
21 | *
22 | * @return true if exists level what is active.
23 | */
24 | @Override
25 | public boolean isActive() {
26 | for (Pair layer : layers) {
27 | if (layer.getRight().isActive()) return true;
28 | }
29 | return false;
30 | }
31 |
32 | @Override
33 | public void tick() {
34 | for (Pair layer : layers) {
35 | if (layer.getRight().isActive()) {
36 | layer.getRight().tick();
37 | }
38 | }
39 | }
40 |
41 | @Override
42 | public @NotNull Vec3f get3DTransform(@NotNull String modelName, @NotNull TransformType type, float tickDelta, @NotNull Vec3f value0) {
43 | for (Pair layer : layers) {
44 | if (layer.getRight().isActive() && (!FirstPersonMode.isFirstPersonPass() || layer.getRight().getFirstPersonMode(tickDelta).isEnabled())) {
45 | value0 = layer.getRight().get3DTransform(modelName, type, tickDelta, value0);
46 | }
47 | }
48 | return value0;
49 | }
50 |
51 | @Override
52 | public void setupAnim(float tickDelta) {
53 | for (Pair layer : layers) {
54 | layer.getRight().setupAnim(tickDelta);
55 | }
56 | }
57 |
58 |
59 | /**
60 | * Add an animation layer.
61 | * If there are multiple with the same priority, the one, added first will have larger priority
62 | * @param priority priority
63 | * @param layer animation layer
64 | * note: Same priority entries logic is subject to change
65 | */
66 | public void addAnimLayer(int priority, IAnimation layer) {
67 | int search = 0;
68 | //Insert the layer into the correct slot
69 | while (layers.size() > search && layers.get(search).getLeft() < priority) {
70 | search++;
71 | }
72 | layers.add(search, new Pair<>(priority, layer));
73 | }
74 |
75 | /**
76 | * Remove an animation layer
77 | * @param layer needle
78 | * @return true if any elements were removed.
79 | */
80 | public boolean removeLayer(IAnimation layer) {
81 | return layers.removeIf(integerIAnimationPair -> integerIAnimationPair.getRight() == layer);
82 | }
83 |
84 | /**
85 | * Remove EVERY layer with priority
86 | * @param layerLevel search and destroy
87 | * @return true if any elements were removed.
88 | */
89 | public boolean removeLayer(int layerLevel) {
90 | return layers.removeIf(integerIAnimationPair -> integerIAnimationPair.getLeft() == layerLevel);
91 | }
92 |
93 | @Override
94 | public @NotNull FirstPersonMode getFirstPersonMode(float tickDelta) {
95 | for (int i = layers.size(); i > 0;) {
96 | Pair layer = layers.get(--i);
97 | if (layer.getRight().isActive()) { // layer.right.requestFirstPersonMode(tickDelta).takeIf{ it != NONE }?.let{ return@requestFirstPersonMode it }
98 | FirstPersonMode mode = layer.getRight().getFirstPersonMode(tickDelta);
99 | if (mode != FirstPersonMode.NONE) return mode;
100 | }
101 | }
102 | return FirstPersonMode.NONE;
103 | }
104 |
105 | @Override
106 | public @NotNull FirstPersonConfiguration getFirstPersonConfiguration(float tickDelta) {
107 | for (int i = layers.size(); i > 0;) {
108 | Pair layer = layers.get(--i);
109 | if (layer.getRight().isActive()) { // layer.right.requestFirstPersonMode(tickDelta).takeIf{ it != NONE }?.let{ return@requestFirstPersonMode it }
110 | FirstPersonMode mode = layer.getRight().getFirstPersonMode(tickDelta);
111 | if (mode != FirstPersonMode.NONE) return layer.getRight().getFirstPersonConfiguration(tickDelta);
112 | }
113 | }
114 | return IAnimation.super.getFirstPersonConfiguration(tickDelta);
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/api/layered/IActualAnimation.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.api.layered;
2 |
3 | import dev.kosmx.playerAnim.api.firstPerson.FirstPersonConfiguration;
4 | import dev.kosmx.playerAnim.api.firstPerson.FirstPersonMode;
5 | import org.jetbrains.annotations.NotNull;
6 |
7 | /**
8 | * interface for some setters on animation players
9 | */
10 | public interface IActualAnimation> extends IAnimation {
11 |
12 | /**
13 | * @return this for chaining (fluent)
14 | */
15 | @NotNull
16 | T setFirstPersonMode(@NotNull FirstPersonMode firstPersonMode);
17 |
18 |
19 | @NotNull
20 | T setFirstPersonConfiguration(@NotNull FirstPersonConfiguration firstPersonConfiguration);
21 | }
22 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/api/layered/IAnimation.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.api.layered;
2 |
3 |
4 | import dev.kosmx.playerAnim.api.TransformType;
5 | import dev.kosmx.playerAnim.api.firstPerson.FirstPersonConfiguration;
6 | import dev.kosmx.playerAnim.api.firstPerson.FirstPersonMode;
7 | import dev.kosmx.playerAnim.core.util.Vec3f;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | /**
11 | * An entry in {@link AnimationStack}, used to get the animated parts current transform
12 | */
13 | public interface IAnimation {
14 |
15 | /**
16 | * Animation tick, on lag free client 20 [tick/sec]
17 | * You can get the animations time from other places, but it will be invoked when the animation is ACTIVE
18 | */
19 | default void tick(){}
20 |
21 | /**
22 | * Is the animation currently active.
23 | * Tick will only be invoked when ACTIVE
24 | * @return active
25 | */
26 | boolean isActive();
27 |
28 | /**
29 | * Get the transformed value to a model part, transform type.
30 | * @param modelName The questionable model part
31 | * @param type Transform type
32 | * @param tickDelta Time since the last tick. 0-1
33 | * @param value0 The value before the transform. For identity transform return with it.
34 | * @return The new transform value
35 | */
36 | @NotNull Vec3f get3DTransform(@NotNull String modelName, @NotNull TransformType type, float tickDelta, @NotNull Vec3f value0);
37 |
38 | /**
39 | * Called before rendering a character
40 | * @param tickDelta Time since the last tick. 0-1
41 | */
42 | void setupAnim(float tickDelta);
43 |
44 | /**
45 | * Active animation can request first person render mode.
46 | * @param tickDelta current tickDelta
47 | * @return {@link FirstPersonMode}
48 | */
49 | @NotNull
50 | default FirstPersonMode getFirstPersonMode(float tickDelta) {
51 | return FirstPersonMode.NONE;
52 | }
53 |
54 | /**
55 | * @param tickDelta
56 | * @return current first person configuration, only requested when playing this animation
57 | */
58 | @NotNull
59 | default FirstPersonConfiguration getFirstPersonConfiguration(float tickDelta) {
60 | return new FirstPersonConfiguration();
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/api/layered/PlayerAnimationFrame.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.api.layered;
2 |
3 | import dev.kosmx.playerAnim.api.TransformType;
4 | import dev.kosmx.playerAnim.core.util.Pair;
5 | import dev.kosmx.playerAnim.core.util.Vec3f;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import java.util.HashMap;
9 | import java.util.Map;
10 |
11 | /**
12 | * Mixin it into a player, add to its Animation stack,
13 | * and override its tick,
14 | *
15 | * It is a representation of your pose on the frame.
16 | * Override {@link IAnimation#setupAnim(float)} and set the pose there.
17 | */
18 | public abstract class PlayerAnimationFrame implements IAnimation {
19 |
20 | protected PlayerPart head = new PlayerPart();
21 | protected PlayerPart body = new PlayerPart();
22 | protected PlayerPart rightArm = new PlayerPart();
23 | protected PlayerPart leftArm = new PlayerPart();
24 | protected PlayerPart rightLeg = new PlayerPart();
25 | protected PlayerPart leftLeg = new PlayerPart();
26 | protected PlayerPart rightItem = new PlayerPart();
27 | protected PlayerPart leftItem = new PlayerPart();
28 |
29 | HashMap parts = new HashMap<>();
30 |
31 | public PlayerAnimationFrame() {
32 | parts.put("head", head);
33 | parts.put("body", body);
34 | parts.put("rightArm", rightArm);
35 | parts.put("leftArm", leftArm);
36 | parts.put("rightLeg", rightLeg);
37 | parts.put("leftLeg", leftLeg);
38 | parts.put("rightItem", rightItem);
39 | parts.put("leftItem", leftItem);
40 | }
41 |
42 |
43 | @Override
44 | public void tick() {
45 | IAnimation.super.tick();
46 | }
47 |
48 | @Override
49 | public boolean isActive() {
50 | for (Map.Entry entry: parts.entrySet()) {
51 | PlayerPart part = entry.getValue();
52 | if (part.bend != null || part.pos != null || part.rot != null || part.scale != null) return true;
53 | }
54 | return false;
55 | }
56 |
57 | /**
58 | * Reset every part, those parts won't influence the animation
59 | * Don't use it if you don't want to set every part in every frame
60 | */
61 | public void resetPose() {
62 | for (Map.Entry entry: parts.entrySet()) {
63 | entry.getValue().setNull();
64 | }
65 | }
66 |
67 |
68 | @Override
69 | public @NotNull Vec3f get3DTransform(@NotNull String modelName, @NotNull TransformType type, float tickDelta, @NotNull Vec3f value0) {
70 | PlayerPart part = parts.get(modelName);
71 | if (part == null) return value0;
72 | switch (type) {
73 | case POSITION:
74 | return part.pos == null ? value0 : part.pos;
75 | case ROTATION:
76 | return part.rot == null ? value0 : part.rot;
77 | case SCALE:
78 | return part.scale == null ? value0 : part.scale;
79 | case BEND:
80 | return part.bend == null ? value0 : new Vec3f(part.bend.getLeft(), part.bend.getRight(), 0f);
81 | default:
82 | return value0;
83 | }
84 | }
85 |
86 | public static class PlayerPart {
87 | public Vec3f pos;
88 | public Vec3f scale;
89 | public Vec3f rot;
90 | public Pair bend;
91 |
92 | protected void setNull() {
93 | pos = scale = rot = null;
94 | bend = null;
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/api/layered/modifier/AbstractFadeModifier.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.api.layered.modifier;
2 |
3 | import dev.kosmx.playerAnim.api.TransformType;
4 | import dev.kosmx.playerAnim.api.layered.IAnimation;
5 | import dev.kosmx.playerAnim.core.util.Ease;
6 | import dev.kosmx.playerAnim.core.util.Vec3f;
7 | import lombok.Setter;
8 | import org.jetbrains.annotations.NotNull;
9 | import org.jetbrains.annotations.Nullable;
10 |
11 | /**
12 | * Use with ModifierLayer.
13 | * Set an animation as a fade from.
14 | * length in ticks
15 | */
16 | public abstract class AbstractFadeModifier extends AbstractModifier {
17 |
18 | protected int time = 0;
19 | protected int length;
20 |
21 | /**
22 | * Animation to move from, null if transparent (easing in)
23 | * use setBeginAnimation(IAnimation); //generated by Lombok plugin
24 | */
25 | @Nullable
26 | @Setter
27 | protected IAnimation beginAnimation;
28 |
29 | protected AbstractFadeModifier(int length) {
30 | this.length = length;
31 | }
32 |
33 | @Override
34 | public boolean isActive() {
35 | return super.isActive() || beginAnimation != null && beginAnimation.isActive();
36 | }
37 |
38 | @Override
39 | public boolean canRemove() {
40 | return this.length <= time;
41 | }
42 |
43 | @Override
44 | public void setupAnim(float tickDelta) {
45 | super.setupAnim(tickDelta);
46 | if (beginAnimation != null) beginAnimation.setupAnim(tickDelta);
47 | }
48 |
49 | @Override
50 | public void tick() {
51 | super.tick();
52 | if (beginAnimation != null) beginAnimation.tick();
53 | this.time++;
54 | }
55 |
56 | @Override
57 | public @NotNull Vec3f get3DTransform(@NotNull String modelName, @NotNull TransformType type, float tickDelta, @NotNull Vec3f value0) {
58 | if (calculateProgress(tickDelta) > 1) {
59 | return super.get3DTransform(modelName, type, tickDelta, value0);
60 | }
61 | Vec3f animatedVec = super.get3DTransform(modelName, type, tickDelta, value0);
62 | float a = getAlpha(modelName, type, calculateProgress(tickDelta));
63 | Vec3f source = beginAnimation != null && beginAnimation.isActive() ? beginAnimation.get3DTransform(modelName, type, tickDelta, value0) : value0;
64 | return animatedVec.scale(a).add(source.scale(1 - a)); //This would look much better in Kotlin... (operator overloading)
65 | }
66 |
67 | protected float calculateProgress(float f) {
68 | float actualTime = time + f;
69 | return actualTime / length;
70 | }
71 |
72 | /**
73 | * Get the alpha at the given progress
74 | * @param modelName modelName if you want to handle parts differently
75 | * @param type Transform type
76 | * @param progress animation progress, float between 0 and 1
77 | * @return alpha, float between 0 and 1, lower value means less visible from animation
78 | */
79 | protected abstract float getAlpha(String modelName, TransformType type, float progress);
80 |
81 | /**
82 | * Creates a standard fade with some easing in it.
83 | * @param length ease length in ticks
84 | * @param ease ease function from {@link Ease}
85 | * @return fade modifier
86 | */
87 | public static AbstractFadeModifier standardFadeIn(int length, Ease ease) {
88 | return new AbstractFadeModifier(length) {
89 | @Override
90 | protected float getAlpha(String modelName, TransformType type, float progress) {
91 | return ease.invoke(progress);
92 | }
93 | };
94 | }
95 |
96 | /**
97 | * Functional constructor for functional folks
98 | * @param length ease length
99 | * @param function ease function
100 | * @return fade
101 | */
102 | public static AbstractFadeModifier functionalFadeIn(int length, EasingFunction function) {
103 | return new AbstractFadeModifier(length) {
104 | @Override
105 | protected float getAlpha(String modelName, TransformType type, float progress) {
106 | return function.ease(modelName, type, progress);
107 | }
108 | };
109 | }
110 |
111 | @FunctionalInterface
112 | public interface EasingFunction {
113 | float ease(String modelName, TransformType type, float value);
114 | }
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/api/layered/modifier/AbstractModifier.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.api.layered.modifier;
2 |
3 | import dev.kosmx.playerAnim.api.layered.AnimationContainer;
4 | import dev.kosmx.playerAnim.api.layered.IAnimation;
5 | import dev.kosmx.playerAnim.api.layered.ModifierLayer;
6 | import lombok.Setter;
7 | import org.jetbrains.annotations.Nullable;
8 |
9 | @SuppressWarnings("rawtypes")
10 | public abstract class AbstractModifier extends AnimationContainer {
11 |
12 | /**
13 | * ModifierLayer, if you want to do something really fancy!
14 | * Shouldn't be null when playing
15 | */
16 | @Nullable
17 | @Setter
18 | protected ModifierLayer host;
19 |
20 | public AbstractModifier() {
21 | super(null);
22 | }
23 |
24 | /**
25 | * @return modifier can be removed.
26 | */
27 | public boolean canRemove() {
28 | return false;
29 | }
30 |
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/api/layered/modifier/FirstPersonModifier.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.api.layered.modifier;
2 |
3 | import dev.kosmx.playerAnim.api.firstPerson.FirstPersonConfiguration;
4 | import dev.kosmx.playerAnim.api.firstPerson.FirstPersonMode;
5 | import lombok.Getter;
6 | import lombok.Setter;
7 | import org.jetbrains.annotations.NotNull;
8 |
9 | /**
10 | * The {@code FirstPersonModifier} class is responsible for modifying
11 | * the first-person view configuration in a system. It allows enabling or disabling
12 | * specific arms and/or items in the first-person view using predefined configurations.
13 | *
14 | *
This class extends {@code AbstractModifier} and overrides the methods
15 | * {@code getFirstPersonConfiguration} and {@code getFirstPersonMode} to return the current
16 | * configuration and mode defined by the {@code FirstPersonConfigEnum} and {@code FirstPersonMode}, respectively.
17 | *
18 | *
The default configuration is {@code ENABLE_BOTH_ARMS}, which enables
19 | * both arms and items in the first-person view. The default mode is {@code DISABLED}.
20 | */
21 | @Setter
22 | public class FirstPersonModifier extends AbstractModifier {
23 | /**
24 | * The currently active first-person configuration. This determines
25 | * which arms and items are visible in the first-person view.
26 | *
27 | *
Defaults to {@code ENABLE_BOTH_ARMS}, which displays both arms
28 | * and items in the first-person view.
29 | */
30 | private FirstPersonConfigEnum currentFirstPersonConfig = FirstPersonConfigEnum.ENABLE_BOTH_ARMS;
31 |
32 | /**
33 | * The currently active first-person mode. This determines whether the
34 | * first-person view is enabled, disabled, or in another predefined mode.
35 | *
36 | *
This method returns the configuration defined by
44 | * the {@code currentFirstPersonConfig} field, which specifies
45 | * which arms and items are visible in the first-person view.
46 | *
47 | * @param tickDelta
48 | * @return The active {@link FirstPersonConfiguration} based on
49 | * {@code currentFirstPersonConfig}.
50 | */
51 | @Override
52 | public @NotNull FirstPersonConfiguration getFirstPersonConfiguration(float tickDelta) {
53 | return currentFirstPersonConfig.getFirstPersonConfiguration();
54 | }
55 |
56 | /**
57 | * Retrieves the current first-person mode.
58 | *
59 | *
This method returns the mode defined by the {@code currentFirstPersonMode} field,
60 | * which specifies whether the first-person view is enabled, disabled, or in another mode.
Each enum constant is associated with a {@link FirstPersonConfiguration}
74 | * object that specifies which arms and items are visible in the first-person view.
75 | */
76 | @Getter
77 | public enum FirstPersonConfigEnum {
78 | /**
79 | * Enables both arms and both items in the first-person view.
80 | */
81 | ENABLE_BOTH_ARMS(new FirstPersonConfiguration(true, true, true, true)),
82 | /**
83 | * Disables both arms and both items in the first-person view.
84 | */
85 | DISABLE_BOTH_ARMS(new FirstPersonConfiguration(false, false, false, false)),
86 | /**
87 | * Enables only the right arm and its associated item in the first-person view.
88 | */
89 | ONLY_RIGHT_ARM_AND_ITEM(new FirstPersonConfiguration(true, false, true, false)),
90 | /**
91 | * Enables only the left arm and its associated item in the first-person view.
92 | */
93 | ONLY_LEFT_ARM_AND_ITEM(new FirstPersonConfiguration(false, true, false, true)),
94 | /**
95 | * Enables only the right arm without its associated item in the first-person view.
96 | */
97 | ONLY_RIGHT_ARM(new FirstPersonConfiguration(true, false, false, false)),
98 | /**
99 | * Enables only the left arm without its associated item in the first-person view.
100 | */
101 | ONLY_LEFT_ARM(new FirstPersonConfiguration(false, true, false, false)),
102 | /**
103 | * Enables only the right-hand item without showing the arm in the first-person view.
104 | */
105 | ONLY_RIGHT_ITEM(new FirstPersonConfiguration(false, false, true, false)),
106 | /**
107 | * Enables only the left-hand item without showing the arm in the first-person view.
108 | */
109 | ONLY_LEFT_ITEM(new FirstPersonConfiguration(false, false, false, true));
110 |
111 | private final FirstPersonConfiguration firstPersonConfiguration;
112 |
113 | FirstPersonConfigEnum(@NotNull FirstPersonConfiguration firstPersonConfiguration) {
114 | this.firstPersonConfiguration = firstPersonConfiguration;
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/api/layered/modifier/MirrorModifier.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.api.layered.modifier;
2 |
3 | import dev.kosmx.playerAnim.api.TransformType;
4 | import dev.kosmx.playerAnim.api.firstPerson.FirstPersonConfiguration;
5 | import dev.kosmx.playerAnim.core.util.Vec3f;
6 | import lombok.AllArgsConstructor;
7 | import lombok.Getter;
8 | import lombok.NoArgsConstructor;
9 | import lombok.Setter;
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | import java.util.Collections;
13 | import java.util.HashMap;
14 | import java.util.Map;
15 |
16 | @NoArgsConstructor
17 | @AllArgsConstructor
18 | public class MirrorModifier extends AbstractModifier {
19 |
20 | public static final Map mirrorMap;
21 |
22 | /**
23 | * Enable the modifier
24 | */
25 | @Getter
26 | @Setter
27 | private boolean enabled = true;
28 |
29 | @Override
30 | public @NotNull Vec3f get3DTransform(@NotNull String modelName, @NotNull TransformType type, float tickDelta, @NotNull Vec3f value0) {
31 | if (!isEnabled()) return super.get3DTransform(modelName, type, tickDelta, value0);
32 |
33 | if (mirrorMap.containsKey(modelName)) modelName = mirrorMap.get(modelName);
34 | value0 = transformVector(value0, type);
35 |
36 | Vec3f vec3f = super.get3DTransform(modelName, type, tickDelta, value0);
37 | return transformVector(vec3f, type);
38 | }
39 |
40 | // Override candidate
41 | @Override
42 | public @NotNull FirstPersonConfiguration getFirstPersonConfiguration(float tickDelta) {
43 | FirstPersonConfiguration configuration = super.getFirstPersonConfiguration(tickDelta);
44 | if (isEnabled()) {
45 | return new FirstPersonConfiguration()
46 | .setShowLeftArm(configuration.isShowRightArm())
47 | .setShowRightArm(configuration.isShowLeftArm())
48 | .setShowLeftItem(configuration.isShowRightItem())
49 | .setShowRightItem(configuration.isShowLeftItem());
50 | } else return configuration;
51 | }
52 |
53 | protected Vec3f transformVector(Vec3f value0, TransformType type) {
54 | switch (type) {
55 | case POSITION:
56 | return new Vec3f(-value0.getX(), value0.getY(), value0.getZ());
57 | case ROTATION:
58 | return new Vec3f(value0.getX(), -value0.getY(), -value0.getZ());
59 | case BEND:
60 | return new Vec3f(value0.getX(), -value0.getY(), value0.getZ());
61 | default:
62 | return value0; //why?!
63 | }
64 | }
65 |
66 | static {
67 | HashMap partMap = new HashMap<>();
68 | partMap.put("leftArm", "rightArm");
69 | partMap.put("leftLeg", "rightLeg");
70 | partMap.put("leftItem", "rightItem");
71 | partMap.put("rightArm", "leftArm");
72 | partMap.put("rightLeg", "leftLeg");
73 | partMap.put("rightItem", "leftItem");
74 | mirrorMap = Collections.unmodifiableMap(partMap);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/api/layered/modifier/SpeedModifier.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.api.layered.modifier;
2 |
3 | import dev.kosmx.playerAnim.api.TransformType;
4 | import dev.kosmx.playerAnim.core.util.Vec3f;
5 | import lombok.NoArgsConstructor;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | /**
9 | * Modifies the animation speed.
10 | * speed = 2 means twice the speed, the animation will take half as long
11 | * length = 1/speed
12 | */
13 | @NoArgsConstructor
14 | public class SpeedModifier extends AbstractModifier {
15 | public float speed = 1;
16 |
17 | private float delta = 0;
18 |
19 | private float shiftedDelta = 0;
20 |
21 |
22 | public SpeedModifier(float speed) {
23 | if (!Float.isFinite(speed)) throw new IllegalArgumentException("Speed must be a finite number");
24 | this.speed = speed;
25 | }
26 |
27 | @Override
28 | public void tick() {
29 | float delta = 1f - this.delta;
30 | this.delta = 0;
31 | step(delta);
32 | }
33 |
34 | @Override
35 | public void setupAnim(float tickDelta) {
36 | float delta = tickDelta - this.delta; //this should stay positive
37 | this.delta = tickDelta;
38 | step(delta);
39 | }
40 |
41 | protected void step(float delta) {
42 | delta *= speed;
43 | delta += shiftedDelta;
44 | while (delta > 1) {
45 | delta -= 1;
46 | super.tick();
47 | }
48 | super.setupAnim(delta);
49 | this.shiftedDelta = delta;
50 | }
51 |
52 | @Override
53 | public @NotNull Vec3f get3DTransform(@NotNull String modelName, @NotNull TransformType type, float tickDelta, @NotNull Vec3f value0) {
54 | return super.get3DTransform(modelName, type, shiftedDelta, value0);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/api/package-info.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.api;
2 | /*
3 | * This will be moved to an independent library,
4 | * But classes here should stay close to what they already are.
5 | */
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/AnimationFormat.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | /**
7 | * Where is the emote from
8 | * use dev.kosmx.playerAnim.minecraftApi.codec, AnimationCodecs class for deserializing instead.
9 | *
14 | * Use {@link AnimationSerializing#deserializeAnimation(Reader)} to deserialize
15 | * or {@link AnimationSerializing#serializeAnimation(KeyframeAnimation)} to serialize.
16 | * @deprecated use AnimationCodecs instead
17 | */
18 | @Deprecated(forRemoval = true)
19 | public class AnimationSerializing {
20 |
21 | /**
22 | * Deserialize animations from Emotecraft or GeckoLib InputStreamReader
23 | * AnimatinCodecs#serialize()
24 | * @param stream inputStreamReader
25 | * @return List of animations
26 | */
27 | @Deprecated(forRemoval = true)
28 | public static List deserializeAnimation(Reader stream) {
29 | return AnimationJson.GSON.fromJson(stream, AnimationJson.getListedTypeToken());
30 | }
31 |
32 | /**
33 | * Deserialize animations from Emotecraft or GeckoLib InputStream
34 | * use AnimatinCodecs#serialize()
35 | * @param stream inputStream
36 | * @return List of animations
37 | */
38 | @Deprecated(forRemoval = true)
39 | public static List deserializeAnimation(InputStream stream) throws IOException {
40 | try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
41 | return deserializeAnimation(reader);
42 | }
43 | }
44 |
45 | //Emotecraft binary is an emotecraft-specific thing.
46 |
47 | /**
48 | * Serialize animation into Emotecraft JSON format
49 | * @param animation animation
50 | * @return string
51 | */
52 | public static String serializeAnimation(KeyframeAnimation animation) {
53 | return AnimationJson.GSON.toJson(animation, KeyframeAnimation.class);
54 | }
55 |
56 | /**
57 | * Write the animation to output stream
58 | * @param animation animation
59 | * @param writer writer
60 | * @return writer
61 | * @throws IOException writer errors
62 | */
63 | public static Writer writeAnimation(KeyframeAnimation animation, Writer writer) throws IOException {
64 | writer.write(serializeAnimation(animation));
65 | return writer;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/opennbs/NBS.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.opennbs;
2 |
3 | import dev.kosmx.playerAnim.core.data.opennbs.format.CustomInstrument;
4 | import dev.kosmx.playerAnim.core.data.opennbs.format.Header;
5 | import dev.kosmx.playerAnim.core.data.opennbs.format.Layer;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | public class NBS {
11 | public final Header header;
12 | final ArrayList layers;
13 | int length;
14 | byte customInstrumentCount;
15 | final ArrayList customInstruments;
16 |
17 |
18 |
19 | public NBS(Header header, ArrayList layers, ArrayList customInstruments) {
20 | if(header.Layer_count != layers.size()){
21 | if(layers.size() == 0){
22 | for(int i = 0; i < header.Layer_count; i++){
23 | layers.add(new Layer());
24 | }
25 | }
26 | else throw new IllegalArgumentException("Layer count have to be same in the header with the layers size");
27 | }
28 | this.header = header;
29 | this.layers = layers;
30 | this.customInstruments = customInstruments;
31 | }
32 |
33 | public ArrayList getLayers() {
34 | return layers;
35 | }
36 |
37 | List getNotesUntilTick(int tickFrom, int tickTo){
38 | ArrayList notes = new ArrayList<>();
39 | for(Layer layer:this.layers){
40 | if(tickFrom > tickTo){
41 | notes.addAll(layer.getNotesFrom(tickFrom, this.length));
42 | notes.addAll(layer.getNotesFrom(header.Loop_on_off() ? header.Loop_start_tick -1 : -1, tickTo));
43 | }
44 | else {
45 | notes.addAll(layer.getNotesFrom(tickFrom, tickTo));
46 | }
47 | }
48 | return notes;
49 | }
50 |
51 | public int getLength() {
52 | return length;
53 | }
54 |
55 | public void setLength(int length) {
56 | this.length = (length/(int) (header.Time_signature) + 1)*header.Time_signature;
57 | }
58 |
59 | public static class Builder{
60 | public Header header = new Header();
61 | public ArrayList layers = new ArrayList<>();
62 | public ArrayList customInstruments;
63 |
64 | public NBS build(){
65 | return new NBS(header, layers, customInstruments);
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/opennbs/NBSFileUtils.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.opennbs;
2 |
3 | import dev.kosmx.playerAnim.core.data.opennbs.format.Header;
4 | import dev.kosmx.playerAnim.core.data.opennbs.format.Layer;
5 |
6 | import java.io.DataInputStream;
7 | import java.io.IOException;
8 | import java.nio.charset.StandardCharsets;
9 |
10 | /**
11 | * R/W nbs files
12 | */
13 |
14 | public class NBSFileUtils {
15 |
16 | final static int maxWorkingVersion = 5;
17 |
18 | //some methods are from EmotecraftCommon. these have to be separated if I'll make a lib from this!!!
19 | public static NBS read(DataInputStream stream) throws IOException {
20 | if(readShort(stream) != 0){
21 | throw new IOException("Can't read old NBS format.");
22 | }
23 | NBS.Builder songBuilder = new NBS.Builder();
24 | Header header = songBuilder.header;
25 | header.NBS_version = stream.readByte();
26 | int version = header.NBS_version; //just for faster coning
27 | if(version > maxWorkingVersion) throw new IOException("Can't read newer NBS format than " + maxWorkingVersion + "."); //I'll probably run into this
28 | header.Vanilla_instrument_count = stream.readByte();
29 | if(version >= 3)header.Song_length = readShort(stream);
30 | header.Layer_count = readShort(stream);
31 | header.Song_name = readString(stream);
32 | header.Song_author = readString(stream);
33 | header.Song_original_author = readString(stream);
34 | header.Song_description = readString(stream);
35 | header.Song_tempo = readShort(stream);
36 | header.Auto_saving = stream.readByte();
37 | header.Auto_saving_duration = stream.readByte();
38 | header.Time_signature = stream.readByte();
39 | header.Minutes_spent = readInt(stream);
40 | header.Left_clicks = readInt(stream);
41 | header.Right_clicks = readInt(stream);
42 | header.Note_blocks_added = readInt(stream);
43 | header.Note_blocks_removed = readInt(stream);
44 | header.MIDI_Schematic_file_name = readString(stream);
45 | if(version >= 4){ //looping
46 | header.Loop_on_off = stream.readByte();
47 | header.Max_loop_count = stream.readByte();
48 | header.Loop_start_tick = readShort(stream);
49 | }
50 |
51 | //Part 2
52 |
53 | for(int i = 0; i < header.Layer_count; i++){
54 | songBuilder.layers.add(new Layer()); //Precreate layers for later use :)
55 | }
56 |
57 | int maxLength = 0;
58 |
59 | int tick = -1;
60 | for(short jumpToTheNextTick = readShort(stream); jumpToTheNextTick != 0; jumpToTheNextTick = readShort(stream)){
61 | tick += jumpToTheNextTick;
62 | for(int layer = -1, jumpToTheNextLayer = readShort(stream); jumpToTheNextLayer != 0; jumpToTheNextLayer = readShort(stream)){
63 | layer += jumpToTheNextLayer;
64 | Layer.Note note = songBuilder.layers.get(layer).addNote(tick);
65 | if(note == null){
66 | throw new IOException("Creeper, Aww man"); //sry for putting this into an MC song stuff
67 | }
68 | note.instrument = stream.readByte();
69 | note.key = stream.readByte();
70 | if(version >= 4){
71 | note.velocity = stream.readByte();
72 | note.panning = stream.readByte();
73 | note.pitch = readShort(stream);
74 | }
75 | maxLength = Math.max(maxLength, tick);
76 | }
77 | }
78 | //Part 3 :
79 |
80 | for (Layer layer: songBuilder.layers){
81 | layer.name = readString(stream);
82 | if(version >= 4)layer.lock = stream.readByte();
83 | layer.volume = stream.readByte();
84 | layer.stereo = stream.readByte();
85 | }
86 | if(stream.readByte() != 0){
87 | throw new IOException("NBSUtils can not handle custom instruments (yet)");
88 | }
89 |
90 | NBS song = songBuilder.build();
91 | song.setLength(maxLength);
92 | return song;
93 | }
94 |
95 | static String readString(DataInputStream stream) throws IOException {
96 | int len = readInt(stream);
97 | if(len < 0){
98 | throw new IOException("The string's length is less than zero. You wanna me to read it backwards???");
99 | }
100 | byte[] bytes = new byte[len];
101 | if(stream.read(bytes) != len){
102 | throw new IOException("Invalid string");
103 | }
104 | return new String(bytes, StandardCharsets.UTF_8); //:D
105 | }
106 |
107 | static int readInt(DataInputStream stream) throws IOException{
108 | int i = 0;
109 | for(int n = 0; n<4; n++){
110 | i |= (stream.read() << (8*n));
111 | }
112 | return i;
113 | }
114 | static short readShort(DataInputStream stream) throws IOException{
115 | short i = 0;
116 | for(int n = 0; n<2; n++){
117 | i |= (stream.read() << (8*n));
118 | }
119 | return i;
120 | }
121 |
122 |
123 | }
124 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/opennbs/SoundPlayer.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.opennbs;
2 |
3 | import dev.kosmx.playerAnim.core.data.opennbs.format.Layer;
4 | import org.jetbrains.annotations.Nullable;
5 |
6 | import java.util.List;
7 | import java.util.function.Consumer;
8 |
9 | /**
10 | * Plays {@link NBS} objects
11 | * It will need to be played on ClientSide
12 | */
13 | public class SoundPlayer {
14 | final NBS song;
15 | final float songPerMCTick;
16 | int mcTick = 0; //MC tick (20 tps) not the song's custom
17 | int soundTick = -1; //MCTick * (song tickspeed/MCTickspeed)
18 | boolean isPlaying = true; //set false, when stopped. Newer set to true after stopped, instead create a new player.
19 | //To play the song in some interesting ways
20 | final Consumer playSound;
21 |
22 | public SoundPlayer(NBS song, Consumer soundPlayer, int tickToBegin) {
23 | this.song = song;
24 | this.songPerMCTick = ((float) song.header.Song_tempo) / 2000f;
25 | this.playSound = soundPlayer;
26 | this.mcTick = tickToBegin;
27 | }
28 |
29 | public void tick(){
30 | int newSongTick = (int) (mcTick++ * songPerMCTick);
31 | if(newSongTick > song.header.Loop_start_tick && song.header.Loop_on_off()){
32 | if(song.header.Max_loop_count != 0){
33 | int loop = song.header.Max_loop_count & 0xFF; //turn it into an unsigned byte
34 | if((newSongTick - song.header.Loop_start_tick) / (song.getLength() - song.header.Loop_start_tick) > loop){
35 | this.stop();
36 | return;
37 | }
38 | }
39 | newSongTick = (newSongTick - song.header.Loop_start_tick) % (song.getLength() - song.header.Loop_start_tick) + song.header.Loop_start_tick;
40 | }
41 | else if(newSongTick > song.getLength()){
42 | this.stop();
43 | return;
44 | }
45 | if(newSongTick == this.soundTick){
46 | return; //Nothing has happened, can continue;
47 | }
48 | List notesToPlay = this.song.getNotesUntilTick(this.soundTick, newSongTick);
49 | //MinecraftClient.getInstance().world.playSoundFromEntity();
50 | notesToPlay.forEach(this.playSound);
51 |
52 | this.soundTick = newSongTick;
53 |
54 | }
55 |
56 | public void stop(){
57 | this.isPlaying = false;
58 | }
59 |
60 | //My favorite one :D
61 | public static boolean isPlayingSong(@Nullable SoundPlayer player){
62 | return player != null && player.isPlaying;
63 | }
64 |
65 | //TODO put it somewhere else where MC code is available and DELETE ME
66 | /*
67 | public static Instrument getInstrumentFromCode(byte code){
68 |
69 | //That is more efficient than a switch case...
70 | Instrument[] instruments = {Instrument.HARP, Instrument.BASS, Instrument.BASEDRUM, Instrument.SNARE, Instrument.HAT,
71 | Instrument.GUITAR, Instrument.FLUTE, Instrument.BELL, Instrument.CHIME, Instrument.XYLOPHONE,Instrument.IRON_XYLOPHONE,
72 | Instrument.COW_BELL, Instrument.DIDGERIDOO, Instrument.BIT, Instrument.BANJO, Instrument.PLING};
73 |
74 | if(code >= 0 && code < instruments.length){
75 | return instruments[code];
76 | }
77 | return Instrument.HARP; //I don't want to crash here
78 | }
79 |
80 | */
81 | }
82 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/opennbs/format/CustomInstrument.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.opennbs.format;
2 |
3 | /**
4 | * IDK what to do with it...
5 | */
6 | public interface CustomInstrument {
7 | void setName(String name);
8 | void setToSongFile(String path);
9 | void setsetSoundPitch(byte pitch);
10 | void setPressKey(boolean pressKey);
11 | }
12 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/opennbs/format/Header.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.opennbs.format;
2 |
3 | /**
4 | * Sound format header. contains Name, click amount, length, everything from header
5 | * field names and descriptions are form https://opennbs.org/nbs
6 | *
7 | * Deprecated fields are not used by OPEN NOTE BLOCK STUDIO
8 | */
9 | public class Header {
10 | /**
11 | * The version of the new NBS format.
12 | */
13 | public byte NBS_version;
14 | /**
15 | * Amount of default instruments when the song was saved. This is needed to determine at what index custom instruments start.
16 | */
17 | public byte Vanilla_instrument_count;
18 | /**
19 | * The length of the song, measured in ticks. Divide this by the tempo to get the length of the song in seconds. Note Block Studio doesn't really care about this value, the song size is calculated in the second part.
20 | * (Note: this was re-added in NBS version 3)
21 | */
22 | @Deprecated
23 | public short Song_length;
24 | /**
25 | * The last layer with at least one note block in it, or the last layer that has had its name, volume or stereo changed.
26 | */
27 | public short Layer_count;
28 | /**
29 | * The name of the song.
30 | */
31 | public String Song_name;
32 | /**
33 | * The author of the song.
34 | */
35 | public String Song_author;
36 | /**
37 | * The original author of the song.
38 | */
39 | public String Song_original_author;
40 | /**
41 | * The description of the song.
42 | */
43 | public String Song_description;
44 | /**
45 | * The tempo of the song multiplied by 100 (for example, 1225 instead of 12.25). Measured in ticks per second.
46 | */
47 | public short Song_tempo;
48 | /**
49 | * Whether auto-saving has been enabled (0 or 1). As of NBS version 4 this value is still saved to the file, but no longer used in the program.
50 | */
51 | @Deprecated
52 | public byte Auto_saving;
53 | public boolean Auto_saving(){
54 | return this.Auto_saving != 0;
55 | }
56 | /**
57 | * The amount of minutes between each auto-save (if it has been enabled) (1-60). As of NBS version 4 this value is still saved to the file, but no longer used in the program.
58 | */
59 | @Deprecated
60 | public byte Auto_saving_duration;
61 | /**
62 | * The time signature of the song. If this is 3, then the signature is 3/4. Default is 4. This value ranges from 2-8.
63 | */
64 | public byte Time_signature;
65 | /**
66 | * Amount of minutes spent on the project.
67 | */
68 | public int Minutes_spent;
69 | /**
70 | * Amount of times the user has left-clicked.
71 | */
72 | public int Left_clicks;
73 | /**
74 | * Amount of times the user has right-clicked.
75 | */
76 | public int Right_clicks;
77 | /**
78 | * Amount of times the user has added a note block.
79 | */
80 | public int Note_blocks_added;
81 | /**
82 | * The amount of times the user have removed a note block.
83 | */
84 | public int Note_blocks_removed;
85 | /**
86 | * If the song has been imported from a .mid or .schematic file, that file name is stored here (only the name of the file, not the path).
87 | */
88 | public String MIDI_Schematic_file_name;
89 | /**
90 | * Whether looping is on or off. (0 = off, 1 = on)
91 | */
92 | public byte Loop_on_off;
93 | public boolean Loop_on_off(){
94 | return this.Loop_on_off != 0;
95 | }
96 | /**
97 | * 0 = infinite. Other values mean the amount of times the song loops.
98 | */
99 | public byte Max_loop_count;
100 | /**
101 | * Determines which part of the song (in ticks) it loops back to.
102 | */
103 | public short Loop_start_tick;
104 |
105 | //This is the end of the header
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/opennbs/format/Layer.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.opennbs.format;
2 |
3 | import org.jetbrains.annotations.Nullable;
4 |
5 | import java.util.ArrayList;
6 |
7 | public class Layer {
8 | /**
9 | * The name of the layer.
10 | */
11 | public String name;
12 | /**
13 | * Whether or not this layer has been marked as locked. 1 = locked.
14 | */
15 | public byte lock;
16 | public boolean getLock(){
17 | return this.lock != 0;
18 | }
19 | public void setLock(boolean newValue){
20 | this.lock = (byte) (newValue ? 1 : 0);
21 | }
22 | /**
23 | * The volume of the layer (percentage). Ranges from 0-100.
24 | */
25 | public byte volume = 100;
26 | /**
27 | * How much this layer is panned to the left/right. 0 is 2 blocks right, 100 is center, 200 is 2 blocks left.
28 | */
29 | public byte stereo = 100;
30 |
31 | private int lastUsedTickPos = 0;
32 |
33 | public final ArrayList notes = new ArrayList<>();
34 |
35 | /**
36 | * search the position of the last keyframe
37 | * INTERNAL
38 | * @param tick when
39 | * @return pos in the array
40 | */
41 | public int findAtTick(int tick){
42 | int i = - 1;
43 | if(this.notes.size() > lastUsedTickPos + 1 && this.notes.get(lastUsedTickPos + 1).tick <= tick) i = lastUsedTickPos;
44 | while(this.notes.size() > i + 1 && this.notes.get(i + 1).tick <= tick){
45 | i++;
46 | }
47 | lastUsedTickPos = i;
48 | return i;
49 | }
50 |
51 | @Nullable
52 | public Note addNote(int tick){
53 | if(this.getLock()){
54 | return null;
55 | }
56 | int i = findAtTick(tick);
57 | if(i > 0 && notes.get(i).tick == tick){
58 | return null;
59 | }
60 | Note note = new Note(tick);
61 | notes.add(i + 1, note);
62 | return note;
63 | }
64 |
65 | public ArrayList getNotesFrom(int fromTick, int toTick) {
66 | ArrayList returnNotes = new ArrayList<>();
67 | int posAtBegin = findAtTick(fromTick);
68 | if(notes.size() >= posAtBegin){
69 | posAtBegin++;
70 | }
71 | int posAtEnd = findAtTick(toTick);
72 | if(notes.size()>= posAtEnd){
73 | posAtEnd++;
74 | }
75 | if(posAtBegin < 0){
76 | posAtBegin = 0;
77 | }
78 | for(; posAtBegin < posAtEnd; posAtBegin++){
79 | returnNotes.add(this.notes.get(posAtBegin));
80 | }
81 | return returnNotes;
82 | }
83 |
84 | public ArrayList getNotesFrom(int toTick) {
85 | return getNotesFrom(this.lastUsedTickPos, toTick);
86 | }
87 |
88 | public class Note {
89 | /**
90 | * The instrument of the note block. This is 0-15, or higher if the song uses custom instruments.
91 | * 0 = Piano (Air)
92 | * 1 = Double Bass (Wood)
93 | * 2 = Bass Drum (Stone)
94 | * 3 = Snare Drum (Sand)
95 | * 4 = Click (Glass)
96 | * 5 = Guitar (Wool)
97 | * 6 = Flute (Clay)
98 | * 7 = Bell (Block of Gold)
99 | * 8 = Chime (Packed Ice)
100 | * 9 = Xylophone (Bone Block)
101 | * 10 = Iron Xylophone (Iron Block)
102 | * 11 = Cow Bell (Soul Sand)
103 | * 12 = Didgeridoo (Pumpkin)
104 | * 13 = Bit (Block of Emerald)
105 | * 14 = Banjo (Hay)
106 | * 15 = Pling (Glowstone)
107 | */
108 | public byte instrument;
109 | /**
110 | * The key of the note block, from 0-87, where 0 is A0 and 87 is C8. 33-57 is within the 2-octave limit.
111 | */
112 | public byte key;
113 | /**
114 | * The velocity/volume of the note block, from 0% to 100%.
115 | */
116 | public byte velocity = 100;
117 | /**
118 | * The stereo position of the note block, from 0-200. 100 is center panning.
119 | */
120 | public byte panning = 100;
121 | /**
122 | * The fine pitch of the note block, from -32,768 to 32,767 cents (but the max in Note Block Studio is limited to -1200 and +1200). 0 is no fine-tuning. ±100 cents is a single semitone difference. After reading this, we go back to step 2.
123 | */
124 | public short pitch = 0;
125 |
126 | /**
127 | * Where is that note exactly. to be able to search without replaying the whole binary.
128 | */
129 | public final int tick;
130 |
131 | public Note(int tick) {
132 | this.tick = tick;
133 | }
134 |
135 | /**
136 | * @return sound's pitch as a float
137 | */
138 | public float getPitch(){
139 | return (float)Math.pow(2.0D, (double)(this.key + this.pitch/100 - 45) / 12.0D); //key 45 is F#
140 | }
141 |
142 | /**
143 | * @return sound value in percents (including the channels volume)
144 | */
145 | public float getVolume(){
146 | return this.velocity/10000f*volume; //there is why notes can't be static
147 | }
148 | }
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/opennbs/format/package-info.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.opennbs.format;
2 | /*
3 | For everything common in formats. Like the header.
4 | */
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/opennbs/package-info.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.opennbs;
2 | /*
3 |
4 | [OpenNBS](https://opennbs.org/nbs) format reader, writer and most important, Player
5 | May be separated into a different package.
6 | (It will use MC code once, in the player.)
7 |
8 | */
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/quarktool/InverseEase.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.quarktool;
2 |
3 | import dev.kosmx.playerAnim.core.util.Ease;
4 | import dev.kosmx.playerAnim.core.util.Easing;
5 |
6 | public class InverseEase {
7 | public static Ease inverse(Ease ease){
8 | String str = ease.toString();
9 | if(str.substring(0, 2).equals("IN") && ! str.substring(0, 5).equals("INOUT")){
10 | return (Easing.easeFromString("OUT" + str.substring(2)));
11 | }else if(str.substring(0, 3).equals("OUT")){
12 | return (Easing.easeFromString("IN" + str.substring(3)));
13 | }else return ease;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/quarktool/Move.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.quarktool;
2 |
3 |
4 | import dev.kosmx.playerAnim.core.util.Ease;
5 |
6 | public class Move implements Playable {
7 | private final Ease ease;
8 | private final float value;
9 | private float valueBefore;
10 | private float valueAfter;
11 | private boolean isInitialized = false;
12 | private PartMap.PartValue part;
13 | private final int length;
14 |
15 | public Move(PartMap.PartValue part, float value, int length, Ease ease){
16 | this.ease = ease;
17 | this.length = length;
18 | this.value = value;
19 | this.part = part;
20 | }
21 |
22 | @Override
23 | public int playForward(int time) throws QuarkParsingError{
24 | if(! isInitialized){
25 | this.isInitialized = true;
26 | this.valueBefore = part.getValue();
27 | this.valueAfter = this.value;
28 | this.part.addValue(time, time + this.length, this.valueAfter, ease);
29 | }else{
30 | this.part.hold();
31 | this.part.addValue(time, valueBefore, ease);
32 | this.part.addValue(time + this.length, valueAfter, Ease.CONSTANT);
33 | //this.part.setValue(this.valueAfter);
34 | }
35 | return time + this.length;
36 | }
37 |
38 | @Override
39 | public int playBackward(int time) throws QuarkParsingError{
40 | if(! isInitialized) throw new QuarkParsingError();
41 | this.part.hold();
42 | this.part.addValue(time, this.valueAfter, InverseEase.inverse(ease));
43 | this.part.addValue(time + this.length, this.valueBefore, Ease.CONSTANT);
44 | //this.part.setValue(this.valueBefore);
45 | return time + this.length;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/quarktool/PartMap.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.quarktool;
2 |
3 |
4 | import dev.kosmx.playerAnim.core.data.KeyframeAnimation;
5 | import dev.kosmx.playerAnim.core.util.Ease;
6 |
7 | public class PartMap {
8 | public KeyframeAnimation.StateCollection part;
9 | public PartValue x;
10 | public PartValue y;
11 | public PartValue z;
12 |
13 | public PartMap(KeyframeAnimation.StateCollection part){
14 | this.part = part;
15 | this.x = new PartValue(this.part.pitch);
16 | this.y = new PartValue(this.part.yaw);
17 | this.z = new PartValue(this.part.roll);
18 | }
19 |
20 | static class PartValue {
21 | private float value;
22 | private int lastTick;
23 | private final KeyframeAnimation.StateCollection.State timeline;
24 |
25 |
26 | private PartValue(KeyframeAnimation.StateCollection.State timeline){
27 | this.timeline = timeline;
28 | }
29 |
30 | public void addValue(int tick, float value, Ease ease){
31 | this.lastTick = tick;
32 | this.timeline.addKeyFrame(tick, value, ease);
33 | }
34 |
35 | public void addValue(int tickFrom, int tickTo, float value, Ease ease) throws QuarkParsingError{
36 | if(tickFrom < this.lastTick){
37 | throw new QuarkParsingError();
38 | }else if(tickFrom == this.lastTick && timeline.getKeyFrames().size() != 0){
39 | timeline.replaceEase(timeline.findAtTick(tickFrom), ease);
40 | //timeline.keyFrames.get(timeline.findAtTick(tickFrom)).ease =ease;
41 | }else{
42 | timeline.addKeyFrame(tickFrom, this.value, ease);
43 | }
44 | this.value = value;
45 | this.lastTick = tickTo;
46 | this.timeline.addKeyFrame(tickTo, this.value, Ease.CONSTANT);
47 | }
48 |
49 | public float getValue(){
50 | return value;
51 | }
52 |
53 | public void hold(){
54 | //this.timeline.keyFrames.get(this.timeline.keyFrames.size() - 1).setEase(Ease.CONSTANT);
55 | this.timeline.replaceEase(this.timeline.length() -1, Ease.CONSTANT);
56 | }
57 |
58 | public void setValue(float valueAfter){
59 | this.value = valueAfter;
60 | }
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/quarktool/Pause.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.quarktool;
2 |
3 | public class Pause implements Playable {
4 | private final int len;
5 |
6 | public Pause(int len){
7 | this.len = len;
8 | }
9 |
10 | @Override
11 | public int playForward(int time){
12 | return time + this.len;
13 | }
14 |
15 | @Override
16 | public int playBackward(int time){
17 | return time + this.len;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/quarktool/Pauseable.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.quarktool;
2 |
3 | public class Pauseable implements Playable {
4 | private final Playable playable;
5 | private final int len;
6 |
7 |
8 | public Pauseable(Playable playable, int len){
9 | this.playable = playable;
10 | this.len = len;
11 | }
12 |
13 |
14 | @Override
15 | public int playForward(int time) throws QuarkParsingError{
16 | return playable.playForward(time + this.len);
17 | }
18 |
19 | @Override
20 | public int playBackward(int time) throws QuarkParsingError{
21 | return playable.playBackward(time) + this.len;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/quarktool/Playable.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.quarktool;
2 |
3 | public interface Playable {
4 |
5 | int playForward(int time) throws QuarkParsingError;
6 |
7 | int playBackward(int time) throws QuarkParsingError;
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/quarktool/QuarkParsingError.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.quarktool;
2 |
3 | public class QuarkParsingError extends Exception {
4 | final String message;
5 |
6 | public QuarkParsingError(String message, int i){
7 | this.message = "at line " + i + " " + message;
8 | }
9 |
10 | public QuarkParsingError(){
11 | this.message = null;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/quarktool/Repeat.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.quarktool;
2 |
3 | public class Repeat implements Playable {
4 | protected final Playable playable;
5 | protected final int delay;
6 | protected int count;
7 |
8 |
9 | public Repeat(Playable parent, int delay, int count) throws QuarkParsingError{
10 | this.playable = parent;
11 | if(count < 0 || count > 128) throw new QuarkParsingError();
12 | this.count = count;
13 | this.delay = delay;
14 | }
15 |
16 | public int playForward(int time) throws QuarkParsingError{
17 | for(int i = 0; i <= count; i++){
18 | time = this.playable.playForward(time);
19 | time += delay;
20 | }
21 | return time;
22 | }
23 |
24 | @Override
25 | public int playBackward(int time) throws QuarkParsingError{
26 | throw new QuarkParsingError();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/quarktool/Reset.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.quarktool;
2 |
3 | import dev.kosmx.playerAnim.core.util.Ease;
4 |
5 | public class Reset implements Playable {
6 | private Playable[] parts;
7 |
8 | public Reset(QuarkReader reader, String all, int len) throws QuarkParsingError{
9 | if(all.equals("all")){
10 | parts = new Playable[18];
11 | addParts(0, reader.head, len);
12 | addParts(3, reader.rightArm, len);
13 | addParts(6, reader.rightLeg, len);
14 | addParts(9, reader.leftArm, len);
15 | addParts(12, reader.leftLeg, len);
16 | addParts(15, reader.torso, len);
17 | }else{
18 | parts = new Playable[3];
19 | addParts(0, reader.getBPFromStr(all.split("_")), len);
20 | }
21 | }
22 |
23 | private void addParts(int i, PartMap part, int len){
24 | parts[i] = new Move(part.x, 0, len, Ease.INOUTQUAD);
25 | parts[i + 1] = new Move(part.y, 0, len, Ease.INOUTQUAD);
26 | parts[i + 2] = new Move(part.z, 0, len, Ease.INOUTQUAD);
27 | }
28 |
29 | @Override
30 | public int playForward(int time) throws QuarkParsingError{
31 | return 0;
32 | }
33 |
34 | @Override
35 | public int playBackward(int time) throws QuarkParsingError{
36 | return 0;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/data/quarktool/Yoyo.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data.quarktool;
2 |
3 | public class Yoyo extends Repeat {
4 | public Yoyo(Playable parent, int delay, int count) throws QuarkParsingError{
5 | super(parent, delay, count);
6 | }
7 |
8 | @Override
9 | public int playForward(int time) throws QuarkParsingError{
10 | int i = 0;
11 | int t = time;
12 | while(true){
13 | if(i++ > this.count) return t;
14 | if(i != 1) t += this.delay;
15 | t = this.playable.playForward(t);
16 | if(i++ > this.count) return t;
17 | t += this.delay;
18 | t = this.playable.playBackward(t);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/impl/AnimationProcessor.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.impl;
2 |
3 |
4 | import dev.kosmx.playerAnim.api.TransformType;
5 | import dev.kosmx.playerAnim.api.firstPerson.FirstPersonConfiguration;
6 | import dev.kosmx.playerAnim.api.firstPerson.FirstPersonMode;
7 | import dev.kosmx.playerAnim.api.layered.IAnimation;
8 | import dev.kosmx.playerAnim.core.util.Pair;
9 | import dev.kosmx.playerAnim.core.util.Vec3f;
10 | import org.jetbrains.annotations.ApiStatus;
11 | import org.jetbrains.annotations.NotNull;
12 |
13 | /**
14 | * Tool to easily play animation to the player.
15 | * internal, do not use
16 | */
17 | @ApiStatus.Internal
18 | public class AnimationProcessor {
19 | private final IAnimation animation;
20 | private float tickDelta = 0f;
21 |
22 | public AnimationProcessor(IAnimation animation) {
23 | this.animation = animation;
24 | }
25 |
26 | public void tick() {
27 | animation.tick();
28 | }
29 |
30 | public boolean isActive() {
31 | return animation.isActive();
32 | }
33 |
34 | public Vec3f get3DTransform(String modelName, TransformType type, Vec3f value0) {
35 | return animation.get3DTransform(modelName, type, this.tickDelta, value0);
36 | }
37 |
38 | public void setTickDelta(float tickDelta) {
39 | this.tickDelta = tickDelta;
40 | this.animation.setupAnim(tickDelta);
41 | }
42 |
43 | public boolean isFirstPersonAnimationDisabled() {
44 | return !animation.getFirstPersonMode(tickDelta).isEnabled();
45 | }
46 |
47 | public @NotNull FirstPersonMode getFirstPersonMode() {
48 | return animation.getFirstPersonMode(tickDelta);
49 | }
50 |
51 | public @NotNull FirstPersonConfiguration getFirstPersonConfiguration() {
52 | return animation.getFirstPersonConfiguration(tickDelta);
53 | }
54 |
55 | public Pair getBend(String modelName) {
56 | Vec3f bendVec = this.get3DTransform(modelName, TransformType.BEND, Vec3f.ZERO);
57 | return new Pair<>(bendVec.getX(), bendVec.getY());
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/impl/event/Event.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.impl.event;
2 |
3 | import java.util.ArrayList;
4 |
5 |
6 | /**
7 | * To register a listener, use {@link Event#register(Object)};
8 | * @param
9 | */
10 | public class Event {
11 | final ArrayList listeners = new ArrayList<>();
12 | final Invoker _invoker;
13 |
14 | public Event(Class clazz, Invoker invoker){
15 | this._invoker = invoker;
16 | }
17 |
18 | /**
19 | * Do EVENT.invoker()./invoke(Objects...)/;
20 | * Only when firing the event.
21 | * @return the invoker
22 | * This shall only be used by the API
23 | */
24 | public final T invoker(){
25 | return _invoker.invoker(listeners);
26 | }
27 |
28 | /**
29 | * Register a new event listener;
30 | * See the actual event documentation for return type
31 | * @param listener the listener.
32 | */
33 | public void register(T listener){
34 | if(listener == null) throw new NullPointerException("listener can not be null");
35 | listeners.add(listener);
36 | }
37 |
38 | /**
39 | * unregister the listener
40 | * @param listener listener to unregister, or a similar listener if it has equals() function.
41 | */
42 | public void unregister(T listener){
43 | listeners.remove(listener);
44 | }
45 |
46 | @FunctionalInterface
47 | public interface Invoker{
48 | T invoker(Iterable listeners);
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/impl/event/EventResult.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.impl.event;
2 |
3 | /**
4 | * Generic event results
5 | * See the actual event documentation for actual behaviour
6 | */
7 | public enum EventResult {
8 | /**
9 | * Your listener did nothing, in won't change the result of the event
10 | */
11 | PASS,
12 |
13 | /**
14 | * Cancel the event and success. see the event's documentation
15 | */
16 | SUCCESS,
17 |
18 | /**
19 | * Event failed, cancel the further processing, see the event's documentation
20 | */
21 | FAIL,
22 |
23 | /**
24 | * Cancel the event, then does nothing. sometimes the same as SUCCESS
25 | */
26 | CONSUME
27 | }
28 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/util/Ease.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.util;
2 |
3 | import lombok.Getter;
4 |
5 | import java.util.function.Function;
6 |
7 | /**
8 | * Easings form easings.net
9 | * + constant + linear
10 | */
11 | public enum Ease {
12 | LINEAR(0, arg -> easeIn(f -> f)),
13 | CONSTANT(1, arg -> easeIn(f -> 0f)),
14 |
15 | // Sine
16 | INSINE(6, arg -> easeIn(Easing::sine)),
17 | OUTSINE(7, arg -> easeOut(Easing::sine)),
18 | INOUTSINE(8, arg -> easeInOut(Easing::sine)),
19 |
20 | // Cubic
21 | INCUBIC(9, arg -> easeIn(Easing::cubic)),
22 | OUTCUBIC(10, arg -> easeOut(Easing::cubic)),
23 | INOUTCUBIC(11, arg -> easeInOut(Easing::cubic)),
24 |
25 | // Quadratic
26 | INQUAD(12, arg -> easeIn(Easing::quadratic)),
27 | OUTQUAD(13, arg -> easeOut(Easing::quadratic)),
28 | INOUTQUAD(14, arg -> easeInOut(Easing::quadratic)),
29 |
30 | // Quart
31 | INQUART(15, arg -> easeIn(Easing.pow(4))),
32 | OUTQUART(16, arg -> easeOut(Easing.pow(4))),
33 | INOUTQUART(17, arg -> easeInOut(Easing.pow(4))),
34 |
35 | // Quint
36 | INQUINT(18, arg -> easeIn(Easing.pow(5))),
37 | OUTQUINT(19, arg -> easeOut(Easing.pow(5))),
38 | INOUTQUINT(20, arg -> easeInOut(Easing.pow(5))),
39 |
40 | // Expo
41 | INEXPO(21, arg -> easeIn(Easing::exp)),
42 | OUTEXPO(22, arg -> easeOut(Easing::exp)),
43 | INOUTEXPO(23, arg -> easeInOut(Easing::exp)),
44 |
45 | // Cricle
46 | INCIRC(24, arg -> easeIn(Easing::circle)),
47 | OUTCIRC(25, arg -> easeOut(Easing::circle)),
48 | INOUTCIRC(26, arg -> easeInOut(Easing::circle)),
49 |
50 | // Back
51 | INBACK(27, arg -> easeIn(Easing.back(arg))),
52 | OUTBACK(28, arg -> easeOut(Easing.back(arg))),
53 | INOUTBACK(29, arg -> easeInOut(Easing.back(arg))),
54 |
55 | // Elastic
56 | INELASTIC(30, arg -> easeIn(Easing.elastic(arg))),
57 | OUTELASTIC(31, arg -> easeOut(Easing.elastic(arg))),
58 | INOUTELASTIC(32, arg -> easeInOut(Easing.elastic(arg))),
59 |
60 | // Bounce
61 | INBOUNCE(33, arg -> easeIn(Easing.bounce(arg))),
62 | OUTBOUNCE(34, arg -> easeOut(Easing.bounce(arg))),
63 | INOUTBOUNCE(35, arg -> easeInOut(Easing.bounce(arg))),
64 |
65 | CATMULLROM(36, arg -> easeInOut(Easing::catmullRom)),
66 | STEP(37, arg -> easeIn(Easing.step(arg)));
67 |
68 | @Getter
69 | final byte id;
70 | private final Function> impl;
71 |
72 | /**
73 | * @param id id
74 | * @param impl implementation
75 | */
76 | Ease(byte id, Function> impl) {
77 | this.id = id;
78 | this.impl = impl;
79 | }
80 |
81 | /**
82 | * @param id id
83 | * @param impl implementation
84 | */
85 | Ease(int id, Function> impl) {
86 | this((byte) id, impl);
87 | }
88 |
89 | /**
90 | * Run the easing
91 | * @param f float between 0 and 1
92 | * @return ease(f)
93 | */
94 | public float invoke(float f) {
95 | return invoke(f, null);
96 | }
97 |
98 | /**
99 | * Run the easing
100 | * @param t float between 0 and 1
101 | * @param n float easing argument
102 | * @return ease(t, n)
103 | */
104 | public float invoke(float t, Float n) {
105 | return this.impl.apply(n).apply(t);
106 | }
107 |
108 | //To be able to send these as bytes instead of String names.
109 | public static Ease getEase(byte b){
110 | for(Ease ease:Ease.values()){
111 | if(ease.id == b) return ease;
112 | }
113 | return LINEAR;
114 | }
115 |
116 | public static Function easeIn(Function function) {
117 | return function;
118 | }
119 |
120 | public static Function easeOut(Function function) {
121 | return time -> 1 - function.apply(1 - time);
122 | }
123 |
124 | public static Function easeInOut(Function function) {
125 | return time -> {
126 | if (time < 0.5F) {
127 | return function.apply(time * 2F) / 2F;
128 | }
129 |
130 | return 1 - function.apply((1 - time) * 2F) / 2F;
131 | };
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/util/Easing.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.util;
2 |
3 |
4 | import org.jetbrains.annotations.Nullable;
5 |
6 | import java.util.function.Function;
7 |
8 | public class Easing {
9 |
10 | /**
11 | * Easing functions from easings.net
12 | * All function have a string codename
13 | * EasingFromString
14 | *
15 | * All function needs an input between 0 and 1
16 | *
17 | * @deprecated Just use {@link Ease#invoke(float)}
18 | */
19 | @Deprecated
20 | public static float easingFromEnum(@Nullable Ease type, float f) {
21 | return type != null ? type.invoke(f) : f;
22 | }
23 |
24 | /**
25 | * @param string ease name
26 | * @return ease
27 | */
28 | public static Ease easeFromString(String string){
29 | try{
30 | if(string.substring(0, 4).equalsIgnoreCase("EASE")){
31 | string = string.substring(4);
32 | }
33 | return Ease.valueOf(string.toUpperCase());
34 | } catch(Exception exception){
35 | //Main.log(Level.ERROR, "Ease name unknown: \"" + string + "\" using linear", true);
36 | //Main.log(Level.WARN, exception.toString());
37 | return Ease.LINEAR;
38 | }
39 | }
40 |
41 | public static float sine(float n) {
42 | return 1 - (float) Math.cos(n * Math.PI / 2F);
43 | }
44 |
45 | public static float cubic(float n) {
46 | return n * n * n;
47 | }
48 |
49 | public static float quadratic(float n) {
50 | return n * n;
51 | }
52 |
53 | public static Function pow(float n) {
54 | return t -> (float) Math.pow(t, n);
55 | }
56 |
57 | public static float exp(float n) {
58 | return (float) Math.pow(2, 10 * (n - 1));
59 | }
60 |
61 | public static float circle(float n) {
62 | return 1 - (float) Math.sqrt(1 - n * n);
63 | }
64 |
65 | public static Function back(Float n) {
66 | final float n2 = n == null ? 1.70158F : n * 1.70158F;
67 |
68 | return t -> t * t * ((n2 + 1) * t - n2);
69 | }
70 |
71 | public static Function elastic(Float n) {
72 | float n2 = n == null ? 1 : n;
73 |
74 | return t -> (float) (1 - Math.pow(Math.cos(t * Math.PI / 2f), 3) * Math.cos(t * n2 * Math.PI));
75 | }
76 |
77 | public static Function bounce(Float n) {
78 | final float n2 = n == null ? 0.5F : n;
79 |
80 | Function one = x -> 121f / 16f * x * x;
81 | Function two = x -> (float) (121f / 4f * n2 * Math.pow(x - 6f / 11f, 2) + 1 - n2);
82 | Function three = x -> (float) (121 * n2 * n2 * Math.pow(x - 9f / 11f, 2) + 1 - n2 * n2);
83 | Function four = x -> (float) (484 * n2 * n2 * n2 * Math.pow(x - 10.5f / 11f, 2) + 1 - n2 * n2 * n2);
84 |
85 | return t -> Math.min(Math.min(one.apply(t), two.apply(t)), Math.min(three.apply(t), four.apply(t)));
86 | }
87 |
88 | public static Function step(Float n) {
89 | float n2 = n == null ? 2 : n;
90 |
91 | if (n2 < 2)
92 | throw new IllegalArgumentException("Steps must be >= 2, got: " + n2);
93 |
94 | final int steps = (int)n2;
95 |
96 | return t -> {
97 | float result = 0;
98 |
99 | if (t < 0)
100 | return result;
101 |
102 | float stepLength = (1 / (float)steps);
103 |
104 | if (t > (result = (steps - 1) * stepLength))
105 | return result;
106 |
107 | int testIndex;
108 | int leftBorderIndex = 0;
109 | int rightBorderIndex = steps - 1;
110 |
111 | while (rightBorderIndex - leftBorderIndex != 1) {
112 | testIndex = leftBorderIndex + (rightBorderIndex - leftBorderIndex) / 2;
113 |
114 | if (t >= testIndex * stepLength) {
115 | leftBorderIndex = testIndex;
116 | }
117 | else {
118 | rightBorderIndex = testIndex;
119 | }
120 | }
121 |
122 | return leftBorderIndex * stepLength;
123 | };
124 | }
125 |
126 | public static float catmullRom(float n) {
127 | return (0.5f * (2.0f * (n + 1) + ((n + 2) - n) * 1
128 | + (2.0f * n - 5.0f * (n + 1) + 4.0f * (n + 2) - (n + 3)) * 1
129 | + (3.0f * (n + 1) - n - 3.0f * (n + 2) + (n + 3)) * 1));
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/util/MathHelper.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.util;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.nio.Buffer;
6 | import java.nio.ByteBuffer;
7 | import java.util.LinkedList;
8 | import java.util.List;
9 |
10 | //like MC math helper but without MC
11 | public class MathHelper {
12 |
13 | public static float lerp(float delta, float start, float end) {
14 | return start + delta * (end - start);
15 | }
16 |
17 | public static double lerp(double delta, double start, double end) {
18 | return start + delta * (end - start);
19 | }
20 |
21 | public static int colorHelper(int r, int g, int b, int a){
22 | return ((a & 255) << 24) | ((r & 255) << 16) | ((g & 255) << 8) | (b & 255); //Sometimes minecraft uses ints as color...
23 | }
24 |
25 | /**
26 | * Clamp f to -Pi until Pi range
27 | * @param f radians
28 | * @return radians
29 | */
30 | public static float clampToRadian(float f){
31 | final double a = Math.PI*2;
32 | double b = ((f + Math.PI)%a);
33 | if(b < 0){
34 | b += a;
35 | }
36 | return (float) (b - Math.PI);
37 | }
38 |
39 |
40 | /**
41 | * similar? to Java 9+ {@link InputStream#readAllBytes()}
42 | * because of compatibility, I can not use that
43 | * @param stream read this stream
44 | * @return ByteBuffer from stream
45 | * @throws IOException ...
46 | */
47 | public static ByteBuffer readFromIStream(InputStream stream) throws IOException {
48 | List> listOfBites = new LinkedList<>();
49 | int totalSize = 0;
50 | while (true){
51 | int estimatedSize = stream.available();
52 | byte[] bytes = new byte[Math.max(1, estimatedSize)];
53 | int i = stream.read(bytes);
54 | if(i < 1) break;
55 | totalSize += i;
56 | listOfBites.add(new Pair<>(i, bytes));
57 | }
58 | ByteBuffer byteBuffer = ByteBuffer.allocate(totalSize);
59 | for(Pair i:listOfBites){
60 | byteBuffer.put(i.getRight(), 0, i.getLeft());
61 | }
62 | ((Buffer)byteBuffer).position(0); //set position to 0, we'll read it
63 | return byteBuffer;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/util/NetworkHelper.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.util;
2 |
3 |
4 | import java.io.IOException;
5 | import java.nio.ByteBuffer;
6 | import java.nio.charset.StandardCharsets;
7 | import java.util.UUID;
8 |
9 | /**
10 | * I can't use Minecraft's string and uuid byte reader in a bukkit plugin, I need to implement these.
11 | * This can still here, but it can be removed if unused
12 | */
13 | public final class NetworkHelper {
14 | public static String readString(ByteBuffer buf) throws IOException {
15 | int len = buf.getInt();
16 | if(len < 0){
17 | throw new IOException("The received encoded string buffer length is less than zero! Weird string!");
18 | }
19 | byte[] b = new byte[len];
20 | buf.get(b); //that is safe to use.
21 |
22 | return new String(b, StandardCharsets.UTF_8);
23 | }
24 |
25 | public static void writeString(ByteBuffer buf, String str){
26 | byte[] b = str.getBytes(StandardCharsets.UTF_8);
27 | buf.putInt(b.length);
28 | buf.put(b);
29 | }
30 |
31 | //copied from MC
32 | public static String readVarString(ByteBuffer buf) throws IOException {
33 | int j = readVarInt(buf);
34 | if (j < 0) {
35 | throw new IOException("The received encoded string buffer length is less than zero! Weird string!");
36 | } else {
37 | byte[] bytes = new byte[j];
38 | buf.get(bytes);
39 | return new String(bytes, StandardCharsets.UTF_8);
40 | }
41 | }
42 | public static void writeVarString(ByteBuffer buf, String str){
43 | byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
44 | writeVarInt(buf, bytes.length);
45 | buf.put(bytes);
46 | }
47 |
48 | public static UUID readUUID(ByteBuffer buf){
49 | long a = buf.getLong();
50 | long b = buf.getLong();
51 | return new UUID(a, b); //The order is important
52 | }
53 | public static void writeUUID(ByteBuffer buf, UUID uuid){
54 | buf.putLong(uuid.getMostSignificantBits());
55 | buf.putLong(uuid.getLeastSignificantBits());
56 | }
57 |
58 | //copied from minecraft
59 | public static int readVarInt(ByteBuffer buf) {
60 | int i = 0;
61 | int j = 0;
62 |
63 | byte b;
64 | do {
65 | b = buf.get();
66 | i |= (b & 127) << j++ * 7;
67 | if (j > 5) {
68 | throw new RuntimeException("VarInt too big");
69 | }
70 | } while((b & 128) == 128);
71 |
72 | return i;
73 | }
74 |
75 | //copied from minecraft
76 | public static void writeVarInt(ByteBuffer buf, int i){
77 | while((i & - 128) != 0){
78 | buf.put((byte) (i & 127 | 128));
79 | i >>>= 7;
80 | }
81 |
82 | buf.put((byte) i);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/util/Pair.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.util;
2 |
3 | //I Didn't find any pair in Java common... so here is it
4 |
5 | import lombok.Getter;
6 |
7 | import javax.annotation.concurrent.Immutable;
8 | import java.util.Objects;
9 |
10 | /**
11 | * Pair, stores two objects.
12 | * @param Left object
13 | * @param Right object
14 | */
15 | @Getter
16 | @Immutable
17 | public class Pair {
18 | final L left;
19 | final R right;
20 |
21 | /**
22 | * Creates a pair from two values
23 | * @param left left member
24 | * @param right right member
25 | */
26 | public Pair(L left, R right){
27 | this.left = left;
28 | this.right = right;
29 | }
30 |
31 | @Override
32 | @SuppressWarnings("rawtypes")
33 | public boolean equals(Object o){
34 | if(o instanceof Pair){
35 | Pair o2 = (Pair) o;
36 | return Objects.equals(this.left, o2.left) && Objects.equals(right, o2.right);
37 | }
38 | return false;
39 | }
40 |
41 | @Override
42 | public int hashCode() {
43 | int hash = 0;
44 | hash = hash * 31 + (left == null ? 0 : left.hashCode());
45 | hash = hash * 31 + (right == null ? 0 : right.hashCode());
46 | return hash;
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return "Pair{" +
52 | "left=" + left +
53 | ", right=" + right +
54 | '}';
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/util/SetableSupplier.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.util;
2 |
3 | import java.util.function.Supplier;
4 |
5 | /*
6 | * I'll use this...
7 | */
8 | public class SetableSupplier implements Supplier {
9 | T object;
10 |
11 | /**
12 | * :D
13 | * @param object T
14 | */
15 | public void set(T object) {
16 | this.object = object;
17 | }
18 |
19 | @Override
20 | public T get() {
21 | return this.object;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/util/UUIDMap.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.util;
2 |
3 | import java.util.Collection;
4 | import java.util.HashMap;
5 | import java.util.Iterator;
6 | import java.util.UUID;
7 | import java.util.function.Predicate;
8 | import java.util.function.Supplier;
9 |
10 | //HashMap but with making my life easier
11 | public class UUIDMap> extends HashMap implements Iterable {
12 | public T put(T v){
13 | return this.put(v.get(), v);
14 | }
15 |
16 | public void addAll(Collection m) {
17 | for(T t : m){
18 | this.put(t);
19 | }
20 | }
21 |
22 |
23 | @Override
24 | public Iterator iterator() {
25 | return this.values().iterator();
26 | }
27 |
28 | public void add(T value) {
29 | this.put(value);
30 | }
31 |
32 | public boolean contains(T value) {
33 | return this.containsKey(value.get());
34 | }
35 |
36 | public void removeIf(Predicate predicate){
37 | this.values().removeIf(predicate);
38 | }
39 | }
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/util/Vec3d.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.util;
2 |
3 | /**
4 | * Three-dimensional double vector
5 | */
6 | public class Vec3d extends Vector3 {
7 |
8 | public Vec3d(Double x, Double y, Double z) {
9 | super(x, y, z);
10 | }
11 |
12 | public double squaredDistanceTo(Vec3d vec3d){
13 | double a = this.x - vec3d.x;
14 | double b = this.y - vec3d.y;
15 | double c = this.z - vec3d.z;
16 | return a*a + b*b + c*c;
17 | }
18 |
19 | /**
20 | * Scale the vector
21 | * @param scalar scalar
22 | * @return scaled vector
23 | */
24 | public Vec3d scale(double scalar) {
25 | return new Vec3d(this.getX() * scalar, this.getY() * scalar, this.getZ() * scalar);
26 | }
27 |
28 | /**
29 | * Add two vectors
30 | * @param other other vector
31 | * @return sum vector
32 | */
33 | public Vec3d add(Vec3d other) {
34 | return new Vec3d(this.getX() + other.getX(), this.getY() + other.getY(), this.getZ() + other.getZ());
35 | }
36 |
37 | /**
38 | * Dot product with other vector
39 | * @param other rhs operand
40 | * @return v
41 | */
42 | public double dotProduct(Vec3d other) {
43 | return this.getX() * other.getX() + this.getY() * other.getY() + this.getZ() * other.getZ();
44 | }
45 |
46 | /**
47 | * Cross product
48 | * @param other rhs operand
49 | * @return v
50 | */
51 | public Vec3d crossProduct(Vec3d other) {
52 | return new Vec3d(
53 | this.getY()*other.getZ() - this.getZ()*other.getY(),
54 | this.getZ()*other.getX() - this.getX()*other.getZ(),
55 | this.getX()*other.getY() - this.getY()*other.getX()
56 | );
57 | }
58 |
59 | /**
60 | * Subtract a vector from this
61 | * @param rhs rhs operand
62 | * @return v
63 | */
64 | public Vec3d subtract(Vec3d rhs) {
65 | //You could have guessed what will happen here.
66 | return add(rhs.scale(-1));
67 | }
68 |
69 | public double distanceTo(Vec3d vec3d){
70 | return Math.sqrt(squaredDistanceTo(vec3d));
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/util/Vec3f.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.util;
2 |
3 | import javax.annotation.concurrent.Immutable;
4 |
5 | @Immutable
6 | public class Vec3f extends Vector3 {
7 |
8 | public static final Vec3f ZERO = new Vec3f(0f, 0f, 0f);
9 |
10 | public Vec3f(float x, float y, float z) {
11 | super(x, y, z);
12 | }
13 |
14 | public double squaredDistanceTo(Vec3d vec3d){
15 | double a = this.x - vec3d.x;
16 | double b = this.y - vec3d.y;
17 | double c = this.z - vec3d.z;
18 | return a*a + b*b + c*c;
19 | }
20 |
21 | /**
22 | * Scale the vector
23 | * @param scalar scalar
24 | * @return scaled vector
25 | */
26 | public Vec3f scale(float scalar) {
27 | return new Vec3f(this.getX() * scalar, this.getY() * scalar, this.getZ() * scalar);
28 | }
29 |
30 | /**
31 | * Add two vectors
32 | * @param other other vector
33 | * @return sum vector
34 | */
35 | public Vec3f add(Vec3f other) {
36 | return new Vec3f(this.getX() + other.getX(), this.getY() + other.getY(), this.getZ() + other.getZ());
37 | }
38 |
39 | /**
40 | * Dot product with other vector
41 | * @param other rhs operand
42 | * @return v
43 | */
44 | public float dotProduct(Vec3f other) {
45 | return this.getX() * other.getX() + this.getY() * other.getY() + this.getZ() * other.getZ();
46 | }
47 |
48 | /**
49 | * Cross product
50 | * @param other rhs operand
51 | * @return v
52 | */
53 | public Vec3f crossProduct(Vec3f other) {
54 | return new Vec3f(
55 | this.getY()*other.getZ() - this.getZ()*other.getY(),
56 | this.getZ()*other.getX() - this.getX()*other.getZ(),
57 | this.getX()*other.getY() - this.getY()*other.getX()
58 | );
59 | }
60 |
61 | /**
62 | * Subtract a vector from this
63 | * @param rhs rhs operand
64 | * @return v
65 | */
66 | public Vec3f subtract(Vec3f rhs) {
67 | //You could have guessed what will happen here.
68 | return add(rhs.scale(-1));
69 | }
70 |
71 | public double distanceTo(Vec3d vec3d){
72 | return Math.sqrt(squaredDistanceTo(vec3d));
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/coreLib/src/main/java/dev/kosmx/playerAnim/core/util/Vector3.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.util;
2 |
3 | import javax.annotation.concurrent.Immutable;
4 | import java.util.Objects;
5 |
6 |
7 | /**
8 | * 3 dimensional generic vector implementation
9 | * @param value type
10 | */
11 | @Immutable
12 | public class Vector3 {
13 | N x, y, z;
14 |
15 | public Vector3(N x, N y, N z){
16 | this.x = x;
17 | this.y = y;
18 | this.z = z;
19 | }
20 |
21 | public N getX() {
22 | return x;
23 | }
24 |
25 | public N getY() {
26 | return y;
27 | }
28 |
29 | public N getZ() {
30 | return z;
31 | }
32 |
33 | @Override
34 | public boolean equals(Object o) {
35 | if (this == o) return true;
36 | if (!(o instanceof Vector3)) return false;
37 | Vector3> vector3 = (Vector3>) o;
38 | return Objects.equals(x, vector3.x) && Objects.equals(y, vector3.y) && Objects.equals(z, vector3.z);
39 | }
40 |
41 | @Override
42 | public int hashCode() {
43 | return Objects.hash(x, y, z);
44 | }
45 |
46 | @Override
47 | public String toString() {
48 | return "Vec3f[" + this.getX() + "; " + this.getY() + "; " + this.getZ() + "]";
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/coreLib/src/test/java/dev/kosmx/playerAnim/core/data/KeyframeAnimationTest.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data;
2 |
3 | import dev.kosmx.playerAnim.core.util.Ease;
4 | import org.junit.jupiter.api.Assertions;
5 | import org.junit.jupiter.api.Test;
6 |
7 | import java.util.Random;
8 |
9 | public class KeyframeAnimationTest {
10 | @Test
11 | public void testKeyframeAnimation() {
12 | KeyframeAnimation.StateCollection.State state = new KeyframeAnimation.StateCollection(0).x;
13 | // Easy case
14 | state.addKeyFrame(1, 0, Ease.CONSTANT);
15 | state.addKeyFrame(5, 0, Ease.CONSTANT);
16 | state.addKeyFrame(10, 10, Ease.CONSTANT);
17 | state.addKeyFrame(10, 10, Ease.CONSTANT);
18 |
19 | state.addKeyFrame(15, 10, Ease.CONSTANT);
20 |
21 | verify(state);
22 | state.getKeyFrames().clear();
23 |
24 |
25 | // random case
26 | Random random = new Random();
27 |
28 | for (int i = 0; i < 10000; i += random.nextInt(100)) {
29 | state.addKeyFrame(i, i, Ease.CONSTANT);
30 | }
31 |
32 | verify(state);
33 | }
34 |
35 | public static void verify(KeyframeAnimation.StateCollection.State state) {
36 |
37 | for (int t = 0; t < state.getKeyFrames().size(); t++) {
38 | // Iterative, 100% works algorithm
39 |
40 | int i = -1;
41 | while (state.getKeyFrames().size() > i + 1 && state.getKeyFrames().get(i + 1).tick <= t) {
42 | i++;
43 | }
44 |
45 | Assertions.assertEquals(i, state.findAtTick(t), "KeyframeAnimationTest failed at tick " + t);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/coreLib/src/test/java/dev/kosmx/playerAnim/core/data/LayerTest.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data;
2 |
3 | import dev.kosmx.playerAnim.api.layered.AnimationStack;
4 | import dev.kosmx.playerAnim.api.layered.IAnimation;
5 | import dev.kosmx.playerAnim.api.layered.ModifierLayer;
6 | import dev.kosmx.playerAnim.core.util.Pair;
7 | import org.junit.jupiter.api.Test;
8 |
9 | import java.lang.reflect.Field;
10 | import java.util.List;
11 | import java.util.Random;
12 |
13 | public class LayerTest {
14 |
15 | @Test
16 | public void testLayers() throws NoSuchFieldException, IllegalAccessException {
17 | AnimationStack stack = new AnimationStack();
18 | Random random = new Random();
19 | for (int i = 0; i < 128; i++) {
20 | stack.addAnimLayer(random.nextInt()%10000, new ModifierLayer<>());
21 | }
22 |
23 | //This should not be accessible while using it as an API, but for the test, it is completely reasonable
24 | Field layersRef = AnimationStack.class.getDeclaredField("layers");
25 | layersRef.setAccessible(true);
26 | List> layers = (List>)layersRef.get(stack);
27 |
28 | int i = Integer.MIN_VALUE;
29 | for (Pair layer : layers) {
30 | int n = layer.getLeft();
31 | if (n < i) {
32 | System.out.println(layers);
33 | throw new AssertionError("Layers are not in order");
34 | }
35 | i = n;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/coreLib/src/test/java/dev/kosmx/playerAnim/core/data/StringTest.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.core.data;
2 |
3 | import dev.kosmx.playerAnim.core.data.gson.GeckoLibSerializer;
4 | import org.junit.jupiter.api.Assertions;
5 | import org.junit.jupiter.api.Test;
6 |
7 |
8 | public class StringTest {
9 |
10 | @Test
11 | public void camelTest() {
12 | String str = "camel_case_string";
13 | String converted = "camelCaseString";
14 | Assertions.assertEquals(converted, GeckoLibSerializer.snake2Camel(str));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx4G
2 |
3 | minecraft_version=1.21.1
4 |
5 | archives_base_name=player-animation-lib
6 | #Major: API break, Minor: non-breaking but significant, Patch: minor bugfix/change + MC implementation fix
7 | mod_version=2.0.1
8 | maven_group=dev.kosmx.player-anim
9 |
10 |
11 | fabric_loader_version=0.16.9
12 | fabric_api_version=0.110.0+1.21.1
13 |
14 | forge_version=21.1.89
15 |
16 | # from localmaven TODO change
17 | bendy_lib=5.1
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KosmX/minecraftPlayerAnimator/93e08373367850a0e956550740adc684305d82ab/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KosmX/minecraftPlayerAnimator/93e08373367850a0e956550740adc684305d82ab/icon.png
--------------------------------------------------------------------------------
/minecraft/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | }
4 |
5 | repositories {
6 | mavenCentral()
7 | }
8 |
9 | subprojects {
10 | apply plugin: "dev.architectury.loom"
11 |
12 | loom {
13 | silentMojangMappingsLicense()
14 | }
15 |
16 | dependencies {
17 | minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
18 | // The following line declares the mojmap mappings, you may use other mappings as well
19 | mappings loom.officialMojangMappings()
20 | // The following line declares the yarn mappings you may select this one as well.
21 | // mappings "net.fabricmc:yarn:1.19+build.2:v2"
22 | }
23 | }
24 |
25 | if (keysExists) {
26 | task publishMod {
27 | finalizedBy(":${project.name}:fabric:modrinth")
28 | finalizedBy(":${project.name}:forge:modrinth")
29 | finalizedBy(":${project.name}:fabric:curseforge")
30 | finalizedBy(":${project.name}:forge:curseforge")
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/minecraft/common/build.gradle:
--------------------------------------------------------------------------------
1 | architectury {
2 | common("neoforge", "fabric")
3 | }
4 |
5 | loom {
6 | accessWidenerPath = file("src/main/resources/playerAnimator.accesswidener")
7 | }
8 |
9 | dependencies {
10 | // We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
11 | // Do NOT use other classes from fabric loader
12 | modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
13 | implementation project(':coreLib')
14 |
15 | //modApi "dev.architectury:architectury:${rootProject.architectury_version}"
16 |
17 | modCompileOnly "io.github.kosmx.bendy-lib:bendy-lib:${project.bendy_lib}"
18 | //modCompileOnly "maven.modrinth:3dskinlayers:1.5.2-fabric-1.19"
19 |
20 | }
21 |
22 | publishing {
23 | publications {
24 | mavenCommon(MavenPublication) {
25 | artifactId = rootProject.archives_base_name
26 | from components.java
27 |
28 | /*
29 | pom.withXml {
30 |
31 | def depsNode = asNode().appendNode("dependencies")
32 |
33 | def apiNode = depsNode.appendNode("dependency")
34 | apiNode.appendNode("groupId", project.group)
35 | apiNode.appendNode("artifactId", "animCore")
36 | apiNode.appendNode("version", project.version)
37 | apiNode.appendNode("scope", "compile")
38 | }//*/
39 | }
40 | }
41 |
42 | // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
43 | repositories {
44 | repositories {
45 |
46 | if (project.keysExists) {
47 | maven {
48 | url = 'https://maven.kosmx.dev/'
49 | credentials {
50 | username = project.keys.kosmx_maven_user
51 | password = project.keys.kosmx_maven
52 | }
53 | }
54 | maven {
55 | name = "GitHubPackages"
56 | url = "https://maven.pkg.github.com/kosmx/minecraftPlayerAnimator"
57 | credentials {
58 | username = System.getenv("GITHUB_ACTOR")
59 | password = System.getenv("GITHUB_TOKEN")
60 | }
61 | }
62 | } else {
63 | mavenLocal()
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/minecraft/common/src/main/java/dev/kosmx/playerAnim/impl/Helper.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.impl;
2 |
3 | import dev.architectury.injectables.annotations.ExpectPlatform;
4 | import org.jetbrains.annotations.ApiStatus;
5 | import org.jetbrains.annotations.Nullable;
6 |
7 | import java.util.concurrent.atomic.AtomicBoolean;
8 |
9 | /**
10 | * Helper Utility class
11 | */
12 | @ApiStatus.Internal
13 | public final class Helper {
14 |
15 | @Nullable
16 | private static AtomicBoolean isBendyLibLoaded = null;
17 |
18 | public static boolean isBendEnabled() {
19 | if (isBendyLibLoaded == null) isBendyLibLoaded = new AtomicBoolean(isBendyLibPresent());
20 | return isBendyLibLoaded.get();
21 | }
22 |
23 | @ExpectPlatform
24 | public static boolean isBendyLibPresent() {
25 | throw new AssertionError();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/minecraft/common/src/main/java/dev/kosmx/playerAnim/impl/IAnimatedPlayer.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.impl;
2 |
3 | import dev.kosmx.playerAnim.api.IPlayer;
4 | import dev.kosmx.playerAnim.api.layered.IAnimation;
5 | import dev.kosmx.playerAnim.impl.animation.AnimationApplier;
6 | import net.minecraft.resources.ResourceLocation;
7 | import org.jetbrains.annotations.ApiStatus;
8 | import org.jetbrains.annotations.NotNull;
9 | import org.jetbrains.annotations.Nullable;
10 |
11 | /**
12 | * Even if it is marked as internal API, this interface should not change
13 | */
14 |
15 | @ApiStatus.Internal
16 | public interface IAnimatedPlayer extends IPlayer {
17 |
18 | /**
19 | * @deprecated potential name conflict on mixin interface
20 | * use {@code IAnimatedPlayer#playerAnimator_getAnimation}
21 | */
22 | @Deprecated(forRemoval = true)
23 | default AnimationApplier getAnimation() {
24 | return playerAnimator_getAnimation();
25 | }
26 |
27 |
28 | AnimationApplier playerAnimator_getAnimation();
29 |
30 | /**
31 | * Get an animation associated with the player
32 | * @param id Animation identifier, please start with your modid to avoid collision
33 | * @return animation or null if not exists
34 | * @apiNote This function does not register the animation, just store it.
35 | */
36 | @Nullable
37 | IAnimation playerAnimator_getAnimation(@NotNull ResourceLocation id);
38 |
39 | /**
40 | * Set an animation associated with the player
41 | *
42 | * @param id Animation identifier. Please don't override/remove other mod animations, always use your modid!
43 | * @param animation animation to store in the player, null to clear stored animation
44 | * @return The previously stored animation.
45 | */
46 | @Nullable
47 | IAnimation playerAnimator_setAnimation(@NotNull ResourceLocation id, @Nullable IAnimation animation);
48 | }
49 |
--------------------------------------------------------------------------------
/minecraft/common/src/main/java/dev/kosmx/playerAnim/impl/IMutableModel.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.impl;
2 |
3 | import dev.kosmx.playerAnim.core.impl.AnimationProcessor;
4 | import dev.kosmx.playerAnim.core.util.SetableSupplier;
5 | import org.jetbrains.annotations.ApiStatus;
6 |
7 | @ApiStatus.Internal
8 | public interface IMutableModel {
9 |
10 | void setEmoteSupplier(SetableSupplier emoteSupplier);
11 |
12 | SetableSupplier getEmoteSupplier();
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/minecraft/common/src/main/java/dev/kosmx/playerAnim/impl/IPlayerModel.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.impl;
2 |
3 | import org.jetbrains.annotations.ApiStatus;
4 |
5 | @ApiStatus.Internal
6 | public interface IPlayerModel {
7 | void playerAnimator_prepForFirstPersonRender();
8 | }
9 |
--------------------------------------------------------------------------------
/minecraft/common/src/main/java/dev/kosmx/playerAnim/impl/IUpperPartHelper.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.impl;
2 |
3 | import org.jetbrains.annotations.ApiStatus;
4 |
5 | @ApiStatus.Internal
6 | public interface IUpperPartHelper {
7 | boolean isUpperPart();
8 |
9 | void setUpperPart(boolean bl);
10 | }
11 |
--------------------------------------------------------------------------------
/minecraft/common/src/main/java/dev/kosmx/playerAnim/impl/animation/AnimationApplier.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.impl.animation;
2 |
3 |
4 | import dev.kosmx.playerAnim.api.TransformType;
5 | import dev.kosmx.playerAnim.api.layered.IAnimation;
6 | import dev.kosmx.playerAnim.core.impl.AnimationProcessor;
7 | import dev.kosmx.playerAnim.core.util.MathHelper;
8 | import dev.kosmx.playerAnim.core.util.Pair;
9 | import dev.kosmx.playerAnim.core.util.Vec3f;
10 | import net.minecraft.client.model.geom.ModelPart;
11 | import org.jetbrains.annotations.ApiStatus;
12 |
13 | @ApiStatus.Internal
14 | public class AnimationApplier extends AnimationProcessor {
15 | public AnimationApplier(IAnimation animation) {
16 | super(animation);
17 | }
18 |
19 | public void updatePart(String partName, ModelPart part) {
20 | Vec3f pos = this.get3DTransform(partName, TransformType.POSITION, new Vec3f(part.x, part.y, part.z));
21 | part.x = pos.getX();
22 | part.y = pos.getY();
23 | part.z = pos.getZ();
24 | Vec3f rot = this.get3DTransform(partName, TransformType.ROTATION, new Vec3f( // clamp guards
25 | MathHelper.clampToRadian(part.xRot),
26 | MathHelper.clampToRadian(part.yRot),
27 | MathHelper.clampToRadian(part.zRot)));
28 | part.setRotation(rot.getX(), rot.getY(), rot.getZ());
29 | Vec3f scale = this.get3DTransform(partName, TransformType.SCALE,
30 | new Vec3f(part.xScale, part.yScale, part.zScale)
31 | );
32 | part.xScale = scale.getX();
33 | part.yScale = scale.getY();
34 | part.zScale = scale.getZ();
35 | if (!partName.equals("head")) {
36 | if (partName.equals("torso")) {
37 | Pair torsoBend = getBend(partName);
38 | Pair bodyBend = getBend("body");
39 | IBendHelper.INSTANCE.bend(part, new Pair<>(torsoBend.getLeft() + bodyBend.getLeft(), torsoBend.getRight() + bodyBend.getRight()));
40 | } else {
41 | IBendHelper.INSTANCE.bend(part, getBend(partName));
42 | }
43 | }
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/minecraft/common/src/main/java/dev/kosmx/playerAnim/impl/animation/BendHelper.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.impl.animation;
2 |
3 | import dev.kosmx.playerAnim.core.util.Pair;
4 | import io.github.kosmx.bendylib.ModelPartAccessor;
5 | import io.github.kosmx.bendylib.impl.BendableCuboid;
6 | import net.minecraft.client.model.geom.ModelPart;
7 | import net.minecraft.core.Direction;
8 | import org.jetbrains.annotations.ApiStatus;
9 | import org.jetbrains.annotations.Nullable;
10 |
11 | @ApiStatus.Internal
12 | public class BendHelper implements IBendHelper {
13 | @Override
14 | public void bend(ModelPart modelPart, float axis, float rotation){
15 | // Don't enable bend until rotation is bigger than epsilon. This should avoid unnecessary heavy calculations.
16 | if (Math.abs(rotation) >= 0.0001f) {
17 | ModelPartAccessor.optionalGetCuboid(modelPart, 0).ifPresent(mutableCuboid -> ((BendableCuboid) mutableCuboid.getAndActivateMutator("bend")).applyBend(axis, rotation));
18 | } else {
19 | ModelPartAccessor.optionalGetCuboid(modelPart, 0).ifPresent(mutableCuboid -> mutableCuboid.getAndActivateMutator(null));
20 | }
21 | }
22 |
23 | @Override
24 | public void bend(ModelPart modelPart, @Nullable Pair pair){
25 | if(pair != null) {
26 | this.bend(modelPart, pair.getLeft(), pair.getRight());
27 | }
28 | else {
29 | //ModelPartAccessor.getCuboid(modelPart, 0).getAndActivateMutator(null);
30 | ModelPartAccessor.optionalGetCuboid(modelPart, 0).ifPresent(mutableCuboid -> mutableCuboid.getAndActivateMutator(null));
31 | }
32 | }
33 |
34 | @Override
35 | public void initBend(ModelPart modelPart, Direction direction) {
36 | ModelPartAccessor.optionalGetCuboid(modelPart, 0).ifPresent(mutableModelPart -> mutableModelPart.registerMutator("bend", data -> new BendableCuboid.Builder().setDirection(direction).build(data)));
37 | }
38 |
39 | @Override
40 | public void initCapeBend(ModelPart modelPart) {
41 | ModelPartAccessor.optionalGetCuboid(modelPart, 0).ifPresent(mutableModelPart -> mutableModelPart.registerMutator("bend", data -> {
42 | data.pivot = 6;
43 | return new BendableCuboid.Builder().setDirection(Direction.UP).build(data);
44 | }));
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/minecraft/common/src/main/java/dev/kosmx/playerAnim/impl/animation/IBendHelper.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.impl.animation;
2 |
3 | import com.mojang.blaze3d.vertex.PoseStack;
4 | import dev.kosmx.playerAnim.core.util.Pair;
5 | import dev.kosmx.playerAnim.impl.Helper;
6 | import net.minecraft.client.model.geom.ModelPart;
7 | import net.minecraft.core.Direction;
8 | import org.jetbrains.annotations.ApiStatus;
9 | import org.jetbrains.annotations.Nullable;
10 | import org.joml.Quaternionf;
11 | import org.joml.Vector3f;
12 |
13 | @ApiStatus.Internal
14 | public interface IBendHelper {
15 |
16 | IBendHelper INSTANCE = Helper.isBendEnabled() ? new BendHelper() : new DummyBendable();
17 | static void rotateMatrixStack(PoseStack matrices, Pair pair){
18 | float offset = 0.375f;
19 | matrices.translate(0, offset, 0);
20 | float bend = pair.getRight();
21 | float axisf = - pair.getLeft();
22 | Vector3f axis = new Vector3f((float) Math.cos(axisf), 0, (float) Math.sin(axisf));
23 | matrices.mulPose(new Quaternionf().rotateAxis(bend, axis));
24 | matrices.translate(0, - offset, 0);
25 | }
26 |
27 | void bend(ModelPart modelPart, float a, float b);
28 |
29 | void bend(ModelPart modelPart, @Nullable Pair pair);
30 |
31 | void initBend(ModelPart modelPart, Direction direction);
32 |
33 | void initCapeBend(ModelPart modelPart);
34 |
35 | class DummyBendable implements IBendHelper {
36 |
37 | @Override
38 | public void bend(ModelPart modelPart, float a, float b) {
39 |
40 | }
41 |
42 | @Override
43 | public void bend(ModelPart modelPart, @org.jetbrains.annotations.Nullable Pair pair) {
44 |
45 | }
46 |
47 | @Override
48 | public void initBend(ModelPart modelPart, Direction direction) {
49 |
50 | }
51 |
52 | @Override
53 | public void initCapeBend(ModelPart modelPart) {
54 |
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/minecraft/common/src/main/java/dev/kosmx/playerAnim/impl/compat/skinLayers/SkinLayersTransformer.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.impl.compat.skinLayers;
2 |
3 | import org.jetbrains.annotations.ApiStatus;
4 | import org.slf4j.Logger;
5 |
6 | @ApiStatus.Internal
7 | public class SkinLayersTransformer {
8 |
9 | public static void init(Logger logger) {
10 | /*
11 | logger.info("Loading 3D skin compat");
12 |
13 | LayerFeatureTransformerAPI.setLayerTransformer((player, matrixStack, modelPart) -> {
14 | if (((IUpperPartHelper)modelPart).isUpperPart()) {
15 | IBendHelper.rotateMatrixStack(matrixStack, ((IAnimatedPlayer) player).playerAnimator_getAnimation().getBend("body"));
16 | }
17 | });
18 |
19 | */
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/minecraft/common/src/main/java/dev/kosmx/playerAnim/impl/mixin/MixinConfig.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.impl.mixin;
2 |
3 | import dev.kosmx.playerAnim.impl.Helper;
4 | import org.jetbrains.annotations.ApiStatus;
5 | import org.objectweb.asm.tree.ClassNode;
6 | import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
7 | import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
8 |
9 | import java.util.List;
10 | import java.util.Set;
11 |
12 | @ApiStatus.Internal
13 | public class MixinConfig implements IMixinConfigPlugin {
14 |
15 | @Override
16 | public void onLoad(String mixinPackage) {
17 |
18 | }
19 |
20 | @Override
21 | public String getRefMapperConfig() {
22 | return null;
23 | }
24 |
25 | @Override
26 | public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
27 | if (mixinClassName.endsWith("bendOnly") && !Helper.isBendEnabled()) {
28 | return false;
29 | }
30 | return true;
31 | }
32 |
33 | @Override
34 | public void acceptTargets(Set myTargets, Set otherTargets) {
35 |
36 | }
37 |
38 | @Override
39 | public List getMixins() {
40 | return null;
41 | }
42 |
43 | @Override
44 | public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
45 |
46 | }
47 |
48 | @Override
49 | public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
50 |
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/minecraft/common/src/main/java/dev/kosmx/playerAnim/minecraftApi/PlayerAnimationAccess.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.minecraftApi;
2 |
3 | import dev.kosmx.playerAnim.api.IPlayer;
4 | import dev.kosmx.playerAnim.api.layered.AnimationStack;
5 | import dev.kosmx.playerAnim.api.layered.IAnimation;
6 | import dev.kosmx.playerAnim.core.impl.event.Event;
7 | import dev.kosmx.playerAnim.impl.IAnimatedPlayer;
8 | import net.fabricmc.api.EnvType;
9 | import net.fabricmc.api.Environment;
10 | import net.minecraft.client.player.AbstractClientPlayer;
11 | import net.minecraft.resources.ResourceLocation;
12 | import org.jetbrains.annotations.NotNull;
13 | import org.jetbrains.annotations.Nullable;
14 |
15 | @Environment(EnvType.CLIENT)
16 | public final class PlayerAnimationAccess {
17 |
18 | /**
19 | * Get the animation stack for a player entity on the client.
20 | *
21 | * Or you can use {@code ((IPlayer) player).getAnimationStack();}
22 | *
23 | * @param player The ClientPlayer object
24 | * @return The players' animation stack
25 | */
26 | public static AnimationStack getPlayerAnimLayer(AbstractClientPlayer player) throws IllegalArgumentException {
27 | if (player instanceof IPlayer) {
28 | return ((IPlayer) player).getAnimationStack();
29 | } else throw new IllegalArgumentException(player + " is not a player or library mixins failed");
30 | }
31 |
32 | /**
33 | * Allows mods to store animation layers associated with player.
34 | * Stored data does not get automatically registered.
35 | * @param player player entity
36 | * @return data accessor type, you can use get() and set() on it (kotlin getter/setter compatible)
37 | * @throws IllegalArgumentException if the given argument is not a player, or api mixins have failed (normally never)
38 | * @implNote data is stored in the player object (using mixins), using it is more efficient than any objectMap as objectMap solution does not know when to delete the data.
39 | */
40 | public static PlayerAssociatedAnimationData getPlayerAssociatedData(@NotNull AbstractClientPlayer player) {
41 | if (player instanceof IAnimatedPlayer animatedPlayer) {
42 | return new PlayerAssociatedAnimationData(animatedPlayer);
43 | } else throw new IllegalArgumentException(player + " is not a player or library mixins failed");
44 | }
45 |
46 | /**
47 | * If you don't want to create your own mixin, you can use this event to add animation to players
48 | * The event will fire for every player and if the player reloads, it will fire again.
49 | *
50 | * NOTE: When the event fires, {@link IPlayer#getAnimationStack()} will be null, you'll have to use the given stack.
51 | */
52 | public static final Event REGISTER_ANIMATION_EVENT = new Event<>(AnimationRegister.class, listeners -> (player, animationStack) -> {
53 | for (AnimationRegister listener : listeners) {
54 | listener.registerAnimation(player, animationStack);
55 | }
56 | });
57 |
58 | @FunctionalInterface
59 | public interface AnimationRegister {
60 | /**
61 | * Player object is in construction, it will be invoked when you can register animation
62 | * It will be invoked for every player only ONCE (it isn't a tick function)
63 | * @param player Client player object, can be the main player or other player
64 | * @param animationStack the players AnimationStack, unique for every player
65 | */
66 | void registerAnimation(@NotNull AbstractClientPlayer player, @NotNull AnimationStack animationStack);
67 | }
68 |
69 | public static class PlayerAssociatedAnimationData {
70 | @NotNull
71 | private final IAnimatedPlayer player;
72 |
73 | public PlayerAssociatedAnimationData(@NotNull IAnimatedPlayer player) {
74 | this.player = player;
75 | }
76 |
77 | /**
78 | * Get an animation associated with the player
79 | * @param id Animation identifier, please start with your modid to avoid collision
80 | * @return animation or null if not exists
81 | * @apiNote This function does not register the animation, just store it.
82 | */
83 | @Nullable public IAnimation get(@NotNull ResourceLocation id) {
84 | return player.playerAnimator_getAnimation(id);
85 | }
86 |
87 | /**
88 | * Set an animation associated with the player
89 | *
90 | * @param id Animation identifier. Please don't override/remove other mod animations, always use your modid!
91 | * @param animation animation to store in the player, null to clear stored animation
92 | * @return The previously stored animation.
93 | */
94 | @Nullable public IAnimation set(@NotNull ResourceLocation id, @Nullable IAnimation animation) {
95 | return player.playerAnimator_setAnimation(id, animation);
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/minecraft/common/src/main/java/dev/kosmx/playerAnim/minecraftApi/PlayerAnimationFactory.java:
--------------------------------------------------------------------------------
1 | package dev.kosmx.playerAnim.minecraftApi;
2 |
3 | import dev.kosmx.playerAnim.api.layered.AnimationStack;
4 | import dev.kosmx.playerAnim.api.layered.IAnimation;
5 | import net.minecraft.client.player.AbstractClientPlayer;
6 | import net.minecraft.resources.ResourceLocation;
7 | import org.jetbrains.annotations.ApiStatus;
8 | import org.jetbrains.annotations.NotNull;
9 | import org.jetbrains.annotations.Nullable;
10 |
11 | import java.util.ArrayList;
12 | import java.util.List;
13 | import java.util.Map;
14 | import java.util.Optional;
15 | import java.util.function.Function;
16 |
17 | /**
18 | * Animation factory, the factory will be invoked whenever a client-player is constructed.
19 | * The returned animation will be automatically registered and added to playerAssociated data.
20 | *