mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-17 11:24:56 -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.data.mappings.ModIdentifier
|
||||
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 kotlin.random.Random
|
||||
|
||||
@ -23,6 +24,7 @@ data class Block(val identifier: ModIdentifier) {
|
||||
var rotation: BlockRotations = BlockRotations.NONE
|
||||
var properties: Set<BlockProperties> = setOf()
|
||||
val blockModels: MutableList<BlockModel> = mutableListOf()
|
||||
val blockRenderers: MutableList<BlockRenderer> = mutableListOf()
|
||||
|
||||
constructor(identifier: ModIdentifier, properties: Set<BlockProperties>, rotation: BlockRotations) : this(identifier) {
|
||||
this.properties = properties
|
||||
@ -112,11 +114,11 @@ data class Block(val identifier: ModIdentifier) {
|
||||
return String.format("%s%s", identifier, out)
|
||||
}
|
||||
|
||||
fun getBlockModel(position: BlockPosition): BlockModel {
|
||||
fun getBlockRenderer(position: BlockPosition): BlockRenderer {
|
||||
if (Minosoft.getConfig().config.game.other.antiMoirePattern) {
|
||||
// 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.particle.Particle
|
||||
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.util.logging.Log
|
||||
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 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
|
||||
|
||||
@ -379,11 +381,7 @@ class VersionMapping(var version: Version?) {
|
||||
data["parent"]?.asString?.let {
|
||||
parent = loadBlockModel(mod, it, fullModData)
|
||||
}
|
||||
|
||||
model = data["conditional"]?.let {
|
||||
// ToDo
|
||||
return@let BlockModel(parent, data)
|
||||
} ?: BlockModel(parent, data)
|
||||
model = BlockModel(parent, data)
|
||||
|
||||
blockModels[identifier] = model
|
||||
return model
|
||||
@ -396,24 +394,18 @@ class VersionMapping(var version: Version?) {
|
||||
}
|
||||
val blockData = fullModData.getAsJsonObject(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
|
||||
|
||||
for (value in states) {
|
||||
blockData["states"]?.let {
|
||||
for (value in it.asJsonArray) {
|
||||
check(value is JsonObject) { "Invalid model json" }
|
||||
|
||||
val state = loadBlockState(identifier, value)
|
||||
var ckecked = false
|
||||
for (blockState in blockStates) {
|
||||
if (blockState.bareEquals(state)) {
|
||||
for (type in value.getAsJsonArray("types")) {
|
||||
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
|
||||
}
|
||||
@ -424,6 +416,28 @@ class VersionMapping(var version: Version?) {
|
||||
}
|
||||
}
|
||||
}
|
||||
blockData["conditional"]?.let {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun loadBlockState(identifier: ModIdentifier, blockStateJson: JsonObject): Block {
|
||||
@ -466,7 +480,6 @@ class VersionMapping(var version: Version?) {
|
||||
data["id"]?.asInt.let {
|
||||
entityIdClassMap[it] = clazz
|
||||
}
|
||||
|
||||
}
|
||||
var parent: ModIdentifier? = null
|
||||
var metaDataIndexOffset = 0
|
||||
@ -496,7 +509,6 @@ class VersionMapping(var version: Version?) {
|
||||
throw RuntimeException("entities.json is invalid")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
entityMetaIndexOffsetParentMapping[identifier] = Pair(parent, metaDataIndexOffset)
|
||||
}
|
||||
|
@ -84,12 +84,7 @@ class ChunkRenderer(private val connection: Connection, private val world: World
|
||||
} else {
|
||||
section.getBlock(position.getLocationByDirection(Directions.EAST))
|
||||
}
|
||||
|
||||
// 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))
|
||||
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))
|
||||
}
|
||||
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
|
||||
|
||||
for (block in blocks) {
|
||||
for (model in block.blockModels) {
|
||||
for (model in block.blockRenderers) {
|
||||
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.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.chunk.models
|
||||
package de.bixilon.minosoft.gui.rendering.chunk.models.loading
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import de.bixilon.minosoft.data.Directions
|
||||
import de.bixilon.minosoft.data.mappings.blocks.Block
|
||||
import de.bixilon.minosoft.gui.rendering.textures.Texture
|
||||
import glm_.glm
|
||||
import glm_.mat4x4.Mat4
|
||||
import glm_.vec3.Vec3
|
||||
|
||||
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 var elements: MutableList<BlockModelElement> = parent?.elements?.toMutableList() ?: mutableListOf()
|
||||
private val fullFaceDirections: MutableSet<Directions> = parent?.fullFaceDirections?.toMutableSet() ?: mutableSetOf()
|
||||
var elements: MutableList<BlockModelElement> = parent?.elements?.toMutableList() ?: mutableListOf()
|
||||
val fullFaceDirections: MutableSet<Directions> = parent?.fullFaceDirections?.toMutableSet() ?: mutableSetOf()
|
||||
private var rotation: Vec3
|
||||
private var uvLock = 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 {
|
||||
var currentValue: String = type
|
||||
|
||||
while (currentValue.startsWith("#")) {
|
||||
textures[currentValue.removePrefix("#")].let {
|
||||
if (it == null) {
|
||||
@ -116,7 +80,6 @@ open class BlockModel(val parent: BlockModel? = null, json: JsonObject) {
|
||||
currentValue = it
|
||||
}
|
||||
}
|
||||
|
||||
return currentValue
|
||||
}
|
||||
|
||||
@ -129,25 +92,6 @@ open class BlockModel(val parent: BlockModel? = null, json: JsonObject) {
|
||||
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 {
|
||||
for (element in elements) {
|
||||
if (textureMapping[element.getTexture(direction)]?.isTransparent == true) {
|
||||
@ -157,16 +101,4 @@ open class BlockModel(val parent: BlockModel? = null, json: JsonObject) {
|
||||
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