Jump to content

Orthographic and Isometric projection


AntiRix

Recommended Posts

Hi,

 

I'm trying to create a mod which is able to create both 2D and 3D tiled maps of a minecraft world by rendering the actual blocks. I believe the best approach would be to mod the camera to have orthographic and isometric projection, have it take a screenshot at position 1, then teleport to position 2 and take another screenshot, etc, creating overlapping images which I'll somehow have to stitch together and then divide into tiles.

 

So what I'd like to know is:

 

• Is that the best approach or is there a better way?
• How can I alter the camera's projection? I can't seem to find anything relating to perspective in the player object.

Edited by AntiRix
Link to comment
Share on other sites

You may want to look at what happens when you change Minecraft.currentViewEntity (or similarly named) and what happens when you go into either of the 3rd person views.

The way I would do this would be to use RenderChunk.rebuildChunk to generate the render of the terrain, then use reflection to access the BufferBuilder instances that it writes to and draw what you need to draw with that data.

Edited by Cadiboo

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

Ok, I've found what you're referring to:

 

for (; this.gameSettings.keyBindTogglePerspective.isPressed(); this.renderGlobal.setDisplayListEntitiesDirty())
{
	++this.gameSettings.thirdPersonView;

	if (this.gameSettings.thirdPersonView > 2)
	{
		this.gameSettings.thirdPersonView = 0;
	}

	if (this.gameSettings.thirdPersonView == 0)
	{
		this.entityRenderer.loadEntityShader(this.getRenderViewEntity());
	}
	else if (this.gameSettings.thirdPersonView == 1)
	{
		this.entityRenderer.loadEntityShader((Entity)null);
	}
}

 

if (block.getDefaultState().getRenderType() != EnumBlockRenderType.INVISIBLE)
{
	BufferBuilder bufferbuilder = generator.getRegionRenderCacheBuilder().getWorldRendererByLayerId(j);

	if (!compiledchunk.isLayerStarted(blockrenderlayer1))
	{
		compiledchunk.setLayerStarted(blockrenderlayer1);
		this.preRenderBlocks(bufferbuilder, blockpos);
	}

	aboolean[j] |= blockrendererdispatcher.renderBlock(iblockstate, blockpos$mutableblockpos, this.region, bufferbuilder);
}

 

loadEntityShader doesn't mean anything - it's just loading shaders. I can't see where I'm meant to be going with that.

 

I assume if modifying the client directly, that I'd need to insert code before the line aboolean[j].... One thing I don't understand though - where am I meant to get the BufferBuilder from? I can't steal variables from inside a method.

Link to comment
Share on other sites

I meant step through what happens when the camera’s viewpoint changes.

 

35 minutes ago, AntiRix said:

One thing I don't understand though - where am I meant to get the BufferBuilder from? I can't steal variables from inside a method.

You could make your own simpler implementation of RenderChunk/rebuildChunk that only deals with what you want or you could instantiate a new RenderChunk with the chunk you want to render, call rebuildChunk (and other necessary methods) to rebuild the chunk, and then get the data (rendered blocks) from the BufferBuilders stored in this.

33 minutes ago, AntiRix said:

generator.getRegionRenderCacheBuilder().getWorldRendererByLayerId(j);

You might not even need reflection to acces the data.

I would do it this way because I already have a bit of knowledge and experience on how chunks are rendered. Another way might work better for you

Edited by Cadiboo

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

Because I'm completely flying blind with this, this is the best guess I can come up with:

 

RenderChunk r = new RenderChunk(mc.world, mc.renderGlobal, 0);
ChunkCompileTaskGenerator generator = new ChunkCompileTaskGenerator(r, Type.REBUILD_CHUNK, 10.0);
r.rebuildChunk(mc.player.chunkCoordX, mc.player.chunkCoordY, mc.player.chunkCoordZ, generator);

BufferBuilder builder = generator.getRegionRenderCacheBuilder().getWorldRendererByLayerId(0);

 

As guaranteed and expected, this crashes the game.

 

The index property of RenderChunk doesn't appear to do anything. I assume I just have to have the same value for getWorldRendererByLayerId().

 

I can't trace the meaning of the 3rd double parameter of ChunkCompileTaskGenerator() either, so I've just used a random double value. I have no clue what you'd need a distance to render a chunk for.

 

The code is completely uncommented and so convoluted that it's impossible to decipher what anything's doing. It's so frustrating and I've been trying for hours. There is zero help on the internet for anything to do with rendering to a file.

Aside from that, are you sure I'm able to achieve what I want with this method? I'm trying to create the following, including lighting.

 

Minecraft-village-modeling-cinema4d-render-isometric.jpg

Edited by AntiRix
Link to comment
Share on other sites

8 minutes ago, AntiRix said:

Yes, with isometric projection

I'm not sure whether Minecraft uses either of those, you may want to look for where minecraft sets up OpenGL specifically the window and see which it uses. 

10 minutes ago, AntiRix said:

Yes

Then basically you are just going to want to render the specific chunks on a different Fram Buffer Object there was a thread on here earlier about taking a screenshot of a specific entity and I believe it was V0idwa1k3r who gave an excellent description and example on how to do this. 

14 minutes ago, AntiRix said:

where lighting and shadows are preserved

Lighting in minecraft doesn't work the same way it does in the picture without custom shaders. However with the method above it should preserve the lighting as displayed.

15 minutes ago, AntiRix said:

stitched together afterwards with no apparent joins.

