Jump to content

[1.14.4] Z fighting issues with semi-transparent entity layers


JayZX535

Recommended Posts

Hello all,

I'm working with an entity that has a large number of layers (currently five, and I'll likely be adding a few more).  Tl;dr, these entities can come in a LOT of different variations of coat color and markings.  Up close, they look great, but if I get a little ways away, the ones that show multiple layers start to have Z fighting issues.

273115326_zfighting.png.942c3b895a1582634fc850b338ae8bb4.png

 

You can see how the two entities with more markings here are showing that weird streaky pattern.  That isn't supposed to be there.  Most of the layers I have rely on transparency to blend well, and I've admittedly been fighting the GL system to make that work.  I thought I had it figured out, but I'm wondering if I'm still missing something here.  Minecraft doesn't have a good example of using multiple layers for something like this, so I'm struggling a little to figure out if I'm using the GL system properly.

 

In the past, I countered this problem by using the same layer rendering system that horses use, and that appeared to work great.  However, I've since switched to a system that colors the textures in the renderer itself (like sheep's wool does).  This allows me to have many more diverse options of color, however I'm not at all sure how to make that work with the system the horse renderer uses.  If I could switch back to that and retain the ability to color each layer independently I def would, but I'm not sure if that's possible or not.

 

I've been trying to keep the layers as compact as possible, because there's enough of them that the entity starts to develop a slight "glow" when they're all enabled (I believe due to the semi-transparent outer layers).  Right now they each have a size increase of 0.001.  Stepping that up to 0.01 seemed to help with the Z fighting, but then I started getting the glow around the entity.

 

The render code in the layer classes all runs along these lines:

 

public void render(ModEntity entityIn, float p_212842_2_, float p_212842_3_, float p_212842_4_, float p_212842_5_, float p_212842_6_, float p_212842_7_, float p_212842_8_) {
  if (!entityIn.isInvisible() && entityIn.showThisMarkingLayer())
  {
           this.bindTexture(this.getEntityTexture(entityIn));
           GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
           GlStateManager.enableBlend();
           float[] afloat = entityIn.getColor().getColorComponentValues(entityIn.getShade());
           GlStateManager.color3f(afloat[0], afloat[1], afloat[2]);
           this.getEntityModel().setModelAttributes(this.wolfmodel);
           this.wolfmodel.setLivingAnimations(entityIn, p_212842_2_, p_212842_3_, p_212842_4_);
           this.wolfmodel.render(entityIn, p_212842_2_, p_212842_3_, p_212842_5_, p_212842_6_, p_212842_7_, p_212842_8_);
           GlStateManager.disableBlend();
  }
}

 

Is there something I could do to help stop the Z fighting?  Also, is there a definitive order that I should be calling GL functions in?  What I have appears to work but I'd rather get it spot on by the books if I can...

Link to comment
Share on other sites

Howdy

I think trying to put five layers one on top of the other is going to be a struggle, and if you're using alpha blending that makes it worse because it becomes important what order you render the textures in (i.e. the rearmost first, then progressively nearer).  Sometimes that's easy to do, other times almost impossible.

The problem you're seeing is related to the precision of the depth buffer (which is why the size increase of 0.001 works fine up close but doesn't work further away).  There is no single magic number or OpenGL setting that can fix this, it's a fundamental rendering problem.

 

You could try adjusting your size increase depending on how far away the entity is from the viewer, that might help (i.e. your "glow" will be unnoticeable further away).

 

Personally I would consider using a custom dynamic texture for each entity, i.e. one that you generate algorithmically similar to the vanilla map - see DynamicTexture and MapItemRenderer.  There would be a few details to work out but it would avoid the problem of multiple layers entirely.  I've done that with blocks and item before but admittedly not with entity models.

 

-TGG

 

  • Like 1
Link to comment
Share on other sites

On 3/11/2020 at 1:21 AM, TheGreyGhost said:

Howdy

I think trying to put five layers one on top of the other is going to be a struggle, and if you're using alpha blending that makes it worse because it becomes important what order you render the textures in (i.e. the rearmost first, then progressively nearer).  Sometimes that's easy to do, other times almost impossible.

The problem you're seeing is related to the precision of the depth buffer (which is why the size increase of 0.001 works fine up close but doesn't work further away).  There is no single magic number or OpenGL setting that can fix this, it's a fundamental rendering problem.

 

