func getShader(gl *webgl.Context, typ int, source string) (*js.Object, bool) { shader := gl.CreateShader(typ) gl.ShaderSource(shader, source) gl.CompileShader(shader) if !gl.GetShaderParameter(shader, gl.COMPILE_STATUS).Bool() { js.Global.Call("alert", gl.GetShaderInfoLog(shader)) return nil, false } return shader, true }
func setResolution( gl *webgl.Context, canvas *js.Object, uResolution *js.Object) { width := canvas.Get("clientWidth").Int() height := canvas.Get("clientHeight").Int() if (canvas.Get("width").Int() != width) || (canvas.Get("height").Int() != height) { canvas.Set("width", width) canvas.Set("height", height) } gl.Viewport(0, 0, width, height) gl.Uniform2f(uResolution, float32(width), float32(height)) }
func setupConnection(gl *webgl.Context) { document := js.Global.Get("document") location := document.Get("location") ws, err := websocket.New(fmt.Sprintf("ws://%s/render", location.Get("host"))) assert(err) renderer := make(chan struct{}) onOpen := func(ev *js.Object) { setup := setupMessage{ Resolution: imgCmResolution, ClearColor: [4]byte{127, 127, 127, 255}, } msg, err := json.Marshal(setup) assert(err) assert(ws.Send(string(msg))) go updateCamera(ws, gl, renderer) } onMessage := func(ev *js.Object) { face := frameId % 6 fmt.Println("Received face:", face) data := js.Global.Get("Uint8Array").New(ev.Get("data")) gl.Call("texImage2D", gl.TEXTURE_CUBE_MAP_POSITIVE_X+face, 0, gl.RGBA, imgCmResolution, imgCmResolution, 0, gl.RGBA, gl.UNSIGNED_BYTE, data) frameId++ select { case renderer <- struct{}{}: default: } } ws.BinaryType = "arraybuffer" ws.AddEventListener("open", false, onOpen) ws.AddEventListener("message", false, onMessage) }
func initShader(gl *webgl.Context, canvas *js.Object) (*js.Object, bool) { shader := gl.CreateProgram() vertexShader, ok := getShader(gl, gl.VERTEX_SHADER, vertexShaderSource) if !ok { js.Global.Call("alert", "Error getting vertex shader") return nil, false } fragShader, ok := getShader(gl, gl.FRAGMENT_SHADER, fragShaderSource) if !ok { js.Global.Call("alert", "Error getting fragment shader") return nil, false } gl.AttachShader(shader, vertexShader) gl.AttachShader(shader, fragShader) gl.LinkProgram(shader) if !gl.GetProgramParameterb(shader, gl.LINK_STATUS) { js.Global.Call("alert", "couldnt init shaders :(") return nil, false } gl.UseProgram(shader) return shader, true }
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 }
func setupShaders(gl *webgl.Context) *js.Object { vs := gl.CreateShader(gl.VERTEX_SHADER) gl.ShaderSource(vs, vsSource) gl.CompileShader(vs) if gl.GetShaderParameter(vs, gl.COMPILE_STATUS).Bool() == false { throw(errors.New(gl.GetShaderInfoLog(vs))) } ps := gl.CreateShader(gl.FRAGMENT_SHADER) gl.ShaderSource(ps, psSource) gl.CompileShader(ps) if gl.GetShaderParameter(ps, gl.COMPILE_STATUS).Bool() == false { throw(errors.New(gl.GetShaderInfoLog(ps))) } program := gl.CreateProgram() gl.AttachShader(program, vs) gl.AttachShader(program, ps) gl.LinkProgram(program) gl.UseProgram(program) return program }
func updateCamera(ws *websocket.WebSocket, gl *webgl.Context, renderer <-chan struct{}) { const tick30hz = (1000 / 30) * time.Millisecond var ( camera trace.FreeFlightCamera oldPos [3]float32 positions = make(chan [3]float32, 1) ) positions <- oldPos go func() { for { pos := <-positions fmt.Println("New position:", pos) m, err := json.Marshal(updateMessage{Position: pos}) assert(err) assert(ws.Send(string(m))) <-renderer } }() for _ = range time.Tick(tick30hz) { switch { case keys[38]: // Up camera.YRot += cameraSpeed case keys[40]: // Down camera.YRot -= cameraSpeed case keys[37]: // Left camera.XRot += cameraSpeed case keys[39]: // Right camera.XRot -= cameraSpeed case keys[87]: // W camera.Move(cameraSpeed) case keys[83]: // S camera.Move(-cameraSpeed) case keys[65]: // A camera.Strafe(cameraSpeed) case keys[68]: // D camera.Strafe(-cameraSpeed) case keys[69]: // E camera.Lift(cameraSpeed) case keys[81]: // Q camera.Lift(-cameraSpeed) } if oldPos != camera.Pos { select { case positions <- camera.Pos: oldPos = camera.Pos default: } } mat := mat4.Ident mat.AssignEulerRotation(camera.XRot, camera.YRot, 0) mat.Transpose() gl.UniformMatrix4fv(uniformViewLocation, false, mat.Slice()) gl.DrawArrays(gl.TRIANGLES, 0, 6) } }
func setupTextures(gl *webgl.Context, program *js.Object) { gl.ActiveTexture(gl.TEXTURE0) gl.BindTexture(gl.TEXTURE_CUBE_MAP, gl.CreateTexture()) for i := 0; i < 6; i++ { gl.Call("texImage2D", gl.TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, gl.RGBA, imgCmResolution, imgCmResolution, 0, gl.RGBA, gl.UNSIGNED_BYTE, nil) } gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR) /* gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST) */ gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) gl.Uniform1i(gl.GetUniformLocation(program, "s_texture"), 0) }
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) }