Jump to content

Recommended Posts

Posted

Heyho Guys!

I need a few tips on rendering. I have several things I want to do and I want to know whether there is an easy solution to them, possibly a single method that I can call. Optionally also a self written method...

[*]Rendering a texture in a solidified way (like an Item)

[*]Rendering an IBakedModel

[*]Like 1, but with the enchantment effect on the texture. Also: overlaying a drawn face with the enchantment glint

[*]Rendering an .obj file

[*]Rendering an Item or Block like they are shown in a GUI (2D) but solidified (like 1). Not sure if this is even possible

 

I hope, some of you have got an idea how to do some of the things above!

 

-BM

Posted

Heyho Guys!

I need a few tips on rendering. I have several things I want to do and I want to know whether there is an easy solution to them, possibly a single method that I can call. Optionally also a self written method...

[*]Rendering a texture in a solidified way (like an Item)

[*]Rendering an IBakedModel

[*]Like 1, but with the enchantment effect on the texture. Also: overlaying a drawn face with the enchantment glint

[*]Rendering an .obj file

[*]Rendering an Item or Block like they are shown in a GUI (2D) but solidified (like 1). Not sure if this is even possible

 

I hope, some of you have got an idea how to do some of the things above!

 

-BM

Hi

 

Those are all possible, 1 - 3 are quite easy with a bit of digging into the rendering code.  4 is possible if you port the 1.7.10 object model code (for use in a tessellator), I recall a post about this a few months ago - or use Blender or similar to convert your obj to B3D (which is now supported in Forge).

 

5 is also possible:

first part is to render your 3D model to a texture, second part is to convert the texture to an item with  "thickness".  This class does the first part (renders your 3D model to a flat texture) and the item generation will do the second bit.

https://github.com/TheGreyGhost/SpeedyTools/blob/master/src/main/java/speedytools/clientside/selections/SelectionBlockTextures.java

You would need to render from an oblique angle instead of looking directly at each face, but that's a simple rotation and scale.

 

Let me know if you have trouble tracking down 1 -3 , I am AFK for 10 hours but can look at it this evening.

 

-TGG

Posted

Hi

 

1: look in ItemModelGenerator.makeItemModel()

2: check out BlockModelRenderer

3: This link shows an example of custom layers for your item model

https://github.com/TheGreyGhost/MinecraftByExample/blob/master/src/main/java/minecraftbyexample/mbe11_item_variants/Notes.txt

and this one talks a bit more about layers and about the enchantment "glint"

http://greyminecraftcoder.blogspot.com.au/2014/12/item-rendering-18.html

See also RenderItem.renderEffect()

 

-TGG

Posted

Oh, thanks that you researched that for me!

 

I don't have time to look at it right now, but I will do this the next days.

 

I just wanted to add another thing I might want to do, namely taking several textures, and baking them together dynamically. (Like the tools in Tinker's Construct). So I have a texture for, say a shovel, and another one for a bigger shovel handle and I want to overlay them to be used as one texture. Even better if this works with Alpha as well, so that you can see the texture of a texture even if its overlayed by a halfway transparent one.

Posted

With ICustomModelLoader and your own IBakedModel you can do this quite easily.

https://github.com/MinecraftForge/MinecraftForge/blob/master/src/test/java/net/minecraftforge/debug/ModelBakeEventDebug.java

or

https://github.com/TheGreyGhost/MinecraftByExample/blob/master/src/main/java/minecraftbyexample/mbe05_block_smartblockmodel2/Notes.txt

 

You've got enough rendering challenges there to last you for quite a while I think :P

 

-TGG

 

 

Posted

I'll note that you might be able to get vanilla to render an item by creating an EntityItem and telling the rederer to render it.  (No need to spawn it in the world).

 

It'll have more overhead than some other solutions, but it's a straightforward way to get the existing systems to do what you want as long as you can supply the necessary object.  It's how I did it for a display case like block in 1.7.10 and I'd wager that it'd still work in 1.8, due to the ease of creating an EntityItem and the fact that such entities need to be independently rendered (like all entities).

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Posted

OK, so I experimented and here are my results:

 

1. The method TGG mentioned is basically a Generator for an IBakedModel, not a renderer for a simple texture solidified, so I decided to write my own method that renders the texture on the fly:

 

public static void renderSolidifiedFace(TexPos position, float thickness) {
	int imgwidth = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_WIDTH);
	int imgheight = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_HEIGHT);
	int width = (int) (imgwidth * position.getWidth());
	int height = (int) (imgheight * position.getHeight());

	stretchTextures = true;

	rotate(-90, 1, 0, 0);

	startDrawingQuads();
	renderQuadFace(new Vertex(0, -thickness / 2.0, 0), new Vertex(1, -thickness / 2.0, 0), new Vertex(1, -thickness / 2.0, 1), new Vertex(0, -1, 0), position);
	renderQuadFace(new Vertex(1, thickness / 2.0, 0), new Vertex(0, thickness / 2.0, 0), new Vertex(0, thickness / 2.0, 1), new Vertex(0, -1, 0), position.interpolate(1, 0, 0, 1));

	for (int u = 0; u < width; u++) {
		for (int v = 0; v < height; v++) {
			double x = (double) u / width;
			double y = (double) v / height;
			double X = (double) (u + 1) / width;
			double Y = (double) (v + 1) / height;
			double z = -thickness / 2.0;
			double Z = thickness / 2.0;
			stretchTextures = true;
			renderAABoxWalls(new Vertex(x, -thickness / 2.0, y), new Vertex(X, thickness / 2.0, Y), position.interpolate(x, y, X, Y));
			stretchTextures = false;
		}
	}
	draw();
	stretchTextures = false;

	rotate(90, 1, 0, 0);
}

