Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Texturing a cube in libgdx 3D

Tags:

java

3d

libgdx

I wrote the following class to create cube models in libgdx.

package io.github.l0lk.zombieyeti.block;

import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.VertexAttributes;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g3d.Material;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute;
import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder;
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;

public enum BlockType {

    GRASS("dirt", 0, 0, 3, 0, 2, 0),
    STONE("stone", 1, 0, 1, 0, 1, 0),
    PLANK("plank", 4, 0, 4, 0, 4, 0),
    GLASS("glass", 1, 3, 1, 3, 1, 3);

    public static int TEXTURE_WIDTH = 16;

    String name;
    int textureTopX, textureTopY;
    int textureSideX, textureSideY;
    int textureBottomX, textureBottomY;

    TextureRegion textureRegionTop = null;
    TextureRegion textureRegionSide = null;
    TextureRegion textureRegionBottom = null;

    Model model;

    BlockType(String name, int textureTopX, int textureTopY, int textureSideX, int textureSideY, int textureBottomX, int textureBottomY) {
        this.name = name;
        this.textureTopX = textureTopX;
        this.textureTopY = textureTopY;
        this.textureSideX = textureSideX;
        this.textureSideY = textureSideY;
        this.textureBottomX = textureBottomX;
        this.textureBottomY = textureBottomY;
    }

    public static void createTexturesAndModels(Texture texture) {
        ModelBuilder modelBuilder = new ModelBuilder();

        for(BlockType type : values()) {
            type.textureRegionTop = new TextureRegion(texture, type.textureTopX * TEXTURE_WIDTH, type.textureTopY * TEXTURE_WIDTH, TEXTURE_WIDTH, TEXTURE_WIDTH);
            type.textureRegionSide = new TextureRegion(texture, type.textureSideX * TEXTURE_WIDTH, type.textureSideY * TEXTURE_WIDTH, TEXTURE_WIDTH, TEXTURE_WIDTH);
            type.textureRegionBottom = new TextureRegion(texture, type.textureBottomX * TEXTURE_WIDTH, type.textureBottomY * TEXTURE_WIDTH, TEXTURE_WIDTH, TEXTURE_WIDTH);

            int attr = VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal | VertexAttributes.Usage.TextureCoordinates;
            modelBuilder.begin();
            MeshPartBuilder meshPartBuilder = modelBuilder.part("box", GL20.GL_TRIANGLES, attr, new Material(TextureAttribute.createDiffuse(type.textureRegionTop)));
            meshPartBuilder.setUVRange(type.textureRegionTop);
            meshPartBuilder.rect(-0.5f,-0.5f,-0.5f, -0.5f, 0.5f, -0.5f, 0.5f,0.5f,-0.5f, 0.5f,-0.5f,-0.5f, 0, 0, -1);
            meshPartBuilder.setUVRange(type.textureRegionSide);
            meshPartBuilder.rect(-0.5f,0.5f,0.5f, -0.5f,-0.5f,0.5f,  0.5f,-0.5f,0.5f, 0.5f,0.5f,0.5f, 0,0,1);
            meshPartBuilder.setUVRange(type.textureRegionSide);
            meshPartBuilder.rect(-0.5f,-0.5f,0.5f, -0.5f,-0.5f,-0.5f,  0.5f,-0.5f,-0.5f, 0.5f,-0.5f,0.5f, 0,-1,0);
            meshPartBuilder.setUVRange(type.textureRegionSide);
            meshPartBuilder.rect(-0.5f,0.5f,-0.5f, -0.5f,0.5f,0.5f,  0.5f,0.5f,0.5f, 0.5f,0.5f,-0.5f, 0,1,0);
            meshPartBuilder.setUVRange(type.textureRegionSide);
            meshPartBuilder.rect(-0.5f,-0.5f,0.5f, -0.5f,0.5f,0.5f,  -0.5f,0.5f,-0.5f, -0.5f,-0.5f,-0.5f, -1,0,0);
            meshPartBuilder.setUVRange(type.textureRegionBottom);
            meshPartBuilder.rect(0.5f,-0.5f,-0.5f, 0.5f,0.5f,-0.5f,  0.5f,0.5f,0.5f, 0.5f,-0.5f,0.5f, 1,0,0);
            type.model = modelBuilder.end();
        }
    }
}

This is the texture file I use as input.

Texture file

That code produces the following cubes (depending on the texture). Image of the rendered cubes

Any idea how to fix this? I have tried to read about this but still cant fully understand the problem.

EDIT 1:

After setting every texture to null except in new Material(TextureAttribute.createDiffuse(type.textureRegionTop))); I got the following result:

Image of results

like image 944
Alexander Sagen Avatar asked Sep 03 '25 05:09

Alexander Sagen


1 Answers

Let us review the classes Texture and TextureRegion in libGDX:

  • Teture: A tetxure is a (most commonly 2D) image where the intent is to paste it onto a 3D polygon to give it more visual detail.

  • TextureRegion: For efficiency and convenience multiple textures can be combined into a single file, as is the case with your image file. So for an image that is not a single texture by itself, you would define parts of the image as separate texture regions.

From a libGDX API perspective, there are two ways to tell it to use a TextureRegion:

  1. Call createDiffuse(TextureRegion) which sets a fixed region of your image file as texture.

  2. Call createDiffuse(Texture) to load the whole Texture as Material and then call setUVRange(TextureRegion).

Your error is that you inappropriately mixed those two methods: You call createDiffuse(TextureRegion) and then ALSO call setUVRange(TextureRegion), so you define a texture region TWICE. My guess is this results in a tetxure region within a texture region with the size of exactly a single pixel (as there are 16x16 textures of 16x16 pixels) which explains your flat color results.

