mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-09 07:20:04 -04:00
disable leaves culling, fix some biome bugs, fix block rotation bug, fix uv lock bugs
This commit is contained in:
parent
7344fdda6c
commit
47b620bedc
@ -16,6 +16,7 @@ package de.bixilon.minosoft.data.registries.blocks
|
||||
import de.bixilon.minosoft.data.registries.blocks.types.*
|
||||
import de.bixilon.minosoft.data.registries.blocks.types.button.StoneButtonBlock
|
||||
import de.bixilon.minosoft.data.registries.blocks.types.button.WoodenButtonBlock
|
||||
import de.bixilon.minosoft.data.registries.blocks.types.leaves.LeavesBlock
|
||||
import de.bixilon.minosoft.data.registries.blocks.types.portal.NetherPortalBlock
|
||||
import de.bixilon.minosoft.data.registries.blocks.types.redstone.ComparatorBlock
|
||||
import de.bixilon.minosoft.data.registries.blocks.types.redstone.RepeaterBlock
|
||||
@ -41,4 +42,5 @@ object DefaultBlockFactories : DefaultClassFactory<BlockFactory<*>>(
|
||||
KelpBlock,
|
||||
StoneButtonBlock,
|
||||
WoodenButtonBlock,
|
||||
LeavesBlock,
|
||||
)
|
||||
|
@ -117,6 +117,8 @@ open class Block(
|
||||
return blockState.outlineShape
|
||||
}
|
||||
|
||||
open fun canCull(blockState: BlockState, other: BlockState): Boolean = true
|
||||
|
||||
companion object : ResourceLocationDeserializer<Block>, BlockFactory<Block> {
|
||||
override fun deserialize(registries: Registries?, resourceLocation: ResourceLocation, data: Map<String, Any>): Block {
|
||||
check(registries != null) { "Registries is null!" }
|
||||
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2021 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.data.registries.blocks.types.leaves
|
||||
|
||||
import de.bixilon.minosoft.data.registries.ResourceLocation
|
||||
import de.bixilon.minosoft.data.registries.blocks.BlockFactory
|
||||
import de.bixilon.minosoft.data.registries.blocks.BlockState
|
||||
import de.bixilon.minosoft.data.registries.blocks.types.Block
|
||||
import de.bixilon.minosoft.data.registries.registries.Registries
|
||||
|
||||
open class LeavesBlock(resourceLocation: ResourceLocation, registries: Registries, data: Map<String, Any>) : Block(resourceLocation, registries, data) {
|
||||
|
||||
override fun canCull(blockState: BlockState, other: BlockState): Boolean {
|
||||
if (other.block is LeavesBlock) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
companion object : BlockFactory<LeavesBlock> {
|
||||
|
||||
override fun build(resourceLocation: ResourceLocation, registries: Registries, data: Map<String, Any>): LeavesBlock {
|
||||
return LeavesBlock(resourceLocation, registries, data)
|
||||
}
|
||||
}
|
||||
}
|
@ -18,13 +18,12 @@ import de.bixilon.minosoft.data.registries.blocks.BlockState
|
||||
import de.bixilon.minosoft.data.world.ChunkSection.Companion.index
|
||||
import de.bixilon.minosoft.data.world.biome.accessor.BiomeAccessor
|
||||
import de.bixilon.minosoft.data.world.biome.source.BiomeSource
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.chunkPosition
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.inChunkSectionPosition
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.inSectionHeight
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.sectionHeight
|
||||
import de.bixilon.minosoft.modding.event.EventInitiators
|
||||
import de.bixilon.minosoft.modding.event.events.ChunkDataChangeEvent
|
||||
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||
import de.bixilon.minosoft.util.KUtil.unsafeCast
|
||||
import glm_.vec2.Vec2i
|
||||
import glm_.vec3.Vec3i
|
||||
@ -59,15 +58,15 @@ class Chunk(
|
||||
operator fun get(sectionHeight: Int): ChunkSection? = sections?.getOrNull(sectionHeight - lowestSection)
|
||||
|
||||
fun get(x: Int, y: Int, z: Int): BlockState? {
|
||||
return this[y.sectionHeight]?.blocks?.get(x, y % ProtocolDefinition.SECTION_HEIGHT_Y, z)
|
||||
return this[y.sectionHeight]?.blocks?.get(x, y.inSectionHeight, z)
|
||||
}
|
||||
|
||||
operator fun get(position: Vec3i): BlockState? = get(position.x, position.y, position.z)
|
||||
|
||||
fun set(x: Int, y: Int, z: Int, blockState: BlockState?, blockEntity: BlockEntity? = null) {
|
||||
val section = getOrPut(y.sectionHeight)
|
||||
section.blocks[x, y % ProtocolDefinition.SECTION_HEIGHT_Y, z] = blockState
|
||||
section.blockEntities[x, y % ProtocolDefinition.SECTION_HEIGHT_Y, z] = blockEntity // ToDo
|
||||
section.blocks[x, y.inSectionHeight, z] = blockState
|
||||
section.blockEntities[x, y.inSectionHeight, z] = blockEntity // ToDo
|
||||
}
|
||||
|
||||
operator fun set(position: Vec3i, blockState: BlockState?) = set(position.x, position.y, position.z, blockState)
|
||||
@ -79,13 +78,13 @@ class Chunk(
|
||||
}
|
||||
|
||||
fun getBlockEntity(x: Int, y: Int, z: Int): BlockEntity? {
|
||||
return this[y.sectionHeight]?.blockEntities?.get(x, y % ProtocolDefinition.SECTION_HEIGHT_Y, z)
|
||||
return this[y.sectionHeight]?.blockEntities?.get(x, y.inSectionHeight, z)
|
||||
}
|
||||
|
||||
fun getBlockEntity(position: Vec3i): BlockEntity? = getBlockEntity(position.x, position.y, position.z)
|
||||
|
||||
fun setBlockEntity(x: Int, y: Int, z: Int, blockEntity: BlockEntity?) {
|
||||
getOrPut(y.sectionHeight).blockEntities[x, y % ProtocolDefinition.SECTION_HEIGHT_Y, z] = blockEntity
|
||||
getOrPut(y.sectionHeight).blockEntities[x, y.inSectionHeight, z] = blockEntity
|
||||
}
|
||||
|
||||
fun setBlockEntity(position: Vec3i, blockEntity: BlockEntity?) = setBlockEntity(position.x, position.y, position.z, blockEntity)
|
||||
@ -206,14 +205,8 @@ class Chunk(
|
||||
|
||||
override fun getBiome(x: Int, y: Int, z: Int): Biome? {
|
||||
if (cacheBiomes) {
|
||||
val sectionHeight = y.sectionHeight
|
||||
val section = this[sectionHeight]
|
||||
if (section == null) {
|
||||
// ToDo: Faster
|
||||
val chunkPosition = Vec3i(x, y, z).chunkPosition
|
||||
return connection.world.cacheBiomeAccessor?.getBiome(x, y, z, chunkPosition.x, chunkPosition.y, this, null)
|
||||
}
|
||||
return section.biomes[x, y % ProtocolDefinition.SECTION_HEIGHT_Y, z]
|
||||
val section = this[y.sectionHeight] ?: return connection.world.cacheBiomeAccessor?.getBiome((chunkPosition.x shl 4) or x, y, (chunkPosition.y shl 4) or z, chunkPosition.x, chunkPosition.y, this, null)
|
||||
return section.biomes[x, y.inSectionHeight, z]
|
||||
}
|
||||
return biomeSource?.getBiome(x and 0x0F, y, z and 0x0F)
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ import de.bixilon.minosoft.data.registries.dimension.DimensionProperties
|
||||
import de.bixilon.minosoft.data.registries.sounds.SoundEvent
|
||||
import de.bixilon.minosoft.data.world.biome.accessor.BiomeAccessor
|
||||
import de.bixilon.minosoft.data.world.biome.accessor.NoiseBiomeAccessor
|
||||
import de.bixilon.minosoft.data.world.biome.accessor.WorldBiomeAccessor
|
||||
import de.bixilon.minosoft.gui.rendering.particle.ParticleRenderer
|
||||
import de.bixilon.minosoft.gui.rendering.particle.types.Particle
|
||||
import de.bixilon.minosoft.gui.rendering.sound.AudioPlayer
|
||||
@ -67,7 +66,6 @@ class World(
|
||||
var difficulty: Difficulties? = null
|
||||
var difficultyLocked = false
|
||||
var hashedSeed = 0L
|
||||
val biomeAccessor: BiomeAccessor = WorldBiomeAccessor(this)
|
||||
var time = 0L
|
||||
var age = 0L
|
||||
var raining = false
|
||||
@ -151,11 +149,11 @@ class World(
|
||||
}
|
||||
|
||||
override fun getBiome(blockPosition: Vec3i): Biome? {
|
||||
return biomeAccessor.getBiome(blockPosition)
|
||||
return this[blockPosition.chunkPosition]?.getBiome(blockPosition.inChunkPosition)
|
||||
}
|
||||
|
||||
override fun getBiome(x: Int, y: Int, z: Int): Biome? {
|
||||
return biomeAccessor.getBiome(x, y, z)
|
||||
return this[Vec2i(x shr 4, z shr 4)]?.getBiome(x and 0x0F, y, z and 0x0F)
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
|
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2021 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.data.world.biome.accessor
|
||||
|
||||
import de.bixilon.minosoft.data.registries.biomes.Biome
|
||||
import de.bixilon.minosoft.data.world.World
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.chunkPosition
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.inChunkPosition
|
||||
import glm_.vec3.Vec3i
|
||||
|
||||
class WorldBiomeAccessor(val world: World) : BiomeAccessor {
|
||||
|
||||
override fun getBiome(x: Int, y: Int, z: Int): Biome? {
|
||||
return getBiome(Vec3i(x, y, z)) // ToDo
|
||||
}
|
||||
|
||||
override fun getBiome(blockPosition: Vec3i): Biome? {
|
||||
return world[blockPosition.chunkPosition]?.getBiome(blockPosition.inChunkPosition)
|
||||
}
|
||||
}
|
@ -62,9 +62,6 @@ class Camera(
|
||||
var fogStart = Minosoft.config.config.game.camera.viewDistance * ProtocolDefinition.SECTION_WIDTH_X.toFloat() // ToDo
|
||||
private var mouseSensitivity = Minosoft.config.config.game.controls.moseSensitivity
|
||||
|
||||
@Deprecated("", ReplaceWith("connection.player"))
|
||||
val entity: LocalPlayerEntity
|
||||
get() = connection.player
|
||||
private var lastMousePosition: Vec2d = Vec2d(0.0, 0.0)
|
||||
private var zoom = 0.0f
|
||||
|
||||
@ -288,7 +285,7 @@ class Camera(
|
||||
}
|
||||
|
||||
private fun setSkyColor() {
|
||||
renderWindow[SkyRenderer.Companion]?.let { skyRenderer ->
|
||||
renderWindow[SkyRenderer]?.let { skyRenderer ->
|
||||
skyRenderer.baseColor = connection.world.getBiome(connection.player.positionInfo.blockPosition)?.skyColor ?: RenderConstants.DEFAULT_SKY_COLOR
|
||||
|
||||
|
||||
|
@ -4,7 +4,7 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureTransparenci
|
||||
|
||||
object CullUtil {
|
||||
|
||||
fun Array<FaceProperties>.canCull(properties: FaceProperties, sameBlock: Boolean): Boolean {
|
||||
fun Array<FaceProperties>.canCull(properties: FaceProperties, blockCull: Boolean): Boolean {
|
||||
val sizeStartX = properties.sizeStart.x
|
||||
val sizeStartY = properties.sizeStart.y
|
||||
val sizeEndX = properties.sizeEnd.x
|
||||
@ -16,7 +16,7 @@ object CullUtil {
|
||||
&& property.sizeEnd.x >= sizeEndX
|
||||
&& property.sizeEnd.y >= sizeEndY
|
||||
&& !((properties.transparency == TextureTransparencies.OPAQUE && property.transparency != TextureTransparencies.OPAQUE)
|
||||
|| (properties.transparency != TextureTransparencies.OPAQUE && property.transparency == properties.transparency && !sameBlock)
|
||||
|| (properties.transparency != TextureTransparencies.OPAQUE && property.transparency == properties.transparency && !blockCull)
|
||||
|| (properties.transparency == TextureTransparencies.TRANSPARENT && property.transparency == TextureTransparencies.TRANSLUCENT))
|
||||
) {
|
||||
return true
|
||||
|
@ -53,7 +53,7 @@ class BakedBlockStateModel(
|
||||
neighbourProperties = neighboursModel.getTouchingFaceProperties(random, direction.inverted)
|
||||
}
|
||||
for (face in faces) {
|
||||
if (face.touching && neighbourProperties != null && neighbourProperties.isNotEmpty() && neighbourProperties.canCull(face, blockState == neighbour)) {
|
||||
if (face.touching && neighbourProperties != null && neighbourProperties.isNotEmpty() && neighbourProperties.canCull(face, neighbour != null && blockState.block.canCull(blockState, neighbour))) {
|
||||
continue
|
||||
}
|
||||
face.singleRender(positionArray, mesh, light, ambientLight)
|
||||
|
@ -22,22 +22,22 @@ import de.bixilon.minosoft.util.KUtil.unsafeCast
|
||||
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.compoundCast
|
||||
|
||||
class MultipartRootModel(
|
||||
private val conditions: MutableMap<MutableSet<Set<Map<BlockProperties, Any>>>, Set<UnbakedBlockStateModel>>,
|
||||
private val conditions: MutableMap<MutableSet<Map<BlockProperties, Set<Any>>>, MutableSet<UnbakedBlockStateModel>>,
|
||||
) : RootModel {
|
||||
|
||||
private fun Set<Map<BlockProperties, Any>>.matches(blockState: BlockState): Boolean {
|
||||
var matches = false
|
||||
private fun Map<BlockProperties, Set<Any>>.matches(blockState: BlockState): Boolean {
|
||||
var matches = true
|
||||
|
||||
for (propertyMap in this) {
|
||||
for ((property, values) in this) {
|
||||
var singleMatches = false
|
||||
for ((property, value) in propertyMap) {
|
||||
for (value in values) {
|
||||
if (blockState.properties[property] == value) {
|
||||
singleMatches = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (singleMatches) {
|
||||
matches = true
|
||||
if (!singleMatches) {
|
||||
matches = false
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -45,13 +45,15 @@ class MultipartRootModel(
|
||||
return matches
|
||||
}
|
||||
|
||||
private fun MutableSet<Set<Map<BlockProperties, Any>>>.matchesAny(blockState: BlockState): Boolean {
|
||||
private fun Set<Map<BlockProperties, Set<Any>>>.matchesAny(blockState: BlockState): Boolean {
|
||||
var matches = true
|
||||
for (or in this) {
|
||||
if (!or.matches(blockState)) {
|
||||
matches = false
|
||||
break
|
||||
continue
|
||||
}
|
||||
matches = true
|
||||
break
|
||||
}
|
||||
return matches
|
||||
}
|
||||
@ -70,26 +72,31 @@ class MultipartRootModel(
|
||||
|
||||
companion object {
|
||||
|
||||
private fun getCondition(data: MutableMap<String, Any>): MutableSet<Map<BlockProperties, Any>> {
|
||||
val singleCondition: MutableSet<Map<BlockProperties, Any>> = mutableSetOf()
|
||||
private fun getCondition(data: MutableMap<String, Any>): MutableMap<BlockProperties, Set<Any>> {
|
||||
val condition: MutableMap<BlockProperties, Set<Any>> = mutableMapOf()
|
||||
for ((propertyName, value) in data) {
|
||||
val properties: MutableMap<BlockProperties, Any> = mutableMapOf()
|
||||
var property: BlockProperties? = null
|
||||
val values: MutableSet<Any> = mutableSetOf()
|
||||
|
||||
for (propertyValue in value.toString().split("|")) {
|
||||
properties += BlockProperties.parseProperty(propertyName, propertyValue)
|
||||
val (parsedProperty, parsedValue) = BlockProperties.parseProperty(propertyName, propertyValue)
|
||||
if (property == null) {
|
||||
property = parsedProperty
|
||||
}
|
||||
values += parsedValue
|
||||
}
|
||||
singleCondition += properties
|
||||
condition[property!!] = values
|
||||
}
|
||||
return singleCondition
|
||||
return condition
|
||||
}
|
||||
|
||||
operator fun invoke(models: Map<ResourceLocation, GenericUnbakedModel>, data: List<Any>): MultipartRootModel {
|
||||
val conditions: MutableMap<MutableSet<Set<Map<BlockProperties, Any>>>, Set<UnbakedBlockStateModel>> = mutableMapOf()
|
||||
val conditions: MutableMap<MutableSet<Map<BlockProperties, Set<Any>>>, MutableSet<UnbakedBlockStateModel>> = mutableMapOf()
|
||||
|
||||
|
||||
for (modelData in data) {
|
||||
check(modelData is Map<*, *>)
|
||||
val condition: MutableSet<Set<Map<BlockProperties, Any>>> = mutableSetOf()
|
||||
val condition: MutableSet<Map<BlockProperties, Set<Any>>> = mutableSetOf()
|
||||
val applyData = modelData["apply"]!!
|
||||
val apply: MutableSet<UnbakedBlockStateModel> = mutableSetOf()
|
||||
if (applyData is Map<*, *>) {
|
||||
@ -113,7 +120,7 @@ class MultipartRootModel(
|
||||
|
||||
|
||||
|
||||
conditions[condition] = apply
|
||||
conditions.getOrPut(condition) { mutableSetOf() } += apply
|
||||
}
|
||||
|
||||
return MultipartRootModel(conditions)
|
||||
|
@ -87,7 +87,6 @@ data class UnbakedBlockStateModel(
|
||||
val touchingFaceProperties: Array<MutableList<FaceProperties>> = Array(Directions.SIZE) { mutableListOf() }
|
||||
|
||||
for (element in model.elements) {
|
||||
val rescale = element.rotation?.rescale ?: false
|
||||
for (face in element.faces) {
|
||||
val texture = resolvedTextures[face.texture.removePrefix("#")]!! // ToDo: Allow direct texture names?
|
||||
val positions = face.direction.getPositions(element.from, element.to)
|
||||
@ -119,13 +118,14 @@ data class UnbakedBlockStateModel(
|
||||
texturePositions = texturePositions.rotateLeft((face.rotation % 360) / 90).toTypedArray()
|
||||
}
|
||||
|
||||
if (this.uvLock && this.rotation != null && face.direction.axis != Axes.Z) {
|
||||
var rad = this.rotation[face.direction.axis].rad
|
||||
if (direction.negative) {
|
||||
rad = -rad
|
||||
if (this.uvLock && this.rotation != null) {
|
||||
val axis = when (face.direction) {
|
||||
Directions.UP, Directions.DOWN -> Axes.Y
|
||||
else -> Axes.X
|
||||
}
|
||||
val rad = this.rotation[axis].rad
|
||||
for ((index, position) in texturePositions.withIndex()) {
|
||||
texturePositions[index] = (Vec3(position.x - 0.5f, 0.0f, position.y - 0.5f).apply { rotateAssign(rad, direction.axis) }).xz + 0.5f
|
||||
texturePositions[index] = (Vec3(position.x - 0.5f, 0.0f, position.y - 0.5f).apply { rotateAssign(rad, axis) }).xz + 0.5f
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,37 +141,24 @@ object VecUtil {
|
||||
return this * cos + (axis cross this) * sin + axis * (axis dot this) * (1 - cos)
|
||||
}
|
||||
|
||||
fun Int.chunkPosition(multiplier: Int): Int {
|
||||
return if (this >= 0) {
|
||||
this / multiplier
|
||||
} else {
|
||||
((this + 1) / multiplier) - 1
|
||||
}
|
||||
}
|
||||
|
||||
val Vec3i.chunkPosition: Vec2i
|
||||
get() = Vec2i(this.x.chunkPosition(ProtocolDefinition.SECTION_WIDTH_X), this.z.chunkPosition(ProtocolDefinition.SECTION_WIDTH_Z))
|
||||
get() = Vec2i(x shr 4, z shr 4)
|
||||
|
||||
val Vec3i.inChunkPosition: Vec3i
|
||||
get() = Vec3i(x and 0x0F, y, this.z and 0x0F)
|
||||
|
||||
val Vec3i.inChunkSectionPosition: Vec3i
|
||||
get() {
|
||||
val inVec2i = inChunkPosition
|
||||
val y = if (y < 0) {
|
||||
((ProtocolDefinition.SECTION_HEIGHT_Y + (y % ProtocolDefinition.SECTION_HEIGHT_Y))) % ProtocolDefinition.SECTION_HEIGHT_Y
|
||||
} else {
|
||||
y % ProtocolDefinition.SECTION_HEIGHT_Y
|
||||
}
|
||||
return Vec3i(inVec2i.x, y, inVec2i.z)
|
||||
get() = Vec3i(x and 0x0F, y.inSectionHeight, z and 0x0F)
|
||||
|
||||
val Int.inSectionHeight: Int
|
||||
get() = if (this < 0) {
|
||||
((ProtocolDefinition.SECTION_HEIGHT_Y + (this % ProtocolDefinition.SECTION_HEIGHT_Y))) % ProtocolDefinition.SECTION_HEIGHT_Y
|
||||
} else {
|
||||
this % ProtocolDefinition.SECTION_HEIGHT_Y
|
||||
}
|
||||
|
||||
val Int.sectionHeight: Int
|
||||
get() = if (this < 0) {
|
||||
(this + 1) / ProtocolDefinition.SECTION_HEIGHT_Y - 1
|
||||
} else {
|
||||
this / ProtocolDefinition.SECTION_HEIGHT_Y
|
||||
}
|
||||
get() = this shr 4
|
||||
|
||||
val Vec3i.sectionHeight: Int
|
||||
get() = y.sectionHeight
|
||||
|
@ -67,8 +67,8 @@ object Vec3Util {
|
||||
this -= 0.5f
|
||||
}
|
||||
|
||||
rotateAssign(-rotation.x, Axes.X)
|
||||
rotateAssign(rotation.y, Axes.Y)
|
||||
rotateAssign(rotation.x, Axes.X)
|
||||
|
||||
if (centerBlock) {
|
||||
this += 0.5f
|
||||
|
Loading…
x
Reference in New Issue
Block a user