(I used some of my own rendering methods and objects, I hope, its self-explanatory anyway)

With this method the rendering is pretty easy and the results are awesome.

0mn1ovk.png


 

2. I dug a bit in the source code and found an easy method to render an IBakedModel. Unfortunately, is does not work for builtin/entity renderers, but I didn't expect that anyway.

 

public static void renderModel(IBakedModel model) {
	translate(0.5, 0.5, 0.5);
	scale(2.0, 2.0, 2.0);
	ClientUtils.mc().getRenderItem().renderItem(new ItemStack(Blocks.stone), model);
	scale(0.5, 0.5, 0.5);
	translate(-0.5, -0.5, -0.5);
}

BTW, does anyone know why IBakedModel is deprecated?


 

3. I looked at RenderItem.renderEffect(), but I don't understand what it does. Mostly because the OpenGL constants are there as numbers, not as the constant names. Maybe someone could help me understanding how this works, this would help me a lot.


 

4. I found an example on Forge's B3D loader, but it didn't work well for me.

https://github.com/MinecraftForge/MinecraftForge/blob/master/src/test/java/net/minecraftforge/debug/ModelLoaderRegistryDebug.java

I created a B3D model which has an Animation (scaling up/down along the y axis). I applied this model to a block using the method in the example. My model was rendered wrong, namely 0.5 Blocks moved away along every axis and without texture. Also, I discovered that the animation does not work. Any ideas what I could have done wrong?

 

Block class:

public class BlockMagicTable extends BlockContainer {

private ExtendedBlockState state = new ExtendedBlockState(this, new IProperty[0], new IUnlistedProperty[]{ B3DLoader.B3DFrameProperty.instance });
private int counter;

@Override
    public IBlockState getExtendedState(IBlockState state, IBlockAccess world, BlockPos pos)
    {
        IModel model = ModelLoaderRegistry.getModel(new ResourceLocation("magicum:block/Untitled.b3d"));
        B3DLoader.B3DState defaultState = ((B3DLoader.Wrapper)model).getDefaultState();
        B3DLoader.B3DState newState = new B3DLoader.B3DState(defaultState.getAnimation(), this.counter);
        return ((IExtendedBlockState)this.state.getBaseState()).withProperty(B3DLoader.B3DFrameProperty.instance, newState);
    }

@Override
    public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumFacing side, float hitX, float hitY, float hitZ)
    {
        if(world.isRemote)
        {
            if(player.isSneaking()) this.counter--;
            else this.counter++;
            Log.info_("Counter:%s", this.counter);
            world.markBlockRangeForRenderUpdate(pos, pos);
        }
        return false;
    }
[...]
}

Initialization (in client preInit):

B3DLoader.instance.addDomain(Magicum.MODID);
ModelBakery.addVariantName(Item.getItemFromBlock(block), "magicum:Untitled.b3d");
ModelLoader.setCustomModelResourceLocation(Item.getItemFromBlock(MagicumBlocks.magic_table), 0, new ModelResourceLocation("magicum:block/Untitled.b3d", "inventory"));

...please write if you need more code.


 

5. Well, it's a fancy class in this GitHub repo, but I really don't understand how I use it and how this can help me with my problem.


 

