diff options
Diffstat (limited to 'src/Scene.scala')
-rw-r--r-- | src/Scene.scala | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/src/Scene.scala b/src/Scene.scala new file mode 100644 index 0000000..9b59f7b --- /dev/null +++ b/src/Scene.scala @@ -0,0 +1,152 @@ +package net.iximeow.raytrace + +import Objects._ + +import java.awt.image.BufferedImage +import javax.imageio._ +import java.io.File + +case class Scene(walls: Seq[Segment]) { + val buffer = new BufferedImage(800, 600, BufferedImage.TYPE_INT_RGB) + + def render(scale: Double = 1, xoff: Int = 0, yoff: Int = 0, color: Int = 0x808000, normals: Boolean = false): Unit = { + for (wall <- walls) { + wall.renderTo(buffer, scale, 400, 300, color = color) + } + for (wall <- walls) { + wall.normal.renderTo(buffer, scale, 400, 300, color = 0xc00000) + } + } + + def save(path: String = "render.png"): Unit = { + ImageIO.write(buffer, "png", new File(path)) + } + + def cast(r: Ray, steps: Int): Seq[Segment] = { + (0 until steps).foldLeft(Seq.empty[Segment] -> r) { case (p: (Seq[Segment], Ray), i: Int) => { + val (prevRay, nextRay) = castSingle(p._2) + ((p._1 :+ prevRay.toSegment) -> nextRay): (Seq[Segment], Ray) + }}._1 + } + + def castSingle(r: Ray): (Ray, Ray) = { + val asSeg = r.toSegment + + def reflect(firstIntersection: (Segment, Point)): (Ray, Ray) = { + val minAngle = { + val fromStart = Raymath.angleBetween( + r.initial, + firstIntersection._2, + firstIntersection._1.at(0) + ) + val fromEnd = Raymath.angleBetween( + r.initial, + firstIntersection._2, + firstIntersection._1.at(1) + ) + + println("Fromstart: " + Raymath.toDegrees(fromStart)) + println("Fromend: " + Raymath.toDegrees(fromEnd)) + + if (Math.abs(fromStart) < Math.PI / 2) { + fromStart + } else { + fromEnd + } + + fromStart + } + + val maxAngle = Math.PI - minAngle + + val baseAngle = Math.atan2(firstIntersection._1.y, firstIntersection._1.x) + println("base angle: " + Raymath.toDegrees(baseAngle)) + + val reflectedAngle = baseAngle + minAngle + + if (minAngle < 0 || minAngle > Math.PI * 2) { + println("lol") + (r.endingAt(firstIntersection._2), r.endingAt(firstIntersection._2)) //Ray(0, 0, firstIntersection._2)) + } else { + val (x, y) = ( + Math.cos(reflectedAngle) * 3, + Math.sin(reflectedAngle) * 3 + ) + + // Sure hope this is right... + (r.endingAt(firstIntersection._2), Ray(x, y, firstIntersection._2)) + } + } + val intersections: Seq[(Segment, Point)] = walls.flatMap(w => { + w.intersectChecked(asSeg) + .map(x => (w, x)) + }) + .filter { case (w: Segment, x: Point) => asSeg.tFor(x).map(_ > 0.0000001).getOrElse(false) } + + def isBehind(start: Segment, wall: Segment): Boolean = { + val normal = Ray(-wall.y, wall.x, Point(0, 0)) + val rebased = Ray(start.x, start.y, Point(0, 0)) + val cosAngle = normal.dot(rebased) / (normal.mag * rebased.mag) + cosAngle > 0 + } + + val continuedIntersections = intersections + .filter(i => { + val otherT = i._1.tFor(i._2) + otherT.map(t => t >= 0 && t <= 1).getOrElse(true) + }) + + val stoppedIntersections = intersections + .filter(i => { + val otherT = i._1.tFor(i._2) + otherT.map(t => t >= 0 && t <= 1 && isBehind(asSeg, i._1)).getOrElse(false) + }) + + def fnMin(x: (Segment, Point), y: (Segment, Point)) = if (asSeg.tFor(x._2).get < asSeg.tFor(y._2).get) x else y + val firstStop: Option[(Segment, Point)] = stoppedIntersections.reduceOption(fnMin(_, _)) + val firstReflect: Option[(Segment, Point)] = continuedIntersections.reduceOption(fnMin(_, _)) + + (firstStop, firstReflect) match { + case (None, None) => + (r, Ray(r.x, r.y, r.toSegment.at(1))) + case (Some(stop), None) => + (r.endingAt(stop._2), Ray(0, 0, r.initial)) + case (None, Some(cont)) => reflect(cont)/* reflect */ + case (Some(stop), Some(cont)) => { + if (fnMin(stop, cont) == stop) { + (r.endingAt(stop._2), Ray(0, 0, r.initial)) + // stop + } else { + reflect(cont) + // reflect + } + } + } + } +} + +object Scene { + def generateMirror(r: Double, segments: Int, arcSize: Double, at: Point, rotated: Double): Seq[Segment] = { + val sizePerSegment = arcSize / segments + val points = (0 to segments) map { i => + val angle = i * sizePerSegment + rotated + at + Point(Math.cos(angle) * r, Math.sin(angle) * r) + } + points.sliding(2).map { case Seq(start, end) => + Segment.fromPoints(start, end) + }.toSeq + } + + def generateParabola(a: Double, b: Double, w: Double, w_i: Double, segments: Int, at: Point, rotated: Double): Seq[Segment] = { + val points = (-segments / 2 to segments / 2) map { i => + val w_curr = (i / segments.toDouble) * w + w_i + at + Point(a * w_curr, b * w_curr * w_curr) + } + points.sliding(2).map { case Seq(start, end) => + Segment.fromPoints(start, end) + }.toSeq + } + + def rotate(walls: Seq[Segment], angle: Double) = + walls.map(_.rotate(angle)) +} |