func setupGeometry(gl *webgl.Context, program *js.Object) { gl.BindBuffer(gl.ARRAY_BUFFER, gl.CreateBuffer()) gl.BufferData(gl.ARRAY_BUFFER, buildArray(), gl.STATIC_DRAW) positionLocation := gl.GetAttribLocation(program, "a_position") gl.EnableVertexAttribArray(positionLocation) gl.VertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0) }
func (o *Obj) Read(reader io.Reader, gl *webgl.Context) error { var group *ObjGroup var materialName string var positions []float32 var normals []float32 var texcoords []float32 var float2 [2]float32 = [2]float32{0, 0} var float3 [3]float32 = [3]float32{0, 0, 0} scanner := bufio.NewScanner(reader) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" || strings.HasPrefix(line, "#") { // Blank line or comment, ignore it continue } // Split line into fields on whitespace fields := strings.Fields(line) switch strings.ToLower(fields[0]) { // Vertex position. case "v": if err := parseFloat3(fields[1:4], &float3); err != nil { return err } positions = append(positions, float3[0], float3[1], float3[2]) // Vertex normal. case "vn": if err := parseFloat3(fields[1:4], &float3); err != nil { return err } normals = append(normals, float3[0], float3[1], float3[2]) // Vertex texture coordinates. case "vt": if err := parseFloat2(fields[1:3], &float2); err != nil { return err } texcoords = append(texcoords, float2[0], 1.0-float2[1]) // Face indices, specified in sets of "position/uv/normal". case "f": faces := fields[1:len(fields)] if group == nil { group = &ObjGroup{MaterialName: materialName} o.Groups = append(o.Groups, group) } group.faces = append(group.faces, faces) // New group, with a name. case "g": group = &ObjGroup{Name: fields[1], MaterialName: materialName} o.Groups = append(o.Groups, group) // Object name. The obj will only have one object statement. case "o": o.Name = fields[1] // Material library. I'm not handling this for now. Instead, call // SetMaterial() for each of the named materials. // case "mtllib": // Specifies the material for the current group (and any future groups // that don't have their own usemtl statement). case "usemtl": materialName = fields[1] if group != nil { group.MaterialName = materialName } } } if err := scanner.Err(); err != nil { return err } // Normalize the vertices. OBJ does two things that cause problems for // modern renderers: it allows faces to be polygons, instead of only // triangles; and it allows each face vertex to have a different index // for each stream (position, normal, uv). // // This code creates triangle fans out of any faces that have more than // three vertexes, and merges distinct groupings of pos/normal/uv into // a single vertex stream. var faceIndices [3]uint16 = [3]uint16{0, 0, 0} o.tupleIndex = 0 for _, g := range o.Groups { for _, f := range g.faces { for i := 1; i < len(f)-1; i++ { var err error if faceIndices[0], err = o.mergeTuple(f[i], positions, normals, texcoords); err != nil { return err } if faceIndices[1], err = o.mergeTuple(f[0], positions, normals, texcoords); err != nil { return err } if faceIndices[2], err = o.mergeTuple(f[i+1], positions, normals, texcoords); err != nil { return err } g.indices = append(g.indices, faceIndices[0], faceIndices[1], faceIndices[2]) } } g.IndexBuffer = gl.CreateBuffer() gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, g.IndexBuffer) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, g.indices, gl.STATIC_DRAW) g.NumIndices = len(g.indices) } o.VertexBuffer = gl.CreateBuffer() gl.BindBuffer(gl.ARRAY_BUFFER, o.VertexBuffer) gl.BufferData(gl.ARRAY_BUFFER, o.vertices, gl.STATIC_DRAW) return nil }