Jump to content

Recommended Posts

Posted

I have a TileEntity that employs its own TileEntitySpecialRenderer. I'm using the Tessellator out of convenience, and settinng various Gl-caps-- especially a texture with alpha values between 0.0 and 1.0. It's a little bit crazy to explain the problem so just imagine a simple block whose 6 faces are all a single 16x16 of 1:1:1 RGB texture at 0.5 alpha.

 

Now imagine that there are two of these buggers in the world at head height and arranged like so "R-B" where "R" is this block and it happens to be Red ( tessellator.setColorRGBA_F(1.0f, 0.0f, 0.0f, 1.0f) ), "-" is air, and "B" is another TileEntity instance other than the fact that it knows to be Blue. You are looking directly at "R" and so "-B" are behind it in that order.

 

However, what I am experiencing is that, depending on the order of placement, and/or the order the entities are loaded (it seems), one might or might not see the Blue block. And then, usually if you swing around and look at things the other way (as in, "R-B" -swing around-> "B-R") then, through the Blue block you can see the Red block (which is the correct result). And, moreover, if you look such that you can see both blocks in your field of view (e.g., fly 10m over 'em both and look down) then BOTH render just fine and as expected.

 

What gives... why is this?

 

Here are my caps I am enabling/disabling:

	GL11.glDisable(GL11.GL_LIGHTING);
	GL11.glEnable(GL11.GL_CULL_FACE);
	GL11.glDisable(GL11.GL_ALPHA_TEST);
	GL11.glEnable(GL11.GL_BLEND);
	GL11.glEnable(GL11.GL_DEPTH_TEST);
	GL11.glAlphaFunc(GL11.GL_GREATER, 0.1f);
	GL11.glDepthMask(true);

 

I've tinkered with all of those (enabling/disabling/trying differing values) but the results never align with my needs. I've also tinkered with Block.getRenderPass() and Block.canRenderInPass(int pass) methods and-- they have impact but no combination of settings appears to work properly.

 

FWIW, My tessellator logic draws 8 quads per face (e.g., CW then CCW). User right-clicks the block with Dye-in-hand to change the color (and this works); color is saved/loaded via NBT and tile-updates (works great). The real issue is the weird alpha occlusion behavior. It's like, I want the TESRs to render in reverse order of how MineCraft decides to.

 

And. To make matters worse: I have another TileEntity (call it "X") with its own TESR and, guess what? Sometimes you can see it "through" the "R" or "B" block; and other times you cannot (yet it renders by itself just fine all other times).

 

Does MineCraft's rendering engine have issues relating to render-order and how can I control or give hints to the engine to render "R" and "B" blocks after rendering "X" blocks (and whatever else)?

 

Thanks!

Posted

Hi

 

When alpha blending, the rendering order of the faces is important because if you draw the near face first, the further-away face can get culled, and also because the result of blending A onto B is usually not the same as blending B onto A.

You could try turning off depth buffer rendering when rendering your transparent faces, so that front faces don't totally hide the rear faces.  But that might not help; you may need to sort the faces in your TESR to make sure you always render them from furthest-first to nearest-last.

 

eg here for more background info

http://www.opengl.org/archives/resources/faq/technical/transparency.htm  15.070

http://www.opengl.org/wiki/Transparency_Sorting

http://www.openglsuperbible.com/2013/08/20/is-order-independent-transparency-really-necessary/

 

Minecraft has pass 0 vs pass 1 to make sure that transparent (alpha blended) is rendered after non-alpha blended.

See this link, the "Rendering Transparent Blocks" section

http://greyminecraftcoder.blogspot.com.au/p/list-of-topics.html

 

(Although- offhand I'm not sure how that directly applies to TESR.  I think that TESR are rendered after all the blocks are rendered, including alpha (pass 1) blocks).

 

-TGG

 

-TGG

 

 

 

Posted

@TGG: Hey, all that got me thinking (thank-you) and as I was experimenting with face-render-order I discovered a few things. I found your post about order-independent rendering especially helpful even though it didn't help in my situation.

 

First: I think you are correct that TESRs don't render in the 0/1 passes but after. I know this because I was enabling Depth-writing since it brought up another issue I wasn't worried about right now. But, I fixed it so that my block rendered in pass-0, and then disabled depth-writes in the TESR and it is no longer an issue! Yet, and here's the thing: a vanilla sign (that uses a TESR) doesn't get alpha-blended. (*, see following)

 

Secondly: sadly, the face rendering order didn't pay off and I tried numerous combinations. It's easy to draw the faces farthest-first. But, and we all have a big one, how am I to render the farther-block's faces when Minecraft decides to stimulate the nearer TESR first and the next farther second? Culling seems not to be the issue. (**)

 

Thirdly (and *): launching Minecraft and looking through the nearest transparent block (of mine) at a sign: the sign is not colored at all. Scooting over and looking through another transparent block (of mine), however, does correctly render the sign colored. I tried many various glBlendFunction's to no avail and even came across this interesting site: http://www.andersriggelsen.dk/glblendfunc.php (you gott'a love the death star).

 

Fourthly if you are still keeping count: I'm starting to think I need my TESRs when invoked to renderTileEntityAt -> to message to a singleton of this. Once the singleton figures that all of the rellevant TESRs have signaled, then it computes transparency order and has each render in the proper order. But, geeze. That sounds like reinventing the wheel!

 

Fifthly: to determine the player's view bearing-- which way the player is looking-- I need to interrogate the differences between the player's X:Y:Z and the X:Y:Z provided in the renderTileEntityAt call. Or is there a simpler method like, dunn'o, "public static Vec3 PlayerHelper.playerIsLookingTowards()"? it's another case of reinventing the wheel and so be it if so.

 

Unless something is obvious, I think I'm going to have to reproduce the effect in simplified code/classes. It may just be a limitation in Minecraft. So: why aren't beacon-beams transparent? Oh, but they DO use some alpha-blending (32/255)..

 

** PS: I'm using culling simply because I like the effect better. I can disable it all the same. But I still have all these issues with it on or off.

 

--

Chris.

Posted

Hi

 

Yeah it gets complicated real fast when you start trying to do alpha transparency "properly".  You could add an independent rendering step, sort of a custom TESR render like you say and I'm sure it's possible (I've done something very similar myself), although it was a lot of work to get it right and you'd have to be real careful to make sure it doesn't overload the game if there are too many TESRs.

 

Re x,y,z : from memory the render x,y,z are relative to the player position, i.e. translated so that the player is at 0,0,0.  You can tell pretty easily where the player is looking from its yaw and pitch.  For more clues look in EntityRenderer.renderWorld(), which also deals with the Camera Frustrum (eg so that things behind you, outside of your viewing frustrum, don't get rendered).

 

-TGG

 

PS nifty site, thanks for the link :)

Posted

Well hooray, I've simplified the problem into a reproducible study! But not so much. The issue persists.

 

I've simplified my project into a set of 7 classes (OK, well, I detest that CommonProxy gets annotated as "serverSide" and "clientSide" is "ClientProxy" so the extra class is "AbstractProxy" of which "ClientProxy" and "ServerProxy" extend); nest 'em? nah. And I would prefer to utilize an Interface for shared values but, for this simplification, it's OK and they're just stuck in the mod's core class. I will digress about "par1" all over the place and truly go over the edge regarding line-placement of opening and closing, "{" and "}," brackets. It's "x" it's not "par1" or "var1" or whatever. And no, I have not turned my computer off and then on again.

 

My ambition is to a) figure out Git Hub, b) commit this study there, c) mutate it so there are two blocks: the first that doesn't render correctly to showcase this issue (and that's what I have right now), and second to path-find this notion of a trailing renderator (prefer "executrix" at this point for dead folk like me at this point) in order to properly render the second-kind-a-block.

 

