diff --git a/minecraft/1.8/src/main/java/nova/core/wrapper/mc/forge/v18/wrapper/block/backward/BWBlock.java b/minecraft/1.8/src/main/java/nova/core/wrapper/mc/forge/v18/wrapper/block/backward/BWBlock.java index ad19d3f6f..48ebe747d 100644 --- a/minecraft/1.8/src/main/java/nova/core/wrapper/mc/forge/v18/wrapper/block/backward/BWBlock.java +++ b/minecraft/1.8/src/main/java/nova/core/wrapper/mc/forge/v18/wrapper/block/backward/BWBlock.java @@ -22,6 +22,8 @@ import net.minecraft.block.BlockSnow; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.init.Blocks; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; @@ -34,6 +36,7 @@ import nova.core.block.component.BlockProperty; import nova.core.block.component.LightEmitter; import nova.core.component.misc.Collider; +import nova.core.component.renderer.StaticRenderer; import nova.core.component.transform.BlockTransform; import nova.core.item.ItemFactory; import nova.core.retention.Data; @@ -44,6 +47,7 @@ import nova.core.world.World; import nova.core.wrapper.mc.forge.v18.util.WrapperEvent; import nova.core.wrapper.mc.forge.v18.wrapper.block.world.BWWorld; +import nova.core.wrapper.mc.forge.v18.wrapper.render.backward.BWBakedModel; import nova.internal.core.Game; import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; @@ -98,6 +102,29 @@ public BWBlock(net.minecraft.block.Block block, World world, Vector3D pos) { .collect(Collectors.toSet()); }); //TODO: Set selection bounds + components.add(new StaticRenderer()) + .onRender(model -> { + switch (block.getRenderType()) { + default: + // rendering of other type + // TODO + case 1: + // fluid rendering + // TODO + break; + case 2: + // chest rendering + // Handled by DynamicRenderer + break; + case 3: + // model rendering + model.addChild(new BWBakedModel(Minecraft.getMinecraft().getBlockRendererDispatcher() + .getModelFromBlockState(blockState(), getMcBlockAccess(), new BlockPos(x(), y(), z())), DefaultVertexFormats.BLOCK)); + break; + } + }); + // TODO: TileEntity rendering using DynamicRenderer + WrapperEvent.BWBlockCreate event = new WrapperEvent.BWBlockCreate(world, pos, this, mcBlock); Game.events().publish(event); } diff --git a/minecraft/1.8/src/main/java/nova/core/wrapper/mc/forge/v18/wrapper/item/BWItem.java b/minecraft/1.8/src/main/java/nova/core/wrapper/mc/forge/v18/wrapper/item/BWItem.java index bf500470b..44f79f05d 100644 --- a/minecraft/1.8/src/main/java/nova/core/wrapper/mc/forge/v18/wrapper/item/BWItem.java +++ b/minecraft/1.8/src/main/java/nova/core/wrapper/mc/forge/v18/wrapper/item/BWItem.java @@ -20,10 +20,14 @@ package nova.core.wrapper.mc.forge.v18.wrapper.item; +import net.minecraft.client.Minecraft; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import nova.core.component.renderer.StaticRenderer; import nova.core.item.Item; import nova.core.retention.Storable; +import nova.core.wrapper.mc.forge.v18.wrapper.render.backward.BWBakedModel; + /** * @author Stan @@ -42,6 +46,12 @@ public BWItem(net.minecraft.item.Item item, int meta, NBTTagCompound tag) { this.item = item; this.meta = meta; this.tag = tag; + + components.add(new StaticRenderer()) + .onRender(model -> { + model.addChild(new BWBakedModel(Minecraft.getMinecraft().getRenderItem() + .getItemModelMesher().getItemModel(makeItemStack(count())))); + }); } public net.minecraft.item.Item getItem() { diff --git a/minecraft/1.8/src/main/java/nova/core/wrapper/mc/forge/v18/wrapper/render/FWSmartModel.java b/minecraft/1.8/src/main/java/nova/core/wrapper/mc/forge/v18/wrapper/render/FWSmartModel.java index d4b04b9bb..eb86ba752 100644 --- a/minecraft/1.8/src/main/java/nova/core/wrapper/mc/forge/v18/wrapper/render/FWSmartModel.java +++ b/minecraft/1.8/src/main/java/nova/core/wrapper/mc/forge/v18/wrapper/render/FWSmartModel.java @@ -34,8 +34,11 @@ import nova.core.render.model.Vertex; import nova.core.util.Direction; import nova.core.wrapper.mc.forge.v18.render.RenderUtility; +import nova.core.wrapper.mc.forge.v18.wrapper.render.backward.BWBakedModel; import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -84,14 +87,19 @@ protected List modelToQuads(Model modelIn) { .stream() .flatMap( model -> { - if (model instanceof MeshModel) { + if (model instanceof BWBakedModel) { + return Stream.concat(((BWBakedModel) model).getGeneralQuads().stream(), + Arrays.stream(Direction.VALID_DIRECTIONS) + .map(((BWBakedModel) model)::getFaceQuads) + .flatMap(Collection::stream)); + } else if (model instanceof MeshModel) { MeshModel meshModel = (MeshModel) model; return meshModel.faces .stream() .map( face -> { TextureAtlasSprite texture = face.texture.map(RenderUtility.instance::getTexture) - .orElse(Minecraft.getMinecraft().getTextureMapBlocks().getMissingSprite()); + .orElseGet(() -> Minecraft.getMinecraft().getTextureMapBlocks().getMissingSprite()); List vertexData = face.vertices .stream() .map(v -> vertexToInts(v, texture, face.normal)) @@ -137,7 +145,7 @@ public boolean isBuiltInRenderer() { @Override public TextureAtlasSprite getTexture() { - return null; + return Minecraft.getMinecraft().getTextureMapBlocks().getMissingSprite(); } @Override diff --git a/minecraft/1.8/src/main/java/nova/core/wrapper/mc/forge/v18/wrapper/render/backward/BWBakedModel.java b/minecraft/1.8/src/main/java/nova/core/wrapper/mc/forge/v18/wrapper/render/backward/BWBakedModel.java new file mode 100644 index 000000000..64929c29e --- /dev/null +++ b/minecraft/1.8/src/main/java/nova/core/wrapper/mc/forge/v18/wrapper/render/backward/BWBakedModel.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2017 NOVA, All rights reserved. + * This library is free software, licensed under GNU Lesser General Public License version 3 + * + * This file is part of NOVA. + * + * NOVA 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. + * + * NOVA 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 NOVA. If not, see . + */ + +package nova.core.wrapper.mc.forge.v18.wrapper.render.backward; + +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.client.renderer.vertex.VertexFormatElement; +import net.minecraft.client.resources.model.IBakedModel; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import nova.core.render.Color; +import nova.core.render.model.Face; +import nova.core.render.model.MeshModel; +import nova.core.render.model.Model; +import nova.core.render.model.Vertex; +import nova.core.util.Direction; +import nova.core.util.math.MatrixStack; +import nova.core.util.math.TransformUtil; +import nova.core.util.math.Vector3DUtil; +import nova.core.wrapper.mc.forge.v18.wrapper.assets.AssetConverter; +import nova.internal.core.Game; +import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; +import org.apache.commons.math3.geometry.euclidean.twod.Vector2D; +import org.apache.commons.math3.linear.LUDecomposition; +import org.apache.commons.math3.linear.MatrixUtils; +import org.apache.commons.math3.linear.RealMatrix; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author ExE Boss + */ +public class BWBakedModel extends MeshModel { + + @SuppressWarnings("deprecation") + public final IBakedModel wrapped; + + public final VertexFormat format; + + public BWBakedModel(@SuppressWarnings("deprecation") IBakedModel wrapped) { + this(wrapped, DefaultVertexFormats.ITEM); + } + + @SuppressWarnings("unchecked") + public BWBakedModel(@SuppressWarnings("deprecation") IBakedModel wrapped, VertexFormat format) { + this.wrapped = wrapped; + this.format = format; + this.matrix.translate(-0.5, -0.5, -0.5); + + if (!((List) format.getElements()).stream().anyMatch(VertexFormatElement::isPositionElement)) + return; // VertexFormat doesn't have a position + + getGeneralQuads().stream() + .map(this::quadToFace) + .forEachOrdered(faces::add); + + Arrays.stream(Direction.VALID_DIRECTIONS) + .map(this::getFaceQuads) + .flatMap(Collection::stream) + .map(this::quadToFace) + .forEachOrdered(faces::add); + } + + @Override + public Set flatten(MatrixStack matrixStack) { + Set models = new HashSet<>(); + + matrixStack.pushMatrix(); + matrixStack.transform(matrix.getMatrix()); + //Create a new model with transformation applied. + MeshModel transformedModel = clone(); + // correct formula for Normal Matrix is transpose(inverse(mat3(model_mat)) + // we have to augemnt that to 4x4 + RealMatrix normalMatrix3x3 = new LUDecomposition(matrixStack.getMatrix().getSubMatrix(0, 2, 0, 2), 1e-5).getSolver().getInverse().transpose(); + RealMatrix normalMatrix = MatrixUtils.createRealMatrix(4, 4); + normalMatrix.setSubMatrix(normalMatrix3x3.getData(), 0, 0); + normalMatrix.setEntry(3, 3, 1); + + transformedModel.faces.stream().forEach(f -> { + f.normal = TransformUtil.transform(f.normal, normalMatrix); + f.vertices.forEach(v -> v.vec = matrixStack.apply(v.vec)); + } + ); + + models.add(transformedModel); + //Flatten child models + matrixStack.pushMatrix(); + matrixStack.translate(0.5, 0.5, 0.5); + models.addAll(children.stream().flatMap(m -> m.flatten(matrixStack).stream()).collect(Collectors.toSet())); + matrixStack.popMatrix().popMatrix(); + return models; + } + + public List getFaceQuads(Direction direction) { + if (direction == Direction.UNKNOWN) + return getGeneralQuads(); + return getFaceQuads((EnumFacing) Game.natives().toNative(direction)); + } + + @SuppressWarnings("unchecked") + public List getFaceQuads(EnumFacing direction) { + return wrapped.getFaceQuads(direction); + } + + @SuppressWarnings("unchecked") + public List getGeneralQuads() { + return wrapped.getGeneralQuads(); + } + + @SuppressWarnings("unchecked") + public Face quadToFace(BakedQuad quad) { + // TODO: Parse according to format + Face face = new Face(); + final VertexFormat format = this.format; // 1.11.2 stores VertexFormat on a per-quad basis. + + int[] data = quad.getVertexData(); + Optional texture = Optional.ofNullable(wrapped.getTexture()); + + final Optional posElement = ((Collection)format.getElements()).stream() + .filter(VertexFormatElement::isPositionElement) + .findFirst(); + + final Optional uvElement = ((Collection)format.getElements()).stream() + .filter(vfe -> vfe.getUsage() == VertexFormatElement.EnumUsage.UV) + .findFirst(); + + face.texture = texture + .filter(t -> uvElement.isPresent()) + .map(TextureAtlasSprite::getIconName) + .map(ResourceLocation::new) + .map(AssetConverter.instance()::toNovaTexture); + + // `VertexFormat` offsets are for a `ByteBuffer` + // `data` is an int array, so we convert it + + // TODO: support offsets which are not divisible by four + final int posOffset = posElement.map(VertexFormatElement::getOffset).map(i -> i / 4).orElse(-1); + final int uvOffset = uvElement.map(VertexFormatElement::getOffset).map(i -> i / 4).orElse(-1); + final int colorOffset = format.hasColor() ? (format.getColorOffset() / 4) : -1; + final int normalOffset = format.hasNormal() ? (format.getNormalOffset() / 4) : -1; + + for (int i = 0; i < data.length; i += 7) { + Vector3D pos = posElement.isPresent() ? new Vector3D( + Float.intBitsToFloat(data[i + posOffset]), + Float.intBitsToFloat(data[i + posOffset + 1]), + Float.intBitsToFloat(data[i + posOffset + 2])) : Vector3D.ZERO; + + Vector2D uv = uvElement.isPresent() ? new Vector2D( + deinterpolateU(Float.intBitsToFloat(data[i + uvOffset]), texture), + deinterpolateV(Float.intBitsToFloat(data[i + uvOffset + 1]), texture)) : Vector2D.ZERO; + + Vertex vertex = new Vertex(pos, uv); + if (format.hasColor()) { + if (DefaultVertexFormats.BLOCK.equals(format)) + vertex.color = Color.argb(data[i + colorOffset]); + else + vertex.color = Color.rgba(data[i + colorOffset]); + } + + Optional normal = Optional.empty(); + if (format.hasNormal()) { + int mergedNormal = data[i + normalOffset]; + if (mergedNormal != 0) + normal = Optional.of(new Vector3D(((byte)(mergedNormal & 0xFF)) / 127D, + ((byte)((mergedNormal >> 8) & 0xFF)) / 127D, + ((byte)((mergedNormal >> 16) & 0xFF)) / 127D)); + } + + if (format.hasNormal()) + vertex.normal = normal; + face.drawVertex(vertex); + } + face.normal = Vector3DUtil.calculateNormal(face); + return face; + } + + private double deinterpolateU(double u, Optional texture) { + if (!texture.isPresent()) + return u; + TextureAtlasSprite tex = texture.get(); + return (u - tex.getMinU()) / (tex.getMaxU() - tex.getMinU()); + } + + private double deinterpolateV(double v, Optional texture) { + if (!texture.isPresent()) + return v; + TextureAtlasSprite tex = texture.get(); + return (v - tex.getMinV()) / (tex.getMaxV() - tex.getMinV()); + } +}