Model pipeline system.

Should replace all ad-hoc quad generation methods in forge, and make IBakedModel -> WorldRenderer data transfer faster. Added IVertexConsumer + helper classes; lighting that works correctly for non-axis-aligned faces using the new infrastructure. Changed smooth lighting algorithm, now it should work correctly for everything.
New block lighter can be disabled in the forge config options.
This commit is contained in:
RainWarrior 2015-06-29 01:19:10 +03:00
parent ebe9b6d4cb
commit dd365d7220
29 changed files with 1518 additions and 204 deletions

View file

@ -1,5 +1,14 @@
--- ../src-base/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java --- ../src-base/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java
+++ ../src-work/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java +++ ../src-work/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java
@@ -24,7 +24,7 @@
{
private BlockModelShapes field_175028_a;
private final GameSettings field_175026_b;
- private final BlockModelRenderer field_175027_c = new BlockModelRenderer();
+ private final BlockModelRenderer field_175027_c = new net.minecraftforge.client.model.pipeline.ForgeBlockModelRenderer();
private final ChestRenderer field_175024_d = new ChestRenderer();
private final BlockFluidRenderer field_175025_e = new BlockFluidRenderer();
private static final String __OBFID = "CL_00002520";
@@ -49,6 +49,24 @@ @@ -49,6 +49,24 @@
{ {
p_175020_1_ = block.func_176221_a(p_175020_1_, p_175020_4_, p_175020_2_); p_175020_1_ = block.func_176221_a(p_175020_1_, p_175020_4_, p_175020_2_);

View file

@ -0,0 +1,17 @@
--- ../src-base/minecraft/net/minecraft/client/renderer/WorldRenderer.java
+++ ../src-work/minecraft/net/minecraft/client/renderer/WorldRenderer.java
@@ -520,6 +520,14 @@
}
}
+ public void checkAndGrow()
+ {
+ if (this.field_179008_i >= this.field_179009_s - this.field_179011_q.func_177338_f())
+ {
+ this.func_178983_e(2097152);
+ }
+ }
+
@SideOnly(Side.CLIENT)
public class State
{

View file

@ -0,0 +1,13 @@
--- ../src-base/minecraft/net/minecraft/client/renderer/block/model/BakedQuad.java
+++ ../src-work/minecraft/net/minecraft/client/renderer/block/model/BakedQuad.java
@@ -5,8 +5,9 @@
import net.minecraftforge.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
-public class BakedQuad
+public class BakedQuad implements net.minecraftforge.client.model.pipeline.IVertexProducer
{
+ @Override public void pipe(net.minecraftforge.client.model.pipeline.IVertexConsumer consumer) { net.minecraftforge.client.model.pipeline.LightUtil.putBakedQuad(consumer, this); }
protected final int[] field_178215_a;
protected final int field_178213_b;
protected final EnumFacing field_178214_c;

View file

@ -14,7 +14,13 @@
EnumFacing enumfacing1 = func_178410_a(aint); EnumFacing enumfacing1 = func_178410_a(aint);
if (p_178414_8_) if (p_178414_8_)
@@ -40,11 +45,16 @@ @@ -35,16 +40,22 @@
this.func_178408_a(aint, enumfacing1);
}
+ net.minecraftforge.client.ForgeHooksClient.fillNormal(aint, enumfacing1);
return new BakedQuad(aint, p_178414_3_.field_178245_c, enumfacing1);
}
private int[] func_178405_a(BlockPartFace p_178405_1_, TextureAtlasSprite p_178405_2_, EnumFacing p_178405_3_, float[] p_178405_4_, ModelRotation p_178405_5_, BlockPartRotation p_178405_6_, boolean p_178405_7_, boolean p_178405_8_) private int[] func_178405_a(BlockPartFace p_178405_1_, TextureAtlasSprite p_178405_2_, EnumFacing p_178405_3_, float[] p_178405_4_, ModelRotation p_178405_5_, BlockPartRotation p_178405_6_, boolean p_178405_7_, boolean p_178405_8_)
{ {
@ -32,7 +38,7 @@
} }
return aint; return aint;
@@ -90,12 +100,17 @@ @@ -90,12 +101,17 @@
private void func_178402_a(int[] p_178402_1_, int p_178402_2_, EnumFacing p_178402_3_, BlockPartFace p_178402_4_, float[] p_178402_5_, TextureAtlasSprite p_178402_6_, ModelRotation p_178402_7_, BlockPartRotation p_178402_8_, boolean p_178402_9_, boolean p_178402_10_) private void func_178402_a(int[] p_178402_1_, int p_178402_2_, EnumFacing p_178402_3_, BlockPartFace p_178402_4_, float[] p_178402_5_, TextureAtlasSprite p_178402_6_, ModelRotation p_178402_7_, BlockPartRotation p_178402_8_, boolean p_178402_9_, boolean p_178402_10_)
{ {
@ -52,7 +58,7 @@
this.func_178404_a(p_178402_1_, k, p_178402_2_, vector3d, j, p_178402_6_, p_178402_4_.field_178243_e); this.func_178404_a(p_178402_1_, k, p_178402_2_, vector3d, j, p_178402_6_, p_178402_4_.field_178243_e);
} }
@@ -156,14 +171,19 @@ @@ -156,14 +172,19 @@
public int func_178415_a(Vector3d p_178415_1_, EnumFacing p_178415_2_, int p_178415_3_, ModelRotation p_178415_4_, boolean p_178415_5_) public int func_178415_a(Vector3d p_178415_1_, EnumFacing p_178415_2_, int p_178415_3_, ModelRotation p_178415_4_, boolean p_178415_5_)
{ {

View file

@ -9,17 +9,16 @@
this.func_175041_b(); this.func_175041_b();
} }
@@ -212,6 +212,9 @@ @@ -222,7 +222,7 @@
private void func_175033_a(WorldRenderer p_175033_1_, BakedQuad p_175033_2_, int p_175033_3_) BakedQuad bakedquad;
{ int j;
p_175033_1_.func_178981_a(p_175033_2_.func_178209_a());
+ if(p_175033_2_ instanceof net.minecraftforge.client.model.IColoredBakedQuad) - for (Iterator iterator = p_175032_2_.iterator(); iterator.hasNext(); this.func_175033_a(p_175032_1_, bakedquad, j))
+ net.minecraftforge.client.ForgeHooksClient.putQuadColor(p_175033_1_, p_175033_2_, p_175033_3_); + for (Iterator iterator = p_175032_2_.iterator(); iterator.hasNext(); net.minecraftforge.client.model.pipeline.LightUtil.renderQuadColor(p_175032_1_, bakedquad, j))
+ else {
p_175033_1_.func_178968_d(p_175033_3_); bakedquad = (BakedQuad)iterator.next();
this.func_175038_a(p_175033_1_, p_175033_2_); j = p_175032_3_;
} @@ -302,6 +302,10 @@
@@ -302,6 +305,10 @@
modelresourcelocation = new ModelResourceLocation("bow_pulling_0", "inventory"); modelresourcelocation = new ModelResourceLocation("bow_pulling_0", "inventory");
} }
} }
@ -30,7 +29,7 @@
if (modelresourcelocation != null) if (modelresourcelocation != null)
{ {
@@ -314,6 +321,11 @@ @@ -314,6 +318,11 @@
protected void func_175034_a(ItemTransformVec3f p_175034_1_) protected void func_175034_a(ItemTransformVec3f p_175034_1_)
{ {
@ -42,7 +41,7 @@
if (p_175034_1_ != ItemTransformVec3f.field_178366_a) if (p_175034_1_ != ItemTransformVec3f.field_178366_a)
{ {
GlStateManager.func_179109_b(p_175034_1_.field_178365_c.x + field_175055_b, p_175034_1_.field_178365_c.y + field_175056_c, p_175034_1_.field_178365_c.z + field_175053_d); GlStateManager.func_179109_b(p_175034_1_.field_178365_c.x + field_175055_b, p_175034_1_.field_178365_c.y + field_175056_c, p_175034_1_.field_178365_c.z + field_175053_d);
@@ -335,23 +347,7 @@ @@ -335,23 +344,7 @@
GlStateManager.func_179120_a(770, 771, 1, 0); GlStateManager.func_179120_a(770, 771, 1, 0);
GlStateManager.func_179094_E(); GlStateManager.func_179094_E();
@ -67,7 +66,7 @@
this.func_180454_a(p_175040_1_, p_175040_2_); this.func_180454_a(p_175040_1_, p_175040_2_);
GlStateManager.func_179121_F(); GlStateManager.func_179121_F();
@@ -374,7 +370,7 @@ @@ -374,7 +367,7 @@
GlStateManager.func_179112_b(770, 771); GlStateManager.func_179112_b(770, 771);
GlStateManager.func_179131_c(1.0F, 1.0F, 1.0F, 1.0F); GlStateManager.func_179131_c(1.0F, 1.0F, 1.0F, 1.0F);
this.func_180452_a(p_175042_2_, p_175042_3_, ibakedmodel.func_177556_c()); this.func_180452_a(p_175042_2_, p_175042_3_, ibakedmodel.func_177556_c());
@ -76,7 +75,7 @@
this.func_180454_a(p_175042_1_, ibakedmodel); this.func_180454_a(p_175042_1_, ibakedmodel);
GlStateManager.func_179118_c(); GlStateManager.func_179118_c();
GlStateManager.func_179101_C(); GlStateManager.func_179101_C();
@@ -485,10 +481,11 @@ @@ -485,10 +478,11 @@
GlStateManager.func_179126_j(); GlStateManager.func_179126_j();
} }
@ -91,7 +90,7 @@
GlStateManager.func_179140_f(); GlStateManager.func_179140_f();
GlStateManager.func_179097_i(); GlStateManager.func_179097_i();
GlStateManager.func_179090_x(); GlStateManager.func_179090_x();
@@ -501,7 +498,7 @@ @@ -501,7 +495,7 @@
this.func_175044_a(worldrenderer, p_180453_3_ + 2, p_180453_4_ + 13, 13, 2, 0); this.func_175044_a(worldrenderer, p_180453_3_ + 2, p_180453_4_ + 13, 13, 2, 0);
this.func_175044_a(worldrenderer, p_180453_3_ + 2, p_180453_4_ + 13, 12, 1, i1); this.func_175044_a(worldrenderer, p_180453_3_ + 2, p_180453_4_ + 13, 12, 1, i1);
this.func_175044_a(worldrenderer, p_180453_3_ + 2, p_180453_4_ + 13, j1, 1, l); this.func_175044_a(worldrenderer, p_180453_3_ + 2, p_180453_4_ + 13, j1, 1, l);
@ -100,7 +99,7 @@
GlStateManager.func_179141_d(); GlStateManager.func_179141_d();
GlStateManager.func_179098_w(); GlStateManager.func_179098_w();
GlStateManager.func_179145_e(); GlStateManager.func_179145_e();
@@ -1072,12 +1069,26 @@ @@ -1072,12 +1066,26 @@
this.func_175029_a(Blocks.field_150420_aW, BlockHugeMushroom.EnumType.ALL_INSIDE.func_176896_a(), "brown_mushroom_block"); this.func_175029_a(Blocks.field_150420_aW, BlockHugeMushroom.EnumType.ALL_INSIDE.func_176896_a(), "brown_mushroom_block");
this.func_175029_a(Blocks.field_150419_aX, BlockHugeMushroom.EnumType.ALL_INSIDE.func_176896_a(), "red_mushroom_block"); this.func_175029_a(Blocks.field_150419_aX, BlockHugeMushroom.EnumType.ALL_INSIDE.func_176896_a(), "red_mushroom_block");
this.func_175031_a(Blocks.field_150380_bt, "dragon_egg"); this.func_175031_a(Blocks.field_150380_bt, "dragon_egg");

View file

@ -52,6 +52,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockPos; import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumWorldBlockLayer; import net.minecraft.util.EnumWorldBlockLayer;
import net.minecraft.util.IRegistry; import net.minecraft.util.IRegistry;
import net.minecraft.util.MovingObjectPosition; import net.minecraft.util.MovingObjectPosition;
@ -84,6 +85,7 @@ import org.lwjgl.opengl.GL11;
//import static net.minecraftforge.client.IItemRenderer.ItemRenderType.*; //import static net.minecraftforge.client.IItemRenderer.ItemRenderType.*;
//import static net.minecraftforge.client.IItemRenderer.ItemRendererHelper.*; //import static net.minecraftforge.client.IItemRenderer.ItemRendererHelper.*;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
public class ForgeHooksClient public class ForgeHooksClient
@ -682,7 +684,7 @@ public class ForgeHooksClient
int ncg = Math.min(0xFF, (int)(cg * vcg / 0xFF)); int ncg = Math.min(0xFF, (int)(cg * vcg / 0xFF));
int ncb = Math.min(0xFF, (int)(cb * vcb / 0xFF)); int ncb = Math.min(0xFF, (int)(cb * vcb / 0xFF));
int nca = Math.min(0xFF, (int)(ca * vca / 0xFF)); int nca = Math.min(0xFF, (int)(ca * vca / 0xFF));
renderer.putColorRGBA(renderer.getColorIndex(i + 1), ncr, ncg, ncb, nca); renderer.putColorRGBA(renderer.getColorIndex(4 - i), ncr, ncg, ncb, nca);
} }
} }
@ -710,4 +712,15 @@ public class ForgeHooksClient
{ {
tileItemMap.put(Pair.of(item, metadata), TileClass); tileItemMap.put(Pair.of(item, metadata), TileClass);
} }
public static void fillNormal(int[] faceData, EnumFacing facing)
{
int x = ((byte)(facing.getFrontOffsetX() * 127)) & 0xFF;
int y = ((byte)(facing.getFrontOffsetY() * 127)) & 0xFF;
int z = ((byte)(facing.getFrontOffsetZ() * 127)) & 0xFF;
for(int i = 0; i < 4; i++)
{
faceData[i * 7 + 6] = x | (y << 0x08) | (z << 0x10);
}
}
} }