But not until Monday. And besides codes, I'll post screen shots of the issue.

 

BTW, hadn't mentioned this, but I'm using "forgeSrc-1.7.10-10.13.0.1180.jar" and no other mods than this one; in Eclipse Kepler SR2. My windows-8-pro computer has 8 logical i7 processors @ 3ghz, 16gb ram, small ssd drive :(, and a wicked graphics adapter :).

 

Posted

Playing around in net.minecraft.client.renderer.EntityRenderer I found, in the

renderWorld(...)

method:

     ForgeHooksClient.dispatchRenderLast(renderglobal, p_78471_1_);

Which lead me on to register (and

@SubscribeEvent

) a class to

onRenderLastEvent(RenderWorldLastEvent event)

.

 

That method:

[*]Fetches all of my TileEntities into a list

[*]For each, computes its distance to the player

[*]Sorts the list farthest first to nearest last (the

AlphaTileEntity

class implements

Comparable

)

[*]Then renders each in that order

 

And it works without needing a TESR at all. Ideally I should check if the TileEntity is in view, but I skipped that for now.

 

Before I paste in the code I'll demonstrate the effect seen using the TESR and without (via RenderWorldLastEvent).

Using a TESR:

 

 

SgkcyMR.png

Notice that neither the sign nor orange transparency are shaded white; yet the green transparency is shaded.

 