This will be hard, however I admire your bravery.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

4 hours ago, Animefan8888 said:

This will be hard

If the OP renders the things chunk-by-chunk and then simply joins the render output together then it won't be. However the shadows will not work correctly, this needs a two pass drawing with drawing the shadows in the second pass.

 

6 hours ago, AntiRix said:

I'm trying to create the following, including lighting.

You will need a custom shader then. Minecraft doesn't have lightsources that cast shadows.

 

9 hours ago, AntiRix said:

orthographic and isometric projection

Since minecraft uses outdated opengl (1.x) for stuff the camera isn't just a collection of uniforms passed to a shader but instead GL api calls that set up the projection matrix. You can see the game setting up it's projection in EntityRenderer#setupCameraTransform. Specifically it uses the Project class to manipulate the matrix. You might be able to do something similar. Or just change the matrix with basic opengl calls, that should work too.

 

Also relevant:

Topic where I described how to render to a framebuffer:

 

Link to comment
Share on other sites

2 minutes ago, V0idWa1k3r said:

If the OP renders the things chunk-by-chunk and then simply joins the render output together then it won't be. However the shadows will not work correctly, this needs a two pass drawing with drawing the shadows in the second pass.

The way I read what the OP said was basically take multiple images and stitch them together not just rendering all the chunks they want then putting that into an image.

 

IE if you take a picture from a camera at one position then another from a different one and combining it to make it look like one larger picture.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

I still need desperate help with this, if anyone's watching this thread.

I've combined a hook by Cadiboo with the OpenGL code by V0idWa1k3r to create this:

 

	@SubscribeEvent
	public void onPostRebuildChunk(final RebuildChunkPostEvent event)
	{
		//BufferBuilder buffer_builder = event.getGenerator().getRegionRenderCacheBuilder().getWorldRendererByLayer(BlockRenderLayer.SOLID);
		
		// Define the width and the height of the framebuffer, the texture and as a result the final png file.
		int width = 256;
		int height = 256;

		// Get the framebuffer object that was already in use since we have to restore the state when we are done
		Framebuffer fbo = Minecraft.getMinecraft().getFramebuffer();

//		// Create a new framebuffer object with the width and the height defined. The last parameter defines whether to use depth or not.
//		Framebuffer framebuffer = new Framebuffer(width, height, true);
//
//		// Bind the created framebuffer as the active framebuffer. The last parameter also adjusts the viewport to the dimensions of our framebuffer.
//		framebuffer.bindFramebuffer(true);
//
//		// These are not really needed, however I prefer do draw over black. By default the texture would be white.
//		GlStateManager.clearColor(0, 0, 0, 1);
//
//		// No need to clear depth/stencil since those are clean as is since nothing has been drawn yet.
//		GlStateManager.clear(GL11.GL_COLOR_BUFFER_BIT);
//
//		GlStateManager.pushMatrix();
//		
//		// MAKE ISOMETRIC HERE
//		
//		// Draw the actual entity. You might want to play with positions and scaling. 
//		//GuiInventory.drawEntityOnScreen(200, 200, 100, 0, 0, new EntitySheep(Minecraft.getMinecraft().world));
//		Tessellator.getInstance().draw(); // randomly guessing how to get the BufferBuilder to draw to the screen here
//		GlStateManager.popMatrix();
//		
//		// Allocate a buffer for GL to dump pixels into.
//		IntBuffer pixels = BufferUtils.createIntBuffer(width * height);
//
//		// Bind the framebuffer's texture.
//		GlStateManager.bindTexture(framebuffer.framebufferTexture);
//
//		// Dump the pixels onto the IntBuffer. Note that the pixel format is BGRA and the pixel type is 8 bits per color.
//		GlStateManager.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
//
//		// Allocate the array to hold pixel values.
//		int[] vals = new int[width * height];
//
//		// Copy the buffer to the array.
//		pixels.get(vals);
//
//		// Rearrange pixel values to correct positions so they can be read by the BufferedImage correctly.
//		TextureUtil.processPixelValues(vals, width, height);
//
//		// Create the BufferedImage object.
//		BufferedImage bufferedimage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//
//		// Copy the pixels from the array onto the BufferedImage.
//		bufferedimage.setRGB(0, 0, width, height, vals, 0, width);
//
//		// Create a file to store the image within. Here the file will be outputted to the game's base directory with a name of img.png.
//		File f = new File("C:\\Users\\Ricky\\Desktop\\CHUNKS.png");
//		
//		try {
//			f.createNewFile();
//			
//			// Finally write the buffered image into the file.
//			ImageIO.write(bufferedimage, "png", f);
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//		
//		// Delete the framebuffer from memory. It is no longer needed.
//		framebuffer.deleteFramebuffer();
//
//		// If the game had a buffer bound. In most cases it did but who knows what could be the case with mods and such.
//		if (fbo != null)
//		{
//		    // Restore the original framebuffer. The parameter set to true also restores the viewport.
//		    fbo.bindFramebuffer(true);
//		}
//		else
//		{
//		    // If the game didn't have a framebuffer bound we need to restore the default one. It's ID is always 0.
//		    GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
//		    
//		    // We also need to restore the viewport back in this case. 
//		    GlStateManager.viewport(0, 0, Minecraft.getMinecraft().displayWidth, Minecraft.getMinecraft().displayHeight);
//		}
	}

 

But it's crashing when instantiating a FrameBuffer, saying "No OpenGL context found in the current thread".

Edited by AntiRix
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.