mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-17 03:15:35 -04:00
rendering: add support for conditional models
This commit is contained in:
parent
467b8c2415
commit
74e5482096
@ -15,7 +15,8 @@ package de.bixilon.minosoft.data.mappings.blocks
|
|||||||
import de.bixilon.minosoft.Minosoft
|
import de.bixilon.minosoft.Minosoft
|
||||||
import de.bixilon.minosoft.data.mappings.ModIdentifier
|
import de.bixilon.minosoft.data.mappings.ModIdentifier
|
||||||
import de.bixilon.minosoft.data.world.BlockPosition
|
import de.bixilon.minosoft.data.world.BlockPosition
|
||||||
import de.bixilon.minosoft.gui.rendering.chunk.models.BlockModel
|
import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModel
|
||||||
|
import de.bixilon.minosoft.gui.rendering.chunk.models.renderable.BlockRenderer
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@ -23,6 +24,7 @@ data class Block(val identifier: ModIdentifier) {
|
|||||||
var rotation: BlockRotations = BlockRotations.NONE
|
var rotation: BlockRotations = BlockRotations.NONE
|
||||||
var properties: Set<BlockProperties> = setOf()
|
var properties: Set<BlockProperties> = setOf()
|
||||||
val blockModels: MutableList<BlockModel> = mutableListOf()
|
val blockModels: MutableList<BlockModel> = mutableListOf()
|
||||||
|
val blockRenderers: MutableList<BlockRenderer> = mutableListOf()
|
||||||
|
|
||||||
constructor(identifier: ModIdentifier, properties: Set<BlockProperties>, rotation: BlockRotations) : this(identifier) {
|
constructor(identifier: ModIdentifier, properties: Set<BlockProperties>, rotation: BlockRotations) : this(identifier) {
|
||||||
this.properties = properties
|
this.properties = properties
|
||||||
@ -112,11 +114,11 @@ data class Block(val identifier: ModIdentifier) {
|
|||||||
return String.format("%s%s", identifier, out)
|
return String.format("%s%s", identifier, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getBlockModel(position: BlockPosition): BlockModel {
|
fun getBlockRenderer(position: BlockPosition): BlockRenderer {
|
||||||
if (Minosoft.getConfig().config.game.other.antiMoirePattern) {
|
if (Minosoft.getConfig().config.game.other.antiMoirePattern) {
|
||||||
// ToDo: Support weight attribute
|
// ToDo: Support weight attribute
|
||||||
return blockModels.random(Random(position.hashCode()))
|
return blockRenderers.random(Random(position.hashCode()))
|
||||||
}
|
}
|
||||||
return blockModels.iterator().next()
|
return blockRenderers.iterator().next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,9 @@ import de.bixilon.minosoft.data.mappings.blocks.BlockProperties
|
|||||||
import de.bixilon.minosoft.data.mappings.blocks.BlockRotations
|
import de.bixilon.minosoft.data.mappings.blocks.BlockRotations
|
||||||
import de.bixilon.minosoft.data.mappings.particle.Particle
|
import de.bixilon.minosoft.data.mappings.particle.Particle
|
||||||
import de.bixilon.minosoft.data.mappings.statistics.Statistic
|
import de.bixilon.minosoft.data.mappings.statistics.Statistic
|
||||||
import de.bixilon.minosoft.gui.rendering.chunk.models.BlockModel
|
import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockCondition
|
||||||
|
import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModel
|
||||||
|
import de.bixilon.minosoft.gui.rendering.chunk.models.renderable.BlockRenderer
|
||||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||||
import de.bixilon.minosoft.util.logging.Log
|
import de.bixilon.minosoft.util.logging.Log
|
||||||
import javafx.util.Pair
|
import javafx.util.Pair
|
||||||
@ -61,7 +63,7 @@ class VersionMapping(var version: Version?) {
|
|||||||
private val entityMetaIndexOffsetParentMapping: MutableMap<ModIdentifier, Pair<ModIdentifier, Int>> = mutableMapOf() // identifier, <Parent, Offset>
|
private val entityMetaIndexOffsetParentMapping: MutableMap<ModIdentifier, Pair<ModIdentifier, Int>> = mutableMapOf() // identifier, <Parent, Offset>
|
||||||
private val entityIdClassMap = HashBiMap.create<Int, Class<out Entity?>>(120)
|
private val entityIdClassMap = HashBiMap.create<Int, Class<out Entity?>>(120)
|
||||||
|
|
||||||
private val blockModels = HashBiMap.create<ModIdentifier, BlockModel>(500)
|
val blockModels = HashBiMap.create<ModIdentifier, BlockModel>(500)
|
||||||
|
|
||||||
var parentMapping: VersionMapping? = null
|
var parentMapping: VersionMapping? = null
|
||||||
|
|
||||||
@ -379,11 +381,7 @@ class VersionMapping(var version: Version?) {
|
|||||||
data["parent"]?.asString?.let {
|
data["parent"]?.asString?.let {
|
||||||
parent = loadBlockModel(mod, it, fullModData)
|
parent = loadBlockModel(mod, it, fullModData)
|
||||||
}
|
}
|
||||||
|
model = BlockModel(parent, data)
|
||||||
model = data["conditional"]?.let {
|
|
||||||
// ToDo
|
|
||||||
return@let BlockModel(parent, data)
|
|
||||||
} ?: BlockModel(parent, data)
|
|
||||||
|
|
||||||
blockModels[identifier] = model
|
blockModels[identifier] = model
|
||||||
return model
|
return model
|
||||||
@ -396,31 +394,47 @@ class VersionMapping(var version: Version?) {
|
|||||||
}
|
}
|
||||||
val blockData = fullModData.getAsJsonObject(identifierString)
|
val blockData = fullModData.getAsJsonObject(identifierString)
|
||||||
val identifier = ModIdentifier(mod, identifierString)
|
val identifier = ModIdentifier(mod, identifierString)
|
||||||
val states: JsonArray? = blockData["states"]?.asJsonArray
|
|
||||||
if (states == null) {
|
|
||||||
Log.warn("Block model state: Not states (%s)", identifier)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val blockStates = getBlockId(identifier)!!.blocks
|
val blockStates = getBlockId(identifier)!!.blocks
|
||||||
|
|
||||||
for (value in states) {
|
blockData["states"]?.let {
|
||||||
check(value is JsonObject) { "Invalid model json" }
|
for (value in it.asJsonArray) {
|
||||||
|
check(value is JsonObject) { "Invalid model json" }
|
||||||
val state = loadBlockState(identifier, value)
|
val state = loadBlockState(identifier, value)
|
||||||
var ckecked = false
|
var ckecked = false
|
||||||
for (blockState in blockStates) {
|
for (blockState in blockStates) {
|
||||||
if (blockState.bareEquals(state)) {
|
if (blockState.bareEquals(state)) {
|
||||||
for (type in value.getAsJsonArray("types")) {
|
for (type in value.getAsJsonArray("types")) {
|
||||||
check(type is JsonObject) { "Invalid block type json" }
|
check(type is JsonObject) { "Invalid block type json" }
|
||||||
blockState.blockModels.add(BlockModel(blockModels[ModIdentifier(type["model"].asString.replace("block/", ""))], type))
|
blockState.blockRenderers.add(BlockRenderer(type, this))
|
||||||
|
}
|
||||||
|
ckecked = true
|
||||||
}
|
}
|
||||||
ckecked = true
|
}
|
||||||
|
if (!ckecked) {
|
||||||
|
Log.warn("Block model state: Block is null (%s)", state)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ckecked) {
|
}
|
||||||
Log.warn("Block model state: Block is null (%s)", state)
|
blockData["conditional"]?.let {
|
||||||
continue
|
val conditions = mutableListOf<Pair<BlockCondition, JsonObject>>()
|
||||||
|
for (entry in it.asJsonArray) {
|
||||||
|
entry.asJsonObject?.let conditionLet@ { condition ->
|
||||||
|
condition["properties"]?.let { properties ->
|
||||||
|
conditions.add(Pair(BlockCondition(properties.asJsonObject), condition))
|
||||||
|
return@conditionLet
|
||||||
|
}
|
||||||
|
conditions.add(Pair(BlockCondition.TRUE_CONDITION, condition))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (blockState in blockStates) {
|
||||||
|
val apply = mutableListOf<JsonObject>()
|
||||||
|
for (condition in conditions) {
|
||||||
|
if (condition.key.contains(blockState)) {
|
||||||
|
apply.add(condition.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blockState.blockRenderers.add(BlockRenderer(apply, this))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -466,7 +480,6 @@ class VersionMapping(var version: Version?) {
|
|||||||
data["id"]?.asInt.let {
|
data["id"]?.asInt.let {
|
||||||
entityIdClassMap[it] = clazz
|
entityIdClassMap[it] = clazz
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
var parent: ModIdentifier? = null
|
var parent: ModIdentifier? = null
|
||||||
var metaDataIndexOffset = 0
|
var metaDataIndexOffset = 0
|
||||||
@ -496,7 +509,6 @@ class VersionMapping(var version: Version?) {
|
|||||||
throw RuntimeException("entities.json is invalid")
|
throw RuntimeException("entities.json is invalid")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
entityMetaIndexOffsetParentMapping[identifier] = Pair(parent, metaDataIndexOffset)
|
entityMetaIndexOffsetParentMapping[identifier] = Pair(parent, metaDataIndexOffset)
|
||||||
}
|
}
|
||||||
|
@ -84,12 +84,7 @@ class ChunkRenderer(private val connection: Connection, private val world: World
|
|||||||
} else {
|
} else {
|
||||||
section.getBlock(position.getLocationByDirection(Directions.EAST))
|
section.getBlock(position.getLocationByDirection(Directions.EAST))
|
||||||
}
|
}
|
||||||
|
block.getBlockRenderer(BlockPosition(chunkLocation, sectionHeight, position)).render(Vec3(position.x + chunkLocation.x * ProtocolDefinition.SECTION_WIDTH_X, position.y + sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y, position.z + chunkLocation.z * ProtocolDefinition.SECTION_WIDTH_Z), data, arrayOf(blockBelow, blockAbove, blockNorth, blockSouth, blockWest, blockEast))
|
||||||
// if (block.identifier.fullIdentifier != "minecraft:dispenser") {
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
|
|
||||||
block.getBlockModel(BlockPosition(chunkLocation, sectionHeight, position)).render(Vec3(position.x + chunkLocation.x * ProtocolDefinition.SECTION_WIDTH_X, position.y + sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y, position.z + chunkLocation.z * ProtocolDefinition.SECTION_WIDTH_Z), data, arrayOf(blockBelow, blockAbove, blockNorth, blockSouth, blockWest, blockEast))
|
|
||||||
}
|
}
|
||||||
return data.toFloatArray()
|
return data.toFloatArray()
|
||||||
}
|
}
|
||||||
@ -136,7 +131,7 @@ class ChunkRenderer(private val connection: Connection, private val world: World
|
|||||||
textureMap[TextureArray.DEBUG_TEXTURE.name] = TextureArray.DEBUG_TEXTURE
|
textureMap[TextureArray.DEBUG_TEXTURE.name] = TextureArray.DEBUG_TEXTURE
|
||||||
|
|
||||||
for (block in blocks) {
|
for (block in blocks) {
|
||||||
for (model in block.blockModels) {
|
for (model in block.blockRenderers) {
|
||||||
model.resolveTextures(textures, textureMap)
|
model.resolveTextures(textures, textureMap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,226 +0,0 @@
|
|||||||
/*
|
|
||||||
* Minosoft
|
|
||||||
* Copyright (C) 2020 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.gui.rendering.chunk.models
|
|
||||||
|
|
||||||
import com.google.gson.JsonArray
|
|
||||||
import com.google.gson.JsonObject
|
|
||||||
import de.bixilon.minosoft.data.Axes
|
|
||||||
import de.bixilon.minosoft.data.Directions
|
|
||||||
import de.bixilon.minosoft.gui.rendering.textures.Texture
|
|
||||||
import de.bixilon.minosoft.gui.rendering.textures.TextureArray
|
|
||||||
import glm_.glm
|
|
||||||
import glm_.mat4x4.Mat4
|
|
||||||
import glm_.vec2.Vec2
|
|
||||||
import glm_.vec3.Vec3
|
|
||||||
import glm_.vec4.Vec4
|
|
||||||
|
|
||||||
|
|
||||||
open class BlockModelElement(data: JsonObject) {
|
|
||||||
private val faces: MutableMap<Directions, BlockModelFace> = mutableMapOf()
|
|
||||||
val fullFaceDirections: MutableSet<Directions> = mutableSetOf()
|
|
||||||
var fullFace = false
|
|
||||||
private var positions: Array<Vec3>
|
|
||||||
|
|
||||||
init {
|
|
||||||
var from = Vec3(0, 0, 0)
|
|
||||||
var to = Vec3(16, 16, 16)
|
|
||||||
data["from"]?.let {
|
|
||||||
val array = it.asJsonArray
|
|
||||||
from = Vec3(array[0].asFloat, array[1].asFloat, array[2].asFloat)
|
|
||||||
}
|
|
||||||
data["to"]?.let {
|
|
||||||
val array = it.asJsonArray
|
|
||||||
to = Vec3(array[0].asFloat, array[1].asFloat, array[2].asFloat)
|
|
||||||
}
|
|
||||||
positions = arrayOf(
|
|
||||||
Vec3(from),
|
|
||||||
Vec3(to.x, from.y, from.z),
|
|
||||||
Vec3(from.x, from.y, to.z),
|
|
||||||
Vec3(to.x, from.y, to.z),
|
|
||||||
Vec3(from.x, to.y, from.z),
|
|
||||||
Vec3(to.x, to.y, from.z),
|
|
||||||
Vec3(from.x, to.y, to.z),
|
|
||||||
Vec3(to),
|
|
||||||
)
|
|
||||||
var rotate = Vec3()
|
|
||||||
data["rotation"]?.let {
|
|
||||||
val rotation = it.asJsonObject
|
|
||||||
val axis = Axes.valueOf(rotation["axis"].asString.toUpperCase())
|
|
||||||
val angle = glm.radians(rotation["angle"].asDouble)
|
|
||||||
rotate(axis, angle, jsonArrayToVec3(rotation["origin"].asJsonArray))
|
|
||||||
rotate = when (axis) {
|
|
||||||
Axes.X -> Vec3(angle, 0, 0)
|
|
||||||
Axes.Y -> Vec3(0, angle, 0)
|
|
||||||
Axes.Z -> Vec3(0, 0, angle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data["faces"]?.let {
|
|
||||||
for ((directionName, json) in it.asJsonObject.entrySet()) {
|
|
||||||
var direction = Directions.valueOf(directionName.toUpperCase())
|
|
||||||
faces[direction] = BlockModelFace(json.asJsonObject)
|
|
||||||
direction = getRotatedDirection(rotate, direction)
|
|
||||||
fullFace = positions.containsAll(fullTestPositions[direction]) // TODO: check if texture is transparent ==> && ! texture.isTransparent
|
|
||||||
if (fullFace) {
|
|
||||||
fullFaceDirections.add(direction)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for ((i, position) in positions.withIndex()) {
|
|
||||||
positions[i] = BlockModel.transformPosition(position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun rotate(axis: Axes, angle: Double, origin: Vec3) {
|
|
||||||
// TODO: optimize for 90deg, 180deg, 270deg rotations
|
|
||||||
for ((i, position) in positions.withIndex()) {
|
|
||||||
var transformedPosition = position - origin
|
|
||||||
transformedPosition = rotateVector(transformedPosition, angle, axis)
|
|
||||||
positions[i] = transformedPosition + origin
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun rotateVector(original: Vec3, angle: Double, axis: Axes): Vec3 {
|
|
||||||
fun getRotatedValues(x: Float, y: Float, sin: Double, cos: Double): Pair<Float, Float> {
|
|
||||||
return Pair((x * cos - y * sin).toFloat(), (x * sin + y * cos).toFloat())
|
|
||||||
}
|
|
||||||
return when (axis) {
|
|
||||||
Axes.X -> run {
|
|
||||||
val rotatedValues = getRotatedValues(original.y, original.z, glm.sin(angle), glm.cos(angle))
|
|
||||||
return@run Vec3(original.x, rotatedValues.first, rotatedValues.second)
|
|
||||||
}
|
|
||||||
Axes.Y -> run {
|
|
||||||
val rotatedValues = getRotatedValues(original.x, original.z, glm.sin(angle), glm.cos(angle))
|
|
||||||
return@run Vec3(rotatedValues.first, original.y, rotatedValues.second)
|
|
||||||
}
|
|
||||||
Axes.Z -> run {
|
|
||||||
val rotatedValues = getRotatedValues(original.x, original.y, glm.sin(angle), glm.cos(angle))
|
|
||||||
return@run Vec3(rotatedValues.first, rotatedValues.second, original.z)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getRotatedDirection(rotation: Vec3, direction: Directions): Directions {
|
|
||||||
if (rotation == Vec3(0, 0, 0)) {
|
|
||||||
return direction
|
|
||||||
}
|
|
||||||
var rotatedDirectionVector = rotateVector(direction.directionVector, rotation.z.toDouble(), Axes.Z)
|
|
||||||
rotatedDirectionVector = rotateVector(rotatedDirectionVector, rotation.y.toDouble(), Axes.Y)
|
|
||||||
return Directions.byDirection(rotateVector(rotatedDirectionVector, rotation.x.toDouble(), Axes.X))
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun render(textureMapping: MutableMap<String, Texture>, modelMatrix: Mat4, direction: Directions, rotation: Vec3, data: MutableList<Float>) {
|
|
||||||
val realDirection = getRotatedDirection(rotation, direction)
|
|
||||||
val positionTemplate = FACE_POSITION_MAP_TEMPLATE[realDirection.ordinal]
|
|
||||||
|
|
||||||
val face = faces[realDirection] ?: return // Not our face
|
|
||||||
val texture = textureMapping[face.textureName] ?: TextureArray.DEBUG_TEXTURE
|
|
||||||
// if (texture.isTransparent) {
|
|
||||||
// return // ToDo: force render transparent faces
|
|
||||||
// }
|
|
||||||
|
|
||||||
val drawPositions = arrayOf(positions[positionTemplate[0]], positions[positionTemplate[1]], positions[positionTemplate[2]], positions[positionTemplate[3]])
|
|
||||||
|
|
||||||
fun addToData(vec3: Vec3, textureCoordinates: Vec2) {
|
|
||||||
val input = Vec4(vec3, 1.0f)
|
|
||||||
val output = modelMatrix * input
|
|
||||||
data.add(output.x)
|
|
||||||
data.add(output.y)
|
|
||||||
data.add(output.z)
|
|
||||||
data.add(textureCoordinates.x * texture.widthFactor)
|
|
||||||
data.add(textureCoordinates.y * texture.heightFactor)
|
|
||||||
data.add(Float.fromBits(texture.id)) // ToDo: Compact this
|
|
||||||
|
|
||||||
// ToDo: Send this only once per texture
|
|
||||||
data.add(texture.animationFrameTime.toFloat())
|
|
||||||
data.add(texture.animations.toFloat())
|
|
||||||
data.add(texture.heightFactor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createQuad(drawPositions: Array<Vec3>, texturePositions: IntArray) {
|
|
||||||
addToData(drawPositions[0], face.positions[texturePositions[1]])
|
|
||||||
addToData(drawPositions[3], face.positions[texturePositions[2]])
|
|
||||||
addToData(drawPositions[2], face.positions[texturePositions[3]])
|
|
||||||
addToData(drawPositions[2], face.positions[texturePositions[3]])
|
|
||||||
addToData(drawPositions[1], face.positions[texturePositions[0]])
|
|
||||||
addToData(drawPositions[0], face.positions[texturePositions[1]])
|
|
||||||
}
|
|
||||||
|
|
||||||
createQuad(drawPositions, FACE_TEXTURE_POSITION[realDirection.ordinal])
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isCullFace(direction: Directions): Boolean {
|
|
||||||
return faces[direction]?.cullFace == direction
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getTexture(direction: Directions): String? {
|
|
||||||
return faces[direction]?.textureName
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun jsonArrayToVec3(array: JsonArray): Vec3 {
|
|
||||||
return Vec3(array[0].asFloat, array[1].asFloat, array[2].asFloat)
|
|
||||||
}
|
|
||||||
|
|
||||||
private const val BLOCK_RESOLUTION = 16
|
|
||||||
|
|
||||||
val FACE_POSITION_MAP_TEMPLATE = arrayOf(
|
|
||||||
intArrayOf(0, 2, 3, 1),
|
|
||||||
intArrayOf(6, 4, 5, 7),
|
|
||||||
intArrayOf(1, 5, 4, 0),
|
|
||||||
intArrayOf(2, 6, 7, 3),
|
|
||||||
intArrayOf(6, 2, 0, 4),
|
|
||||||
intArrayOf(5, 1, 3, 7),
|
|
||||||
)
|
|
||||||
|
|
||||||
val FACE_TEXTURE_POSITION = arrayOf(
|
|
||||||
intArrayOf(0, 1, 2, 3),
|
|
||||||
intArrayOf(0, 1, 2, 3),
|
|
||||||
intArrayOf(3, 2, 1, 0),
|
|
||||||
intArrayOf(0, 1, 2, 3),
|
|
||||||
intArrayOf(2, 3, 0, 1),
|
|
||||||
intArrayOf(1, 0, 3, 2),
|
|
||||||
)
|
|
||||||
|
|
||||||
private val POSITION_1 = Vec3(0, 0, 0)
|
|
||||||
private val POSITION_2 = Vec3(BLOCK_RESOLUTION, 0, 0)
|
|
||||||
private val POSITION_3 = Vec3(0, 0, BLOCK_RESOLUTION)
|
|
||||||
private val POSITION_4 = Vec3(BLOCK_RESOLUTION, 0, BLOCK_RESOLUTION)
|
|
||||||
|
|
||||||
private val POSITION_5 = Vec3(0, BLOCK_RESOLUTION, 0)
|
|
||||||
private val POSITION_6 = Vec3(BLOCK_RESOLUTION, BLOCK_RESOLUTION, 0)
|
|
||||||
private val POSITION_7 = Vec3(0, BLOCK_RESOLUTION, BLOCK_RESOLUTION)
|
|
||||||
private val POSITION_8 = Vec3(BLOCK_RESOLUTION, BLOCK_RESOLUTION, BLOCK_RESOLUTION)
|
|
||||||
|
|
||||||
val fullTestPositions = mapOf(
|
|
||||||
Pair(Directions.EAST, setOf(POSITION_1, POSITION_3, POSITION_5, POSITION_7)),
|
|
||||||
Pair(Directions.WEST, setOf(POSITION_2, POSITION_4, POSITION_6, POSITION_8)),
|
|
||||||
Pair(Directions.DOWN, setOf(POSITION_1, POSITION_2, POSITION_3, POSITION_4)),
|
|
||||||
Pair(Directions.UP, setOf(POSITION_5, POSITION_6, POSITION_7, POSITION_8)),
|
|
||||||
Pair(Directions.SOUTH, setOf(POSITION_1, POSITION_2, POSITION_5, POSITION_6)),
|
|
||||||
Pair(Directions.NORTH, setOf(POSITION_3, POSITION_4, POSITION_7, POSITION_8)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> Array<T>.containsAll(set: Set<T>?): Boolean {
|
|
||||||
set?.let {
|
|
||||||
for (value in set) {
|
|
||||||
if (!this.contains(value)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
* Minosoft
|
|
||||||
* Copyright (C) 2020 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.gui.rendering.chunk.models
|
|
||||||
|
|
||||||
import com.google.gson.JsonObject
|
|
||||||
import de.bixilon.minosoft.data.Directions
|
|
||||||
import glm_.vec2.Vec2
|
|
||||||
|
|
||||||
class BlockModelFace(data: JsonObject) {
|
|
||||||
val textureName: String = data.get("texture").asString.removePrefix("#")
|
|
||||||
val cullFace: Directions?
|
|
||||||
|
|
||||||
val positions: Array<Vec2>
|
|
||||||
|
|
||||||
init {
|
|
||||||
var textureStart = Vec2(0, 0)
|
|
||||||
var textureEnd = Vec2(16, 16)
|
|
||||||
data["uv"]?.asJsonArray?.let {
|
|
||||||
textureStart = Vec2(it[0].asFloat, it[1].asFloat)
|
|
||||||
textureEnd = Vec2(it[2].asFloat, it[3].asFloat)
|
|
||||||
}
|
|
||||||
positions = arrayOf(
|
|
||||||
uvToFloat(Vec2(textureStart.x, textureStart.y)),
|
|
||||||
uvToFloat(Vec2(textureStart.x, textureEnd.y)),
|
|
||||||
uvToFloat(Vec2(textureEnd.x, textureEnd.y)),
|
|
||||||
uvToFloat(Vec2(textureEnd.x, textureStart.y)),
|
|
||||||
)
|
|
||||||
|
|
||||||
cullFace = data["cullface"]?.asString?.let {
|
|
||||||
return@let if (it == "bottom") {
|
|
||||||
Directions.DOWN
|
|
||||||
} else {
|
|
||||||
Directions.valueOf(it.toUpperCase())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private fun uvToFloat(uv: Float): Float {
|
|
||||||
return (uv) / 16f
|
|
||||||
}
|
|
||||||
|
|
||||||
fun uvToFloat(vec2: Vec2): Vec2 {
|
|
||||||
return Vec2(uvToFloat(vec2.x), uvToFloat(vec2.y))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,45 @@
|
|||||||
|
package de.bixilon.minosoft.gui.rendering.chunk.models.loading;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import de.bixilon.minosoft.data.mappings.blocks.Block;
|
||||||
|
import de.bixilon.minosoft.data.mappings.blocks.BlockProperties;
|
||||||
|
import de.bixilon.minosoft.data.mappings.blocks.BlockRotations;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class BlockCondition {
|
||||||
|
public static final BlockCondition TRUE_CONDITION = new BlockCondition() {
|
||||||
|
@Override
|
||||||
|
public boolean contains(Block block) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private HashSet<BlockProperties> properties;
|
||||||
|
private BlockRotations rotation;
|
||||||
|
|
||||||
|
public BlockCondition(JsonObject json) {
|
||||||
|
properties = new HashSet<>();
|
||||||
|
rotation = BlockRotations.NONE;
|
||||||
|
for (Map.Entry<String, JsonElement> entry : json.entrySet()) {
|
||||||
|
String value = entry.getValue().getAsString();
|
||||||
|
if (BlockProperties.PROPERTIES_MAPPING.containsKey(entry.getKey())) {
|
||||||
|
properties.add(BlockProperties.PROPERTIES_MAPPING.get(entry.getKey()).get(value));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rotation = BlockRotations.ROTATION_MAPPING.get(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockCondition() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(Block block) {
|
||||||
|
if (rotation != BlockRotations.NONE && rotation != block.getRotation()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return block.getProperties().containsAll(properties);
|
||||||
|
}
|
||||||
|
}
|
@ -11,21 +11,19 @@
|
|||||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.bixilon.minosoft.gui.rendering.chunk.models
|
package de.bixilon.minosoft.gui.rendering.chunk.models.loading
|
||||||
|
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import de.bixilon.minosoft.data.Directions
|
import de.bixilon.minosoft.data.Directions
|
||||||
import de.bixilon.minosoft.data.mappings.blocks.Block
|
|
||||||
import de.bixilon.minosoft.gui.rendering.textures.Texture
|
import de.bixilon.minosoft.gui.rendering.textures.Texture
|
||||||
import glm_.glm
|
import glm_.glm
|
||||||
import glm_.mat4x4.Mat4
|
|
||||||
import glm_.vec3.Vec3
|
import glm_.vec3.Vec3
|
||||||
|
|
||||||
open class BlockModel(val parent: BlockModel? = null, json: JsonObject) {
|
open class BlockModel(val parent: BlockModel? = null, json: JsonObject) {
|
||||||
private val textures: MutableMap<String, String> = parent?.textures?.toMutableMap() ?: mutableMapOf()
|
val textures: MutableMap<String, String> = parent?.textures?.toMutableMap() ?: mutableMapOf()
|
||||||
private val textureMapping: MutableMap<String, Texture> = mutableMapOf()
|
private val textureMapping: MutableMap<String, Texture> = mutableMapOf()
|
||||||
private var elements: MutableList<BlockModelElement> = parent?.elements?.toMutableList() ?: mutableListOf()
|
var elements: MutableList<BlockModelElement> = parent?.elements?.toMutableList() ?: mutableListOf()
|
||||||
private val fullFaceDirections: MutableSet<Directions> = parent?.fullFaceDirections?.toMutableSet() ?: mutableSetOf()
|
val fullFaceDirections: MutableSet<Directions> = parent?.fullFaceDirections?.toMutableSet() ?: mutableSetOf()
|
||||||
private var rotation: Vec3
|
private var rotation: Vec3
|
||||||
private var uvLock = false // ToDo
|
private var uvLock = false // ToDo
|
||||||
private var rescale = false // ToDo
|
private var rescale = false // ToDo
|
||||||
@ -72,42 +70,8 @@ open class BlockModel(val parent: BlockModel? = null, json: JsonObject) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open fun render(position: Vec3, data: MutableList<Float>, neighbourBlocks: Array<Block?>) {
|
|
||||||
val modelMatrix = Mat4().translate(position)
|
|
||||||
.rotate(rotation.z, Vec3(0, 0, -1))
|
|
||||||
.rotate(rotation.y, Vec3(0, -1, 0))
|
|
||||||
.rotate(rotation.x, Vec3(1, 0, 0))
|
|
||||||
// ToDo: this should be made easier/more efficient
|
|
||||||
|
|
||||||
for (direction in Directions.DIRECTIONS) {
|
|
||||||
for (element in elements) {
|
|
||||||
val blockFullFace = fullFaceDirections.contains(direction)
|
|
||||||
|
|
||||||
var neighbourBlockFullFace = false
|
|
||||||
neighbourBlocks[direction.ordinal]?.blockModels?.let { // ToDo: Improve this
|
|
||||||
for (model in it) {
|
|
||||||
if (model.fullFaceDirections.contains(direction.inverse())) {
|
|
||||||
neighbourBlockFullFace = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blockFullFace && neighbourBlockFullFace) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (!blockFullFace && neighbourBlockFullFace) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
element.render(textureMapping, modelMatrix, direction, rotation, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun getTextureByType(type: String): String {
|
private fun getTextureByType(type: String): String {
|
||||||
var currentValue: String = type
|
var currentValue: String = type
|
||||||
|
|
||||||
while (currentValue.startsWith("#")) {
|
while (currentValue.startsWith("#")) {
|
||||||
textures[currentValue.removePrefix("#")].let {
|
textures[currentValue.removePrefix("#")].let {
|
||||||
if (it == null) {
|
if (it == null) {
|
||||||
@ -116,7 +80,6 @@ open class BlockModel(val parent: BlockModel? = null, json: JsonObject) {
|
|||||||
currentValue = it
|
currentValue = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return currentValue
|
return currentValue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,25 +92,6 @@ open class BlockModel(val parent: BlockModel? = null, json: JsonObject) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resolveTextures(indexed: MutableList<Texture>, textureMap: MutableMap<String, Texture>) {
|
|
||||||
for ((key, textureName) in textures) {
|
|
||||||
if (!textureName.startsWith("#")) {
|
|
||||||
var texture: Texture? = null
|
|
||||||
var index: Int? = textureMap[textureName]?.let {
|
|
||||||
texture = it
|
|
||||||
indexed.indexOf(it)
|
|
||||||
}
|
|
||||||
if (index == null || index == -1) {
|
|
||||||
index = textureMap.size
|
|
||||||
texture = Texture(textureName, index)
|
|
||||||
textureMap[textureName] = texture!!
|
|
||||||
indexed.add(texture!!)
|
|
||||||
}
|
|
||||||
textureMapping[key] = texture!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isTransparent(direction: Directions): Boolean {
|
fun isTransparent(direction: Directions): Boolean {
|
||||||
for (element in elements) {
|
for (element in elements) {
|
||||||
if (textureMapping[element.getTexture(direction)]?.isTransparent == true) {
|
if (textureMapping[element.getTexture(direction)]?.isTransparent == true) {
|
||||||
@ -157,16 +101,4 @@ open class BlockModel(val parent: BlockModel? = null, json: JsonObject) {
|
|||||||
return false
|
return false
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun transformPosition(position: Vec3): Vec3 {
|
|
||||||
fun positionToFloat(uv: Float): Float {
|
|
||||||
return (uv - 8f) / 16f
|
|
||||||
}
|
|
||||||
|
|
||||||
return Vec3(positionToFloat(position.x), positionToFloat(position.y), positionToFloat(position.z))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,173 @@
|
|||||||
|
package de.bixilon.minosoft.gui.rendering.chunk.models.loading
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import de.bixilon.minosoft.data.Axes
|
||||||
|
import de.bixilon.minosoft.data.Directions
|
||||||
|
import glm_.glm
|
||||||
|
import glm_.vec3.Vec3
|
||||||
|
|
||||||
|
open class BlockModelElement(data: JsonObject) {
|
||||||
|
val faces: MutableMap<Directions, BlockModelFace> = mutableMapOf()
|
||||||
|
val fullFaceDirections: MutableSet<Directions> = mutableSetOf()
|
||||||
|
var fullFace = false
|
||||||
|
var positions: Array<Vec3>
|
||||||
|
|
||||||
|
init {
|
||||||
|
var from = Vec3(0, 0, 0)
|
||||||
|
var to = Vec3(16, 16, 16)
|
||||||
|
data["from"]?.let {
|
||||||
|
val array = it.asJsonArray
|
||||||
|
from = Vec3(array[0].asFloat, array[1].asFloat, array[2].asFloat)
|
||||||
|
}
|
||||||
|
data["to"]?.let {
|
||||||
|
val array = it.asJsonArray
|
||||||
|
to = Vec3(array[0].asFloat, array[1].asFloat, array[2].asFloat)
|
||||||
|
}
|
||||||
|
positions = arrayOf(
|
||||||
|
Vec3(from),
|
||||||
|
Vec3(to.x, from.y, from.z),
|
||||||
|
Vec3(from.x, from.y, to.z),
|
||||||
|
Vec3(to.x, from.y, to.z),
|
||||||
|
Vec3(from.x, to.y, from.z),
|
||||||
|
Vec3(to.x, to.y, from.z),
|
||||||
|
Vec3(from.x, to.y, to.z),
|
||||||
|
Vec3(to),
|
||||||
|
)
|
||||||
|
var rotate = Vec3()
|
||||||
|
data["rotation"]?.let {
|
||||||
|
val rotation = it.asJsonObject
|
||||||
|
val axis = Axes.valueOf(rotation["axis"].asString.toUpperCase())
|
||||||
|
val angle = glm.radians(rotation["angle"].asDouble)
|
||||||
|
rotatePositions(positions, axis, angle, jsonArrayToVec3(rotation["origin"].asJsonArray))
|
||||||
|
rotate = when (axis) {
|
||||||
|
Axes.X -> run { return@run Vec3(angle, 0, 0) }
|
||||||
|
Axes.Y -> run { return@run Vec3(0, angle, 0) }
|
||||||
|
Axes.Z -> run { return@run Vec3(0, 0, angle) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data["faces"]?.let {
|
||||||
|
for ((directionName, json) in it.asJsonObject.entrySet()) {
|
||||||
|
var direction = Directions.valueOf(directionName.toUpperCase())
|
||||||
|
faces[direction] = BlockModelFace(json.asJsonObject, from, to, direction)
|
||||||
|
direction = getRotatedDirection(rotate, direction)
|
||||||
|
fullFace = positions.containsAll(fullTestPositions[direction]) // TODO: check if texture is transparent ==> && ! texture.isTransparent
|
||||||
|
if (fullFace) {
|
||||||
|
fullFaceDirections.add(direction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ((i, position) in positions.withIndex()) {
|
||||||
|
positions[i] = transformPosition(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isCullFace(direction: Directions): Boolean {
|
||||||
|
return faces[direction]?.cullFace == direction
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTexture(direction: Directions): String? {
|
||||||
|
return faces[direction]?.textureName
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun jsonArrayToVec3(array: JsonArray) : Vec3 {
|
||||||
|
return Vec3(array[0].asFloat, array[1].asFloat, array[2].asFloat)
|
||||||
|
}
|
||||||
|
private const val BLOCK_RESOLUTION = 16
|
||||||
|
|
||||||
|
val FACE_POSITION_MAP_TEMPLATE = arrayOf(
|
||||||
|
intArrayOf(0, 2, 3, 1),
|
||||||
|
intArrayOf(6, 4, 5, 7),
|
||||||
|
intArrayOf(1, 5, 4, 0),
|
||||||
|
intArrayOf(2, 6, 7, 3),
|
||||||
|
intArrayOf(6, 2, 0, 4),
|
||||||
|
intArrayOf(5, 1, 3, 7)
|
||||||
|
)
|
||||||
|
|
||||||
|
private val POSITION_1 = Vec3(0, 0, 0)
|
||||||
|
private val POSITION_2 = Vec3(BLOCK_RESOLUTION, 0, 0)
|
||||||
|
private val POSITION_3 = Vec3(0, 0, BLOCK_RESOLUTION)
|
||||||
|
private val POSITION_4 = Vec3(BLOCK_RESOLUTION, 0, BLOCK_RESOLUTION)
|
||||||
|
|
||||||
|
private val POSITION_5 = Vec3(0, BLOCK_RESOLUTION, 0)
|
||||||
|
private val POSITION_6 = Vec3(BLOCK_RESOLUTION, BLOCK_RESOLUTION, 0)
|
||||||
|
private val POSITION_7 = Vec3(0, BLOCK_RESOLUTION, BLOCK_RESOLUTION)
|
||||||
|
private val POSITION_8 = Vec3(BLOCK_RESOLUTION, BLOCK_RESOLUTION, BLOCK_RESOLUTION)
|
||||||
|
|
||||||
|
val fullTestPositions = mapOf(
|
||||||
|
Pair(Directions.EAST, setOf(POSITION_1, POSITION_3, POSITION_5, POSITION_7)),
|
||||||
|
Pair(Directions.WEST, setOf(POSITION_2, POSITION_4, POSITION_6, POSITION_8)),
|
||||||
|
Pair(Directions.DOWN, setOf(POSITION_1, POSITION_2, POSITION_3, POSITION_4)),
|
||||||
|
Pair(Directions.UP, setOf(POSITION_5, POSITION_6, POSITION_7, POSITION_8)),
|
||||||
|
Pair(Directions.SOUTH, setOf(POSITION_1, POSITION_2, POSITION_5, POSITION_6)),
|
||||||
|
Pair(Directions.NORTH, setOf(POSITION_3, POSITION_4, POSITION_7, POSITION_8)),
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getRotatedDirection(rotation: Vec3, direction: Directions): Directions {
|
||||||
|
if (rotation == Vec3(0, 0, 0)) {
|
||||||
|
return direction
|
||||||
|
}
|
||||||
|
var rotatedDirectionVector = rotateVector(direction.directionVector, rotation.z.toDouble(), Axes.Z)
|
||||||
|
rotatedDirectionVector = rotateVector(rotatedDirectionVector, rotation.y.toDouble(), Axes.Y)
|
||||||
|
return Directions.byDirection(rotateVector(rotatedDirectionVector, rotation.x.toDouble(), Axes.X))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rotateVector(original: Vec3, angle: Double, axis: Axes): Vec3 {
|
||||||
|
fun getRotatedValues(x: Float, y: Float, sin: Double, cos: Double): Pair<Float, Float> {
|
||||||
|
return Pair((x * cos - y * sin).toFloat(), (x * sin + y * cos).toFloat())
|
||||||
|
}
|
||||||
|
return when (axis) {
|
||||||
|
Axes.X -> run {
|
||||||
|
val rotatedValues = getRotatedValues(original.y, original.z, glm.sin(angle), glm.cos(angle))
|
||||||
|
return@run Vec3(original.x, rotatedValues.first, rotatedValues.second)
|
||||||
|
}
|
||||||
|
Axes.Y -> run {
|
||||||
|
val rotatedValues = getRotatedValues(original.x, original.z, glm.sin(angle), glm.cos(angle))
|
||||||
|
return@run Vec3(rotatedValues.first, original.y, rotatedValues.second)
|
||||||
|
}
|
||||||
|
Axes.Z -> run {
|
||||||
|
val rotatedValues = getRotatedValues(original.x, original.y, glm.sin(angle), glm.cos(angle))
|
||||||
|
return@run Vec3(rotatedValues.first, rotatedValues.second, original.z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun rotatePositions(positions: Array<Vec3>, axis: Axes, angle: Double, origin: Vec3) {
|
||||||
|
// TODO: optimize for 90deg, 180deg, 270deg rotations
|
||||||
|
if (angle == 0.0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for ((i, position) in positions.withIndex()) {
|
||||||
|
var transformedPosition = position - origin
|
||||||
|
transformedPosition = rotateVector(transformedPosition, angle, axis)
|
||||||
|
positions[i] = transformedPosition + origin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun rotatePositionsAxes(positions: Array<Vec3>, angles: Vec3) {
|
||||||
|
rotatePositions(positions, Axes.Z, angles.z.toDouble(), Vec3())
|
||||||
|
rotatePositions(positions, Axes.Y, angles.y.toDouble(), Vec3())
|
||||||
|
rotatePositions(positions, Axes.X, angles.x.toDouble(), Vec3())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun transformPosition(position: Vec3): Vec3 {
|
||||||
|
fun positionToFloat(uv: Float): Float {
|
||||||
|
return (uv - 8f) / 16f
|
||||||
|
}
|
||||||
|
return Vec3(positionToFloat(position.x), positionToFloat(position.y), positionToFloat(position.z))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> Array<T>.containsAll(set: Set<T>?): Boolean {
|
||||||
|
if (set != null) {
|
||||||
|
for (value in set) {
|
||||||
|
if (! this.contains(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package de.bixilon.minosoft.gui.rendering.chunk.models.loading
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import de.bixilon.minosoft.data.Directions
|
||||||
|
import glm_.vec2.Vec2
|
||||||
|
import glm_.vec3.Vec3
|
||||||
|
|
||||||
|
class BlockModelFace(data: JsonObject, from: Vec3, to: Vec3, direction: Directions) {
|
||||||
|
val textureName: String = data.get("texture").asString.removePrefix("#")
|
||||||
|
val cullFace: Directions?
|
||||||
|
|
||||||
|
private var positions: Array<Vec2>
|
||||||
|
|
||||||
|
init {
|
||||||
|
var textureStart = Vec2(0, 0)
|
||||||
|
var textureEnd = Vec2(16, 16)
|
||||||
|
when (direction) {
|
||||||
|
Directions.EAST, Directions.WEST -> run {
|
||||||
|
textureStart = Vec2(from.z.toInt(), 16 - from.y.toInt());
|
||||||
|
textureEnd = Vec2(to.z.toInt(), 16 - to.y.toInt());
|
||||||
|
}
|
||||||
|
Directions.UP, Directions.DOWN -> {
|
||||||
|
textureStart = Vec2(from.x.toInt(), 16 - from.z.toInt());
|
||||||
|
textureEnd = Vec2(to.x.toInt(), 16 - to.z.toInt());
|
||||||
|
}
|
||||||
|
Directions.NORTH, Directions.SOUTH -> {
|
||||||
|
textureStart = Vec2(from.x.toInt(), 16 - from.y.toInt());
|
||||||
|
textureEnd = Vec2(to.x.toInt(), 16 - to.y.toInt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data["uv"]?.asJsonArray?.let {
|
||||||
|
textureStart = Vec2(it[0].asFloat, it[1].asFloat)
|
||||||
|
textureEnd = Vec2(it[2].asFloat, it[3].asFloat)
|
||||||
|
}
|
||||||
|
positions = arrayOf(
|
||||||
|
uvToFloat(Vec2(textureStart.x, textureStart.y)),
|
||||||
|
uvToFloat(Vec2(textureStart.x, textureEnd.y)),
|
||||||
|
uvToFloat(Vec2(textureEnd.x, textureEnd.y)),
|
||||||
|
uvToFloat(Vec2(textureEnd.x, textureStart.y)),
|
||||||
|
)
|
||||||
|
|
||||||
|
cullFace = data["cullface"]?.asString?.let {
|
||||||
|
return@let if (it == "bottom") {
|
||||||
|
Directions.DOWN
|
||||||
|
} else {
|
||||||
|
Directions.valueOf(it.toUpperCase())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
positions = arrayOf(
|
||||||
|
Vec2(uvToFloat(textureStart.x), uvToFloat(textureStart.y)),
|
||||||
|
Vec2(uvToFloat(textureStart.x), uvToFloat(textureEnd.y)),
|
||||||
|
Vec2(uvToFloat(textureEnd.x), uvToFloat(textureEnd.y)),
|
||||||
|
Vec2(uvToFloat(textureEnd.x), uvToFloat(textureStart.y)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTexturePositionArray(direction: Directions): Array<Vec2?> {
|
||||||
|
val template = textureTemplate[direction.ordinal]
|
||||||
|
val result = arrayOfNulls<Vec2>(template.size)
|
||||||
|
for (i in template.indices) {
|
||||||
|
result[i] = positions[template[i]]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun uvToFloat(uv: Float): Float {
|
||||||
|
return (uv) / 16f
|
||||||
|
}
|
||||||
|
|
||||||
|
fun uvToFloat(vec2: Vec2): Vec2 {
|
||||||
|
return Vec2(uvToFloat(vec2.x), uvToFloat(vec2.y))
|
||||||
|
}
|
||||||
|
|
||||||
|
val textureTemplate = arrayOf(
|
||||||
|
arrayOf(0, 1, 2, 3, ),
|
||||||
|
arrayOf(0, 1, 2, 3, ),
|
||||||
|
arrayOf(3, 2, 1, 0, ),
|
||||||
|
arrayOf(0, 1, 2, 3, ),
|
||||||
|
arrayOf(2, 3, 0, 1, ),
|
||||||
|
arrayOf(1, 0, 3, 2, ),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package de.bixilon.minosoft.gui.rendering.chunk.models.renderable
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import de.bixilon.minosoft.data.Directions
|
||||||
|
import de.bixilon.minosoft.data.mappings.ModIdentifier
|
||||||
|
import de.bixilon.minosoft.data.mappings.blocks.Block
|
||||||
|
import de.bixilon.minosoft.data.mappings.versions.VersionMapping
|
||||||
|
import de.bixilon.minosoft.gui.rendering.textures.Texture
|
||||||
|
import glm_.mat4x4.Mat4
|
||||||
|
import glm_.vec3.Vec3
|
||||||
|
|
||||||
|
class BlockRenderer() {
|
||||||
|
val textures: MutableMap<String, String> = mutableMapOf()
|
||||||
|
private val fullFaceDirections: MutableSet<Directions> = mutableSetOf()
|
||||||
|
private val elements: MutableSet<ElementRenderer> = mutableSetOf()
|
||||||
|
private val rotation: Vec3 = Vec3()
|
||||||
|
private val textureMapping: MutableMap<String, Texture> = mutableMapOf()
|
||||||
|
|
||||||
|
constructor(entry: JsonObject, mapping: VersionMapping) : this() {
|
||||||
|
loadElements(entry, mapping)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadElements(entry: JsonObject, mapping: VersionMapping) {
|
||||||
|
this.elements.addAll(ElementRenderer.createElements(entry, mapping))
|
||||||
|
val parent = mapping.blockModels[ModIdentifier(entry["model"].asString.replace("block/", ""))]
|
||||||
|
textures.putAll(parent!!.textures)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(models: List<JsonObject>, mapping: VersionMapping) : this() {
|
||||||
|
for (state in models) {
|
||||||
|
loadElements(state, mapping)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resolveTextures(indexed: MutableList<Texture>, textureMap: MutableMap<String, Texture>) {
|
||||||
|
for ((key, textureName) in textures) {
|
||||||
|
if (!textureName.startsWith("#")) {
|
||||||
|
var texture: Texture? = null
|
||||||
|
var index: Int? = textureMap[textureName]?.let {
|
||||||
|
texture = it
|
||||||
|
indexed.indexOf(it)
|
||||||
|
}
|
||||||
|
if (index == null || index == -1) {
|
||||||
|
index = textureMap.size
|
||||||
|
texture = Texture(textureName, index)
|
||||||
|
textureMap[textureName] = texture!!
|
||||||
|
indexed.add(texture!!)
|
||||||
|
}
|
||||||
|
textureMapping[key] = texture!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun render(position: Vec3, data: MutableList<Float>, neighbourBlocks: Array<Block?>) {
|
||||||
|
val modelMatrix = Mat4().translate(Vec3(position.x, position.y, position.z))
|
||||||
|
.rotate(rotation.z, Vec3(0, 0, -1))
|
||||||
|
.rotate(rotation.y, Vec3(0, -1, 0))
|
||||||
|
.rotate(rotation.x, Vec3(1, 0, 0 ))
|
||||||
|
// ToDo: this should be made easier/more efficient
|
||||||
|
|
||||||
|
for (direction in Directions.DIRECTIONS) {
|
||||||
|
for (element in elements) {
|
||||||
|
val blockFullFace = fullFaceDirections.contains(direction)
|
||||||
|
|
||||||
|
var neighbourBlockFullFace = false
|
||||||
|
neighbourBlocks[direction.ordinal]?.blockModels?.let { // ToDo: Improve this
|
||||||
|
for (model in it) {
|
||||||
|
if (model.fullFaceDirections.contains(direction.inverse())) {
|
||||||
|
neighbourBlockFullFace = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (blockFullFace && neighbourBlockFullFace) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!blockFullFace && neighbourBlockFullFace) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
element.render(textureMapping, modelMatrix, direction, rotation, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package de.bixilon.minosoft.gui.rendering.chunk.models.renderable
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import de.bixilon.minosoft.data.Directions
|
||||||
|
import de.bixilon.minosoft.data.mappings.ModIdentifier
|
||||||
|
import de.bixilon.minosoft.data.mappings.versions.VersionMapping
|
||||||
|
import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModelElement
|
||||||
|
import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModelFace
|
||||||
|
import de.bixilon.minosoft.gui.rendering.textures.Texture
|
||||||
|
import de.bixilon.minosoft.gui.rendering.textures.TextureArray
|
||||||
|
import glm_.Java.Companion.glm
|
||||||
|
import glm_.mat4x4.Mat4
|
||||||
|
import glm_.vec2.Vec2
|
||||||
|
import glm_.vec3.Vec3
|
||||||
|
import glm_.vec4.Vec4
|
||||||
|
|
||||||
|
class ElementRenderer(element: BlockModelElement, rotation: Vec3, uvlock: Boolean) {
|
||||||
|
private val faces: MutableMap<Directions, BlockModelFace> = element.faces
|
||||||
|
private var positions: Array<Vec3> = element.positions.clone()
|
||||||
|
|
||||||
|
init {
|
||||||
|
BlockModelElement.rotatePositionsAxes(positions, rotation)
|
||||||
|
// TODO : uvlock
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun render(textureMapping: MutableMap<String, Texture>, modelMatrix: Mat4, direction: Directions, rotation: Vec3, data: MutableList<Float>) {
|
||||||
|
val realDirection = BlockModelElement.getRotatedDirection(rotation, direction)
|
||||||
|
val positionTemplate = BlockModelElement.FACE_POSITION_MAP_TEMPLATE[realDirection.ordinal]
|
||||||
|
|
||||||
|
val face = faces[realDirection] ?: return // Not our face
|
||||||
|
val texture = textureMapping[face.textureName] ?: TextureArray.DEBUG_TEXTURE
|
||||||
|
// if (texture.isTransparent) {
|
||||||
|
// return // ToDo: force render transparent faces
|
||||||
|
// }
|
||||||
|
|
||||||
|
val drawPositions = arrayOf(positions[positionTemplate[0]], positions[positionTemplate[1]], positions[positionTemplate[2]], positions[positionTemplate[3]])
|
||||||
|
|
||||||
|
fun addToData(vec3: Vec3, textureCoordinates: Vec2) {
|
||||||
|
val input = Vec4(vec3, 1.0f)
|
||||||
|
val output = modelMatrix * input
|
||||||
|
data.add(output.x)
|
||||||
|
data.add(output.y)
|
||||||
|
data.add(output.z)
|
||||||
|
data.add(textureCoordinates.x * texture.widthFactor)
|
||||||
|
data.add(textureCoordinates.y * texture.heightFactor)
|
||||||
|
data.add(Float.fromBits(texture.id)) // ToDo: Compact this
|
||||||
|
|
||||||
|
// ToDo: Send this only once per texture
|
||||||
|
data.add(texture.animationFrameTime.toFloat())
|
||||||
|
data.add(texture.animations.toFloat())
|
||||||
|
data.add(texture.heightFactor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createQuad(drawPositions: Array<Vec3>, texturePositions: Array<Vec2?>) {
|
||||||
|
addToData(drawPositions[0], texturePositions[1]!!)
|
||||||
|
addToData(drawPositions[3], texturePositions[2]!!)
|
||||||
|
addToData(drawPositions[2], texturePositions[3]!!)
|
||||||
|
addToData(drawPositions[2], texturePositions[3]!!)
|
||||||
|
addToData(drawPositions[1], texturePositions[0]!!)
|
||||||
|
addToData(drawPositions[0], texturePositions[1]!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val texturePositions = face.getTexturePositionArray(realDirection)
|
||||||
|
createQuad(drawPositions, texturePositions)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun createElements(state: JsonObject, mapping: VersionMapping): MutableList<ElementRenderer> {
|
||||||
|
val rotation = glm.radians(vec3InJsonObject(state))
|
||||||
|
val uvlock = state["uvlock"]?.asBoolean ?: false
|
||||||
|
val parentElements = mapping.blockModels[ModIdentifier(state["model"].asString.replace("block/", ""))]!!.elements
|
||||||
|
val result: MutableList<ElementRenderer> = mutableListOf()
|
||||||
|
for (parentElement in parentElements) {
|
||||||
|
result.add(ElementRenderer(parentElement, rotation, uvlock))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun vec3InJsonObject(json: JsonObject): Vec3 {
|
||||||
|
return Vec3(json["x"]?.asFloat?: 0, json["y"]?.asFloat?: 0, json["z"]?.asFloat?: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user