Moving around to the other side, we see this instead:

IUTdPGk.png

The sign is not properly shaded, but all the transparencies are.

 

 

 

Via RenderWorldLastEvent:

 

 

3GYFHYe.png

The sign is properly shaded as well the transparencies.

mGjwG51.png

Again, a-OK from the other side.

 

 

 

Following are my classes (I simply altered the value of oldSchool in the ModBase class to affect usage of the TESR or not).

AlphaRenderer (the TESR):

 

 



package tofer17;

import org.lwjgl.opengl.GL11;

import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ResourceLocation;

public class AlphaRenderer extends TileEntitySpecialRenderer {

    private static final ResourceLocation ALPHA_TEXTURE = new ResourceLocation(ModBase.MOD_ID + ":textures/entities/alpha.png");


@Override
public void renderTileEntityAt (TileEntity tileEntity, double x, double y, double z, float partialTicks) {
if ( ModBase.oldSchool ) renderTileEntityAt( (AlphaTileEntity)tileEntity, x, y, z, partialTicks );
}


protected static void renderTileEntityAt (AlphaTileEntity alpha, double x, double y, double z, float partialTicks) {

final TextureManager textureManager = Minecraft.getMinecraft().getTextureManager();

if ( textureManager != null ) textureManager.bindTexture( ALPHA_TEXTURE );

GL11.glPushMatrix();

final Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();

GL11.glShadeModel(GL11.GL_SMOOTH);

GL11.glDisable(GL11.GL_LIGHTING);

GL11.glEnable(GL11.GL_CULL_FACE);

GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc( GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA );

GL11.glDisable(GL11.GL_ALPHA_TEST);

GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glDepthMask(false);


final int[] rgbColors = ModBase.COLORS[ alpha.getColor() ];

GL11.glColor4f( (float)rgbColors[0] / 256.0f, (float)rgbColors[1] / 256.0f, (float)rgbColors[2] / 256.0f, 0.5f );

GL11.glTranslated( (float)x, (float)y, (float)z );

final double zd = 0.0d; // Zero
final double od = 1.0d; // One
final double td = 3.0d; // Three

final double u = 1.0d; // u
final double v = 0.0d; // v having fun

// O = Outer, I = Inner, F1 = face-1, F2 = face-2, etc., NEWS = North, East, etc.
// ABCD: order, and the u,v values

// O-F1 - W
tessellator.addVertexWithUV(zd, od, zd, u,v); // A 1,0
tessellator.addVertexWithUV(zd, od, od, v,v); // B 0,0
tessellator.addVertexWithUV(zd, td, od, v,u); // C 0,1
tessellator.addVertexWithUV(zd, td, zd, u,u); // D 1,1

// O-F2 - N
tessellator.addVertexWithUV(zd, td, zd, u,u); // D 1,1
tessellator.addVertexWithUV(od, td, zd, v,u); // C 0,1
tessellator.addVertexWithUV(od, od, zd, v,v); // B 0,0
tessellator.addVertexWithUV(zd, od, zd, u,v); // A 1,0

// O-F3 - E
tessellator.addVertexWithUV(od, td, zd, u,u); // D 1,1
tessellator.addVertexWithUV(od, td, od, v,u); // C 0,1
tessellator.addVertexWithUV(od, od, od, v,v); // B 0,0
tessellator.addVertexWithUV(od, od, zd, u,v); // A 1,0

// O-F4 - S
tessellator.addVertexWithUV(zd, od, od, u,v); // A 1,0
tessellator.addVertexWithUV(od, od, od, v,v); // B 0,0
tessellator.addVertexWithUV(od, td, od, v,u); // C 0,1
tessellator.addVertexWithUV(zd, td, od, u,u); // D 1,1


// I-F1 - W
tessellator.addVertexWithUV(zd, td, zd, u,u); // D 1,1
tessellator.addVertexWithUV(zd, td, od, v,u); // C 0,1
tessellator.addVertexWithUV(zd, od, od, v,v); // B 0,0
tessellator.addVertexWithUV(zd, od, zd, u,v); // A 1,0

// I-F2 - N
tessellator.addVertexWithUV(zd, od, zd, u,v); // A 1,0
tessellator.addVertexWithUV(od, od, zd, v,v); // B 0,0
tessellator.addVertexWithUV(od, td, zd, v,u); // C 0,1
tessellator.addVertexWithUV(zd, td, zd, u,u); // D 1,1

// I-F3 - E
tessellator.addVertexWithUV(od, od, zd, u,v); // A 1,0
tessellator.addVertexWithUV(od, od, od, v,v); // B 0,0
tessellator.addVertexWithUV(od, td, od, v,u); // C 0,1
tessellator.addVertexWithUV(od, td, zd, u,u); // D 1,1

// I-F4 - S
tessellator.addVertexWithUV(zd, td, od, u,u); // D 1,1
tessellator.addVertexWithUV(od, td, od, v,u); // C 0,1
tessellator.addVertexWithUV(od, od, od, v,v); // B 0,0
tessellator.addVertexWithUV(zd, od, od, u,v); // A 1,0


tessellator.draw();

GL11.glPopMatrix();


        GL11.glShadeModel(GL11.GL_FLAT);
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glDisable(GL11.GL_CULL_FACE);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glDepthMask(true);

}


}

 

 

 

