diff --git a/src/main/java/com/glisco/isometricrenders/property/DefaultPropertyBundle.java b/src/main/java/com/glisco/isometricrenders/property/DefaultPropertyBundle.java index 0c4a2b3..b393c00 100644 --- a/src/main/java/com/glisco/isometricrenders/property/DefaultPropertyBundle.java +++ b/src/main/java/com/glisco/isometricrenders/property/DefaultPropertyBundle.java @@ -75,10 +75,15 @@ public void applyToViewMatrix(Matrix4fStack modelViewStack) { this.updateAndApplyRotationOffset(modelViewStack); } - public float rotationOffset() { + public float getRotationOffset() { return this.rotationOffset; } + public void setRotationOffset(int offset) { + this.rotationOffset = offset; + this.rotationOffsetUpdated = true; + } + protected void updateAndApplyRotationOffset(Matrix4fStack modelViewStack) { if (rotationSpeed.get() != 0) { if (!this.rotationOffsetUpdated) { diff --git a/src/main/java/com/glisco/isometricrenders/render/DefaultRenderable.java b/src/main/java/com/glisco/isometricrenders/render/DefaultRenderable.java index 0ae4411..29d0d04 100644 --- a/src/main/java/com/glisco/isometricrenders/render/DefaultRenderable.java +++ b/src/main/java/com/glisco/isometricrenders/render/DefaultRenderable.java @@ -51,7 +51,7 @@ protected void withParticleCamera(Consumer action) { Camera camera = MinecraftClient.getInstance().getEntityRenderDispatcher().camera; float previousYaw = camera.getYaw(), previousPitch = camera.getPitch(); - ((CameraInvoker) camera).isometric$setRotation(this.properties().rotation.get() + 180 + this.properties().rotationOffset(), this.properties().slant.get()); + ((CameraInvoker) camera).isometric$setRotation(this.properties().rotation.get() + 180 + this.properties().getRotationOffset(), this.properties().slant.get()); action.accept(camera); ((CameraInvoker) camera).isometric$setRotation(previousYaw, previousPitch); diff --git a/src/main/java/com/glisco/isometricrenders/screen/RenderScreen.java b/src/main/java/com/glisco/isometricrenders/screen/RenderScreen.java index 92a8109..fd622c1 100644 --- a/src/main/java/com/glisco/isometricrenders/screen/RenderScreen.java +++ b/src/main/java/com/glisco/isometricrenders/screen/RenderScreen.java @@ -257,6 +257,9 @@ protected void build(FlowLayout rootComponent) { try (var builder = IsometricUI.row(rightColumn)) { this.exportAnimationButton = Components.button(Translate.gui("export_animation"), button -> { if (this.memoryGuard.canFit(this.estimateMemoryUsage(exportFrames)) || Screen.hasShiftDown()) { + if (this.renderable.properties() instanceof DefaultPropertyBundle dpb) { + dpb.setRotationOffset(0); + } this.remainingAnimationFrames = exportFrames; this.client.getWindow().setFramerateLimit(Integer.parseInt(framerateField.getText())); @@ -271,7 +274,7 @@ protected void build(FlowLayout rootComponent) { builder.row.child(Components.button(Translate.gui("format." + animationFormat.extension), button -> { animationFormat = animationFormat.next(); button.setMessage(Translate.gui("format." + animationFormat.extension)); - }).horizontalSizing(Sizing.fixed(35))); + }).horizontalSizing(Sizing.fixed(40))); } IsometricUI.dynamicLabel(rightColumn, () -> { @@ -407,8 +410,10 @@ public void render(DrawContext context, int mouseX, int mouseY, float delta) { overwriteLatest.set(overwriteValue); if (throwable != null) return; - this.exportAnimationButton.setMessage(Translate.gui("converting")); - this.client.execute(() -> this.notify(Translate.gui("converting_image_sequence"))); + if (animationFormat != FFmpegDispatcher.Format.SERIES) { + this.exportAnimationButton.setMessage(Translate.gui("converting")); + this.client.execute(() -> this.notify(Translate.gui("converting_image_sequence"))); + } FFmpegDispatcher.assemble( this.renderable.exportPath(), diff --git a/src/main/java/com/glisco/isometricrenders/util/ExportPathSpec.java b/src/main/java/com/glisco/isometricrenders/util/ExportPathSpec.java index 87626b9..9c3428d 100644 --- a/src/main/java/com/glisco/isometricrenders/util/ExportPathSpec.java +++ b/src/main/java/com/glisco/isometricrenders/util/ExportPathSpec.java @@ -30,6 +30,9 @@ public Path resolveOffset() { public File resolveFile(String extension) { return ImageIO.next(exportRoot().resolve(this.effectiveOffset()).resolve(this.filename + "." + extension)).toFile(); } + public Path resolveDir() { + return ImageIO.next(exportRoot().resolve(this.effectiveOffset()).resolve(this.filename)); + } public ExportPathSpec relocate(String newOffset) { return new ExportPathSpec(newOffset, this.filename, this.ignoreSaveIntoRoot); diff --git a/src/main/java/com/glisco/isometricrenders/util/FFmpegDispatcher.java b/src/main/java/com/glisco/isometricrenders/util/FFmpegDispatcher.java index 08a9508..15d0337 100644 --- a/src/main/java/com/glisco/isometricrenders/util/FFmpegDispatcher.java +++ b/src/main/java/com/glisco/isometricrenders/util/FFmpegDispatcher.java @@ -3,6 +3,7 @@ import com.glisco.isometricrenders.IsometricRenders; import com.glisco.isometricrenders.property.GlobalProperties; import net.minecraft.util.Util; +import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; @@ -13,6 +14,8 @@ import java.util.List; import java.util.concurrent.CompletableFuture; +import static com.glisco.isometricrenders.property.GlobalProperties.animationFormat; + public class FFmpegDispatcher { private static Boolean ffmpegDetected = null; @@ -59,6 +62,25 @@ public static CompletableFuture detectFFmpeg() { public static CompletableFuture assemble(ExportPathSpec target, Path sourcePath, Format format) { target.resolveOffset().toFile().mkdirs(); + if (animationFormat == FFmpegDispatcher.Format.SERIES) { + try { + Path export = target.resolveDir(); + if (Files.exists(export)) + FileUtils.deleteDirectory(export.toFile()); + Files.createDirectory(export); + var files = Files.list(sourcePath) + .filter(path -> path.getFileName().toString().matches("seq_\\d+\\.png")) + .toList(); + for (var file : files) + Files.move(file, export.resolve(file.getFileName().toString().substring(4))); + return CompletableFuture.completedFuture(export.toFile()); + } + catch (IOException e) { + IsometricRenders.LOGGER.error("Could not launch ffmpeg", e); + return CompletableFuture.failedFuture(e); + } + } + final var defaultArgs = new ArrayList<>(List.of(new String[]{ "ffmpeg", "-y", @@ -105,7 +127,8 @@ public static CompletableFuture assemble(ExportPathSpec target, Path sourc public enum Format { APNG("apng", new String[]{"-plays", "0"}), GIF("gif", new String[]{"-plays", "0", "-pix_fmt", "yuv420p"}), - MP4("mp4", new String[]{"-preset", "slow", "-crf", "20", "-pix_fmt", "yuv420p"}); + MP4("mp4", new String[]{"-preset", "slow", "-crf", "20", "-pix_fmt", "yuv420p"}), + SERIES("series", new String[]{}); public final String extension; public final String[] arguments; @@ -117,9 +140,10 @@ public enum Format { public Format next() { return switch (this) { - case MP4 -> APNG; case APNG -> GIF; case GIF -> MP4; + case MP4 -> SERIES; + case SERIES -> APNG; }; } } diff --git a/src/main/resources/assets/isometric-renders/lang/en_us.json b/src/main/resources/assets/isometric-renders/lang/en_us.json index c69be38..bbbcf27 100644 --- a/src/main/resources/assets/isometric-renders/lang/en_us.json +++ b/src/main/resources/assets/isometric-renders/lang/en_us.json @@ -51,6 +51,7 @@ "gui.isometric-renders.format.mp4": "MP4", "gui.isometric-renders.format.apng": "APNG", "gui.isometric-renders.format.gif": "GIF", + "gui.isometric-renders.format.series": "SERIES", "gui.isometric-renders.detecting_ffmpeg": "Detecting FFmpeg...", "gui.isometric-renders.no_ffmpeg_1": "FFmpeg was not detected", "gui.isometric-renders.no_ffmpeg_2": "Please download it from",