I've not thought about 6 a lot, want to solve the others first.

Posted

Hi

 

1.  keen.

2.IBakedModel is deprecated because one of the forge team (Rainwarrior I think) wants you to use IFlexibleBakedModel instead.

/*

* Version of IBakedModel with less restriction on camera transformations and with explicit format of the baked array.

*/

Just ignore it unless you are trying to do fancy stuff with your models and you know what you're doing.

3. In order to figure out the OpenGL constants, you can do this-

a) Convert the number to hexadecimal

eg

        GlStateManager.depthFunc(514);

becomes

        GlStateManager.depthFunc(0x202);

b) Search the GL11 class for 0x202

--> GL_EQUAL = 0x202,

or

        GlStateManager.matrixMode(5890);

-->  GlStateManager.matrixMode(0x1702);

--> GlStateManager.matrixMode(GL_TEXTURE);

 

This methods renders the model again, but binding the "glint" texture, using an unusual blend mode and translating/rotating the texture in a time-dependent way to give it the moving glint appearance.

4. Sorry, had no experience with B3D, can't help there...

5. How I think you could solve your problem:

a) Render your 3D item or block as normal, but to a texture instead of to the screen.  You now have a flat 2D texture of your item/block that looks the same as when displayed in your inventory. 

b) You can then convert it into an "item with thickness" exactly the same as you did in 1)

The methods in that class in the repo show you how to do step a).  You will need to rotate the block to be oblique view instead of direct-on to a face like that class does.

 

-TGG

 

Posted

After a large bit of refactoring, I got the glint effect to work partially. After a bit more research on the glMatrixMode method, I finally understood what the method does and I changed it a bit so that it works in my favor.

I have now a pretty useful enchantment renderer. It has some restrictions, but it works fine.

 

	/**
 * Runs the given renderer wrapped in a Runnable as a renderer for the
 * enchantment glint effect. This only works if the current texture is not
 * changed during execution of the runnable. Also, the colour should not
 * be changed during rendering, or it will lead to some weird results.
 * Basically, the only things that should be done are Matrix transformations
 * and rendering calls of vertices.
 * <p>
 * If some of the settings are changed during the rendering call or if the
 * texture scale is wrong, this can lead to pretty weird results. If you use
 * this method you need to play around with the scale until you find a good
 * value.
 * <p>
 * Usage:
 *
 * <pre>
 * anyRenderer(x, y, z);
 * runRendererWithGlint(new Runnable() {
 *     public void run() {
 *         anyRenderer(x, y, z);
 *     }
 * });
 * </pre>
 * <p>
 * <strong>
 * <em>The WorldRenderer needs to be off when this method is called!</em>
 * </strong><br>
 * <em>This discards the current texture</em>
 *
 * @param renderer
 * the renderer to run as a glint renderer.
 * @param textureScale
 * the scale of the texture. This sets the scale of the enchantment glint
 * texture parts. Some renderers need a value here in order to
 * display properly. You need to experiment a bit in order to find a good
 * value. For vanilla models, the standard value is 8.
 */
