Jump to content

Recommended Posts

Posted (edited)

Hey guys!

I created a block (slab, to be axact), that takes on the texture of the block it is clicked on. For that I used Baked Models. Now nearly everything works fine, but the slab texture (when covered by a block) is way too dark (see attached pictures), as if the light level was zero. As you can see in the first picture, it works well for the json-block-model, when the slab does not contain any block (oak planks texture). So I guess the problem is somewhere in the baked model, but I can't figure it out.

Here's the code for my BakedModel-Class extending IDynamicBakedModel:

getQuads (Overrides interface method)

public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull Random rand, @Nonnull IModelData extraData) {
        //get block saved in slab's tile entity
        BlockState mimic = extraData.getData(FrameBlockTile.MIMIC);
        //check if not empty
        if (mimic != null && !(mimic.getBlock() instanceof FrameBlock)) {
            //location of BlockModel
            ModelResourceLocation location = BlockModelShapes.getModelLocation(mimic);
            if (location != null) {
                IBakedModel model = Minecraft.getInstance().getModelManager().getModel(location);
                model.getBakedModel().getQuads(mimic, side, rand, extraData);
                if (model != null) {
                    //new quad-model with data from slab
                    return getMimicQuads(mimic, side, rand, extraData);
                }
            }
        }
        return Collections.emptyList();

the called method "getMimicQuads":

public List<BakedQuad> getMimicQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull Random rand, @Nonnull IModelData extraData) {
        if (side != null) {
            return Collections.emptyList();
        }
        BlockState mimic = extraData.getData(FrameBlockTile.MIMIC);
        if (mimic!=null) {
          	//get texture from block saved in slab
            TextureAtlasSprite texture = Minecraft.getInstance().getAtlasSpriteGetter(AtlasTexture.LOCATION_BLOCKS_TEXTURE).apply(new ResourceLocation(mimic.getBlock().getRegistryName().getNamespace(), "block/"+mimic.getBlock().getRegistryName().getPath()));
            List<BakedQuad> quads = new ArrayList<>();
          	//create 6 faces of slab
            //down
            quads.add(createQuad(v(1, 0, 0), v(1, 0, 1), v(0, 0, 1), v(0, 0, 0), texture));
            //up
            quads.add(createQuad(v(0, 0.5, 0), v(0, 0.5, 1), v(1, 0.5, 1), v(1, 0.5, 0), texture));
            //sides
            quads.add(createHalfQuad(v(0, 0, 0), v(0, 0, 1), v(0, 0.5, 1), v(0, 0.5, 0), texture));
            quads.add(createHalfQuad(v(0, 0, 0), v(0, 0.5, 0), v(1, 0.5, 0), v(1, 0, 0), texture));
            quads.add(createHalfQuad(v(0, 0, 1), v(1, 0, 1), v(1, 0.5, 1), v(0, 0.5, 1), texture));
            quads.add(createHalfQuad(v(1, 0.5, 0), v(1, 0.5, 1), v(1, 0, 1), v(1, 0, 0), texture));

            return quads;
        }
        return Collections.emptyList();
    }

the called createQuad and createHalfQuad methods:

	//for squared part of slab (top and bottom side)
	private BakedQuad createQuad(Vec3d v1, Vec3d v2, Vec3d v3, Vec3d v4, TextureAtlasSprite sprite) {
        Vec3d normal = v3.subtract(v2).crossProduct(v1.subtract(v2)).normalize();

        BakedQuadBuilder builder = new BakedQuadBuilder(sprite);
        builder.setQuadOrientation(Direction.getFacingFromVector(normal.x, normal.y, normal.z));
        putVertex(builder, normal, v1.x, v1.y, v1.z, 0, 0, sprite, 1.0f, 1.0f, 1.0f);
        putVertex(builder, normal, v2.x, v2.y, v2.z, 0, 16, sprite, 1.0f, 1.0f, 1.0f);
        putVertex(builder, normal, v3.x, v3.y, v3.z, 16, 16, sprite, 1.0f, 1.0f, 1.0f);
        putVertex(builder, normal, v4.x, v4.y, v4.z, 16, 0, sprite, 1.0f, 1.0f, 1.0f);
        return builder.build();
    }
	//for rectangles (sides of slab)
	private BakedQuad createHalfQuad(Vec3d v1, Vec3d v2, Vec3d v3, Vec3d v4, TextureAtlasSprite sprite) {
        Vec3d normal = v3.subtract(v2).crossProduct(v1.subtract(v2)).normalize();

        BakedQuadBuilder builder = new BakedQuadBuilder(sprite);
        builder.setQuadOrientation(Direction.getFacingFromVector(normal.x, normal.y, normal.z));
        putVertex(builder, normal, v1.x, v1.y, v1.z, 0, 0, sprite, 1.0f, 1.0f, 1.0f);
        putVertex(builder, normal, v2.x, v2.y, v2.z, 0, 8, sprite, 1.0f, 1.0f, 1.0f);
        putVertex(builder, normal, v3.x, v3.y, v3.z, 16, 8, sprite, 1.0f, 1.0f, 1.0f);
        putVertex(builder, normal, v4.x, v4.y, v4.z, 16, 0, sprite, 1.0f, 1.0f, 1.0f);
        return builder.build();
    }

