diff --git a/fabric-renderer-v0/build.gradle b/fabric-renderer-v0/build.gradle new file mode 100644 index 0000000000..9fed8941c6 --- /dev/null +++ b/fabric-renderer-v0/build.gradle @@ -0,0 +1,6 @@ +archivesBaseName = "fabric-renderer-v0" +version = getSubprojectVersion(project, "0.1.0") + +dependencies { + compile project(path: ':fabric-api-base', configuration: 'dev') +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/Renderer.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/Renderer.java new file mode 100644 index 0000000000..8948ce55fb --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/Renderer.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.api; + +import net.fabricmc.fabric.renderer.v1.api.material.MaterialFinder; +import net.fabricmc.fabric.renderer.v1.api.material.RenderMaterial; +import net.fabricmc.fabric.renderer.v1.api.mesh.MeshBuilder; +import net.minecraft.util.Identifier; + +/** + * Interface for rendering plug-ins that provide enhanced capabilities + * for model lighting, buffering and rendering. Such plug-ins implement the + * enhanced model rendering interfaces specified by the Fabric API.

+ */ +public interface Renderer { + /** + * Obtain a new {@link MeshBuilder} instance used to create + * baked models with enhanced features.

+ * + * Renderer does not retain a reference to returned instances and they should be re-used for + * multiple models when possible to avoid memory allocation overhead. + */ + MeshBuilder meshBuilder(); + + /** + * Obtain a new {@link MaterialFinder} instance used to retrieve + * standard {@link RenderMaterial} instances.

+ * + * Renderer does not retain a reference to returned instances and they should be re-used for + * multiple materials when possible to avoid memory allocation overhead. + */ + MaterialFinder materialFinder(); + + /** + * Return a material previously registered via {@link #registerMaterial(Identifier, RenderMaterial)}. + * Will return null if no material was found matching the given identifier. + */ + RenderMaterial materialById(Identifier id); + + /** + * Register a material for re-use by other mods or models within a mod. + * The registry does not persist registrations - mods must create and register + * all materials at game initialization.

+ * + * Returns false if a material with the given identifier is already present, + * leaving the existing material intact. + */ + boolean registerMaterial(Identifier id, RenderMaterial material); +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/RendererAccess.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/RendererAccess.java new file mode 100644 index 0000000000..ccd6a390ed --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/RendererAccess.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.api; + +import net.fabricmc.fabric.renderer.v1.impl.RendererAccessImpl; + +/** + * Registration and access for rendering extensions. + */ +public interface RendererAccess { + RendererAccess INSTANCE = RendererAccessImpl.INSTANCE; + + /** + * Rendering extension mods must implement {@link Renderer} and + * call this method during initialization.

+ * + * Only one {@link Renderer} plug-in can be active in any game instance. + * If a second mod attempts to register this method will throw an UnsupportedOperationException. + */ + void registerRenderer(Renderer plugin); + + /** + * Access to the current {@link Renderer} for creating and retrieving model builders + * and materials. Will return null if no render plug in is active. + */ + Renderer getRenderer(); + + /** + * Performant test for {@link #getRenderer()} != null; + */ + boolean hasRenderer(); +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/material/MaterialFinder.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/material/MaterialFinder.java new file mode 100644 index 0000000000..9ee9850aa9 --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/material/MaterialFinder.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.api.material; + +import net.fabricmc.fabric.renderer.v1.api.Renderer; +import net.fabricmc.fabric.renderer.v1.api.mesh.QuadEmitter; +import net.fabricmc.fabric.renderer.v1.api.render.RenderContext; +import net.minecraft.block.Block; +import net.minecraft.block.BlockRenderLayer; + +/** + * Finds standard {@link RenderMaterial} instances used to communicate + * quad rendering characteristics to a {@link RenderContext}.

+ * + * Must be obtained via {@link Renderer#materialFinder()}. + */ +public interface MaterialFinder { + /** + * Returns the standard material encoding all + * of the current settings in this finder. The settings in + * this finder are not changed.

+ * + * Resulting instances can and should be re-used to prevent + * needless memory allocation. {@link Renderer} implementations + * may or may not cache standard material instances. + */ + RenderMaterial find(); + + /** + * Resets this instance to default values. Values will match those + * in effect when an instance is newly obtained via {@link Renderer#materialFinder()}. + */ + MaterialFinder clear(); + + /** + * + * Reserved for future use. Behavior for values > 1 is currently undefined. + */ + MaterialFinder spriteDepth(int depth); + + /** + * Defines how sprite pixels will be blended with the scene. + * Accepts {link @BlockRenderLayer} values and blending behavior + * will emulate the way that Minecraft renders each pass. But this does + * NOT mean the sprite will be rendered in a specific render pass - some + * implementations may not use the standard Minecraft render passes.

+ * + * CAN be null and is null by default. A null value means the renderer + * will use {@link Block#getRenderLayer()} for the associate block, or + * {@link BlockRenderLayer#TRANSLUCENT} for item renders. (Normal Minecraft rendering) + */ + MaterialFinder blendMode(int spriteIndex, BlockRenderLayer blendMode); + + /** + * Vertex color(s) will be modified for quad color index unless disabled.

+ */ + MaterialFinder disableColorIndex(int spriteIndex, boolean disable); + + /** + * Vertex color(s) will be modified for diffuse shading unless disabled. + */ + MaterialFinder disableDiffuse(int spriteIndex, boolean disable); + + /** + * Vertex color(s) will be modified for ambient occlusion unless disabled. + */ + MaterialFinder disableAo(int spriteIndex, boolean disable); + + /** + * When true, sprite texture and color will be rendered at full brightness. + * Lightmap values provided via {@link QuadEmitter#lightmap(int)} will be ignored. + * False by default

+ * + * This is the preferred method for emissive lighting effects. Some renderers + * with advanced lighting models may not use block lightmaps and this method will + * allow per-sprite emissive lighting in future extensions that support overlay sprites.

+ * + * Note that color will still be modified by diffuse shading and ambient occlusion, + * unless disabled via {@link #disableAo(int, boolean)} and {@link #disableDiffuse(int, boolean)}. + */ + MaterialFinder emissive(int spriteIndex, boolean isEmissive); +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/material/RenderMaterial.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/material/RenderMaterial.java new file mode 100644 index 0000000000..058d91cb4c --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/material/RenderMaterial.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.api.material; + +import net.fabricmc.fabric.renderer.v1.api.Renderer; +import net.fabricmc.fabric.renderer.v1.api.mesh.MeshBuilder; +import net.fabricmc.fabric.renderer.v1.api.mesh.MutableQuadView; +import net.minecraft.block.Block; +import net.minecraft.util.Identifier; + +/** + * All model quads have an associated render material governing + * how the quad will be rendered.

+ * + * A material instance is always immutable and thread-safe. References to a material + * remain valid until the end of the current game session.

+ * + * Materials can be registered and shared between mods using {@link Renderer#registerMaterial(net.minecraft.util.Identifier, RenderMaterial)}. + * The registering mod is responsible for creating each registered material at startup.

+ * + * Materials are not required to know their registration identity, and two materials + * with the same attributes may or may not satisfy equality and identity tests. Model + * implementations should never attempt to analyze materials or implement control logic based on them. + * They are only tokens for communicating quad attributes to the ModelRenderer.

+ * + * There are three classes of materials...

+ * + * STANDARD MATERIALS

+ * + * Standard materials have "normal" rendering with control over lighting, + * color, and texture blending. In the default renderer, "normal" rendering + * emulates unmodified Minecraft. Other renderers may offer a different aesthetic.

+ * + * The number of standard materials is finite, but not necessarily small. + * To find a standard material, use {@link Renderer#materialFinder()}.

+ * + * All renderer implementations should support standard materials.

+ * + * SHADER MATERIALS

+ * + * Shader materials are standard materials with a vertex and fragment shader attached. + * The quad attributes for standard materials have standard vertex attribute bindings + * for cross-compatibility of shaders. Vertex colors will be modified (lit) by the + * renderer before the vertex shader runs, unless lighting (AO and diffuse) has been + * disabled in the standard material.

+ * + * Shader materials do not have to implement any sort of "standard" render, and the quad + * vertex attributes can be re-purposed to fit the needs of the shader author.

+ * + * Shader materials are an optional {@link Renderer} feature. + * {@link Renderer#shaderManager()} will return null if shaders are unsupported.

+ * + * SPECIAL MATERIALS

+ * + * Special materials are implemented directly by the Renderer implementation, typically + * with the aim of providing advanced/extended features. Such materials may offer additional + * vertex attributes via extensions to {@link MeshBuilder} and {@link MutableQuadView}.

+ * + * Special materials can be obtained using {@link Renderer#materialById(Identifier)} + * with a known identifier. Renderers may provide other means of access. Popular + * special materials could be implemented by multiple renderers, however there is + * no requirement that special materials be cross-compatible. + */ +public interface RenderMaterial { + /** + * This will be identical to the material that would be obtained by calling {@link MaterialFinder#find()} + * on a new, unaltered, {@link MaterialFinder} instance. It is defined here for clarity and convenience. + * + * Quads using this material use {@link Block#getRenderLayer()} of the associated block to determine texture blending, + * honor block color index, are non-emissive, and apply both diffuse and ambient occlusion shading to vertex colors.

+ * + * All standard, non-fluid baked models are rendered using this material. + */ + Identifier MATERIAL_STANDARD = new Identifier("fabric", "standard"); + + /** + * How many sprite color/uv coordinates are in the material. + * Behavior for values > 1 is currently undefined. + * See {@link MaterialFinder#spriteDepth(int)} + */ + int spriteDepth(); +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/mesh/Mesh.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/mesh/Mesh.java new file mode 100644 index 0000000000..799beb3dd5 --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/mesh/Mesh.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.api.mesh; + +import java.util.function.Consumer; + +import net.fabricmc.fabric.renderer.v1.api.Renderer; + +/** + * A bundle of one or more {@link QuadView} instances encoded by the renderer, + * typically via {@link Renderer#meshBuilder()}.

+ * + * Similar in purpose to the List instances returned by BakedModel, but + * affords the renderer the ability to optimize the format for performance + * and memory allocation.

+ * + * Only the renderer should implement or extend this interface. + */ +public interface Mesh { + /** + * Use to access all of the quads encoded in this mesh. The quad instances + * sent to the consumer will likely be threadlocal/reused and should never + * be retained by the consumer. + */ + public void forEach(Consumer consumer); +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/mesh/MeshBuilder.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/mesh/MeshBuilder.java new file mode 100644 index 0000000000..89b4a28ce6 --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/mesh/MeshBuilder.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.api.mesh; + +import net.minecraft.client.render.BufferBuilder; + +/** + * Similar in purpose to {@link BufferBuilder} but simpler + * and not tied to NIO or any other specific implementation, + * plus designed to handle both static and dynamic building.

+ * + * Decouples models from the vertex format(s) used by + * ModelRenderer to allow compatibility across diverse implementations.

+ */ +public interface MeshBuilder { + /** + * Returns the {@link QuadEmitter} used to append quad to this mesh. + * Calling this method a second time invalidates any prior result. + * Do not retain references outside the context of building the mesh. + */ + QuadEmitter getEmitter(); + + /** + * Returns a new {@link Mesh} instance containing all + * quads added to this builder and resets the builder to an empty state

+ */ + Mesh build(); +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/mesh/MutableQuadView.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/mesh/MutableQuadView.java new file mode 100644 index 0000000000..44c0698430 --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/mesh/MutableQuadView.java @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.api.mesh; + +import net.fabricmc.fabric.renderer.v1.api.Renderer; +import net.fabricmc.fabric.renderer.v1.api.material.MaterialFinder; +import net.fabricmc.fabric.renderer.v1.api.material.RenderMaterial; +import net.minecraft.client.render.model.BakedQuad; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.util.math.Vector3f; +import net.minecraft.util.math.Direction; + +/** + * A mutable {@link QuadView} instance. The base interface for + * {@link QuadEmitter} and for dynamic renders/mesh transforms.

+ * + * Instances of {@link MutableQuadView} will practically always be + * threadlocal and/or reused - do not retain references.

+ * + * Only the renderer should implement or extend this interface. + */ +public interface MutableQuadView extends QuadView { + /** + * Causes texture to appear with no rotation. + * Pass in bakeFlags parameter to {@link #spriteBake(int, Sprite, int)}. + */ + int BAKE_ROTATE_NONE = 0; + + /** + * Causes texture to appear rotated 90 deg. relative to nominal face. + * Pass in bakeFlags parameter to {@link #spriteBake(int, Sprite, int)}. + */ + int BAKE_ROTATE_90 = 1; + + /** + * Causes texture to appear rotated 180 deg. relative to nominal face. + * Pass in bakeFlags parameter to {@link #spriteBake(int, Sprite, int)}. + */ + int BAKE_ROTATE_180 = 2; + + /** + * Causes texture to appear rotated 270 deg. relative to nominal face. + * Pass in bakeFlags parameter to {@link #spriteBake(int, Sprite, int)}. + */ + int BAKE_ROTATE_270 = 3; + + /** + * When enabled, texture coordinate are assigned based on vertex position. + * Any existing uv coordinates will be replaced. + * Pass in bakeFlags parameter to {@link #spriteBake(int, Sprite, int)}.

+ * + * UV lock always derives texture coordinates based on nominal face, even + * when the quad is not co-planar with that face, and the result is + * the same as if the quad were projected onto the nominal face, which + * is usually the desired result.

+ */ + int BAKE_LOCK_UV = 4; + + /** + * When set, U texture coordinates for the given sprite are + * flipped as part of baking. Can be useful for some randomization + * and texture mapping scenarios. Results are different than what + * can be obtained via rotation and both can be applied. + * Pass in bakeFlags parameter to {@link #spriteBake(int, Sprite, int)}. + */ + int BAKE_FLIP_U = 8; + + /** + * Same as {@link MutableQuadView#BAKE_FLIP_U} but for V coordinate. + */ + int BAKE_FLIP_V = 16; + + /** + * UV coordinates by default are assumed to be 0-16 scale for consistency + * with conventional Minecraft model format. This is scaled to 0-1 during + * baking before interpolation. Model loaders that already have 0-1 coordinates + * can avoid wasteful multiplication/division by passing 0-1 coordinates directly. + * Pass in bakeFlags parameter to {@link #spriteBake(int, Sprite, int)}. + */ + int BAKE_NORMALIZED = 32; + + /** + * Assigns a different material to this quad. Useful for transformation of + * existing meshes because lighting and texture blending are controlled by material.

+ */ + MutableQuadView material(RenderMaterial material); + + /** + * If non-null, quad is coplanar with a block face which, if known, simplifies + * or shortcuts geometric analysis that might otherwise be needed. + * Set to null if quad is not coplanar or if this is not known. + * Also controls face culling during block rendering.

+ * + * Null by default.

+ * + * When called with a non-null value, also sets {@link #nominalFace(Direction)} + * to the same value.

+ * + * This is different than the value reported by {@link BakedQuad#getFace()}. That value + * is computed based on face geometry and must be non-null in vanilla quads. + * That computed value is returned by {@link #lightFace()}. + */ + MutableQuadView cullFace(Direction face); + + /** + * Provides a hint to renderer about the facing of this quad. Not required, + * but if provided can shortcut some geometric analysis if the quad is parallel to a block face. + * Should be the expected value of {@link #lightFace()}. Value will be confirmed + * and if invalid the correct light face will be calculated.

+ * + * Null by default, and set automatically by {@link #cullFace()}.

+ * + * Models may also find this useful as the face for texture UV locking and rotation semantics.

+ * + * NOTE: This value is not persisted independently when the quad is encoded. + * When reading encoded quads, this value will always be the same as {@link #lightFace()}. + */ + MutableQuadView nominalFace(Direction face); + + /** + * Value functions identically to {@link BakedQuad#getColorIndex()} and is + * used by renderer / model builder in same way. Default value is -1. + */ + MutableQuadView colorIndex(int colorIndex); + + /** + * Enables bulk vertex data transfer using the standard Minecraft vertex formats. + * This method should be performant whenever caller's vertex representation makes it feasible.

+ * + * Calling this method does not emit the quad. + */ + MutableQuadView fromVanilla(int[] quadData, int startIndex, boolean isItem); + + /** + * Encodes an integer tag with this quad that can later be retrieved via + * {@link QuadView#tag()}. Useful for models that want to perform conditional + * transformation or filtering on static meshes. + */ + MutableQuadView tag(int tag); + + /** + * Sets the geometric vertex position for the given vertex, + * relative to block origin. (0,0,0). Minecraft rendering is designed + * for models that fit within a single block space and is recommended + * that coordinates remain in the 0-1 range, with multi-block meshes + * split into multiple per-block models. + */ + MutableQuadView pos(int vertexIndex, float x, float y, float z); + + /** + * Same as {@link #pos(float, float, float)} but accepts vector type. + */ + default MutableQuadView pos(int vertexIndex, Vector3f vec) { + return pos(vertexIndex, vec.x(), vec.y(), vec.z()); + } + + /** + * Adds a vertex normal. Models that have per-vertex + * normals should include them to get correct lighting when it matters. + * Computed face normal is used when no vertex normal is provided.

+ * + * {@link Renderer} implementations should honor vertex normals for + * diffuse lighting - modifying vertex color(s) or packing normals in the vertex + * buffer as appropriate for the rendering method/vertex format in effect. + */ + MutableQuadView normal(int vertexIndex, float x, float y, float z); + + /** + * Same as {@link #normal(float, float, float, extra)} but accepts vector type. + */ + default MutableQuadView normal(int vertexIndex, Vector3f vec) { + return normal(vertexIndex, vec.x(), vec.y(), vec.z()); + } + + /** + * Accept vanilla lightmap values. Input values will override lightmap values + * computed from world state if input values are higher. Exposed for completeness + * but some rendering implementations with non-standard lighting model may not honor it.

+ * + * For emissive rendering, it is better to use {@link MaterialFinder#emissive(int, boolean)}. + */ + MutableQuadView lightmap(int vertexIndex, int lightmap); + + /** + * Convenience: set lightmap for all vertices at once.

+ * + * For emissive rendering, it is better to use {@link MaterialFinder#emissive(int, boolean)}. + * See {@link #lightmap(int, int)}. + */ + default MutableQuadView lightmap(int b0, int b1, int b2, int b3) { + lightmap(0, b0); + lightmap(1, b1); + lightmap(2, b2); + lightmap(3, b3); + return this; + } + + /** + * Set sprite color. Behavior for spriteIndex values > 0 is currently undefined. + */ + MutableQuadView spriteColor(int vertexIndex, int spriteIndex, int color); + + /** + * Convenience: set sprite color for all vertices at once. Behavior for spriteIndex values > 0 is currently undefined. + */ + default MutableQuadView spriteColor(int spriteIndex, int c0, int c1, int c2, int c3) { + spriteColor(0, spriteIndex, c0); + spriteColor(1, spriteIndex, c1); + spriteColor(2, spriteIndex, c2); + spriteColor(3, spriteIndex, c3); + return this; + } + + /** + * Set sprite atlas coordinates. Behavior for spriteIndex values > 0 is currently undefined. + */ + MutableQuadView sprite(int vertexIndex, int spriteIndex, float u, float v); + + /** + * Assigns sprite atlas u,v coordinates to this quad for the given sprite. + * Can handle UV locking, rotation, interpolation, etc. Control this behavior + * by passing additive combinations of the BAKE_ flags defined in this interface. + * Behavior for spriteIndex values > 0 is currently undefined. + */ + MutableQuadView spriteBake(int spriteIndex, Sprite sprite, int bakeFlags); +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/mesh/QuadEmitter.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/mesh/QuadEmitter.java new file mode 100644 index 0000000000..62ecc458f9 --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/mesh/QuadEmitter.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.api.mesh; + +import net.fabricmc.fabric.renderer.v1.api.material.RenderMaterial; +import net.fabricmc.fabric.renderer.v1.api.render.RenderContext; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.util.math.Vector3f; +import net.minecraft.util.math.Direction; + +/** + * Specialized {@link MutableQuadView} obtained via {@link MeshBuilder#getEmitter()} + * to append quads during mesh building.

+ * + * Also obtained from {@link RenderContext#getEmitter(RenderMaterial)} to submit + * dynamic quads one-by-one at render time.

+ * + * Instances of {@link QuadEmitter} will practically always be + * threadlocal and/or reused - do not retain references.

+ * + * Only the renderer should implement or extend this interface. + */ +public interface QuadEmitter extends MutableQuadView { + @Override + QuadEmitter material(RenderMaterial material); + + @Override + QuadEmitter cullFace(Direction face); + + @Override + QuadEmitter nominalFace(Direction face); + + @Override + QuadEmitter colorIndex(int colorIndex); + + @Override + QuadEmitter fromVanilla(int[] quadData, int startIndex, boolean isItem); + + @Override + QuadEmitter tag(int tag); + + @Override + QuadEmitter pos(int vertexIndex, float x, float y, float z); + + @Override + default QuadEmitter pos(int vertexIndex, Vector3f vec) { + MutableQuadView.super.pos(vertexIndex, vec); + return this; + } + + @Override + default QuadEmitter normal(int vertexIndex, Vector3f vec) { + MutableQuadView.super.normal(vertexIndex, vec); + return this; + } + + @Override + QuadEmitter lightmap(int vertexIndex, int lightmap); + + @Override + default QuadEmitter lightmap(int b0, int b1, int b2, int b3) { + MutableQuadView.super.lightmap(b0, b1, b2, b3); + return this; + } + + @Override + QuadEmitter spriteColor(int vertexIndex, int spriteIndex, int color); + + @Override + default QuadEmitter spriteColor(int spriteIndex, int c0, int c1, int c2, int c3) { + MutableQuadView.super.spriteColor(spriteIndex, c0, c1, c2, c3); + return this; + } + + @Override + QuadEmitter sprite(int vertexIndex, int spriteIndex, float u, float v); + + default QuadEmitter spriteUnitSquare(int spriteIndex) { + sprite(0, spriteIndex, 0, 0); + sprite(1, spriteIndex, 0, 1); + sprite(2, spriteIndex, 1, 1); + sprite(3, spriteIndex, 1, 0); + return this; + } + + @Override + QuadEmitter spriteBake(int spriteIndex, Sprite sprite, int bakeFlags); + + /** + * Helper method to assign vertex coordinates for a square aligned with the given face. + * Ensures that vertex order is consistent with vanilla convention. (Incorrect order can + * lead to bad AO lighting.)

+ * + * Square will be parallel to the given face and coplanar with the face if depth == 0. + * All coordinates are normalized (0-1). + */ + default QuadEmitter square(Direction nominalFace, float left, float bottom, float right, float top, float depth) { + cullFace(depth == 0 ? nominalFace : null); + nominalFace(nominalFace); + switch(nominalFace) + { + case UP: + depth = 1 - depth; + top = 1 - top; + bottom = 1 - bottom; + + case DOWN: + pos(0, left, depth, top); + pos(1, left, depth, bottom); + pos(2, right, depth, bottom); + pos(3, right, depth, top); + break; + + case EAST: + depth = 1 - depth; + left = 1 - left; + right = 1 - right; + + case WEST: + pos(0, depth, top, left); + pos(1, depth, bottom, left); + pos(2, depth, bottom, right); + pos(3, depth, top, right); + break; + + case SOUTH: + depth = 1 - depth; + left = 1 - left; + right = 1 - right; + + case NORTH: + pos(0, right, top, depth); + pos(1, right, bottom, depth); + pos(2, left, bottom, depth); + pos(3, left, top, depth); + break; + } + return this; + } + + /** + * In static mesh building, causes quad to be appended to the mesh being built. + * In a dynamic render context, create a new quad to be output to rendering. + * In both cases, current instance is reset to default values. + */ + QuadEmitter emit(); +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/mesh/QuadView.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/mesh/QuadView.java new file mode 100644 index 0000000000..3009f8d8c1 --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/mesh/QuadView.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.api.mesh; + +import net.fabricmc.fabric.renderer.v1.api.material.RenderMaterial; +import net.minecraft.client.render.model.BakedQuad; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.util.math.Vector3f; +import net.minecraft.util.math.Direction; + +/** + * Interface for reading quad data encoded by {@link MeshBuilder}. + * Enables models to do analysis, re-texturing or translation without knowing the + * renderer's vertex formats and without retaining redundant information.

+ * + * Only the renderer should implement or extend this interface. + */ +public interface QuadView { + /** + * Reads baked vertex data and outputs standard baked quad + * vertex data in the given array and location.

+ * + * @param spriteIndex The sprite to be used for the quad. + * Behavior for values > 0 is currently undefined. + * + * @param target Target array for the baked quad data. + * + * @param targetIndex Starting position in target array - array must have + * at least 28 elements available at this index. + * + * @param isItem If true, will output vertex normals. Otherwise will output + * lightmaps, per Minecraft vertex formats for baked models. + */ + void toVanilla(int spriteIndex, int[] target, int targetIndex, boolean isItem); + + /** + * Extracts all quad properties except material to the given {@link MutableQuadView} instance. + * Must be used before calling {@link MutableQuadView#emit()} on the target instance. + * Meant for re-texturing, analysis and static transformation use cases. + */ + void copyTo(MutableQuadView target); + + /** + * Retrieves the material serialized with the quad. + */ + RenderMaterial material(); + + /** + * Retrieves the quad color index serialized with the quad. + */ + int colorIndex(); + + /** + * Equivalent to {@link BakedQuad#getFace()}. This is the face used for vanilla lighting + * calculations and will be the block face to which the quad is most closely aligned. Always + * the same as cull face for quads that are on a block face, but never null.

+ */ + Direction lightFace(); + + /** + * If non-null, quad should not be rendered in-world if the + * opposite face of a neighbor block occludes it.

+ * + * See {@link MutableQuadView#cullFace(Direction)}. + */ + Direction cullFace(); + + /** + * See {@link MutableQuadView#nominalFace(Direction)}. + */ + Direction nominalFace(); + + /** + * Normal of the quad as implied by geometry. Will be invalid + * if quad vertices are not co-planar. Typically computed lazily + * on demand and not encoded.

+ * + * Not typically needed by models. Exposed to enable standard lighting + * utility functions for use by renderers. + */ + Vector3f faceNormal(); + + /** + * Generates a new BakedQuad instance with texture + * coordinates and colors from the given sprite.

+ * + * @param source Data previously packed by {@link MeshBuilder}. + * + * @param sourceIndex Index where packed data starts. + * + * @param spriteIndex The sprite to be used for the quad. + * Behavior for values > 0 is currently undefined. + * + * @param sprite {@link MutableQuadView} does not serialize sprites + * so the sprite must be provided by the caller. + * + * @param isItem If true, will output vertex normals. Otherwise will output + * lightmaps, per Minecraft vertex formats for baked models. + * + * @return A new baked quad instance with the closest-available appearance + * supported by vanilla features. Will retain emissive light maps, for example, + * but the standard Minecraft renderer will not use them. + */ + default BakedQuad toBakedQuad(int spriteIndex, Sprite sprite, boolean isItem) { + int vertexData[] = new int[28]; + toVanilla(spriteIndex, vertexData, 0, isItem); + return new BakedQuad(vertexData, colorIndex(), lightFace(), sprite); + } + + /** + * Retrieves the integer tag encoded with this quad via {@link MutableQuadView#tag(int)}. + * Will return zero if no tag was set. For use by models. + */ + int tag(); + + /** + * Pass a non-null target to avoid allocation - will be returned with values. + * Otherwise returns a new instance. + * See {@link VertexEditor#pos(float, float, float)} + */ + Vector3f copyPos(int vertexIndex, Vector3f target); + + /** + * Convenience: access x, y, z by index 0-2 + */ + float posByIndex(int vertexIndex, int coordinateIndex); + + /** + * Geometric position, x coordinate. + */ + float x(int vertexIndex); + + /** + * Geometric position, y coordinate. + */ + float y(int vertexIndex); + + /** + * Geometric position, z coordinate. + */ + float z(int vertexIndex); + + /** + * If false, no vertex normal was provided. + * Lighting should use face normal in that case. + * See {@link VertexEditor#normal(float, float, float, float)} + */ + boolean hasNormal(int vertexIndex); + + /** + * Pass a non-null target to avoid allocation - will be returned with values. + * Otherwise returns a new instance. Returns null if normal not present. + */ + Vector3f copyNormal(int vertexIndex, Vector3f target); + + /** + * Will return {@link Float#NaN} if normal not present. + */ + float normalX(int vertexIndex); + + /** + * Will return {@link Float#NaN} if normal not present. + */ + float normalY(int vertexIndex); + + /** + * Will return {@link Float#NaN} if normal not present. + */ + float normalZ(int vertexIndex); + + /** + * Minimum block brightness. Zero if not set. + */ + int lightmap(int vertexIndex); + + /** + * Retrieve vertex color. + */ + int spriteColor(int vertexIndex, int spriteIndex); + + /** + * Retrieve horizontal sprite atlas coordinates. + */ + float spriteU(int vertexIndex, int spriteIndex); + + /** + * Retrieve vertical sprite atlas coordinates + */ + float spriteV(int vertexIndex, int spriteIndex); +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/model/DynamicModelBlockEntity.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/model/DynamicModelBlockEntity.java new file mode 100644 index 0000000000..721c485eaf --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/model/DynamicModelBlockEntity.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.api.model; + +import net.minecraft.block.entity.BlockEntity; + +/** + * Interface for {@link BlockEntity}s which provide dynamic model state data.

+ * + * Dynamic model state data is separate from BlockState, and will be + * cached during render chunk building on the main thread (safely) and accessible + * during chunk rendering on non-main threads.

+ * + * For this reason, please ensure that all accesses to the passed model data are + * thread-safe. This can be achieved by, for example, passing a pre-generated + * immutable object, or ensuring all gets performed on the passed object are atomic + * and well-checked for unusual states.

+ */ +@FunctionalInterface +public interface DynamicModelBlockEntity { + /** + * @return The model state data provided by this block entity. Can be null. + */ + Object getDynamicModelData(); +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/model/FabricBakedModel.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/model/FabricBakedModel.java new file mode 100644 index 0000000000..8759b319d9 --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/model/FabricBakedModel.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.api.model; + +import java.util.Random; +import java.util.function.Supplier; + +import net.fabricmc.fabric.renderer.v1.api.Renderer; +import net.fabricmc.fabric.renderer.v1.api.render.RenderContext; +import net.fabricmc.fabric.renderer.v1.api.render.TerrainBlockView; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.block.BlockModelRenderer; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ExtendedBlockView; + +/** + * Interface for baked models that output meshes with enhanced rendering features. + * Can also be used to generate or customize outputs based on world state instead of + * or in addition to block state when render chunks are rebuilt.

+ * + * Note for {@link Renderer} implementors: Fabric causes BakedModel to extend this + * interface with {@link #isVanillaAdapter()} == true and to produce standard vertex data. + * This means any BakedModel instance can be safely cast to this interface without an instanceof check. + */ +public interface FabricBakedModel { + /** + * When true, signals renderer this producer is implemented through {@link BakedModel#getQuads(BlockState, net.minecraft.util.math.Direction, Random)}. + * Also means the model does not rely on any non-vanilla features. + * Allows the renderer to optimize or route vanilla models through the unmodified vanilla pipeline if desired.

+ + * Fabric overrides to true for vanilla baked models. + * Enhanced models that use this API should return false, + * otherwise the API will not recognize the model.

+ */ + boolean isVanillaAdapter(); + + /** + * This method will be called during chunk rebuilds to generate both the static and + * dynamic portions of a block model when the model implements this interface and + * {@link #isVanillaAdapter()} returns false.

+ * + * During chunk rebuild, this method will always be called exactly one time per block + * position, irrespective of which or how many faces or block render layers are included + * in the model. Models must output all quads/meshes in a single pass.

+ * + * Also called to render block models outside of chunk rebuild or block entity rendering. + * Typically this happens when the block is being rendered as an entity, not as a block placed in the world. + * Currently this happens for falling blocks and blocks being pushed by a piston, but renderers + * should invoke this for all calls to {@link BlockModelRenderer#tesselate(ExtendedBlockView, BakedModel, BlockState, BlockPos, net.minecraft.client.render.BufferBuilder, boolean, Random, long)} + * that occur outside of chunk rebuilds to allow for features added by mods, unless + * {@link #isVanillaAdapter()} returns true.

+ * + * Outside of chunk rebuilds, this method will be called every frame. Model implementations should + * rely on pre-baked meshes as much as possible and keep transformation to a minimum. The provided + * block position may be the nearest block position and not actual. For this reason, neighbor + * state lookups are best avoided or will require special handling. Block entity lookups are + * likely to fail and/or give meaningless results.

+ * + * In all cases, renderer will handle face occlusion and filter quads on faces obscured by + * neighboring blocks (if appropriate). Models only need to consider "sides" to the + * extent the model is driven by connection with neighbor blocks or other world state.

+ * + * Note: with {@link BakedModel#getQuads(BlockState, net.minecraft.util.math.Direction, Random)}, the random + * parameter is normally initialized with the same seed prior to each face layer. + * Model authors should note this method is called only once per block, and call the provided + * Random supplier multiple times if re-seeding is necessary. For wrapped vanilla baked models, + * it will probably be easier to use {@link RenderContext#fallbackModelConsumer()} which handles + * re-seeding per face automatically.

+ * + * @param Access to world state. Using {@link TerrainBlockView#getCachedRenderData(BlockPos)} to + * retrieve block entity state unless thread safety can be guaranteed. + * @param safeBlockEntityAccessor Thread-safe access to block entity data + * @param state Block state for model being rendered. + * @param pos Position of block for model being rendered. + * @param randomSupplier Random object seeded per vanilla conventions. Call multiple times to re-seed. + * Will not be thread-safe. Do not cache or retain a reference. + * @param context Accepts model output. + */ + void emitBlockQuads(TerrainBlockView blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context); + + /** + * This method will be called during item rendering to generate both the static and + * dynamic portions of an item model when the model implements this interface and + * {@link #isVanillaAdapter()} returns false.

+ * + * Vanilla item rendering is normally very limited. It ignores lightmaps, vertex colors, + * and vertex normals. Renderers are expected to implement enhanced features for item + * models. If a feature is impractical due to performance or other concerns, then the + * renderer must at least give acceptable visual results without the need for special- + * case handling in model implementations.

+ * + * Calls to this method will generally happen on the main client thread but nothing + * prevents a mod or renderer from calling this method concurrently. Implementations + * should not mutate the ItemStack parameter, and best practice will be to make the + * method thread-safe.

+ * + * Implementing this method does NOT mitigate the need to implement a functional + * {@link BakedModel#getItemPropertyOverrides()} method, because this method will be called + * on the result of {@link #getItemPropertyOverrides()}. However, that + * method can simply return the base model because the output from this method will + * be used for rendering.

+ * + * Renderer implementations should also use this method to obtain the quads used + * for item enchantment glint rendering. This means models can put geometric variation + * logic here, instead of returning every possible shape from {@link #getItemPropertyOverrides()} + * as vanilla baked models. + */ + void emitItemQuads(ItemStack stack, Supplier randomSupplier, RenderContext context); +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/model/ForwardingBakedModel.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/model/ForwardingBakedModel.java new file mode 100644 index 0000000000..49402f7cc7 --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/model/ForwardingBakedModel.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.api.model; + +import java.util.List; +import java.util.Random; +import java.util.function.Supplier; + +import net.fabricmc.fabric.renderer.v1.api.render.RenderContext; +import net.fabricmc.fabric.renderer.v1.api.render.TerrainBlockView; +import net.fabricmc.fabric.renderer.v1.impl.DamageModel; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.BakedQuad; +import net.minecraft.client.render.model.json.ModelItemPropertyOverrideList; +import net.minecraft.client.render.model.json.ModelTransformation; +import net.minecraft.client.texture.Sprite; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; + +/** + * Base class for specialized model implementations that need to wrap other baked models. + * Avoids boilerplate code for pass-through methods. For example usage see {@link DamageModel}. + */ +public abstract class ForwardingBakedModel implements BakedModel, FabricBakedModel { + /** implementations must set this somehow */ + protected BakedModel wrapped; + + @Override + public void emitBlockQuads(TerrainBlockView blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { + ((FabricBakedModel)wrapped).emitBlockQuads(blockView, state, pos, randomSupplier, context); + } + + @Override + public boolean isVanillaAdapter() { + return ((FabricBakedModel)wrapped).isVanillaAdapter(); + } + + @Override + public void emitItemQuads(ItemStack stack, Supplier randomSupplier, RenderContext context) { + ((FabricBakedModel)wrapped).emitItemQuads(stack, randomSupplier, context); + } + + @Override + public List getQuads(BlockState blockState, Direction face, Random rand) { + return wrapped.getQuads(blockState, face, rand); + } + + @Override + public boolean useAmbientOcclusion() { + return wrapped.useAmbientOcclusion(); + } + + @Override + public boolean hasDepthInGui() { + return wrapped.hasDepthInGui(); + } + + @Override + public boolean isBuiltin() { + return wrapped.isBuiltin(); + } + + @Override + public Sprite getSprite() { + return wrapped.getSprite(); + } + + @Override + public ModelTransformation getTransformation() { + return wrapped.getTransformation(); + } + + @Override + public ModelItemPropertyOverrideList getItemPropertyOverrides() { + return wrapped.getItemPropertyOverrides(); + } +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/model/ModelHelper.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/model/ModelHelper.java new file mode 100644 index 0000000000..e096bb7d40 --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/model/ModelHelper.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.api.model; + +import java.util.Arrays; +import java.util.List; + +import com.google.common.collect.ImmutableList; + +import net.fabricmc.fabric.renderer.v1.api.mesh.Mesh; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.model.BakedQuad; +import net.minecraft.client.render.model.json.ModelTransformation; +import net.minecraft.client.render.model.json.Transformation; +import net.minecraft.client.util.math.Vector3f; +import net.minecraft.util.math.Direction; + +/** + * Collection of utilities for model implementations. + */ +public abstract class ModelHelper { + private ModelHelper() {} + + /** Result from {@link #toFaceIndex(Direction)} for null values. */ + public static final int NULL_FACE_ID = 6; + + /** + * Convenient way to encode faces that may be null. + * Null is returned as {@link #NULL_FACE_ID}. + * Use {@link #faceFromIndex(int)} to retrieve encoded face. + */ + public static int toFaceIndex(Direction face) { + return face == null ? NULL_FACE_ID : face.getId(); + } + + /** + * Use to decode a result from {@link #toFaceIndex(Direction)}. + * Return value will be null if encoded value was null. + * Can also be used for no-allocation iteration of {@link Direction#values()}, + * optionally including the null face. (Use < or <= {@link #NULL_FACE_ID} + * to exclude or include the null value, respectively.) + */ + public static Direction faceFromIndex(int faceIndex) { + return FACES[faceIndex]; + } + + /** see {@link #faceFromIndex(int)} */ + private static final Direction[] FACES = Arrays.copyOf(Direction.values(), 7); + + /** + * Converts a mesh into an array of lists of vanilla baked quads. + * Useful for creating vanilla baked models when required for compatibility. + * The array indexes correspond to {@link Direction#getId()} with the + * addition of {@link #NULL_FACE_ID}.

+ * + * Retrieves sprites from the block texture atlas via {@link SpriteFinder}. + */ + public static List[] toQuadLists(Mesh mesh) { + SpriteFinder finder = SpriteFinder.get(MinecraftClient.getInstance().getSpriteAtlas()); + + @SuppressWarnings("unchecked") + final ImmutableList.Builder[] builders = new ImmutableList.Builder[7]; + for(int i = 0; i < 7; i++) { + builders[i] = ImmutableList.builder(); + } + + mesh.forEach(q -> { + final int limit = q.material().spriteDepth(); + for(int l = 0; l < limit; l++) { + Direction face = q.cullFace(); + builders[face == null ? 6 : face.getId()].add(q.toBakedQuad(l, finder.find(q, l), false)); + } + }) ; + + @SuppressWarnings("unchecked") + List[] result = new List[7]; + for(int i = 0; i < 7; i++) { + result[i] = builders[i].build(); + } + return result; + } + + /** + * The vanilla model transformation logic is closely coupled with model deserialization. + * That does little good for modded model loaders and procedurally generated models. + * This convenient construction method applies the same scaling factors used for vanilla models. + * This means you can use values from a vanilla JSON file as inputs to this method. + */ + private static Transformation makeTransform( + float rotationX, float rotationY, float rotationZ, + float translationX, float translationY, float translationZ, + float scaleX, float scaleY, float scaleZ) { + Vector3f translation = new Vector3f(translationX, translationY, translationZ); + translation.scale(0.0625f); + translation.clamp(-5.0F, 5.0F); + return new Transformation( + new Vector3f(rotationX, rotationY, rotationZ), + translation, + new Vector3f(scaleX, scaleY, scaleZ)); + } + + public static final Transformation TRANSFORM_BLOCK_GUI = makeTransform(30, 225, 0, 0, 0, 0, 0.625f, 0.625f, 0.625f); + public static final Transformation TRANSFORM_BLOCK_GROUND = makeTransform(0, 0, 0, 0, 3, 0, 0.25f, 0.25f, 0.25f); + public static final Transformation TRANSFORM_BLOCK_FIXED = makeTransform(0, 0, 0, 0, 0, 0, 0.5f, 0.5f, 0.5f); + public static final Transformation TRANSFORM_BLOCK_3RD_PERSON_RIGHT = makeTransform(75, 45, 0, 0, 2.5f, 0, 0.375f, 0.375f, 0.375f); + public static final Transformation TRANSFORM_BLOCK_1ST_PERSON_RIGHT = makeTransform(0, 45, 0, 0, 0, 0, 0.4f, 0.4f, 0.4f); + public static final Transformation TRANSFORM_BLOCK_1ST_PERSON_LEFT = makeTransform(0, 225, 0, 0, 0, 0, 0.4f, 0.4f, 0.4f); + + /** + * Mimics the vanilla model transformation used for most vanilla blocks, + * and should be suitable for most custom block-like models. + */ + public static final ModelTransformation MODEL_TRANSFORM_BLOCK = new ModelTransformation( + TRANSFORM_BLOCK_3RD_PERSON_RIGHT, + TRANSFORM_BLOCK_3RD_PERSON_RIGHT, + TRANSFORM_BLOCK_1ST_PERSON_LEFT, + TRANSFORM_BLOCK_1ST_PERSON_RIGHT, + Transformation.NONE, + TRANSFORM_BLOCK_GUI, + TRANSFORM_BLOCK_GROUND, + TRANSFORM_BLOCK_FIXED); +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/model/SpriteFinder.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/model/SpriteFinder.java new file mode 100644 index 0000000000..9ba797ca5a --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/model/SpriteFinder.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.api.model; + +import net.fabricmc.fabric.renderer.v1.api.mesh.Mesh; +import net.fabricmc.fabric.renderer.v1.api.mesh.MutableQuadView; +import net.fabricmc.fabric.renderer.v1.api.mesh.QuadView; +import net.fabricmc.fabric.renderer.v1.impl.SpriteFinderImpl; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.texture.SpriteAtlasTexture; + +/** + * Indexes a texture atlas to allow fast lookup of Sprites from + * baked vertex coordinates. Main use is for {@link Mesh}-based models + * to generate vanilla quads on demand without tracking and retaining + * the sprites that were baked into the mesh. In other words, this class + * supplies the sprite parameter for {@link QuadView#toBakedQuad(int, Sprite, boolean)}. + */ +public interface SpriteFinder { + /** + * Retrieves or creates the finder for the given atlas. + * Instances should not be retained as fields or they must be + * refreshed whenever there is a resource reload or other event + * that causes atlas textures to be re-stitched. + */ + public static SpriteFinder get(SpriteAtlasTexture atlas) { + return SpriteFinderImpl.get(atlas); + } + + /** + * Finds the atlas sprite containing the vertex centroid of the quad. + * Vertex centroid is essentially the mean u,v coordinate - the intent being + * to find a point that is unambiguously inside the sprite (vs on an edge.)

+ * + * Should be reliable for any convex quad or triangle. May fail for non-convex quads. + * Note that all the above refers to u,v coordinates. Geometric vertex does not matter, + * except to the extent it was used to determine u,v. + */ + Sprite find(QuadView quad, int textureIndex); + + /** + * Alternative to {@link #find(QuadView, int)} when vertex centroid is already + * known or unsuitable. Expects normalized (0-1) coordinates on the atlas texture, + * which should already be the case for u,v values in vanilla baked quads and in + * {@link QuadView} after calling {@link MutableQuadView#spriteBake(int, Sprite, int)}.

+ * + * Coordinates must be in the sprite interior for reliable results. Generally will + * be easier to use {@link #find(QuadView, int)} unless you know the vertex + * centroid will somehow not be in the quad interior. This method will be slightly + * faster if you already have the centroid or another appropriate value. + */ + Sprite find(float u, float v); +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/render/RenderContext.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/render/RenderContext.java new file mode 100644 index 0000000000..c6b3109200 --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/render/RenderContext.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016, 2017, 2018 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.api.render; + +import java.util.function.Consumer; + +import net.fabricmc.fabric.renderer.v1.api.mesh.Mesh; +import net.fabricmc.fabric.renderer.v1.api.mesh.MeshBuilder; +import net.fabricmc.fabric.renderer.v1.api.mesh.MutableQuadView; +import net.fabricmc.fabric.renderer.v1.api.mesh.QuadEmitter; +import net.fabricmc.fabric.renderer.v1.api.model.FabricBakedModel; +import net.minecraft.client.render.model.BakedModel; + +/** + * This defines the instance made available to models for buffering vertex data at render time. + */ +public interface RenderContext { + /** + * Used by models to send vertex data previously baked via {@link MeshBuilder}. + * The fastest option and preferred whenever feasible. + */ + Consumer meshConsumer(); + + /** + * Fabric causes vanilla baked models to send themselves + * via this interface. Can also be used by compound models that contain a mix + * of vanilla baked models, packaged quads and/or dynamic elements. + */ + Consumer fallbackConsumer(); + + /** + * Returns a {@link QuadEmitter} instance that emits directly to the render buffer. + * It remains necessary to call {@link QuadEmitter#emit()} to output the quad.

+ * + * This method will always be less performant than passing pre-baked meshes + * via {@link #meshConsumer()}. It should be used sparingly for model components that + * demand it - text, icons, dynamic indicators, or other elements that vary too + * much for static baking to be feasible.

+ * + * Calling this method invalidates any {@link QuadEmitter} returned earlier. + * Will be threadlocal/re-used - do not retain references. + */ + QuadEmitter getEmitter(); + + /** + * Causes all models/quads/meshes sent to this consumer to be transformed by the provided + * {@link QuadTransform} that edits each quad before buffering. Quads in the mesh will + * be passed to the {@link QuadTransform} for modification before offsets, face culling or lighting are applied. + * Meant for animation and mesh customization.

+ * + * You MUST call {@link #popTransform()} after model is done outputting quads. + * + * More than one transformer can be added to the context. Transformers are applied in reverse order. + * (Last pushed is applied first.)

+ * + * Meshes are never mutated by the transformer - only buffered quads. This ensures thread-safe + * use of meshes/models across multiple chunk builders.

+ * + * Only the renderer should implement or extend this interface. + */ + void pushTransform(QuadTransform transform); + + /** + * Removes the transformation added by the last call to {@link #pushTransform(Consumer)}. + * MUST be called before exiting from {@link FabricBakedModel} .emit... methods. + */ + void popTransform(); + + @FunctionalInterface + public static interface QuadTransform { + /** + * Return false to filter out quads from rendering. When more than one transform + * is in effect, returning false means unapplied transforms will not receive the quad. + */ + boolean transform(MutableQuadView quad); + } +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/render/TerrainBlockView.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/render/TerrainBlockView.java new file mode 100644 index 0000000000..217639005a --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/api/render/TerrainBlockView.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016, 2017, 2018 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.api.render; + +import net.fabricmc.fabric.renderer.v1.api.model.DynamicModelBlockEntity; +import net.fabricmc.fabric.renderer.v1.api.model.FabricBakedModel; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ExtendedBlockView; + +/** + * BlockView-extending interface to be used by {@link FabricBakedModel} for dynamic model + * customization. It ensures thread safety and exploits data cached in render + * chunks for performance and data consistency.

+ * + * There are differences from BlockView consumers must understand:

+ * + * BlockEntity implementations that provide data for model customization should implement + * {@link DynamicModelBlockEntity} which will be queried on the main thread when a render + * chunk is enqueued for rebuild. The model should retrieve the results via {@link #getCachedRenderData()}. + * While {@link #getBlockEntity(net.minecraft.util.math.BlockPos)} is not disabled, it + * is not thread-safe for use on render threads. Models that violate this guidance are + * responsible for any necessary synchronization or collision detection.

+ * + * {@link #getBlockState(net.minecraft.util.math.BlockPos)} and {@link #getFluidState(net.minecraft.util.math.BlockPos)} + * will always reflect the state cached with the render chunk. Block and fluid states + * can thus be different from main-thread world state due to lag between block update + * application from network packets and render chunk rebuilds. Use of {@link #getCachedRenderData()} + * will ensure consistency of model state with the rest of the chunk being rendered.

+ * + * Models should avoid using {@link ExtendedBlockView#getBlockEntity(BlockPos)} + * to ensure thread safety because this view may be accessed outside the main client thread. + * Models that require Block Entity data should implement {@link DynamicModelBlockEntity} + * and then use {@link #getCachedRenderData(BlockPos)} to retrieve it. When called from the + * main thread, that method will simply retrieve the data directly.

+ * + * This interface is only guaranteed to be present in the client environment. + */ +public interface TerrainBlockView extends ExtendedBlockView { + /** + * For models associated with Block Entities that implement {@link DynamicModelBlockEntity} + * this will be the most recent value provided by that implementation for the given block position.

+ * + * Null in all other cases, or if the result from the implementation was null.

+ * + * @param pos Position of the block for the block model. + */ + default Object getCachedRenderData(BlockPos pos) { + BlockEntity be = this.getBlockEntity(pos); + return be == null ? null : ((DynamicModelBlockEntity)be).getDynamicModelData(); + } +} \ No newline at end of file diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/impl/DamageModel.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/impl/DamageModel.java new file mode 100644 index 0000000000..ee42e1466a --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/impl/DamageModel.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.impl; + +import java.util.Random; +import java.util.function.Supplier; + +import net.fabricmc.fabric.renderer.v1.api.RendererAccess; +import net.fabricmc.fabric.renderer.v1.api.material.RenderMaterial; +import net.fabricmc.fabric.renderer.v1.api.mesh.MutableQuadView; +import net.fabricmc.fabric.renderer.v1.api.model.FabricBakedModel; +import net.fabricmc.fabric.renderer.v1.api.model.ForwardingBakedModel; +import net.fabricmc.fabric.renderer.v1.api.render.RenderContext; +import net.fabricmc.fabric.renderer.v1.api.render.TerrainBlockView; +import net.fabricmc.fabric.renderer.v1.api.render.RenderContext.QuadTransform; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.texture.Sprite; +import net.minecraft.util.math.BlockPos; + +/** + * Specialized model wrapper that implements a general-purpose + * block-breaking render for enhanced models.

+ * + * Works by intercepting all model output and redirecting to dynamic + * quads that are baked with single-layer, UV-locked damage texture. + */ +public class DamageModel extends ForwardingBakedModel { + static final RenderMaterial DAMAGE_MATERIAL = RendererAccess.INSTANCE.hasRenderer() ? RendererAccess.INSTANCE.getRenderer().materialFinder().find() : null; + + private DamageTransform damageTransform = new DamageTransform(); + + public void prepare(BakedModel wrappedModel, Sprite sprite, BlockState blockState, BlockPos blockPos) { + this.damageTransform.damageSprite = sprite; + this.wrapped = wrappedModel; + } + + @Override + public void emitBlockQuads(TerrainBlockView blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { + context.pushTransform(damageTransform); + ((FabricBakedModel)wrapped).emitBlockQuads(blockView, state, pos, randomSupplier, context); + context.popTransform(); + } + + private static class DamageTransform implements QuadTransform { + private Sprite damageSprite; + + @Override + public boolean transform(MutableQuadView quad) { + quad.material(DAMAGE_MATERIAL); + quad.spriteBake(0, damageSprite, MutableQuadView.BAKE_LOCK_UV); + quad.colorIndex(-1); + return true; + } + } +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/impl/RendererAccessImpl.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/impl/RendererAccessImpl.java new file mode 100644 index 0000000000..d2d7b6c964 --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/impl/RendererAccessImpl.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.impl; + +import net.fabricmc.fabric.renderer.v1.api.Renderer; +import net.fabricmc.fabric.renderer.v1.api.RendererAccess; + +public final class RendererAccessImpl implements RendererAccess{ + public static final RendererAccessImpl INSTANCE = new RendererAccessImpl(); + + // private constructor + private RendererAccessImpl() { }; + + @Override + public final void registerRenderer(Renderer renderer) { + if(renderer == null) { + throw new NullPointerException("Attempt to register a NULL rendering plug-in."); + } else if(activeRenderer != null) { + throw new UnsupportedOperationException("A second rendering plug-in attempted to register. Multiple rendering plug-ins are not supported."); + } else { + activeRenderer = renderer; + hasActiveRenderer = true; + } + } + + private Renderer activeRenderer = null; + + /** avoids null test every call to {@link #hasRenderer()} */ + private boolean hasActiveRenderer = false; + + @Override + public final Renderer getRenderer() { + return activeRenderer; + } + + @Override + public final boolean hasRenderer() { + return hasActiveRenderer; + } +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/impl/SpriteFinderImpl.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/impl/SpriteFinderImpl.java new file mode 100644 index 0000000000..de88e8e75e --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/impl/SpriteFinderImpl.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.impl; + +import java.util.Map; +import java.util.function.Consumer; + +import net.fabricmc.fabric.renderer.v1.api.mesh.QuadView; +import net.fabricmc.fabric.renderer.v1.api.model.SpriteFinder; +import net.minecraft.client.texture.MissingSprite; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.texture.SpriteAtlasTexture; +import net.minecraft.util.Identifier; + +/** + * Indexes an atlas sprite to allow fast lookup of Sprites from + * baked vertex coordinates. Implementation is a straightforward + * quad tree. Other options that were considered were linear search + * (slow) and direct indexing of fixed-size cells. Direct indexing + * would be fastest but would be memory-intensive for large atlases + * and unsuitable for any atlas that isn't consistently aligned to + * a fixed cell size. + */ +public class SpriteFinderImpl implements SpriteFinder { + private final Node root; + + public SpriteFinderImpl(Map sprites) { + root = new Node(0.5f, 0.5f, 0.25f); + sprites.values().forEach(root::add); + } + + @Override + public Sprite find(QuadView quad, int textureIndex) { + float u = 0; + float v = 0; + for(int i = 0; i < 4; i++) { + u += quad.spriteU(i, textureIndex); + v += quad.spriteV(i, textureIndex); + } + return find(u * 0.25f, v * 0.25f); + } + + @Override + public Sprite find(float u, float v) { + return root.find(u, v); + } + + private static class Node { + final float midU; + final float midV; + final float cellRadius; + Object lowLow = null; + Object lowHigh = null; + Object highLow = null; + Object highHigh = null; + + Node(float midU, float midV, float radius) { + this.midU = midU; + this.midV = midV; + this.cellRadius = radius; + } + + static final float EPS = 0.00001f; + + void add(Sprite sprite) { + final boolean lowU = sprite.getMinU() < midU - EPS; + final boolean highU = sprite.getMaxU() > midU + EPS; + final boolean lowV = sprite.getMinV() < midV - EPS; + final boolean highV = sprite.getMaxV() > midV + EPS; + if(lowU && lowV) { + addInner(sprite, lowLow, -1, -1, q -> lowLow = q); + } + if(lowU && highV) { + addInner(sprite, lowHigh, -1, 1, q -> lowHigh = q); + } + if(highU && lowV) { + addInner(sprite, highLow, 1, -1, q -> highLow = q); + } + if(highU && highV) { + addInner(sprite, highHigh, 1, 1, q -> highHigh = q); + } + } + + private void addInner(Sprite sprite, Object quadrant, int uStep, int vStep, Consumer setter) { + if(quadrant == null) { + setter.accept(sprite); + } else if (quadrant instanceof Node) { + ((Node)quadrant).add(sprite); + } else { + Node n = new Node(midU + cellRadius * uStep, midV + cellRadius * vStep, cellRadius * 0.5f); + if(quadrant instanceof Sprite) { + n.add((Sprite)quadrant); + } + n.add(sprite); + setter.accept(n); + } + } + + private Sprite find(float u, float v) { + if(u < midU) { + return v < midV ? findInner(lowLow, u, v) : findInner(lowHigh, u, v); + } else { + return v < midV ? findInner(highLow, u, v) : findInner(highHigh, u, v); + } + } + + private Sprite findInner(Object quadrant, float u, float v) { + if(quadrant instanceof Sprite) { + return (Sprite)quadrant; + } else if (quadrant instanceof Node) { + return ((Node)quadrant).find(u, v); + } else { + return MissingSprite.getMissingSprite(); + } + } + } + + public static SpriteFinderImpl get(SpriteAtlasTexture atlas) { + return ((SpriteFinderAccess)atlas).fabric_spriteFinder(); + } + + public static interface SpriteFinderAccess { + SpriteFinderImpl fabric_spriteFinder(); + } +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinBakedModel.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinBakedModel.java new file mode 100644 index 0000000000..e55564d001 --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinBakedModel.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.mixin; + +import java.util.Random; +import java.util.function.Supplier; + +import org.spongepowered.asm.mixin.Mixin; + +import net.fabricmc.fabric.renderer.v1.api.model.FabricBakedModel; +import net.fabricmc.fabric.renderer.v1.api.render.RenderContext; +import net.fabricmc.fabric.renderer.v1.api.render.TerrainBlockView; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; + +/** + * Avoids instanceof checks and enables consistent code path for all baked models. + */ +@Mixin(BakedModel.class) +public interface MixinBakedModel extends FabricBakedModel { + @Override + public default boolean isVanillaAdapter() { + return true; + } + + @Override + public default void emitBlockQuads(TerrainBlockView blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { + context.fallbackConsumer().accept((BakedModel)this); + } + + @Override + default void emitItemQuads(ItemStack stack, Supplier randomSupplier, RenderContext context) { + context.fallbackConsumer().accept((BakedModel)this); + } +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinBlockEntity.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinBlockEntity.java new file mode 100644 index 0000000000..4c6804fbcc --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinBlockEntity.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.mixin; + +import net.fabricmc.fabric.renderer.v1.api.model.DynamicModelBlockEntity; +import net.minecraft.block.entity.BlockEntity; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(BlockEntity.class) +public class MixinBlockEntity implements DynamicModelBlockEntity { + @Override + public Object getDynamicModelData() { + return null; + } +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinBlockRenderManager.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinBlockRenderManager.java new file mode 100644 index 0000000000..cb7872449c --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinBlockRenderManager.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.mixin; + +import java.util.Random; + +import org.apache.commons.lang3.tuple.MutablePair; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.fabricmc.fabric.renderer.v1.api.model.FabricBakedModel; +import net.fabricmc.fabric.renderer.v1.impl.DamageModel; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.Tessellator; +import net.minecraft.client.render.block.BlockModelRenderer; +import net.minecraft.client.render.block.BlockRenderManager; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.texture.Sprite; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ExtendedBlockView; + +/** + * Implements hook for block-breaking render. + */ +@Mixin(BlockRenderManager.class) +public abstract class MixinBlockRenderManager { + @Shadow private BlockModelRenderer renderer; + @Shadow private Random random; + + private static final ThreadLocal> DAMAGE_STATE = ThreadLocal.withInitial(() -> MutablePair.of(new DamageModel(), null)); + + /** + * Intercept the model assignment from getModel() - simpler than capturing entire LVT. + */ + @ModifyVariable(method = "tesselateDamage", at = @At(value = "STORE", ordinal = 0), allow = 1, require = 1) + private BakedModel hookTesselateDamageModel(BakedModel modelIn) { + DAMAGE_STATE.get().right = modelIn; + return modelIn; + } + + /** + * If the model we just captured is a fabric model, render it using a specialized + * damage render context and cancel rest of the logic. Avoids creating a bunch of + * vanilla quads for complex meshes and honors dynamic model geometry. + */ + @Inject(method = "tesselateDamage", cancellable = true, + at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/client/render/block/BlockModels;getModel(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/model/BakedModel;")) + private void hookTesselateDamage(BlockState blockState, BlockPos blockPos, Sprite sprite, ExtendedBlockView blockView, CallbackInfo ci) { + MutablePair damageState = DAMAGE_STATE.get(); + if(damageState.right != null && !((FabricBakedModel)damageState.right).isVanillaAdapter()) { + damageState.left.prepare(damageState.right, sprite, blockState, blockPos); + this.renderer.tesselate(blockView, damageState.left, blockState, blockPos, Tessellator.getInstance().getBufferBuilder(), true, this.random, blockState.getRenderingSeed(blockPos)); + ci.cancel(); + } + } +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinChunkRendererRegion.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinChunkRendererRegion.java new file mode 100644 index 0000000000..3ac2a116e0 --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinChunkRendererRegion.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.mixin; + +import java.util.HashMap; +import java.util.Map; + +import net.minecraft.client.render.chunk.ChunkRendererRegion; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.fabricmc.fabric.renderer.v1.api.model.DynamicModelBlockEntity; +import net.fabricmc.fabric.renderer.v1.api.render.TerrainBlockView; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.chunk.WorldChunk; + +@Mixin(ChunkRendererRegion.class) +public abstract class MixinChunkRendererRegion implements TerrainBlockView { + private HashMap fabric_renderDataObjects; + + @Inject(at = @At("RETURN"), method = "") + public void init(World world, int cxOff, int czOff, WorldChunk[][] chunks, BlockPos posFrom, BlockPos posTo, CallbackInfo info) { + HashMap map = new HashMap<>(); + + for (WorldChunk[] chunkA : chunks) { + for (WorldChunk chunkB : chunkA) { + for (Map.Entry entry: chunkB.getBlockEntities().entrySet()) { + BlockPos entPos = entry.getKey(); + if (entPos.getX() >= posFrom.getX() && entPos.getX() <= posTo.getX() + && entPos.getY() >= posFrom.getY() && entPos.getY() <= posTo.getY() + && entPos.getZ() >= posFrom.getZ() && entPos.getZ() <= posTo.getZ()) { + + Object o = ((DynamicModelBlockEntity) entry.getValue()).getDynamicModelData(); + if (o != null) { + map.put(entPos, o); + } + } + } + } + } + + this.fabric_renderDataObjects = map; + } + + @Override + public Object getCachedRenderData(BlockPos pos) { + return fabric_renderDataObjects.get(pos); + } +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinSpriteAtlasTexture.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinSpriteAtlasTexture.java new file mode 100644 index 0000000000..8c3f8c6cac --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinSpriteAtlasTexture.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.mixin; + +import java.util.Map; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.fabricmc.fabric.renderer.v1.impl.SpriteFinderImpl; +import net.fabricmc.fabric.renderer.v1.impl.SpriteFinderImpl.SpriteFinderAccess; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.texture.SpriteAtlasTexture; +import net.minecraft.util.Identifier; + +@Mixin(SpriteAtlasTexture.class) +public class MixinSpriteAtlasTexture implements SpriteFinderAccess { + @Shadow private Map sprites; + + private SpriteFinderImpl fabric_spriteFinder = null; + + @Inject(at = @At("RETURN"), method = "upload") + private void uploadHook(SpriteAtlasTexture.Data input, CallbackInfo info) { + fabric_spriteFinder = null; + } + + @Override + public SpriteFinderImpl fabric_spriteFinder() { + SpriteFinderImpl result = fabric_spriteFinder; + if(result == null) { + result = new SpriteFinderImpl(sprites); + fabric_spriteFinder = result; + } + return result; + } +} diff --git a/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinWorld.java b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinWorld.java new file mode 100644 index 0000000000..d16d9ac499 --- /dev/null +++ b/fabric-renderer-v0/src/main/java/net/fabricmc/fabric/renderer/v1/mixin/MixinWorld.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.renderer.v1.mixin; + +import org.spongepowered.asm.mixin.Mixin; + +import net.fabricmc.fabric.renderer.v1.api.render.TerrainBlockView; +import net.minecraft.world.World; + +/** Make {@link World} implement {@link TerrainBlockView}. */ +@Mixin(World.class) +public abstract class MixinWorld implements TerrainBlockView { + +} diff --git a/fabric-renderer-v0/src/main/resources/fabric-renderer-v0.mixins.json b/fabric-renderer-v0/src/main/resources/fabric-renderer-v0.mixins.json new file mode 100644 index 0000000000..e738ca67b1 --- /dev/null +++ b/fabric-renderer-v0/src/main/resources/fabric-renderer-v0.mixins.json @@ -0,0 +1,16 @@ +{ + "required": true, + "package": "net.fabricmc.fabric.renderer.v1.mixin", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "MixinBakedModel", + "MixinBlockEntity", + "MixinBlockRenderManager", + "MixinSpriteAtlasTexture", + "MixinChunkRendererRegion", + "MixinWorld" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/fabric-renderer-v0/src/main/resources/fabric.mod.json b/fabric-renderer-v0/src/main/resources/fabric.mod.json new file mode 100644 index 0000000000..110b6d19bf --- /dev/null +++ b/fabric-renderer-v0/src/main/resources/fabric.mod.json @@ -0,0 +1,9 @@ +{ + "schemaVersion": 1, + "id": "fabric-rendering-v0", + "version": "${version}", + "license": "Apache-2.0", + "mixins": [ + "fabric-renderer-v0.mixins.json" + ] +} diff --git a/settings.gradle b/settings.gradle index 79a5c21ab8..d70223c54f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -28,6 +28,7 @@ include 'fabric-networking-v0' include 'fabric-networking-blockentity-v0' include 'fabric-object-builders-v0' include 'fabric-registry-sync-v0' +include 'fabric-renderer-v0' include 'fabric-rendering-v0' include 'fabric-resource-loader-v0' include 'fabric-tag-extensions-v0' diff --git a/src/main/resources/assets/fabric/icon.png b/src/main/resources/assets/fabric/icon.png deleted file mode 100644 index 7c97cba4be..0000000000 Binary files a/src/main/resources/assets/fabric/icon.png and /dev/null differ diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json deleted file mode 100644 index 5ff12ce6bf..0000000000 --- a/src/main/resources/fabric.mod.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "schemaVersion": 1, - "id": "fabric", - "name": "Fabric API", - "version": "${version}", - "description": "Core API module providing key hooks and intercompatibility features.", - "license": "Apache-2.0", - "icon": "assets/fabric/icon.png", - "contact": { - "homepage": "https://fabricmc.net", - "irc": "irc://irc.esper.net:6667/fabric", - "issues": "https://github.com/FabricMC/fabric/issues", - "sources": "https://github.com/FabricMC/fabric" - } -} \ No newline at end of file