You could try adjusting your size increase depending on how far away the entity is from the viewer, that might help (i.e. your "glow" will be unnoticeable further away).

 

Personally I would consider using a custom dynamic texture for each entity, i.e. one that you generate algorithmically similar to the vanilla map - see DynamicTexture and MapItemRenderer.  There would be a few details to work out but it would avoid the problem of multiple layers entirely.  I've done that with blocks and item before but admittedly not with entity models.

 

-TGG

 

Dang, that's more of a fundamental problem than I'd have liked, but it's good to know.  Thank you so much for your explanation and suggestions!

 

I've spent some time messing with the dynamic textures, but it seems like that really didn't like what I was trying to do.  Spawning in one entity gave me about one frame every three seconds, so unfortunately I don't think the system likes entities a lot (as far as I can tell it's only used in Vanilla code with entities for the Ender Dragon's death animation, which doesn't have to read a texture at all and just relies on randomly generated numbers).

 

I had better results with your other suggestion (scaling the size increase according to the player's distance), however.  This works, technically!  I'm not getting the Z fighting anymore, even when I have all 5 of my current layers applied.  However, with five layers, the game does start chugging a bit when you have a lot of these entities spawned.  It's nowhere near as bad as the lag I had with the dynamic textures, but it is noticeable, and I'd like to cut it down if possible (especially since I have a couple more layers I would like to add).  This is my current render code-- is there any way I can do this in a more optimized manner so as to reduce lag?  I highly suspect it's coming from the distance calculations, so I'm wondering if I can do those more efficiently...

 

if (!wcWolf.isInvisible()) {
			PlayerEntity player = Minecraft.getInstance().player;
			float dist = wcWolf.getDistance(player);
			int scalemodifier = 1;
			GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
	        GlStateManager.enableBlend();
			if (wcWolf.showMerle())
			{
				float[] afloat = wcWolf.getEumelaninColor().getColorComponentValues(wcWolf.getEuShade());
		        GlStateManager.color3f(afloat[0], afloat[1], afloat[2]);
				this.bindTexture(this.getResourceLocation(wcWolf, 0));
				this.wolfmodel = new WCWolfModel<WCWolfEntity>(0.001F * scalemodifier * dist);
				this.getEntityModel().setModelAttributes(this.wolfmodel);
		        this.wolfmodel.setLivingAnimations(wcWolf, limbSwing, limbSwingAmount, partialTick);
		        this.wolfmodel.render(wcWolf, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale);
		        scalemodifier ++;
			}
			if (wcWolf.showAgouti())
			{
				float[] afloat = wcWolf.getPhaeomelaninColor().getColorComponentValues(wcWolf.getPhaeShade());
		        GlStateManager.color3f(afloat[0], afloat[1], afloat[2]);
		        this.bindTexture(this.getResourceLocation(wcWolf, 1));
				this.wolfmodel = new WCWolfModel<WCWolfEntity>(0.001F * scalemodifier * dist);
				this.getEntityModel().setModelAttributes(this.wolfmodel);
		        this.wolfmodel.setLivingAnimations(wcWolf, limbSwing, limbSwingAmount, partialTick);
		        this.wolfmodel.render(wcWolf, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale);
		        scalemodifier ++;
			}
			if (wcWolf.showUrajiro())
			{
				float[] afloat = EnumWCFurColor.OFF_WHITE.getColorComponentValues(wcWolf.getPhaeShade());
		        GlStateManager.color3f(afloat[0], afloat[1], afloat[2]);
		        this.bindTexture(this.getResourceLocation(wcWolf, 2));
				this.wolfmodel = new WCWolfModel<WCWolfEntity>(0.001F * scalemodifier * dist);
				this.getEntityModel().setModelAttributes(this.wolfmodel);
		        this.wolfmodel.setLivingAnimations(wcWolf, limbSwing, limbSwingAmount, partialTick);
		        this.wolfmodel.render(wcWolf, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale);
		        scalemodifier ++;
			}
			if (wcWolf.showBrindle())
			{
				float[] afloat = wcWolf.getEumelaninColor().getColorComponentValues(wcWolf.getEuShade());
		        GlStateManager.color3f(afloat[0], afloat[1], afloat[2]);
		        this.bindTexture(this.getResourceLocation(wcWolf, 3));
				this.wolfmodel = new WCWolfModel<WCWolfEntity>(0.001F * scalemodifier * dist);
				this.getEntityModel().setModelAttributes(this.wolfmodel);
		        this.wolfmodel.setLivingAnimations(wcWolf, limbSwing, limbSwingAmount, partialTick);
		        this.wolfmodel.render(wcWolf, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale);
		        scalemodifier ++;
			}
			if (wcWolf.showWhite())
			{
				float[] afloat = EnumWCFurColor.WHITE.getColorComponentValues(0);
		        GlStateManager.color3f(afloat[0], afloat[1], afloat[2]);
		        this.bindTexture(this.getResourceLocation(wcWolf, 4));
				this.wolfmodel = new WCWolfModel<WCWolfEntity>(0.001F * scalemodifier * dist);
				this.getEntityModel().setModelAttributes(this.wolfmodel);
		        this.wolfmodel.setLivingAnimations(wcWolf, limbSwing, limbSwingAmount, partialTick);
		        this.wolfmodel.render(wcWolf, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale);
		        scalemodifier ++;
			}
			GlStateManager.disableBlend();
		}

 

The checks to determine if the layer needs to be shown and the color grabbers shouldn't be the issue.  At least, they worked well when everything was on its own render layer.  When I had it set up like that, the game ran fine even with many entities spawned, but that was when I had the issues with the z fighting.

 

Thank you again for your help!

Edited by JayZX535
Realized I did a couple oopses in my code. This is why we don't code too late at night, folks. Still lagging, but this time it's not laggy AND broken.
Link to comment
Share on other sites

Update: Running some initial framerate diagnostics this morning to get more exact numbers...

 

Singleplayer world (7260MB memory) run through Eclipse:

Normally generated world (none of my entities)- 59-60 FPS, stable

One of my entities- Brief drop to 56, then back to 60 stable

Five of my entities- Brief drop, then back to 60 stable

Twenty-five of my entities- Brief drop, then back to 60 stable

Fifty of my entities- Started dropping with around thirty entities present, stabilized out at 30fps

One hundred of my entities- continued to drop, stabilized out between 10-15fps

 

I ran /debug with 100 of my entities spawned and it said I was getting 20 ticks per second, so definitely render lag.

 

So it does take a decent number of entities spawned for this to be an issue.  However, since this mob is meant to be a pet, it's not infeasible for someone to decide to have that many of them.  Is there any way I can continue to reduce this lag, or is it an inherent issue with having that many alpha layers?

Link to comment
Share on other sites

Hi

The dynamic texture should actually be very quick if you cache the texture (i.e. generate it only once for your entity and then re-use the same one every frame - for example by storing a map of entityID -> texture in the renderer class).  The texture itself is tiny (a few kB) so render memory shouldn't be a problem either.

 

In your second approach, regenerating the wolf models every frame might be slowing it down significantly.  You could test this by generating them once only when the entity is created and then seeing what frame rate you get.  If the model generation is the issue, you could consider having a few different cached wolf sizes, i.e. you have

* five wolf models for a distance <= 1 metre

* five wolf models for a distance of 1 - 4 metres

* five wolf models for a distance of 4 - 20 metres

* five wolf models for > 20 metres

and then you pick the ones for the appropriate distance.

 

If your computer is reasonably powerful I think it should be able to handle that much rendering without such a drastic framerate drop.  The wolf is only made up of a few quads.

 

-TGG

 

 

 

Link to comment
Share on other sites

19 minutes ago, TheGreyGhost said:

Hi

The dynamic texture should actually be very quick if you cache the texture (i.e. generate it only once for your entity and then re-use the same one every frame - for example by storing a map of entityID -> texture in the renderer class).  The texture itself is tiny (a few kB) so render memory shouldn't be a problem either.

 

In your second approach, regenerating the wolf models every frame might be slowing it down significantly.  You could test this by generating them once only when the entity is created and then seeing what frame rate you get.  If the model generation is the issue, you could consider having a few different cached wolf sizes, i.e. you have

* five wolf models for a distance <= 1 metre

* five wolf models for a distance of 1 - 4 metres

* five wolf models for a distance of 4 - 20 metres

* five wolf models for > 20 metres

and then you pick the ones for the appropriate distance.

 

If your computer is reasonably powerful I think it should be able to handle that much rendering without such a drastic framerate drop.  The wolf is only made up of a few quads.

 

-TGG

 

 

 

Ahh, I think I was trying to generate it every frame.  That's likely what the problem was.  I'll check on that approach again and see if I can get it to work better... that may be a lot more effective.

 

Regarding rendering every frame, I highly suspect that's the case.  My computer is pretty powerful, but I want to make the mod as accessible as possible, even for those with less powerful computers.  If mine is struggling, that doesn't bode too well for those with weaker hardware, heh.

 

I'll revisit my attempt at dynamic textures.  It seems like that's probably going to be the best approach... thanks again!

Link to comment
Share on other sites

Okay, so I'm continuing on the attempt at making dynamic textures.  I basically cloned the MapItemRenderer class, and reworked it to take info from one of my mod entities rather than MapData, and to draw a texture based off the entity's traits.  However, now I'm not too sure how to register it.  I see that MapItemRenderer is registered with this line in GameRenderer:

      this.mapItemRenderer = new MapItemRenderer(mcIn.getTextureManager());

I presume that I need to register my texture handler class somewhere on my end as well, but I'm not sure where that should actually go.  It also looks like there are a variety of other methods there that reference it.  Obviously I can't just edit that class, so how should I go about registering that code?  Do I need to create an extension of GameRenderer with my own methods pertaining to my texture generator, and then register that?

 

The only main difference between this and the MapItemRenderer class is that this just generates the textures-- and then, assuming that works, I'll just return the proper ResourceLocation for the actual entity texture from my entity renderer class...

Link to comment
Share on other sites

Hi

Based on my efforts in previous versions, I think using TextureManager.getDynamicTextureLocation() to create a new DynamicTexture is the likely best way.

 

Based on the map renderer, it looks like you could call getDynamicTextureLocation() immediately before rendering your wolf for the first time.

 

i.e.

1) Is this the first call for this wolf pattern?  (If not - use cached texture)

