Skip to content

Commit

Permalink
1.20.4, attempt at optimizing animations
Browse files Browse the repository at this point in the history
  • Loading branch information
FoundationGames committed Jun 14, 2024
1 parent 2d08028 commit 6b22ca1
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 56 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
id 'fabric-loom' version '1.2-SNAPSHOT'
id 'fabric-loom' version '1.6-SNAPSHOT'
}

sourceCompatibility = JavaVersion.VERSION_17
Expand Down
12 changes: 6 additions & 6 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
org.gradle.jvmargs=-Xmx1G

minecraft_version=1.20.1
yarn_mappings=1.20.1+build.3
loader_version=0.14.21
minecraft_version=1.20.4
yarn_mappings=1.20.4+build.3
loader_version=0.15.11

#Fabric api
fabric_version=0.83.1+1.20.1
# Fabric API
fabric_version=0.97.1+1.20.4

mod_version = 0.6+1.20
mod_version = 0.6.1+1.20.4
maven_group = io.github.foundationgames
archives_base_name = animatica

2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,32 @@
import com.google.common.collect.ImmutableList;
import io.github.foundationgames.animatica.Animatica;
import io.github.foundationgames.animatica.util.TextureUtil;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.texture.NativeImageBackedTexture;
import net.minecraft.resource.ResourceManager;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AnimatedTexture extends NativeImageBackedTexture {
public static final ExecutorService EXECUTORS = Executors.newFixedThreadPool(4);

public final Animation[] anims;
private final NativeImage original;
private int frame = 0;

private CompletableFuture<Void> frameWaitingOn = null;

public static Optional<AnimatedTexture> tryCreate(ResourceManager resources, Identifier targetTexId, List<AnimationMeta> anims) {
try (var targetTexResource = resources.getResourceOrThrow(targetTexId).getInputStream()) {
return Optional.of(new AnimatedTexture(resources, anims, NativeImage.read(targetTexResource)));
Expand All @@ -36,7 +46,7 @@ public AnimatedTexture(ResourceManager resources, List<AnimationMeta> metas, Nat
}
this.original = image;

updateAndDraw(this.getImage(), true);
updateAndDraw(this.getImage(), true, MinecraftClient.getInstance());
this.upload();
}

Expand All @@ -50,7 +60,15 @@ public boolean canLoop() {
return true;
}

public boolean updateAndDraw(NativeImage image, boolean force) {
private @Nullable CompletableFuture<Void> getFrameWaitingOn() {
if (this.frameWaitingOn != null && this.frameWaitingOn.isDone()) {
this.frameWaitingOn = null;
}

return this.frameWaitingOn;
}

public void updateAndDraw(NativeImage image, boolean force, Executor exec) {
boolean changed = false;

if (canLoop()) {
Expand All @@ -69,31 +87,34 @@ public boolean updateAndDraw(NativeImage image, boolean force) {
}

if (changed || force) {
image.copyFrom(this.original);
// Skip if still waiting for a frame to finish
if (this.getFrameWaitingOn() == null) {
this.frameWaitingOn = CompletableFuture.supplyAsync(() -> {
image.copyFrom(this.original);

Phase phase;
for (var anim : anims) {
phase = anim.getCurrentPhase();
if (phase instanceof InterpolatedPhase iPhase) {
TextureUtil.blendCopy(anim.sourceTexture, 0, iPhase.prevV, 0, iPhase.v, anim.width, anim.height, image, anim.targetX, anim.targetY, iPhase.blend.getBlend(anim.getPhaseFrame()));
} else {
TextureUtil.copy(anim.sourceTexture, 0, phase.v, anim.width, anim.height, image, anim.targetX, anim.targetY);
}
}

Phase phase;
for (var anim : anims) {
phase = anim.getCurrentPhase();
if (phase instanceof InterpolatedPhase iPhase) {
TextureUtil.blendCopy(anim.sourceTexture, 0, iPhase.prevV, 0, iPhase.v, anim.width, anim.height, image, anim.targetX, anim.targetY, iPhase.blend.getBlend(anim.getPhaseFrame()));
} else {
TextureUtil.copy(anim.sourceTexture, 0, phase.v, anim.width, anim.height, image, anim.targetX, anim.targetY);
}
return null;
}, exec).thenAccept(v -> MinecraftClient.getInstance().execute(this::upload));
}
}

for (var anim : anims) {
anim.advance();
}
frame++;

return changed;
}

public void tick() {
if (this.updateAndDraw(this.getImage(), false)) {
this.upload();
}
this.updateAndDraw(this.getImage(), false, EXECUTORS);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.github.foundationgames.animatica.mixin;

import net.minecraft.client.texture.NativeImage;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

@Mixin(NativeImage.class)
public interface NativeImageAccessor {
@Accessor("pointer")
long getPointer();
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package io.github.foundationgames.animatica.util;

import io.github.foundationgames.animatica.mixin.NativeImageAccessor;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.util.math.MathHelper;
import org.lwjgl.system.MemoryUtil;

public enum TextureUtil {;
private static final long SIZEOF_INT = 4;

/**
* Copy a section of an image into another image
Expand All @@ -18,20 +21,20 @@ public enum TextureUtil {;
* @param dv The v coordinate on the destination image to place the selection at
*/
public static void copy(NativeImage src, int u, int v, int w, int h, NativeImage dest, int du, int dv) {
// iterate through the entire section of the image to be copied over
for (int rx = 0; rx < w; rx++) {
for (int ry = 0; ry < h; ry++) {
// the current x/y coordinates in the source image
int srcX = u + rx;
int srcY = v + ry;
// the corresponding target x/y coordinates in the target image
int trgX = du + rx;
int trgY = dv + ry;

// set the color of the target pixel on the destination image
// to the color from the corresponding pixel on the source image
dest.setColor(trgX, trgY, src.getColor(srcX, srcY));
}
w = MathHelper.clamp(dest.getWidth() - du, 0, w);
h = MathHelper.clamp(dest.getHeight() - dv, 0, h);

long srcPtr = ((NativeImageAccessor)(Object)src).getPointer();
long dstPtr = ((NativeImageAccessor)(Object)dest).getPointer();

for (int row = 0; row < h; row++) {
int srcRowIdx = ((v + row) * src.getWidth()) + u;
var srcRow = MemoryUtil.memIntBuffer(srcPtr + (srcRowIdx * SIZEOF_INT), w);

int trgRowIdx = ((dv + row) * dest.getWidth()) + du;
var trgRow = MemoryUtil.memIntBuffer(dstPtr + (trgRowIdx * SIZEOF_INT), w);

MemoryUtil.memCopy(srcRow, trgRow);
}
}

Expand All @@ -52,22 +55,24 @@ public static void copy(NativeImage src, int u, int v, int w, int h, NativeImage
* second (0 = solid first image, 1 = solid second image)
*/
public static void blendCopy(NativeImage src, int u0, int v0, int u1, int v1, int w, int h, NativeImage dest, int du, int dv, float blend) {
// iterate through the entire section of the image to be copied over
for (int rx = 0; rx < w; rx++) {
for (int ry = 0; ry < h; ry++) {
// the first set of x/y coordinates in the source image
int srcX0 = u0 + rx;
int srcY0 = v0 + ry;
// the second set of x/y coordinates in the source image
int srcX1 = u1 + rx;
int srcY1 = v1 + ry;
// the corresponding target x/y coordinates in the target image
int trgX = du + rx;
int trgY = dv + ry;

// set the color of the target pixel on the destination image to a blend
// of the colors from the corresponding pixels on the source image
dest.setColor(trgX, trgY, lerpColor(src.getFormat(), src.getColor(srcX0, srcY0), src.getColor(srcX1, srcY1), blend));
w = MathHelper.clamp(dest.getWidth() - du, 0, w);
h = MathHelper.clamp(dest.getHeight() - dv, 0, h);

long srcPtr = ((NativeImageAccessor)(Object)src).getPointer();
long dstPtr = ((NativeImageAccessor)(Object)dest).getPointer();

for (int row = 0; row < h; row++) {
int src0RowIdx = ((v0 + row) * src.getWidth()) + u0;
var src0Row = MemoryUtil.memIntBuffer(srcPtr + (src0RowIdx * SIZEOF_INT), w);

int src1RowIdx = ((v1 + row) * src.getWidth()) + u1;
var src1Row = MemoryUtil.memIntBuffer(srcPtr + (src1RowIdx * SIZEOF_INT), w);

int trgRowIdx = ((dv + row) * dest.getWidth()) + du;
var trgRow = MemoryUtil.memIntBuffer(dstPtr + (trgRowIdx * SIZEOF_INT), w);

for (int col = 0; col < w; col++) {
trgRow.put(col, lerpColor(src.getFormat(), src0Row.get(col), src1Row.get(col), blend));
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/animatica.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
"package": "io.github.foundationgames.animatica.mixin",
"compatibilityLevel": "JAVA_16",
"client": [
"RenderSystemMixin",
"IdentifierMixin",
"NativeImageAccessor",
"RenderSystemMixin",
"VideoOptionsScreenMixin"
],
"injectors": {
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"depends": {
"fabricloader": ">=0.11.3",
"fabric": "*",
"minecraft": ["1.19.4", "1.20.x"],
"minecraft": ["1.20.x"],
"java": ">=16"
}
}

0 comments on commit 6b22ca1

Please sign in to comment.