And AlphaRenderLastEventHandler

 

 



package tofer17;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityClientPlayerMP;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;

public class AlphaRenderLastEventHandler {

// Our subset
private final List<AlphaTileEntity> alphas = new ArrayList<AlphaTileEntity>();


@SubscribeEvent
public void onRenderLastEvent (RenderWorldLastEvent event) {

if ( ModBase.oldSchool ) return;

// Clear our subset
alphas.clear();

// Fetch all AlphaTileEntities
for ( Object o : event.context.tileEntities ) {
if ( o instanceof tofer17.AlphaTileEntity ) {
alphas.add( (AlphaTileEntity)o );
}
}

if ( alphas.isEmpty() ) return; // Move along, nothing to see here...

// Compute player's position
//  From RenderGlobal: double d0 = p_147589_1_.prevPosX + (p_147589_1_.posX - p_147589_1_.prevPosX) * (double)p_147589_3_;
final EntityClientPlayerMP thePlayer = Minecraft.getMinecraft().thePlayer;
final double partialTicks = (double)event.partialTicks;
final double playerX = thePlayer.prevPosX + (thePlayer.posX - thePlayer.prevPosX) * partialTicks;
final double playerY = thePlayer.prevPosY + (thePlayer.posY - thePlayer.prevPosY) * partialTicks;
final double playerZ = thePlayer.prevPosZ + (thePlayer.posZ - thePlayer.prevPosZ) * partialTicks;


// Compute distances
for ( AlphaTileEntity alpha : alphas )
alpha.setDistanceToPlayer( alpha.getDistanceFrom( playerX, playerY, playerZ ) );

// Sort them farthest to nearest
Collections.sort( alphas );


// Render them!
for ( AlphaTileEntity alpha : alphas ) {

// Translate off the player
final double x = (double)alpha.xCoord - playerX;
final double y = (double)alpha.yCoord - playerY;
final double z = (double)alpha.zCoord - playerZ;

// Old school render
AlphaRenderer.renderTileEntityAt( alpha, x, y, z, event.partialTicks );
}


}


}

 

For what it's worth, I register the event like this:

	MinecraftForge.EVENT_BUS.register( new AlphaRenderLastEventHandler() );

(in ModBase init...)

 

 

 

And there you have it!

  • 6 months later...
Posted

Apologies for the necro-bump, but I'd like to follow up with a "yay, this works" and one particular issue I've found. The Tile renders on top of the bounding box. I don't suppose there might be a quick/easy fix for this?

  • Like 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.