Skip to content

Cadiboo/NoCubes

Repository files navigation

Build Curseforge Downloads Curseforge Versions Modrinth Downloads Discord
A mod for 1.12.2+ by Cadiboo that creates smooth terrain in Minecraft.
Website
CurseForge Page
NoCubes

Note: NoCubes is not feature complete and is still very much in development.

How (technical)

See also the project's TODO list, Trello board and TV Show

Visuals

This feature works regardless of if the server has the mod installed or not.

Here's how Minecraft's rendering system works:

  • Minecraft renders 16x16x16 block sized ‘render chunks’ of your world
  • Building the visual data for the entire chunk each frame would be inefficient so the visual data is built once when the chunk is loaded and only rebuilt when it changes (i.e. a block in the chunk changes). The render data for each visible chunk is drawn to the screen every frame (but not recalculated each frame)
  • To build this render data Minecraft’s engine allocates some memory for this render data and then goes through (iterates) every block in the 16x16x16 area, gets the model for the block (which contains all the faces and textures to render), calculates light for the block, calculates colouring for the block and then puts this data for each face into the chunk render data it is building

Here's where NoCubes comes in:

Collisions

This feature only works when both the client and server have NoCubes installed.

NoCubes changes Minecraft's terrain collisions to be smooth too (i.e. you can walk on the smooth terrain, rather than on the original cubes).

Here's how Minecraft's rendering system works:

  • Whenever you walk on terrain, Minecraft looks at the nearby blocks
  • For each block the engine gets its collision shape
  • If the player is inside the shape, it pushes the player out

NoCubes changes the shape that the collision system sees for each block:

  • For each nearby block, NoCubes redirects Minecraft's collision system's request for the block's collision shape to NoCubes' collision code
  • NoCubes' collision code then runs the mesh generator (Surface Nets by default) on that block and the surrounding blocks to get a list of mesh faces
  • It then turns each one of these mesh faces into shapes and gives them back to Minecraft's collision engine

This approach is wasteful because the mesh generator is being called for the same area multiple times rather than once for the whole nearby area. Work is being done to fix this.

Meshing

Reading the above you might've started wondering "What is a mesh generator?". Mesh generators (aka isosurface extractors) are the underlying technology that makes NoCubes work. A mesh generator is an algorithm (NoCubes supports multiple different ones) that takes a field (aka area) of voxels (aka cubes/blocks) and turns them into faces (quads/squares or triangles). Here are some links:

Isosurface extraction isn't the only way of procedurally generating a mesh though, Wave Function Collapse is another very interesting approach that gives artists much more control over the final product:

Setting up a development environment (using IntelliJ)

  1. Clone/download this repo
  2. Open NoCubes/build.gradle in IntelliJ and select 'Open as Gradle Project'
  3. Go to the gradle sidebar on the right and click the 🔄 "Reload All Gradle Projects" icon
  4. Go to the gradle sidebar on the right and select "🐘 NoCubes > Tasks > forgegradle runs > genIntellijRuns" and double click it to run it (may have to rerun it multiple times, it often fails to download assets)
  5. Go to the run configurations dropdown in the right of the top 'sidebar' and select 'runClient'
  6. Clicking the 🕷 "Debug 'runClient'" button will start Minecraft with NoCubes running

OptiFine compatibility

OptiFine is a bit hard to work with
To get it running in your development environment:

  1. Download the OptiFine version that you want from optifine.net
  2. Download OptiFine dev tweaker from GitHub
  3. Put both jar files into NoCubes/run/mods/
  4. Run the game and OptiFine & Shaders will load

To be able to also compile against OptiFine (do the above steps first):

  1. You must have set up NoCubes and had gradle install and deobfuscate Minecraft first
  2. Make sure that you have Minecraft for the version you're running NoCubes on
    If you don't, just open the minecraft launcher, select the right version and hit 'Play'. The launcher will download everything and you can just quit the game when it starts. To find the version of Minecraft that NoCubes is being built against look in gradle.properties.
  3. Download OptiFineDeobf from GitHub
  4. Run the OptiFine jar in NoCubes/run/mods/ and select 'Extract', put the resulting extracted jar somewhere outside the mods folder
  5. Run OptiFineDeobf, select the extracted OptiFine jar that you just created and select the NoCubes folder for the project folder
  6. Select a mappings file that maps from SRG to MCP/official (don't use obf -> MCP mappings)
  7. Select 'Make Public' and 'Forge Dev jar' then click 'Deobf'
  8. Put the deobfuscated OptiFine jar into NoCubes/libs/
  9. Refresh gradle in your IDE and you should be able to compile against OptiFine's classes (and running OptiFine still works as it did before)

Note: You can replace the normal OptiFine jar with the extracted (non-deobfuscated) one in NoCubes/run/mods/ (this may speed up OptiFine's loading time)

NoCubes' OptiFine compatibility should not be shipped to production compiled directly against OptiFine's classes because it can crash if OptiFine isn't present. Instead, the code should be converted to Reflection.
Using Reflection has the added benefit of allowing the same NoCubes jar to support multiple versions of OptiFine.

Other OptiFine info (just putting this here so I don't forget and can come back to it at some point)

It's possible to get Forge to do most of the work by adding the following code to build.gradle.

repositories {
  flatDir {
    dirs 'run/mods'
  }
}
dependencies {
  // OptiFine is the first dependency because so that we can compile against its version of vanilla's classes, not Forge's
  // Needs a group classifier even though it's not used, this can be anything, I used 'undefined'
  compileOnly fg.deobf('undefined:OptiFine_1.16.5_HD_U_G8_pre12_MOD.jar:')
  
  //... other dependencies like Forge
}

However, this doesn't fully work because Forge doesn't apply my ATs to OptiFine's classes :(
It's probably possible to run AccessTransformers ourselves against the deobfed OptiFine dependency, but I can't figure out how