Create a world-to-screen projection helper

We also grab the view matrix and set up a world rendering hook

I needed to use LWJGL 2's util for GLU, but I can probably switch to
JOML or something
This commit is contained in:
Charlotte Som 2022-02-04 18:25:19 +00:00
parent d11f3e0927
commit 0d831078a8
4 changed files with 156 additions and 0 deletions

View file

@ -49,6 +49,8 @@ dependencies {
}
implementation("io.github.spair:imgui-java-natives-windows-ft:$imguiVersion")
implementation(files("vendor/lwjgl-util.jar"))
runtimeOnly("org.joml:joml:1.10.2")
runtimeOnly("org.anarres:jcpp:1.4.14")
modRuntimeOnly(files("vendor/iris-mc1.18.1-1.2.0-pre.jar"))

View file

@ -0,0 +1,46 @@
package codes.som.hibiscus.mixins;
import codes.som.hibiscus.HibiscusMod;
import codes.som.hibiscus.events.RenderWorldEvent;
import codes.som.hibiscus.util.graphics.WorldToScreenProjection;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.Matrix4f;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(GameRenderer.class)
public abstract class MixinGameRenderer {
@Shadow
@Final
private MinecraftClient client;
@Shadow
@Final
private Camera camera;
@Inject(
method = "renderWorld(FJLnet/minecraft/client/util/math/MatrixStack;)V",
at = @At(
value = "INVOKE_STRING",
target = "Lnet/minecraft/util/profiler/Profiler;swap(Ljava/lang/String;)V",
args = "ldc=hand",
shift = At.Shift.BEFORE))
private void preRenderHand(float tickDelta, long limitTime, MatrixStack matrices, CallbackInfo ci) {
Matrix4f modelView = matrices.peek().getModel();
WorldToScreenProjection.INSTANCE.updateViewport(client.getWindow(), modelView);
matrices.push();
RenderSystem.applyModelViewMatrix();
HibiscusMod.bus().fire(new RenderWorldEvent(tickDelta, this.camera, matrices));
matrices.pop();
}
}

View file

@ -1,5 +1,8 @@
package codes.som.hibiscus.events
import codes.som.hibiscus.api.event.Event
import net.minecraft.client.render.Camera
import net.minecraft.client.util.math.MatrixStack
class PostRenderAllEvent(val delta: Float) : Event
class RenderWorldEvent(val delta: Float, val camera: Camera, val matrices: MatrixStack) : Event

View file

