Add .blend export
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
package com.replaymod.render.blend.exporters;
|
||||
|
||||
import com.replaymod.render.blend.BlendState;
|
||||
import com.replaymod.render.blend.Exporter;
|
||||
import com.replaymod.render.blend.data.DMaterial;
|
||||
import com.replaymod.render.blend.data.DMesh;
|
||||
import com.replaymod.render.blend.data.DObject;
|
||||
import com.replaymod.replaystudio.us.myles.ViaVersion.util.ReflectionUtil;
|
||||
import lombok.SneakyThrows;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GLAllocation;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.RenderGlobal;
|
||||
import net.minecraft.client.renderer.chunk.CompiledChunk;
|
||||
import net.minecraft.client.renderer.chunk.RenderChunk;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.client.renderer.vertex.VertexBuffer;
|
||||
import net.minecraft.util.BlockPos;
|
||||
import net.minecraft.util.EnumWorldBlockLayer;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.util.vector.Vector2f;
|
||||
import org.lwjgl.util.vector.Vector3f;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ChunkExporter implements Exporter {
|
||||
private final Map<BlockPos, DObject> chunkObjects = new HashMap<>();
|
||||
private final Map<BlockPos, Map<EnumWorldBlockLayer, DObject>> chunkLayerObjects = new HashMap<>();
|
||||
private final List<Pair<RenderChunk, EnumWorldBlockLayer>> chunks = new ArrayList<>();
|
||||
private DObject chunksObject;
|
||||
private DMaterial material;
|
||||
private int frame;
|
||||
|
||||
public void addChunkUpdate(RenderChunk chunk, EnumWorldBlockLayer layer) {
|
||||
chunks.add(Pair.of(chunk, layer));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setup() throws IOException {
|
||||
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
@SuppressWarnings("unchecked")
|
||||
List<RenderGlobal.ContainerLocalRenderInformation> renderInfos = mc.renderGlobal.renderInfos;
|
||||
for (RenderGlobal.ContainerLocalRenderInformation renderInfo : renderInfos) {
|
||||
RenderChunk renderChunk = ReflectionUtil.get(renderInfo, "renderChunk", RenderChunk.class); // SneakyThrows
|
||||
CompiledChunk compiledChunk = renderChunk.getCompiledChunk();
|
||||
if (!compiledChunk.isEmpty()) {
|
||||
for (EnumWorldBlockLayer layer : EnumWorldBlockLayer.values()) {
|
||||
addChunkUpdate(renderChunk, layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chunksObject = new DObject(DObject.Type.OB_EMPTY);
|
||||
chunksObject.id.name = "Chunks";
|
||||
chunksObject.layers = 1 << 1;
|
||||
BlendState.getState().getScene().base.add(chunksObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws IOException {
|
||||
chunks.clear();
|
||||
chunkObjects.clear();
|
||||
chunksObject = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preFrame(int frame) throws IOException {
|
||||
this.frame = frame;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postFrame(int frame) throws IOException {
|
||||
Minecraft.getMinecraft().getTextureManager().bindTexture(TextureMap.locationBlocksTexture);
|
||||
material = BlendState.getState().getMaterials().getActiveMaterial();
|
||||
|
||||
for (Pair<RenderChunk, EnumWorldBlockLayer> pair : chunks) {
|
||||
RenderChunk chunk = pair.getLeft();
|
||||
EnumWorldBlockLayer layer = pair.getRight();
|
||||
DObject chunkObject = chunkObjects.get(chunk.getPosition());
|
||||
if (chunkObject == null) {
|
||||
chunkObject = buildChunkObject(chunk);
|
||||
chunkObjects.put(chunk.getPosition(), chunkObject);
|
||||
chunkLayerObjects.put(chunk.getPosition(), new EnumMap<>(EnumWorldBlockLayer.class));
|
||||
}
|
||||
DObject layerObject = buildChunkLayerObject(chunkObject, chunk, layer);
|
||||
if (layerObject == null) continue;
|
||||
Map<EnumWorldBlockLayer, DObject> layerObjects = chunkLayerObjects.get(chunk.getPosition());
|
||||
DObject oldLayerObject = layerObjects.get(layer);
|
||||
if (oldLayerObject != null) {
|
||||
oldLayerObject.keyframe("hide", 0, frame, 1f);
|
||||
}
|
||||
if (frame > 0) {
|
||||
layerObject.keyframe("hide", 0, frame - 1, 1f);
|
||||
layerObject.keyframe("hide", 0, frame, 0f);
|
||||
} else {
|
||||
layerObject.keyframe("hide", 0, frame, 0f);
|
||||
}
|
||||
layerObjects.put(layer, layerObject);
|
||||
}
|
||||
chunks.clear();
|
||||
}
|
||||
|
||||
private DObject buildChunkObject(RenderChunk renderChunk) {
|
||||
BlockPos pos = renderChunk.getPosition();
|
||||
DObject chunkObject = new DObject(DObject.Type.OB_EMPTY);
|
||||
chunkObject.setParent(chunksObject);
|
||||
chunkObject.id.name = "Chunk[" + pos.getX()/16 + "/" + pos.getY()/16 + "/" + pos.getZ()/16 + "]";
|
||||
chunkObject.loc = new Vector3f(pos.getX(), -pos.getZ(), pos.getY());
|
||||
return chunkObject;
|
||||
}
|
||||
|
||||
private DObject buildChunkLayerObject(DObject chunkObject, RenderChunk renderChunk, EnumWorldBlockLayer layer) {
|
||||
VertexBuffer vertexBuffer = renderChunk.getVertexBufferByLayer(layer.ordinal());
|
||||
if (vertexBuffer == null) return null;
|
||||
|
||||
DObject layerObject = new DObject(buildChunkLayerMesh(vertexBuffer));
|
||||
layerObject.setParent(chunkObject);
|
||||
layerObject.id.name = chunkObject.id.name + " - " + layer + " - Frame " + frame;
|
||||
return layerObject;
|
||||
}
|
||||
|
||||
private DMesh buildChunkLayerMesh(VertexBuffer vertexBuffer) {
|
||||
vertexBuffer.bindBuffer();
|
||||
int size = GL15.glGetBufferParameteri(OpenGlHelper.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
|
||||
ByteBuffer byteBuffer = GLAllocation.createDirectByteBuffer(size);
|
||||
GL15.glGetBufferSubData(OpenGlHelper.GL_ARRAY_BUFFER, 0, byteBuffer);
|
||||
vertexBuffer.unbindBuffer();
|
||||
|
||||
DMesh mesh = new DMesh();
|
||||
mesh.materials.add(material);
|
||||
DMesh.Vertex v1 = null, v2 = null, v3 = null;
|
||||
Vector2f uv1 = new Vector2f(),
|
||||
uv2 = new Vector2f(),
|
||||
uv3 = new Vector2f(),
|
||||
uv4 = new Vector2f();
|
||||
int c1 = 0, c2 = 0, c3 = 0;
|
||||
for (int i = 0; i < size / STRIDE; i++) {
|
||||
// See VboRenderList.setupArrayPointers for offsets
|
||||
DMesh.Vertex vert = new DMesh.Vertex(
|
||||
byteBuffer.getFloat(STRIDE * i),
|
||||
-byteBuffer.getFloat(STRIDE * i + 8),
|
||||
byteBuffer.getFloat(STRIDE * i + 4)
|
||||
);
|
||||
float u = byteBuffer.getFloat(STRIDE * i + 16);
|
||||
float v = 1 - byteBuffer.getFloat(STRIDE * i + 20);
|
||||
int c = byteBuffer.getInt(STRIDE * i + 12);
|
||||
// TODO tex, norm, etc.
|
||||
if (v1 == null) {
|
||||
v1 = vert;
|
||||
uv1.set(u, v);
|
||||
c1 = c;
|
||||
} else if (v2 == null) {
|
||||
v2 = vert;
|
||||
uv2.set(u, v);
|
||||
c2 = c;
|
||||
} else if (v3 == null) {
|
||||
v3 = vert;
|
||||
uv3.set(u, v);
|
||||
c3 = c;
|
||||
} else {
|
||||
uv4.set(u, v);
|
||||
mesh.addQuad(
|
||||
v1, v2, v3, vert,
|
||||
uv1, uv2, uv3, uv4,
|
||||
c1, c2, c3, c,
|
||||
0
|
||||
);
|
||||
v1 = v2 = v3 = null;
|
||||
}
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
private static final int STRIDE = 28;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.replaymod.render.blend.exporters;
|
||||
|
||||
import com.replaymod.render.blend.BlendState;
|
||||
import com.replaymod.render.blend.Exporter;
|
||||
import com.replaymod.render.blend.data.DObject;
|
||||
import lombok.SneakyThrows;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.entity.Entity;
|
||||
import org.lwjgl.util.vector.Matrix4f;
|
||||
import org.lwjgl.util.vector.Vector3f;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.replaymod.render.blend.Util.getGlModelViewMatrix;
|
||||
|
||||
public class EntityExporter implements Exporter {
|
||||
private final Minecraft mc = Minecraft.getMinecraft();
|
||||
private final RenderState renderState;
|
||||
private DObject entitiesObject;
|
||||
private Map<Entity, DObject> entityObjects;
|
||||
|
||||
public EntityExporter(RenderState renderState) {
|
||||
this.renderState = renderState;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setup() throws IOException {
|
||||
entitiesObject = new DObject(DObject.Type.OB_EMPTY);
|
||||
entitiesObject.id.name = "Entities";
|
||||
entitiesObject.layers = 1 << 1;
|
||||
BlendState.getState().getScene().base.add(entitiesObject);
|
||||
|
||||
entityObjects = new IdentityHashMap<>();
|
||||
}
|
||||
|
||||
public void preEntitiesRender() {
|
||||
Matrix4f modelView = getGlModelViewMatrix();
|
||||
// Entities are rendered relative to the viewer location.
|
||||
// We however want our Entities object to not move when the viewer does,
|
||||
// so we position it at 0/0/0 and instead have the entities themselves move more
|
||||
Matrix4f.translate(new Vector3f(
|
||||
(float) -mc.getRenderManager().viewerPosX,
|
||||
(float) -mc.getRenderManager().viewerPosY,
|
||||
(float) -mc.getRenderManager().viewerPosZ
|
||||
), modelView, modelView);
|
||||
renderState.push(entitiesObject, modelView);
|
||||
}
|
||||
|
||||
public void postEntitiesRender() {
|
||||
renderState.pop();
|
||||
}
|
||||
|
||||
public void preRender(Entity entity, double dx, double dy, double dz, float yaw, float renderPartialTicks) {
|
||||
DObject entityObject = entityObjects.get(entity);
|
||||
if (entityObject == null) {
|
||||
entityObject = new DObject(DObject.Type.OB_EMPTY);
|
||||
entityObject.setParent(renderState.peekObject());
|
||||
entityObject.id.name = entity.getName();
|
||||
entityObjects.put(entity, entityObject);
|
||||
}
|
||||
renderState.pushObject(entityObject);
|
||||
|
||||
// Normally the entity renderer will translate to dx/dy/dz but we want that translation outside of
|
||||
// the entity object (on the entity object itself).
|
||||
// So, we translate there, then store the model-view-matrix and then translate back to let the
|
||||
// renderer translate there again.
|
||||
// Instead of actually translating, we just add the translation on the current model-view-matrix.
|
||||
Matrix4f modelView = getGlModelViewMatrix();
|
||||
Matrix4f.translate(new Vector3f((float) dx, (float) dy, (float) dz), modelView, modelView);
|
||||
renderState.pushModelView(modelView);
|
||||
renderState.applyLastModelViewTransformToObject();
|
||||
|
||||
entityObject.keyframeLocRotScale(renderState.getFrame());
|
||||
entityObject.setVisible(renderState.getFrame());
|
||||
}
|
||||
|
||||
public void postRender(Entity entity, double dx, double dy, double dz, float yaw, float renderPartialTicks) {
|
||||
renderState.pop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.replaymod.render.blend.exporters;
|
||||
|
||||
import com.replaymod.render.blend.BlendMeshBuilder;
|
||||
import com.replaymod.render.blend.Exporter;
|
||||
import com.replaymod.render.blend.data.DMesh;
|
||||
import com.replaymod.render.blend.data.DObject;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.client.resources.model.IBakedModel;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraftforge.client.model.pipeline.LightUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ItemExporter implements Exporter {
|
||||
private final RenderState renderState;
|
||||
|
||||
public ItemExporter(RenderState renderState) {
|
||||
this.renderState = renderState;
|
||||
}
|
||||
|
||||
public void onRender(IBakedModel model, ItemStack stack) {
|
||||
DObject object = getObjectForItemStack(model, stack);
|
||||
|
||||
renderState.pushObject(object);
|
||||
renderState.pushModelView();
|
||||
renderState.applyLastModelViewTransformToObject();
|
||||
|
||||
object.setVisible(renderState.getFrame());
|
||||
object.keyframeLocRotScale(renderState.getFrame());
|
||||
|
||||
renderState.pop();
|
||||
}
|
||||
|
||||
private DObject getObjectForItemStack(IBakedModel model, ItemStack stack) {
|
||||
int frame = renderState.getFrame();
|
||||
DObject parent = renderState.peekObject();
|
||||
DObject object = null;
|
||||
for (DObject child : parent.getChildren()) {
|
||||
if (child.lastFrame < frame
|
||||
&& child instanceof ItemBasedDObject
|
||||
&& ((ItemBasedDObject) child).isBasedOn(model, stack)) {
|
||||
object = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (object == null) {
|
||||
object = new ItemBasedDObject(model, stack);
|
||||
object.id.name = stack.getDisplayName();
|
||||
object.setParent(parent);
|
||||
}
|
||||
object.lastFrame = frame;
|
||||
return object;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static DMesh generateMeshForItemStack(IBakedModel model, ItemStack stack) {
|
||||
DMesh mesh = new DMesh();
|
||||
BlendMeshBuilder builder = new BlendMeshBuilder(mesh);
|
||||
builder.setWellBehaved(true);
|
||||
builder.startDrawingQuads();
|
||||
builder.setVertexFormat(DefaultVertexFormats.ITEM);
|
||||
|
||||
for (EnumFacing face : EnumFacing.values()) {
|
||||
renderQuads(builder, model.getFaceQuads(face), stack);
|
||||
}
|
||||
renderQuads(builder, model.getGeneralQuads(), stack);
|
||||
|
||||
builder.finishDrawing();
|
||||
return mesh;
|
||||
}
|
||||
|
||||
private static void renderQuads(WorldRenderer buffer, List<BakedQuad> quads, ItemStack stack) {
|
||||
for (BakedQuad quad : quads) {
|
||||
int color = stack != null && quad.hasTintIndex()
|
||||
? stack.getItem().getColorFromItemStack(stack, quad.getTintIndex()) | 0xff000000
|
||||
: 0xffffffff;
|
||||
LightUtil.renderQuadColor(buffer, quad, color);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ItemBasedDObject extends DObject {
|
||||
private final IBakedModel model;
|
||||
private final ItemStack stack;
|
||||
private boolean valid;
|
||||
|
||||
public ItemBasedDObject(IBakedModel model, ItemStack stack) {
|
||||
super(generateMeshForItemStack(model, stack));
|
||||
this.model = model;
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
public boolean isBasedOn(IBakedModel model, ItemStack scale) {
|
||||
return this.model == model; // FIXME ignores color of stack
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(int frame) {
|
||||
valid = true;
|
||||
super.setVisible(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return valid;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.replaymod.render.blend.exporters;
|
||||
|
||||
import com.replaymod.render.blend.BlendMeshBuilder;
|
||||
import com.replaymod.render.blend.Exporter;
|
||||
import com.replaymod.render.blend.data.DMesh;
|
||||
import com.replaymod.render.blend.data.DObject;
|
||||
import net.minecraft.client.model.ModelBox;
|
||||
import net.minecraft.client.model.ModelRenderer;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static com.replaymod.core.versions.MCVer.*;
|
||||
import static com.replaymod.render.blend.Util.isGlTextureMatrixIdentity;
|
||||
|
||||
public class ModelRendererExporter implements Exporter {
|
||||
private final RenderState renderState;
|
||||
|
||||
public ModelRendererExporter(RenderState renderState) {
|
||||
this.renderState = renderState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() throws IOException {
|
||||
}
|
||||
|
||||
public void preRenderModel(ModelRenderer model, float scale) {
|
||||
DObject object = getObjectForModel(model, scale);
|
||||
renderState.pushObject(object);
|
||||
renderState.pushModelView();
|
||||
}
|
||||
|
||||
public void onRenderModel() {
|
||||
if (!GL11.glIsEnabled(GL11.GL_TEXTURE_2D) || !isGlTextureMatrixIdentity()) {
|
||||
return;
|
||||
}
|
||||
renderState.popModelView();
|
||||
renderState.pushModelView();
|
||||
renderState.applyLastModelViewTransformToObject();
|
||||
DObject object = renderState.peekObject();
|
||||
object.setVisible(renderState.getFrame());
|
||||
object.keyframeLocRotScale(renderState.getFrame());
|
||||
}
|
||||
|
||||
public void postRenderModel() {
|
||||
renderState.pop();
|
||||
}
|
||||
|
||||
private DObject getObjectForModel(ModelRenderer model, float scale) {
|
||||
int frame = renderState.getFrame();
|
||||
DObject parent = renderState.peekObject();
|
||||
DObject object = null;
|
||||
for (DObject child : parent.getChildren()) {
|
||||
if (child.lastFrame < frame
|
||||
&& child instanceof ModelBasedDObject
|
||||
&& ((ModelBasedDObject) child).isBasedOn(model, scale)) {
|
||||
object = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (object == null) {
|
||||
object = new ModelBasedDObject(model, scale);
|
||||
object.id.name = model.boxName;
|
||||
object.setParent(parent);
|
||||
}
|
||||
object.lastFrame = frame;
|
||||
return object;
|
||||
}
|
||||
|
||||
private static DMesh generateMesh(ModelRenderer model, float scale) {
|
||||
DMesh mesh = new DMesh();
|
||||
BlendMeshBuilder builder = new BlendMeshBuilder(mesh);
|
||||
for (ModelBox box : cubeList(model)) {
|
||||
box.render(builder, scale);
|
||||
}
|
||||
builder.maybeFinishDrawing();
|
||||
return mesh;
|
||||
}
|
||||
|
||||
private static class ModelBasedDObject extends DObject {
|
||||
private final ModelRenderer model;
|
||||
private final float scale;
|
||||
private boolean valid;
|
||||
|
||||
public ModelBasedDObject(ModelRenderer model, float scale) {
|
||||
super(generateMesh(model, scale));
|
||||
this.model = model;
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
public boolean isBasedOn(ModelRenderer model, float scale) {
|
||||
return this.model == model && Math.abs(this.scale - scale) < 1e-4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(int frame) {
|
||||
valid = true;
|
||||
super.setVisible(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return valid;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.replaymod.render.blend.exporters;
|
||||
|
||||
import com.replaymod.render.blend.BlendMeshBuilder;
|
||||
import com.replaymod.render.blend.BlendState;
|
||||
import com.replaymod.render.blend.Exporter;
|
||||
import com.replaymod.render.blend.data.DMesh;
|
||||
import com.replaymod.render.blend.data.DObject;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.particle.EntityFX;
|
||||
import net.minecraft.entity.Entity;
|
||||
import org.lwjgl.util.vector.Matrix4f;
|
||||
import org.lwjgl.util.vector.Vector3f;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.replaymod.render.blend.Util.getGlModelViewMatrix;
|
||||
|
||||
public class ParticlesExporter implements Exporter {
|
||||
private final Minecraft mc = Minecraft.getMinecraft();
|
||||
private final RenderState renderState;
|
||||
private DObject pointAtObject;
|
||||
private DObject particlesObject;
|
||||
private DObject litParticlesObject;
|
||||
private Map<Entity, DObject> particleObjects;
|
||||
private Map<Entity, DObject> particleObjectsSeen;
|
||||
|
||||
public ParticlesExporter(RenderState renderState) {
|
||||
this.renderState = renderState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() throws IOException {
|
||||
if (true) return; // FIXME make configurable (also other methods below)
|
||||
// FIXME replace with camera
|
||||
pointAtObject = new DObject(DObject.Type.OB_EMPTY);
|
||||
pointAtObject.id.name = "Particles Target";
|
||||
pointAtObject.layers = 1 << 2;
|
||||
BlendState.getState().getScene().base.add(pointAtObject);
|
||||
|
||||
particlesObject = new DObject(DObject.Type.OB_EMPTY);
|
||||
particlesObject.id.name = "Particles";
|
||||
particlesObject.layers = 1 << 1;
|
||||
BlendState.getState().getScene().base.add(particlesObject);
|
||||
|
||||
litParticlesObject = new DObject(DObject.Type.OB_EMPTY);
|
||||
litParticlesObject.id.name = "Lit Particles";
|
||||
litParticlesObject.layers = 1 << 1;
|
||||
BlendState.getState().getScene().base.add(litParticlesObject);
|
||||
|
||||
particleObjects = new IdentityHashMap<>();
|
||||
particleObjectsSeen = new IdentityHashMap<>();
|
||||
}
|
||||
|
||||
public void preParticlesRender(boolean lit) {
|
||||
if (true) return;
|
||||
Matrix4f modelView = getGlModelViewMatrix();
|
||||
// Particles are rendered relative to the viewer location.
|
||||
// We however want our Particles object to not move when the viewer does,
|
||||
// so we position it at 0/0/0 and instead have the particles themselves move more
|
||||
Matrix4f.translate(new Vector3f(
|
||||
(float) -mc.getRenderManager().viewerPosX,
|
||||
(float) -mc.getRenderManager().viewerPosY,
|
||||
(float) -mc.getRenderManager().viewerPosZ
|
||||
), modelView, modelView);
|
||||
renderState.push(lit ? litParticlesObject : particlesObject, modelView);
|
||||
}
|
||||
|
||||
public void postParticlesRender() {
|
||||
if (true) return;
|
||||
renderState.pop();
|
||||
}
|
||||
|
||||
public void onRender(EntityFX particle, float renderPartialTicks) {
|
||||
if (true) return;
|
||||
DObject particleObject = particleObjects.get(particle);
|
||||
if (particleObject == null) {
|
||||
particleObject = new DObject(DObject.Type.OB_EMPTY);
|
||||
particleObject.setParent(renderState.peekObject());
|
||||
particleObject.pointAt = pointAtObject;
|
||||
particleObject.layers = 1 << 2;
|
||||
particleObject.id.name = particle.getClass().getSimpleName();
|
||||
particleObject.keyframe("hide", 0, renderState.getFrame() - 1, 1f);
|
||||
particleObject.keyframe("hide", 0, renderState.getFrame(), 0f);
|
||||
particleObjects.put(particle, particleObject);
|
||||
}
|
||||
particleObjectsSeen.put(particle, particleObject);
|
||||
renderState.pushObject(particleObject);
|
||||
|
||||
// Normally the particle renderer will translate to dx/dy/dz but we want that translation outside of
|
||||
// the particle object (on the particle object itself).
|
||||
// So, we translate there, then store the model-view-matrix and then translate back to let the
|
||||
// renderer translate there again.
|
||||
// Instead of actually translating, we just add the translation on the current model-view-matrix.
|
||||
Matrix4f modelView = getGlModelViewMatrix();
|
||||
double dx = particle.prevPosX + (particle.posX - particle.prevPosX) * renderPartialTicks - EntityFX.interpPosX;
|
||||
double dy = particle.prevPosY + (particle.posY - particle.prevPosY) * renderPartialTicks - EntityFX.interpPosY;
|
||||
double dz = particle.prevPosZ + (particle.posZ - particle.prevPosZ) * renderPartialTicks - EntityFX.interpPosZ;
|
||||
Vector3f offset = new Vector3f((float) dx, (float) dy, (float) dz);
|
||||
Matrix4f.translate(offset, modelView, modelView);
|
||||
renderState.pushModelView(modelView);
|
||||
renderState.applyLastModelViewTransformToObject();
|
||||
|
||||
particleObject.keyframeLoc(renderState.getFrame());
|
||||
particleObject.keyframeScale(renderState.getFrame());
|
||||
|
||||
DObject frameObject = new DObject(generateMeshForParticle(particle, offset));
|
||||
frameObject.setParent(particleObject);
|
||||
frameObject.layers = 1 << 2;
|
||||
frameObject.id.name = particleObject.id.name + " - Frame " + renderState.getFrame();
|
||||
frameObject.keyframe("hide", 0, renderState.getFrame() - 1, 1f);
|
||||
frameObject.keyframe("hide", 0, renderState.getFrame(), 0f);
|
||||
frameObject.keyframe("hide", 0, renderState.getFrame() + 1, 1f);
|
||||
|
||||
renderState.pop();
|
||||
}
|
||||
|
||||
private DMesh generateMeshForParticle(EntityFX particle, Vector3f offset) {
|
||||
DMesh mesh = new DMesh();
|
||||
BlendMeshBuilder builder = new BlendMeshBuilder(mesh);
|
||||
builder.setOffset(offset);
|
||||
builder.setWellBehaved(true);
|
||||
builder.startDrawingQuads();
|
||||
particle.func_180434_a(builder, Minecraft.getMinecraft().getRenderViewEntity(), 0, 1, 1, 0, 0, 0);
|
||||
builder.finishDrawing();
|
||||
return mesh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postFrame(int frame) throws IOException {
|
||||
if (true) return;
|
||||
for (Map.Entry<Entity, DObject> entry : particleObjects.entrySet()) {
|
||||
if (!particleObjectsSeen.containsKey(entry.getKey())) {
|
||||
DObject object = entry.getValue();
|
||||
object.keyframe("hide", 0, renderState.getFrame(), 1f);
|
||||
}
|
||||
}
|
||||
particleObjects = particleObjectsSeen;
|
||||
particleObjectsSeen = new IdentityHashMap<>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.replaymod.render.blend.exporters;
|
||||
|
||||
import com.replaymod.render.blend.BlendState;
|
||||
import com.replaymod.render.blend.Exporter;
|
||||
import com.replaymod.render.blend.data.DMaterial;
|
||||
import com.replaymod.render.blend.data.DObject;
|
||||
import org.lwjgl.util.vector.Matrix4f;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import static com.replaymod.render.ReplayModRender.LOGGER;
|
||||
import static com.replaymod.render.blend.Util.getGlModelViewMatrix;
|
||||
import static com.replaymod.render.blend.Util.glScaleToBlend;
|
||||
import static com.replaymod.render.blend.Util.glToBlend;
|
||||
import static com.replaymod.render.blend.Util.posFromMat;
|
||||
import static com.replaymod.render.blend.Util.rotFromMat;
|
||||
import static com.replaymod.render.blend.Util.scaleFromMat;
|
||||
import static com.replaymod.render.blend.Util.scaleMat3;
|
||||
|
||||
public class RenderState implements Exporter {
|
||||
|
||||
private final BlendState blendState;
|
||||
private final ModelRendererExporter modelExporter = new ModelRendererExporter(this);
|
||||
private final Deque<DObject> objectStack = new LinkedList<>();
|
||||
private final Deque<Matrix4f> modelViewStack = new LinkedList<>();
|
||||
|
||||
private int frame;
|
||||
|
||||
public RenderState(BlendState blendState) {
|
||||
this.blendState = blendState;
|
||||
|
||||
blendState.register(modelExporter);
|
||||
}
|
||||
|
||||
public int getFrame() {
|
||||
return frame;
|
||||
}
|
||||
|
||||
public DMaterial getCurrentMaterial() {
|
||||
return blendState.getMaterials().getActiveMaterial();
|
||||
}
|
||||
|
||||
public ModelRendererExporter getModelExporter() {
|
||||
return modelExporter;
|
||||
}
|
||||
|
||||
public Deque<DObject> getObjectStack() {
|
||||
return objectStack;
|
||||
}
|
||||
|
||||
public void push(DObject object, Matrix4f modelView) {
|
||||
pushObject(object);
|
||||
pushModelView(modelView);
|
||||
}
|
||||
|
||||
public void pushObject(DObject object) {
|
||||
objectStack.push(object);
|
||||
}
|
||||
|
||||
public void pushModelView() {
|
||||
pushModelView(getGlModelViewMatrix());
|
||||
}
|
||||
|
||||
public void pushModelView(Matrix4f modelView) {
|
||||
modelViewStack.push(modelView);
|
||||
}
|
||||
|
||||
public void pop() {
|
||||
popObject();
|
||||
popModelView();
|
||||
}
|
||||
|
||||
public DObject popObject() {
|
||||
return objectStack.pop();
|
||||
}
|
||||
|
||||
public Matrix4f popModelView() {
|
||||
return modelViewStack.pop();
|
||||
}
|
||||
|
||||
public DObject peekObject() {
|
||||
return objectStack.peek();
|
||||
}
|
||||
|
||||
public Matrix4f peekModelView() {
|
||||
return modelViewStack.peek();
|
||||
}
|
||||
|
||||
public void applyLastModelViewTransformToObject() {
|
||||
applyLastModelViewTransformToObject(peekObject());
|
||||
}
|
||||
|
||||
public void applyLastModelViewTransformToObject(DObject object) {
|
||||
Matrix4f mat = calcLastModelViewTransform();
|
||||
|
||||
posFromMat(mat, object.loc);
|
||||
scaleFromMat(mat, object.scale);
|
||||
scaleMat3(mat, object.scale);
|
||||
rotFromMat(mat, object.rot);
|
||||
|
||||
glToBlend(object.loc);
|
||||
glScaleToBlend(object.scale);
|
||||
glToBlend(object.rot);
|
||||
}
|
||||
|
||||
public Matrix4f calcLastModelViewTransform() {
|
||||
Iterator<Matrix4f> iter = modelViewStack.iterator();
|
||||
Matrix4f newModelView = iter.next();
|
||||
Matrix4f prevModelView = iter.next();
|
||||
// We need to apply the same transformation T to the object that has transformed the old into the new matrix:
|
||||
// O * T = N <=> T = O^{-1} * N
|
||||
return Matrix4f.mul(Matrix4f.invert(prevModelView, null), newModelView, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preFrame(int frame) throws IOException {
|
||||
this.frame = frame;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postFrame(int frame) throws IOException {
|
||||
if (!objectStack.isEmpty()) {
|
||||
LOGGER.warn("Post frame with non-empty object stack! ({} remaining)", objectStack.size());
|
||||
objectStack.clear();
|
||||
}
|
||||
if (!modelViewStack.isEmpty()) {
|
||||
LOGGER.warn("Post frame with non-empty model-view stack! ({} remaining)", modelViewStack.size());
|
||||
modelViewStack.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws IOException {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.replaymod.render.blend.exporters;
|
||||
|
||||
import com.replaymod.render.blend.BlendState;
|
||||
import com.replaymod.render.blend.Exporter;
|
||||
import com.replaymod.render.blend.data.DObject;
|
||||
import lombok.SneakyThrows;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.BlockPos;
|
||||
import org.lwjgl.util.vector.Matrix4f;
|
||||
import org.lwjgl.util.vector.Vector3f;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.replaymod.render.blend.Util.getGlModelViewMatrix;
|
||||
import static com.replaymod.render.blend.Util.getTileEntityId;
|
||||
|
||||
public class TileEntityExporter implements Exporter {
|
||||
private final Minecraft mc = Minecraft.getMinecraft();
|
||||
private final RenderState renderState;
|
||||
private DObject tileEntitiesObject;
|
||||
private Map<TileEntity, DObject> tileEntityObjects;
|
||||
private Map<TileEntity, DObject> tileEntityObjectsSeen;
|
||||
|
||||
public TileEntityExporter(RenderState renderState) {
|
||||
this.renderState = renderState;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setup() throws IOException {
|
||||
tileEntitiesObject = new DObject(DObject.Type.OB_EMPTY);
|
||||
tileEntitiesObject.id.name = "TileEntities";
|
||||
tileEntitiesObject.layers = 1 << 1;
|
||||
BlendState.getState().getScene().base.add(tileEntitiesObject);
|
||||
|
||||
tileEntityObjects = new IdentityHashMap<>();
|
||||
tileEntityObjectsSeen = new IdentityHashMap<>();
|
||||
}
|
||||
|
||||
public void preTileEntitiesRender() {
|
||||
Matrix4f modelView = getGlModelViewMatrix();
|
||||
// Tile entities are rendered relative to the viewer location.
|
||||
// We however want our TileEntities object to not move when the viewer does,
|
||||
// so we position it at 0/0/0 and instead have the tile entities themselves move more
|
||||
Matrix4f.translate(new Vector3f(
|
||||
(float) -mc.getRenderManager().viewerPosX,
|
||||
(float) -mc.getRenderManager().viewerPosY,
|
||||
(float) -mc.getRenderManager().viewerPosZ
|
||||
), modelView, modelView);
|
||||
renderState.push(tileEntitiesObject, modelView);
|
||||
}
|
||||
|
||||
public void postTileEntitiesRender() {
|
||||
renderState.pop();
|
||||
}
|
||||
|
||||
public void preRender(TileEntity tileEntity, double dx, double dy, double dz, float renderPartialTicks, int destroyStage) {
|
||||
DObject tileEntityObject = tileEntityObjects.get(tileEntity);
|
||||
if (tileEntityObject == null) {
|
||||
tileEntityObject = new DObject(DObject.Type.OB_EMPTY);
|
||||
tileEntityObject.setParent(renderState.peekObject());
|
||||
BlockPos pos = tileEntity.getPos();
|
||||
tileEntityObject.id.name = getTileEntityId(tileEntity) + "( " + pos.getX() + "/" + pos.getY() + "/" + pos.getZ() + ")";
|
||||
tileEntityObject.keyframe("hide", 0, renderState.getFrame() - 1, 1f);
|
||||
tileEntityObject.keyframe("hide", 0, renderState.getFrame(), 0f);
|
||||
tileEntityObjects.put(tileEntity, tileEntityObject);
|
||||
}
|
||||
tileEntityObjectsSeen.put(tileEntity, tileEntityObject);
|
||||
renderState.pushObject(tileEntityObject);
|
||||
|
||||
// Normally the tile entity renderer will translate to dx/dy/dz but we want that translation outside of
|
||||
// the tile entity object (on the tile entity object itself).
|
||||
// So, we translate there, then store the model-view-matrix and then translate back to let the
|
||||
// renderer translate there again.
|
||||
// Instead of actually translating, we just add the translation on the current model-view-matrix.
|
||||
Matrix4f modelView = getGlModelViewMatrix();
|
||||
Matrix4f.translate(new Vector3f((float) dx, (float) dy, (float) dz), modelView, modelView);
|
||||
renderState.pushModelView(modelView);
|
||||
renderState.applyLastModelViewTransformToObject();
|
||||
|
||||
tileEntityObject.keyframeLocRotScale(renderState.getFrame());
|
||||
}
|
||||
|
||||
public void postRender() {
|
||||
renderState.pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postFrame(int frame) throws IOException {
|
||||
for (Map.Entry<TileEntity, DObject> entry : tileEntityObjects.entrySet()) {
|
||||
if (!tileEntityObjectsSeen.containsKey(entry.getKey())) {
|
||||
DObject object = entry.getValue();
|
||||
object.keyframe("hide", 0, renderState.getFrame(), 1f);
|
||||
}
|
||||
}
|
||||
tileEntityObjects = tileEntityObjectsSeen;
|
||||
tileEntityObjectsSeen = new IdentityHashMap<>();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user