create_ponder_wonder

Exports Create ponders to disk.
git clone git://git.oshgnacknak.de/create_ponder_wonder.git
Log | Files | Refs | README

commit e9d8fceb80d65a5e11eead88a63729b031afc2e4
parent 184c8892982dc4ec1bf159986235089ce6cb6f87
Author: Oshgnacknak <osh@oshgnacknak.de>
Date:   Sat, 10 Apr 2021 16:48:26 +0200

Implement asynchronous renderer

Diffstat:
Msrc/main/java/de/oshgnacknak/create_ponder_wonder/CreatePonderWonder.java | 9+++++++++
Asrc/main/java/de/oshgnacknak/create_ponder_wonder/PonderRenderer.java | 191+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/main/java/de/oshgnacknak/create_ponder_wonder/commands/StartRenderingCommand.java | 18++++++++++++++++++
Asrc/main/java/de/oshgnacknak/create_ponder_wonder/commands/StopRenderingCommand.java | 15+++++++++++++++
4 files changed, 233 insertions(+), 0 deletions(-)

diff --git a/src/main/java/de/oshgnacknak/create_ponder_wonder/CreatePonderWonder.java b/src/main/java/de/oshgnacknak/create_ponder_wonder/CreatePonderWonder.java @@ -1,6 +1,7 @@ package de.oshgnacknak.create_ponder_wonder; import de.oshgnacknak.create_ponder_wonder.commands.AllCommands; +import net.minecraft.client.Minecraft; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.Mod; import org.apache.logging.log4j.LogManager; @@ -11,7 +12,15 @@ public class CreatePonderWonder { public static final String MODID = "create_ponder_wonder"; public static final Logger LOGGER = LogManager.getLogger(MODID); + public static final PonderRenderer PONDER_RENDERER = new PonderRenderer(); + public CreatePonderWonder() { MinecraftForge.EVENT_BUS.addListener(AllCommands::register); } + + public static void chat(String msg) { + if (Minecraft.getInstance().player != null) { + Minecraft.getInstance().player.sendChatMessage(msg); + } + } } \ No newline at end of file diff --git a/src/main/java/de/oshgnacknak/create_ponder_wonder/PonderRenderer.java b/src/main/java/de/oshgnacknak/create_ponder_wonder/PonderRenderer.java @@ -0,0 +1,190 @@ +package de.oshgnacknak.create_ponder_wonder; + +import com.simibubi.create.foundation.ponder.PonderRegistry; +import com.simibubi.create.foundation.ponder.PonderWonderUI; +import io.netty.util.concurrent.GlobalEventExecutor; +import io.netty.util.concurrent.Promise; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.NativeImage; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class PonderRenderer { + + private static final int FPS = 60; + private static final int MAX_FRAMES = 15; + private static final int MAX_PONDERS_AT_ONCE = 10; + + private final Lock lock; + private final BlockingQueue<PonderWonderUI> pondersToRender; + private final AtomicInteger currentlyRendering; + private final Condition renderDone; + + private ExecutorService executorService; + private boolean rendering; + private boolean allEnqueued; + private String basePath; + + public PonderRenderer() { + lock = new ReentrantLock(); + pondersToRender = new ArrayBlockingQueue<>(MAX_PONDERS_AT_ONCE); + currentlyRendering = new AtomicInteger(); + renderDone = lock.newCondition(); + } + + public void start(String basePath) { + if (rendering) { + CreatePonderWonder.chat("Error: cannot be done twice"); + throw new IllegalStateException("Cannot start rendering twice"); + } + + rendering = true; + this.basePath = basePath; + currentlyRendering.set(0); + pondersToRender.clear(); + + executorService = Executors.newCachedThreadPool(); + executorService.submit(this::renderPonders); + executorService.submit(this::enqueueAllPonders); + + CreatePonderWonder.LOGGER.info("Started rendering ponders"); + CreatePonderWonder.chat("Started rendering ponders"); + } + + public void stop() { + if (!rendering) { + CreatePonderWonder.chat("Aleady stopped..."); + return; + } + + CreatePonderWonder.LOGGER.warn("Stopping rendering ponders abruptly"); + CreatePonderWonder.chat("Stopping rendering ponders abruptly"); + + try { + executorService.shutdown(); + while (!executorService.awaitTermination(3, TimeUnit.SECONDS)) { + executorService.shutdownNow(); + } + rendering = false; + + CreatePonderWonder.LOGGER.info("Stopped rendering ponders"); + CreatePonderWonder.chat("Stopped rendering ponders"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private void renderPonders() { + try { + while (rendering) { + if (currentlyRendering.get() < MAX_PONDERS_AT_ONCE) { + renderPonder(pondersToRender.take()); + } else { + renderDone.wait(); + } + + if (allEnqueued) { + rendering = false; + CreatePonderWonder.LOGGER.info("All ponders rendered: {}", basePath); + CreatePonderWonder.chat("All ponders rendered: " + basePath); + return; + } + } + } catch (Exception e) { + CreatePonderWonder.chat("Error: " + e.getMessage()); + CreatePonderWonder.LOGGER.error("Exception whilst rendering ponders", e); + } + } + + private void renderPonder(PonderWonderUI ponder) { + lock.lock(); + + try { + currentlyRendering.incrementAndGet(); + Path path = renderFrames(ponder); + + CreatePonderWonder.chat("Finished rendering Ponder: " + path); + CreatePonderWonder.LOGGER.info("Finished rendering Ponder: {}", path); + + } catch (IOException | InterruptedException | ExecutionException e) { + CreatePonderWonder.chat("Error: " + e.getMessage()); + CreatePonderWonder.LOGGER.error("Could not save image", e); + } finally { + currentlyRendering.decrementAndGet(); + renderDone.signal(); + lock.unlock(); + } + } + + private Path renderFrames(PonderWonderUI ponder) throws InterruptedException, ExecutionException, IOException { + Path path = Paths.get( + basePath, + CreatePonderWonder.MODID, + ponder.getActiveScene().getString("out")); + Files.createDirectories(path); + + for (int frame = 0; frame < MAX_FRAMES; frame++) { + Promise<NativeImage> promise = renderFrame(ponder, frame); + NativeImage img = promise.get(); + + Path out = path.resolve(String.format("%06d.png", frame)); + img.write(out); + + if (frame % 3 == 2) { + ponder.tick(); + } + } + + return path; + } + + public Promise<NativeImage> renderFrame(PonderWonderUI ponder, int frame) { + Promise<NativeImage> promise = GlobalEventExecutor.INSTANCE.newPromise(); + + float pt = (frame % FPS) / (FPS / 3.0f); + Minecraft.getInstance().field_213275_aU.add(() -> { + try { + NativeImage img = RenderUtils.render(ms -> + ponder.ponderWonderRenderWindow(ms, pt)); + promise.setSuccess(img); + } catch (Exception e) { + promise.setFailure(e); + } + }); + + return promise; + } + + private void enqueueAllPonders() { + allEnqueued = false; + Iterable<PonderWonderUI> ponders = getPonders(); + + try { + for (PonderWonderUI ponder : ponders) { + pondersToRender.put(ponder); + } + allEnqueued = true; + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private Iterable<PonderWonderUI> getPonders() { + return PonderRegistry.all + .values() + .stream() + .map(PonderRegistry::compile) + .flatMap(List::stream) + .map(PonderWonderUI::new) + ::iterator; + } +}+ \ No newline at end of file diff --git a/src/main/java/de/oshgnacknak/create_ponder_wonder/commands/StartRenderingCommand.java b/src/main/java/de/oshgnacknak/create_ponder_wonder/commands/StartRenderingCommand.java @@ -0,0 +1,18 @@ +package de.oshgnacknak.create_ponder_wonder.commands; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import de.oshgnacknak.create_ponder_wonder.CreatePonderWonder; +import net.minecraft.command.CommandSource; + +public class StartRenderingCommand implements Command<CommandSource> { + + @Override + public int run(CommandContext<CommandSource> context) throws CommandSyntaxException { + String path = StringArgumentType.getString(context, "path"); + CreatePonderWonder.PONDER_RENDERER.start(path); + return SINGLE_SUCCESS; + } +} diff --git a/src/main/java/de/oshgnacknak/create_ponder_wonder/commands/StopRenderingCommand.java b/src/main/java/de/oshgnacknak/create_ponder_wonder/commands/StopRenderingCommand.java @@ -0,0 +1,15 @@ +package de.oshgnacknak.create_ponder_wonder.commands; + +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import de.oshgnacknak.create_ponder_wonder.CreatePonderWonder; +import net.minecraft.command.CommandSource; + +public class StopRenderingCommand implements com.mojang.brigadier.Command<net.minecraft.command.CommandSource> { + + @Override + public int run(CommandContext<CommandSource> context) throws CommandSyntaxException { + CreatePonderWonder.PONDER_RENDERER.stop(); + return SINGLE_SUCCESS; + } +}