diff --git a/DynmapCore/src/main/java/org/dynmap/exporter/OBJExport.java b/DynmapCore/src/main/java/org/dynmap/exporter/OBJExport.java index 180221e7..7df1fcbd 100644 --- a/DynmapCore/src/main/java/org/dynmap/exporter/OBJExport.java +++ b/DynmapCore/src/main/java/org/dynmap/exporter/OBJExport.java @@ -366,8 +366,8 @@ public class OBJExport { /* If no patches, see if custom model */ if(patches == null) { CustomBlockModel cbm = models.getCustomBlockModel(blk); - if(cbm != null) { /* If so, get our meshes */ - patches = cbm.getMeshForBlock(map); + if (cbm != null) { /* If so, get our meshes */ + patches = cbm.getMeshForBlock(map); } } if (patches != null) { diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/CustomBlockModel.java b/DynmapCore/src/main/java/org/dynmap/hdmap/CustomBlockModel.java index b7a2fbc9..ec298d08 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/CustomBlockModel.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/CustomBlockModel.java @@ -43,6 +43,9 @@ public class CustomBlockModel extends HDBlockModel { return render.getMaximumTextureCount(HDBlockModels.pdf); } + public boolean isOnlyBlockStateSensitive() { + return render.isOnlyBlockStateSensitive(); + } private static final RenderPatch[] empty_list = new RenderPatch[0]; public RenderPatch[] getMeshForBlock(MapDataContext ctx) { diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/HDScaledBlockModels.java b/DynmapCore/src/main/java/org/dynmap/hdmap/HDScaledBlockModels.java index ef805ae2..71c1423c 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/HDScaledBlockModels.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/HDScaledBlockModels.java @@ -5,14 +5,22 @@ import org.dynmap.utils.PatchDefinition; public class HDScaledBlockModels { private short[][] modelvectors; - private PatchDefinition[][] patches; - private CustomBlockModel[] custom; + // These are scale invariant - only need once + private static PatchDefinition[][] patches; + private static CustomBlockModel[] custom; public HDScaledBlockModels(int scale) { short[][] blockmodels = new short[DynmapBlockState.getGlobalIndexMax()][]; - PatchDefinition[][] patches = new PatchDefinition[DynmapBlockState.getGlobalIndexMax()][]; - CustomBlockModel[] custom = new CustomBlockModel[DynmapBlockState.getGlobalIndexMax()]; - + PatchDefinition[][] newpatches = null; + if (patches == null) { + newpatches = new PatchDefinition[DynmapBlockState.getGlobalIndexMax()][]; + patches = newpatches; + } + CustomBlockModel[] newcustom = null; + if (custom == null) { + newcustom = new CustomBlockModel[DynmapBlockState.getGlobalIndexMax()]; + custom = newcustom; + } for(Integer gidx : HDBlockModels.models_by_id_data.keySet()) { HDBlockModel m = HDBlockModels.models_by_id_data.get(gidx); @@ -34,18 +42,19 @@ public class HDScaledBlockModels { } } else if(m instanceof HDBlockPatchModel) { - HDBlockPatchModel pm = (HDBlockPatchModel)m; - patches[gidx] = pm.getPatches(); + if (newpatches != null) { + HDBlockPatchModel pm = (HDBlockPatchModel)m; + newpatches[gidx] = pm.getPatches(); + } } else if(m instanceof CustomBlockModel) { - CustomBlockModel cbm = (CustomBlockModel)m; - custom[gidx] = cbm; + if (newcustom != null) { + CustomBlockModel cbm = (CustomBlockModel)m; + newcustom[gidx] = cbm; + } } } - this.modelvectors = blockmodels; - this.patches = patches; - this.custom = custom; } public final short[] getScaledModel(DynmapBlockState blk) { diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java b/DynmapCore/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java index 1ae259be..5b44a113 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; import org.dynmap.Client; import org.dynmap.Color; @@ -27,6 +28,7 @@ import org.dynmap.renderer.RenderPatchFactory.SideVisible; import org.dynmap.storage.MapStorage; import org.dynmap.storage.MapStorageTile; import org.dynmap.utils.BlockStep; +import org.dynmap.utils.DynIntHashMap; import org.dynmap.hdmap.TexturePack.BlockTransparency; import org.dynmap.utils.DynmapBufferedImage; import org.dynmap.utils.LightLevels; @@ -79,6 +81,9 @@ public class IsoHDPerspective implements HDPerspective { private static final BlockStep [] semi_steps = { BlockStep.Y_PLUS, BlockStep.X_MINUS, BlockStep.X_PLUS, BlockStep.Z_MINUS, BlockStep.Z_PLUS }; + // Cache for custom meshes by state (shared, reusable) + private static RenderPatch[][] custom_meshes_by_globalstateindex = null; + private class OurPerspectiveState implements HDPerspectiveState { DynmapBlockState blocktype = DynmapBlockState.AIR; DynmapBlockState lastblocktype = DynmapBlockState.AIR; @@ -554,7 +559,15 @@ public class IsoHDPerspective implements HDPerspective { this.setCustomFluidMesh(patches); } } - else { + // Else, if block state specific model + else if (cbm.isOnlyBlockStateSensitive()) { + patches = this.getCustomMeshForState(bt); // Look up mesh by state + if (patches == null) { // Miss, generate it + patches = cbm.getMeshForBlock(mapiter); + this.setCustomMeshForState(bt, patches); + } + } + else { // Else, block specific patches = this.getCustomMesh(); if (patches == null) { patches = cbm.getMeshForBlock(mapiter); @@ -919,6 +932,12 @@ public class IsoHDPerspective implements HDPerspective { long key = this.mapiter.getBlockKey(); /* Get key for current block */ return (RenderPatch[])custom_meshes.get(key); } + /** + * Get custom mesh for block, if defined (null if not) + */ + public final RenderPatch[] getCustomMeshForState(DynmapBlockState bs) { + return (RenderPatch[]) custom_meshes_by_globalstateindex[bs.globalStateIndex]; + } /** * Save custom mesh for block */ @@ -926,6 +945,12 @@ public class IsoHDPerspective implements HDPerspective { long key = this.mapiter.getBlockKey(); /* Get key for current block */ custom_meshes.put(key, mesh); } + /** + * Set custom mesh for block, if defined (null if not) + */ + public final void setCustomMeshForState(DynmapBlockState bs, RenderPatch[] mesh) { + custom_meshes_by_globalstateindex[bs.globalStateIndex] = mesh; + } /** * Get custom fluid mesh for block, if defined (null if not) */ @@ -943,6 +968,9 @@ public class IsoHDPerspective implements HDPerspective { } public IsoHDPerspective(DynmapCore core, ConfigurationNode configuration) { + if (custom_meshes_by_globalstateindex == null) { + custom_meshes_by_globalstateindex = new RenderPatch[DynmapBlockState.getGlobalIndexMax()][]; + } name = configuration.getString("name", null); if(name == null) { Log.severe("Perspective definition missing name - must be defined and unique"); diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/BoxRenderer.java b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/BoxRenderer.java index c28f568a..a8568e08 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/BoxRenderer.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/BoxRenderer.java @@ -73,4 +73,8 @@ public class BoxRenderer extends CustomRenderer { public RenderPatch[] getRenderPatchList(MapDataContext ctx) { return model; } + @Override + public boolean isOnlyBlockStateSensitive() { + return true; + } } diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/BoxStateRenderer.java b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/BoxStateRenderer.java index 40a4c04e..1c5c798c 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/BoxStateRenderer.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/BoxStateRenderer.java @@ -89,4 +89,8 @@ public class BoxStateRenderer extends CustomRenderer { public RenderPatch[] getRenderPatchList(MapDataContext ctx) { return models[ctx.getBlockType().stateIndex]; } + @Override + public boolean isOnlyBlockStateSensitive() { + return true; + } } diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/ChestStateRenderer.java b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/ChestStateRenderer.java index 639bc7dc..464e1e7b 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/ChestStateRenderer.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/ChestStateRenderer.java @@ -31,4 +31,8 @@ public class ChestStateRenderer extends ChestRenderer { } return models[byIndex[idx].ordinal()]; } + @Override + public boolean isOnlyBlockStateSensitive() { + return true; + } } diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/CuboidRenderer.java b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/CuboidRenderer.java index d3bfc14f..677e6199 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/CuboidRenderer.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/CuboidRenderer.java @@ -123,4 +123,8 @@ public class CuboidRenderer extends CustomRenderer { int idx = ctx.getBlockType().stateIndex; return models[idx % models.length]; } + @Override + public boolean isOnlyBlockStateSensitive() { + return true; + } } diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/FenceGateBlockRenderer.java b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/FenceGateBlockRenderer.java index 8621bac8..cb245de1 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/FenceGateBlockRenderer.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/FenceGateBlockRenderer.java @@ -108,4 +108,8 @@ public class FenceGateBlockRenderer extends CustomRenderer { return meshes[meta & 0x7]; } + @Override + public boolean isOnlyBlockStateSensitive() { + return true; + } } diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/FenceGateBlockStateRenderer.java b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/FenceGateBlockStateRenderer.java index 333dcc91..468ddbfb 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/FenceGateBlockStateRenderer.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/FenceGateBlockStateRenderer.java @@ -120,5 +120,9 @@ public class FenceGateBlockStateRenderer extends CustomRenderer { // 32 states: meta%2=powered|unpowered, (meta/2)%2=open/closed, (meta/4)%2=in-wall/not-in-wall, (meta/8)%4=n/s/w/e return meshes[(meta >> 1) & 0xF]; // Don't care about powered: models are 0-15 } + @Override + public boolean isOnlyBlockStateSensitive() { + return true; + } } diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/FenceWallBlockStateRenderer.java b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/FenceWallBlockStateRenderer.java index 91020a39..a22f552e 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/FenceWallBlockStateRenderer.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/FenceWallBlockStateRenderer.java @@ -216,4 +216,8 @@ public class FenceWallBlockStateRenderer extends CustomRenderer { } return meshes[off]; } + @Override + public boolean isOnlyBlockStateSensitive() { + return true; + } } diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/GlowLichenStateRenderer.java b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/GlowLichenStateRenderer.java index ecf4b531..620e6a54 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/GlowLichenStateRenderer.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/GlowLichenStateRenderer.java @@ -55,4 +55,8 @@ public class GlowLichenStateRenderer extends CustomRenderer { int idx = ((ctx.getBlockType().stateIndex & 0x7C) >> 1) + (ctx.getBlockType().stateIndex & 0x1); // Shift out waterlogged bit return meshes[idx]; } + @Override + public boolean isOnlyBlockStateSensitive() { + return true; + } } diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/HeadRenderer.java b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/HeadRenderer.java index a483cdff..1cd85579 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/HeadRenderer.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/HeadRenderer.java @@ -61,4 +61,8 @@ public class HeadRenderer extends CustomRenderer { else return meshes[0]; } + @Override + public boolean isOnlyBlockStateSensitive() { + return true; + } } diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/PaneStateRenderer.java b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/PaneStateRenderer.java index 910bb7dd..9d42e1c8 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/PaneStateRenderer.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/PaneStateRenderer.java @@ -14,4 +14,8 @@ public class PaneStateRenderer extends PaneRenderer { int meshidx = (((idx & 0x10) == 0) ? SIDE_XP : 0) | (((idx & 0x08) == 0) ? SIDE_ZN : 0) | (((idx & 0x04) == 0) ? SIDE_ZP : 0) | (((idx & 0x01) == 0) ? SIDE_XN : 0); return meshes[meshidx]; } + @Override + public boolean isOnlyBlockStateSensitive() { + return true; + } } diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/RedstoneWireStateRenderer.java b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/RedstoneWireStateRenderer.java index 95f4129c..7eef5828 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/RedstoneWireStateRenderer.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/RedstoneWireStateRenderer.java @@ -45,4 +45,8 @@ public class RedstoneWireStateRenderer extends RedstoneWireRenderer { } return getMesh(pidx); } + @Override + public boolean isOnlyBlockStateSensitive() { + return true; + } } diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/RotatedBoxRenderer.java b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/RotatedBoxRenderer.java index 41d1e3f6..5d8364d9 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/RotatedBoxRenderer.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/RotatedBoxRenderer.java @@ -131,4 +131,8 @@ public class RotatedBoxRenderer extends CustomRenderer { Log.info("Unmatched rotation index: " + textureIdx + " for " + ctx.getBlockType()); return models[0]; } + @Override + public boolean isOnlyBlockStateSensitive() { + return idx_attrib == null; + } } diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/RotatedPatchRenderer.java b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/RotatedPatchRenderer.java index f127261c..a2dbec01 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/RotatedPatchRenderer.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/RotatedPatchRenderer.java @@ -154,4 +154,8 @@ public class RotatedPatchRenderer extends CustomRenderer { return basemodel; } } + @Override + public boolean isOnlyBlockStateSensitive() { + return idx_attrib == null; + } } diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/StairStateRenderer.java b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/StairStateRenderer.java index 0fa80a30..2642a067 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/StairStateRenderer.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/StairStateRenderer.java @@ -159,4 +159,8 @@ public class StairStateRenderer extends CustomRenderer { } return rp; } + @Override + public boolean isOnlyBlockStateSensitive() { + return true; + } } diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/VineStateRenderer.java b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/VineStateRenderer.java index a0ef20da..f7ff12ce 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/VineStateRenderer.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/VineStateRenderer.java @@ -52,4 +52,8 @@ public class VineStateRenderer extends CustomRenderer { public RenderPatch[] getRenderPatchList(MapDataContext ctx) { return meshes[ctx.getBlockType().stateIndex]; } + @Override + public boolean isOnlyBlockStateSensitive() { + return true; + } } diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/WallHeadRenderer.java b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/WallHeadRenderer.java index c1ec25a2..2f2b625e 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/WallHeadRenderer.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/WallHeadRenderer.java @@ -61,4 +61,8 @@ public class WallHeadRenderer extends CustomRenderer { else return meshes[0]; } + @Override + public boolean isOnlyBlockStateSensitive() { + return true; + } } diff --git a/DynmapCoreAPI/src/main/java/org/dynmap/renderer/CustomRenderer.java b/DynmapCoreAPI/src/main/java/org/dynmap/renderer/CustomRenderer.java index da9a6a5f..ed59217a 100644 --- a/DynmapCoreAPI/src/main/java/org/dynmap/renderer/CustomRenderer.java +++ b/DynmapCoreAPI/src/main/java/org/dynmap/renderer/CustomRenderer.java @@ -184,4 +184,12 @@ public abstract class CustomRenderer { public RenderPatch getSidePatch(RenderPatchFactory rpf, int side, int rot, int textureidx) { return getSidePatch(rpf, side, 0.0, 0.0, 1.0, 0.0, 1.0, rot, textureidx); } + /** + * Test if the given renderer is purely a function of the block state of the given block (that is, not + * directly tied to neighbor blocks, location, or other factors). If true, the returned model for a getRenderPatchList() is + * cached by block state, versus by specific block location/instance. + */ + public boolean isOnlyBlockStateSensitive() { + return false; + } } diff --git a/DynmapCoreAPI/src/main/java/org/dynmap/renderer/DynmapBlockState.java b/DynmapCoreAPI/src/main/java/org/dynmap/renderer/DynmapBlockState.java index 4c14d800..b4c2af51 100644 --- a/DynmapCoreAPI/src/main/java/org/dynmap/renderer/DynmapBlockState.java +++ b/DynmapCoreAPI/src/main/java/org/dynmap/renderer/DynmapBlockState.java @@ -549,4 +549,18 @@ public class DynmapBlockState { public String toString() { return fullName; } + // Equals check + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj instanceof DynmapBlockState) { + return ((DynmapBlockState)obj).globalStateIndex == this.globalStateIndex; + } + return false; + } + // Hashcode + @Override + public int hashCode() { + return this.globalStateIndex; + } }