Example #1
0
// Sets up anything that wouldn't have been loaded from disk, including
// all opengl data, and sets up finalizers for that data.
func (d *Dictionary) setupGlStuff() {
	d.dlists = make(map[string]uint32)
	d.strs = make(map[string]strBuffer)
	d.pars = make(map[string]strBuffer)
	// TODO: This finalizer is untested
	runtime.SetFinalizer(d, func(d *Dictionary) {
		render.Queue(func() {
			for _, v := range d.dlists {
				gl.DeleteLists(gl.Uint(v), 1)
			}
		})
	})

	render.Queue(func() {
		gl.Enable(gl.TEXTURE_2D)
		gl.GenTextures(1, (*gl.Uint)(&d.texture))
		gl.BindTexture(gl.TEXTURE_2D, gl.Uint(d.texture))
		gl.TexEnvf(gl.TEXTURE_ENV, gl.TEXTURE_ENV_MODE, gl.MODULATE)
		gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
		gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
		gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
		gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
		glu.Build2DMipmaps(gl.TEXTURE_2D, 4, d.data.Dx, d.data.Dy, gl.RGBA, d.data.Pix)
		gl.Disable(gl.TEXTURE_2D)
	})
}
Example #2
0
func (e *Entity) Render(pos mathgl.Vec2, width float32) {
	var rgba [4]float64
	gl.GetDoublev(gl.CURRENT_COLOR, (*gl.Double)(&rgba[0]))
	e.last_render_width = width
	gl.Enable(gl.TEXTURE_2D)
	e.drawReticle(pos, rgba)
	if e.sprite.sp != nil {
		dxi, dyi := e.sprite.sp.Dims()
		dx := float32(dxi)
		dy := float32(dyi)
		tx, ty, tx2, ty2 := e.sprite.sp.Bind()
		gl.Begin(gl.QUADS)
		gl.TexCoord2d(gl.Double(tx), gl.Double(-ty))
		gl.Vertex2f(gl.Float(pos.X), gl.Float(pos.Y))
		gl.TexCoord2d(gl.Double(tx), gl.Double(-ty2))
		gl.Vertex2f(gl.Float(pos.X), gl.Float(pos.Y+dy*width/dx))
		gl.TexCoord2d(gl.Double(tx2), gl.Double(-ty2))
		gl.Vertex2f(gl.Float(pos.X+width), gl.Float(pos.Y+dy*width/dx))
		gl.TexCoord2d(gl.Double(tx2), gl.Double(-ty))
		gl.Vertex2f(gl.Float(pos.X+width), gl.Float(pos.Y))
		gl.End()
	}
}
Example #3
0
func main() {
	defer func() {
		if r := recover(); r != nil {
			data := debug.Stack()
			base.Error().Printf("PANIC: %v\n", r)
			base.Error().Printf("PANIC: %s\n", string(data))
			base.CloseLog()
			fmt.Printf("PANIC: %s\n", string(data))
		}
	}()
	base.Log().Printf("Version %s", Version())
	sys.Startup()
	sound.Init()
	render.Init()
	render.Queue(func() {
		sys.CreateWindow(10, 10, wdx, wdy)
		sys.EnableVSync(true)
		err := gl.Init()
		if err != nil {
			panic(err)
		}
		gl.Enable(gl.BLEND)
		gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
	})
	base.InitShaders()
	runtime.GOMAXPROCS(8)
	ui, err := gui.Make(gin.In(), gui.Dims{wdx, wdy}, filepath.Join(datadir, "fonts", "skia.ttf"))
	if err != nil {
		panic(err.Error())
	}
	loadAllRegistries()

	// TODO: Might want to be able to reload stuff, but this is sensitive because it
	// is loading textures.  We should probably redo the sprite system so that this
	// is easier to safely handle.
	game.LoadAllEntities()

	// Set up editors
	editors = map[string]house.Editor{
		"room":  house.MakeRoomEditorPanel(),
		"house": house.MakeHouseEditorPanel(),
	}
	for name, editor := range editors {
		path := base.GetStoreVal(fmt.Sprintf("last %s path", name))
		path = filepath.Join(datadir, path)
		if path != "" {
			editor.Load(path)
		}
	}
	editor_name = "room"
	editor = editors[editor_name]

	edit_mode := false
	game.Restart = func() {
		base.Log().Printf("Restarting...")
		ui.RemoveChild(game_box)
		game_box = &lowerLeftTable{gui.MakeAnchorBox(gui.Dims{1024, 768})}
		err = game.InsertStartMenu(game_box)
		if err != nil {
			panic(err)
		}
		ui.AddChild(game_box)
		base.Log().Printf("Restarted")
	}
	game.Restart()

	if base.IsDevel() {
		ui.AddChild(base.MakeConsole())
	}
	sys.Think()
	// Wait until now to create the dictionary because the render thread needs
	// to be running in advance.
	render.Queue(func() {
		ui.Draw()
	})
	render.Purge()

	var profile_output *os.File
	heap_prof_count := 0

	for key_map["quit"].FramePressCount() == 0 {
		sys.Think()
		render.Queue(func() {
			sys.SwapBuffers()
			ui.Draw()
		})
		render.Purge()

		for _, child := range game_box.GetChildren() {
			if gp, ok := child.(*game.GamePanel); ok {
				game_panel = gp
			}
		}

		if base.IsDevel() {
			if key_map["cpu profile"].FramePressCount() > 0 {
				if profile_output == nil {
					profile_output, err = os.Create(filepath.Join(datadir, "cpu.prof"))
					if err == nil {
						err = pprof.StartCPUProfile(profile_output)
						if err != nil {
							base.Log().Printf("Unable to start CPU profile: %v\n", err)
							profile_output.Close()
							profile_output = nil
						}
						base.Log().Printf("profout: %v\n", profile_output)
					} else {
						base.Log().Printf("Unable to start CPU profile: %v\n", err)
					}
				} else {
					pprof.StopCPUProfile()
					profile_output.Close()
					profile_output = nil
				}
			}

			if key_map["heap profile"].FramePressCount() > 0 {
				out, err := os.Create(filepath.Join(datadir, fmt.Sprintf("heap-%d.prof", heap_prof_count)))
				heap_prof_count++
				if err == nil {
					err = pprof.WriteHeapProfile(out)
					out.Close()
					if err != nil {
						base.Warn().Printf("Unable to write heap profile: %v", err)
					}
				} else {
					base.Warn().Printf("Unable to create heap profile: %v", err)
				}
			}

			if key_map["manual mem"].FramePressCount() > 0 {
				base.Log().Printf(memory.TotalAllocations())
			}

			if key_map["game mode"].FramePressCount()%2 == 1 {
				base.Log().Printf("Game mode change: %t", edit_mode)
				if edit_mode {
					ui.RemoveChild(editor)
					ui.AddChild(game_box)
				} else {
					ui.RemoveChild(game_box)
					ui.AddChild(editor)
				}
				edit_mode = !edit_mode

				if key_map["row up"].FramePressCount() > 0 {
					house.Num_rows += 25
				}
				if key_map["row down"].FramePressCount() > 0 {
					house.Num_rows -= 25
				}
				if key_map["steps up"].FramePressCount() > 0 {
					house.Num_steps++
				}
				if key_map["steps down"].FramePressCount() > 0 {
					house.Num_steps--
				}
				if key_map["noise up"].FramePressCount() > 0 {
					house.Noise_rate += 10
				}
				if key_map["noise down"].FramePressCount() > 0 {
					house.Noise_rate -= 10
				}
				if key_map["foo"].FramePressCount() > 0 {
					house.Foo = (house.Foo + 1) % 2
				}
			}

			if edit_mode {
				editMode()
			} else {
				gameMode()
			}
		}
		// Draw a cursor at the cursor - for testing an osx bug in glop.
		// zx, zy := gin.In().GetCursor("Mouse").Point()
		// render.Queue(func() {
		//   gl.Color4ub(255, 0, 0, 255)
		//   gl.Begin(gl.LINES)
		//   {
		//     gl.Vertex2i(int32(zx-25), int32(zy))
		//     gl.Vertex2i(int32(zx+25), int32(zy))
		//     gl.Vertex2i(int32(zx), int32(zy-25))
		//     gl.Vertex2i(int32(zx), int32(zy+25))
		//   }
		//   gl.End()
		// })
	}
}
Example #4
0
func (d *Dictionary) RenderString(s string, x, y, z, height float64, just Justification) {
	if len(s) == 0 {
		return
	}
	strbuf, ok := d.strs[s]
	if !ok {
		defer d.RenderString(s, x, y, z, height, just)
	}
	size := unsafe.Sizeof(dictVert{})
	scale := height / float64(d.data.Maxy-d.data.Miny)
	width := float32(d.figureWidth(s) * scale)
	x_pos := float32(x)
	switch just {
	case Center:
		x_pos -= width / 2
	case Right:
		x_pos -= width
	}
	if ok {
		gl.PushMatrix()
		defer gl.PopMatrix()
		gl.Translated(gl.Double(x_pos), gl.Double(y), gl.Double(z))
		gl.Scaled(gl.Double(scale), gl.Double(scale), 1)

		gl.PushAttrib(gl.COLOR_BUFFER_BIT)
		defer gl.PopAttrib()
		gl.Enable(gl.BLEND)
		gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)

		gl.Enable(gl.TEXTURE_2D)
		gl.BindTexture(gl.TEXTURE_2D, gl.Uint(d.texture))

		gl.BindBuffer(gl.ARRAY_BUFFER, gl.Uint(strbuf.vbuffer))

		gl.EnableClientState(gl.VERTEX_ARRAY)
		gl.VertexPointer(2, gl.FLOAT, gl.Sizei(size), nil)

		gl.EnableClientState(gl.TEXTURE_COORD_ARRAY)
		gl.TexCoordPointer(2, gl.FLOAT, gl.Sizei(size), gl.Pointer(unsafe.Offsetof(strbuf.vs[0].u)))

		gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.Uint(strbuf.ibuffer))
		gl.DrawElements(gl.TRIANGLES, gl.Sizei(len(strbuf.is)), gl.UNSIGNED_SHORT, nil)

		gl.DisableClientState(gl.VERTEX_ARRAY)
		gl.DisableClientState(gl.TEXTURE_COORD_ARRAY)
		return
	}
	x_pos = 0
	for _, r := range s {
		info := d.getInfo(r)
		xleft := x_pos + float32(info.Bounds.Min.X)
		xright := x_pos + float32(info.Bounds.Max.X)
		ytop := float32(d.data.Maxy - info.Bounds.Min.Y)
		ybot := float32(height) + float32(d.data.Miny-info.Bounds.Max.Y)
		start := uint16(len(strbuf.vs))
		strbuf.is = append(strbuf.is, start+0)
		strbuf.is = append(strbuf.is, start+1)
		strbuf.is = append(strbuf.is, start+2)
		strbuf.is = append(strbuf.is, start+0)
		strbuf.is = append(strbuf.is, start+2)
		strbuf.is = append(strbuf.is, start+3)
		strbuf.vs = append(strbuf.vs, dictVert{
			x: xleft,
			y: ybot,
			u: float32(info.Pos.Min.X) / float32(d.data.Dx),
			v: float32(info.Pos.Max.Y) / float32(d.data.Dy),
		})
		strbuf.vs = append(strbuf.vs, dictVert{
			x: xleft,
			y: ytop,
			u: float32(info.Pos.Min.X) / float32(d.data.Dx),
			v: float32(info.Pos.Min.Y) / float32(d.data.Dy),
		})
		strbuf.vs = append(strbuf.vs, dictVert{
			x: xright,
			y: ytop,
			u: float32(info.Pos.Max.X) / float32(d.data.Dx),
			v: float32(info.Pos.Min.Y) / float32(d.data.Dy),
		})
		strbuf.vs = append(strbuf.vs, dictVert{
			x: xright,
			y: ybot,
			u: float32(info.Pos.Max.X) / float32(d.data.Dx),
			v: float32(info.Pos.Max.Y) / float32(d.data.Dy),
		})
		x_pos += float32(info.Advance)
	}
	gl.GenBuffers(1, (*gl.Uint)(&strbuf.vbuffer))
	gl.BindBuffer(gl.ARRAY_BUFFER, gl.Uint(strbuf.vbuffer))
	gl.BufferData(gl.ARRAY_BUFFER, gl.Sizeiptr(int(size)*len(strbuf.vs)), gl.Pointer(&strbuf.vs[0].x), gl.STATIC_DRAW)

	gl.GenBuffers(1, (*gl.Uint)(&strbuf.ibuffer))
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.Uint(strbuf.ibuffer))
	gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, gl.Sizeiptr(int(unsafe.Sizeof(strbuf.is[0]))*len(strbuf.is)), gl.Pointer(&strbuf.is[0]), gl.STATIC_DRAW)
	d.strs[s] = strbuf
}
Example #5
0
// Need floor, right wall, and left wall matrices to draw the details
func (room *Room) render(floor, left, right mathgl.Mat4, zoom float32, base_alpha byte, drawables []Drawable, los_tex *LosTexture, floor_drawers []FloorDrawer) {
	do_color := func(r, g, b, a byte) {
		R, G, B, A := room.Color()
		A = alphaMult(A, base_alpha)
		gl.Color4ub(gl.Ubyte(alphaMult(R, r)), gl.Ubyte(alphaMult(G, g)), gl.Ubyte(alphaMult(B, b)), gl.Ubyte(alphaMult(A, a)))
	}
	gl.Enable(gl.TEXTURE_2D)
	gl.Enable(gl.BLEND)
	gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
	gl.Enable(gl.STENCIL_TEST)
	gl.ClearStencil(0)
	gl.Clear(gl.STENCIL_BUFFER_BIT)

	gl.EnableClientState(gl.VERTEX_ARRAY)
	gl.EnableClientState(gl.TEXTURE_COORD_ARRAY)
	defer gl.DisableClientState(gl.VERTEX_ARRAY)
	defer gl.DisableClientState(gl.TEXTURE_COORD_ARRAY)

	var vert roomVertex

	planes := []plane{
		{room.left_buffer, room.Wall, &left},
		{room.right_buffer, room.Wall, &right},
		{room.floor_buffer, room.Floor, &floor},
	}

	gl.PushMatrix()
	defer gl.PopMatrix()

	if los_tex != nil {
		gl.LoadMatrixf((*gl.Float)(&floor[0]))
		gl.ClientActiveTexture(gl.TEXTURE1)
		gl.ActiveTexture(gl.TEXTURE1)
		gl.Enable(gl.TEXTURE_2D)
		gl.EnableClientState(gl.TEXTURE_COORD_ARRAY)
		los_tex.Bind()
		gl.BindBuffer(gl.ARRAY_BUFFER, gl.Uint(room.vbuffer))
		gl.TexCoordPointer(2, gl.FLOAT, gl.Sizei(unsafe.Sizeof(vert)), gl.Pointer(unsafe.Offsetof(vert.los_u)))
		gl.ClientActiveTexture(gl.TEXTURE0)
		gl.ActiveTexture(gl.TEXTURE0)
		base.EnableShader("los")
		base.SetUniformI("los", "tex2", 1)
	}

	var mul, run mathgl.Mat4
	for _, plane := range planes {
		gl.LoadMatrixf((*gl.Float)(&floor[0]))
		run.Assign(&floor)

		// Render the doors and cut out the stencil buffer so we leave them empty
		// if they're open
		switch plane.mat {
		case &left:
			gl.StencilFunc(gl.ALWAYS, 1, 1)
			gl.StencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE)
			for _, door := range room.Doors {
				if door.Facing != FarLeft {
					continue
				}
				door.TextureData().Bind()
				R, G, B, A := door.Color()
				do_color(R, G, B, alphaMult(A, room.far_left.wall_alpha))
				gl.ClientActiveTexture(gl.TEXTURE0)
				door.TextureData().Bind()
				if door.door_glids.floor_buffer != 0 {
					gl.BindBuffer(gl.ARRAY_BUFFER, gl.Uint(door.threshold_glids.vbuffer))
					gl.VertexPointer(3, gl.FLOAT, gl.Sizei(unsafe.Sizeof(vert)), gl.Pointer(unsafe.Offsetof(vert.x)))
					gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.Uint(door.door_glids.floor_buffer))
					gl.TexCoordPointer(2, gl.FLOAT, gl.Sizei(unsafe.Sizeof(vert)), gl.Pointer(unsafe.Offsetof(vert.u)))
					gl.ClientActiveTexture(gl.TEXTURE1)
					gl.TexCoordPointer(2, gl.FLOAT, gl.Sizei(unsafe.Sizeof(vert)), gl.Pointer(unsafe.Offsetof(vert.los_u)))
					gl.DrawElements(gl.TRIANGLES, door.door_glids.floor_count, gl.UNSIGNED_SHORT, nil)
				}
			}
			gl.StencilFunc(gl.NOTEQUAL, 1, 1)
			gl.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP)
			do_color(255, 255, 255, room.far_left.wall_alpha)

		case &right:
			gl.StencilFunc(gl.ALWAYS, 1, 1)
			gl.StencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE)
			for _, door := range room.Doors {
				if door.Facing != FarRight {
					continue
				}
				door.TextureData().Bind()
				R, G, B, A := door.Color()
				do_color(R, G, B, alphaMult(A, room.far_right.wall_alpha))
				gl.ClientActiveTexture(gl.TEXTURE0)
				door.TextureData().Bind()
				if door.door_glids.floor_buffer != 0 {
					gl.BindBuffer(gl.ARRAY_BUFFER, gl.Uint(door.threshold_glids.vbuffer))
					gl.VertexPointer(3, gl.FLOAT, gl.Sizei(unsafe.Sizeof(vert)), gl.Pointer(unsafe.Offsetof(vert.x)))
					gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.Uint(door.door_glids.floor_buffer))
					gl.TexCoordPointer(2, gl.FLOAT, gl.Sizei(unsafe.Sizeof(vert)), gl.Pointer(unsafe.Offsetof(vert.u)))
					gl.ClientActiveTexture(gl.TEXTURE1)
					gl.TexCoordPointer(2, gl.FLOAT, gl.Sizei(unsafe.Sizeof(vert)), gl.Pointer(unsafe.Offsetof(vert.los_u)))
					gl.DrawElements(gl.TRIANGLES, door.door_glids.floor_count, gl.UNSIGNED_SHORT, nil)
				}
			}
			gl.StencilFunc(gl.NOTEQUAL, 1, 1)
			gl.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP)
			do_color(255, 255, 255, room.far_right.wall_alpha)

		case &floor:
			gl.StencilFunc(gl.ALWAYS, 2, 2)
			gl.StencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE)
			do_color(255, 255, 255, 255)
		}

		gl.ClientActiveTexture(gl.TEXTURE0)
		gl.BindBuffer(gl.ARRAY_BUFFER, gl.Uint(room.vbuffer))
		gl.VertexPointer(3, gl.FLOAT, gl.Sizei(unsafe.Sizeof(vert)), gl.Pointer(unsafe.Offsetof(vert.x)))
		gl.TexCoordPointer(2, gl.FLOAT, gl.Sizei(unsafe.Sizeof(vert)), gl.Pointer(unsafe.Offsetof(vert.u)))
		gl.ClientActiveTexture(gl.TEXTURE1)
		if los_tex != nil {
			los_tex.Bind()
		}
		gl.BindBuffer(gl.ARRAY_BUFFER, gl.Uint(room.vbuffer))
		gl.TexCoordPointer(2, gl.FLOAT, gl.Sizei(unsafe.Sizeof(vert)), gl.Pointer(unsafe.Offsetof(vert.los_u)))
		// Now draw the walls
		gl.LoadMatrixf((*gl.Float)(&floor[0]))
		plane.texture.Data().Bind()
		gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.Uint(plane.index_buffer))
		if (plane.mat == &left || plane.mat == &right) && strings.Contains(string(room.Wall.Path), "gradient.png") {
			base.EnableShader("gorey")
			base.SetUniformI("gorey", "tex", 0)
			base.SetUniformI("gorey", "foo", Foo)
			base.SetUniformF("gorey", "num_rows", Num_rows)
			base.SetUniformF("gorey", "noise_rate", Noise_rate)
			base.SetUniformF("gorey", "num_steps", Num_steps)
		}
		if plane.mat == &floor && strings.Contains(string(room.Floor.Path), "gradient.png") {
			base.EnableShader("gorey")
			base.SetUniformI("gorey", "tex", 0)
			base.SetUniformI("gorey", "foo", Foo)
			base.SetUniformF("gorey", "num_rows", Num_rows)
			base.SetUniformF("gorey", "noise_rate", Noise_rate)
			base.SetUniformF("gorey", "num_steps", Num_steps)
			zexp := math.Log(float64(zoom))
			frac := 1 - 1/zexp
			frac = (frac - 0.6) * 5.0
			switch {
			case frac > 0.7:
				base.SetUniformI("gorey", "range", 1)
			case frac > 0.3:
				base.SetUniformI("gorey", "range", 2)
			default:
				base.SetUniformI("gorey", "range", 3)
			}
		}
		if plane.mat == &floor {
			R, G, B, _ := room.Color()
			gl.Color4ub(gl.Ubyte(R), gl.Ubyte(G), gl.Ubyte(B), 255)
		}
		gl.DrawElements(gl.TRIANGLES, gl.Sizei(room.floor_count), gl.UNSIGNED_SHORT, nil)
		if los_tex != nil {
			base.EnableShader("los")
		} else {
			base.EnableShader("")
		}
	}

	for _, wt := range room.WallTextures {
		if room.wall_texture_gl_map == nil {
			room.wall_texture_gl_map = make(map[*WallTexture]wallTextureGlIds)
			room.wall_texture_state_map = make(map[*WallTexture]wallTextureState)
		}
		ids := room.wall_texture_gl_map[wt]
		state := room.wall_texture_state_map[wt]
		var new_state wallTextureState
		new_state.flip = wt.Flip
		new_state.rot = wt.Rot
		new_state.x = wt.X
		new_state.y = wt.Y
		new_state.room.x = room.X
		new_state.room.y = room.Y
		new_state.room.dx = room.Size.Dx
		new_state.room.dy = room.Size.Dy
		if new_state != state {
			wt.setupGlStuff(room.X, room.Y, room.Size.Dx, room.Size.Dy, &ids)
			room.wall_texture_gl_map[wt] = ids
			room.wall_texture_state_map[wt] = new_state
		}
		gl.LoadMatrixf((*gl.Float)(&floor[0]))
		if ids.vbuffer != 0 {
			wt.Texture.Data().Bind()
			R, G, B, A := wt.Color()

			gl.ClientActiveTexture(gl.TEXTURE0)
			gl.BindBuffer(gl.ARRAY_BUFFER, gl.Uint(ids.vbuffer))
			gl.VertexPointer(3, gl.FLOAT, gl.Sizei(unsafe.Sizeof(vert)), gl.Pointer(unsafe.Offsetof(vert.x)))
			gl.TexCoordPointer(2, gl.FLOAT, gl.Sizei(unsafe.Sizeof(vert)), gl.Pointer(unsafe.Offsetof(vert.u)))
			gl.ClientActiveTexture(gl.TEXTURE1)
			gl.TexCoordPointer(2, gl.FLOAT, gl.Sizei(unsafe.Sizeof(vert)), gl.Pointer(unsafe.Offsetof(vert.los_u)))
			gl.ClientActiveTexture(gl.TEXTURE0)
			if ids.floor_buffer != 0 {
				gl.StencilFunc(gl.ALWAYS, 2, 2)
				gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.Uint(ids.floor_buffer))
				gl.Color4ub(gl.Ubyte(R), gl.Ubyte(G), gl.Ubyte(B), gl.Ubyte(A))
				gl.DrawElements(gl.TRIANGLES, ids.floor_count, gl.UNSIGNED_SHORT, nil)
			}
			if ids.left_buffer != 0 {
				gl.StencilFunc(gl.ALWAYS, 1, 1)
				gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.Uint(ids.left_buffer))
				do_color(R, G, B, alphaMult(A, room.far_left.wall_alpha))
				gl.DrawElements(gl.TRIANGLES, ids.left_count, gl.UNSIGNED_SHORT, nil)
			}
			if ids.right_buffer != 0 {
				gl.StencilFunc(gl.ALWAYS, 1, 1)
				gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.Uint(ids.right_buffer))
				do_color(R, G, B, alphaMult(A, room.far_right.wall_alpha))
				gl.DrawElements(gl.TRIANGLES, ids.right_count, gl.UNSIGNED_SHORT, nil)
			}
		}
	}
	base.EnableShader("marble")
	base.SetUniformI("marble", "tex2", 1)
	base.SetUniformF("marble", "room_x", float32(room.X))
	base.SetUniformF("marble", "room_y", float32(room.Y))
	for _, door := range room.Doors {
		door.setupGlStuff(room)
		if door.threshold_glids.vbuffer == 0 {
			continue
		}
		if door.AlwaysOpen() {
			continue
		}
		if door.highlight_threshold {
			gl.Color4ub(255, 255, 255, 255)
		} else {
			gl.Color4ub(128, 128, 128, 255)
		}
		gl.BindBuffer(gl.ARRAY_BUFFER, gl.Uint(door.threshold_glids.vbuffer))
		gl.VertexPointer(3, gl.FLOAT, gl.Sizei(unsafe.Sizeof(vert)), gl.Pointer(unsafe.Offsetof(vert.x)))
		gl.TexCoordPointer(2, gl.FLOAT, gl.Sizei(unsafe.Sizeof(vert)), gl.Pointer(unsafe.Offsetof(vert.u)))
		gl.ClientActiveTexture(gl.TEXTURE1)
		gl.TexCoordPointer(2, gl.FLOAT, gl.Sizei(unsafe.Sizeof(vert)), gl.Pointer(unsafe.Offsetof(vert.los_u)))
		gl.ClientActiveTexture(gl.TEXTURE0)
		gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.Uint(door.threshold_glids.floor_buffer))
		gl.DrawElements(gl.TRIANGLES, door.threshold_glids.floor_count, gl.UNSIGNED_SHORT, nil)
	}
	base.EnableShader("")
	if los_tex != nil {
		base.EnableShader("")
		gl.ActiveTexture(gl.TEXTURE1)
		gl.Disable(gl.TEXTURE_2D)
		gl.ActiveTexture(gl.TEXTURE0)
		gl.ClientActiveTexture(gl.TEXTURE1)
		gl.DisableClientState(gl.TEXTURE_COORD_ARRAY)
		gl.ClientActiveTexture(gl.TEXTURE0)
	}

	run.Assign(&floor)
	mul.Translation(float32(-room.X), float32(-room.Y), 0)
	run.Multiply(&mul)
	gl.LoadMatrixf((*gl.Float)(&run[0]))
	gl.StencilFunc(gl.EQUAL, 2, 3)
	gl.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP)
	room_rect := image.Rect(room.X, room.Y, room.X+room.Size.Dx, room.Y+room.Size.Dy)
	for _, fd := range floor_drawers {
		x, y := fd.Pos()
		dx, dy := fd.Dims()
		if room_rect.Overlaps(image.Rect(x, y, x+dx, y+dy)) {
			fd.RenderOnFloor()
		}
	}

	do_color(255, 255, 255, 255)
	gl.LoadIdentity()
	gl.Disable(gl.STENCIL_TEST)
	room.renderFurniture(floor, 255, drawables, los_tex)

	gl.ClientActiveTexture(gl.TEXTURE1)
	gl.Disable(gl.TEXTURE_2D)
	gl.ClientActiveTexture(gl.TEXTURE0)
	base.EnableShader("")
}