2) call getDynamicTexture() to get a dynamic texture with RL "wolf1" or "wolf2" or whatever

3) modify the texture's NativeImage to your wolf's pattern

4) Bind your renderer to that texture RL, exactly as if it were a static texture eg

 

         IVertexBuilder ivertexbuilder = bufferIn.getBuffer(RenderType.getEntityTranslucent(resourceLocationForThisWolfPattern));

or 

  public ResourceLocation getEntityTexture(WolfEntity wolfEntity) {
return wolfEntity.resourceLocationForThisWolf()
}

 

-TGG

 

 

 

 

 

 

Link to comment
Share on other sites

On 3/19/2020 at 2:24 AM, TheGreyGhost said:

Hi

Based on my efforts in previous versions, I think using TextureManager.getDynamicTextureLocation() to create a new DynamicTexture is the likely best way.

 

Based on the map renderer, it looks like you could call getDynamicTextureLocation() immediately before rendering your wolf for the first time.

 

i.e.

1) Is this the first call for this wolf pattern?  (If not - use cached texture)

2) call getDynamicTexture() to get a dynamic texture with RL "wolf1" or "wolf2" or whatever

3) modify the texture's NativeImage to your wolf's pattern

4) Bind your renderer to that texture RL, exactly as if it were a static texture eg

 


         IVertexBuilder ivertexbuilder = bufferIn.getBuffer(RenderType.getEntityTranslucent(resourceLocationForThisWolfPattern));

