diff --git a/renderer3/hrend/math.go b/renderer3/hrend/math.go index aeeddff..f4c2d56 100644 --- a/renderer3/hrend/math.go +++ b/renderer3/hrend/math.go @@ -132,10 +132,8 @@ func (m *Mat44f) SetProjection(fov float32, aspect float32, near float32, far fl m.Set(0, 0, near/right) m.Set(1, 1, near/top) m.Set(2, 2, -(far+near)/(far-near)) - //m.Set(2, 2, -(far)/(far-near)) m.Set(3, 2, -1) m.Set(2, 3, -(2*far*near)/(far-near)) - //m.Set(2, 3, -(far*near)/(far-near)) } func (m *Mat44f) SetViewport(tl Vec3f, br Vec3f) { //width, height, depth int) { @@ -237,8 +235,25 @@ func (m *Mat44f) SetLookAt(from *Vec3f, to *Vec3f, up *Vec3f) { m.Set(2, 3, from.Z) } -// Multiply the given point by our vector. Remember this is row-major order -func (m *Mat44f) MultiplyPoint3(p Vec3f) Vec3f { +// Multiply the given point by our vector. Remember this is row-major order. +// Point is NOT scaled back +func (m *Mat44f) MultiplyPoint3(p Vec3f) (Vec3f, float32) { + var out Vec3f + // We hope very much that Go will optimize the function calls for us, + // along with computing the constants. + out.X = p.X*m.Get(0, 0) + p.Y*m.Get(0, 1) + p.Z*m.Get(0, 2) + m.Get(0, 3) + out.Y = p.X*m.Get(1, 0) + p.Y*m.Get(1, 1) + p.Z*m.Get(1, 2) + m.Get(1, 3) + out.Z = p.X*m.Get(2, 0) + p.Y*m.Get(2, 1) + p.Z*m.Get(2, 2) + m.Get(2, 3) + w := p.X*m.Get(3, 0) + p.Y*m.Get(3, 1) + p.Z*m.Get(3, 2) + m.Get(3, 3) + // out.X /= w + // if w != 1 { + // out.Y /= w + // out.Z /= w + // } + return out, w +} + +func (m *Mat44f) MultiplyPoint3Conventional(p Vec3f) Vec3f { var out Vec3f // We hope very much that Go will optimize the function calls for us, // along with computing the constants. diff --git a/renderer3/hrend/render.go b/renderer3/hrend/render.go index 374a13b..92c3641 100644 --- a/renderer3/hrend/render.go +++ b/renderer3/hrend/render.go @@ -2,8 +2,29 @@ package hrend import ( // "log" +// "math" ) +type ObjectDef struct { + Model *ObjModel + Texture Framebuffer // This needs to go somewhere else eventually! + Pos Vec3f + LookVec Vec3f + Scale float32 + Lighting bool +} + +func NewObjectDef(model *ObjModel, texture Framebuffer) *ObjectDef { + result := ObjectDef{ + Model: model, + Texture: texture, + LookVec: Vec3f{X: 0, Y: 0, Z: -1}, + Scale: 1, + Lighting: true, + } + return &result +} + // Figure out the minimum bounding box for a triangle defined by // these vertices. Returns the top left and bottom right points, // inclusive @@ -117,10 +138,10 @@ func TriangleTextured(fb *RenderBuffer, texture Framebuffer, intensity float32, //boundsBRf.Z < 0 || boundsTLf.Z > 1 { return } - if boundsBRf.Z < 0 || boundsTLf.Z > 1 { - //log.Print(boundsTLf, boundsBRf) - return - } + // if boundsBRf.Z < 0 || boundsTLf.Z > 1 { + // //log.Print(boundsTLf, boundsBRf) + // return + // } v0 := v0v.Pos.ToVec2i() v1 := v1v.Pos.ToVec2i() v2 := v2v.Pos.ToVec2i() @@ -212,3 +233,103 @@ func TriangleTextured(fb *RenderBuffer, texture Framebuffer, intensity float32, w2_y += w2_yi } } + +//func PerspectiveAndClip(//o * ObjectDef, screenmat *Mat44f, width int, height int) { + +// Return true if the face should be culled +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 + // This is what it essentially is + // e1 := v1.Sub(&v2) + // e2 := v1.Sub(&v3) + // // If viewing front face, it should be pointing in the positive z direction + // return e1.CrossProduct(e2).Z <= 0 + // But we know we can just use x and y since this is post projection + + //l1 := f[2].Pos.Sub(&f[0].Pos) + //n := l1.CrossProduct(f[1].Pos.Sub(&f[0].Pos)) +} + +// func TriangleTextured(fb *RenderBuffer, texture Framebuffer, intensity float32, v0v Vertex, v1v Vertex, v2v Vertex) { +// func PerspectiveAndClip(infaces []Facef, matrix3d *Mat44f, outfaces []Facef) { +func PerspectiveAndClip(face Facef, matrix3d *Mat44f) []Facef { //, outfaces []Facef) { + //var facei = 0 + outfaces := make([]Facef, 0, 2) + var sc Facef + var w [3]float32 + var d [3]float32 + outers := make([]int, 0, 3) + // miny := float32(math.MaxFloat32) + // minyx := float32(math.MaxFloat32) + // minyi := 0 + //var modelmat Mat44f + //var intensity float32 + //modelmat.SetLookAt(&o.Pos, o.Pos.Add(&o.LookVec), &camup) + //modelmat.ScaleSelf(o.Scale) + //matrix3d := modelmat.Multiply(screenmat) + //for _, f := range infaces { + 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 { + outers = append(outers, i) + } + // if sc[i].Pos.Y < miny || sc[i].Pos.Y < { + // miny = sc[i].Pos.Y + // minyi = i + // } + //miny = min(miny, sc[i].Pos.Y) + //minz = min(minz, sc[i].Pos.Z) + //maxz = max(maxz, sc[i].Pos.Z) + } + + // 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 !BackfaceCull(sc[0].Pos, sc[1].Pos, sc[2].Pos) { + outfaces = append(outfaces, sc) + } + //outfaces[facei] = sc + //facei += 1 + 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 + + // log.Print(o.Model.Faces[0][0].Pos, o.Model.Faces[0][1].Pos, o.Model.Faces[0][2].Pos) + // log.Print(sc[0].Pos, sc[1].Pos, sc[2].Pos) + // log.Print(matrix3d) + // for i := range 3 { + // // Perspective divide (?) and screen coord + // sc[i].Pos.ViewportSelf(width, height) + // } + // //log.Print(sc[0].Pos, sc[1].Pos, sc[2].Pos, matrix3d) + // if o.Lighting { + // l1 := f[2].Pos.Sub(&f[0].Pos) + // n := l1.CrossProduct(f[1].Pos.Sub(&f[0].Pos)) + // n = n.Normalize() + // // light = lookvec // use this for weird things + // intensity = n.MultSimp(&light) + // if intensity < 0 { + // intensity = 0 + // } + // intensity = (intensity + float32(*minlight)) / (1 + float32(*minlight)) + // } else { + // intensity = 1.0 + // } + // TriangleTextured(&rb, o.Texture, intensity, sc[0], sc[1], sc[2]) + //} +} diff --git a/renderer3/main.go b/renderer3/main.go index f12c74f..c9030fc 100644 --- a/renderer3/main.go +++ b/renderer3/main.go @@ -32,25 +32,25 @@ func must(err error) { } } -type ObjectDef struct { - Model *hrend.ObjModel - Texture hrend.Framebuffer // This needs to go somewhere else eventually! - Pos hrend.Vec3f - LookVec hrend.Vec3f - Scale float32 - Lighting bool -} - -func NewObjectDef(model *hrend.ObjModel, texture hrend.Framebuffer) *ObjectDef { - result := ObjectDef{ - Model: model, - Texture: texture, - LookVec: hrend.Vec3f{X: 0, Y: 0, Z: -1}, - Scale: 1, - Lighting: true, - } - return &result -} +// type ObjectDef struct { +// Model *hrend.ObjModel +// Texture hrend.Framebuffer // This needs to go somewhere else eventually! +// Pos hrend.Vec3f +// LookVec hrend.Vec3f +// Scale float32 +// Lighting bool +// } +// +// func NewObjectDef(model *hrend.ObjModel, texture hrend.Framebuffer) *ObjectDef { +// result := ObjectDef{ +// Model: model, +// Texture: texture, +// LookVec: hrend.Vec3f{X: 0, Y: 0, Z: -1}, +// Scale: 1, +// Lighting: true, +// } +// return &result +// } func loadObject(name string) (*hrend.ObjModel, hrend.Framebuffer) { ofile := filepath.Join("../", name+".obj") @@ -126,7 +126,7 @@ func CameraInput(yaw, pitch float32) (float32, float32, hrend.Vec3f) { // translate the new camera movement based on the yaw var moverot hrend.Mat44f moverot.SetRotationY(-yaw) - newcamtrans = moverot.MultiplyPoint3(newcamtrans) + newcamtrans = moverot.MultiplyPoint3Conventional(newcamtrans) return yaw, pitch, newcamtrans } @@ -183,7 +183,7 @@ func main() { // Generate world wtexraw := Checkerboard([]color.Color{color.RGBA{R: 0, G: 255, B: 0, A: 255}, color.RGBA{R: 50, G: 150, B: 0, A: 255}}, 32) wtex := hrend.NewTexture(wtexraw, 1) - world := FlatTerrain(1) + world := FlatTerrain(10) // Generate skybox skyraw := Gradient1px(color.RGBA{R: 100, G: 100, B: 255, A: 255}, color.RGBA{R: 255, G: 255, B: 255, A: 255}, 32) @@ -199,13 +199,13 @@ func main() { } // And the actual objects for the scene. We also put the world in there - objects := make([]*ObjectDef, 0) - objects = append(objects, NewObjectDef(world, wtex)) - objects = append(objects, NewObjectDef(sky, skytex)) // the actual skybox + objects := make([]*hrend.ObjectDef, 0) + objects = append(objects, hrend.NewObjectDef(world, wtex)) + objects = append(objects, hrend.NewObjectDef(sky, skytex)) // the actual skybox skyobj := objects[len(objects)-1] skyobj.Scale = 50 skyobj.Lighting = false - objects = append(objects, NewObjectDef(models[1], textures[1])) + objects = append(objects, hrend.NewObjectDef(models[1], textures[1])) objects[len(objects)-1].Pos.Y += 1 objects[len(objects)-1].Pos.Z -= 2 @@ -243,49 +243,51 @@ func main() { } } - var sc [3]hrend.Vertex + //var faces []hrend.Facef //[3]hrend.Vertex var modelmat hrend.Mat44f var intensity float32 - var minz = float32(math.MaxFloat32) - var maxz = float32(-math.MaxFloat32) + //var minz = float32(math.MaxFloat32) + //var maxz = float32(-math.MaxFloat32) for _, o := range objects { // Create the final matrix modelmat.SetLookAt(&o.Pos, o.Pos.Add(&o.LookVec), &camup) modelmat.ScaleSelf(o.Scale) matrix3d := modelmat.Multiply(screenmat) for _, f := range o.Model.Faces { - for i := range 3 { - sc[i] = f[i] - sc[i].Pos = matrix3d.MultiplyPoint3(f[i].Pos) - minz = min(minz, sc[i].Pos.Z) - maxz = max(maxz, sc[i].Pos.Z) - } - log.Print(o.Model.Faces[0][0].Pos, o.Model.Faces[0][1].Pos, o.Model.Faces[0][2].Pos) - log.Print(sc[0].Pos, sc[1].Pos, sc[2].Pos) - log.Print(matrix3d) - for i := range 3 { - // Perspective divide (?) and screen coord - sc[i].Pos.ViewportSelf(*width, *height) - } - //log.Print(sc[0].Pos, sc[1].Pos, sc[2].Pos, matrix3d) - if o.Lighting { - l1 := f[2].Pos.Sub(&f[0].Pos) - n := l1.CrossProduct(f[1].Pos.Sub(&f[0].Pos)) - n = n.Normalize() - // light = lookvec // use this for weird things - intensity = n.MultSimp(&light) - if intensity < 0 { - intensity = 0 + for _, sc := range hrend.PerspectiveAndClip(f, matrix3d) { + // for i := range 3 { + // sc[i] = f[i] + // sc[i].Pos = matrix3d.MultiplyPoint3(f[i].Pos) + // minz = min(minz, sc[i].Pos.Z) + // maxz = max(maxz, sc[i].Pos.Z) + // } + // log.Print(o.Model.Faces[0][0].Pos, o.Model.Faces[0][1].Pos, o.Model.Faces[0][2].Pos) + // log.Print(sc[0].Pos, sc[1].Pos, sc[2].Pos) + // log.Print(matrix3d) + for i := range 3 { + // Perspective divide (?) and screen coord + sc[i].Pos.ViewportSelf(*width, *height) } - intensity = (intensity + float32(*minlight)) / (1 + float32(*minlight)) - } else { - intensity = 1.0 + //log.Print(sc[0].Pos, sc[1].Pos, sc[2].Pos, matrix3d) + if o.Lighting { + l1 := f[2].Pos.Sub(&f[0].Pos) + n := l1.CrossProduct(f[1].Pos.Sub(&f[0].Pos)) + n = n.Normalize() + // light = lookvec // use this for weird things + intensity = n.MultSimp(&light) + if intensity < 0 { + intensity = 0 + } + intensity = (intensity + float32(*minlight)) / (1 + float32(*minlight)) + } else { + intensity = 1.0 + } + hrend.TriangleTextured(&rb, o.Texture, intensity, sc[0], sc[1], sc[2]) } - hrend.TriangleTextured(&rb, o.Texture, intensity, sc[0], sc[1], sc[2]) - break // only render one face + //break // only render one face //hrend.TriangleFlat(&rb, hrend.Col2Uint(byte(255*intensity), byte(255*intensity), byte(255*intensity)), sc[0].Pos, sc[1].Pos, sc[2].Pos) } - break // only render one object + //break // only render one object } //log.Print(minz, maxz)