The easiest way to fix it is using number 2 and replacing createDiffuse(type.textureRegionTop) with createDiffuse(texture)! I will also demonstrate number 1 below for completeness.


Before:

img1

After:

img2

I also added transparency for glass using BlendingAttribute(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA):

img3

And turned back-face culling off using IntAttribute.createCullFace(GL20.GL_NONE) and DepthTestAttribute(false):

img4

The cubes are pointing to the side to due up-direction in OpenGL being the +Y-direction, but I think you'll manage to fix this yourself. (:


Here is a complete working example. Create a minimal libGDX project with their tool and save the texture as "minecraft.png" under e.g. android/assets. I used Kotlin rather than Java because I had a Kotlin project lying around, but you only need to change your code from two lines before modelBuilder.begin() to .end() anyway (and glClearColor):

package io.github.l0lk.zombieyeti.block;

import com.badlogic.gdx.ApplicationAdapter
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.graphics.g3d.*
import com.badlogic.gdx.graphics.g3d.attributes.*
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder

enum class BlockType(var nameca: String, var textureTopX: Int, var textureTopY: Int, var textureSideX: Int, var textureSideY: Int, var textureBottomX: Int, var textureBottomY: Int) {
    GRASS("dirt", 0, 0, 3, 0, 2, 0),
    STONE("stone", 1, 0, 1, 0, 1, 0),
    PLANK("plank", 4, 0, 4, 0, 4, 0),
    GLASS("glass", 1, 3, 1, 3, 1, 3);

    var textureRegionTop: TextureRegion? = null
    var textureRegionSide: TextureRegion? = null
    var textureRegionBottom: TextureRegion? = null
    var model: Model? = null

    companion object {
        var TEXTURE_WIDTH = 16
        fun createTexturesAndModels(texture: Texture?) {
            val modelBuilder = ModelBuilder()
            for (type in values()) {
                type.textureRegionTop = TextureRegion(texture, type.textureTopX * TEXTURE_WIDTH, type.textureTopY * TEXTURE_WIDTH, TEXTURE_WIDTH, TEXTURE_WIDTH)
                type.textureRegionSide = TextureRegion(texture, type.textureSideX * TEXTURE_WIDTH, type.textureSideY * TEXTURE_WIDTH, TEXTURE_WIDTH, TEXTURE_WIDTH)
                type.textureRegionBottom = TextureRegion(texture, type.textureBottomX * TEXTURE_WIDTH, type.textureBottomY * TEXTURE_WIDTH, TEXTURE_WIDTH, TEXTURE_WIDTH)
                val attr = (VertexAttributes.Usage.Position or VertexAttributes.Usage.Normal or VertexAttributes.Usage.TextureCoordinates).toLong()
                val materialAttrs = arrayOf(BlendingAttribute(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA), IntAttribute.createCullFace(GL20.GL_NONE), DepthTestAttribute(false))
                modelBuilder.begin()
                modelBuilder.part("box", GL20.GL_TRIANGLES, attr, Material(TextureAttribute.createDiffuse(type.textureRegionTop), *materialAttrs))
                        .rect(-0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0f, 0f, -1f)
                modelBuilder.part("box", GL20.GL_TRIANGLES, attr, Material(TextureAttribute.createDiffuse(type.textureRegionSide), *materialAttrs))
                        .rect(-0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0f, 0f, 1f)
                modelBuilder.part("box", GL20.GL_TRIANGLES, attr, Material(TextureAttribute.createDiffuse(type.textureRegionSide), *materialAttrs))
                        .rect(-0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0f, -1f, 0f)
                modelBuilder.part("box", GL20.GL_TRIANGLES, attr, Material(TextureAttribute.createDiffuse(type.textureRegionSide), *materialAttrs))
                        .rect(-0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0f, 1f, 0f)
                modelBuilder.part("box", GL20.GL_TRIANGLES, attr, Material(TextureAttribute.createDiffuse(type.textureRegionSide), *materialAttrs))
                        .rect(-0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -1f, 0f, 0f)
                modelBuilder.part("box", GL20.GL_TRIANGLES, attr, Material(TextureAttribute.createDiffuse(type.textureRegionBottom), *materialAttrs))
                        .rect(0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 1f, 0f, 0f)
                type.model = modelBuilder.end()
            }
        }
    }
}

class Game : ApplicationAdapter() {

    lateinit var environment: Environment
    lateinit var cam: PerspectiveCamera
    lateinit var modelBatch: ModelBatch
    lateinit var models: List<ModelInstance>

    override fun create() {
        environment = Environment().apply { set(ColorAttribute(ColorAttribute.AmbientLight, 1f, 1f, 1f, 1f)) }
        BlockType.createTexturesAndModels( Texture(Gdx.files.internal("minecraft.png")) )
        modelBatch = ModelBatch()
        models = (0..3).map { ModelInstance(BlockType.values()[it].model).apply { transform.translate(it/1f, 0f, 0f) }}

        cam = PerspectiveCamera(67F, Gdx.graphics.width.toFloat(), Gdx.graphics.height.toFloat()).apply {
            position.set(4f, 2f, 2f)
            lookAt(2f, 0f, 0f)
            update()
        }
    }

    override fun render() {
        Gdx.gl.glViewport(0, 0, Gdx.graphics.width, Gdx.graphics.height)
        Gdx.gl.glClearColor(0f, 0f, 0f, 1f)
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT or GL20.GL_DEPTH_BUFFER_BIT)

        modelBatch.apply {
            begin(cam)
            models.forEach { render(it, environment) }
            end()
        }
    }
}
like image 70
xjcl Avatar answered Sep 04 '25 19:09

xjcl