or 


  public ResourceLocation getEntityTexture(WolfEntity wolfEntity) {
return wolfEntity.resourceLocationForThisWolf()
}

 

-TGG

 

Hey again,

 

Thank you so much for all your help!  I've got the basics of the dynamic texture working-- I got it to load my base texture file and use it as a dynamic texture.  This reduces lag immensely once the texture is active-- I was able to have 200-300 entities onscreen before my frame rate hit 30fps, so I'd count that pretty reasonable even in the event that someone does want to make a dog army.

 

However, I'm now a bit stuck as to the next step.  For one thing, I need to take a grey-and-white texture (think vanilla sheep's wool) and color it.  For another, I then need to repeat that process several more times, combining the textures as I go into a single BufferedImage.  And I've had very little success on either account.

 

Most of the research I've been doing has been telling me to either use for loops and iterate through the image pixel by pixel, or to use Java's Graphics to create an overlay and color the image that way.  The second method has seemed like the simplest/most intuitive, but I think it may be having some issues when handling an image that's not directly being rendered to the screen (the idea is to create/manipulate the texture as a BufferedImage, and then convert that into a NativeImage which is used for the texture).  The result has always been an invisible entity, which has a shadow but doesn't render onscreen at all.  I could be totally off-base, but I'm thinking maybe Graphics affects specifically what you draw on the screen, and since my BufferedImage isn't actually drawn until it's saved and converted to the texture, it just... isn't working.  That, or I'm just not understanding it right.

 

The for loop method has had marginally more success-- I've gotten it to actually start working (though I couldn't figure out the equation needed to multiply the new color over the base layer so it had a limited effect), but it's extremely slow.  When trying to load in my entity stress test world, I froze for 3-5 minutes while the game ran a multitude of for-loops going pixel by pixel.  This was with a lot of entities, however I only tried to color the first layer-- I haven't even tried adding more.  And as far as I know, the game does continue to tick in the background.  Being frozen for that long would be an actual hazard, since presumably the game continues to run logic even before the screen can catch up.

 

I want to use the most efficient method possible here, though I do greatly prefer having a lag spike once when the entity loads/updates to having one every single time the game tries to render.  However, such a massive spike is definitely not what I'd prefer.  I think I either need a way to tint the texture all at once, or to space out the lag spikes a little at a time.  I'd rather have the texture appear unfinished (I.e. you see the uncolored base coat, then its color, then the uncolored next layer, etc.) for a little while than have the entire screen freeze for a prolonged period of time.  Is there a method I could use to color/combine textures with alpha values that doesn't require me to loop through pixel by pixel?  (It has occurred to me that I might be able to use the same method I use to combine textures to color them-- if I create a layer that's just a solid color and combine it with the alpha-masked base layer).  If I do have to go pixel by pixel, is there a good way I can run the calculations little-by-little in the background as the rest of the game continues to render and only update the texture when each step finishes completion?  Do you have any recommendations as to how I can go about this?

 

Thank you again!

Link to comment
Share on other sites

Hi

I think you could do it both ways; I've previously used OpenGL to render to a bitmap which then gets used as the texture for a second render.  I've also manually iterated through the pixels & calculated manually.

First way is faster, second is easier to code.

 

Because the texture is so small (64 x 32 pixels = 2048 pixels), it really shouldn't take very long to manually iterate through them and calculate each pixel.  Java can do millions or even hundreds of millions of calculations per second.  Perhaps there is a logic flaw in your code?  Could you paste it so we can have a look?  Or a github link?

 

The basic alpha blending equation for an overlay layer is (for each red, blue, green pixel value)

pixel value = base * (1-alpha) + overlay * (alpha)

That gives you the base layer with the first overlay ("base1").

Then you repeat the calculation again for the second layer with its own alpha

pixel value = base1 * (1-alpha2) + overlay2 * (alpha2)

etc

 

There are lots of other equations possible... it all depends what effect you're trying to achieve.

 

-TGG

 

 

Link to comment
Share on other sites

Hey again,

 

That's good to know-- I was probably doing something wrong then.  If the first method is faster, I probably do want to use that one, even if it's a bit harder to learn...

 

Hm.  Well due to some model changes, these wolves actually use a 64x64 texture, so it is double the size of the normal one.  I think the problem may have been that I declared several variables within the for-loop though, now that I think back on it, so it was creating stuff every time.  That combined with the number of entities may have escalated the slow-down.

 

The current code I have for the color overlay method is this:

 

BufferedImage tintedImage = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
Color color = new Color(this.entity.getTextureColor(0)[0], this.entity.getTextureColor(0)[1], this.entity.getTextureColor(0)[2]);
		  
Graphics graphics = tintedImage.createGraphics();
graphics.setXORMode(color);
graphics.drawImage(img, 0, 0, null); // NOT 'tintedImage'
graphics.dispose();
return tintedImage; // Image will be tinted

 

This has been based off recommendations I'm seeing elsewhere on the interwebs, however I think it's been for people drawing directly to the screen in a standalone application, and it doesn't seem to be returning right in my case.  I'm getting what appears to be a completely transparent texture-- the entity exists in the world, but is completely invisible despite not throwing any errors.  I haven't worked with any methods through OpenGL though, so if that's a faster way to go, that seems like it might be optimal.  Do you have an example of that I could take a look at?

 

In essence, what I'm trying to do is take a texture like this

wcwolf_base.png.8b22ba821e11de6cbbbfaa1669a69e4c.png

and apply an RGB value to it as a tint, basically as if you were using the "multiply" layer mode in an image editing program (preserving the texture of the base layer but with the new color).  Then I'll have to take multiple colored layers (some with alpha transparency) and stack them, so that they blend together to make a unique marking pattern.  The end result is something like this (picture from when I was using the old layer system, which had the Z fighting and/or glow issues).

