diff --git a/renderer3/hrend/math.go b/renderer3/hrend/math.go index d6ad3f0..5dae4d1 100644 --- a/renderer3/hrend/math.go +++ b/renderer3/hrend/math.go @@ -329,6 +329,18 @@ func (v0 *Vec3f) Scale(s float32) *Vec3f { } } +func LerpVec3f(v0 Vec3f, v1 Vec3f, t float32) Vec3f { + return Vec3f{ + X: (1-t)*v0.X + t*v1.X, + Y: (1-t)*v0.Y + t*v1.Y, + Z: (1-t)*v0.Z + t*v1.Z, + } +} + +func LerpF32(a, b, t float32) float32 { + return (1-t)*a + t*b +} + func (v0 *Vec3f) CrossProduct(v1 *Vec3f) *Vec3f { return &Vec3f{ X: v0.Y*v1.Z - v0.Z*v1.Y, diff --git a/renderer3/hrend/render.go b/renderer3/hrend/render.go index c8996f1..8b3bdc7 100644 --- a/renderer3/hrend/render.go +++ b/renderer3/hrend/render.go @@ -207,41 +207,99 @@ func BackfaceCull(v1, v2, v3 Vec3f) bool { return (v1.X-v2.X)*(v3.Y-v2.Y)-(v1.Y-v2.Y)*(v3.X-v2.X) >= 0 } +func conditionalAddTriangle(sc Facef, w [3]float32, out []Facef) []Facef { + // The triangle is fine + for i := range 3 { + if w[i] != 1 { + sc[i].Pos.X /= w[i] + sc[i].Pos.Y /= w[i] + sc[i].Pos.Z /= w[i] + } + } + // Backface culling: no need to do anything with triangles facing the wrong way + if EdgeFunction(sc[0].Pos, sc[1].Pos, sc[2].Pos) <= 0 { + out = append(out, sc) + } + + return out +} + func PerspectiveAndClip(face Facef, matrix3d *Mat44f) []Facef { outfaces := make([]Facef, 0, 2) var sc Facef var w [3]float32 var d [3]float32 outers := make([]int, 0, 3) + inners := make([]int, 0, 3) for i := range 3 { sc[i] = face[i] sc[i].Pos, w[i] = matrix3d.MultiplyPoint3(face[i].Pos) d[i] = sc[i].Pos.Z + w[i] - if d[i] < 0 { + if d[i] < 0.001 { outers = append(outers, i) + } else { + inners = append(inners, i) } } - // Just to test: reject any that have points outside. We don't clip - if len(outers) > 0 { - return outfaces - } else { - for i := range 3 { - //sc[i] - if w[i] != 1 { - sc[i].Pos.X /= w[i] - sc[i].Pos.Y /= w[i] - sc[i].Pos.Z /= w[i] - } - } - // Backface culling: no need to do anything with triangles facing the wrong way - if EdgeFunction(sc[0].Pos, sc[1].Pos, sc[2].Pos) <= 0 { - outfaces = append(outfaces, sc) - } - //outfaces[facei] = sc - //facei += 1 - return outfaces + + if len(outers) == 2 { // The one triangle thing + ai := inners[0] + bi := outers[0] + ci := outers[1] + + // Calc how far along we are on each of these lines. These are the new points + tab := d[ai] / (d[ai] - d[bi]) + tac := d[ai] / (d[ai] - d[ci]) + + // The two points that aren't a need to be the interpolated values + sc[bi].Pos = LerpVec3f(sc[ai].Pos, sc[bi].Pos, tab) + sc[ci].Pos = LerpVec3f(sc[ai].Pos, sc[ci].Pos, tac) + w[bi] = LerpF32(w[ai], w[bi], tab) + w[ci] = LerpF32(w[ai], w[ci], tac) + + outfaces = conditionalAddTriangle(sc, w, outfaces) + } else if len(outers) == 1 { // The two triangle thing, two new corners + ai := outers[0] + bi := inners[0] + ci := inners[1] + + tab := d[ai] / (d[ai] - d[bi]) + tac := d[ai] / (d[ai] - d[ci]) + wa := w[ai] + + // This time, we're generating two new points. + sct := sc + + // Only ONE point needs to be modified: the one outer. Remember that + // tab and tac are the distance to that point itself, so a still needs + // to be the first value here + sct[ai].Pos = LerpVec3f(sc[ai].Pos, sc[bi].Pos, tab) + w[ai] = LerpF32(w[ai], w[bi], tab) + outfaces = conditionalAddTriangle(sct, w, outfaces) + + // Now that we've replaced the far point, we also need to replace + // the original B point that we used, since that's part of the other + // triangle. But simply replacing it will make the triangle invisible, + // since it inverts the winding order (I think) + sct[bi].Pos = LerpVec3f(sc[ai].Pos, sc[ci].Pos, tac) + w[bi] = LerpF32(wa, w[ci], tac) + // Now swap the a and b + w[ai], w[bi] = w[bi], w[ai] + sct[ai], sct[bi] = sct[bi], sct[ai] + outfaces = conditionalAddTriangle(sct, w, outfaces) + + /* + sc2[ai].Pos = LerpVec3f(sc2[ai].Pos, sc2[ci].Pos, tac) + w[ai] = LerpF32(w[ai], w[ci], tac) + //outfaces = conditionalAddTriangle(sc2, w, outfaces) + */ + + } else if len(outers) != 3 { // Output the face itself, no modification + outfaces = conditionalAddTriangle(sc, w, outfaces) } + return outfaces + // TODO: Now that we're here doing it like this, might as well remove faces // that are fully outside the other clipping zones. No need to do actual clipping... // just full rejections. This saves a BIT of processing... though not much