package geometry import ( "errors" "github.com/go-gl/gl/v4.6-core/gl" "github.com/go-gl/mathgl/mgl32" "github.com/udhos/gwob" ) // attributes: // position 3 * float // texture 2 * float // normal 3 * float // tangent 3 * float type Geometry struct { vao uint32 vbo uint32 ebo uint32 size int32 } func (g Geometry) Draw() { gl.BindVertexArray(g.vao) gl.BindBuffer(gl.ARRAY_BUFFER, g.vbo) gl.DrawElementsWithOffset(gl.TRIANGLES, g.size, gl.UNSIGNED_INT, 0) } func (g Geometry) Delete() { gl.DeleteVertexArrays(1, &g.vao) gl.DeleteBuffers(1, &g.ebo) gl.DeleteBuffers(1, &g.vbo) } func new(coords []float32, indicies []int) (Geometry, error) { var geometry Geometry unsignedIndicies := make([]uint32, len(indicies)) for i, v := range indicies { unsignedIndicies[i] = uint32(v) } // vao gl.GenVertexArrays(1, &geometry.vao) gl.BindVertexArray(geometry.vao) // vbo gl.GenBuffers(1, &geometry.vbo) gl.BindBuffer(gl.ARRAY_BUFFER, geometry.vbo) gl.BufferData(gl.ARRAY_BUFFER, len(coords)*4, gl.Ptr(coords), gl.STATIC_DRAW) var i uint32 = 0 // attributes // position gl.VertexAttribPointerWithOffset(i, 3, gl.FLOAT, false, 11*4, 0) gl.EnableVertexAttribArray(i) i++ // texture gl.VertexAttribPointerWithOffset(i, 2, gl.FLOAT, false, 11*4, 3*4) gl.EnableVertexAttribArray(i) i++ // normal gl.VertexAttribPointerWithOffset(2, 3, gl.FLOAT, false, 11*4, 5*4) gl.EnableVertexAttribArray(i) i++ // tangent gl.VertexAttribPointerWithOffset(i, 3, gl.FLOAT, false, 11*4, 8*4) gl.EnableVertexAttribArray(i) i++ // ebo gl.GenBuffers(1, &geometry.ebo) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, geometry.ebo) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(unsignedIndicies)*4, gl.Ptr(unsignedIndicies), gl.STATIC_DRAW) geometry.size = int32(len(indicies)) return geometry, nil } func LoadOBJ(path string) (geometry Geometry, err error) { o, err := gwob.NewObjFromFile(path, &gwob.ObjParserOptions{}) if err != nil { return geometry, err } if !o.NormCoordFound { return geometry, errors.New("missing normals in OBJ") } if !o.TextCoordFound { return geometry, errors.New("missing texture UVs in OBJ") } // add tangent coords := make([]float32, 0, len(o.Coord)*11) tangents := make([][3]float32, len(o.Coord)/8) // iterate faces for i := 0; i < len(o.Indices); i += 3 { i0 := o.Indices[i+0] i1 := o.Indices[i+1] i2 := o.Indices[i+2] // positions v0 := o.Coord[i0*8 : i0*8+3] v1 := o.Coord[i1*8 : i1*8+3] v2 := o.Coord[i2*8 : i2*8+3] // UVs uv0 := o.Coord[i0*8+3 : i0*8+5] uv1 := o.Coord[i1*8+3 : i1*8+5] uv2 := o.Coord[i2*8+3 : i2*8+5] // edges edge1 := [3]float32{v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]} edge2 := [3]float32{v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2]} // UV deltas delta1 := [2]float32{uv1[0] - uv0[0], uv1[1] - uv0[1]} delta2 := [2]float32{uv2[0] - uv0[0], uv2[1] - uv0[1]} // calculate tangents f := 1.0 / (delta1[0]*delta2[1] - delta2[0]*delta1[1]) tangent := [3]float32{ f * (delta2[1]*edge1[0] - delta1[1]*edge2[0]), f * (delta2[1]*edge1[1] - delta1[1]*edge2[1]), f * (delta2[1]*edge1[2] - delta1[1]*edge2[2]), } tangents[i0] = mgl32.Vec3(tangents[i0]).Add(tangent) tangents[i1] = mgl32.Vec3(tangents[i1]).Add(tangent) tangents[i2] = mgl32.Vec3(tangents[i2]).Add(tangent) } // build coords for i := 0; i < len(o.Coord)/8; i++ { pos := o.Coord[i*8 : i*8+3] uv := o.Coord[i*8+3 : i*8+5] norm := o.Coord[i*8+5 : i*8+8] tan := mgl32.Vec3(tangents[i]).Normalize() coords = append(coords, pos...) coords = append(coords, uv...) coords = append(coords, norm...) coords = append(coords, tan[0:3]...) } return new(coords, o.Indices) } func Screen() (Geometry, error) { return new( []float32{ // xyz, uv, nrm, tan -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, -1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, }, []int{ 0, 1, 2, 1, 3, 2, }) }