Initial support for model visibility system. Implemented it for OBJ, B3D and normal vanilla models; fixed NPE accessing parent state in B3DModel. Fixes #2801.

This commit is contained in:
RainWarrior committed 2016-05-30 01:20:43 +03:00
1 parent 4008983a5d
commit ce481da1d1
6 files changed
+228 -170

No files matched your search

@@ -63,6 +63,7 @@ import net.minecraftforge.client.model.animation.ModelBlockAnimation;
import net.minecraftforge.common.ForgeModContainer;
import net.minecraftforge.common.model.IModelPart;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.Models;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.common.model.animation.IClip;
import net.minecraftforge.common.property.IExtendedBlockState;
@@ -526,6 +527,10 @@ public final class ModelLoader extends ModelBakery
SimpleBakedModel.Builder builder = (new SimpleBakedModel.Builder(model, model.createOverrides())).setTexture(particle);
for(int i = 0; i < model.getElements().size(); i++)
{
if(modelState.apply(Optional.of(Models.getHiddenModelPart(ImmutableList.of(Integer.toString(i))))).isPresent())
{
continue;
}
BlockPart part = model.getElements().get(i);
TRSRTransformation transformation = baseState;
if(newTransforms.get(i) != null)
@@ -47,6 +47,7 @@ import net.minecraftforge.client.model.b3d.B3DModel.Vertex;
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
import net.minecraftforge.common.model.IModelPart;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.Models;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.common.model.animation.IClip;
import net.minecraftforge.common.model.animation.IJoint;
@@ -209,7 +210,14 @@ public enum B3DLoader implements ICustomModelLoader
public Optional<TRSRTransformation> apply(Optional<? extends IModelPart> part)
{
// TODO make more use of Optional
if(!part.isPresent()) return parent.apply(part);
if(!part.isPresent())
{
if(parent != null)
{
return parent.apply(part);
}
return Optional.absent();
}
if(!(part.get() instanceof NodeJoint))
{
return Optional.absent();
@@ -364,6 +372,10 @@ public enum B3DLoader implements ICustomModelLoader
}
}
/**
* @deprecated use AnimationProperty.
*/
@Deprecated
public static enum B3DFrameProperty implements IUnlistedProperty<B3DState>
{
INSTANCE;
@@ -679,26 +691,30 @@ public enum B3DLoader implements ICustomModelLoader
if(quads == null)
{
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
generateQuads(builder, node, this.state);
generateQuads(builder, node, this.state, ImmutableList.<String>of());
quads = builder.build();
}
// TODO: caching?
if(this.state != modelState)
{
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
generateQuads(builder, node, modelState);
generateQuads(builder, node, modelState, ImmutableList.<String>of());
return builder.build();
}
return quads;
}
private void generateQuads(ImmutableList.Builder<BakedQuad> builder, Node<?> node, final IModelState state)
private void generateQuads(ImmutableList.Builder<BakedQuad> builder, Node<?> node, final IModelState state, ImmutableList<String> path)
{
ImmutableList.Builder<String> pathBuilder = ImmutableList.builder();
pathBuilder.addAll(path);
pathBuilder.add(node.getName());
ImmutableList<String> newPath = pathBuilder.build();
for(Node<?> child : node.getNodes().values())
{
generateQuads(builder, child, state);
generateQuads(builder, child, state, newPath);
}
if(node.getKind() instanceof Mesh && meshes.contains(node.getName()))
if(node.getKind() instanceof Mesh && meshes.contains(node.getName()) && !state.apply(Optional.of(Models.getHiddenModelPart(newPath))).isPresent())
{
Mesh mesh = (Mesh)node.getKind();
Collection<Face> faces = mesh.bake(new Function<Node<?>, Matrix4f>()
@@ -32,16 +32,15 @@ import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.client.model.IModelCustomData;
import net.minecraftforge.client.model.IPerspectiveAwareModel;
import net.minecraftforge.client.model.IRetexturableModel;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.client.model.*;
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
import net.minecraftforge.common.model.IModelPart;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.Models;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.common.property.IUnlistedProperty;
import net.minecraftforge.common.property.Properties;
import net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.lang3.tuple.Pair;
@@ -1036,6 +1035,7 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
}
}
@Deprecated
public static class Group implements IModelPart
{
public static final String DEFAULT_NAME = "OBJModel.Default.Element.Name";
@@ -1092,6 +1092,7 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
}
}
@Deprecated
public static class OBJState implements IModelState
{
protected Map<String, Boolean> visibilityMap = Maps.newHashMap();
@@ -1230,6 +1231,7 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
}
}
@Deprecated
public enum OBJProperty implements IUnlistedProperty<OBJState>
{
INSTANCE;
@@ -1262,7 +1264,7 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
private final OBJModel model;
private IModelState state;
private final VertexFormat format;
private Set<BakedQuad> quads;
private ImmutableList<BakedQuad> quads;
private ImmutableMap<String, TextureAtlasSprite> textures;
private TextureAtlasSprite sprite = ModelLoader.White.INSTANCE;
@@ -1277,98 +1279,122 @@ public class OBJModel implements IRetexturableModel, IModelCustomData
public void scheduleRebake()
{
this.quads = null;
}
// FIXME: merge with getQuads
@Override
public List<BakedQuad> getQuads(IBlockState blockState, EnumFacing side, long rand)
{
if(side != null) return ImmutableList.of();
if (side != null) return ImmutableList.of();
if (quads == null)
{
quads = Collections.synchronizedSet(new LinkedHashSet<BakedQuad>());
Set<Face> faces = Collections.synchronizedSet(new LinkedHashSet<Face>());
Optional<TRSRTransformation> transform = Optional.absent();
for (Group g : this.model.getMatLib().getGroups().values())
quads = buildQuads(this.state);
}
if (blockState instanceof IExtendedBlockState)
{
IExtendedBlockState exState = (IExtendedBlockState) blockState;
if (exState.getUnlistedNames().contains(Properties.AnimationProperty))
{
// g.minUVBounds = this.model.getMatLib().minUVBounds;
// g.maxUVBounds = this.model.getMatLib().maxUVBounds;
// FMLLog.info("Group: %s u: [%f, %f] v: [%f, %f]", g.name, g.minUVBounds[0], g.maxUVBounds[0], g.minUVBounds[1], g.maxUVBounds[1]);
if (this.state instanceof OBJState)
IModelState newState = exState.getValue(Properties.AnimationProperty);
if (newState != null)
{
OBJState state = (OBJState) this.state;
if (state.parent != null)
newState = new ModelStateComposition(this.state, newState);
return buildQuads(newState);
}
}
}
return quads;
}
private ImmutableList<BakedQuad> buildQuads(IModelState modelState)
{
List<BakedQuad> quads = Lists.newArrayList();
Collections.synchronizedSet(new LinkedHashSet<BakedQuad>());
Set<Face> faces = Collections.synchronizedSet(new LinkedHashSet<Face>());
Optional<TRSRTransformation> transform = Optional.absent();
for (Group g : this.model.getMatLib().getGroups().values())
{
// g.minUVBounds = this.model.getMatLib().minUVBounds;
// g.maxUVBounds = this.model.getMatLib().maxUVBounds;
// FMLLog.info("Group: %s u: [%f, %f] v: [%f, %f]", g.name, g.minUVBounds[0], g.maxUVBounds[0], g.minUVBounds[1], g.maxUVBounds[1]);
if(modelState.apply(Optional.of(Models.getHiddenModelPart(ImmutableList.of(g.getName())))).isPresent())
{
continue;
}
if (modelState instanceof OBJState)
{
OBJState state = (OBJState) modelState;
if (state.parent != null)
{
transform = state.parent.apply(Optional.<IModelPart>absent());
}
//TODO: can this be replaced by updateStateVisibilityMap(OBJState)?
if (state.getGroupNamesFromMap().contains(Group.ALL))
{
state.visibilityMap.clear();
for (String s : this.model.getMatLib().getGroups().keySet())
{
transform = state.parent.apply(Optional.<IModelPart>absent());
state.visibilityMap.put(s, state.operation.performOperation(true));
}
//TODO: can this be replaced by updateStateVisibilityMap(OBJState)?
if (state.getGroupNamesFromMap().contains(Group.ALL))
}
else if (state.getGroupNamesFromMap().contains(Group.ALL_EXCEPT))
{
List<String> exceptList = state.getGroupNamesFromMap().subList(1, state.getGroupNamesFromMap().size());
state.visibilityMap.clear();
for (String s : this.model.getMatLib().getGroups().keySet())
{
state.visibilityMap.clear();
for (String s : this.model.getMatLib().getGroups().keySet())
if (!exceptList.contains(s))
{
state.visibilityMap.put(s, state.operation.performOperation(true));
}
}
else if (state.getGroupNamesFromMap().contains(Group.ALL_EXCEPT))
{
List<String> exceptList = state.getGroupNamesFromMap().subList(1, state.getGroupNamesFromMap().size());
state.visibilityMap.clear();
for (String s : this.model.getMatLib().getGroups().keySet())
{
if (!exceptList.contains(s))
{
state.visibilityMap.put(s, state.operation.performOperation(true));
}
}
}
else
{
for (String s : state.visibilityMap.keySet())
{
state.visibilityMap.put(s, state.operation.performOperation(state.visibilityMap.get(s)));
}
}
if (state.getGroupsWithVisibility(true).contains(g.getName()))
{
faces.addAll(g.applyTransform(transform));
}
}
else
{
transform = state.apply(Optional.<IModelPart>absent());
for (String s : state.visibilityMap.keySet())
{
state.visibilityMap.put(s, state.operation.performOperation(state.visibilityMap.get(s)));
}
}
if (state.getGroupsWithVisibility(true).contains(g.getName()))
{
faces.addAll(g.applyTransform(transform));
}
}
for (Face f : faces)
else
{
if (this.model.getMatLib().materials.get(f.getMaterialName()).isWhite())
{
for (Vertex v : f.getVertices())
{//update material in each vertex
if (!v.getMaterial().equals(this.model.getMatLib().getMaterial(v.getMaterial().getName())))
{
v.setMaterial(this.model.getMatLib().getMaterial(v.getMaterial().getName()));
}
}
sprite = ModelLoader.White.INSTANCE;
} else sprite = this.textures.get(f.getMaterialName());
UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format);
builder.setContractUVs(true);
builder.setQuadOrientation(EnumFacing.getFacingFromVector(f.getNormal().x, f.getNormal().y, f.getNormal().z));
builder.setTexture(sprite);
Normal faceNormal = f.getNormal();
putVertexData(builder, f.verts[0], faceNormal, TextureCoordinate.getDefaultUVs()[0], sprite);
putVertexData(builder, f.verts[1], faceNormal, TextureCoordinate.getDefaultUVs()[1], sprite);
putVertexData(builder, f.verts[2], faceNormal, TextureCoordinate.getDefaultUVs()[2], sprite);
putVertexData(builder, f.verts[3], faceNormal, TextureCoordinate.getDefaultUVs()[3], sprite);
quads.add(builder.build());
transform = modelState.apply(Optional.<IModelPart>absent());
faces.addAll(g.applyTransform(transform));
}
}
List<BakedQuad> quadList = Collections.synchronizedList(Lists.newArrayList(quads));
return quadList;
for (Face f : faces)
{
if (this.model.getMatLib().materials.get(f.getMaterialName()).isWhite())
{
for (Vertex v : f.getVertices())
{//update material in each vertex
if (!v.getMaterial().equals(this.model.getMatLib().getMaterial(v.getMaterial().getName())))
{
v.setMaterial(this.model.getMatLib().getMaterial(v.getMaterial().getName()));
}
}
sprite = ModelLoader.White.INSTANCE;
}
else sprite = this.textures.get(f.getMaterialName());
UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format);
builder.setContractUVs(true);
builder.setQuadOrientation(EnumFacing.getFacingFromVector(f.getNormal().x, f.getNormal().y, f.getNormal().z));
builder.setTexture(sprite);
Normal faceNormal = f.getNormal();
putVertexData(builder, f.verts[0], faceNormal, TextureCoordinate.getDefaultUVs()[0], sprite);
putVertexData(builder, f.verts[1], faceNormal, TextureCoordinate.getDefaultUVs()[1], sprite);
putVertexData(builder, f.verts[2], faceNormal, TextureCoordinate.getDefaultUVs()[2], sprite);
putVertexData(builder, f.verts[3], faceNormal, TextureCoordinate.getDefaultUVs()[3], sprite);
quads.add(builder.build());
}
return ImmutableList.copyOf(quads);
}
private final void putVertexData(UnpackedBakedQuad.Builder builder, Vertex v, Normal faceNormal, TextureCoordinate defUV, TextureAtlasSprite sprite)
@@ -0,0 +1,18 @@
package net.minecraftforge.common.model;
import com.google.common.collect.ImmutableList;
final class HiddenModelPart implements IModelPart
{
private final ImmutableList<String> path;
HiddenModelPart(ImmutableList<String> path)
{
this.path = path;
}
ImmutableList<String> getPath()
{
return path;
}
}
@@ -0,0 +1,24 @@
package net.minecraftforge.common.model;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
import com.google.common.collect.UnmodifiableIterator;
public enum Models
{
;
public static IModelPart getHiddenModelPart(ImmutableList<String> path)
{
return new HiddenModelPart(path);
}
public static UnmodifiableIterator<String> getParts(IModelPart part)
{
if(part instanceof HiddenModelPart)
{
return ((HiddenModelPart) part).getPath().iterator();
}
return Iterators.emptyIterator();
}
}