border_collie.png.8b7a2912ebc471f64ac4967ae6f23784.png

You can see how the white markings aren't fully opaque-- they blend into the black ones (which are on their own layer) gradually.  So I need to be able to replicate that layered effect when combining my textures.

 

I'm very new to graphical stuff, but I want to make sure I can have the most efficient solution for this so that people aren't waiting around for textures to load.  If the OpenGL method would be more efficient, then if possible I think I'd like to try and figure that one out.  Otherwise I'll take another crack at that for loop and see if I can get it to go a little faster this time.

 

Thank you for the blending formula, too.  That was one thing that had me really stumped!

Link to comment
Share on other sites

Hi 

 

If your entity is rendering as transparent, it sounds like there is something wrong with your texture.  Perhaps your texture has an alpha channel which you have set to zero?  Or perhaps it is not binding properly.

I would suggest using a breakpoint to inspect your tinted image after doing graphics.drawImage().  It contains an array of int which you can inspect directly, that will show you which part of your code is causing the problem.

I'd also suggest manually generating a test BufferedImage (eg fill it manually with 0xffffffff = opaque white) and seeing whether you get a white wolf when you render with it.

 

I also think that the XOR mode is very unlikely to give you the effect you want; you probably need setComposite() instead.

 

I doubt that the OpenGL method would be much more efficient than your Graphics code approach, and it's a lot more complicated.  Maybe leave that as a last resort...

 

-TGG

 

 

Link to comment
Share on other sites

Thank you again for all your time and help!  I think I'm slowly getting the hang of this...

 

So, I've kept playing around with this and had much more success.  I also figured out what was going on with the for loops: I was having it print out the values of the pixels, which although it's a great debugging tool, apparently I'd severely underestimated its impact on performance.  I'll be removing those troubleshooting println()s for the final version, and without them the lag is much more bearable-- only a few seconds even for a large number of entities.  So that should be a non-issue.

 

You were right in that I was accidentally drawing a fully transparent texture.  I've continued messing around with it and now that's no longer the case!  Graphics does work as well, I was also just using it wrong.  I'm now using setComposite to combine layers, but when it comes to applying color I've had the best luck using for loops to multiply each pixel.  So far that seems to be working almost perfectly.  However, there's one remaining problem.

 

Somehow, somewhere, the colors aren't rendering true.  They're a little-- or in some cases a lot shifted.  And it seems to be on the actual rendering and not the calculations?  For example, I printed the RGB values used to set a pixel color for one dog's base color and got "0.73356414, 0.64879674, 0.5183853".  When inputted into this color calculator, it shows up as a sandy yellow-brown.  However the dog itself...

 

blue.png.58a2cef96d70c4423db330dd3b203d67.png

 

It looks like the color is being assigned properly?  There's a slight bit of deviance between the number I put into the color and the one I get out, but it's a difference of 0.0015 or so at most, which I don't think should be enough to shift the color value that drastically??  So I'm thinking it may have something to do with the color mode of the image and Java shifting colors that don't quite align with what it's seeing?  Do you know how I could get Java to base the image color model around the actual colors I'm putting in?  Or is something else going wrong?  The actual colors coming from my print statements have seemed spot on when I put them into that color site, so this one has me stumped...

 

Code is as follows...

 

//img comes from another method that loads a greyscale texture from a file as a BufferedImage.TYPE_INT_ARGB .  I've also been using BufferedImage.TRANPARENT .  Not sure which is better but neither has fixed my problem.
private BufferedImage applyColor(BufferedImage img, Color tint)
	   {
		   BufferedImage tintedImg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
		   Graphics2D graphics = tintedImg.createGraphics();
		   graphics.drawImage(img, 0, 0, null);
		   graphics.dispose();
		   
		   float redBase = (float) tint.getRed() * (1/255F);
		   float greenBase = (float) tint.getGreen() * (1/255F);
		   float blueBase = (float) tint.getBlue() * (1/255F);
		   
		   for (int i = 0; i < tintedImg.getWidth(); i++)
		   {
			   for (int j = 0; j < tintedImg.getHeight(); j++)
		       {
             	      //I want to keep the alpha value of the source while only changing its color
				   float alpha = (float) tintedImg.getColorModel().getAlpha(tintedImg.getRaster().getDataElements(i, j, null)) * (1/255F);
			       float redTint = (float) tintedImg.getColorModel().getRed(tintedImg.getRaster().getDataElements(i, j, null)) * (1/255F);
			       float greenTint = (float) tintedImg.getColorModel().getGreen(tintedImg.getRaster().getDataElements(i, j, null)) * (1/255F);
			       float blueTint = (float) tintedImg.getColorModel().getBlue(tintedImg.getRaster().getDataElements(i, j, null)) * (1/255F);
			       
			       float redOut = redBase * redTint;
			       float greenOut = greenBase * greenTint;
			       float blueOut = blueBase * blueTint;
			       
			       Color colorOut = new Color (redOut, greenOut, blueOut, alpha);
			       tintedImg.setRGB(i, j, colorOut.getRGB());
		       }
		   }
		   
		   return tintedImg;
	   }

 