and the putVertex-method for creating the vertices:

private void putVertex(BakedQuadBuilder builder, Vec3d normal,
                           double x, double y, double z, float u, float v, TextureAtlasSprite sprite, float r, float g, float b) {

        ImmutableList<VertexFormatElement> elements = builder.getVertexFormat().getElements().asList();
        for (int j = 0 ; j < elements.size() ; j++) {
            VertexFormatElement e = elements.get(j);
            switch (e.getUsage()) {
                case POSITION:
                    builder.put(j, (float) x, (float) y, (float) z, 1.0f);
                    break;
                case COLOR:
                    builder.put(j, r, g, b, 1.0f);
                    break;
                case UV:
                    switch (e.getIndex()) {
                        case 0:
                            float iu = sprite.getInterpolatedU(u);
                            float iv = sprite.getInterpolatedV(v);
                            builder.put(j, iu, iv);
                            break;
                        case 2:
                            builder.put(j, 0f, 1f);
                            break;
                        default:
                            builder.put(j);
                            break;
                    }
                    break;
                case NORMAL:
                    builder.put(j, (float) normal.x, (float) normal.y, (float) normal.z);
                    break;
                default:
                    builder.put(j);
                    break;
            }
        }
    }

 

Ambient Occlusion is set to true (I read, that this might be the reason, but when I toggled it multiple times nothing really changed)

Gui3d and BuildInRenderer both return false

 

As I said, the texture is too dark, like if the light level was zero and I want to fix that. I really don't know what to do anymore. Any help is appreciated. Thanks in advance

(I hope you understand, what I was trying to say, english is not my native language)

Kind Regards, Manu

2020-06-10_13.30.52.png

2020-06-10_13.30.55.png

Edited by PianoManu
Problem is solved. Thanks to you, TGG!
Posted

Howdy

I don't see you put any lightmap information into your vertex, but I presume you are using the Block vertex format?  In my previous digging through the rendering code, I sometime found that to be important.

 

DefaultVertexFormats.BLOCK

    // IVertexBuilder::addQuad and FaceBakery; see also DefaultVertexFormats.BLOCK.
    // Summary:
    //    faceData[i + 0] = Float.floatToRawIntBits(positionIn.getX());
    //    faceData[i + 1] = Float.floatToRawIntBits(positionIn.getY());
    //    faceData[i + 2] = Float.floatToRawIntBits(positionIn.getZ());
    //    faceData[i + 3] = shadeColor;
    //    faceData[i + 4] = Float.floatToRawIntBits(textureU));
    //    faceData[i + 5] = Float.floatToRawIntBits(textureV));
    //    faceData[i + 6] = baked lighting (blocklight + skylight)
    //    faceData[i + 7] = normal;

 

