mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-04 03:16:24 +01:00
This is fine
- Support rendering the fire animation with instances. - Add scaleX/Y/Z methods to Scale. - Add Camera to VisualFrameContext. - Add camera rotation and look vectors to shader uniforms.
This commit is contained in:
parent
0177849395
commit
9ab8559efe
11 changed files with 271 additions and 22 deletions
|
@ -3,6 +3,8 @@ package com.jozufozu.flywheel.api.visual;
|
|||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.joml.FrustumIntersection;
|
||||
|
||||
import net.minecraft.client.Camera;
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
public interface VisualFrameContext {
|
||||
double cameraX();
|
||||
|
@ -16,4 +18,6 @@ public interface VisualFrameContext {
|
|||
float partialTick();
|
||||
|
||||
DistanceUpdateLimiter limiter();
|
||||
|
||||
Camera camera();
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import net.minecraft.core.Vec3i;
|
|||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class FrameUniforms implements UniformProvider {
|
||||
public static final int SIZE = 194;
|
||||
public static final int SIZE = 228;
|
||||
|
||||
private RenderContext context;
|
||||
|
||||
|
@ -29,12 +29,12 @@ public class FrameUniforms implements UniformProvider {
|
|||
public void write(long ptr) {
|
||||
Vec3i renderOrigin = VisualizationManager.getOrThrow(context.level())
|
||||
.getRenderOrigin();
|
||||
Vec3 camera = context.camera()
|
||||
.getPosition();
|
||||
var camera = context.camera();
|
||||
|
||||
var camX = (float) (camera.x - renderOrigin.getX());
|
||||
var camY = (float) (camera.y - renderOrigin.getY());
|
||||
var camZ = (float) (camera.z - renderOrigin.getZ());
|
||||
Vec3 cameraPos = camera.getPosition();
|
||||
var camX = (float) (cameraPos.x - renderOrigin.getX());
|
||||
var camY = (float) (cameraPos.y - renderOrigin.getY());
|
||||
var camZ = (float) (cameraPos.z - renderOrigin.getZ());
|
||||
|
||||
viewProjection.set(context.viewProjection());
|
||||
viewProjection.translate(-camX, -camY, -camZ);
|
||||
|
@ -45,23 +45,41 @@ public class FrameUniforms implements UniformProvider {
|
|||
}
|
||||
|
||||
MatrixMath.writeUnsafe(viewProjection, ptr + 96);
|
||||
MemoryUtil.memPutFloat(ptr + 160, camX);
|
||||
MemoryUtil.memPutFloat(ptr + 164, camY);
|
||||
MemoryUtil.memPutFloat(ptr + 168, camZ);
|
||||
MemoryUtil.memPutFloat(ptr + 172, 0f); // empty component of vec4 because we don't trust std140
|
||||
MemoryUtil.memPutInt(ptr + 176, getConstantAmbientLightFlag(context));
|
||||
writeVec3(ptr + 160, camX, camY, camZ);
|
||||
|
||||
var lookVector = camera.getLookVector();
|
||||
writeVec3(ptr + 176, lookVector.x, lookVector.y, lookVector.z);
|
||||
|
||||
writeVec2(ptr + 192, camera.getXRot(), camera.getYRot());
|
||||
|
||||
MemoryUtil.memPutInt(ptr + 208, getConstantAmbientLightFlag(context));
|
||||
|
||||
writeTime(ptr + 212);
|
||||
}
|
||||
|
||||
private void writeTime(long ptr) {
|
||||
int ticks = context.renderer()
|
||||
.getTicks();
|
||||
float partialTick = context.partialTick();
|
||||
float renderTicks = ticks + partialTick;
|
||||
float renderSeconds = renderTicks / 20f;
|
||||
|
||||
MemoryUtil.memPutInt(ptr + 180, ticks);
|
||||
MemoryUtil.memPutFloat(ptr + 184, partialTick);
|
||||
MemoryUtil.memPutFloat(ptr + 188, renderTicks);
|
||||
MemoryUtil.memPutFloat(ptr + 192, renderSeconds);
|
||||
MemoryUtil.memPutInt(ptr, ticks);
|
||||
MemoryUtil.memPutFloat(ptr + 4, partialTick);
|
||||
MemoryUtil.memPutFloat(ptr + 8, renderTicks);
|
||||
MemoryUtil.memPutFloat(ptr + 12, renderSeconds);
|
||||
}
|
||||
|
||||
private static void writeVec3(long ptr, float camX, float camY, float camZ) {
|
||||
MemoryUtil.memPutFloat(ptr, camX);
|
||||
MemoryUtil.memPutFloat(ptr + 4, camY);
|
||||
MemoryUtil.memPutFloat(ptr + 8, camZ);
|
||||
MemoryUtil.memPutFloat(ptr + 12, 0f); // empty component of vec4 because we don't trust std140
|
||||
}
|
||||
|
||||
private static void writeVec2(long ptr, float camX, float camY) {
|
||||
MemoryUtil.memPutFloat(ptr, camX);
|
||||
MemoryUtil.memPutFloat(ptr + 4, camY);
|
||||
}
|
||||
|
||||
private static int getConstantAmbientLightFlag(RenderContext context) {
|
||||
|
|
|
@ -5,6 +5,9 @@ import org.joml.FrustumIntersection;
|
|||
import com.jozufozu.flywheel.api.visual.DistanceUpdateLimiter;
|
||||
import com.jozufozu.flywheel.api.visual.VisualFrameContext;
|
||||
|
||||
import net.minecraft.client.Camera;
|
||||
|
||||
public record VisualFrameContextImpl(double cameraX, double cameraY, double cameraZ, FrustumIntersection frustum,
|
||||
float partialTick, DistanceUpdateLimiter limiter) implements VisualFrameContext {
|
||||
float partialTick, DistanceUpdateLimiter limiter,
|
||||
Camera camera) implements VisualFrameContext {
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ public class VisualizationManagerImpl implements VisualizationManager {
|
|||
viewProjection.translate((float) (renderOrigin.getX() - cameraX), (float) (renderOrigin.getY() - cameraY), (float) (renderOrigin.getZ() - cameraZ));
|
||||
FrustumIntersection frustum = new FrustumIntersection(viewProjection);
|
||||
|
||||
return new VisualFrameContextImpl(cameraX, cameraY, cameraZ, frustum, ctx.partialTick(), frameLimiter);
|
||||
return new VisualFrameContextImpl(cameraX, cameraY, cameraZ, frustum, ctx.partialTick(), frameLimiter, ctx.camera());
|
||||
}
|
||||
|
||||
protected DistanceUpdateLimiterImpl createUpdateLimiter() {
|
||||
|
|
|
@ -11,7 +11,7 @@ public class ShadowInstance extends AbstractInstance {
|
|||
public float alpha;
|
||||
public float radius;
|
||||
|
||||
protected ShadowInstance(InstanceType<?> type, InstanceHandle handle) {
|
||||
public ShadowInstance(InstanceType<?> type, InstanceHandle handle) {
|
||||
super(type, handle);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,10 +73,12 @@ public class TransformedInstance extends ColoredLitInstance implements Transform
|
|||
}
|
||||
|
||||
public TransformedInstance setTransform(PoseStack stack) {
|
||||
this.model.set(stack.last()
|
||||
.pose());
|
||||
this.normal.set(stack.last()
|
||||
.normal());
|
||||
return setTransform(stack.last());
|
||||
}
|
||||
|
||||
public TransformedInstance setTransform(PoseStack.Pose pose) {
|
||||
this.model.set(pose.pose());
|
||||
this.normal.set(pose.normal());
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,4 +6,16 @@ public interface Scale<Self extends Scale<Self>> {
|
|||
default Self scale(float factor) {
|
||||
return scale(factor, factor, factor);
|
||||
}
|
||||
|
||||
default Self scaleX(float factor) {
|
||||
return scale(factor, 1, 1);
|
||||
}
|
||||
|
||||
default Self scaleY(float factor) {
|
||||
return scale(1, factor, 1);
|
||||
}
|
||||
|
||||
default Self scaleZ(float factor) {
|
||||
return scale(1, 1, factor);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,12 +34,14 @@ public abstract class AbstractEntityVisual<T extends Entity> extends AbstractVis
|
|||
protected final T entity;
|
||||
protected final EntityVisibilityTester visibilityTester;
|
||||
protected final ShadowComponent shadow;
|
||||
protected final FireComponent fire;
|
||||
|
||||
public AbstractEntityVisual(VisualizationContext ctx, T entity) {
|
||||
super(ctx, entity.level());
|
||||
this.entity = entity;
|
||||
visibilityTester = new EntityVisibilityTester(entity, ctx.renderOrigin(), 1.5f);
|
||||
shadow = new ShadowComponent(ctx, entity);
|
||||
fire = new FireComponent(ctx, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -93,5 +95,6 @@ public abstract class AbstractEntityVisual<T extends Entity> extends AbstractVis
|
|||
@Override
|
||||
protected void _delete() {
|
||||
shadow.delete();
|
||||
fire.delete();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
package com.jozufozu.flywheel.lib.visual;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.joml.Vector4f;
|
||||
import org.joml.Vector4fc;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||
import com.jozufozu.flywheel.api.material.Material;
|
||||
import com.jozufozu.flywheel.api.model.Mesh;
|
||||
import com.jozufozu.flywheel.api.model.Model;
|
||||
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
|
||||
import com.jozufozu.flywheel.api.visual.VisualFrameContext;
|
||||
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||
import com.jozufozu.flywheel.lib.instance.InstanceTypes;
|
||||
import com.jozufozu.flywheel.lib.instance.TransformedInstance;
|
||||
import com.jozufozu.flywheel.lib.material.Materials;
|
||||
import com.jozufozu.flywheel.lib.material.SimpleMaterial;
|
||||
import com.jozufozu.flywheel.lib.model.ModelCache;
|
||||
import com.jozufozu.flywheel.lib.model.QuadMesh;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Axis;
|
||||
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.model.ModelBakery;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
||||
/**
|
||||
* A component that uses instances to render the fire animation on an entity.
|
||||
*/
|
||||
public class FireComponent {
|
||||
private final VisualizationContext context;
|
||||
private final Entity entity;
|
||||
private final PoseStack stack = new PoseStack();
|
||||
|
||||
private final InstanceRecycler<TransformedInstance> fire0;
|
||||
private final InstanceRecycler<TransformedInstance> fire1;
|
||||
|
||||
public FireComponent(VisualizationContext context, Entity entity) {
|
||||
this.context = context;
|
||||
this.entity = entity;
|
||||
|
||||
fire0 = new InstanceRecycler<>(() -> context.instancerProvider()
|
||||
.instancer(InstanceTypes.TRANSFORMED, FireModel.CACHE.get(ModelBakery.FIRE_0), RenderStage.AFTER_BLOCK_ENTITIES)
|
||||
.createInstance());
|
||||
fire1 = new InstanceRecycler<>(() -> context.instancerProvider()
|
||||
.instancer(InstanceTypes.TRANSFORMED, FireModel.CACHE.get(ModelBakery.FIRE_1), RenderStage.AFTER_BLOCK_ENTITIES)
|
||||
.createInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the fire instances. You'd typically call this in your visual's
|
||||
* {@link com.jozufozu.flywheel.api.visual.DynamicVisual#beginFrame(VisualFrameContext) beginFrame} method.
|
||||
*
|
||||
* @param context The frame context.
|
||||
*/
|
||||
public void beginFrame(VisualFrameContext context) {
|
||||
fire0.resetCount();
|
||||
fire1.resetCount();
|
||||
|
||||
if (entity.displayFireAnimation()) {
|
||||
setupInstances(context);
|
||||
}
|
||||
|
||||
fire0.discardExtra();
|
||||
fire1.discardExtra();
|
||||
}
|
||||
|
||||
private void setupInstances(VisualFrameContext context) {
|
||||
double entityX = Mth.lerp(context.partialTick(), entity.xOld, entity.getX());
|
||||
double entityY = Mth.lerp(context.partialTick(), entity.yOld, entity.getY());
|
||||
double entityZ = Mth.lerp(context.partialTick(), entity.zOld, entity.getZ());
|
||||
var renderOrigin = this.context.renderOrigin();
|
||||
|
||||
final float scale = entity.getBbWidth() * 1.4F;
|
||||
final float maxHeight = entity.getBbHeight() / scale;
|
||||
float width = 1;
|
||||
float y = 0;
|
||||
float z = 0;
|
||||
|
||||
stack.setIdentity();
|
||||
stack.translate(entityX - renderOrigin.getX(), entityY - renderOrigin.getY(), entityZ - renderOrigin.getZ());
|
||||
stack.scale(scale, scale, scale);
|
||||
stack.mulPose(Axis.YP.rotationDegrees(-context.camera()
|
||||
.getYRot()));
|
||||
stack.translate(0.0F, 0.0F, -0.3F + (float) ((int) maxHeight) * 0.02F);
|
||||
|
||||
for (int i = 0; y < maxHeight; ++i) {
|
||||
var instance = (i % 2 == 0 ? this.fire0 : this.fire1).get()
|
||||
.setTransform(stack)
|
||||
.scaleX(width)
|
||||
.translate(0, y, z);
|
||||
|
||||
if (i / 2 % 2 == 0) {
|
||||
// Vanilla flips the uv directly, but it's easier for us to flip the whole model.
|
||||
instance.scaleX(-1);
|
||||
}
|
||||
|
||||
instance.setBlockLight(LightTexture.block(240));
|
||||
|
||||
instance.setChanged();
|
||||
|
||||
y += 0.45F;
|
||||
// Get narrower as we go up.
|
||||
width *= 0.9F;
|
||||
// Offset each one so they don't z-fight.
|
||||
z += 0.03F;
|
||||
}
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
fire0.delete();
|
||||
fire1.delete();
|
||||
}
|
||||
|
||||
private static class FireModel implements Model {
|
||||
// Parameterize by the material instead of the sprite
|
||||
// because Material#sprite is a surprisingly heavy operation.
|
||||
public static final ModelCache<net.minecraft.client.resources.model.Material> CACHE = new ModelCache<>(mat -> new FireModel(mat.sprite()));
|
||||
|
||||
public static final Material MATERIAL = SimpleMaterial.builderOf(Materials.CHUNK_CUTOUT_UNSHADED)
|
||||
.backfaceCulling(false) // Disable backface because we want to be able to flip the model.
|
||||
.build();
|
||||
private static final Vector4fc BOUNDING_SPHERE = new Vector4f(0, 0.5f, 0, (float) (Math.sqrt(2) * 0.5));
|
||||
|
||||
private final ImmutableMap<Material, Mesh> meshes;
|
||||
|
||||
private FireModel(TextureAtlasSprite sprite) {
|
||||
meshes = ImmutableMap.of(MATERIAL, new FireMesh(sprite));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Material, Mesh> meshes() {
|
||||
return meshes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector4fc boundingSphere() {
|
||||
return BOUNDING_SPHERE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int vertexCount() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
|
||||
}
|
||||
|
||||
private record FireMesh(TextureAtlasSprite sprite) implements QuadMesh {
|
||||
@Override
|
||||
public int vertexCount() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(MutableVertexList vertexList) {
|
||||
float u0 = sprite.getU0();
|
||||
float v0 = sprite.getV0();
|
||||
float u1 = sprite.getU1();
|
||||
float v1 = sprite.getV1();
|
||||
writeVertex(vertexList, 0, 0.5f, 0, u1, v1);
|
||||
writeVertex(vertexList, 1, -0.5f, 0, u0, v1);
|
||||
writeVertex(vertexList, 2, -0.5f, 1.4f, u0, v0);
|
||||
writeVertex(vertexList, 3, 0.5f, 1.4f, u1, v0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector4fc boundingSphere() {
|
||||
return BOUNDING_SPHERE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
|
||||
}
|
||||
|
||||
// Magic numbers taken from:
|
||||
// net.minecraft.client.renderer.entity.EntityRenderDispatcher#fireVertex
|
||||
private static void writeVertex(MutableVertexList vertexList, int i, float x, float y, float u, float v) {
|
||||
vertexList.x(i, x);
|
||||
vertexList.y(i, y);
|
||||
vertexList.z(i, 0);
|
||||
vertexList.r(i, 1);
|
||||
vertexList.g(i, 1);
|
||||
vertexList.b(i, 1);
|
||||
vertexList.u(i, u);
|
||||
vertexList.v(i, v);
|
||||
vertexList.overlay(i, OverlayTexture.NO_OVERLAY);
|
||||
vertexList.light(i, 240);
|
||||
vertexList.normalX(i, 0);
|
||||
vertexList.normalY(i, 1);
|
||||
vertexList.normalZ(i, 0);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -111,6 +111,7 @@ public class MinecartVisual<T extends AbstractMinecart> extends AbstractEntityVi
|
|||
@Override
|
||||
public void beginFrame(VisualFrameContext context) {
|
||||
shadow.beginFrame(context);
|
||||
fire.beginFrame(context);
|
||||
|
||||
if (!isVisible(context.frustum())) {
|
||||
return;
|
||||
|
|
|
@ -13,6 +13,8 @@ layout(std140) uniform _FlwFrameUniforms {
|
|||
FrustumPlanes flw_frustumPlanes;
|
||||
mat4 flw_viewProjection;
|
||||
vec4 flw_cameraPos;
|
||||
vec4 flw_cameraLook;
|
||||
vec2 flw_cameraRot;
|
||||
uint flw_constantAmbientLight;
|
||||
|
||||
uint flw_ticks;
|
||||
|
|
Loading…
Reference in a new issue