Thank you again!

Link to comment
Share on other sites

Hi

Cool sounds like you've nearly cracked it.

 

I think there are really only a couple of ways your brown dog could turn blue... either he's being rendered with a coloured tint (I see something suspicious in vanilla... TintedAgeableModel which appears to be used for the wolf when it is wet), or your texture image isn't right - perhaps the setRGB isn't working quite right.  Lighting can sometimes give things a blue tint but I don't think that's the likely problem in this case.

 

You could test the difference by making your texture deliberately white (or pure red, pure blue, or pure green) and seeing what happens to the rendered colour.

 

You could also set a breakpoint in the WolfRenderer class and trace into the render method to see what it's doing with the quads (i.e. if it's rendering with a tint)

 

-TGG

 

Link to comment
Share on other sites

8 hours ago, TheGreyGhost said:

Hi

Cool sounds like you've nearly cracked it.

 

I think there are really only a couple of ways your brown dog could turn blue... either he's being rendered with a coloured tint (I see something suspicious in vanilla... TintedAgeableModel which appears to be used for the wolf when it is wet), or your texture image isn't right - perhaps the setRGB isn't working quite right.  Lighting can sometimes give things a blue tint but I don't think that's the likely problem in this case.

 

You could test the difference by making your texture deliberately white (or pure red, pure blue, or pure green) and seeing what happens to the rendered colour.

 

You could also set a breakpoint in the WolfRenderer class and trace into the render method to see what it's doing with the quads (i.e. if it's rendering with a tint)

 

-TGG

 

Thank you for all your help!  I tried intentionally setting the texture to a specific color, and it soon became apparent that it wasn't an issue with my color-setting.  Even Java's default colors returned bizarre results.  I couldn't see anything wrong with the in-game tinting, so I did some more research and testing.  And... apparently it was actually an issue with Java reading the colors in as ABGR instead of ARGB.  I'm not sure if this is due to me having missed a Java update or something, but I'm gonna be looking into that.  In any case, it was reading my red value as blue, which is why everything was so bizarrely tinted.  Built a function to swap those around and it seems to work beautifully, from what I've seen thus far.  I'll probably also build a texture correction toggle into my config file, because if it's a java issue then I expect there could be potential problems depending on what's installed on user-end too.

 

Thank you so much for all your time and help.  I think I've finally got this all nailed down?  If not I suppose I'll have to pop in with more questions haha.  But I really appreciate it-- this seems to be a much more lightweight method than Minecraft's layer system, in addition to avoiding the Z fighting and flickering, so that's absolutely fantastic!

Link to comment
Share on other sites

Sorry for the wait!  I didn't use a self-contained project for this unfortunately, but these are the relevant parts of code I used to reference it.  I assign a new texture handler from my ClientProxy, and use that to retrieve the handler to get the instance for my entity.  Hopefully this should still be fairly easy to set up and test?

 

Gist

 

I didn't include the methods from my entity class used to get stuff like texture dimensions, path, etc. but you could easily put something static in instead.  Those methods are just designed to standardize things so that I can more easily add new animals and their genetics in the future.

 

Thank you again for all your help!

 

Also, if you notice anything that I could streamline or that I'm not doing quite right, I'm all ears.  Thanks!

Edited by JayZX535
Link to comment
Share on other sites

5 minutes ago, TheGreyGhost said:

Keen, thanks dude

 

I'll grab it and probably work it into my tutorial project in the next month or so with any luck.  And will give credit to you of course :)

Thank you!  Hopefully what I learned here can benefit others too :)

Link to comment
Share on other sites

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.