View file

@ -74,6 +74,10 @@ public class Attributes
return true; return true;
} }
/**
* @deprecated use UnpackedBakedQuad.Builder
*/
@Deprecated
public static void put(ByteBuffer buf, VertexFormatElement e, boolean denormalize, Number fill, Number... ns) public static void put(ByteBuffer buf, VertexFormatElement e, boolean denormalize, Number fill, Number... ns)
{ {
if(e.getElementCount() > ns.length && fill == null) throw new IllegalArgumentException("not enough elements"); if(e.getElementCount() > ns.length && fill == null) throw new IllegalArgumentException("not enough elements");
@ -109,6 +113,10 @@ public class Attributes
} }
} }
/**
* @deprecated use IVertexConsumer
*/
@Deprecated
public static BakedQuad transform(TRSRTransformation transform, BakedQuad quad, VertexFormat format) public static BakedQuad transform(TRSRTransformation transform, BakedQuad quad, VertexFormat format)
{ {
for (VertexFormatElement e : (List<VertexFormatElement>)format.getElements()) for (VertexFormatElement e : (List<VertexFormatElement>)format.getElements())

View file

@ -6,11 +6,8 @@ import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import javax.vecmath.Matrix4f;
import net.minecraft.client.renderer.block.model.ModelBlockDefinition; import net.minecraft.client.renderer.block.model.ModelBlockDefinition;
import net.minecraft.client.resources.model.ModelRotation; import net.minecraft.client.resources.model.ModelRotation;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;

View file

@ -12,7 +12,6 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import javax.vecmath.AxisAngle4d; import javax.vecmath.AxisAngle4d;
import javax.vecmath.AxisAngle4f;
import javax.vecmath.Matrix4f; import javax.vecmath.Matrix4f;
import javax.vecmath.Quat4f; import javax.vecmath.Quat4f;
import javax.vecmath.Vector3f; import javax.vecmath.Vector3f;
@ -21,8 +20,8 @@ import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformT
import net.minecraft.client.resources.model.ModelRotation; import net.minecraft.client.resources.model.ModelRotation;
import net.minecraft.util.JsonUtils; import net.minecraft.util.JsonUtils;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.BlockStateLoader.SubModel;
import net.minecraftforge.client.model.BlockStateLoader.Marker; import net.minecraftforge.client.model.BlockStateLoader.Marker;
import net.minecraftforge.client.model.BlockStateLoader.SubModel;
import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.common.FMLLog;
import com.google.common.base.Optional; import com.google.common.base.Optional;

View file

@ -1,7 +1,5 @@
package net.minecraftforge.client.model; package net.minecraftforge.client.model;
import javax.vecmath.Matrix4f;
import com.google.common.base.Function; import com.google.common.base.Function;
/* /*

View file

@ -1,6 +1,5 @@
package net.minecraftforge.client.model; package net.minecraftforge.client.model;
import net.minecraft.client.renderer.block.model.ModelBlock;
public interface ISmartVariant public interface ISmartVariant
{ {

View file

@ -1,12 +1,12 @@
package net.minecraftforge.client.model; package net.minecraftforge.client.model;
import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.vecmath.Matrix4f; import javax.vecmath.Matrix4f;
import javax.vecmath.Vector4f;
import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms; import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
@ -14,19 +14,17 @@ import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformT
import net.minecraft.client.renderer.block.model.ModelBlock; import net.minecraft.client.renderer.block.model.ModelBlock;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.client.resources.IResourceManager; import net.minecraft.client.resources.IResourceManager;
import net.minecraft.client.resources.model.IBakedModel; import net.minecraft.client.resources.model.IBakedModel;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.lwjgl.BufferUtils;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
public class ItemLayerModel implements IRetexturableModel { public class ItemLayerModel implements IRetexturableModel {
@ -90,13 +88,7 @@ public class ItemLayerModel implements IRetexturableModel {
for(int i = 0; i < textures.size(); i++) for(int i = 0; i < textures.size(); i++)
{ {
TextureAtlasSprite sprite = bakedTextureGetter.apply(textures.get(i)); TextureAtlasSprite sprite = bakedTextureGetter.apply(textures.get(i));
builder.addAll(Iterables.transform(getQuadsForSprite(i, sprite, format), new Function<BakedQuad, BakedQuad>() builder.addAll(getQuadsForSprite(i, sprite, format, transform));
{
public BakedQuad apply(BakedQuad input)
{
return Attributes.transform(transform, input, format);
}
}));
} }
TextureAtlasSprite particle = bakedTextureGetter.apply(textures.isEmpty() ? new ResourceLocation("missingno") : textures.get(0)); TextureAtlasSprite particle = bakedTextureGetter.apply(textures.isEmpty() ? new ResourceLocation("missingno") : textures.get(0));
if(state instanceof IPerspectiveState) if(state instanceof IPerspectiveState)
@ -146,21 +138,18 @@ public class ItemLayerModel implements IRetexturableModel {
{ {
TRSRTransformation tr = transforms.get(cameraTransformType); TRSRTransformation tr = transforms.get(cameraTransformType);
Matrix4f mat = null; Matrix4f mat = null;
if(tr != null && tr != TRSRTransformation.identity()) mat = tr.blockCornerToCenter(tr).getMatrix(); if(tr != null && tr != TRSRTransformation.identity()) mat = TRSRTransformation.blockCornerToCenter(tr).getMatrix();
return Pair.of((IBakedModel)this, mat); return Pair.of((IBakedModel)this, mat);
} }
} }
public ImmutableList<BakedQuad> getQuadsForSprite(int tint, TextureAtlasSprite sprite, VertexFormat format) public ImmutableList<BakedQuad> getQuadsForSprite(int tint, TextureAtlasSprite sprite, VertexFormat format, TRSRTransformation transform)
{ {
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder(); ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
int uMax = sprite.getIconWidth(); int uMax = sprite.getIconWidth();
int vMax = sprite.getIconHeight(); int vMax = sprite.getIconHeight();
ByteBuffer buf = BufferUtils.createByteBuffer(4 * format.getNextOffset());
int[] data;
for(int f = 0; f < sprite.getFrameCount(); f++) for(int f = 0; f < sprite.getFrameCount(); f++)
{ {
int[] pixels = sprite.getFrameTextureData(f)[0]; int[] pixels = sprite.getFrameTextureData(f)[0];
@ -175,26 +164,26 @@ public class ItemLayerModel implements IRetexturableModel {
boolean t = isTransparent(pixels, uMax, vMax, u, v); boolean t = isTransparent(pixels, uMax, vMax, u, v);
if(ptu && !t) // left - transparent, right - opaque if(ptu && !t) // left - transparent, right - opaque
{ {
builder.add(buildSideQuad(buf, format, EnumFacing.WEST, tint, sprite, u, v)); builder.add(buildSideQuad(format, transform, EnumFacing.WEST, tint, sprite, u, v));
} }
if(!ptu && t) // left - opaque, right - transparent if(!ptu && t) // left - opaque, right - transparent
{ {
builder.add(buildSideQuad(buf, format, EnumFacing.EAST, tint, sprite, u, v)); builder.add(buildSideQuad(format, transform, EnumFacing.EAST, tint, sprite, u, v));
} }
if(ptv[u] && !t) // up - transparent, down - opaque if(ptv[u] && !t) // up - transparent, down - opaque
{ {
builder.add(buildSideQuad(buf, format, EnumFacing.UP, tint, sprite, u, v)); builder.add(buildSideQuad(format, transform, EnumFacing.UP, tint, sprite, u, v));
} }
if(!ptv[u] && t) // up - opaque, down - transparent if(!ptv[u] && t) // up - opaque, down - transparent
{ {
builder.add(buildSideQuad(buf, format, EnumFacing.DOWN, tint, sprite, u, v)); builder.add(buildSideQuad(format, transform, EnumFacing.DOWN, tint, sprite, u, v));
} }
ptu = t; ptu = t;
ptv[u] = t; ptv[u] = t;
} }
if(!ptu) // last - opaque if(!ptu) // last - opaque
{ {
builder.add(buildSideQuad(buf, format, EnumFacing.EAST, tint, sprite, uMax, v)); builder.add(buildSideQuad(format, transform, EnumFacing.EAST, tint, sprite, uMax, v));
} }
} }
// last line // last line
@ -202,19 +191,19 @@ public class ItemLayerModel implements IRetexturableModel {
{ {
if(!ptv[u]) if(!ptv[u])
{ {
builder.add(buildSideQuad(buf, format, EnumFacing.DOWN, tint, sprite, u, vMax)); builder.add(buildSideQuad(format, transform, EnumFacing.DOWN, tint, sprite, u, vMax));
} }
} }
} }
// front // front
builder.add(buildQuad(buf, format, EnumFacing.SOUTH, tint, builder.add(buildQuad(format, transform, EnumFacing.SOUTH, tint,
0, 0, 7.5f / 16f, sprite.getMinU(), sprite.getMaxV(), 0, 0, 7.5f / 16f, sprite.getMinU(), sprite.getMaxV(),
0, 1, 7.5f / 16f, sprite.getMinU(), sprite.getMinV(), 0, 1, 7.5f / 16f, sprite.getMinU(), sprite.getMinV(),
1, 1, 7.5f / 16f, sprite.getMaxU(), sprite.getMinV(), 1, 1, 7.5f / 16f, sprite.getMaxU(), sprite.getMinV(),
1, 0, 7.5f / 16f, sprite.getMaxU(), sprite.getMaxV() 1, 0, 7.5f / 16f, sprite.getMaxU(), sprite.getMaxV()
)); ));
// back // back
builder.add(buildQuad(buf, format, EnumFacing.NORTH, tint, builder.add(buildQuad(format, transform, EnumFacing.NORTH, tint,
0, 0, 8.5f / 16f, sprite.getMinU(), sprite.getMaxV(), 0, 0, 8.5f / 16f, sprite.getMinU(), sprite.getMaxV(),
1, 0, 8.5f / 16f, sprite.getMaxU(), sprite.getMaxV(), 1, 0, 8.5f / 16f, sprite.getMaxU(), sprite.getMaxV(),
1, 1, 8.5f / 16f, sprite.getMaxU(), sprite.getMinV(), 1, 1, 8.5f / 16f, sprite.getMaxU(), sprite.getMinV(),
@ -228,7 +217,7 @@ public class ItemLayerModel implements IRetexturableModel {
return (pixels[u + (vMax - 1 - v) * uMax] >> 24 & 0xFF) == 0; return (pixels[u + (vMax - 1 - v) * uMax] >> 24 & 0xFF) == 0;
} }
private static BakedQuad buildSideQuad(ByteBuffer buf, VertexFormat format, EnumFacing side, int tint, TextureAtlasSprite sprite, int u, int v) private static BakedQuad buildSideQuad(VertexFormat format, TRSRTransformation transform, EnumFacing side, int tint, TextureAtlasSprite sprite, int u, int v)
{ {
float x0 = (float)u / sprite.getIconWidth(); float x0 = (float)u / sprite.getIconWidth();
float y0 = (float)v / sprite.getIconHeight(); float y0 = (float)v / sprite.getIconHeight();
@ -256,7 +245,7 @@ public class ItemLayerModel implements IRetexturableModel {
float v0 = 16f * (1f - y0 - side.getDirectionVec().getY() * 1e-2f / sprite.getIconHeight()); float v0 = 16f * (1f - y0 - side.getDirectionVec().getY() * 1e-2f / sprite.getIconHeight());
float v1 = 16f * (1f - y1 - side.getDirectionVec().getY() * 1e-2f / sprite.getIconHeight()); float v1 = 16f * (1f - y1 - side.getDirectionVec().getY() * 1e-2f / sprite.getIconHeight());
return buildQuad( return buildQuad(
buf, format, side.getOpposite(), tint, // getOpposite is related either to the swapping of V direction, or something else format, transform, side.getOpposite(), tint, // getOpposite is related either to the swapping of V direction, or something else
x0, y0, z1, sprite.getInterpolatedU(u0), sprite.getInterpolatedV(v0), x0, y0, z1, sprite.getInterpolatedU(u0), sprite.getInterpolatedV(v0),
x1, y1, z1, sprite.getInterpolatedU(u1), sprite.getInterpolatedV(v1), x1, y1, z1, sprite.getInterpolatedU(u1), sprite.getInterpolatedV(v1),
x1, y1, z2, sprite.getInterpolatedU(u1), sprite.getInterpolatedV(v1), x1, y1, z2, sprite.getInterpolatedU(u1), sprite.getInterpolatedV(v1),
@ -265,48 +254,50 @@ public class ItemLayerModel implements IRetexturableModel {
} }
private static final BakedQuad buildQuad( private static final BakedQuad buildQuad(
ByteBuffer buf, VertexFormat format, EnumFacing side, int tint, VertexFormat format, TRSRTransformation transform, EnumFacing side, int tint,
float x0, float y0, float z0, float u0, float v0, float x0, float y0, float z0, float u0, float v0,
float x1, float y1, float z1, float u1, float v1, float x1, float y1, float z1, float u1, float v1,
float x2, float y2, float z2, float u2, float v2, float x2, float y2, float z2, float u2, float v2,
float x3, float y3, float z3, float u3, float v3) float x3, float y3, float z3, float u3, float v3)
{ {
buf.clear(); UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format);
putVertex(buf, format, side, x0, y0, z0, u0, v0); builder.setQuadTint(tint);
putVertex(buf, format, side, x1, y1, z1, u1, v1); builder.setQuadOrientation(side);
putVertex(buf, format, side, x2, y2, z2, u2, v2); putVertex(builder, format, transform, side, x0, y0, z0, u0, v0);
putVertex(buf, format, side, x3, y3, z3, u3, v3); putVertex(builder, format, transform, side, x1, y1, z1, u1, v1);
buf.flip(); putVertex(builder, format, transform, side, x2, y2, z2, u2, v2);
int[] data = new int[4 * format.getNextOffset() / 4]; putVertex(builder, format, transform, side, x3, y3, z3, u3, v3);
buf.asIntBuffer().get(data); return builder.build();
return new BakedQuad(data, tint, side);
} }
private static void put(ByteBuffer buf, VertexFormatElement e, Float... fs) private static void putVertex(UnpackedBakedQuad.Builder builder, VertexFormat format, TRSRTransformation transform, EnumFacing side, float x, float y, float z, float u, float v)
{ {
Attributes.put(buf, e, true, 0f, fs); Vector4f vec = new Vector4f();
} for(int e = 0; e < format.getElementCount(); e++)
private static void putVertex(ByteBuffer buf, VertexFormat format, EnumFacing side, float x, float y, float z, float u, float v)
{
for(VertexFormatElement e : (List<VertexFormatElement>)format.getElements())
{ {
switch(e.getUsage()) switch(format.getElement(e).getUsage())
{ {
case POSITION: case POSITION:
put(buf, e, x, y, z, 1f); vec.x = x;
vec.y = y;
vec.z = z;
vec.w = 1;
transform.getMatrix().transform(vec);
builder.put(e, vec.x, vec.y, vec.z, vec.w);
break; break;
case COLOR: case COLOR:
put(buf, e, 1f, 1f, 1f, 1f); builder.put(e, 1f, 1f, 1f, 1f);
break; break;
case UV: case UV: if(format.getElement(e).getIndex() == 0)
put(buf, e, u, v, 0f, 1f); {
builder.put(e, u, v, 0f, 1f);
break; break;
}
case NORMAL: case NORMAL:
put(buf, e, (float)side.getFrontOffsetX(), (float)side.getFrontOffsetY(), (float)side.getFrontOffsetZ(), 0f); builder.put(e, (float)side.getFrontOffsetX(), (float)side.getFrontOffsetY(), (float)side.getFrontOffsetZ(), 0f);
break; break;
default: default:
put(buf, e); builder.put(e);
break; break;
} }
} }

View file

@ -1,6 +1,5 @@
package net.minecraftforge.client.model; package net.minecraftforge.client.model;
import java.nio.ByteBuffer;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.EnumMap; import java.util.EnumMap;
@ -11,21 +10,20 @@ import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms; import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.client.resources.IResourceManager; import net.minecraft.client.resources.IResourceManager;
import net.minecraft.client.resources.model.IBakedModel; import net.minecraft.client.resources.model.IBakedModel;
import net.minecraft.client.resources.model.ModelRotation; import net.minecraft.client.resources.model.ModelRotation;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.MathHelper; import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.pipeline.LightUtil;
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.fluids.BlockFluidBase; import net.minecraftforge.fluids.BlockFluidBase;
import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.common.FMLLog;
import org.lwjgl.BufferUtils;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -115,9 +113,6 @@ public class ModelFluid implements IModelCustomData
this.gas = gas; this.gas = gas;
this.state = stateOption; this.state = stateOption;
ByteBuffer buf = BufferUtils.createByteBuffer(4 * format.getNextOffset());
int[] data;
faceQuads = Maps.newEnumMap(EnumFacing.class); faceQuads = Maps.newEnumMap(EnumFacing.class);
for(EnumFacing side : EnumFacing.values()) for(EnumFacing side : EnumFacing.values())
{ {
@ -157,36 +152,34 @@ public class ModelFluid implements IModelCustomData
float s = MathHelper.sin(flow) * scale; float s = MathHelper.sin(flow) * scale;
EnumFacing side = gas ? EnumFacing.DOWN : EnumFacing.UP; EnumFacing side = gas ? EnumFacing.DOWN : EnumFacing.UP;
buf.clear(); UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format);
builder.setQuadOrientation(side);
builder.setQuadColored();
for(int i = gas ? 3 : 0; i != (gas ? -1 : 4); i+= (gas ? -1 : 1)) for(int i = gas ? 3 : 0; i != (gas ? -1 : 4); i+= (gas ? -1 : 1))
{ {
putVertex( putVertex(
buf, side, builder, side,
x[i], y[i], z[i], x[i], y[i], z[i],
topSprite.getInterpolatedU(8 + c * (x[i] * 2 - 1) + s * (z[i] * 2 - 1)), topSprite.getInterpolatedU(8 + c * (x[i] * 2 - 1) + s * (z[i] * 2 - 1)),
topSprite.getInterpolatedV(8 + c * (x[(i + 1) % 4] * 2 - 1) + s * (z[(i + 1) % 4] * 2 - 1))); topSprite.getInterpolatedV(8 + c * (x[(i + 1) % 4] * 2 - 1) + s * (z[(i + 1) % 4] * 2 - 1)));
} }
buf.flip(); faceQuads.put(side, ImmutableList.<BakedQuad>of(builder.build()));
data = new int[4 * format.getNextOffset() / 4];
buf.asIntBuffer().get(data);
faceQuads.put(side, ImmutableList.<BakedQuad>of(new IColoredBakedQuad.ColoredBakedQuad(data, -1, side)));
// bottom // bottom
side = side.getOpposite(); side = side.getOpposite();
buf.clear(); builder = new UnpackedBakedQuad.Builder(format);
builder.setQuadOrientation(side);
builder.setQuadColored();
for(int i = gas ? 3 : 0; i != (gas ? -1 : 4); i+= (gas ? -1 : 1)) for(int i = gas ? 3 : 0; i != (gas ? -1 : 4); i+= (gas ? -1 : 1))
{ {
putVertex( putVertex(
buf, side, builder, side,
z[i], gas ? 1 : 0, x[i], z[i], gas ? 1 : 0, x[i],
still.getInterpolatedU(z[i] * 16), still.getInterpolatedU(z[i] * 16),
still.getInterpolatedV(x[i] * 16)); still.getInterpolatedV(x[i] * 16));
} }
buf.flip(); faceQuads.put(side, ImmutableList.<BakedQuad>of(builder.build()));
data = new int[4 * format.getNextOffset() / 4];
buf.asIntBuffer().get(data);
faceQuads.put(side, ImmutableList.<BakedQuad>of(new IColoredBakedQuad.ColoredBakedQuad(data, -1, side)));
// sides // sides
@ -197,22 +190,21 @@ public class ModelFluid implements IModelCustomData
for(int k = 0; k < 2; k++) for(int k = 0; k < 2; k++)
{ {
buf.clear(); builder = new UnpackedBakedQuad.Builder(format);
builder.setQuadOrientation(side);
builder.setQuadColored();
for(int j = 0; j < 4; j++) for(int j = 0; j < 4; j++)
{ {
int l = (k * 3) + (1 - 2 * k) * j; int l = (k * 3) + (1 - 2 * k) * j;
float yl = z[l] * y[(i + x[l]) % 4]; float yl = z[l] * y[(i + x[l]) % 4];
if(gas && z[l] == 0) yl = 1; if(gas && z[l] == 0) yl = 1;
putVertex( putVertex(
buf, side, builder, side,
x[(i + x[l]) % 4], yl, z[(i + x[l]) % 4], x[(i + x[l]) % 4], yl, z[(i + x[l]) % 4],
flowing.getInterpolatedU(x[l] * 8), flowing.getInterpolatedU(x[l] * 8),
flowing.getInterpolatedV((gas ? yl : 1 - yl) * 8)); flowing.getInterpolatedV((gas ? yl : 1 - yl) * 8));
} }
buf.flip(); q[k] = builder.build();
data = new int[4 * format.getNextOffset() / 4];
buf.asIntBuffer().get(data);
q[k] = new IColoredBakedQuad.ColoredBakedQuad(data, -1, side);
} }
faceQuads.put(side, ImmutableList.of(q[0], q[1])); faceQuads.put(side, ImmutableList.of(q[0], q[1]));
} }
@ -221,70 +213,49 @@ public class ModelFluid implements IModelCustomData
{ {
// 1 quad for inventory // 1 quad for inventory
buf.clear(); UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format);
builder.setQuadOrientation(EnumFacing.UP);
builder.setQuadColored();
for(int i = 0; i < 4; i++) for(int i = 0; i < 4; i++)
{ {
putVertex( putVertex(
buf, EnumFacing.UP, builder, EnumFacing.UP,
z[i], x[i], 0, z[i], x[i], 0,
still.getInterpolatedU(x[i] * 16), still.getInterpolatedU(x[i] * 16),
still.getInterpolatedV(z[i] * 16)); still.getInterpolatedV(z[i] * 16));
} }
buf.flip(); faceQuads.put(EnumFacing.SOUTH, ImmutableList.<BakedQuad>of(builder.build()));
data = new int[4 * format.getNextOffset() / 4];
buf.asIntBuffer().get(data);
faceQuads.put(EnumFacing.SOUTH, ImmutableList.<BakedQuad>of(new IColoredBakedQuad.ColoredBakedQuad(data, -1, EnumFacing.UP)));
} }
} }
private void put(ByteBuffer buf, VertexFormatElement e, Float... fs) private void putVertex(UnpackedBakedQuad.Builder builder, EnumFacing side, float x, float y, float z, float u, float v)
{ {
Attributes.put(buf, e, true, 0f, fs); for(int e = 0; e < format.getElementCount(); e++)
}
private float diffuse(EnumFacing side)
{
switch(side)
{
case DOWN:
return .5f;
case UP:
return 1f;
case NORTH:
case SOUTH:
return .8f;
default:
return .6f;
}
}
private void putVertex(ByteBuffer buf, EnumFacing side, float x, float y, float z, float u, float v)
{
for(VertexFormatElement e : (List<VertexFormatElement>)format.getElements())
{ {
// TODO transformation // TODO transformation
switch(e.getUsage()) switch(format.getElement(e).getUsage())
{ {
case POSITION: case POSITION:
put(buf, e, x - side.getDirectionVec().getX() * eps, y, z - side.getDirectionVec().getZ() * eps, 1f); builder.put(e, x - side.getDirectionVec().getX() * eps, y, z - side.getDirectionVec().getZ() * eps, 1f);
break; break;
case COLOR: case COLOR:
// temporarily add diffuse lighting float d = LightUtil.diffuseLight(side);
float d = diffuse(side); builder.put(e,
put(buf, e,
d * ((color >> 16) & 0xFF) / 255f, d * ((color >> 16) & 0xFF) / 255f,
d * ((color >> 8) & 0xFF) / 255f, d * ((color >> 8) & 0xFF) / 255f,
d * (color & 0xFF) / 255f, d * (color & 0xFF) / 255f,
((color >> 24) & 0xFF) / 255f); ((color >> 24) & 0xFF) / 255f);
break; break;
case UV: case UV: if(format.getElement(e).getIndex() == 0)
put(buf, e, u, v, 0f, 1f); {
builder.put(e, u, v, 0f, 1f);
break; break;
}
case NORMAL: case NORMAL:
put(buf, e, (float)side.getFrontOffsetX(), (float)side.getFrontOffsetX(), (float)side.getFrontOffsetX(), 0f); builder.put(e, (float)side.getFrontOffsetX(), (float)side.getFrontOffsetY(), (float)side.getFrontOffsetZ(), 0f);
break; break;
default: default:
put(buf, e); builder.put(e);
break; break;
} }
} }

View file

@ -5,7 +5,6 @@ import java.awt.Graphics2D;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -35,6 +34,7 @@ import net.minecraft.client.renderer.block.statemap.IStateMapper;
import net.minecraft.client.renderer.texture.IIconCreator; import net.minecraft.client.renderer.texture.IIconCreator;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.texture.TextureMap; import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.resources.IResourceManager; import net.minecraft.client.resources.IResourceManager;
import net.minecraft.client.resources.model.BuiltInModel; import net.minecraft.client.resources.model.BuiltInModel;
@ -47,18 +47,13 @@ import net.minecraft.item.Item;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.IRegistry; import net.minecraft.util.IRegistry;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.registry.GameData; import net.minecraftforge.fml.common.registry.GameData;
import net.minecraftforge.fml.common.registry.RegistryDelegate; import net.minecraftforge.fml.common.registry.RegistryDelegate;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.Level;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
@ -123,7 +118,7 @@ public class ModelLoader extends ModelBakery
return Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(location.toString()); return Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(location.toString());
} }
}; };
IFlexibleBakedModel missingBaked = missingModel.bake(missingModel.getDefaultState(), Attributes.DEFAULT_BAKED_FORMAT, textureGetter); IFlexibleBakedModel missingBaked = missingModel.bake(missingModel.getDefaultState(), DefaultVertexFormats.ITEM, textureGetter);
for (Entry<ModelResourceLocation, IModel> e : stateModels.entrySet()) for (Entry<ModelResourceLocation, IModel> e : stateModels.entrySet())
{ {
if(e.getValue() == getMissingModel()) if(e.getValue() == getMissingModel())
@ -132,7 +127,7 @@ public class ModelLoader extends ModelBakery
} }
else else
{ {
bakedRegistry.putObject(e.getKey(), e.getValue().bake(e.getValue().getDefaultState(), Attributes.DEFAULT_BAKED_FORMAT, textureGetter)); bakedRegistry.putObject(e.getKey(), e.getValue().bake(e.getValue().getDefaultState(), DefaultVertexFormats.ITEM, textureGetter));
} }
} }
return bakedRegistry; return bakedRegistry;
@ -328,6 +323,7 @@ public class ModelLoader extends ModelBakery
return new ItemLayerModel(model).bake(perState, format, bakedTextureGetter); return new ItemLayerModel(model).bake(perState, format, bakedTextureGetter);
} }
if(isCustomRenderer(model)) return new IFlexibleBakedModel.Wrapper(new BuiltInModel(transforms), format); if(isCustomRenderer(model)) return new IFlexibleBakedModel.Wrapper(new BuiltInModel(transforms), format);
// TODO perspective awareness for this
return bakeNormal(model, state.apply(this), format, bakedTextureGetter, state instanceof UVLock); return bakeNormal(model, state.apply(this), format, bakedTextureGetter, state instanceof UVLock);
} }

View file

@ -1,6 +1,5 @@
package net.minecraftforge.client.model; package net.minecraftforge.client.model;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -18,8 +17,6 @@ import net.minecraftforge.fml.common.FMLLog;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
import com.google.common.base.Throwables;
/* /*
* Central hub for custom model loaders. * Central hub for custom model loaders.
*/ */

View file

@ -8,15 +8,15 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import org.apache.commons.lang3.tuple.Pair;
import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms; import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.resources.model.ModelRotation;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;

View file

@ -2,7 +2,6 @@ package net.minecraftforge.client.model.b3d;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -13,6 +12,7 @@ import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.vecmath.Matrix4f; import javax.vecmath.Matrix4f;
import javax.vecmath.Vector3f;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.BakedQuad;
@ -20,15 +20,12 @@ import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType; import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.client.resources.IResource; import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager; import net.minecraft.client.resources.IResourceManager;
import net.minecraft.client.resources.model.IBakedModel; import net.minecraft.client.resources.model.IBakedModel;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.Attributes;
import net.minecraftforge.client.model.IColoredBakedQuad.ColoredBakedQuad;
import net.minecraftforge.client.model.ICustomModelLoader; import net.minecraftforge.client.model.ICustomModelLoader;
import net.minecraftforge.client.model.IFlexibleBakedModel; import net.minecraftforge.client.model.IFlexibleBakedModel;
import net.minecraftforge.client.model.IModel; import net.minecraftforge.client.model.IModel;
@ -44,7 +41,6 @@ import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.client.model.ModelLoaderRegistry; import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.client.model.TRSRTransformation; import net.minecraftforge.client.model.TRSRTransformation;
import net.minecraftforge.client.model.b3d.B3DModel.Animation; import net.minecraftforge.client.model.b3d.B3DModel.Animation;
import net.minecraftforge.client.model.b3d.B3DModel.Bone;
import net.minecraftforge.client.model.b3d.B3DModel.Face; import net.minecraftforge.client.model.b3d.B3DModel.Face;
import net.minecraftforge.client.model.b3d.B3DModel.IKind; import net.minecraftforge.client.model.b3d.B3DModel.IKind;
import net.minecraftforge.client.model.b3d.B3DModel.Key; import net.minecraftforge.client.model.b3d.B3DModel.Key;
@ -52,13 +48,14 @@ import net.minecraftforge.client.model.b3d.B3DModel.Mesh;
import net.minecraftforge.client.model.b3d.B3DModel.Node; import net.minecraftforge.client.model.b3d.B3DModel.Node;
import net.minecraftforge.client.model.b3d.B3DModel.Texture; import net.minecraftforge.client.model.b3d.B3DModel.Texture;
import net.minecraftforge.client.model.b3d.B3DModel.Vertex; import net.minecraftforge.client.model.b3d.B3DModel.Vertex;
import net.minecraftforge.client.model.pipeline.LightUtil;
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.common.property.IUnlistedProperty; import net.minecraftforge.common.property.IUnlistedProperty;
import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple; import org.apache.commons.lang3.tuple.Triple;
import org.lwjgl.BufferUtils;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
@ -68,7 +65,6 @@ import com.google.common.cache.LoadingCache;
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimap;
/* /*
* Loader for Blitz3D models. * Loader for Blitz3D models.
@ -481,7 +477,6 @@ public class B3DLoader implements ICustomModelLoader
private final VertexFormat format; private final VertexFormat format;
private final ImmutableMap<String, TextureAtlasSprite> textures; private final ImmutableMap<String, TextureAtlasSprite> textures;
private final ByteBuffer buf;
private ImmutableList<BakedQuad> quads; private ImmutableList<BakedQuad> quads;
private static final int BYTES_IN_INT = Integer.SIZE / Byte.SIZE; private static final int BYTES_IN_INT = Integer.SIZE / Byte.SIZE;
@ -493,8 +488,6 @@ public class B3DLoader implements ICustomModelLoader
this.state = state; this.state = state;
this.format = format; this.format = format;
this.textures = textures; this.textures = textures;
buf = BufferUtils.createByteBuffer(VERTICES_IN_QUAD * format.getNextOffset());
} }
public List<BakedQuad> getFaceQuads(EnumFacing side) public List<BakedQuad> getFaceQuads(EnumFacing side)
@ -517,7 +510,7 @@ public class B3DLoader implements ICustomModelLoader
builder.addAll(new BakedWrapper(new B3DLoader.Wrapper(model.getLocation(), model.getTextureMap(), childMesh), state, format, textures).getGeneralQuads()); builder.addAll(new BakedWrapper(new B3DLoader.Wrapper(model.getLocation(), model.getTextureMap(), childMesh), state, format, textures).getGeneralQuads());
} }
} }
Multimap<Vertex, Pair<Float, Node<Bone>>> weightMap = mesh.getKind().getWeightMap(); mesh.getKind().getWeightMap();
Collection<Face> faces = mesh.getKind().getFaces(); Collection<Face> faces = mesh.getKind().getFaces();
faces = mesh.getKind().bake(new Function<Node<?>, Matrix4f>() faces = mesh.getKind().bake(new Function<Node<?>, Matrix4f>()
{ {
@ -529,85 +522,77 @@ public class B3DLoader implements ICustomModelLoader
}); });
for(Face f : faces) for(Face f : faces)
{ {
buf.clear(); UnpackedBakedQuad.Builder quadBuilder = new UnpackedBakedQuad.Builder(format);
quadBuilder.setQuadOrientation(EnumFacing.getFacingFromVector(f.getNormal().x, f.getNormal().y, f.getNormal().z));
quadBuilder.setQuadColored();
List<Texture> textures = null; List<Texture> textures = null;
if(f.getBrush() != null) textures = f.getBrush().getTextures(); if(f.getBrush() != null) textures = f.getBrush().getTextures();
TextureAtlasSprite sprite; TextureAtlasSprite sprite;
if(textures == null || textures.isEmpty()) sprite = this.textures.get("missingno"); if(textures == null || textures.isEmpty()) sprite = this.textures.get("missingno");
else if(textures.get(0) == B3DModel.Texture.White) sprite = ModelLoader.White.instance; else if(textures.get(0) == B3DModel.Texture.White) sprite = ModelLoader.White.instance;
else sprite = this.textures.get(textures.get(0).getPath()); else sprite = this.textures.get(textures.get(0).getPath());
putVertexData(f.getV1(), sprite); putVertexData(quadBuilder, f.getV1(), f.getNormal(), sprite);
putVertexData(f.getV2(), sprite); putVertexData(quadBuilder, f.getV2(), f.getNormal(), sprite);
putVertexData(f.getV3(), sprite); putVertexData(quadBuilder, f.getV3(), f.getNormal(), sprite);
putVertexData(f.getV3(), sprite); putVertexData(quadBuilder, f.getV3(), f.getNormal(), sprite);
buf.flip(); builder.add(quadBuilder.build());
int[] data = new int[VERTICES_IN_QUAD * format.getNextOffset() / BYTES_IN_INT];
buf.asIntBuffer().get(data);
builder.add(new ColoredBakedQuad(data, -1, EnumFacing.getFacingFromVector(f.getNormal().x, f.getNormal().y, f.getNormal().z)));
} }
quads = builder.build(); quads = builder.build();
} }
return quads; return quads;
} }
private void put(VertexFormatElement e, Float... fs) private final void putVertexData(UnpackedBakedQuad.Builder builder, Vertex v, Vector3f faceNormal, TextureAtlasSprite sprite)
{
Attributes.put(buf, e, true, 0f, fs);
}
@SuppressWarnings("unchecked")
private final void putVertexData(Vertex v, TextureAtlasSprite sprite)
{ {
// TODO handle everything not handled (texture transformations, bones, transformations, normals, e.t.c) // TODO handle everything not handled (texture transformations, bones, transformations, normals, e.t.c)
int oldPos = buf.position(); for(int e = 0; e < format.getElementCount(); e++)
Number[] ns = new Number[16];
for(int i = 0; i < ns.length; i++) ns[i] = 0f;
for(VertexFormatElement e : (List<VertexFormatElement>)format.getElements())
{ {
switch(e.getUsage()) switch(format.getElement(e).getUsage())
{ {
case POSITION: case POSITION:
put(e, v.getPos().x, v.getPos().y, v.getPos().z, 1f); builder.put(e, v.getPos().x, v.getPos().y, v.getPos().z, 1);
break; break;
case COLOR: case COLOR:
float d = LightUtil.diffuseLight(faceNormal.x, faceNormal.y, faceNormal.z);
if(v.getColor() != null) if(v.getColor() != null)
{ {
put(e, v.getColor().x, v.getColor().y, v.getColor().z, v.getColor().w); builder.put(e, d * v.getColor().x, d * v.getColor().y, d * v.getColor().z, v.getColor().w);
} }
else else
{ {
put(e, 1f, 1f, 1f, 1f); builder.put(e, d, d, d, 1);
} }
break; break;
case UV: case UV:
// TODO handle more brushes // TODO handle more brushes
if(e.getIndex() < v.getTexCoords().length) if(format.getElement(e).getIndex() < v.getTexCoords().length)
{ {
put(e, builder.put(e,
sprite.getInterpolatedU(v.getTexCoords()[0].x * 16), sprite.getInterpolatedU(v.getTexCoords()[0].x * 16),
sprite.getInterpolatedV(v.getTexCoords()[0].y * 16), sprite.getInterpolatedV(v.getTexCoords()[0].y * 16),
0f, 0,
1f 1
); );
} }
else else
{ {
put(e, 0f, 0f, 0f, 1f); builder.put(e, 0, 0, 0, 1);
} }
break; break;
case NORMAL: case NORMAL:
// TODO if(v.getNormal() != null)
put(e, 0f, 1f, 0f, 1f); {
break; builder.put(e, v.getNormal().x, v.getNormal().y, v.getNormal().z, 1);
case GENERIC: }
// TODO else
put(e, 0f, 0f, 0f, 0f); {
builder.put(e, faceNormal.x, faceNormal.y, faceNormal.z, 1);
}
break; break;
default: default:
break; builder.put(e);
} }
} }
buf.position(oldPos + format.getNextOffset());
} }
public boolean isAmbientOcclusion() public boolean isAmbientOcclusion()

View file

@ -0,0 +1,200 @@
package net.minecraftforge.client.model.pipeline;
import net.minecraft.block.Block;
import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.MathHelper;
import net.minecraft.world.IBlockAccess;
public class BlockInfo
{
private IBlockAccess world;
private Block block;
private BlockPos blockPos;
private final boolean[][][] translucent = new boolean[3][3][3];
private final int[][][] s = new int[3][3][3];
private final int[][][] b = new int[3][3][3];
private final float[][][][] skyLight = new float[3][2][2][2];
private final float[][][][] blockLight = new float[3][2][2][2];
private final float[][][] ao = new float[3][3][3];
private float shx = 0, shy = 0, shz = 0;
private int cachedTint = -1;
private int cachedMultiplier = -1;
public int getColorMultiplier(int tint)
{
if(cachedTint == tint) return cachedMultiplier;
return block.colorMultiplier(world, blockPos, tint);
}
public void updateShift()
{
shx = shy = shz = 0;
long rand = 0;
// FIXME
switch(block.getOffsetType())
{
case XYZ:
rand = MathHelper.getPositionRandom(blockPos);
shy = ((float)((rand >> 20) & 0xF) / 0xF - 1) * .2f;
case XZ:
shx = ((float)((rand >> 16) & 0xF) / 0xF - .5f) * .5f;
shz = ((float)((rand >> 24) & 0xF) / 0xF - .5f) * .5f;
default:
}
}
public void setWorld(IBlockAccess world)
{
this.world = world;
cachedTint = -1;
cachedMultiplier = -1;
}
public void setBlock(Block block)
{
this.block = block;
cachedTint = -1;
cachedMultiplier = -1;
}
public void setBlockPos(BlockPos blockPos)
{
this.blockPos = blockPos;
cachedTint = -1;
cachedMultiplier = -1;
}
private float combine(int c, int s1, int s2, int s3)
{
if(c == 0) c = Math.max(0, Math.max(s1, s2) - 1);
if(s1 == 0) s1 = Math.max(0, c - 1);
if(s2 == 0) s2 = Math.max(0, c - 1);
if(s3 == 0) s3 = Math.max(0, Math.max(s1, s2) - 1);
return (float)(c + s1 + s2 + s3) * 0x20 / (4 * 0xFFFF);
}
public void updateLightMatrix()
{
boolean full = false;
for(int x = 0; x <= 2; x++)
{
for(int y = 0; y <= 2; y++)
{
for(int z = 0; z <= 2; z++)
{
BlockPos pos = blockPos.add(x - 1, y - 1, z - 1);
Block block = world.getBlockState(pos).getBlock();
translucent[x][y][z] = block.isTranslucent();
//translucent[x][y][z] = world.getBlockState(pos).getBlock().getLightOpacity(world, pos) == 0;
int brightness = this.block.getMixedBrightnessForBlock(world, pos);
s[x][y][z] = (brightness >> 0x14) & 0xF;
b[x][y][z] = (brightness >> 0x04) & 0xF;
ao[x][y][z] = block.getAmbientOcclusionLightValue();
if(x == 1 && y == 1 && z == 1)
{
full = block.isFullCube();
}
}
}
}
if(!full)
{
for(EnumFacing side : EnumFacing.values())
{
int x = side.getFrontOffsetX() + 1;
int y = side.getFrontOffsetY() + 1;
int z = side.getFrontOffsetZ() + 1;
s[x][y][z] = Math.max(s[1][1][1] - 1, s[x][y][z]);
b[x][y][z] = Math.max(b[1][1][1] - 1, b[x][y][z]);
}
}
for(int x = 0; x < 2; x++)
{
for(int y = 0; y < 2; y++)
{
for(int z = 0; z < 2; z++)
{
int x1 = x * 2;
int y1 = y * 2;
int z1 = z * 2;
boolean tx = translucent[x1][1][z1] || translucent[x1][y1][1];
skyLight[0][x][y][z] = combine(s[x1][1][1], s[x1][1][z1], s[x1][y1][1], tx ? s[x1][y1][z1] : s[x1][1][1]);
blockLight[0][x][y][z] = combine(b[x1][1][1], b[x1][1][z1], b[x1][y1][1], tx ? b[x1][y1][z1] : b[x1][1][1]);
boolean ty = translucent[x1][y1][1] || translucent[1][y1][z1];
skyLight[1][x][y][z] = combine(s[1][y1][1], s[x1][y1][1], s[1][y1][z1], ty ? s[x1][y1][z1] : s[1][y1][1]);
blockLight[1][x][y][z] = combine(b[1][y1][1], b[x1][y1][1], b[1][y1][z1], ty ? b[x1][y1][z1] : b[1][y1][1]);
boolean tz = translucent[1][y1][z1] || translucent[1][y1][z1];
skyLight[2][x][y][z] = combine(s[1][1][z1], s[1][y1][z1], s[x1][1][z1], tz ? s[x1][y1][z1] : s[1][1][z1]);
blockLight[2][x][y][z] = combine(b[1][1][z1], b[1][y1][z1], b[x1][1][z1], tz ? b[x1][y1][z1] : b[1][1][z1]);
}
}
}
}
public IBlockAccess getWorld()
{
return world;
}
public Block getBlock()
{
return block;
}
public BlockPos getBlockPos()
{
return blockPos;
}
public boolean[][][] getTranslucent()
{
return translucent;
}
public float[][][][] getSkyLight()
{
return skyLight;
}
public float[][][][] getBlockLight()
{
return blockLight;
}
public float[][][] getAo()
{
return ao;
}
public float getShx()
{
return shx;
}
public float getShy()
{
return shy;
}
public float getShz()
{
return shz;
}
public int getCachedTint()
{
return cachedTint;
}
public int getCachedMultiplier()
{
return cachedMultiplier;
}
}

View file

@ -0,0 +1,107 @@
package net.minecraftforge.client.model.pipeline;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.BlockModelRenderer;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.resources.model.IBakedModel;
import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.world.IBlockAccess;
import net.minecraftforge.common.ForgeModContainer;
public class ForgeBlockModelRenderer extends BlockModelRenderer
{
private final ThreadLocal<VertexLighterFlat> lighterFlat = new ThreadLocal<VertexLighterFlat>()
{
@Override
protected VertexLighterFlat initialValue()
{
return new VertexLighterFlat();
}
};
private final ThreadLocal<VertexLighterSmoothAo> lighterSmooth = new ThreadLocal<VertexLighterSmoothAo>()
{
@Override
protected VertexLighterSmoothAo initialValue()
{
return new VertexLighterSmoothAo();
}
};
private final ThreadLocal<WorldRenderer> lastRendererFlat = new ThreadLocal<WorldRenderer>();
private final ThreadLocal<WorldRenderer> lastRendererSmooth = new ThreadLocal<WorldRenderer>();
@Override
public boolean renderModelStandard(IBlockAccess world, IBakedModel model, Block block, BlockPos pos, WorldRenderer wr, boolean checkSides)
{
if(ForgeModContainer.forgeLightPipelineEnabled)
{
if(wr != lastRendererFlat.get())
{
lastRendererFlat.set(wr);
lighterFlat.get().setParent(new WorldRendererConsumer(wr));
}
return render(lighterFlat.get(), world, model, block, pos, wr, checkSides);
}
else
{
return super.renderModelStandard(world, model, block, pos, wr, checkSides);
}
}
@Override
public boolean renderModelAmbientOcclusion(IBlockAccess world, IBakedModel model, Block block, BlockPos pos, WorldRenderer wr, boolean checkSides)
{
if(ForgeModContainer.forgeLightPipelineEnabled)
{
if(wr != lastRendererSmooth.get())
{
lastRendererSmooth.set(wr);
lighterSmooth.get().setParent(new WorldRendererConsumer(wr));
}
return render(lighterSmooth.get(), world, model, block, pos, wr, checkSides);
}
else
{
return super.renderModelAmbientOcclusion(world, model, block, pos, wr, checkSides);
}
}
public static boolean render(VertexLighterFlat lighter, IBlockAccess world, IBakedModel model, Block block, BlockPos pos, WorldRenderer wr, boolean checkSides)
{
lighter.setWorld(world);
lighter.setBlock(block);
lighter.setBlockPos(pos);
boolean empty = true;
List<BakedQuad> quads = model.getGeneralQuads();
if(!quads.isEmpty())
{
lighter.updateBlockInfo();
empty = false;
for(BakedQuad quad : quads)
{
quad.pipe(lighter);
}
}
for(EnumFacing side : EnumFacing.values())
{
quads = model.getFaceQuads(side);
if(!quads.isEmpty())
{
if(!checkSides || block.shouldSideBeRendered(world, pos.offset(side), side))
{
if(empty) lighter.updateBlockInfo();
empty = false;
for(BakedQuad quad : quads)
{
quad.pipe(lighter);
}
}
}
}
return !empty;
}
}

View file

@ -0,0 +1,23 @@
package net.minecraftforge.client.model.pipeline;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.EnumFacing;
/**
* Assumes that the data length is not less than e.getElementCount().
* Also assumes that element index passed will increment from 0 to format.getElementCount() - 1.
* Normal, Color and UV are assumed to be in 0-1 range.
*/
public interface IVertexConsumer
{
/**
* @return the format that should be used for passed data.
*/
VertexFormat getVertexFormat();
void setQuadTint(int tint);
void setQuadOrientation(EnumFacing orientation);
void setQuadColored();
void put(int element, float... data);
}

View file

@ -0,0 +1,10 @@
package net.minecraftforge.client.model.pipeline;
public interface IVertexProducer
{
/**
* @param consumer consumer to receive the vertex data this producer can provide
*/
void pipe(IVertexConsumer consumer);
}

View file

@ -0,0 +1,311 @@
package net.minecraftforge.client.model.pipeline;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.block.model.BakedQuad;
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.renderer.vertex.VertexFormatElement.EnumUsage;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.client.model.IColoredBakedQuad;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
public class LightUtil
{
public static float diffuseLight(float x, float y, float z)
{
float s2 = (float)Math.pow(2, .5);
float y1 = y + 3 - 2 * s2;
return (x * x * 0.6f + (y1 * y1 * (3 + 2 * s2)) / 8 + z * z * 0.8f);
}
public static float diffuseLight(EnumFacing side)
{
switch(side)
{
case DOWN:
return .5f;
case UP:
return 1f;
case NORTH:
case SOUTH:
return .8f;
default:
return .6f;
}
}
public static EnumFacing toSide(float x, float y, float z)
{
if(Math.abs(x) > Math.abs(y))
{
if(Math.abs(x) > Math.abs(z))
{
if(x < 0) return EnumFacing.WEST;
return EnumFacing.EAST;
}
else
{
if(z < 0) return EnumFacing.NORTH;
return EnumFacing.SOUTH;
}
}
else
{
if(Math.abs(y) > Math.abs(z))
{
if(y < 0) return EnumFacing.DOWN;
return EnumFacing.UP;
}
else
{
if(z < 0) return EnumFacing.NORTH;
return EnumFacing.SOUTH;
}
}
}
private static final LoadingCache<VertexFormat, int[]> formatMaps = CacheBuilder.newBuilder()
.maximumSize(10)
.build(new CacheLoader<VertexFormat, int[]>()
{
public int[] load(VertexFormat format)
{
return mapFormats(format, DefaultVertexFormats.ITEM);
}
});
public static void putBakedQuad(IVertexConsumer consumer, BakedQuad quad)
{
consumer.setQuadOrientation(quad.getFace());
if(quad.hasTintIndex())
{
consumer.setQuadTint(quad.getTintIndex());
}
if(quad instanceof IColoredBakedQuad)
{
consumer.setQuadColored();
}
//int[] eMap = mapFormats(consumer.getVertexFormat(), DefaultVertexFormats.ITEM);
float[] data = new float[4];
int[] eMap = formatMaps.getUnchecked(consumer.getVertexFormat());
for(int v = 0; v < 4; v++)
{
for(int e = 0; e < consumer.getVertexFormat().getElementCount(); e++)
{
if(eMap[e] != DefaultVertexFormats.ITEM.getElementCount())
{
unpack(quad.getVertexData(), data, DefaultVertexFormats.ITEM, v, eMap[e]);
consumer.put(e, data);
}
else
{
consumer.put(e);
}
}
}
}
public static int[] mapFormats(VertexFormat from, VertexFormat to)
{
int[] eMap = new int[from.getElementCount()];
for(int e = 0; e < from.getElementCount(); e++)
{
VertexFormatElement expected = from.getElement(e);
int e2;
for(e2 = 0; e2 < to.getElementCount(); e2++)
{
VertexFormatElement current = to.getElement(e2);
if(expected.getUsage() == current.getUsage() && expected.getIndex() == current.getIndex())
{
break;
}
}
eMap[e] = e2;
}
return eMap;
}
public static void unpack(int[] from, float[] to, VertexFormat formatFrom, int v, int e)
{
VertexFormatElement element = formatFrom.getElement(e);
for(int i = 0; i < 4; i++)
{
if(i < element.getElementCount())
{
int pos = v * formatFrom.getNextOffset() + element.getOffset() + element.getType().getSize() * i;
int index = pos >> 2;
int offset = pos & 3;
int bits = from[index];
bits = bits >>> (offset * 8);
if((pos + element.getType().getSize() - 1) / 4 != index)
{
bits |= from[index + 1] << ((4 - offset) * 8);
}
int mask = (256 << (8 * (element.getType().getSize() - 1))) - 1;
bits &= mask;
switch(element.getType())
{
case FLOAT:
to[i] = Float.intBitsToFloat(bits);
break;
case UBYTE:
case USHORT:
to[i] = (float)bits / mask;
break;
case UINT:
to[i] = (float)((double)(bits & 0xFFFFFFFFL) / 0xFFFFFFFFL);
break;
case BYTE:
to[i] = ((float)(byte)bits) / mask * 2;
break;
case SHORT:
to[i] = ((float)(short)bits) / mask * 2;
break;
case INT:
to[i] = ((float)(bits & 0xFFFFFFFFL)) / 0xFFFFFFFFL * 2;
break;
}
}
else
{
to[i] = 0;
}
}
}
public static void pack(float[] from, int[] to, VertexFormat formatTo, int v, int e)
{
VertexFormatElement element = formatTo.getElement(e);
for(int i = 0; i < 4; i++)
{
if(i < element.getElementCount())
{
int pos = v * formatTo.getNextOffset() + element.getOffset() + element.getType().getSize() * i;
int index = pos >> 2;
int offset = pos & 3;
int bits = 0;
int mask = (256 << (8 * (element.getType().getSize() - 1))) - 1;
switch(element.getType())
{
case FLOAT:
bits = Float.floatToRawIntBits(from[i]);
break;
case UBYTE:
case USHORT:
case UINT:
bits = (int)(from[i] * mask);
break;
case BYTE:
case SHORT:
case INT:
bits = (int)(from[i] * mask / 2);
break;
}
to[index] &= ~(mask << (offset * 8));
to[index] |= (((bits & mask) << (offset * 8)));
// TODO handle overflow into to[index + 1]
}
}
}
private static IVertexConsumer tessellator = null;
public static IVertexConsumer getTessellator()
{
if(tessellator == null)
{
Tessellator tes = Tessellator.getInstance();
WorldRenderer wr = tes.getWorldRenderer();
tessellator = new WorldRendererConsumer(wr);
}
return tessellator;
}
private static ItemConsumer itemConsumer = null;
public static ItemConsumer getItemConsumer()
{
if(itemConsumer == null)
{
itemConsumer = new ItemConsumer(getTessellator());
}
return itemConsumer;
}
public static void renderQuadColor(WorldRenderer wr, BakedQuad quad, int auxColor)
{
ItemConsumer cons;
if(wr == Tessellator.getInstance().getWorldRenderer())
{
cons = getItemConsumer();
}
else
{
cons = new ItemConsumer(new WorldRendererConsumer(wr));
}
float r = (float)(auxColor & 0xFF) / 0xFF;
float g = (float)((auxColor >>> 8) & 0xFF) / 0xFF;
float b = (float)((auxColor >>> 16) & 0xFF) / 0xFF;
float a = (float)((auxColor >>> 24) & 0xFF) / 0xFF;
cons.setAuxColor(r, g, b, a);
quad.pipe(cons);
}
public static class ItemConsumer extends VertexTransformer
{
private boolean colored = false;
private int vertices = 0;
private float[] auxColor = new float[]{1, 1, 1, 1};
private float[] buf = new float[4];
public ItemConsumer(IVertexConsumer parent)
{
super(parent);
}
public void setAuxColor(float... auxColor)
{
System.arraycopy(auxColor, 0, this.auxColor, 0, this.auxColor.length);
}
@Override
public void setQuadColored()
{
colored = true;
}
public void put(int element, float... data)
{
if(getVertexFormat().getElement(element).getUsage() == EnumUsage.COLOR)
{
System.arraycopy(auxColor, 0, buf, 0, buf.length);
if(colored)
{
for(int i = 0; i < 4; i++)
{
buf[i] *= data[i];
}
}
super.put(element, buf);
}
else
{
super.put(element, data);
}
if(element == getVertexFormat().getElementCount() - 1)
{
vertices++;
if(vertices == 4)
{
vertices = 0;
colored = false;
}
}
}
}
}

View file

@ -0,0 +1,43 @@
package net.minecraftforge.client.model.pipeline;
import net.minecraft.client.renderer.vertex.VertexFormat;
public abstract class QuadGatheringTransformer implements IVertexConsumer
{
protected IVertexConsumer parent;
protected VertexFormat format;
protected int vertices = 0;
protected float[][][] quadData = null;
public void setParent(IVertexConsumer parent)
{
this.parent = parent;
}
public void setVertexFormat(VertexFormat format)
{
this.format = format;
quadData = new float[format.getElementCount()][4][4];
}
@Override
public VertexFormat getVertexFormat()
{
return format;
}
@Override
public void put(int element, float... data)
{
System.arraycopy(data, 0, quadData[element][vertices], 0, data.length);
if(element == getVertexFormat().getElementCount() - 1) vertices++;
if(vertices == 4)
{
vertices = 0;
processQuad();
}
}
protected abstract void processQuad();
}

View file

@ -0,0 +1,27 @@
package net.minecraftforge.client.model.pipeline;
import net.minecraft.client.renderer.vertex.VertexFormat;
public abstract class TransformerConsumer implements IVertexConsumer {
private IVertexConsumer parent;
protected TransformerConsumer(IVertexConsumer parent)
{
this.parent = parent;
}
@Override
public VertexFormat getVertexFormat()
{
return parent.getVertexFormat();
}
@Override
public void put(int element, float... data)
{
float[] newData = transform(element, data);
parent.put(element, newData);
}
protected abstract float[] transform(int element, float... data);
}

View file

@ -0,0 +1,154 @@
package net.minecraftforge.client.model.pipeline;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.client.model.IColoredBakedQuad;
// advantages: non-fixed-length vertex format, no overhead of packing and unpacking attributes to transform the model
// disadvantages: (possibly) larger memory footprint, overhead on packing the attributes at the final rendering stage
public class UnpackedBakedQuad extends BakedQuad
{
protected final float[][][] unpackedData;
protected final VertexFormat format;
protected boolean packed = false;
public UnpackedBakedQuad(float[][][] unpackedData, int tint, EnumFacing orientation, VertexFormat format)
{
super(new int[format.getNextOffset() /* / 4 * 4 */], tint, orientation);
this.unpackedData = unpackedData;
this.format = format;
}
@Override
public int[] getVertexData()
{
if(!packed)
{
packed = true;
for(int v = 0; v < 4; v++)
{
for(int e = 0; e < format.getElementCount(); e++)
{
LightUtil.pack(unpackedData[v][e], vertexData, format, v, e);
}
}
}
return vertexData;
}
@Override
public void pipe(IVertexConsumer consumer)
{
int[] eMap = LightUtil.mapFormats(consumer.getVertexFormat(), format);
if(hasTintIndex())
{
consumer.setQuadTint(getTintIndex());
}
consumer.setQuadOrientation(getFace());
if(this instanceof IColoredBakedQuad)
{
consumer.setQuadColored();
}
for(int v = 0; v < 4; v++)
{
for(int e = 0; e < consumer.getVertexFormat().getElementCount(); e++)
{
if(eMap[e] != format.getElementCount())
{
consumer.put(e, unpackedData[v][eMap[e]]);
}
else
{
consumer.put(e);
}
}
}
}
public static class Colored extends UnpackedBakedQuad implements IColoredBakedQuad
{
public Colored(float[][][] unpackedData, int tint, EnumFacing orientation, VertexFormat format)
{
super(unpackedData, tint, orientation, format);
}
}
public static class Builder implements IVertexConsumer
{
private final VertexFormat format;
private final float[][][] unpackedData;
private int tint = -1;
private EnumFacing orientation;
private boolean isColored = false;
private int vertices = 0;
private int elements = 0;
private boolean full = false;
public Builder(VertexFormat format)
{
this.format = format;
unpackedData = new float[4][format.getElementCount()][4];
}
public VertexFormat getVertexFormat()
{
return format;
}
public void setQuadTint(int tint)
{
this.tint = tint;
}
public void setQuadOrientation(EnumFacing orientation)
{
this.orientation = orientation;
}
public void setQuadColored()
{
this.isColored = true;
}
public void put(int element, float... data)
{
for(int i = 0; i < 4; i++)
{
if(i < data.length)
{
unpackedData[vertices][element][i] = data[i];
}
else
{
unpackedData[vertices][element][i] = 0;
}
}
elements++;
if(elements == format.getElementCount())
{
vertices++;
elements = 0;
}
if(vertices == 4)
{
full = true;
}
}
public UnpackedBakedQuad build()
{
if(!full)
{
throw new IllegalStateException("not enough data");
}
if(isColored)
{
return new Colored(unpackedData, tint, orientation, format);
}
return new UnpackedBakedQuad(unpackedData, tint, orientation, format);
}
}
}

View file

@ -0,0 +1,226 @@
package net.minecraftforge.client.model.pipeline;
import javax.vecmath.Vector3f;
import com.google.common.base.Objects;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.world.IBlockAccess;
public class VertexLighterFlat extends QuadGatheringTransformer
{
protected final BlockInfo blockInfo = new BlockInfo();
private int tint = -1;
protected int posIndex = -1;
protected int normalIndex = -1;
protected int colorIndex = -1;
protected int lightmapIndex = -1;
@Override
public void setParent(IVertexConsumer parent)
{
super.setParent(parent);
if(Objects.equal(getVertexFormat(), parent.getVertexFormat())) return;
setVertexFormat(getVertexFormat(parent));
for(int i = 0; i < getVertexFormat().getElementCount(); i++)
{
switch(getVertexFormat().getElement(i).getUsage())
{
case POSITION:
posIndex = i;
break;
case NORMAL:
normalIndex = i;
break;
case COLOR:
colorIndex = i;
break;
case UV:
if(getVertexFormat().getElement(i).getIndex() == 1)
{
lightmapIndex = i;
}
break;
default:
}
}
if(posIndex == -1)
{
throw new IllegalArgumentException("vertex lighter needs format with position");
}
if(lightmapIndex == -1)
{
throw new IllegalArgumentException("vertex lighter needs format with lightmap");
}
if(colorIndex == -1)
{
throw new IllegalArgumentException("vertex lighter needs format with color");
}
}
private static VertexFormat getVertexFormat(IVertexConsumer parent)
{
VertexFormat format = parent.getVertexFormat();
if(format.hasNormal()) return format;
format = new VertexFormat(format);
format.setElement(new VertexFormatElement(0, VertexFormatElement.EnumType.FLOAT, VertexFormatElement.EnumUsage.NORMAL, 4));
return format;
}
@Override
protected void processQuad()
{
float[][] position = quadData[posIndex];
float[][] normal = null;
float[][] lightmap = quadData[lightmapIndex];
float[][] color = quadData[colorIndex];
if(normalIndex != -1 && (
quadData[normalIndex][0][0] != -1 ||
quadData[normalIndex][0][1] != -1 ||
quadData[normalIndex][0][2] != -1))
{
normal = quadData[normalIndex];
}
else
{
normal = new float[4][4];
Vector3f v1 = new Vector3f(position[3]);
Vector3f t = new Vector3f(position[1]);
Vector3f v2 = new Vector3f(position[2]);
v1.sub(t);
t.set(position[0]);
v2.sub(t);
v1.cross(v2, v1);
v1.normalize();
for(int v = 0; v < 4; v++)
{
normal[v][0] = v1.x;
normal[v][1] = v1.y;
normal[v][2] = v1.z;
normal[v][3] = 0;
}
}
int multiplier = -1;
if(tint != -1)
{
multiplier = blockInfo.getColorMultiplier(tint);
}
for(int v = 0; v < 4; v++)
{
position[v][0] += blockInfo.getShx();
position[v][1] += blockInfo.getShy();
position[v][2] += blockInfo.getShz();
float x = position[v][0] - .5f;
float y = position[v][1] - .5f;
float z = position[v][2] - .5f;
//if(blockInfo.getBlock().isFullCube())
{
x += normal[v][0] * .5f;
y += normal[v][1] * .5f;
z += normal[v][2] * .5f;
}
updateLightmap(normal[v], lightmap[v], x, y, z);
updateColor(normal[v], color[v], x, y, z, tint, multiplier);
// no need for remapping cause all we could've done is add 1 element to the end
for(int e = 0; e < parent.getVertexFormat().getElementCount(); e++)
{
switch(parent.getVertexFormat().getElement(e).getUsage())
{
case POSITION:
float[] pos = new float[4];
System.arraycopy(position[v], 0, pos, 0, position[v].length);
pos[0] += (blockInfo.getBlockPos().getX() & 0xF);
pos[1] += (blockInfo.getBlockPos().getY() & 0xF);
pos[2] += (blockInfo.getBlockPos().getZ() & 0xF);
parent.put(e, pos);
break;
case NORMAL: if(normalIndex != -1)
{
parent.put(e, normal[v]);
break;
}
case COLOR:
parent.put(e, color[v]);
break;
case UV: if(getVertexFormat().getElement(e).getIndex() == 1)
{
parent.put(e, lightmap[v]);
break;
}
default:
parent.put(e, quadData[e][v]);
}
}
}
tint = -1;
}
protected void updateLightmap(float[] normal, float[] lightmap, float x, float y, float z)
{
float e1 = .5f - 1e4f;
float e2 = 1 - 1e4f;
BlockPos pos = blockInfo.getBlockPos();
if(y < -e1 && normal[1] < -e2) pos = pos.down();
if(y > e1 && normal[1] > e2) pos = pos.up();
if(z < -e1 && normal[2] < -e2) pos = pos.north();
if(z > e1 && normal[2] > e2) pos = pos.south();
if(x < -e1 && normal[0] < -e2) pos = pos.west();
if(x > e1 && normal[0] > e2) pos = pos.east();
int brightness = blockInfo.getBlock().getMixedBrightnessForBlock(blockInfo.getWorld(), pos);
lightmap[0] = ((float)((brightness >> 0x04) & 0xF) * 0x20) / 0xFFFF;
lightmap[1] = ((float)((brightness >> 0x14) & 0xF) * 0x20) / 0xFFFF;
}
protected void updateColor(float[] normal, float[] color, float x, float y, float z, float tint, int multiplier)
{
if(tint != -1)
{
color[0] *= (float)(multiplier >> 0x10 & 0xFF) / 0xFF;
color[1] *= (float)(multiplier >> 0x8 & 0xFF) / 0xFF;
color[2] *= (float)(multiplier & 0xFF) / 0xFF;
}
}
public void setQuadTint(int tint)
{
this.tint = tint;
}
public void setQuadOrientation(EnumFacing orientation) {}
public void setQuadCulled() {}
public void setQuadColored() {}
public void setWorld(IBlockAccess world)
{
blockInfo.setWorld(world);
}
public void setBlock(Block block)
{
blockInfo.setBlock(block);
}
public void setBlockPos(BlockPos blockPos)
{
blockInfo.setBlockPos(blockPos);
}
public void updateBlockInfo()
{
blockInfo.updateShift();
}
}

View file

@ -0,0 +1,160 @@
package net.minecraftforge.client.model.pipeline;
import net.minecraft.util.MathHelper;
public class VertexLighterSmoothAo extends VertexLighterFlat
{
@Override
protected void updateLightmap(float[] normal, float[] lightmap, float x, float y, float z)
{
lightmap[0] = calcLightmap(blockInfo.getBlockLight(), x, y, z);
lightmap[1] = calcLightmap(blockInfo.getSkyLight(), x, y, z);
}
@Override
protected void updateColor(float[] normal, float[] color, float x, float y, float z, float tint, int multiplier)
{
if(tint != -1)
{
color[0] *= (float)(multiplier >> 0x10 & 0xFF) / 0xFF;
color[1] *= (float)(multiplier >> 0x8 & 0xFF) / 0xFF;
color[2] *= (float)(multiplier & 0xFF) / 0xFF;
}
float a = getAo(x, y, z);
color[0] *= a;
color[1] *= a;
color[2] *= a;
}
protected float calcLightmap(float[][][][] light, float x, float y, float z)
{
x *= 2;
y *= 2;
z *= 2;
float l2 = x * x + y * y + z * z;
if(l2 > 6 - 2e-2f)
{
float s = (float)Math.sqrt((6 - 2e-2f) / l2);
x *= s;
y *= s;
z *= s;
}
float ax = Math.abs(x);
float ay = Math.abs(y);
float az = Math.abs(z);
float e1 = 1 + 1e-4f;
if(ax > 2 - 1e-4f && ay <= e1 && az <= e1)
{
x = MathHelper.clamp_float(x, -2 + 1e-4f, 2 - 1e-4f);
}
else if(ay > 2 - 1e-4f && az <= e1 && ax <= e1)
{
y = MathHelper.clamp_float(y, -2 + 1e-4f, 2 - 1e-4f);
}
else if(az > 2 - 1e-4f && ax <= e1 && ay <= e1)
{
z = MathHelper.clamp_float(z, -2 + 1e-4f, 2 - 1e-4f);
}
ax = Math.abs(x);
ay = Math.abs(y);
az = Math.abs(z);
if(ax <= e1 && ay + az > 3f - 1e-4f)
{
float s = (3f - 1e-4f) / (ay + az);
y *= s;
z *= s;
}
else if(ay <= e1 && az + ax > 3f - 1e-4f)
{
float s = (3f - 1e-4f) / (az + ax);
z *= s;
x *= s;
}
else if(az <= e1 && ax + ay > 3f - 1e-4f)
{
float s = (3f - 1e-4f) / (ax + ay);
x *= s;
y *= s;
}
else if(ax + ay + az > 4 - 1e-4f)
{
float s = (4 - 1e-4f) / (ax + ay + az);
x *= s;
y *= s;
z *= s;
}
float l = 0;
float s = 0;
for(int ix = 0; ix <= 1; ix++)
{
for(int iy = 0; iy <= 1; iy++)
{
for(int iz = 0; iz <= 1; iz++)
{
float vx = x * (1 - ix * 2);
float vy = y * (1 - iy * 2);
float vz = z * (1 - iz * 2);
float s3 = vx + vy + vz + 4;
float sx = vy + vz + 3;
float sy = vz + vx + 3;
float sz = vx + vy + 3;
float bx = (2 * vx + vy + vz + 6) / (s3 * sy * sz * (vx + 2));
s += bx;
l += bx * light[0][ix][iy][iz];
float by = (2 * vy + vz + vx + 6) / (s3 * sz * sx * (vy + 2));
s += by;
l += by * light[1][ix][iy][iz];
float bz = (2 * vz + vx + vy + 6) / (s3 * sx * sy * (vz + 2));
s += bz;
l += bz * light[2][ix][iy][iz];
}
}
}
l /= s;
if(l > 15f * 0x20 / 0xFFFF) l = 15f * 0x20 / 0xFFFF;
if(l < 0) l = 0;
return l;
}
protected float getAo(float x, float y, float z)
{
int sx = x < 0 ? 1 : 2;
int sy = y < 0 ? 1 : 2;
int sz = z < 0 ? 1 : 2;
if(x < 0) x++;
if(y < 0) y++;
if(z < 0) z++;
float a = 0;
float[][][] ao = blockInfo.getAo();
a += ao[sx - 1][sy - 1][sz - 1] * (1 - x) * (1 - y) * (1 - z);
a += ao[sx - 1][sy - 1][sz - 0] * (1 - x) * (1 - y) * (0 + z);
a += ao[sx - 1][sy - 0][sz - 1] * (1 - x) * (0 + y) * (1 - z);
a += ao[sx - 1][sy - 0][sz - 0] * (1 - x) * (0 + y) * (0 + z);
a += ao[sx - 0][sy - 1][sz - 1] * (0 + x) * (1 - y) * (1 - z);
a += ao[sx - 0][sy - 1][sz - 0] * (0 + x) * (1 - y) * (0 + z);
a += ao[sx - 0][sy - 0][sz - 1] * (0 + x) * (0 + y) * (1 - z);
a += ao[sx - 0][sy - 0][sz - 0] * (0 + x) * (0 + y) * (0 + z);
a = MathHelper.clamp_float(a, 0, 1);
return a;
}
@Override
public void updateBlockInfo()
{
super.updateBlockInfo();
blockInfo.updateLightMatrix();
}
}

View file

@ -0,0 +1,49 @@
package net.minecraftforge.client.model.pipeline;
import java.util.Arrays;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.EnumFacing;
/**
* Assumes VertexFormatElement is present in the WorlRenderer's vertex format.
*/
public class WorldRendererConsumer implements IVertexConsumer
{
private final WorldRenderer renderer;
private final int[] quadData;
private int v = 0;
public WorldRendererConsumer(WorldRenderer renderer)
{
super();
this.renderer = renderer;
quadData = new int[renderer.getVertexFormat().getNextOffset()/* / 4 * 4 */];
}
public VertexFormat getVertexFormat()
{
return renderer.getVertexFormat();
}
public void put(int e, float... data)
{
LightUtil.pack(data, quadData, getVertexFormat(), v, e);
if(e == getVertexFormat().getElementCount() - 1)
{
v++;
if(v == 4)
{
renderer.addVertexData(quadData);
renderer.checkAndGrow();
Arrays.fill(quadData, 0);
v = 0;
}
}
}
public void setQuadTint(int tint) {}
public void setQuadOrientation(EnumFacing orientation) {}
public void setQuadColored() {}
}

View file

@ -70,6 +70,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
public static boolean disableVersionCheck = false; public static boolean disableVersionCheck = false;
public static int defaultSpawnFuzz = 20; public static int defaultSpawnFuzz = 20;
public static boolean defaultHasSpawnFuzz = true; public static boolean defaultHasSpawnFuzz = true;
public static boolean forgeLightPipelineEnabled = true;
private static Configuration config; private static Configuration config;
@ -222,6 +223,11 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
defaultHasSpawnFuzz = prop.getBoolean(Boolean.TRUE); defaultHasSpawnFuzz = prop.getBoolean(Boolean.TRUE);
propOrder.add(prop.getName()); propOrder.add(prop.getName());
prop = config.get(Configuration.CATEGORY_GENERAL, "forgeLightPipelineEnabled", Boolean.TRUE,
"Enable the forge block rendering pipeline - fixes the lighting of custom models.");
forgeLightPipelineEnabled = prop.getBoolean(Boolean.TRUE);
propOrder.add(prop.getName());
config.setCategoryPropertyOrder(CATEGORY_GENERAL, propOrder); config.setCategoryPropertyOrder(CATEGORY_GENERAL, propOrder);
if (config.hasChanged()) if (config.hasChanged())