Add ODS support
This commit is contained in:
@@ -196,6 +196,11 @@ public class ReplayMod {
|
|||||||
throw new IllegalArgumentException("Equirectangular renderer requires boolean for whether it's stable.");
|
throw new IllegalArgumentException("Equirectangular renderer requires boolean for whether it's stable.");
|
||||||
}
|
}
|
||||||
pipelinePreset = Pipelines.Preset.EQUIRECTANGULAR;
|
pipelinePreset = Pipelines.Preset.EQUIRECTANGULAR;
|
||||||
|
} else if ("ODS".equals(type)) {
|
||||||
|
if (parts.length < 2) {
|
||||||
|
throw new IllegalArgumentException("ODS renderer requires boolean for whether it's stable.");
|
||||||
|
}
|
||||||
|
pipelinePreset = Pipelines.Preset.ODS;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Unknown type: " + parts[0]);
|
throw new IllegalArgumentException("Unknown type: " + parts[0]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import java.io.File;
|
|||||||
@Data
|
@Data
|
||||||
public class RenderSettings {
|
public class RenderSettings {
|
||||||
public enum RenderMethod {
|
public enum RenderMethod {
|
||||||
DEFAULT, STEREOSCOPIC, CUBIC, EQUIRECTANGULAR;
|
DEFAULT, STEREOSCOPIC, CUBIC, EQUIRECTANGULAR, ODS;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ public class CubicOpenGlFrameCapturer extends OpenGlFrameCapturer<CubicOpenGlFra
|
|||||||
public CubicOpenGlFrameCapturer(WorldRenderer worldRenderer, RenderInfo renderInfo, int frameSize) {
|
public CubicOpenGlFrameCapturer(WorldRenderer worldRenderer, RenderInfo renderInfo, int frameSize) {
|
||||||
super(worldRenderer, renderInfo);
|
super(worldRenderer, renderInfo);
|
||||||
this.frameSize = frameSize;
|
this.frameSize = frameSize;
|
||||||
|
worldRenderer.setOmnidirectional(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ public class CubicPboOpenGlFrameCapturer extends
|
|||||||
public CubicPboOpenGlFrameCapturer(WorldRenderer worldRenderer, RenderInfo renderInfo, int frameSize) {
|
public CubicPboOpenGlFrameCapturer(WorldRenderer worldRenderer, RenderInfo renderInfo, int frameSize) {
|
||||||
super(worldRenderer, renderInfo, CubicOpenGlFrameCapturer.Data.class, frameSize * frameSize);
|
super(worldRenderer, renderInfo, CubicOpenGlFrameCapturer.Data.class, frameSize * frameSize);
|
||||||
this.frameSize = frameSize;
|
this.frameSize = frameSize;
|
||||||
|
worldRenderer.setOmnidirectional(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
package com.replaymod.render.capturer;
|
||||||
|
|
||||||
|
import com.replaymod.render.RenderSettings;
|
||||||
|
import com.replaymod.render.frame.CubicOpenGlFrame;
|
||||||
|
import com.replaymod.render.frame.ODSOpenGlFrame;
|
||||||
|
import com.replaymod.render.frame.OpenGlFrame;
|
||||||
|
import com.replaymod.render.rendering.FrameCapturer;
|
||||||
|
import com.replaymod.render.shader.Program;
|
||||||
|
import net.minecraft.client.renderer.GlStateManager;
|
||||||
|
import net.minecraft.crash.CrashReport;
|
||||||
|
import net.minecraft.util.ReportedException;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import org.lwjgl.util.Dimension;
|
||||||
|
import org.lwjgl.util.ReadableDimension;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static net.minecraft.client.renderer.GlStateManager.*;
|
||||||
|
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
|
||||||
|
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
|
||||||
|
|
||||||
|
public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
|
||||||
|
private static final ResourceLocation vertexResource = new ResourceLocation("replaymod", "shader/ods.vert");
|
||||||
|
private static final ResourceLocation fragmentResource = new ResourceLocation("replaymod", "shader/ods.frag");
|
||||||
|
|
||||||
|
private final CubicPboOpenGlFrameCapturer left, right;
|
||||||
|
private final Program shaderProgram;
|
||||||
|
private final Program.Uniform directionVariable;
|
||||||
|
private final Program.Uniform leftEyeVariable;
|
||||||
|
|
||||||
|
private final BooleanState[] previousStates = new BooleanState[3];
|
||||||
|
private final BooleanState previousFogState;
|
||||||
|
|
||||||
|
public ODSFrameCapturer(WorldRenderer worldRenderer, final RenderInfo renderInfo, int frameSize) {
|
||||||
|
RenderInfo fakeInfo = new RenderInfo() {
|
||||||
|
private int call;
|
||||||
|
private float partialTicks;
|
||||||
|
@Override
|
||||||
|
public ReadableDimension getFrameSize() {
|
||||||
|
return renderInfo.getFrameSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTotalFrames() {
|
||||||
|
return renderInfo.getTotalFrames();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float updateForNextFrame() {
|
||||||
|
if (call++ % 2 == 0) {
|
||||||
|
shaderProgram.stopUsing();
|
||||||
|
partialTicks = renderInfo.updateForNextFrame();
|
||||||
|
shaderProgram.use();
|
||||||
|
}
|
||||||
|
return partialTicks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderSettings getRenderSettings() {
|
||||||
|
return renderInfo.getRenderSettings();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
left = new CubicStereoFrameCapturer(worldRenderer, fakeInfo, frameSize);
|
||||||
|
right = new CubicStereoFrameCapturer(worldRenderer, fakeInfo, frameSize);
|
||||||
|
try {
|
||||||
|
shaderProgram = new Program(vertexResource, fragmentResource);
|
||||||
|
shaderProgram.use();
|
||||||
|
leftEyeVariable = shaderProgram.getUniformVariable("leftEye");
|
||||||
|
directionVariable = shaderProgram.getUniformVariable("direction");
|
||||||
|
setTexture("texture", 0);
|
||||||
|
setTexture("lightMap", 1);
|
||||||
|
linkState(0, "textureEnabled");
|
||||||
|
linkState(1, "lightMapEnabled");
|
||||||
|
linkState(2, "hurtTextureEnabled");
|
||||||
|
final Program.Uniform uniform = shaderProgram.getUniformVariable("fogEnabled");
|
||||||
|
previousFogState = GlStateManager.fogState.field_179049_a;
|
||||||
|
uniform.set(previousFogState.currentState);
|
||||||
|
GlStateManager.fogState.field_179049_a = new BooleanState(previousFogState.capability) {
|
||||||
|
@Override
|
||||||
|
public void setState(boolean state) {
|
||||||
|
super.setState(state);
|
||||||
|
uniform.set(state);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
shaderProgram.stopUsing();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ReportedException(CrashReport.makeCrashReport(e, "Creating ODS shaders"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTexture(String texture, int i) {
|
||||||
|
shaderProgram.getUniformVariable(texture).set(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void linkState(int id, String var) {
|
||||||
|
final Program.Uniform uniform = shaderProgram.getUniformVariable(var);
|
||||||
|
previousStates[id] = GlStateManager.textureState[id].texture2DState;
|
||||||
|
uniform.set(previousStates[id].currentState);
|
||||||
|
GlStateManager.textureState[id].texture2DState = new BooleanState(previousStates[id].capability) {
|
||||||
|
@Override
|
||||||
|
public void setState(boolean state) {
|
||||||
|
super.setState(state);
|
||||||
|
uniform.set(state);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDone() {
|
||||||
|
return left.isDone() && right.isDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ODSOpenGlFrame process() {
|
||||||
|
shaderProgram.use();
|
||||||
|
leftEyeVariable.set(true);
|
||||||
|
CubicOpenGlFrame leftFrame = left.process();
|
||||||
|
leftEyeVariable.set(false);
|
||||||
|
CubicOpenGlFrame rightFrame = right.process();
|
||||||
|
shaderProgram.stopUsing();
|
||||||
|
|
||||||
|
if (leftFrame != null && rightFrame != null) {
|
||||||
|
return new ODSOpenGlFrame(leftFrame, rightFrame);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
left.close();
|
||||||
|
right.close();
|
||||||
|
shaderProgram.delete();
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
GlStateManager.textureState[i].texture2DState = previousStates[i];
|
||||||
|
}
|
||||||
|
GlStateManager.fogState.field_179049_a = previousFogState;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CubicStereoFrameCapturer extends CubicPboOpenGlFrameCapturer {
|
||||||
|
public CubicStereoFrameCapturer(WorldRenderer worldRenderer, RenderInfo renderInfo, int frameSize) {
|
||||||
|
super(worldRenderer, renderInfo, frameSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected OpenGlFrame renderFrame(int frameId, float partialTicks, CubicOpenGlFrameCapturer.Data captureData) {
|
||||||
|
pushMatrix();
|
||||||
|
frameBuffer().bindFramebuffer(true);
|
||||||
|
|
||||||
|
clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
enableTexture2D();
|
||||||
|
|
||||||
|
directionVariable.set(captureData.ordinal());
|
||||||
|
worldRenderer.renderWorld(new Dimension(getFrameWidth(), getFrameHeight()), partialTicks, null);
|
||||||
|
|
||||||
|
frameBuffer().unbindFramebuffer();
|
||||||
|
popMatrix();
|
||||||
|
|
||||||
|
return captureFrame(frameId, captureData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,4 +6,5 @@ import java.io.Closeable;
|
|||||||
|
|
||||||
public interface WorldRenderer extends Closeable {
|
public interface WorldRenderer extends Closeable {
|
||||||
void renderWorld(ReadableDimension displaySize, float partialTicks, CaptureData data);
|
void renderWorld(ReadableDimension displaySize, float partialTicks, CaptureData data);
|
||||||
|
void setOmnidirectional(boolean omnidirectional);
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/main/java/com/replaymod/render/frame/ODSOpenGlFrame.java
Normal file
21
src/main/java/com/replaymod/render/frame/ODSOpenGlFrame.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package com.replaymod.render.frame;
|
||||||
|
|
||||||
|
import com.replaymod.render.rendering.Frame;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
|
public class ODSOpenGlFrame implements Frame {
|
||||||
|
@Getter
|
||||||
|
private final CubicOpenGlFrame left, right;
|
||||||
|
|
||||||
|
public ODSOpenGlFrame(CubicOpenGlFrame left, CubicOpenGlFrame right) {
|
||||||
|
Validate.isTrue(left.getFrameId() == right.getFrameId(), "Frame ids do not match.");
|
||||||
|
this.left = left;
|
||||||
|
this.right = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getFrameId() {
|
||||||
|
return left.getFrameId();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -286,6 +286,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
|
|||||||
switch (renderMethodDropdown.getSelectedValue()) {
|
switch (renderMethodDropdown.getSelectedValue()) {
|
||||||
case CUBIC:
|
case CUBIC:
|
||||||
case EQUIRECTANGULAR:
|
case EQUIRECTANGULAR:
|
||||||
|
case ODS:
|
||||||
stabilizePanel.forEach(IGuiCheckbox.class).setEnabled();
|
stabilizePanel.forEach(IGuiCheckbox.class).setEnabled();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -294,7 +295,8 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
|
|||||||
|
|
||||||
// Enable/Disable inject metadata checkbox
|
// Enable/Disable inject metadata checkbox
|
||||||
if (encodingPresetDropdown.getSelectedValue().getFileExtension().equals("mp4")
|
if (encodingPresetDropdown.getSelectedValue().getFileExtension().equals("mp4")
|
||||||
&& renderMethodDropdown.getSelectedValue() == RenderSettings.RenderMethod.EQUIRECTANGULAR) {
|
&& (renderMethodDropdown.getSelectedValue() == RenderSettings.RenderMethod.EQUIRECTANGULAR
|
||||||
|
|| renderMethodDropdown.getSelectedValue() == RenderSettings.RenderMethod.ODS)) {
|
||||||
inject360Metadata.setEnabled().setTooltip(null);
|
inject360Metadata.setEnabled().setTooltip(null);
|
||||||
} else {
|
} else {
|
||||||
inject360Metadata.setDisabled().setTooltip(new GuiTooltip().setColor(Colors.RED)
|
inject360Metadata.setDisabled().setTooltip(new GuiTooltip().setColor(Colors.RED)
|
||||||
@@ -325,6 +327,10 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
|
|||||||
&& (videoWidth / 2 != videoHeight || videoWidth % 2 != 0)) {
|
&& (videoWidth / 2 != videoHeight || videoWidth % 2 != 0)) {
|
||||||
return "replaymod.gui.rendersettings.customresolution.warning.equirectangular";
|
return "replaymod.gui.rendersettings.customresolution.warning.equirectangular";
|
||||||
}
|
}
|
||||||
|
if (method == RenderSettings.RenderMethod.ODS
|
||||||
|
&& videoWidth != videoHeight) {
|
||||||
|
return "replaymod.gui.rendersettings.customresolution.warning.ods";
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ public class EntityRendererHandler implements WorldRenderer {
|
|||||||
|
|
||||||
public CaptureData data;
|
public CaptureData data;
|
||||||
|
|
||||||
|
public boolean omnidirectional;
|
||||||
|
|
||||||
public EntityRendererHandler(RenderSettings settings) {
|
public EntityRendererHandler(RenderSettings settings) {
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
|
||||||
@@ -64,6 +66,11 @@ public class EntityRendererHandler implements WorldRenderer {
|
|||||||
((IEntityRenderer) mc.entityRenderer).replayModRender_setHandler(null);
|
((IEntityRenderer) mc.entityRenderer).replayModRender_setHandler(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOmnidirectional(boolean omnidirectional) {
|
||||||
|
this.omnidirectional = omnidirectional;
|
||||||
|
}
|
||||||
|
|
||||||
public static final class NoCullingClippingHelper extends ClippingHelper {
|
public static final class NoCullingClippingHelper extends ClippingHelper {
|
||||||
@Override
|
@Override
|
||||||
public boolean isBoxInFrustum(double p_78553_1_, double p_78553_3_, double p_78553_5_, double p_78553_7_, double p_78553_9_, double p_78553_11_) {
|
public boolean isBoxInFrustum(double p_78553_1_, double p_78553_3_, double p_78553_5_, double p_78553_7_, double p_78553_9_, double p_78553_11_) {
|
||||||
|
|||||||
@@ -29,9 +29,12 @@ public class MetadataInjector {
|
|||||||
"<GSpherical:Stitched>true</GSpherical:Stitched> " +
|
"<GSpherical:Stitched>true</GSpherical:Stitched> " +
|
||||||
"<GSpherical:StitchingSoftware>"+STITCHING_SOFTWARE+"</GSpherical:StitchingSoftware> " +
|
"<GSpherical:StitchingSoftware>"+STITCHING_SOFTWARE+"</GSpherical:StitchingSoftware> " +
|
||||||
"<GSpherical:ProjectionType>equirectangular</GSpherical:ProjectionType> ";
|
"<GSpherical:ProjectionType>equirectangular</GSpherical:ProjectionType> ";
|
||||||
|
private static final String STEREO_XML_CONTENTS = "<GSpherical:StereoMode>top-bottom</GSpherical:StereoMode>";
|
||||||
private static final String SPHERICAL_XML_FOOTER = "</rdf:SphericalVideo>";
|
private static final String SPHERICAL_XML_FOOTER = "</rdf:SphericalVideo>";
|
||||||
|
|
||||||
private static final String XML_360_METADATA = SPHERICAL_XML_HEADER + SPHERICAL_XML_CONTENTS + SPHERICAL_XML_FOOTER;
|
private static final String XML_360_METADATA = SPHERICAL_XML_HEADER + SPHERICAL_XML_CONTENTS + SPHERICAL_XML_FOOTER;
|
||||||
|
private static final String XML_ODS_METADATA = SPHERICAL_XML_HEADER + SPHERICAL_XML_CONTENTS
|
||||||
|
+ STEREO_XML_CONTENTS + SPHERICAL_XML_FOOTER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These bytes are taken from the variable 'spherical_uuid_id'
|
* These bytes are taken from the variable 'spherical_uuid_id'
|
||||||
@@ -45,11 +48,16 @@ public class MetadataInjector {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private static final byte[] BYTES_360_METADATA = Bytes.concat(UUID_BYTES, XML_360_METADATA.getBytes());
|
private static final byte[] BYTES_360_METADATA = Bytes.concat(UUID_BYTES, XML_360_METADATA.getBytes());
|
||||||
|
private static final byte[] BYTES_ODS_METADATA = Bytes.concat(UUID_BYTES, XML_ODS_METADATA.getBytes());
|
||||||
|
|
||||||
public static void inject360Metadata(File videoFile) {
|
public static void inject360Metadata(File videoFile) {
|
||||||
writeMetadata(videoFile, BYTES_360_METADATA);
|
writeMetadata(videoFile, BYTES_360_METADATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void injectODSMetadata(File videoFile) {
|
||||||
|
writeMetadata(videoFile, BYTES_ODS_METADATA);
|
||||||
|
}
|
||||||
|
|
||||||
private static void writeMetadata(File videoFile, byte[] metadata) {
|
private static void writeMetadata(File videoFile, byte[] metadata) {
|
||||||
File tempFile = null;
|
File tempFile = null;
|
||||||
FileOutputStream videoFileOutputStream = null;
|
FileOutputStream videoFileOutputStream = null;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.replaymod.render.mixin;
|
package com.replaymod.render.mixin;
|
||||||
|
|
||||||
import com.replaymod.render.capturer.CubicOpenGlFrameCapturer;
|
|
||||||
import com.replaymod.render.hooks.EntityRendererHandler;
|
import com.replaymod.render.hooks.EntityRendererHandler;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.particle.EffectRenderer;
|
import net.minecraft.client.particle.EffectRenderer;
|
||||||
@@ -28,7 +27,7 @@ public abstract class MixinEffectRenderer {
|
|||||||
private void renderParticle(EntityFX fx, WorldRenderer worldRenderer, Entity view, float partialTicks,
|
private void renderParticle(EntityFX fx, WorldRenderer worldRenderer, Entity view, float partialTicks,
|
||||||
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
|
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
|
||||||
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler();
|
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler();
|
||||||
if (handler != null && handler.data instanceof CubicOpenGlFrameCapturer.Data) {
|
if (handler != null && handler.omnidirectional) {
|
||||||
// Align all particles towards the camera
|
// Align all particles towards the camera
|
||||||
double dx = fx.prevPosX + (fx.posX - fx.prevPosX) * partialTicks - view.posX;
|
double dx = fx.prevPosX + (fx.posX - fx.prevPosX) * partialTicks - view.posX;
|
||||||
double dy = fx.prevPosY + (fx.posY - fx.prevPosY) * partialTicks - view.posY;
|
double dy = fx.prevPosY + (fx.posY - fx.prevPosY) * partialTicks - view.posY;
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
|
|||||||
@Inject(method = "renderHand", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "renderHand", at = @At("HEAD"), cancellable = true)
|
||||||
private void replayModRender_renderSpectatorHand(float partialTicks, int renderPass, CallbackInfo ci) {
|
private void replayModRender_renderSpectatorHand(float partialTicks, int renderPass, CallbackInfo ci) {
|
||||||
if (replayModRender_handler != null) {
|
if (replayModRender_handler != null) {
|
||||||
if (replayModRender_handler.data instanceof CubicOpenGlFrameCapturer.Data) {
|
if (replayModRender_handler.omnidirectional) {
|
||||||
ci.cancel();
|
ci.cancel();
|
||||||
return; // No spectator hands during 360° view, we wouldn't even know where to put it
|
return; // No spectator hands during 360° view, we wouldn't even know where to put it
|
||||||
}
|
}
|
||||||
@@ -204,7 +204,7 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void replayModRender_gluPerspective(float fovY, float aspect, float zNear, float zFar) {
|
public void replayModRender_gluPerspective(float fovY, float aspect, float zNear, float zFar) {
|
||||||
if (replayModRender_getHandler() != null && replayModRender_getHandler().data instanceof CubicOpenGlFrameCapturer.Data) {
|
if (replayModRender_getHandler() != null && replayModRender_getHandler().omnidirectional) {
|
||||||
fovY = 90;
|
fovY = 90;
|
||||||
aspect = 1;
|
aspect = 1;
|
||||||
}
|
}
|
||||||
@@ -234,7 +234,8 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
|
|||||||
GlStateManager.rotate(-90, 1.0F, 0.0F, 0.0F);
|
GlStateManager.rotate(-90, 1.0F, 0.0F, 0.0F);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (replayModRender_getHandler() != null && replayModRender_getHandler().omnidirectional) {
|
||||||
// Minecraft goes back a little so we have to revert that
|
// Minecraft goes back a little so we have to revert that
|
||||||
GlStateManager.translate(0.0F, 0.0F, 0.1F);
|
GlStateManager.translate(0.0F, 0.0F, 0.1F);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.replaymod.render.mixin;
|
package com.replaymod.render.mixin;
|
||||||
|
|
||||||
import com.replaymod.render.capturer.CubicOpenGlFrameCapturer;
|
|
||||||
import com.replaymod.render.hooks.EntityRendererHandler;
|
import com.replaymod.render.hooks.EntityRendererHandler;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.entity.RenderManager;
|
import net.minecraft.client.renderer.entity.RenderManager;
|
||||||
@@ -22,7 +21,7 @@ public abstract class MixinRenderManager {
|
|||||||
@Inject(method = "doRenderEntity", at = @At("HEAD"))
|
@Inject(method = "doRenderEntity", at = @At("HEAD"))
|
||||||
private void replayModRender_reorientForCubicRendering(Entity entity, double dx, double dy, double dz, float iDoNotKnow, float partialTicks, boolean iDoNotCare, CallbackInfoReturnable ci) {
|
private void replayModRender_reorientForCubicRendering(Entity entity, double dx, double dy, double dz, float iDoNotKnow, float partialTicks, boolean iDoNotCare, CallbackInfoReturnable ci) {
|
||||||
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler();
|
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler();
|
||||||
if (handler != null && handler.data instanceof CubicOpenGlFrameCapturer.Data) {
|
if (handler != null && handler.omnidirectional) {
|
||||||
double pitch = -Math.atan2(dy, Math.sqrt(dx * dx + dz * dz));
|
double pitch = -Math.atan2(dy, Math.sqrt(dx * dx + dz * dz));
|
||||||
double yaw = -Math.atan2(dx, dz);
|
double yaw = -Math.atan2(dx, dz);
|
||||||
playerViewX = (float) Math.toDegrees(pitch);
|
playerViewX = (float) Math.toDegrees(pitch);
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.replaymod.render.processor;
|
||||||
|
|
||||||
|
import com.replaymod.render.frame.ODSOpenGlFrame;
|
||||||
|
import com.replaymod.render.frame.RGBFrame;
|
||||||
|
import com.replaymod.render.utils.ByteBufferPool;
|
||||||
|
import org.lwjgl.util.Dimension;
|
||||||
|
import org.lwjgl.util.ReadableDimension;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class ODSToRGBProcessor extends AbstractFrameProcessor<ODSOpenGlFrame, RGBFrame> {
|
||||||
|
private final EquirectangularToRGBProcessor processor;
|
||||||
|
|
||||||
|
public ODSToRGBProcessor(int frameSize) {
|
||||||
|
processor = new EquirectangularToRGBProcessor(frameSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RGBFrame process(ODSOpenGlFrame rawFrame) {
|
||||||
|
RGBFrame leftFrame = processor.process(rawFrame.getLeft());
|
||||||
|
RGBFrame rightFrame = processor.process(rawFrame.getRight());
|
||||||
|
ReadableDimension size = new Dimension(leftFrame.getSize().getWidth(), leftFrame.getSize().getHeight() * 2);
|
||||||
|
ByteBuffer result = ByteBufferPool.allocate(size.getWidth() * size.getHeight() * 3);
|
||||||
|
result.put(leftFrame.getByteBuffer());
|
||||||
|
result.put(rightFrame.getByteBuffer());
|
||||||
|
result.rewind();
|
||||||
|
ByteBufferPool.release(leftFrame.getByteBuffer());
|
||||||
|
ByteBufferPool.release(rightFrame.getByteBuffer());
|
||||||
|
return new RGBFrame(rawFrame.getFrameId(), size, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
processor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,15 +2,9 @@ package com.replaymod.render.rendering;
|
|||||||
|
|
||||||
import com.replaymod.render.RenderSettings;
|
import com.replaymod.render.RenderSettings;
|
||||||
import com.replaymod.render.capturer.*;
|
import com.replaymod.render.capturer.*;
|
||||||
import com.replaymod.render.frame.CubicOpenGlFrame;
|
import com.replaymod.render.frame.*;
|
||||||
import com.replaymod.render.frame.OpenGlFrame;
|
|
||||||
import com.replaymod.render.frame.RGBFrame;
|
|
||||||
import com.replaymod.render.frame.StereoscopicOpenGlFrame;
|
|
||||||
import com.replaymod.render.hooks.EntityRendererHandler;
|
import com.replaymod.render.hooks.EntityRendererHandler;
|
||||||
import com.replaymod.render.processor.CubicToRGBProcessor;
|
import com.replaymod.render.processor.*;
|
||||||
import com.replaymod.render.processor.EquirectangularToRGBProcessor;
|
|
||||||
import com.replaymod.render.processor.OpenGlToRGBProcessor;
|
|
||||||
import com.replaymod.render.processor.StereoscopicToRGBProcessor;
|
|
||||||
import com.replaymod.render.utils.PixelBufferObject;
|
import com.replaymod.render.utils.PixelBufferObject;
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
@@ -26,6 +20,8 @@ public class Pipelines {
|
|||||||
return newCubicPipeline(renderInfo, consumer);
|
return newCubicPipeline(renderInfo, consumer);
|
||||||
case EQUIRECTANGULAR:
|
case EQUIRECTANGULAR:
|
||||||
return newEquirectangularPipeline(renderInfo, consumer);
|
return newEquirectangularPipeline(renderInfo, consumer);
|
||||||
|
case ODS:
|
||||||
|
return newODSPipeline(renderInfo, consumer);
|
||||||
}
|
}
|
||||||
throw new UnsupportedOperationException("Unknown method: " + method);
|
throw new UnsupportedOperationException("Unknown method: " + method);
|
||||||
}
|
}
|
||||||
@@ -73,4 +69,11 @@ public class Pipelines {
|
|||||||
}
|
}
|
||||||
return new Pipeline<>(capturer, new EquirectangularToRGBProcessor(settings.getVideoWidth() / 4), consumer);
|
return new Pipeline<>(capturer, new EquirectangularToRGBProcessor(settings.getVideoWidth() / 4), consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Pipeline<ODSOpenGlFrame, RGBFrame> newODSPipeline(RenderInfo renderInfo, FrameConsumer<RGBFrame> consumer) {
|
||||||
|
RenderSettings settings = renderInfo.getRenderSettings();
|
||||||
|
FrameCapturer<ODSOpenGlFrame> capturer =
|
||||||
|
new ODSFrameCapturer(new EntityRendererHandler(settings), renderInfo, settings.getVideoWidth() / 4);
|
||||||
|
return new Pipeline<>(capturer, new ODSToRGBProcessor(settings.getVideoWidth() / 4), consumer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,11 @@ public class VideoRenderer implements RenderInfo {
|
|||||||
renderingPipeline.run();
|
renderingPipeline.run();
|
||||||
|
|
||||||
if (settings.isInject360Metadata()) {
|
if (settings.isInject360Metadata()) {
|
||||||
MetadataInjector.inject360Metadata(settings.getOutputFile());
|
if (settings.getRenderMethod() == RenderSettings.RenderMethod.ODS) {
|
||||||
|
MetadataInjector.injectODSMetadata(settings.getOutputFile());
|
||||||
|
} else {
|
||||||
|
MetadataInjector.inject360Metadata(settings.getOutputFile());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
finish();
|
finish();
|
||||||
|
|||||||
102
src/main/java/com/replaymod/render/shader/Program.java
Normal file
102
src/main/java/com/replaymod/render/shader/Program.java
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package com.replaymod.render.shader;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.resources.IResource;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.lwjgl.opengl.ARBFragmentShader;
|
||||||
|
import org.lwjgl.opengl.ARBShaderObjects;
|
||||||
|
import org.lwjgl.opengl.ARBVertexShader;
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.ARBShaderObjects.*;
|
||||||
|
|
||||||
|
public class Program {
|
||||||
|
private final int program;
|
||||||
|
|
||||||
|
public Program(ResourceLocation vertexShader, ResourceLocation fragmentShader) throws Exception {
|
||||||
|
int vertShader = createShader(vertexShader, ARBVertexShader.GL_VERTEX_SHADER_ARB);
|
||||||
|
int fragShader = createShader(fragmentShader, ARBFragmentShader.GL_FRAGMENT_SHADER_ARB);
|
||||||
|
|
||||||
|
program = glCreateProgramObjectARB();
|
||||||
|
if (program == 0) {
|
||||||
|
throw new Exception("glCreateProgramObjectARB failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
glAttachObjectARB(program, vertShader);
|
||||||
|
glAttachObjectARB(program, fragShader);
|
||||||
|
|
||||||
|
glLinkProgramARB(program);
|
||||||
|
if (glGetObjectParameteriARB(program, GL_OBJECT_LINK_STATUS_ARB) == GL11.GL_FALSE) {
|
||||||
|
throw new Exception("Error linking: " + getLogInfo(program));
|
||||||
|
}
|
||||||
|
|
||||||
|
glValidateProgramARB(program);
|
||||||
|
if (glGetObjectParameteriARB(program, GL_OBJECT_VALIDATE_STATUS_ARB) == GL11.GL_FALSE) {
|
||||||
|
throw new Exception("Error validating: " + getLogInfo(program));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int createShader(ResourceLocation resourceLocation, int shaderType) throws Exception {
|
||||||
|
int shader = 0;
|
||||||
|
try {
|
||||||
|
shader = glCreateShaderObjectARB(shaderType);
|
||||||
|
|
||||||
|
if(shader == 0)
|
||||||
|
throw new Exception("glCreateShaderObjectARB failed");
|
||||||
|
|
||||||
|
IResource resource = Minecraft.getMinecraft().getResourceManager().getResource(resourceLocation);
|
||||||
|
try (InputStream is = resource.getInputStream()) {
|
||||||
|
glShaderSourceARB(shader, IOUtils.toString(is));
|
||||||
|
}
|
||||||
|
glCompileShaderARB(shader);
|
||||||
|
|
||||||
|
if (glGetObjectParameteriARB(shader, GL_OBJECT_COMPILE_STATUS_ARB) == GL11.GL_FALSE)
|
||||||
|
throw new RuntimeException("Error creating shader: " + getLogInfo(shader));
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
catch(Exception exc) {
|
||||||
|
glDeleteObjectARB(shader);
|
||||||
|
throw exc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getLogInfo(int obj) {
|
||||||
|
return glGetInfoLogARB(obj, glGetObjectParameteriARB(obj, GL_OBJECT_INFO_LOG_LENGTH_ARB));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void use() {
|
||||||
|
ARBShaderObjects.glUseProgramObjectARB(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopUsing() {
|
||||||
|
ARBShaderObjects.glUseProgramObjectARB(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete() {
|
||||||
|
ARBShaderObjects.glDeleteObjectARB(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uniform getUniformVariable(String name) {
|
||||||
|
return new Uniform(ARBShaderObjects.glGetUniformLocationARB(program, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Uniform {
|
||||||
|
private final int location;
|
||||||
|
|
||||||
|
public Uniform(int location) {
|
||||||
|
this.location = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(boolean bool) {
|
||||||
|
ARBShaderObjects.glUniform1iARB(location, bool ? GL11.GL_TRUE : GL11.GL_FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(int integer) {
|
||||||
|
ARBShaderObjects.glUniform1iARB(location, integer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -110,5 +110,12 @@ public net.minecraft.client.settings.KeyBinding field_151474_i # pressTime
|
|||||||
# Timer
|
# Timer
|
||||||
public net.minecraft.util.Timer *
|
public net.minecraft.util.Timer *
|
||||||
|
|
||||||
|
# GlStateManager
|
||||||
|
public net.minecraft.client.renderer.GlStateManager *
|
||||||
|
public net.minecraft.client.renderer.GlStateManager$TextureState
|
||||||
|
public net.minecraft.client.renderer.GlStateManager$FogState
|
||||||
|
public net.minecraft.client.renderer.GlStateManager$BooleanState
|
||||||
|
public net.minecraft.client.renderer.GlStateManager$BooleanState *
|
||||||
|
|
||||||
# Example
|
# Example
|
||||||
# public net.minecraft.package.ClassName func_some_id(Ljava/lang/Class;IZS)V # methodName
|
# public net.minecraft.package.ClassName func_some_id(Ljava/lang/Class;IZS)V # methodName
|
||||||
|
|||||||
31
src/main/resources/assets/replaymod/shader/ods.frag
Normal file
31
src/main/resources/assets/replaymod/shader/ods.frag
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#version 110
|
||||||
|
|
||||||
|
varying vec4 vertColor;
|
||||||
|
|
||||||
|
varying vec4 textureCoord;
|
||||||
|
varying vec4 lightMapCoord;
|
||||||
|
|
||||||
|
uniform sampler2D texture;
|
||||||
|
uniform sampler2D lightMap;
|
||||||
|
|
||||||
|
uniform bool textureEnabled;
|
||||||
|
uniform bool lightMapEnabled;
|
||||||
|
uniform bool hurtTextureEnabled;
|
||||||
|
uniform bool fogEnabled;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 color = vertColor;
|
||||||
|
if (textureEnabled) {
|
||||||
|
color *= texture2D(texture, textureCoord.st);
|
||||||
|
}
|
||||||
|
if (lightMapEnabled) {
|
||||||
|
color *= texture2D(lightMap, lightMapCoord.st);
|
||||||
|
}
|
||||||
|
if (hurtTextureEnabled) {
|
||||||
|
color = vec4(mix(color.rgb, vec3(1, 0, 0), 0.3), color.a);
|
||||||
|
}
|
||||||
|
if (fogEnabled) {
|
||||||
|
color.rgb = mix(color.rgb, gl_Fog.color.rgb, clamp((gl_FogFragCoord - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0));
|
||||||
|
}
|
||||||
|
gl_FragColor = color;
|
||||||
|
}
|
||||||
79
src/main/resources/assets/replaymod/shader/ods.vert
Normal file
79
src/main/resources/assets/replaymod/shader/ods.vert
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#version 110
|
||||||
|
|
||||||
|
varying vec4 vertColor;
|
||||||
|
varying float vertId;
|
||||||
|
|
||||||
|
varying vec4 textureCoord;
|
||||||
|
varying vec4 lightMapCoord;
|
||||||
|
|
||||||
|
uniform bool leftEye;
|
||||||
|
uniform int direction;
|
||||||
|
|
||||||
|
const float eyeDistance = 0.14;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Transform to view space
|
||||||
|
vec4 position = gl_ModelViewMatrix * gl_Vertex;
|
||||||
|
|
||||||
|
// Distort for ODS
|
||||||
|
// O := The origin
|
||||||
|
// P := The current vertex/point
|
||||||
|
// T := Point of tangency with the tangent going through P
|
||||||
|
// Distance between P and O
|
||||||
|
float distPO = sqrt(position.x * position.x + position.z * position.z);
|
||||||
|
float distTO = eyeDistance * 0.5;
|
||||||
|
// Angle between PO and PT
|
||||||
|
float angP = acos(distTO / distPO);
|
||||||
|
// Angle between PO and TO (angle at the origin)
|
||||||
|
float angO = 90.0 - angP;
|
||||||
|
if (!leftEye) {
|
||||||
|
angO = -angO;
|
||||||
|
}
|
||||||
|
// The angel of OP within the circle, that is between OP and O(0,1)
|
||||||
|
float angOP = atan(position.x, position.z);
|
||||||
|
// The angle of OT within the circle, that is between OT and O(0,1)
|
||||||
|
float angOT = angO + angOP;
|
||||||
|
// Calculate the vector between O and T and finally move the vertex by that vector
|
||||||
|
position -= vec4(distTO * sin(angOT), 0, distTO * cos(angOT), 0);
|
||||||
|
|
||||||
|
// Rotate for different cubic views
|
||||||
|
float z;
|
||||||
|
switch (direction) {
|
||||||
|
case 0: // LEFT
|
||||||
|
z = position.z;
|
||||||
|
position.z = position.x;
|
||||||
|
position.x = -z;
|
||||||
|
break;
|
||||||
|
case 1: // RIGHT
|
||||||
|
z = position.z;
|
||||||
|
position.z = -position.x;
|
||||||
|
position.x = z;
|
||||||
|
break;
|
||||||
|
case 2: // FRONT
|
||||||
|
// No changes required
|
||||||
|
break;
|
||||||
|
case 3: // BACK
|
||||||
|
position.x = -position.x;
|
||||||
|
position.z = -position.z;
|
||||||
|
break;
|
||||||
|
case 4: // TOP
|
||||||
|
z = position.z;
|
||||||
|
position.z = -position.y;
|
||||||
|
position.y = z;
|
||||||
|
break;
|
||||||
|
case 5: // BOTTOM
|
||||||
|
z = position.z;
|
||||||
|
position.z = position.y;
|
||||||
|
position.y = -z;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform to screen space
|
||||||
|
gl_Position = gl_ProjectionMatrix * position;
|
||||||
|
|
||||||
|
// Misc.
|
||||||
|
textureCoord = gl_TextureMatrix[0] * gl_MultiTexCoord0;
|
||||||
|
lightMapCoord = gl_TextureMatrix[1] * gl_MultiTexCoord1;
|
||||||
|
vertColor = gl_Color;
|
||||||
|
gl_FogFragCoord = sqrt(position.x * position.x + position.y * position.y + position.z * position.z);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user