refactor Scene
parent
8ea894cd3e
commit
36de714f46
@ -1,85 +1,64 @@
|
|||||||
package eu.jonahbauer.raytracing.scene;
|
package eu.jonahbauer.raytracing.scene;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.Octree;
|
|
||||||
import eu.jonahbauer.raytracing.math.Range;
|
|
||||||
import eu.jonahbauer.raytracing.math.Ray;
|
import eu.jonahbauer.raytracing.math.Ray;
|
||||||
import eu.jonahbauer.raytracing.math.Vec3;
|
import eu.jonahbauer.raytracing.render.Color;
|
||||||
|
import eu.jonahbauer.raytracing.scene.util.HittableCollection;
|
||||||
|
import eu.jonahbauer.raytracing.scene.util.HittableList;
|
||||||
|
import eu.jonahbauer.raytracing.scene.util.HittableOctree;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public final class Scene implements Hittable {
|
public final class Scene extends HittableCollection {
|
||||||
private final @NotNull Octree<@NotNull Hittable> octree;
|
private final @NotNull HittableOctree octree;
|
||||||
private final @NotNull List<@NotNull Hittable> list;
|
private final @NotNull HittableList list;
|
||||||
|
private final @NotNull SkyBox background;
|
||||||
|
|
||||||
public Scene(@NotNull List<? extends @NotNull Hittable> objects) {
|
public Scene(@NotNull List<? extends @NotNull Hittable> objects) {
|
||||||
this.octree = newOctree(objects);
|
this(Color.BLACK, objects);
|
||||||
this.list = new ArrayList<>();
|
|
||||||
|
|
||||||
for (Hittable object : objects) {
|
|
||||||
var bbox = object.getBoundingBox();
|
|
||||||
if (bbox.isPresent()) {
|
|
||||||
octree.add(bbox.get(), object);
|
|
||||||
} else {
|
|
||||||
list.add(object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public Scene(@NotNull Color background, @NotNull List<? extends @NotNull Hittable> objects) {
|
||||||
public @NotNull Optional<HitResult> hit(@NotNull Ray ray, @NotNull Range range) {
|
this(SkyBox.solid(background), objects);
|
||||||
var state = new State();
|
|
||||||
state.range = range;
|
|
||||||
|
|
||||||
octree.hit(ray, object -> hit(state, ray, object));
|
|
||||||
list.forEach(object -> hit(state, ray, object));
|
|
||||||
|
|
||||||
return Optional.ofNullable(state.result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hit(@NotNull State state, @NotNull Ray ray, @NotNull Hittable object) {
|
public Scene(@NotNull SkyBox background, @NotNull List<? extends @NotNull Hittable> objects) {
|
||||||
var r = object.hit(ray, state.range);
|
var bounded = new ArrayList<Hittable>();
|
||||||
if (r.isPresent()) {
|
var unbounded = new ArrayList<Hittable>();
|
||||||
if (state.range.surrounds(r.get().t())){
|
|
||||||
state.result = r.get();
|
objects.forEach(object -> {
|
||||||
state.range = new Range(state.range.min(), state.result.t());
|
if (object.getBoundingBox().isPresent()) {
|
||||||
}
|
bounded.add(object);
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
unbounded.add(object);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
private static @NotNull Octree<Hittable> newOctree(@NotNull List<? extends Hittable> objects) {
|
this.octree = new HittableOctree(bounded);
|
||||||
Vec3 center = Vec3.ZERO, max = Vec3.MIN, min = Vec3.MAX;
|
this.list = new HittableList(unbounded);
|
||||||
|
this.background = background;
|
||||||
|
}
|
||||||
|
|
||||||
int i = 1;
|
public Scene(@NotNull Hittable @NotNull... objects) {
|
||||||
for (Hittable object : objects) {
|
this(List.of(objects));
|
||||||
var bbox = object.getBoundingBox();
|
|
||||||
if (bbox.isPresent()) {
|
|
||||||
center = Vec3.average(center, bbox.get().center(), i++);
|
|
||||||
max = Vec3.max(max, bbox.get().max());
|
|
||||||
min = Vec3.min(min, bbox.get().min());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Scene(@NotNull Color background, @NotNull Hittable @NotNull... objects) {
|
||||||
|
this(background, List.of(objects));
|
||||||
}
|
}
|
||||||
|
|
||||||
var dimension = Arrays.stream(new double[] {
|
public Scene(@NotNull SkyBox background, @NotNull Hittable @NotNull... objects) {
|
||||||
Math.abs(max.x() - center.x()),
|
this(background, List.of(objects));
|
||||||
Math.abs(max.y() - center.y()),
|
}
|
||||||
Math.abs(max.z() - center.z()),
|
|
||||||
Math.abs(min.x() - center.x()),
|
|
||||||
Math.abs(min.y() - center.y()),
|
|
||||||
Math.abs(min.z() - center.z())
|
|
||||||
}).max().orElse(10);
|
|
||||||
|
|
||||||
return new Octree<>(center, dimension);
|
@Override
|
||||||
|
public void hit(@NotNull Ray ray, @NotNull State state) {
|
||||||
|
octree.hit(ray, state);
|
||||||
|
list.hit(ray, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class State {
|
public @NotNull Color getBackgroundColor(@NotNull Ray ray) {
|
||||||
HitResult result;
|
return background.getColor(ray);
|
||||||
Range range;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
package eu.jonahbauer.raytracing.scene;
|
||||||
|
|
||||||
|
import eu.jonahbauer.raytracing.math.Ray;
|
||||||
|
import eu.jonahbauer.raytracing.render.Color;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface SkyBox {
|
||||||
|
@NotNull Color getColor(@NotNull Ray ray);
|
||||||
|
|
||||||
|
static @NotNull SkyBox gradient(@NotNull Color top, @NotNull Color bottom) {
|
||||||
|
return ray -> {
|
||||||
|
// altitude from -pi/2 to pi/2
|
||||||
|
var alt = Math.copySign(
|
||||||
|
Math.acos(ray.direction().withY(0).unit().times(ray.direction().unit())),
|
||||||
|
ray.direction().y()
|
||||||
|
);
|
||||||
|
return Color.lerp(bottom, top, alt / Math.PI + 0.5);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NotNull SkyBox solid(@NotNull Color color) {
|
||||||
|
return _ -> color;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package eu.jonahbauer.raytracing.scene.flat;
|
package eu.jonahbauer.raytracing.scene.hittable2d;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.BoundingBox;
|
import eu.jonahbauer.raytracing.math.BoundingBox;
|
||||||
import eu.jonahbauer.raytracing.math.Vec3;
|
import eu.jonahbauer.raytracing.math.Vec3;
|
@ -1,4 +1,4 @@
|
|||||||
package eu.jonahbauer.raytracing.scene.flat;
|
package eu.jonahbauer.raytracing.scene.hittable2d;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.Range;
|
import eu.jonahbauer.raytracing.math.Range;
|
||||||
import eu.jonahbauer.raytracing.math.Ray;
|
import eu.jonahbauer.raytracing.math.Ray;
|
@ -1,4 +1,4 @@
|
|||||||
package eu.jonahbauer.raytracing.scene.flat;
|
package eu.jonahbauer.raytracing.scene.hittable2d;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.BoundingBox;
|
import eu.jonahbauer.raytracing.math.BoundingBox;
|
||||||
import eu.jonahbauer.raytracing.math.Vec3;
|
import eu.jonahbauer.raytracing.math.Vec3;
|
@ -1,4 +1,4 @@
|
|||||||
package eu.jonahbauer.raytracing.scene.flat;
|
package eu.jonahbauer.raytracing.scene.hittable2d;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.Vec3;
|
import eu.jonahbauer.raytracing.math.Vec3;
|
||||||
import eu.jonahbauer.raytracing.render.material.Material;
|
import eu.jonahbauer.raytracing.render.material.Material;
|
@ -1,4 +1,4 @@
|
|||||||
package eu.jonahbauer.raytracing.scene.flat;
|
package eu.jonahbauer.raytracing.scene.hittable2d;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.BoundingBox;
|
import eu.jonahbauer.raytracing.math.BoundingBox;
|
||||||
import eu.jonahbauer.raytracing.math.Vec3;
|
import eu.jonahbauer.raytracing.math.Vec3;
|
@ -1,10 +1,12 @@
|
|||||||
package eu.jonahbauer.raytracing.scene;
|
package eu.jonahbauer.raytracing.scene.hittable3d;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.render.material.Material;
|
import eu.jonahbauer.raytracing.render.material.Material;
|
||||||
import eu.jonahbauer.raytracing.math.BoundingBox;
|
import eu.jonahbauer.raytracing.math.BoundingBox;
|
||||||
import eu.jonahbauer.raytracing.math.Range;
|
import eu.jonahbauer.raytracing.math.Range;
|
||||||
import eu.jonahbauer.raytracing.math.Ray;
|
import eu.jonahbauer.raytracing.math.Ray;
|
||||||
import eu.jonahbauer.raytracing.math.Vec3;
|
import eu.jonahbauer.raytracing.math.Vec3;
|
||||||
|
import eu.jonahbauer.raytracing.scene.HitResult;
|
||||||
|
import eu.jonahbauer.raytracing.scene.Hittable;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
@ -0,0 +1,65 @@
|
|||||||
|
package eu.jonahbauer.raytracing.scene.util;
|
||||||
|
|
||||||
|
import eu.jonahbauer.raytracing.math.BoundingBox;
|
||||||
|
import eu.jonahbauer.raytracing.math.Range;
|
||||||
|
import eu.jonahbauer.raytracing.math.Ray;
|
||||||
|
import eu.jonahbauer.raytracing.math.Vec3;
|
||||||
|
import eu.jonahbauer.raytracing.scene.HitResult;
|
||||||
|
import eu.jonahbauer.raytracing.scene.Hittable;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public abstract class HittableCollection implements Hittable {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final @NotNull Optional<HitResult> hit(@NotNull Ray ray, @NotNull Range range) {
|
||||||
|
var state = new State(range);
|
||||||
|
hit(ray, state);
|
||||||
|
return state.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void hit(@NotNull Ray ray, @NotNull State state);
|
||||||
|
|
||||||
|
protected static @NotNull Optional<BoundingBox> getBoundingBox(@NotNull Collection<? extends @NotNull Hittable> objects) {
|
||||||
|
var bbox = new BoundingBox(Vec3.ZERO, Vec3.ZERO);
|
||||||
|
for (var object : objects) {
|
||||||
|
var b = object.getBoundingBox();
|
||||||
|
if (b.isPresent()) {
|
||||||
|
bbox = bbox.expand(b.get());
|
||||||
|
} else {
|
||||||
|
bbox = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.ofNullable(bbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static boolean hit(@NotNull State state, @NotNull Ray ray, @NotNull Hittable object) {
|
||||||
|
var r = object.hit(ray, state.range);
|
||||||
|
if (r.isPresent()) {
|
||||||
|
if (state.range.surrounds(r.get().t())){
|
||||||
|
state.result = r.get();
|
||||||
|
state.range = new Range(state.range.min(), state.result.t());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class State {
|
||||||
|
private @NotNull Range range;
|
||||||
|
private HitResult result;
|
||||||
|
|
||||||
|
private State(@NotNull Range range) {
|
||||||
|
this.range = Objects.requireNonNull(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NotNull Optional<HitResult> getResult() {
|
||||||
|
return Optional.ofNullable(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package eu.jonahbauer.raytracing.scene.util;
|
||||||
|
|
||||||
|
import eu.jonahbauer.raytracing.math.BoundingBox;
|
||||||
|
import eu.jonahbauer.raytracing.math.Ray;
|
||||||
|
import eu.jonahbauer.raytracing.scene.Hittable;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public final class HittableList extends HittableCollection {
|
||||||
|
private final @NotNull List<Hittable> objects;
|
||||||
|
private final @NotNull Optional<BoundingBox> bbox;
|
||||||
|
|
||||||
|
public HittableList(@NotNull List<? extends @NotNull Hittable> objects) {
|
||||||
|
this.objects = List.copyOf(objects);
|
||||||
|
this.bbox = getBoundingBox(this.objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HittableList(@NotNull Hittable @NotNull... objects) {
|
||||||
|
this(List.of(objects));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hit(@NotNull Ray ray, @NotNull State state) {
|
||||||
|
objects.forEach(object -> hit(state, ray, object));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Optional<BoundingBox> getBoundingBox() {
|
||||||
|
return bbox;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package eu.jonahbauer.raytracing.scene.util;
|
||||||
|
|
||||||
|
import eu.jonahbauer.raytracing.math.*;
|
||||||
|
import eu.jonahbauer.raytracing.scene.Hittable;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public final class HittableOctree extends HittableCollection {
|
||||||
|
private final @NotNull Octree<Hittable> objects;
|
||||||
|
private final @NotNull Optional<BoundingBox> bbox;
|
||||||
|
|
||||||
|
public HittableOctree(@NotNull List<? extends @NotNull Hittable> objects) {
|
||||||
|
var result = newOctree(objects);
|
||||||
|
this.objects = result.getKey();
|
||||||
|
this.bbox = Optional.of(result.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public HittableOctree(@NotNull Hittable @NotNull... objects) {
|
||||||
|
this(List.of(objects));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hit(@NotNull Ray ray, @NotNull State state) {
|
||||||
|
objects.hit(ray, object -> hit(state, ray, object));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Optional<BoundingBox> getBoundingBox() {
|
||||||
|
return bbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull Entry<@NotNull Octree<Hittable>, @NotNull BoundingBox> newOctree(@NotNull List<? extends Hittable> objects) {
|
||||||
|
Vec3 center = Vec3.ZERO, max = Vec3.MIN, min = Vec3.MAX;
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
for (var object : objects) {
|
||||||
|
var bbox = object.getBoundingBox().orElseThrow();
|
||||||
|
center = Vec3.average(center, bbox.center(), i++);
|
||||||
|
max = Vec3.max(max, bbox.max());
|
||||||
|
min = Vec3.min(min, bbox.min());
|
||||||
|
}
|
||||||
|
|
||||||
|
var dimension = Arrays.stream(new double[] {
|
||||||
|
Math.abs(max.x() - center.x()),
|
||||||
|
Math.abs(max.y() - center.y()),
|
||||||
|
Math.abs(max.z() - center.z()),
|
||||||
|
Math.abs(min.x() - center.x()),
|
||||||
|
Math.abs(min.y() - center.y()),
|
||||||
|
Math.abs(min.z() - center.z())
|
||||||
|
}).max().orElse(10);
|
||||||
|
|
||||||
|
var out = new Octree<Hittable>(center, dimension);
|
||||||
|
objects.forEach(object -> out.add(object.getBoundingBox().get(), object));
|
||||||
|
return Map.entry(out, new BoundingBox(min, max));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue