package com.replaymod.render.capturer; import com.replaymod.render.frame.OpenGlFrame; import com.replaymod.render.frame.RealLensOpenGlFrame; import com.replaymod.render.rendering.Channel; import com.replaymod.replaystudio.pathing.path.Path; import com.replaymod.simplepathing.ReplayModSimplePathing; import com.replaymod.pathing.properties.LensProperties; import org.apache.commons.lang3.tuple.Triple; import java.util.Collections; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; public class RealLensOpenGlFrameCapturer extends OpenGlFrameCapturer { public static class Data implements CaptureData { public float dx, dy, dz; public float yaw, pitch; public Data(float dx, float dy, float dz, float yaw, float pitch) { this.dx = dx; this.dy = dy; this.dz = dz; this.yaw = yaw; this.pitch = pitch; } } private static Data[] golden_disk(int n, float r) { Data[] points = new Data[n]; final float GOLDEN_ANGLE = (float) (Math.PI * (3.0 - Math.sqrt(5.0))); float random_offset = (float) Math.sqrt(r*r/n) * 0.5f; for (int i = 0; i < n; i++) { float rr = r * (float) Math.sqrt((float) i / n); float t = i * GOLDEN_ANGLE; points[i] = new Data( rr * (float) Math.cos(t) + (ThreadLocalRandom.current().nextFloat() * 2f - 1f) * random_offset, rr * (float) Math.sin(t) + (ThreadLocalRandom.current().nextFloat() * 2f - 1f) * random_offset, 0f, 0f, 0f ); } return points; } private static final float FOCAL_DISTANCE = 5.0f; private static final float APERTURE_RADIUS = 0.05f; private static final int SAMPLES = 64; private static Data[] calculateCameras(float focal_distance, float aperture_radius, int samples) { Data[] cameras = golden_disk(samples, aperture_radius); for (Data cam : cameras) { // Направление от смещённой камеры к фокальной точке (0, 0, FOCAL_DISTANCE) float dirX = 0 - cam.dx; float dirY = 0 - cam.dy; float dirZ = focal_distance - cam.dz; // Вычисляем yaw и pitch из направления cam.yaw = (float) -Math.toDegrees(Math.atan2(dirX, dirZ)); cam.pitch = (float) Math.toDegrees(Math.atan2(dirY, Math.sqrt(dirX*dirX + dirZ*dirZ))); } return cameras; } private final Path positionPath; public RealLensOpenGlFrameCapturer( WorldRenderer worldRenderer, RenderInfo renderInfo ) { super(worldRenderer, renderInfo); this.positionPath = ReplayModSimplePathing.instance.getCurrentTimeline().getPositionPath(); } @Override public Map process() { float partialTicks = renderInfo.updateForNextFrame(); int frameId = framesDone++; int fps = renderInfo.getRenderSettings().getFramesPerSecond(); long keyframeTime = (long) frameId * 1000 / fps; float focalDistance = positionPath.getValue(LensProperties.FOCAL_DISTANCE, keyframeTime).map(Triple::getLeft).orElse(5.0f); float apertureRadius = positionPath.getValue(LensProperties.APERTURE_RADIUS, keyframeTime).map(Triple::getLeft).orElse(0.05f); int samples = renderInfo.getRenderSettings().getLensBlurSamples(); if (positionPath == null) { System.err.println("positionPath is null!"); focalDistance = Float.MAX_VALUE; apertureRadius = 0.01f; } Data[] cameras = calculateCameras(focalDistance, apertureRadius, samples); OpenGlFrame[] frames = new OpenGlFrame[cameras.length]; for (int i = 0; i < cameras.length; i++) { frames[i] = renderFrame(frameId, partialTicks, cameras[i]); } return Collections.singletonMap(Channel.BRGA, new RealLensOpenGlFrame(frames)); } }