diff --git a/chunky/src/java/se/llbit/chunky/block/minecraft/Air.java b/chunky/src/java/se/llbit/chunky/block/minecraft/Air.java index a0e291ed87..402886da48 100644 --- a/chunky/src/java/se/llbit/chunky/block/minecraft/Air.java +++ b/chunky/src/java/se/llbit/chunky/block/minecraft/Air.java @@ -29,5 +29,6 @@ private Air() { solid = false; opaque = false; invisible = true; + refractive = true; } } diff --git a/chunky/src/java/se/llbit/chunky/model/AnimatedQuadModel.java b/chunky/src/java/se/llbit/chunky/model/AnimatedQuadModel.java index 48946e75cf..f361026984 100644 --- a/chunky/src/java/se/llbit/chunky/model/AnimatedQuadModel.java +++ b/chunky/src/java/se/llbit/chunky/model/AnimatedQuadModel.java @@ -3,6 +3,7 @@ import se.llbit.chunky.plugin.PluginApi; import se.llbit.chunky.renderer.scene.Scene; import se.llbit.chunky.resources.AnimatedTexture; +import se.llbit.chunky.world.Material; import se.llbit.math.Quad; import se.llbit.math.Ray; import se.llbit.math.Vector3; @@ -42,7 +43,7 @@ public boolean intersect(Ray ray, Scene scene) { Quad[] quads = getQuads(); AnimatedTexture[] textures = getTextures(); Tint[] tintedQuads = getTints(); - + Material mat = ray.getCurrentMaterial(); // The animation frame to use int j = (int) (scene.getAnimationTime() * animationMode.framerate); if (animationMode.positional) { @@ -58,11 +59,11 @@ public boolean intersect(Ray ray, Scene scene) { Quad quad = quads[i]; if (quad.intersect(ray)) { float[] c = textures[i].getColor(ray.u, ray.v, j); - if (c[3] > Ray.EPSILON) { + if (c[3] > Ray.EPSILON || mat.refractive) { tint = tintedQuads == null ? Tint.NONE : tintedQuads[i]; color = c; ray.t = ray.tNext; - if (quad.doubleSided) + if (quad.doubleSided || mat.refractive) ray.orientNormal(quad.n); else ray.setNormal(quad.n); diff --git a/chunky/src/java/se/llbit/chunky/model/QuadModel.java b/chunky/src/java/se/llbit/chunky/model/QuadModel.java index 31b070b142..f57e721da0 100644 --- a/chunky/src/java/se/llbit/chunky/model/QuadModel.java +++ b/chunky/src/java/se/llbit/chunky/model/QuadModel.java @@ -23,6 +23,7 @@ import se.llbit.chunky.plugin.PluginApi; import se.llbit.chunky.renderer.scene.Scene; import se.llbit.chunky.resources.Texture; +import se.llbit.chunky.world.Material; import se.llbit.math.Quad; import se.llbit.math.Ray; import se.llbit.math.Vector3; @@ -112,6 +113,7 @@ public boolean intersect(Ray ray, Scene scene) { Quad[] quads = getQuads(); Texture[] textures = getTextures(); Tint[] tintedQuads = getTints(); + Material mat = ray.getCurrentMaterial(); float[] color = null; Tint tint = Tint.NONE; @@ -119,11 +121,11 @@ public boolean intersect(Ray ray, Scene scene) { Quad quad = quads[i]; if (quad.intersect(ray)) { float[] c = textures[i].getColor(ray.u, ray.v); - if (c[3] > Ray.EPSILON) { + if (c[3] > Ray.EPSILON || mat.refractive) { tint = tintedQuads == null ? Tint.NONE : tintedQuads[i]; color = c; ray.t = ray.tNext; - if (quad.doubleSided) + if (quad.doubleSided || mat.refractive) ray.orientNormal(quad.n); else ray.setNormal(quad.n); diff --git a/chunky/src/java/se/llbit/chunky/renderer/scene/Scene.java b/chunky/src/java/se/llbit/chunky/renderer/scene/Scene.java index fb4f8adcf4..53312171ab 100644 --- a/chunky/src/java/se/llbit/chunky/renderer/scene/Scene.java +++ b/chunky/src/java/se/llbit/chunky/renderer/scene/Scene.java @@ -360,8 +360,8 @@ public Scene() { branchCount = PersistentSettings.getBranchCountDefault(); palette = new BlockPalette(); - worldOctree = new Octree(octreeImplementation, 1); - waterOctree = new Octree(octreeImplementation, 1); + worldOctree = new Octree(octreeImplementation, 1, Octree.OctreeType.WORLD); + waterOctree = new Octree(octreeImplementation, 1, Octree.OctreeType.WATER); emitterGrid = null; } @@ -744,7 +744,7 @@ private boolean worldIntersection(Ray ray) { } else { r = new Ray(start); r.setCurrentMaterial(start.getPrevMaterial(), start.getPrevData()); - if (waterOctree.enterBlock(this, r, palette) && r.distance < ray.t) { + if (waterOctree.enterBlock(this, r, palette) && r.distance < ray.t + Ray.EPSILON) { ray.t = r.distance; ray.setNormal(r.getNormal()); ray.color.set(r.color); @@ -824,8 +824,8 @@ public synchronized void loadChunks(TaskTracker taskTracker, World world, Collec // Create new octree to fit all chunks. palette = new BlockPalette(); - worldOctree = new Octree(octreeImplementation, requiredDepth); - waterOctree = new Octree(octreeImplementation, requiredDepth); + worldOctree = new Octree(octreeImplementation, requiredDepth, Octree.OctreeType.WORLD); + waterOctree = new Octree(octreeImplementation, requiredDepth, Octree.OctreeType.WATER); grassTexture = biomeStructureFactory.create(); foliageTexture = biomeStructureFactory.create(); @@ -2227,6 +2227,12 @@ private synchronized boolean loadOctree(SceneIOProvider context, TaskTracker tas // this dump is so old that it doesn't contain a water texture (Chunky 2.3.0, #691) waterTexture = BiomeStructure.get(this.biomeStructureImplementation).create(); } + if (worldOctree.type == null) { + worldOctree.type = Octree.OctreeType.WORLD; + } + if (waterOctree.type == null) { + waterOctree.type = Octree.OctreeType.WATER; + } palette = data.palette; palette.applyMaterials(); Log.info("Octree loaded"); diff --git a/chunky/src/java/se/llbit/math/Octree.java b/chunky/src/java/se/llbit/math/Octree.java index 201bdde496..97159093ea 100644 --- a/chunky/src/java/se/llbit/math/Octree.java +++ b/chunky/src/java/se/llbit/math/Octree.java @@ -244,11 +244,26 @@ public int getData() { } } + /** The type of Octree, used to adjust intersection code based on contents*/ + public enum OctreeType { + /** + * Contains all world geometry excluding water, water is removed from water logged blocks + * and full water blocks are replaced with air + */ + WORLD, + /** + * Contains all the water geometry only, other blocks are air + */ + WATER + } + /** * Timestamp of last serialization. */ private long timestamp = 0; + public OctreeType type; + private OctreeImplementation implementation; /** @@ -257,9 +272,10 @@ public int getData() { * * @param octreeDepth The number of levels in the Octree. */ - public Octree(String impl, int octreeDepth) { + public Octree(String impl, int octreeDepth, OctreeType type) { Log.infof("Building new octree (%s)", impl); implementation = getImplementationFactory(impl).create(octreeDepth); + this.type = type; } protected Octree(OctreeImplementation impl) { @@ -484,6 +500,8 @@ public boolean enterBlock(Scene scene, Ray ray, BlockPalette palette) { int depth = implementation.getDepth(); double distance = 0; + //tread air as a null block depending on octreetype and were we are in the trace + boolean includeAir = this.type == OctreeType.WORLD && !ray.getPrevMaterial().isWater(); // floating point division are slower than multiplication so we cache them // We also try to limit the number of time the ray origin is updated @@ -539,9 +557,14 @@ public boolean enterBlock(Scene scene, Ray ray, BlockPalette palette) { ray.distance += distance; distance = 0; if (currentBlock.intersect(ray, scene)) { - if (prevBlock != currentBlock) + if (prevBlock != currentBlock) { //|| (currentBlock.refractive)) { + //for things like glass panes coerce the current mat to be air or water + // TODO testing + if (currentBlock.refractive) { + //ray.setCurrentMaterial(currentBlock.waterlogged ? Water.INSTANCE : Air.INSTANCE); + } return true; - + } ray.o.scaleAdd(Ray.OFFSET, ray.d); offsetX = -ray.o.x * invDx; offsetY = -ray.o.y * invDy; @@ -556,7 +579,7 @@ public boolean enterBlock(Scene scene, Ray ray, BlockPalette palette) { offsetZ = -ray.o.z * invDz; continue; } - } else if (!currentBlock.isSameMaterial(prevBlock) && currentBlock != Air.INSTANCE) { + } else if (!currentBlock.isSameMaterial(prevBlock) && (currentBlock != Air.INSTANCE || includeAir)) { // Origin and distance of ray need to be updated ray.o.scaleAdd(distance, ray.d); ray.distance += distance; diff --git a/chunky/src/java/se/llbit/math/Quad.java b/chunky/src/java/se/llbit/math/Quad.java index 336ed7a489..74c22be8ce 100644 --- a/chunky/src/java/se/llbit/math/Quad.java +++ b/chunky/src/java/se/llbit/math/Quad.java @@ -158,14 +158,14 @@ public void sample(Vector3 loc, Random rand) { */ public boolean intersect(Ray ray) { double u, v; - + Material mat = ray.getCurrentMaterial(); double ix = ray.o.x - QuickMath.floor(ray.o.x + ray.d.x * Ray.OFFSET); double iy = ray.o.y - QuickMath.floor(ray.o.y + ray.d.y * Ray.OFFSET); double iz = ray.o.z - QuickMath.floor(ray.o.z + ray.d.z * Ray.OFFSET); // Test that the ray is heading toward the plane of this quad. double denom = ray.d.dot(n); - if (denom < -Ray.EPSILON || (doubleSided && denom > Ray.EPSILON)) { + if (denom < -Ray.EPSILON || ((doubleSided || mat.refractive) && denom > Ray.EPSILON)) { // Test for intersection with the plane at origin. double t = -(ix * n.x + iy * n.y + iz * n.z + d) / denom;