@ -0,0 +1,105 @@
package codes.som.hibiscus.util.graphics
import codes.som.hibiscus.mc
import net.minecraft.client.util.GlAllocationUtils
import net.minecraft.client.util.Window
import net.minecraft.util.math.MathHelper
import net.minecraft.util.math.Matrix4f
import net.minecraft.util.math.Vec3d
import org.lwjgl.opengl.GL11
import org.lwjgl.util.glu.Project.gluProject
import kotlin.math.absoluteValue
import kotlin.math.sign
object WorldToScreenProjection {
private const val PROJECTION_MATRIX_PLANE_RATIO_INDEX = 10
private const val PROJECTION_MATRIX_FAR_PLANE_INDEX = 14
private const val VIEWPORT_MATRIX_Y_OFFSET_INDEX = 1
private const val VIEWPORT_MATRIX_WIDTH_INDEX = 2
private const val VIEWPORT_MATRIX_HEIGHT_INDEX = 3
private const val PLANE_RATIO = -1.0f
private val FAR_PLANE_DISTANCE = -6.0E7f * MathHelper.SQUARE_ROOT_OF_TWO * 2.0f
private const val WINDOW_POSITION_VECTOR_DIMENSIONALITY = 3
private const val WINDOW_POSITION_VECTOR_X_INDEX = 0
private const val WINDOW_POSITION_VECTOR_Y_INDEX = 1
private const val WINDOW_POSITION_VECTOR_Z_INDEX = 2
private val VIEWPORT_MATRIX =
GlAllocationUtils.allocateByteBuffer(4 * Int.SIZE_BYTES).asIntBuffer() // 4x1 matrix of integers
private val MODEL_MATRIX =
GlAllocationUtils.allocateByteBuffer(16 * Float.SIZE_BYTES).asFloatBuffer() // 4x4 matrix of floats
private val PROJECTION_MATRIX =
GlAllocationUtils.allocateByteBuffer(16 * Float.SIZE_BYTES).asFloatBuffer() // 4x4 matrix of floats
private val WINDOW_POSITION_VECTOR =
GlAllocationUtils.allocateByteBuffer(WINDOW_POSITION_VECTOR_DIMENSIONALITY * Float.SIZE_BYTES).asFloatBuffer()
private var scaledWidth = 0
private var scaledHeight = 0
private var cameraX = 0.0
private var cameraY = 0.0
private var cameraZ = 0.0
fun updateViewport(window: Window, modelView: Matrix4f) {
scaledWidth = window.scaledWidth
scaledHeight = window.scaledHeight
val camera = mc.gameRenderer.camera
val cameraPos = if (camera == null) Vec3d.ZERO else camera.pos
cameraX = cameraPos.x
cameraY = cameraPos.y
cameraZ = cameraPos.z
modelView.writeRowMajor(MODEL_MATRIX)
GL11.glGetFloatv(GL11.GL_PROJECTION_MATRIX, PROJECTION_MATRIX)
PROJECTION_MATRIX.put(PROJECTION_MATRIX_PLANE_RATIO_INDEX, PLANE_RATIO)
PROJECTION_MATRIX.put(PROJECTION_MATRIX_FAR_PLANE_INDEX, FAR_PLANE_DISTANCE)
VIEWPORT_MATRIX.put(VIEWPORT_MATRIX_Y_OFFSET_INDEX, scaledHeight)
VIEWPORT_MATRIX.put(VIEWPORT_MATRIX_WIDTH_INDEX, scaledWidth)
VIEWPORT_MATRIX.put(VIEWPORT_MATRIX_HEIGHT_INDEX, -scaledHeight)
}
fun project(x: Double, y: Double, z: Double, offScreen: Boolean): Vec3d? {
gluProject(
(x - cameraX).toFloat(),
(y - cameraY).toFloat(),
(z - cameraZ).toFloat(),
MODEL_MATRIX,
PROJECTION_MATRIX,
VIEWPORT_MATRIX,
WINDOW_POSITION_VECTOR
)
var windowX = WINDOW_POSITION_VECTOR[WINDOW_POSITION_VECTOR_X_INDEX]
var windowY = WINDOW_POSITION_VECTOR[WINDOW_POSITION_VECTOR_Y_INDEX]
val windowZ = WINDOW_POSITION_VECTOR[WINDOW_POSITION_VECTOR_Z_INDEX]
if (windowZ > 0) {
if (!offScreen) {
return null
}
windowX = scaledWidth - windowX
windowY = scaledHeight - windowY
var offsetFromCentreWindowX = windowX - scaledWidth / 2
var offsetFromCentreWindowY = windowY - scaledWidth / 2
if (offsetFromCentreWindowX.absoluteValue < offsetFromCentreWindowY.absoluteValue) {
val newWindowXOffset = scaledWidth * offsetFromCentreWindowX.sign
offsetFromCentreWindowY *= newWindowXOffset / offsetFromCentreWindowX
offsetFromCentreWindowX = newWindowXOffset
} else {
val newWindowYOffset = scaledHeight * offsetFromCentreWindowY.sign
offsetFromCentreWindowX *= newWindowYOffset / offsetFromCentreWindowY
offsetFromCentreWindowY = newWindowYOffset
}
windowX = scaledWidth / 2 + offsetFromCentreWindowX
windowY = scaledHeight / 2 + offsetFromCentreWindowY
}
return Vec3d(windowX.toDouble(), windowY.toDouble(), windowZ.toDouble())
}
}