You could consider using the face bakery to generate your quads, like this

  /**
   * Returns a quad for the given digit
   * @param digit the digit (0 -> 9)
   * @param isBlank if true: this digit should be blank (is a leading zero)
   * @param minX: the minimum [x,y,z] of the digit quad (from the viewer's point of view).  units = model space i.e. 0->16 is 1 metre block
   * @param maxX: the maximum [x,y,z] of the digit quad (from the viewer's point of view).  units = model space i.e. 0->16 is 1 metre block
   * @return
   */
  private BakedQuad getQuadForDigit(int digit, boolean isBlank, Direction whichFace,
                                    double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
    // generate a BakedQuad for the given digit

    // we can do this manually by providing a list of vertex data, or we can use the FaceBakery::bakeQuads method
    // FaceBakery::bakeQuad is much simpler and suitable for pretty much any block-style rendering, so I've used that here
    // If you want to manually provide vertex data yourself, the format is an array of ints; look in
    // IVertexBuilder::addQuad and FaceBakery; see also DefaultVertexFormats.BLOCK.
    // Summary:
    //    faceData[i + 0] = Float.floatToRawIntBits(positionIn.getX());
    //    faceData[i + 1] = Float.floatToRawIntBits(positionIn.getY());
    //    faceData[i + 2] = Float.floatToRawIntBits(positionIn.getZ());
    //    faceData[i + 3] = shadeColor;
    //    faceData[i + 4] = Float.floatToRawIntBits(textureU));
    //    faceData[i + 5] = Float.floatToRawIntBits(textureV));
    //    faceData[i + 6] = baked lighting (blocklight + skylight)
    //    faceData[i + 7] = normal;
    // When constructing a face manually in this way, the order of vertices is very important!
    // 1) must be added anti-clockwise (from the point of view of the person looking at the face).  Otherwise the face
    //    will point in the wrong direction and it may be invisible (backs of faces are usually culled for block rendering)
    // 2) ambient occlusion (a block lighting effect) assumes that the vertices are added in the order:
    //     top left, then bottom left, then bottom right, then top right - for the east, west, north, south faces.
    //     for the top face: NW, SW, SE, NE.  for the bottom face: SW, NW, NE, SE
    //    If your face has ambient occlusion enabled, and the order is wrong, then the shading will be messed up

    // FaceBakery:
    //  Vanilla uses it to convert from the elements in a block model, i.e.
    //    "elements": [
    //    { "from": [ 7, 0, 7 ],
    //      "to": [ 9, 10, 9 ],
    //      "shade": false,
    //      "faces": {
    //        "down": { "uv": [ 7, 13, 9, 15 ], "texture": "#torch" },
    //        "up":   { "uv": [ 7,  6, 9,  8 ], "texture": "#torch" }
    //      }
    //    },
    //    see https://minecraft.gamepedia.com/Model#Block_models
    //  In order to use the FaceBakery::bakeQuad method, we need to provide:
    //   1) A suitable cuboid 'from' and 'to', in model coordinate (eg the full 1 metre cube is from [0,0,0] to [16, 16, 16])
    //   2) the corresponding [u,v] texture coordinates for the face: [minU,minV] first then [maxU,maxV], again in texels 0->16
    //   3) the face we want to make the quad for (eg up, down, east, west, etc).

    Vector3f from = new Vector3f((float)minX, (float)minY, (float)minZ);
    Vector3f to = new Vector3f((float)maxX, (float)maxY, (float)maxZ);

    // texture UV order is important! i.e. [minU,minV] first then [maxU,maxV]
    float [] uvArray = getDigitUVs(digit, isBlank);
    final int ROTATION_NONE = 0;
    BlockFaceUV blockFaceUV = new BlockFaceUV(uvArray, ROTATION_NONE);

    final Direction NO_FACE_CULLING = null;
    final int TINT_INDEX_NONE = -1;  // used for tintable blocks such as grass, which make a call to BlockColors to change their rendering colour.  -1 for not tintable.
    final String DUMMY_TEXTURE_NAME = "";  // texture name is only needed for loading from json files; not needed here
    BlockPartFace blockPartFace = new BlockPartFace(NO_FACE_CULLING, TINT_INDEX_NONE, DUMMY_TEXTURE_NAME,  blockFaceUV);

    // we have previously registered digitsTexture in StartupClientOnly::onTextureStitchEvent
    AtlasTexture blocksStitchedTextures = ModelLoader.instance().getSpriteMap().getAtlasTexture(AtlasTexture.LOCATION_BLOCKS_TEXTURE);
    TextureAtlasSprite digitsTextures = blocksStitchedTextures.getSprite(digitsTextureRL);

    final IModelTransform NO_TRANSFORMATION = IDENTITY;
    final BlockPartRotation DEFAULT_ROTATION = null;   // rotate based on the face direction
    final boolean APPLY_SHADING = true;
    final ResourceLocation DUMMY_RL = new ResourceLocation("dummy_name");  // used for error message only
    BakedQuad bakedQuad = faceBakery.bakeQuad(from, to, blockPartFace, digitsTextures, whichFace, NO_TRANSFORMATION, DEFAULT_ROTATION,
                             APPLY_SHADING, DUMMY_RL);
    return bakedQuad;
  }

 

If that doesn't help, I'd suggest you add a breakpoint to ForgeBlockModelRenderer::renderModelSmooth (or much easier - ::renderModelFlat if you turn off ambient occlusion) and watch how your block's quads are rendered to the buffer.

 

I've also had this problem arise previously with blocks which used the skylight+blocklight value for rendering but which had a calculated skylight+blocklight of 0 due to the block's settings (I forget which, sorry).   The breakpoint I suggested above should show that, if it's the cause.

 

-TGG

 

 

 

 

  • Like 1
Posted

Hey, thanks for your reply!

I've read your text several times and I'm beginning to understand, but I've never worked with the FaceBakery before, so I don't have any experiences with that. Do I have to rewrite the whole baked model class to extend the FaceBakery instead of implementing IDynamicBakedModel? I've looked through the FaceBakery class and the IVertexBuilder, but I have no clue where to begin with... Maybe you could give me a hint?

Or do I have to rewrite everything at all?

 

Also, where is this code part from? I couldn't find anything on Google, when I searched for "getQuadForDigit". I ask because maybe I could get some more information from the context of the code

 

Thank you for your help!

Posted
21 hours ago, TheGreyGhost said:

I don't see you put any lightmap information into your vertex

Sorry for bothering you again, but I've searched and tested a bit further and honestly I don't know how to put it into the vertex as I don't have any lightmap information (as far as I know) and both the BakedQuadBuilder and the VertexFormatElement do not take/contain any lighting information. How do you mean that? Like, what do I have to add?

Thanks for helping me!

Posted

Howdy

The code is from this example project

https://github.com/TheGreyGhost/MinecraftByExample

(mbe04 AltimeterBakedModel)

 

The definition for the vertex format of BLOCK is 

public static final VertexFormat BLOCK = new VertexFormat(ImmutableList.<VertexFormatElement>builder()
  .add(POSITION_3F)  //[x,y,z]
  .add(COLOR_4UB) // colour info
  .add(TEX_2F) // texture
  .add(TEX_2SB) // lighting (blocklight + skylight)
  .add(NORMAL_3B) // normal 
  .add(PADDING_1B).build());

but you're right, the lighting is often not used / is added during rendering.

 

If you put your code into a github I'll have a look in the next day or so to figure it out.  I think it's almost certainly a block setting or voxelshape problem that's causing the lightmap to be wrong, it won't take me long to confirm it.

 

Cheers

  TGG

 

 

 

  • Thanks 1
Posted (edited)
2 hours ago, TheGreyGhost said:

The code is from this example project

https://github.com/TheGreyGhost/MinecraftByExample

(mbe04 AltimeterBakedModel)

Thank you very much!

 

This is my git repository: https://github.com/PianoManu/BlockCarpentry

The registration of the block is located in the setup-package in the Registration class.

The model loaders are registered in the setup-package in the ClientSetup class - there are three model loaders, the "frameloader" loads the full block and works fine. The problematic one is the "frame_slab_bottom_loader".

The Baked Model for the slab is located in the bakedmodels package as "SlabFrameBottomBakedModel" class

 

Thank you very much for your help! I will be really happy if you find my mistake ?

Regards, Manu

 

Edit: for some weird reason, the "main" directory is outside of the "src" directory in the git repository and you can't open the src directory... But everything is contained in the main directory so I guess it's fine...

Edited by PianoManu
Posted

Howdy

 

The problem is here; you mixed up short (2 bytes long) and float(4 bytes long) so your UV was overwriting dud values into the baked skylight+blocklight.

 

    private void putVertex(BakedQuadBuilder builder, Vec3d normal,
                           double x, double y, double z, float u, float v, TextureAtlasSprite sprite, float r, float g, float b) {

        //ImmutableList<VertexFormatElement> elements = builder.getVertexFormat().getElements().asList();
        ImmutableList<VertexFormatElement> elements = SLAB_BLOCK.getElements().asList();
        for (int j = 0 ; j < elements.size() ; j++) {
            VertexFormatElement e = elements.get(j);
            switch (e.getUsage()) {
                case POSITION:
                    builder.put(j, (float) x, (float) y, (float) z, 1.0f);
                    break;
                case COLOR:
                    builder.put(j, r, g, b, 1.0f);
                    break;
                case UV:
                    switch (e.getIndex()) {
                        case 0:
                            float iu = sprite.getInterpolatedU(u);
                            float iv = sprite.getInterpolatedV(v);
                            builder.put(j, iu, iv);
                            break;
                        case 2:
                            builder.put(j, (short)0, (short)0);  // error is here
                            break;
                        default:
                            builder.put(j);
                            break;
                    }
                    break;
                case NORMAL:
                    builder.put(j, (float) normal.x, (float) normal.y, (float) normal.z);
                    break;
                default:
                    builder.put(j);
                    break;
            }
        }
        builder.setApplyDiffuseLighting(true);
    }

 

Due to non-defensive coding assumptions in IForgeVertexBuilder, it was taking your incorrect slBaked value (2047 instead of 0), overwriting the skylight (15), leaving a packed texture coordinate with zeros in the least significant bits, i.e. effectively 0 skylight = dark.

    default int applyBakedLighting(int lightmapCoord, ByteBuffer data) {
        int bl = LightTexture.getLightBlock(lightmapCoord);
        int sl = LightTexture.getLightSky(lightmapCoord);
        int offset = LightUtil.getLightOffset(0) * 4; // int offset for vertex 0 * 4 bytes per int
        int blBaked = Short.toUnsignedInt(data.getShort(offset)) >> 4;
        int slBaked = Short.toUnsignedInt(data.getShort(offset + 2)) >> 4;
        bl = Math.max(bl, blBaked);
        sl = Math.max(sl, slBaked);
        return LightTexture.packLight(bl, sl);
    }

 

It works fine now...

 

-TGG

 

2020-06-13_13_43_31.png.22d1a6e1f7845bd661978507894c5193.png

  • Thanks 1

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.