Last active
May 17, 2020 18:19
-
-
Save peterhellberg/71ff4dca1f4524791ec0ba1286620a8f to your computer and use it in GitHub Desktop.
Cube software 3d renderer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"image/color" | |
"github.com/faiface/pixel" | |
"github.com/faiface/pixel/imdraw" | |
"github.com/faiface/pixel/pixelgl" | |
"github.com/go-gl/mathgl/mgl64" | |
) | |
var ( | |
w, h float64 = 640, 640 | |
fg = color.RGBA{200, 200, 200, 55} | |
bg = color.RGBA{27, 27, 27, 255} | |
) | |
type Camera struct { | |
Position mgl64.Vec3 | |
Target mgl64.Vec3 | |
} | |
type Face struct { | |
A, B, C int | |
} | |
type Mesh struct { | |
Name string | |
Vertices []mgl64.Vec3 | |
Faces []Face | |
Position mgl64.Vec3 | |
Rotation mgl64.Vec3 | |
} | |
func NewMesh(name string, verticesCount, facesCount int) *Mesh { | |
return &Mesh{ | |
Name: name, | |
Vertices: make([]mgl64.Vec3, verticesCount), | |
Faces: make([]Face, facesCount), | |
} | |
} | |
type Device struct { | |
*imdraw.IMDraw | |
} | |
func NewDevice(imd *imdraw.IMDraw) *Device { | |
return &Device{imd} | |
} | |
func (d *Device) Project(coord mgl64.Vec3, transMat mgl64.Mat4) pixel.Vec { | |
point := mgl64.TransformCoordinate(coord, transMat).Mul(50) | |
x := int(float64(point.X())+w/2) >> 0 | |
y := int(float64(point.Y())+h/2) >> 0 | |
return pixel.V(float64(x), float64(y)) | |
} | |
func (d *Device) Render(camera *Camera, meshes []*Mesh) { | |
viewMatrix := mgl64.LookAtV(camera.Position, camera.Target, mgl64.Vec3{0, 1, 0}) | |
projectionMatrix := mgl64.Perspective(0.90, w/h, 1, 1000) | |
d.Clear() | |
for _, mesh := range meshes { | |
worldMatrix := mgl64.HomogRotate3DX(mesh.Rotation.X()). | |
Mul4(mgl64.HomogRotate3DY(mesh.Rotation.Y())). | |
Mul4(mgl64.HomogRotate3DZ(mesh.Rotation.Z())) | |
transformMatrix := worldMatrix.Mul4(viewMatrix).Mul4(projectionMatrix) | |
for i, face := range mesh.Faces { | |
p1 := d.Project(mesh.Vertices[face.A], transformMatrix) | |
p2 := d.Project(mesh.Vertices[face.B], transformMatrix) | |
p3 := d.Project(mesh.Vertices[face.C], transformMatrix) | |
d.Color = color.RGBA{100 + uint8(i*32%155), uint8(int(fg.G) * i % 255), fg.B / 4, 255} | |
d.Push(p1, p2, p3, p1) | |
d.Polygon(3) | |
} | |
for _, v := range mesh.Vertices { | |
d.Color = color.White | |
d.Push(d.Project(v, transformMatrix)) | |
d.Circle(6, 0) | |
} | |
} | |
} | |
func run() { | |
win, err := pixelgl.NewWindow(pixelgl.WindowConfig{ | |
Bounds: pixel.R(0, 0, w, h), | |
VSync: true, | |
Undecorated: true, | |
}) | |
if err != nil { | |
panic(err) | |
} | |
device := NewDevice(imdraw.New(nil)) | |
camera := &Camera{ | |
Position: mgl64.Vec3{0, 0, 1}, | |
Target: mgl64.Vec3{0, 0, 0}, | |
} | |
cube := newCube() | |
meshes := []*Mesh{cube} | |
for !win.Closed() { | |
win.SetClosed(win.JustPressed(pixelgl.KeyEscape) || win.JustPressed(pixelgl.KeyQ)) | |
win.Clear(bg) | |
cube.Rotation = cube.Rotation.Add(mgl64.Vec3{0.01, 0.01, 0.0}) | |
device.Render(camera, meshes) | |
device.Draw(win) | |
win.Update() | |
} | |
} | |
func main() { | |
pixelgl.Run(run) | |
} | |
func newCube() *Mesh { | |
return &Mesh{ | |
Position: mgl64.Vec3{0, 0, 0}, | |
Vertices: []mgl64.Vec3{ | |
{-1, 1, 1}, | |
{1, 1, 1}, | |
{-1, -1, 1}, | |
{1, -1, 1}, | |
{-1, 1, -1}, | |
{1, 1, -1}, | |
{1, -1, -1}, | |
{-1, -1, -1}, | |
}, | |
Faces: []Face{ | |
// Front | |
{A: 0, B: 1, C: 2}, | |
{A: 1, B: 2, C: 3}, | |
// Back | |
{A: 4, B: 5, C: 6}, | |
{A: 4, B: 6, C: 7}, | |
// Left | |
{A: 1, B: 3, C: 4}, | |
{A: 1, B: 7, C: 4}, | |
// Right | |
{A: 0, B: 2, C: 5}, | |
{A: 0, B: 6, C: 5}, | |
// Top | |
{A: 3, B: 4, C: 5}, | |
{A: 2, B: 3, C: 5}, | |
// Bottom | |
{A: 0, B: 1, C: 6}, | |
{A: 1, B: 7, C: 6}, | |
}, | |
} | |
} |
Author
peterhellberg
commented
Nov 7, 2017
package main
import (
"image/color"
"sort"
"github.com/faiface/pixel"
"github.com/faiface/pixel/imdraw"
"github.com/faiface/pixel/pixelgl"
"github.com/go-gl/mathgl/mgl64"
)
var w, h float64 = 640, 640
type Camera struct {
Position mgl64.Vec3
Target mgl64.Vec3
}
type Triangle struct {
Vectors []pixel.Vec
Color color.RGBA
AvgZ float64
}
type Face struct {
A, B, C int
Color color.RGBA
}
type Mesh struct {
Name string
Vertices []mgl64.Vec3
Faces []Face
Position mgl64.Vec3
Rotation mgl64.Vec3
}
func NewMesh(name string, verticesCount, facesCount int) *Mesh {
return &Mesh{
Name: name,
Vertices: make([]mgl64.Vec3, verticesCount),
Faces: make([]Face, facesCount),
}
}
type Device struct {
*imdraw.IMDraw
}
func NewDevice(imd *imdraw.IMDraw) *Device {
return &Device{imd}
}
func (d *Device) TransformAndProject(coord mgl64.Vec3, transMat mgl64.Mat4) (mgl64.Vec3, pixel.Vec) {
t := mgl64.TransformCoordinate(coord, transMat).Mul(50)
return t, pixel.V(
float64(int(float64(t.X())+w/2)>>0),
float64(int(float64(t.Y())+h/2)>>0),
)
}
func (d *Device) Render(camera *Camera, meshes []*Mesh) {
viewMatrix := mgl64.LookAtV(camera.Position, camera.Target, mgl64.Vec3{0, 1, 0})
projectionMatrix := mgl64.Perspective(0.90, w/h, 1, 1000)
d.Clear()
for _, mesh := range meshes {
worldMatrix := mgl64.HomogRotate3DX(mesh.Rotation.X()).
Mul4(mgl64.HomogRotate3DY(mesh.Rotation.Y())).
Mul4(mgl64.HomogRotate3DZ(mesh.Rotation.Z()))
transformMatrix := worldMatrix.Mul4(viewMatrix).Mul4(projectionMatrix)
var triangles = make([]Triangle, len(mesh.Faces))
for i, face := range mesh.Faces {
t1, p1 := d.TransformAndProject(mesh.Vertices[face.A], transformMatrix)
t2, p2 := d.TransformAndProject(mesh.Vertices[face.B], transformMatrix)
t3, p3 := d.TransformAndProject(mesh.Vertices[face.C], transformMatrix)
triangles[i] = Triangle{
Vectors: []pixel.Vec{p1, p2, p3, p1},
Color: face.Color,
AvgZ: (t1.Z() + t2.Z() + t3.Z()) / 3,
}
}
sort.Slice(triangles, func(i, j int) bool {
return triangles[i].AvgZ > triangles[j].AvgZ
})
for _, t := range triangles {
d.Color = t.Color
d.Push(t.Vectors...)
d.Polygon(0)
for _, v := range t.Vectors {
d.Color = color.White
d.Push(v)
d.Circle(4, 0)
}
}
}
}
func run() {
win, err := pixelgl.NewWindow(pixelgl.WindowConfig{
Bounds: pixel.R(0, 0, w, h),
VSync: true,
Undecorated: true,
})
if err != nil {
panic(err)
}
device := NewDevice(imdraw.New(nil))
camera := &Camera{
Position: mgl64.Vec3{0, 0, 1},
Target: mgl64.Vec3{0, 0, 0},
}
cube := newCube()
meshes := []*Mesh{cube}
for !win.Closed() {
cube.Rotation = cube.Rotation.Add(mgl64.Vec3{0.01, 0.01, 0.0})
win.SetClosed(win.JustPressed(pixelgl.KeyEscape) || win.JustPressed(pixelgl.KeyQ))
win.Clear(color.RGBA{27, 27, 27, 255})
device.Render(camera, meshes)
device.Draw(win)
win.Update()
}
}
func main() {
pixelgl.Run(run)
}
func newCube() *Mesh {
return &Mesh{
Position: mgl64.Vec3{0, 0, 0},
Vertices: []mgl64.Vec3{
{-1, 1, 1},
{1, 1, 1},
{-1, -1, 1},
{1, -1, 1},
{-1, 1, -1},
{1, 1, -1},
{1, -1, -1},
{-1, -1, -1},
},
Faces: []Face{
// Front
{A: 0, B: 1, C: 2, Color: color.RGBA{255, 0, 0, 255}},
{A: 1, B: 2, C: 3, Color: color.RGBA{127, 0, 0, 255}},
// Back
{A: 4, B: 5, C: 6, Color: color.RGBA{0, 255, 0, 255}},
{A: 4, B: 6, C: 7, Color: color.RGBA{0, 127, 0, 255}},
// Left
{A: 1, B: 3, C: 4, Color: color.RGBA{0, 0, 255, 255}},
{A: 1, B: 7, C: 4, Color: color.RGBA{0, 0, 127, 255}},
// Right
{A: 0, B: 2, C: 5, Color: color.RGBA{255, 255, 0, 255}},
{A: 0, B: 6, C: 5, Color: color.RGBA{127, 127, 0, 255}},
// Top
{A: 3, B: 4, C: 5, Color: color.RGBA{0, 255, 255, 255}},
{A: 2, B: 3, C: 5, Color: color.RGBA{0, 127, 127, 255}},
// Bottom
{A: 0, B: 1, C: 6, Color: color.RGBA{255, 0, 255, 255}},
{A: 1, B: 7, C: 6, Color: color.RGBA{127, 0, 127, 255}},
},
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment