A lot of math stuff and text rendering in gmod api style

This commit is contained in:
DBotThePony 2022-01-20 18:37:30 +07:00
parent bb3da639b0
commit 1ce4578336
Signed by: DBot
GPG Key ID: DCC23B5715498507
7 changed files with 644 additions and 59 deletions

View File

@ -9,13 +9,15 @@ import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.blockentity.BeaconRenderer import net.minecraft.client.renderer.blockentity.BeaconRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
import net.minecraft.resources.ResourceLocation import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.world.phys.Vec3 import net.minecraft.world.phys.Vec3
import org.lwjgl.opengl.GL30 import org.lwjgl.opengl.GL30
import ru.dbotthepony.mc.otm.Registry import ru.dbotthepony.mc.otm.Registry
import ru.dbotthepony.mc.otm.block.entity.BlockEntityGravitationStabilizer import ru.dbotthepony.mc.otm.block.entity.BlockEntityGravitationStabilizer
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlockEntityBlackHole import ru.dbotthepony.mc.otm.block.entity.blackhole.BlockEntityBlackHole
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.core.* import ru.dbotthepony.mc.otm.core.*
import kotlin.math.PI
private const val BEAM_WIDTH = 0.2 private const val BEAM_WIDTH = 0.2
@ -82,7 +84,7 @@ private fun quadZ(y: Double): Array<Vector> {
class BlackHoleRenderer(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<BlockEntityBlackHole> { class BlackHoleRenderer(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<BlockEntityBlackHole> {
override fun render( override fun render(
blockEntityBlackHole: BlockEntityBlackHole, tile: BlockEntityBlackHole,
v: Float, v: Float,
poseStack: PoseStack, poseStack: PoseStack,
multiBufferSource: MultiBufferSource, multiBufferSource: MultiBufferSource,
@ -98,8 +100,8 @@ class BlackHoleRenderer(private val context: BlockEntityRendererProvider.Context
RenderSystem.disableCull() RenderSystem.disableCull()
poseStack.pushPose() poseStack.pushPose()
poseStack.translate(0.5, -blockEntityBlackHole.gravitationStrength / 2 + 0.5, 0.5) poseStack.translate(0.5, -tile.gravitationStrength / 2 + 0.5, 0.5)
RenderHelper.colorSphere(poseStack, blockEntityBlackHole.gravitationStrength.toFloat()) RenderHelper.colorSphere(poseStack, tile.gravitationStrength.toFloat())
RenderSystem.enableCull() RenderSystem.enableCull()
RenderSystem.enableTexture() RenderSystem.enableTexture()
@ -137,6 +139,40 @@ class BlackHoleRenderer(private val context: BlockEntityRendererProvider.Context
BufferUploader.end(builder) BufferUploader.end(builder)
RenderSystem.enableCull() RenderSystem.enableCull()
} }
if (
ply != null &&
ply.getCapability(MatteryCapability.ANDROID).isPresent &&
ply.getCapability(MatteryCapability.ANDROID).resolve().get().isAndroid() &&
poseStack.translation().length() < tile.gravitationStrength * 16.0
) {
val facing = tile.blockPos.asVector() - Minecraft.getInstance().gameRenderer.mainCamera.position
val normal = facing.normalize()
val ang = normal.asMutableAngle()
ang.roll = ang.pitch - PI
ang.yaw = ang.yaw - PI * 1.1
ang.pitch = 0.0
poseStack.pushPose()
poseStack.translate(0.5, 0.75, 0.5)
poseStack.translate(ang.forward() * tile.gravitationStrength * 1.1)
poseStack.rotateAroundPoint(poseStack.translation(), ang)
poseStack.scale(0.05f, 0.05f, 0.05f)
val font = Minecraft.getInstance().font
val text1 = TranslatableComponent("otm.3d2d.gravitation_stabilizer.mass", tile.mass.decimalString(2))
val text2 = TranslatableComponent("otm.3d2d.gravitation_stabilizer.strength", "%.2f".format(tile.gravitationStrength))
font.drawAligned(poseStack, text1, TextAlign.TOP_LEFT, 0.8f, 0.8f - font.lineHeight.toFloat() / 2f, 0x0)
font.drawAligned(poseStack, text2, TextAlign.TOP_LEFT, 0.8f, 0.8f + font.lineHeight.toFloat() / 2f, 0x0)
poseStack.translate(0.0, 0.0, -1.0)
font.drawAligned(poseStack, text1, TextAlign.TOP_LEFT, 0f, -font.lineHeight.toFloat() / 2f, 0xFFFFFF)
font.drawAligned(poseStack, text2, TextAlign.TOP_LEFT, 0f, font.lineHeight.toFloat() / 2f, 0xFFFFFF)
poseStack.popPose()
}
} }
override fun shouldRenderOffScreen(p_112306_: BlockEntityBlackHole): Boolean { override fun shouldRenderOffScreen(p_112306_: BlockEntityBlackHole): Boolean {

View File

@ -1,9 +1,119 @@
package ru.dbotthepony.mc.otm.client.render package ru.dbotthepony.mc.otm.client.render
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.blaze3d.vertex.VertexConsumer import com.mojang.blaze3d.vertex.VertexConsumer
import com.mojang.math.Matrix4f import com.mojang.math.Matrix4f
import ru.dbotthepony.mc.otm.core.Vector import com.mojang.math.Vector3f
import net.minecraft.client.gui.Font
import net.minecraft.network.chat.Component
import net.minecraft.util.FormattedCharSequence
import ru.dbotthepony.mc.otm.core.*
fun VertexConsumer.normal(vector: Vector) = normal(vector.x.toFloat(), vector.y.toFloat(), vector.z.toFloat()) fun VertexConsumer.normal(vector: Vector) = normal(vector.x.toFloat(), vector.y.toFloat(), vector.z.toFloat())
fun VertexConsumer.vertex(matrix4f: Matrix4f, vector: Vector) = vertex(matrix4f, vector.x.toFloat(), vector.y.toFloat(), vector.z.toFloat()) fun VertexConsumer.vertex(matrix4f: Matrix4f, vector: Vector) = vertex(matrix4f, vector.x.toFloat(), vector.y.toFloat(), vector.z.toFloat())
fun VertexConsumer.color(color: RGBAColor) = color(color.r, color.g, color.b, color.a) fun VertexConsumer.color(color: RGBAColor) = color(color.r, color.g, color.b, color.a)
fun PoseStack.translate(vector: Vector) = translate(vector.x, vector.y, vector.z)
fun PoseStack.translate(vector: Vector3f) = last().pose().multiplyWithTranslation(vector.x(), vector.y(), vector.z())
fun PoseStack.rotateAroundPoint(point: Vector, axis: Vector, rotation: Float, isDegrees: Boolean = false) {
val last = last()
last.pose().rotateAroundPoint(point, axis, rotation, isDegrees)
last.normal().mul(axis.rotateAroundThis(rotation, isDegrees))
}
fun PoseStack.rotateAroundPoint(point: Vector, rotation: IAngle) {
val last = last()
last.pose().rotateAroundPoint(point, rotation)
// last.normal().mul(rotation.forward().rotateAroundThis(rotation))
}
fun PoseStack.rotateAroundPoint(point: Vector3f, axis: Vector, rotation: Float, isDegrees: Boolean = false) {
val last = last()
last.pose().rotateAroundPoint(point, axis, rotation, isDegrees)
last.normal().mul(axis.rotateAroundThis(rotation, isDegrees))
}
fun PoseStack.rotateAroundPoint(point: Vector3f, rotation: IAngle) {
val last = last()
last.pose().rotateAroundPoint(point, rotation)
// last.normal().mul(rotation.forward().rotateAroundThis(rotation))
}
fun PoseStack.translation(): Vector3f {
return last().pose().getTranslation()
}
enum class TextAlign {
TOP_LEFT,
TOP_CENTER,
TOP_RIGHT,
CENTER_LEFT,
CENTER_CENTER,
CENTER_RIGHT,
BOTTOM_LEFT,
BOTTOM_CENTER,
BOTTOM_RIGHT,
}
fun Font.drawAligned(poseStack: PoseStack, text: String, align: TextAlign, x: Float, y: Float, color: Int): Int {
return when (align) {
TextAlign.TOP_LEFT -> draw(poseStack, text, x, y, color)
TextAlign.TOP_CENTER -> draw(poseStack, text, x - width(text) / 2f, y, color)
TextAlign.TOP_RIGHT -> draw(poseStack, text, x - width(text), y, color)
TextAlign.CENTER_LEFT -> draw(poseStack, text, x, y - lineHeight / 2f, color)
TextAlign.CENTER_CENTER -> draw(poseStack, text, x - width(text) / 2f, y - lineHeight / 2f, color)
TextAlign.CENTER_RIGHT -> draw(poseStack, text, x - width(text), y - lineHeight / 2f, color)
TextAlign.BOTTOM_LEFT -> draw(poseStack, text, x, y - lineHeight, color)
TextAlign.BOTTOM_CENTER -> draw(poseStack, text, x - width(text) / 2f, y - lineHeight, color)
TextAlign.BOTTOM_RIGHT -> draw(poseStack, text, x - width(text), y - lineHeight, color)
}
}
fun Font.drawAligned(poseStack: PoseStack, text: Component, align: TextAlign, x: Float, y: Float, color: Int): Int {
return when (align) {
TextAlign.TOP_LEFT -> draw(poseStack, text, x, y, color)
TextAlign.TOP_CENTER -> draw(poseStack, text, x - width(text) / 2f, y, color)
TextAlign.TOP_RIGHT -> draw(poseStack, text, x - width(text), y, color)
TextAlign.CENTER_LEFT -> draw(poseStack, text, x, y - lineHeight / 2f, color)
TextAlign.CENTER_CENTER -> draw(poseStack, text, x - width(text) / 2f, y - lineHeight / 2f, color)
TextAlign.CENTER_RIGHT -> draw(poseStack, text, x - width(text), y - lineHeight / 2f, color)
TextAlign.BOTTOM_LEFT -> draw(poseStack, text, x, y - lineHeight, color)
TextAlign.BOTTOM_CENTER -> draw(poseStack, text, x - width(text) / 2f, y - lineHeight, color)
TextAlign.BOTTOM_RIGHT -> draw(poseStack, text, x - width(text), y - lineHeight, color)
}
}
fun Font.drawAligned(poseStack: PoseStack, text: FormattedCharSequence, align: TextAlign, x: Float, y: Float, color: Int): Int {
return when (align) {
TextAlign.TOP_LEFT -> draw(poseStack, text, x, y, color)
TextAlign.TOP_CENTER -> draw(poseStack, text, x - width(text) / 2f, y, color)
TextAlign.TOP_RIGHT -> draw(poseStack, text, x - width(text), y, color)
TextAlign.CENTER_LEFT -> draw(poseStack, text, x, y - lineHeight / 2f, color)
TextAlign.CENTER_CENTER -> draw(poseStack, text, x - width(text) / 2f, y - lineHeight / 2f, color)
TextAlign.CENTER_RIGHT -> draw(poseStack, text, x - width(text), y - lineHeight / 2f, color)
TextAlign.BOTTOM_LEFT -> draw(poseStack, text, x, y - lineHeight, color)
TextAlign.BOTTOM_CENTER -> draw(poseStack, text, x - width(text) / 2f, y - lineHeight, color)
TextAlign.BOTTOM_RIGHT -> draw(poseStack, text, x - width(text), y - lineHeight, color)
}
}
fun Font.drawAligned(poseStack: PoseStack, text: String, align: TextAlign, x: Float, y: Float, color: RGBAColor): Int {
return drawAligned(poseStack, text, align, x, y, color.toInt())
}
fun Font.drawAligned(poseStack: PoseStack, text: Component, align: TextAlign, x: Float, y: Float, color: RGBAColor): Int {
return drawAligned(poseStack, text, align, x, y, color.toInt())
}
fun Font.drawAligned(poseStack: PoseStack, text: FormattedCharSequence, align: TextAlign, x: Float, y: Float, color: RGBAColor): Int {
return drawAligned(poseStack, text, align, x, y, color.toInt())
}

View File

@ -2,6 +2,9 @@ package ru.dbotthepony.mc.otm.client.render
import com.mojang.blaze3d.vertex.* import com.mojang.blaze3d.vertex.*
import com.mojang.math.Matrix4f import com.mojang.math.Matrix4f
import com.mojang.math.Vector3f
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.Font
import net.minecraft.client.renderer.MultiBufferSource import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderType import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.blockentity.BeaconRenderer import net.minecraft.client.renderer.blockentity.BeaconRenderer
@ -9,6 +12,8 @@ import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
import net.minecraft.client.renderer.texture.OverlayTexture import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.network.chat.TextComponent
import net.minecraft.network.chat.TranslatableComponent
import ru.dbotthepony.mc.otm.block.BlockBlackHole import ru.dbotthepony.mc.otm.block.BlockBlackHole
import ru.dbotthepony.mc.otm.block.BlockMatteryRotatable import ru.dbotthepony.mc.otm.block.BlockMatteryRotatable
import ru.dbotthepony.mc.otm.block.entity.BlockEntityGravitationStabilizer import ru.dbotthepony.mc.otm.block.entity.BlockEntityGravitationStabilizer
@ -35,13 +40,16 @@ class GravitationStabilizerRenderer(private val context: BlockEntityRendererProv
var len = 64.0 var len = 64.0
val normal = tile.blockState.getValue(BlockMatteryRotatable.FACING_FULL).normal val normal = tile.blockState.getValue(BlockMatteryRotatable.FACING_FULL).normal
val level = tile.level val level = tile.level
var bhTile: BlockEntityBlackHole? = null
if (level != null) { if (level != null) {
for (i in 1 .. BlockEntityGravitationStabilizer.RANGE) { for (i in 1 .. BlockEntityGravitationStabilizer.RANGE) {
val pos = tile.blockPos + normal * i val pos = tile.blockPos + normal * i
val getTile = level.getBlockEntity(pos) as? BlockEntityBlackHole
if (level.getBlockEntity(tile.blockPos + pos) is BlockEntityBlackHole || level.getBlockState(pos).block is BlockBlackHole) { if (getTile != null || level.getBlockState(pos).block is BlockBlackHole) {
len = i.toDouble() len = i.toDouble()
bhTile = getTile
break break
} }
} }
@ -51,12 +59,13 @@ class GravitationStabilizerRenderer(private val context: BlockEntityRendererProv
poseStack.translate(0.5, 0.5, 0.5) poseStack.translate(0.5, 0.5, 0.5)
val matrix = poseStack.last().pose() val matrix = poseStack.last().pose()
val facing: Direction = tile.blockState.getValue(BlockMatteryRotatable.FACING_FULL)
run { run {
val rotation = (System.currentTimeMillis().toDouble() / 1000.0) % (PI * 2) val rotation = (System.currentTimeMillis().toDouble() / 1000.0) % (PI * 2)
val consumer = p_112310_.getBuffer(BEAM_RENDER_TYPE_INNER) val consumer = p_112310_.getBuffer(BEAM_RENDER_TYPE_INNER)
when (tile.blockState.getValue(BlockMatteryRotatable.FACING_FULL)) { when (facing) {
Direction.DOWN -> ray(matrix, consumer, len, rotation, -PI / 2, 0.0, RAY_RADIUS_INNER) Direction.DOWN -> ray(matrix, consumer, len, rotation, -PI / 2, 0.0, RAY_RADIUS_INNER)
Direction.UP -> ray(matrix, consumer, len, rotation, PI / 2, 0.0, RAY_RADIUS_INNER) Direction.UP -> ray(matrix, consumer, len, rotation, PI / 2, 0.0, RAY_RADIUS_INNER)
Direction.NORTH -> ray(matrix, consumer, len, rotation, 0.0, PI / 2, RAY_RADIUS_INNER) Direction.NORTH -> ray(matrix, consumer, len, rotation, 0.0, PI / 2, RAY_RADIUS_INNER)
@ -70,7 +79,7 @@ class GravitationStabilizerRenderer(private val context: BlockEntityRendererProv
val consumer = p_112310_.getBuffer(BEAM_RENDER_TYPE_OUTER) val consumer = p_112310_.getBuffer(BEAM_RENDER_TYPE_OUTER)
val rotation = (-System.currentTimeMillis().toDouble() / 700.0) % (PI * 2) val rotation = (-System.currentTimeMillis().toDouble() / 700.0) % (PI * 2)
when (tile.blockState.getValue(BlockMatteryRotatable.FACING_FULL)) { when (facing) {
Direction.DOWN -> ray(matrix, consumer, len, rotation, -PI / 2, 0.0, RAY_RADIUS_OUTER, OUTER_COLOR) Direction.DOWN -> ray(matrix, consumer, len, rotation, -PI / 2, 0.0, RAY_RADIUS_OUTER, OUTER_COLOR)
Direction.UP -> ray(matrix, consumer, len, rotation, PI / 2, 0.0, RAY_RADIUS_OUTER, OUTER_COLOR) Direction.UP -> ray(matrix, consumer, len, rotation, PI / 2, 0.0, RAY_RADIUS_OUTER, OUTER_COLOR)
Direction.NORTH -> ray(matrix, consumer, len, rotation, 0.0, PI / 2, RAY_RADIUS_OUTER, OUTER_COLOR) Direction.NORTH -> ray(matrix, consumer, len, rotation, 0.0, PI / 2, RAY_RADIUS_OUTER, OUTER_COLOR)
@ -81,6 +90,24 @@ class GravitationStabilizerRenderer(private val context: BlockEntityRendererProv
} }
poseStack.popPose() poseStack.popPose()
if (bhTile != null) {
poseStack.pushPose()
poseStack.translate(0.5, 0.5, 0.5)
val translation = poseStack.translation()
poseStack.translate(facing.opposite.normal * 0.6)
poseStack.rotateAroundPoint(translation, facing.opposite.asAngle().copy(pitch = PI))
poseStack.scale(0.01f, 0.01f, 0.01f)
val font = Minecraft.getInstance().font
font.drawAligned(poseStack, TranslatableComponent("otm.3d2d.gravitation_stabilizer.mass", bhTile.mass.decimalString(2)), TextAlign.TOP_CENTER, 0f, -font.lineHeight.toFloat() / 2f, 0xFFFFFF)
font.drawAligned(poseStack, TranslatableComponent("otm.3d2d.gravitation_stabilizer.strength", "%.2f".format(bhTile.gravitationStrength)), TextAlign.TOP_CENTER, 0f, font.lineHeight.toFloat() / 2f, 0xFFFFFF)
poseStack.popPose()
}
} }
override fun shouldRenderOffScreen(p_112306_: BlockEntityGravitationStabilizer) = true override fun shouldRenderOffScreen(p_112306_: BlockEntityGravitationStabilizer) = true

View File

@ -2,30 +2,68 @@ package ru.dbotthepony.mc.otm.core
import com.mojang.math.Quaternion import com.mojang.math.Quaternion
import com.mojang.math.Vector3f import com.mojang.math.Vector3f
import net.minecraft.core.Direction
import net.minecraft.world.phys.Vec3 import net.minecraft.world.phys.Vec3
import kotlin.math.acos import kotlin.math.*
import kotlin.math.cos
import kotlin.math.sin
typealias Vector = Vec3 typealias Vector = Vec3
val VECTOR_UP = Vector(0.0, 1.0, 0.0) @JvmField val VECTOR_UP = Vector(0.0, 1.0, 0.0)
val VECTOR_FORWARD = Vector(1.0, 0.0, 0.0) @JvmField val VECTOR_FORWARD = Vector(1.0, 0.0, 0.0)
val VECTOR_BACKWARD = Vector(1.0, 0.0, 0.0) @JvmField val VECTOR_BACKWARD = Vector(-1.0, 0.0, 0.0)
val VECTOR_DOWN = Vector(0.0, -1.0, 0.0) @JvmField val VECTOR_DOWN = Vector(0.0, -1.0, 0.0)
val VECTOR_RIGHT = Vector(0.0, 0.0, 1.0) @JvmField val VECTOR_RIGHT = Vector(0.0, 0.0, 1.0)
val VECTOR_LEFT = Vector(0.0, 0.0, -1.0) @JvmField val VECTOR_LEFT = Vector(0.0, 0.0, -1.0)
fun Vector.asAngle(): Angle { fun Vector.asAngle(): Angle {
val norm = normalize() val norm = normalize()
return Angle(asin(norm.y), atan2(norm.x, norm.z))
if (norm.x < 0) {
return Angle(acos(norm.dot(VECTOR_UP)) - Angle.PI_HALF, -acos(Vector(norm.x, 0.0, norm.z).normalize().dot(VECTOR_RIGHT)))
}
return Angle(acos(norm.dot(VECTOR_UP)) - Angle.PI_HALF, acos(Vector(norm.x, 0.0, norm.z).normalize().dot(VECTOR_RIGHT)))
} }
fun Vector.asMutableAngle(): MutableAngle {
val norm = normalize()
return MutableAngle(asin(norm.y), atan2(norm.x, norm.z))
}
fun Vector3f.asAngle(): Angle {
val len = length()
val norm = Vector(x() / len, y() / len, z() / len)
return Angle(asin(norm.y), atan2(norm.x, norm.z))
}
fun Vector3f.asMutableAngle(): MutableAngle {
val len = length()
val norm = Vector(x() / len, y() / len, z() / len)
return MutableAngle(asin(norm.y), atan2(norm.x, norm.z))
}
operator fun Vector3f.unaryMinus(): Vector3f {
setX(-x())
setY(-y())
setZ(-z())
return this
}
fun Vector3f.length() = sqrt(x().toDouble() * x() + y().toDouble() * y() + z().toDouble() * z())
operator fun Vector3f.times(v: Float): Vector3f {
setX(x() * v)
setY(y() * v)
setZ(z() * v)
return this
}
operator fun Vector3f.div(v: Float): Vector3f {
setX(x() / v)
setY(y() / v)
setZ(z() / v)
return this
}
operator fun Vector3f.component1() = x()
operator fun Vector3f.component2() = y()
operator fun Vector3f.component3() = z()
operator fun Vector.plus(direction: Vector): Vector = this.add(direction) operator fun Vector.plus(direction: Vector): Vector = this.add(direction)
operator fun Vector.minus(direction: Vector): Vector = this.subtract(direction) operator fun Vector.minus(direction: Vector): Vector = this.subtract(direction)
operator fun Vector.unaryMinus(): Vector = Vector(-x, -y, -z) operator fun Vector.unaryMinus(): Vector = Vector(-x, -y, -z)
@ -44,19 +82,30 @@ fun Vector.up() = asAngle().up() * this.length()
fun Vector.down() = asAngle().down() * this.length() fun Vector.down() = asAngle().down() * this.length()
fun Vector.rotateAroundAxis(axis: Vector, rotation: Double): Vector { fun Vector.rotateAroundAxis(axis: Vector, rotation: Double): Vector {
return this * cos(rotation) + axis.cross(this) * sin(rotation) + axis * axis.dot(this) * (1 - cos(rotation)) val cross = axis.cross(this)
val dot = axis.dot(this)
val cos = cos(rotation)
val sin = sin(rotation)
//return this * cos(rotation) + axis.cross(this) * sin(rotation) + axis * axis.dot(this) * (1 - cos(rotation))
return Vector(
this.x * cos + cross.x * sin + axis.x * dot * (1.0 - cos),
this.y * cos + cross.y * sin + axis.y * dot * (1.0 - cos),
this.z * cos + cross.z * sin + axis.z * dot * (1.0 - cos),
)
} }
fun Vector.rotate(angle: Angle): Vector { fun Vector.rotate(angle: IAngle): Vector {
val rotatedYaw = this.rotateAroundAxis(VECTOR_UP, angle.yaw) val asMatrix = asMatrix()
return rotatedYaw.rotateAroundAxis(rotatedYaw.asAngle().left(), angle.pitch) angle.rotationXYZ().multiply(asMatrix, inverse = true)
return Vector(asMatrix[0, 0], asMatrix[1, 0], asMatrix[2, 0])
} }
fun Vector.asVector3f(): Vector3f { fun Vector.asVector3f(): Vector3f {
return Vector3f(x.toFloat(), y.toFloat(), z.toFloat()) return Vector3f(x.toFloat(), y.toFloat(), z.toFloat())
} }
fun Quaternion(vec: Vector, rotation: Float, isDegrees: Boolean): Quaternion { fun Quaternion(vec: Vector, rotation: Float, isDegrees: Boolean = false): Quaternion {
return Quaternion(vec.asVector3f(), rotation, isDegrees) return Quaternion(vec.asVector3f(), rotation, isDegrees)
} }
@ -77,55 +126,138 @@ fun Vector.rotate(q: Quaternion): Vector {
return Vector(quaternion.i().toDouble(), quaternion.j().toDouble(), quaternion.k().toDouble()) return Vector(quaternion.i().toDouble(), quaternion.j().toDouble(), quaternion.k().toDouble())
} }
data class Angle(val pitch: Double = 0.0, val yaw: Double = 0.0, val roll: Double = 0.0) { fun Vector.rotateAroundThis(rotation: Float, isDegrees: Boolean = false): Quaternion {
fun forward(): Vector { return Quaternion(this, rotation, isDegrees)
val yaw = yaw }
val x = cos(yaw) * cos(pitch)
val y = -sin(pitch)
val z = sin(yaw) * cos(pitch)
return Vector(x, y, z) fun Vector.asMatrix(): MutableMatrix {
return MutableMatrix(3, 1).also {
it[0, 0] = x
it[1, 0] = y
it[2, 0] = z
}
}
fun Vector.asMatrix4(): MutableMatrix {
return MutableMatrix(4, 1).also {
it[0, 0] = x
it[1, 0] = y
it[2, 0] = z
}
}
fun Direction.asAngle(): Angle {
return when (this) {
Direction.DOWN -> Angle.DOWN
Direction.UP -> Angle.UP
Direction.NORTH -> Angle.NORTH
Direction.SOUTH -> Angle.SOUTH
Direction.WEST -> Angle.WEST
Direction.EAST -> Angle.EAST
}
}
interface IAngle {
val pitch: Double
val yaw: Double
val roll: Double
fun forward() = VECTOR_FORWARD.rotate(this)
fun left() = VECTOR_LEFT.rotate(this)
fun right() = VECTOR_RIGHT.rotate(this)
fun up() = VECTOR_UP.rotate(this)
fun down() = VECTOR_DOWN.rotate(this)
fun rotationX(): MutableMatrix {
if (roll == 0.0) {
return MutableMatrix(3, 3).also { it.identityFast() }
}
return MutableMatrix(3, 3).also {
val s = sin(roll)
val c = cos(roll)
it[0, 0] = 1.0
it[1, 1] = c
it[1, 2] = -s
it[2, 1] = s
it[2, 2] = c
}
} }
fun left(): Vector { fun rotationZ(): MutableMatrix {
return -right() if (pitch == 0.0) {
return MutableMatrix(3, 3).also { it.identityFast() }
}
return MutableMatrix(3, 3).also {
val s = sin(pitch)
val c = cos(pitch)
it[0, 0] = c
it[0, 1] = -s
it[1, 0] = s
it[1, 1] = c
it[2, 2] = 1.0
}
} }
fun right(): Vector { fun rotationY(): MutableMatrix {
val yaw = yaw + PI_HALF if (yaw == 0.0) {
val pitch = 0.0 return MutableMatrix(3, 3).also { it.identityFast() }
val x = cos(yaw) * cos(pitch) }
val y = -sin(pitch)
val z = sin(yaw) * cos(pitch)
return Vector(x, y, z) return MutableMatrix(3, 3).also {
val s = sin(yaw)
val c = cos(yaw)
it[0, 0] = c
it[0, 2] = s
it[2, 0] = -s
it[2, 2] = c
it[1, 1] = 1.0
}
} }
fun up(): Vector { fun rotationXYZ(): MutableMatrix {
val yaw = yaw - PI_HALF if (pitch == 0.0 && yaw == 0.0 && roll == 0.0)
val pitch = pitch - PI_HALF return MutableMatrix(3, 3).also { it.identity() }
val x = cos(yaw) * cos(pitch)
val y = -sin(pitch)
val z = sin(yaw) * cos(pitch)
return Vector(x, y, z) return rotationY().multiply(rotationZ()).multiply(rotationX())
} }
fun down(): Vector { fun rotationXYZW(): MutableMatrix {
val yaw = yaw - PI_HALF if (pitch == 0.0 && yaw == 0.0 && roll == 0.0)
val pitch = pitch + PI_HALF return MutableMatrix(4, 4).also { it.identity() }
val x = cos(yaw) * cos(pitch)
val y = -sin(pitch)
val z = sin(yaw) * cos(pitch)
return Vector(x, y, z) return rotationXYZ().resize(4, 4).also { it[3, 3] = 1.0 }
} }
}
data class Angle(override val pitch: Double = 0.0, override val yaw: Double = 0.0, override val roll: Double = 0.0) : IAngle {
companion object { companion object {
const val PI_HALF = Math.PI / 2.0 const val PI_HALF = Math.PI / 2.0
fun degrees(pitch: Double, yaw: Double, roll: Double): Angle { fun deg(pitch: Double, yaw: Double, roll: Double): Angle {
return Angle(Math.toDegrees(pitch), Math.toDegrees(yaw), Math.toDegrees(roll)) return Angle(Math.toDegrees(pitch), Math.toDegrees(yaw), Math.toDegrees(roll))
} }
@JvmField val ZERO = Angle()
@JvmField val UP = Angle(pitch = PI / 2)
@JvmField val DOWN = Angle(pitch = -PI / 2)
@JvmField val NORTH = Angle()
@JvmField val SOUTH = Angle(yaw = PI)
@JvmField val WEST = Angle(yaw = PI / 2)
@JvmField val EAST = Angle(yaw = -PI / 2)
}
}
data class MutableAngle(override var pitch: Double = 0.0, override var yaw: Double = 0.0, override var roll: Double = 0.0) : IAngle {
companion object {
fun deg(pitch: Double, yaw: Double, roll: Double): MutableAngle {
return MutableAngle(Math.toDegrees(pitch), Math.toDegrees(yaw), Math.toDegrees(roll))
}
} }
} }

View File

@ -15,6 +15,7 @@ operator fun Vec3i.minus(direction: Vec3i): Vec3i = this.subtract(direction)
operator fun Vec3i.minus(direction: Direction): Vec3i = this.subtract(direction.normal) operator fun Vec3i.minus(direction: Direction): Vec3i = this.subtract(direction.normal)
operator fun Vec3i.times(int: Int): Vec3i = this.multiply(int) operator fun Vec3i.times(int: Int): Vec3i = this.multiply(int)
operator fun Vec3i.times(double: Double): Vector = Vector(x * double, y * double, z * double)
fun BlockPos.asVector(): Vector { fun BlockPos.asVector(): Vector {
return Vector(x + 0.5, y + 0.5, z + 0.5) return Vector(x + 0.5, y + 0.5, z + 0.5)

View File

@ -0,0 +1,276 @@
package ru.dbotthepony.mc.otm.core
import com.mojang.math.Matrix3f
import com.mojang.math.Matrix4f
import com.mojang.math.Vector3f
import com.mojang.math.Vector4f
import org.jetbrains.annotations.Contract
import java.nio.FloatBuffer
fun Matrix3f.asMutableMatrix(): MutableMatrix = MutableMatrix(this)
fun Matrix4f.asMutableMatrix(): MutableMatrix = MutableMatrix(this)
fun Matrix4f.rotate(angle: IAngle): Matrix4f {
multiply(angle.rotationXYZW().toMatrix4f())
return this
}
fun Matrix4f.translate(vector: Vector) {
translate(-vector.asVector3f())
}
fun Matrix4f.rotateAroundPoint(point: Vector, axis: Vector, rotation: Float, isDegrees: Boolean = false) {
val p = point.asVector3f()
translate(-p)
multiply(axis.rotateAroundThis(rotation, isDegrees))
translate(-p)
}
fun Matrix4f.rotateAroundPoint(point: Vector, axis: Vector3f, rotation: Float, isDegrees: Boolean = false) {
val p = point.asVector3f()
translate(-p)
multiply(if (isDegrees) axis.rotationDegrees(rotation) else axis.rotation(rotation))
translate(-p)
}
fun Matrix4f.rotateAroundPoint(point: Vector, rotation: IAngle) {
val p = point.asVector3f()
translate(-p)
multiply(rotation.rotationXYZW().toMatrix4f())
translate(-p)
}
fun Matrix4f.rotateAroundPoint(point: Vector3f, axis: Vector, rotation: Float, isDegrees: Boolean = false) {
translate(-point)
multiply(axis.rotateAroundThis(rotation, isDegrees))
translate(-point)
}
fun Matrix4f.rotateAroundPoint(point: Vector3f, rotation: IAngle) {
translate(-point)
multiply(rotation.rotationXYZW().toMatrix4f())
translate(-point)
}
fun Matrix4f.rotateAroundPoint(point: Vector3f, axis: Vector3f, rotation: Float, isDegrees: Boolean = false) {
translate(-point)
multiply(if (isDegrees) axis.rotationDegrees(rotation) else axis.rotation(rotation))
translate(-point)
}
private val floatBuff3 = FloatBuffer.allocate(3 * 3)
private val floatBuff4 = FloatBuffer.allocate(4 * 4)
fun Matrix4f.getTranslation(): Vector3f {
store(floatBuff4)
return Vector3f(floatBuff4[0 + 3 * 4], floatBuff4[1 + 3 * 4], floatBuff4[2 + 3 * 4])
}
fun Matrix4f.getTranslation4(): Vector4f {
store(floatBuff4)
return Vector4f(floatBuff4[0 + 3 * 4], floatBuff4[1 + 3 * 4], floatBuff4[2 + 3 * 4], floatBuff4[3 + 3 * 4])
}
class MutableMatrix(val rows: Int, val columns: Int = 1) {
private val buffer = Array(columns * rows) {0.0}
constructor(matrix3f: Matrix3f) : this(3, 3) {
matrix3f.store(floatBuff3)
load(floatBuff3)
}
constructor(matrix4f: Matrix4f) : this(4, 4) {
matrix4f.store(floatBuff4)
load(floatBuff4)
}
constructor(other: MutableMatrix) : this(other.rows, other.columns) {
for (i in 0 .. buffer.size) {
buffer[i] = other.buffer[i]
}
}
fun identity(): MutableMatrix {
check(columns == rows) { "Matrix is not cubic ($rows by $columns)" }
for (column in 0 until columns) {
for (row in 0 until rows) {
buffer[row + column * rows] = if (column == row) 1.0 else 0.0
}
}
return this
}
fun identityFast(): MutableMatrix {
check(columns == rows) { "Matrix is not cubic ($rows by $columns)" }
for (i in 0 until rows) {
buffer[i + i * columns] = 1.0
}
return this
}
fun toMatrix3f(): Matrix3f {
check(rows == columns && rows == 3) { "This matrix has wrong dimensions: $rows by $columns" }
store(floatBuff3)
return Matrix3f().also { it.load(floatBuff3) }
}
fun toMatrix4f(): Matrix4f {
check(rows == columns && rows == 4) { "This matrix has wrong dimensions: $rows by $columns" }
store(floatBuff4)
return Matrix4f().also { it.load(floatBuff4) }
}
fun store(buff: FloatBuffer) {
for (row in 0 until rows) {
for (column in 0 until columns) {
buff.put(row + column * columns, buffer[row + column * columns].toFloat())
}
}
}
fun store(matrix: Matrix4f) {
check(rows == columns && rows == 4) { "This matrix has wrong dimensions: $rows by $columns" }
store(floatBuff4)
matrix.load(floatBuff4)
}
fun store(matrix: Matrix3f) {
check(rows == columns && rows == 3) { "This matrix has wrong dimensions: $rows by $columns" }
store(floatBuff3)
matrix.load(floatBuff3)
}
fun load(buff: FloatBuffer) {
for (row in 0 until rows) {
for (column in 0 until columns) {
buffer[row + column * columns] = buff[row + column * columns].toDouble()
}
}
}
fun load(matrix: Matrix4f) {
check(rows == columns && rows == 4) { "This matrix has wrong dimensions: $rows by $columns" }
matrix.store(floatBuff4)
load(floatBuff4)
}
fun load(matrix: Matrix3f) {
check(rows == columns && rows == 3) { "This matrix has wrong dimensions: $rows by $columns" }
matrix.store(floatBuff3)
load(floatBuff3)
}
fun resize(rows: Int, columns: Int): MutableMatrix {
return MutableMatrix(rows, columns).also {
for (row in 0 until this.rows.coerceAtMost(rows)) {
for (column in 0 until this.columns.coerceAtMost(columns)) {
it.buffer[row + column * columns] = buffer[row + column * this.columns]
}
}
}
}
@Contract(pure = true)
operator fun get(row: Int, column: Int): Double {
require(row >= 0) { "Row out of bounds: $row" }
require(row < rows) { "Row out of bounds: $row" }
require(column >= 0) { "Column out of bounds: $row" }
require(column < columns) { "Column out of bounds: $row" }
return buffer[row + column * rows]
}
operator fun set(row: Int, column: Int, value: Double) {
require(row >= 0) { "Row out of bounds: $row" }
require(row < rows) { "Row out of bounds: $row" }
require(column >= 0) { "Column out of bounds: $row" }
require(column < columns) { "Column out of bounds: $row" }
buffer[row + column * rows] = value
}
/**
* this = this * other
* при inverse = true
* other = this * other
*
* без создания новой матрицы
*/
fun multiply(other: MutableMatrix, inverse: Boolean = false): MutableMatrix {
check(columns == other.rows) { "Matrixes are not compatible for multiplication (this: $rows by $columns, other: ${other.rows} by ${other.columns})" }
val resultSize = rows * other.columns
check(!inverse && buffer.size == resultSize || inverse && other.buffer.size == resultSize) { "Matrixes are not compatible for ${if (inverse) "direct" else "inverse"} multiplication in place (this: $rows by $columns, other: ${other.rows} by ${other.columns})" }
val buff = Array(resultSize) { 0.0 }
for (row in 0 until rows) {
for (column in 0 until other.columns) {
var sum = 0.0
for (elem in 0 until columns) {
sum += this[row, elem] * other[elem, column]
}
buff[row + column * columns] = sum
}
}
if (inverse) {
for (i in buff.indices) {
other.buffer[i] = buff[i]
}
} else {
for (i in buff.indices) {
buffer[i] = buff[i]
}
}
return if (inverse) other else this
}
/**
* new = this * other
*
* с созданием новой матрицы
*/
operator fun times(other: MutableMatrix): MutableMatrix {
check(columns == other.rows) { "Matrixes are not compatible for multiplication (this: $rows by $columns, other: ${other.rows} by ${other.columns})" }
val new = MutableMatrix(rows, other.columns)
for (row in 0 until rows) {
for (column in 0 until other.columns) {
var sum = 0.0
for (elem in 0 until columns) {
sum += this[row, elem] * other[elem, column]
}
new[row, column] = sum
}
}
return new
}
var translation: Vector
get() {
check(columns == rows && rows == 4) { "Matrix is expected to be 4x4 (this one is $rows by $columns)" }
return Vector(this[0, 3], this[1, 3], this[2, 3])
}
set(value) {
check(columns == rows && rows == 4) { "Matrix is expected to be 4x4 (this one is $rows by $columns)" }
this[0, 0] = 1.0
this[1, 1] = 1.0
this[2, 2] = 1.0
this[3, 3] = 1.0
this[0, 3] = value.x
this[1, 3] = value.y
this[2, 3] = value.z
}
}

View File

@ -41,6 +41,9 @@
"otm.gui.redstone.low.description": "Machine work if no redstone signal is present", "otm.gui.redstone.low.description": "Machine work if no redstone signal is present",
"otm.gui.redstone.high.description": "Machine work only if any redstone signal is present", "otm.gui.redstone.high.description": "Machine work only if any redstone signal is present",
"otm.3d2d.gravitation_stabilizer.mass": "Singularity mass: %s",
"otm.3d2d.gravitation_stabilizer.strength": "Gravitation strength: %s",
"otm.death_reason": "Decommissioned!", "otm.death_reason": "Decommissioned!",
"otm.item.power.infinite.storage": "Stored energy: Infinity / Infinity", "otm.item.power.infinite.storage": "Stored energy: Infinity / Infinity",