func setupConnection() { ctx := canvas.Call("getContext", "2d") img := ctx.Call("getImageData", 0, 0, imgWidth, imgHeight) if img.Get("data").Length() != len(finalImage.Pix) { throw(errors.New("data size of images do not match")) } document := js.Global.Get("document") location := document.Get("location") ws, err := websocket.New(fmt.Sprintf("ws://%s/render", location.Get("host"))) assert(err) renderChan := make(chan struct{}, frameStacking) onOpen := func(ev *js.Object) { setup := setupMessage{ Width: imgWidth, Height: imgHeight, FieldOfView: 45, ColorFormat: colorFormat, ClearColor: [4]byte{127, 127, 127, 255}, } msg, err := json.Marshal(setup) assert(err) assert(ws.Send(string(msg))) go updateCamera(ws, renderChan) } onMessage := func(ev *js.Object) { idx := frameId % 2 data := js.Global.Get("Uint8Array").New(ev.Get("data")).Interface().([]uint8) if isPalette(data) { pal := createPalette(data) palImages = [2]*image.Paletted{ image.NewPaletted(imgRect, pal), image.NewPaletted(imgRect, pal), } return } var ( imageA, imageB image.Image ) if isRGBA(data) { rgbaImages[idx].Pix = data imageA = rgbaImages[0] imageB = rgbaImages[1] } else { palImages[idx].Pix = data imageA = palImages[0] imageB = palImages[1] } // This function could be optimized for this specific senario. assert(trace.Reconstruct(imageA, imageB, finalImage)) arrBuf := js.NewArrayBuffer(finalImage.Pix) buf := js.Global.Get("Uint8ClampedArray").New(arrBuf) img.Get("data").Call("set", buf) ctx.Call("putImageData", img, 0, 0) numFrames++ frameId++ renderChan <- struct{}{} } ws.BinaryType = "arraybuffer" ws.AddEventListener("open", false, onOpen) ws.AddEventListener("message", false, onMessage) }
func main() { flag.Parse() fmt.Sscanf(arguments.windowSize, "%d,%d", &screenWidth, &screenHeight) fmt.Sscanf(arguments.resolution, "%d,%d", &resolutionX, &resolutionY) fp, err := os.Open(arguments.inputFile) if err != nil { fmt.Fprintln(os.Stderr, err) return } defer fp.Close() tree, vpa, err := trace.LoadOctree(fp) if err != nil { panic(err) } maxDepth := trace.TreeWidthToDepth(vpa) sdl.Init(sdl.INIT_EVERYTHING) defer sdl.Quit() title := "AJ's Raytracer" window, err := sdl.CreateWindow(title, sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, screenWidth, screenHeight, sdl.WINDOW_SHOWN) if err != nil { panic(err) } defer window.Destroy() sdl.SetRelativeMouseMode(true) renderer, err := sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED) if err != nil { panic(err) } defer renderer.Destroy() sdl.SetHint(sdl.HINT_RENDER_SCALE_QUALITY, arguments.scaleFilter) renderer.SetLogicalSize(resolutionX, resolutionY) renderer.SetDrawColor(0, 0, 0, 255) rect := image.Rect(0, 0, resolutionX, resolutionY) if arguments.enableJitter { rect.Max.X /= 2 } surfaces := [2]*image.RGBA{image.NewRGBA(rect), image.NewRGBA(rect)} backBuffer := image.NewRGBA(image.Rect(0, 0, resolutionX, resolutionY)) texture, err := renderer.CreateTexture(sdl.PIXELFORMAT_ABGR8888, sdl.TEXTUREACCESS_STREAMING, resolutionX, resolutionY) if err != nil { panic(err) } defer texture.Destroy() var pos [3]float32 fmt.Sscanf(arguments.treePosition, "%f,%f,%f", &pos[0], &pos[1], &pos[2]) cfg := trace.Config{ FieldOfView: float32(arguments.fieldOfView), TreeScale: float32(arguments.treeScale), TreePosition: pos, ViewDist: float32(arguments.viewDistance), Images: surfaces, Jitter: arguments.enableJitter, MultiThreaded: arguments.multiThreaded, Depth: enableDepthTest, } raytracer := trace.NewRaytracer(cfg) defer raytracer.Close() camera := trace.FreeFlightCamera{XRot: 0, YRot: 0} nf := 0 dt := time.Duration(1000 / 60) ft := time.Duration(nf) if arguments.pprof { go func() { fmt.Fprintln(os.Stderr, http.ListenAndServe("localhost:6060", nil)) }() fp, err := os.Create("raytracer.pprof") if err != nil { panic(err) } defer fp.Close() pprof.StartCPUProfile(fp) defer pprof.StopCPUProfile() } for { t := time.Now() dtf := float32(dt / time.Millisecond) for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() { switch t := event.(type) { case *sdl.QuitEvent: return case *sdl.MouseMotionEvent: if enableInput { camera.XRot -= dtf * float32(t.XRel) * mouseSpeed camera.YRot -= dtf * float32(t.YRel) * mouseSpeed } case *sdl.KeyUpEvent: switch t.Keysym.Sym { case sdl.K_ESCAPE: return case sdl.K_f: toggleFullscreen(window) case sdl.K_c: if !arguments.ppm { fmt.Println("Camera:", camera) } case sdl.K_SPACE: enableInput = !enableInput sdl.SetRelativeMouseMode(enableInput) } } } renderer.Clear() if enableInput || arguments.ppm || arguments.pprof { if enableInput { moveCamera(&camera, dtf) window.WarpMouseInWindow(screenWidth/2, screenHeight/2) } if enableDepthTest { raytracer.ClearDepth(raytracer.Frame()) } raytracer.Trace(&camera, tree, maxDepth) } if arguments.enableJitter { if err := trace.Reconstruct(raytracer.Image(0), raytracer.Image(1), backBuffer); err != nil { panic(err) } } else { backBuffer = surfaces[0] } texture.Update(nil, unsafe.Pointer(&backBuffer.Pix[0]), backBuffer.Stride) renderer.Copy(texture, nil, nil) renderer.Present() if arguments.ppm { writePPM(backBuffer) } dt = time.Since(t) ft += dt nf++ if ft >= time.Second { window.SetTitle(fmt.Sprintf("%v - fps: %v, dt: %vms", title, nf, int(ft/time.Millisecond)/nf)) nf = 0 ft = 0 } } }