-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement 1.8 BWBlock and BWItem rendering
- Loading branch information
Showing
3 changed files
with
256 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
219 changes: 219 additions & 0 deletions
219
....8/src/main/java/nova/core/wrapper/mc/forge/v18/wrapper/render/backward/BWBakedModel.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
/* | ||
* 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 <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
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<VertexFormatElement>) 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<Model> flatten(MatrixStack matrixStack) { | ||
Set<Model> 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); | ||
v.normal = v.normal.map(n -> TransformUtil.transform(n, normalMatrix)); | ||
}); | ||
} | ||
); | ||
|
||
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<BakedQuad> getFaceQuads(Direction direction) { | ||
if (direction == Direction.UNKNOWN) | ||
return getGeneralQuads(); | ||
return getFaceQuads((EnumFacing) Game.natives().toNative(direction)); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
public List<BakedQuad> getFaceQuads(EnumFacing direction) { | ||
return wrapped.getFaceQuads(direction); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
public List<BakedQuad> getGeneralQuads() { | ||
return wrapped.getGeneralQuads(); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
public Face quadToFace(BakedQuad quad) { | ||
Face face = new Face(); | ||
final VertexFormat format = this.format; // 1.11.2 stores VertexFormat on a per-quad basis. | ||
|
||
int[] data = quad.getVertexData(); | ||
Optional<TextureAtlasSprite> texture = Optional.ofNullable(wrapped.getTexture()); | ||
|
||
final Optional<VertexFormatElement> posElement = ((Collection<VertexFormatElement>)format.getElements()).stream() | ||
.filter(VertexFormatElement::isPositionElement) | ||
.findFirst(); | ||
|
||
final Optional<VertexFormatElement> uvElement = ((Collection<VertexFormatElement>)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 the offsets | ||
|
||
// 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<Vector3D> 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<TextureAtlasSprite> texture) { | ||
if (!texture.isPresent()) | ||
return u; | ||
TextureAtlasSprite tex = texture.get(); | ||
return (u - tex.getMinU()) / (tex.getMaxU() - tex.getMinU()); | ||
} | ||
|
||
private double deinterpolateV(double v, Optional<TextureAtlasSprite> texture) { | ||
if (!texture.isPresent()) | ||
return v; | ||
TextureAtlasSprite tex = texture.get(); | ||
return (v - tex.getMinV()) / (tex.getMaxV() - tex.getMinV()); | ||
} | ||
} |