public static void runRendererWithGlint(Runnable renderer, float textureScale) {
	GlStateManager.depthMask(false);
	GlStateManager.depthFunc(GL11.GL_EQUAL);
	GlStateManager.disableLighting();
	GlStateManager.blendFunc(GL11.GL_SRC_COLOR, GL11.GL_ONE);
	GlStateManager.alphaFunc(GL11.GL_GREATER, 0.1f);
	enableBlend(true);
	bindTexture(new ResourceLocation("textures/misc/enchanted_item_glint.png"));

	pushMatrix(); // Push MODEL
	GlStateManager.matrixMode(GL11.GL_TEXTURE);
	pushMatrix(); // Push TEXTURE
	scale(textureScale, textureScale, textureScale);
	float f = Minecraft.getSystemTime() % 3000L / 3000.0F / textureScale;
	translate(f, 0.0F, 0.0F);
	rotate(-50.0F, 0.0F, 0.0F, 1.0F);
	GlStateManager.matrixMode(GL11.GL_MODELVIEW);
	setColor(new Color(0.5, 0.2, 0.);
	renderer.run();
	popMatrix(); // Pop MODEL

	pushMatrix(); // Push MODEL
	GlStateManager.matrixMode(GL11.GL_TEXTURE);
	popMatrix(); // Pop TEXTURE
	pushMatrix(); // Push TEXTURE
	scale(textureScale, textureScale, textureScale);
	float f1 = Minecraft.getSystemTime() % 4873L / 4873.0F / textureScale;
	translate(-f1, 0.0F, 0.0F);
	rotate(10.0F, 0.0F, 0.0F, 1.0F);
	GlStateManager.matrixMode(GL11.GL_MODELVIEW);
	setColor(new Color(0.5, 0.2, 0.);
	renderer.run();
	popMatrix(); // Pop MODEL
	GlStateManager.matrixMode(GL11.GL_TEXTURE);
	popMatrix(); // Pop TEXTURE

	GlStateManager.matrixMode(GL11.GL_MODELVIEW);
	GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
	GlStateManager.enableLighting();
	GlStateManager.depthFunc(GL11.GL_LEQUAL);
	GlStateManager.depthMask(true);
}

 

 

Still nothing new about the b3d thing, I think the guys from Forge are the only ones who know how it works.

Also, I understood how the SelectionBlockTextures thing works, at least the basics of it. I still don't quite know how to use it in order to render the "gui" state of the block onto a texture and use it afterwards. Best thing would be if I could just create an instance of a class with the block as an argument, it registers automatically as IResourceManagerReloadListener and provides methods to bind the generated texture to OpenGL.

Posted

I worked a bit with the SelectionBlockTextures thingy and was able to use it.  ;D

Then I decided to make it a bit more generic by providing a Renderer wrapped in a Runnable instead of a block. That's were I obviously messed up, because now I always get a completely black image.

I'll post my code here, hopefully anyone can tell me what I did wrong (maybe I miss some transformations before the rendering?)

 

My modified class:

public class RenderResultTexture implements IResourceManagerReloadListener {

private static final int SBT_COUNT_WARNING = 10;
private static int rrtCount = 0;

private Runnable renderer;
private Function<Integer, Integer> colorFunction = new Function<Integer, Integer>() {
	@Override
	public Integer apply(Integer input) {
		return input | 0xff000000;
	}
};

private final DynamicTexture blockTextures;
private final ResourceLocation textureResourceLocation;
private final TextureManager textureManager;

private final int WIDTH;
private final int HEIGHT;

private boolean isReleased = false;

public RenderResultTexture(int width, int height) {
	this.textureManager = ClientUtils.mc().getTextureManager();
	this.WIDTH = width;
	this.HEIGHT = height;

	this.blockTextures = new DynamicTexture(this.WIDTH, this.HEIGHT);
	this.textureResourceLocation = this.textureManager.getDynamicTextureLocation("RenderResultTexture", this.blockTextures);
	ResourceManagerListenerRegistry.getInstance().registerListener(this);

	this.eraseBlockTextures();
	this.blockTextures.updateDynamicTexture();

	++rrtCount;
	if (rrtCount > SBT_COUNT_WARNING) {
		Log.warn_("[RenderResultTexture] Potential Memory Leak: Allocated %s textures without release()", rrtCount);
	}
}

/**
 * Sets the renderer used by this RenderResultTexture object. The renderer
 * is not called yet. This automatically happens when updateTextures() is
 * called or the resource manager reloads.
 * @param renderer
 * the renderer to call, wrapped in a Runnable
 */
public void setRenderer(Runnable renderer) {
	this.renderer = renderer;
}

/**
 * Sets a new color function. This way, a grayscale image or similar can be
 * achieved.
 *
 * @param newColorFuntion
 * the color function to set.
 */
public void setColorFunction(Function<Integer, Integer> newColorFuntion) {
	if (newColorFuntion != null)
		this.colorFunction = newColorFuntion;
}

/**
 * Binds this texture to the TextureManager, ready to start rendering.
 */
public void bind() {
	this.textureManager.bindTexture(this.textureResourceLocation);
}

/**
 * Resets the renderer and clears the texture.
 */
public void reset() {
	this.setRenderer(null);
	this.eraseBlockTextures();
	this.blockTextures.updateDynamicTexture();
}

/**
 * Releases the stored texture and the memory space that was allocated.<br>
 * <strong>Do not use this object any more after you released it!</strong><br>
 * release() should be called right before the pointer for the object is set
 * to <code>null</code> or to a different value.
 */
public void release() {
	if (!this.isReleased) {
		ResourceManagerListenerRegistry.getInstance().unregisterListener(this);
		this.blockTextures.deleteGlTexture();
		--rrtCount;
		this.isReleased = true;
	}
}

/**
 * Updates the texture sheet and calls the renderer.
 */
public void updateTextures() {
	if (!OpenGlHelper.isFramebufferEnabled()) {
		// frame buffer not available, just use blank texture
		this.eraseBlockTextures();
		return;
	}

	Framebuffer frameBuffer = null;
	try {
		GL11.glPushAttrib(GL11.GL_ENABLE_BIT);
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glPushMatrix();
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glPushMatrix();

		// setup modelview matrix
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glLoadIdentity();
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();

		// set up to render over [0,0,0] to [1,1,1]
		GL11.glOrtho(0.0D, 1.0, 1.0, 0.0, -10.0, 10.0);

		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glEnable(GL11.GL_DEPTH_TEST);
		GL11.glDisable(GL11.GL_LIGHTING);
		GL11.glEnable(GL11.GL_BLEND);
		GL11.glEnable(GL11.GL_CULL_FACE);
		GL11.glEnable(GL11.GL_ALPHA_TEST);
		GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
		final float ALPHA_TEST_THRESHOLD = 0.1F;
		GL11.glAlphaFunc(GL11.GL_GREATER, ALPHA_TEST_THRESHOLD);

		final boolean USE_DEPTH = true;
		frameBuffer = new Framebuffer(this.WIDTH, this.HEIGHT, USE_DEPTH);
		frameBuffer.setFramebufferColor(0, 0, 0, 0);
		frameBuffer.framebufferClear();
		final boolean SET_VIEWPORT_TRUE = true;
		frameBuffer.bindFramebuffer(SET_VIEWPORT_TRUE);

		if (this.renderer != null)
			this.renderer.run();

		this.transferBufferIntoTextureSheet(frameBuffer);

	} catch (Exception e) {
		Log.error_(e.toString());
	} finally {
		if (frameBuffer != null) {
			frameBuffer.deleteFramebuffer();
		}
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glPopMatrix();
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glPopMatrix();
		GL11.glPopAttrib();
	}

	this.blockTextures.updateDynamicTexture();
}

/**
 * Overwrite the texture with blank (black) pixels.
 */
private void eraseBlockTextures() {
	int[] rawTexture = this.blockTextures.getTextureData();

	Arrays.fill(rawTexture, 0, rawTexture.length, Color.BLACK.toInt());
}

/**
 * transfers the given buffer's data into the texture sheet.
 *
 * @param frameBuffer
 * the buffer
 */
private void transferBufferIntoTextureSheet(Framebuffer frameBuffer) {
	frameBuffer.bindFramebufferTexture();
	IntBuffer pixelBuffer = BufferUtils.createIntBuffer(this.WIDTH * this.HEIGHT);
	GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixelBuffer);

	int[] frameData = new int[pixelBuffer.remaining()];
	pixelBuffer.get(frameData);

	int[] textureSheet = this.blockTextures.getTextureData();

	this.copyDataToTextureSheet(frameData, textureSheet);
}

/**
 * Copied the FrameBuffer's frameData to the textureSheet.
 *
 * @param frameData
 * the original data.
 * @param textureSheet
 * the textureSheet's data.
 */
protected void copyDataToTextureSheet(int[] frameData, int[] textureSheet) {
	for (int v = 0; v < this.HEIGHT; ++v) {
		for (int u = 0; u < this.WIDTH; ++u) {
			textureSheet[u + v * this.WIDTH] = this.colorFunction.apply(frameData[u + v * this.WIDTH]);
		}
	}
}

@Override
public void onResourceManagerReload(IResourceManager resourceManager) {
	this.updateTextures();
}
}

How I used it:

                //In the constructor
                ObjectRenderer.bindTexture(holder);
	Dimensions d = ObjectRenderer.getCurrentImageSize();
	this.t = new RenderResultTexture(d.width, d.height);
	this.t.setRenderer(new Runnable() {
		@Override
		public void run() {
			ObjectRenderer.bindTexture(base);
			ObjectRenderer.startDrawingQuads();
			ObjectRenderer.renderQuadFace(new Vertex(0,0,0), new Vertex(1,0,0), new Vertex(1,1,0), new Vertex(0,0,1), new TexPos(0,0,1,1));
			ObjectRenderer.draw();
		}
	});
	this.t.updateTextures();

                //During rendering callthis.t.bind();

	r.scale(0.75, 0.75, 1);
	r.startDrawingQuads();
	r.renderQuadFace(new Vertex(0, 0, 0), new Vertex(1, 0, 0), new Vertex(1, 1, 0), new Vertex(0, 0, 1), new TexPos(0, 0, 1, 1));
	r.draw();

 

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.