Skip to content

Commit

Permalink
More aggressively optimize entity rendering
Browse files Browse the repository at this point in the history
This greatly reduces the amount of memory needed, and provides
a small speed-up of around ~5-10% for complex models.

The biggest change here involves how normals are generated, since
entity model and item model quads always have a unit vector for
their untransformed normal. Knowing this, we can simply extract
the elements from the matrix to get a transformed unit vector, rather
than go through the trouble of actually performing the dot product
summation.
  • Loading branch information
jellysquid3 committed Dec 3, 2023
1 parent 105c326 commit e228d59
Show file tree
Hide file tree
Showing 14 changed files with 407 additions and 314 deletions.
135 changes: 24 additions & 111 deletions src/api/java/net/caffeinemc/mods/sodium/api/math/MatrixHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

import net.caffeinemc.mods.sodium.api.util.NormI8;
import net.minecraft.client.util.math.MatrixStack;
import org.joml.Math;
import net.minecraft.util.math.Direction;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.joml.Vector3f;

/**
* Implements optimized utilities for transforming vectors with a given matrix.
Expand All @@ -13,36 +14,6 @@
* the same as those produced by JOML, otherwise Z-fighting will occur.
*/
public class MatrixHelper {
/**
* @param mat The transformation matrix to apply to the normal
* @param x The X-coordinate of the normal vector
* @param y The Y-coordinate of the normal vector
* @param z The Z-coordinate of the normal vector
* @return The transformed normal vector (in packed format)
*/
public static int transformNormal(Matrix3f mat, float x, float y, float z) {
// The transformed normal vector
float nxt = transformNormalX(mat, x, y, z);
float nyt = transformNormalY(mat, x, y, z);
float nzt = transformNormalZ(mat, x, y, z);

return NormI8.pack(nxt, nyt, nzt);
}

/**
* @param mat The transformation matrix to apply to the normal
* @param norm The normal vector to transform (in packed format)
* @return The transformed normal vector (in packed format)
*/
public static int transformNormal(Matrix3f mat, int norm) {
// The unpacked normal vector
float x = NormI8.unpackX(norm);
float y = NormI8.unpackY(norm);
float z = NormI8.unpackZ(norm);

return transformNormal(mat, x, y, z);
}

/**
* @param mat The transformation matrix to apply to the normal vector
* @param x The X-coordinate of the normal vector
Expand Down Expand Up @@ -120,88 +91,30 @@ public static float transformPositionZ(Matrix4f mat, float x, float y, float z)
* @param angleX The angle to rotate by on the X-axis
*/
public static void rotateZYX(MatrixStack.Entry matrices, float angleZ, float angleY, float angleX) {
float sinX = Math.sin(angleX);
float cosX = Math.cosFromSin(sinX, angleX);
float sinInvX = -sinX;

float sinY = Math.sin(angleY);
float cosY = Math.cosFromSin(sinY, angleY);
float sinInvY = -sinY;
matrices.getPositionMatrix()
.rotateZYX(angleZ, angleY, angleX);

float sinZ = Math.sin(angleZ);
float cosZ = Math.cosFromSin(sinZ, angleZ);
float sinInvZ = -sinZ;

applySinCosMat4(matrices.getPositionMatrix(), sinX, sinY, sinZ, cosX, cosY, cosZ, sinInvX, sinInvY, sinInvZ);
applySinCosMat3(matrices.getNormalMatrix(), sinX, sinY, sinZ, cosX, cosY, cosZ, sinInvX, sinInvY, sinInvZ);
matrices.getNormalMatrix()
.rotateZYX(angleZ, angleY, angleX);
}

private static void applySinCosMat4(Matrix4f mat, float sinX, float sinY, float sinZ, float cosX, float cosY, float cosZ, float sinInvX, float sinInvY, float sinInvZ) {
float nm00 = (mat.m00() * cosZ) + (mat.m10() * sinZ);
float nm01 = (mat.m01() * cosZ) + (mat.m11() * sinZ);
float nm02 = (mat.m02() * cosZ) + (mat.m12() * sinZ);
float nm03 = (mat.m03() * cosZ) + (mat.m13() * sinZ);

float nm10 = (mat.m00() * sinInvZ) + (mat.m10() * cosZ);
float nm11 = (mat.m01() * sinInvZ) + (mat.m11() * cosZ);
float nm12 = (mat.m02() * sinInvZ) + (mat.m12() * cosZ);
float nm13 = (mat.m03() * sinInvZ) + (mat.m13() * cosZ);

float nm20 = (nm00 * sinY) + (mat.m20() * cosY);
float nm21 = (nm01 * sinY) + (mat.m21() * cosY);
float nm22 = (nm02 * sinY) + (mat.m22() * cosY);
float nm23 = (nm03 * sinY) + (mat.m23() * cosY);

// Setting each component individually involves significant overhead since the properties
// for the matrix will be re-calculated each time.
mat.set(
(nm00 * cosY) + (mat.m20() * sinInvY),
(nm01 * cosY) + (mat.m21() * sinInvY),
(nm02 * cosY) + (mat.m22() * sinInvY),
(nm03 * cosY) + (mat.m23() * sinInvY),

(nm10 * cosX) + (nm20 * sinX),
(nm11 * cosX) + (nm21 * sinX),
(nm12 * cosX) + (nm22 * sinX),
(nm13 * cosX) + (nm23 * sinX),

(nm10 * sinInvX) + (nm20 * cosX),
(nm11 * sinInvX) + (nm21 * cosX),
(nm12 * sinInvX) + (nm22 * cosX),
(nm13 * sinInvX) + (nm23 * cosX),

mat.m30(),
mat.m31(),
mat.m32(),
mat.m33()
);
}

private static void applySinCosMat3(Matrix3f mat, float sinX, float sinY, float sinZ, float cosX, float cosY, float cosZ, float sinInvX, float sinInvY, float sinInvZ) {
float nm00 = mat.m00() * cosZ + mat.m10() * sinZ;
float nm01 = mat.m01() * cosZ + mat.m11() * sinZ;
float nm02 = mat.m02() * cosZ + mat.m12() * sinZ;

float nm10 = mat.m00() * sinInvZ + mat.m10() * cosZ;
float nm11 = mat.m01() * sinInvZ + mat.m11() * cosZ;
float nm12 = mat.m02() * sinInvZ + mat.m12() * cosZ;

float nm20 = nm00 * sinY + mat.m20() * cosY;
float nm21 = nm01 * sinY + mat.m21() * cosY;
float nm22 = nm02 * sinY + mat.m22() * cosY;

// Setting each component individually involves significant overhead since the properties
// for the matrix will be re-calculated each time.
mat.set(nm00 * cosY + mat.m20() * sinInvY,
nm01 * cosY + mat.m21() * sinInvY,
nm02 * cosY + mat.m22() * sinInvY,

nm10 * cosX + nm20 * sinX,
nm11 * cosX + nm21 * sinX,
nm12 * cosX + nm22 * sinX,

nm10 * sinInvX + nm20 * cosX,
nm11 * sinInvX + nm21 * cosX,
nm12 * sinInvX + nm22 * cosX);
/**
* Returns the transformed normal vector for a given unit vector (direction). This is significantly faster
* than transforming the vector directly (i.e. with {@link Matrix3f#transform(Vector3f)}), as it can simply
* extract the values from the provided matrix (rather than transforming the vertices.)
*
* @param matrix The transformation matrix
* @param direction The unit vector (direction) to use
* @return A transformed normal in packed format
*/
public static int transformNormal(Matrix3f matrix, Direction direction) {
return switch (direction) {
case DOWN -> NormI8.pack(-matrix.m10, -matrix.m11, -matrix.m12);
case UP -> NormI8.pack( matrix.m10, matrix.m11, matrix.m12);
case NORTH -> NormI8.pack(-matrix.m20, -matrix.m21, -matrix.m22);
case SOUTH -> NormI8.pack( matrix.m20, matrix.m21, matrix.m22);
case WEST -> NormI8.pack(-matrix.m00, -matrix.m01, -matrix.m02);
case EAST -> NormI8.pack( matrix.m00, matrix.m01, matrix.m02);
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@

public interface BakedQuadView extends ModelQuadView {
ModelQuadFacing getNormalFace();

Direction getLightFace();


boolean hasShade();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package me.jellysquid.mods.sodium.client.model.quad;

import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.Direction;

import static me.jellysquid.mods.sodium.client.util.ModelQuadUtil.*;

Expand All @@ -13,8 +14,9 @@ public class ModelQuad implements ModelQuadViewMutable {
private int flags;

private Sprite sprite;
private Direction direction;

private int colorIdx;
private int normal;

@Override
public void setX(int idx, float x) {
Expand Down Expand Up @@ -67,8 +69,8 @@ public void setColorIndex(int index) {
}

@Override
public void setNormal(int norm) {
this.normal = norm;
public void setLightFace(Direction direction) {
this.direction = direction;
}

@Override
Expand Down Expand Up @@ -117,7 +119,7 @@ public Sprite getSprite() {
}

@Override
public int getNormal() {
return this.normal;
public Direction getLightFace() {
return this.direction;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import me.jellysquid.mods.sodium.client.model.quad.properties.ModelQuadFlags;
import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.Direction;

/**
* Provides a read-only view of a model quad. For mutable access to a model quad, see {@link ModelQuadViewMutable}.
Expand Down Expand Up @@ -51,11 +52,11 @@ public interface ModelQuadView {
* @return The sprite texture used by this quad, or null if none is attached
*/
Sprite getSprite();

/**
* @return The normal vector of the quad in 3-byte packed format
* @return The face used by this quad for lighting effects
*/
int getNormal();
Direction getLightFace();

default boolean hasColor() {
return this.getColorIndex() != -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import me.jellysquid.mods.sodium.client.model.quad.properties.ModelQuadFlags;
import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.Direction;

/**
* Provides a mutable view to a model quad.
Expand Down Expand Up @@ -53,12 +54,12 @@ public interface ModelQuadViewMutable extends ModelQuadView {
void setSprite(Sprite sprite);

/**
* Sets the color index used by this quad
* Sets the face used by this quad for lighting effects
*/
void setColorIndex(int index);
void setLightFace(Direction direction);

/**
* Sets the 3-byte packed normal for this quad
* Sets the color index used by this quad
*/
void setNormal(int norm);
void setColorIndex(int index);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,11 @@
import me.jellysquid.mods.sodium.client.world.WorldSlice;
import me.jellysquid.mods.sodium.client.util.DirectionUtil;
import net.caffeinemc.mods.sodium.api.util.ColorABGR;
import net.caffeinemc.mods.sodium.api.util.NormI8;
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler;
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
import net.minecraft.block.BlockState;
import net.minecraft.block.SideShapeType;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.model.ModelLoader;
import net.minecraft.client.texture.Sprite;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.FluidState;
Expand Down Expand Up @@ -62,7 +60,7 @@ public class FluidRenderer {
private final ColorProviderRegistry colorProviderRegistry;

public FluidRenderer(ColorProviderRegistry colorProviderRegistry, LightPipelineProvider lighters) {
this.quad.setNormal(NormI8.pack(0.0f, 1.0f, 0.0f));
this.quad.setLightFace(Direction.UP);

this.lighters = lighters;
this.colorProviderRegistry = colorProviderRegistry;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static void writeQuadVertices(VertexBufferWriter writer, MatrixStack.Entr
long ptr = buffer;

// The packed transformed normal vector
var normal = MatrixHelper.transformNormal(matNormal, quad.getNormal());
var normal = MatrixHelper.transformNormal(matNormal, quad.getLightFace());

for (int i = 0; i < 4; i++) {
// The position vector
Expand Down Expand Up @@ -51,7 +51,7 @@ public static void writeQuadVertices(VertexBufferWriter writer, MatrixStack.Entr
long ptr = buffer;

// The packed transformed normal vector
var normal = MatrixHelper.transformNormal(matNormal, quad.getNormal());
var normal = MatrixHelper.transformNormal(matNormal, quad.getLightFace());

for (int i = 0; i < 4; i++) {
// The position vector
Expand Down
Loading

0 comments on commit e228d59

Please sign in to comment.