Example #1
0
func viewMatrix(pos world.Position) (glm.Matrix4, glm.Matrix4) {
	const eyeZ = .5
	Rd := glm.RotZ(float64(-90 * pos.F.Value()))
	Td := glm.Vector3{float64(-pos.X), float64(-pos.Y), -eyeZ}.Translation()
	Ri := glm.RotZ(float64(90 * pos.F.Value()))
	Ti := glm.Vector3{float64(pos.X), float64(pos.Y), eyeZ}.Translation()
	Vd := Rd.Mult(Td) // Direct.
	Vi := Ti.Mult(Ri) // Inverse.
	return Vd, Vi
}
Example #2
0
func gatherBuildingsPositions(
	rendererPositions map[world.ModelId]Positions,
	buildings world.Buildings,
	offsetX, offsetY float64,
	defaultR *glm.Matrix4, // Can be nil.
	worldToEye glm.Matrix4,
) {
	for coords, building := range buildings {
		position := glm.Vector3{
			float64(coords.X) + offsetX,
			float64(coords.Y) + offsetY,
			0,
		}.Translation()
		facer, ok := building.(world.Facer)
		if ok {
			// We obey the facing of the buildings that have one.
			r := glm.RotZ(float64(90 * facer.Facing().Value()))
			position = position.Mult(r)
		} else {
			// Buildings without facing receive the provided default facing.
			// It is given as a precalculated rotation matrix `defaultR`.
			position = position.Mult(*defaultR)
		}
		position = worldToEye.Mult(position) // Shaders work in view space.
		rendererID := building.Model()
		positions := rendererPositions[rendererID]
		positions = append(positions, position)
		rendererPositions[rendererID] = positions
	}
}
Example #3
0
func main() {
	var programState programState
	var err error
	glfw.SetErrorCallback(errorCallback)

	if !glfw.Init() {
		panic("GLFW initialization failed.")
	}
	defer glfw.Terminate()

	glfw.WindowHint(glfw.ContextVersionMajor, 3)
	glfw.WindowHint(glfw.ContextVersionMinor, 3)
	glfw.WindowHint(glfw.SrgbCapable, glfw.True)
	glfw.WindowHint(glfw.Resizable, glfw.False)
	programState.Gl.Window, err = glfw.CreateWindow(640, 480, "Daggor", nil, nil)
	if err != nil {
		panic(err)
	}
	defer programState.Gl.Window.Destroy()

	programState.Gl.glfwKeyEventList = makeGlfwKeyEventList()
	programState.Gl.Window.SetKeyCallback(programState.Gl.glfwKeyEventList.Callback)

	programState.Gl.Window.MakeContextCurrent()
	if ec := gl.Init(); ec != 0 {
		panic(fmt.Sprintf("OpenGL initialization failed with code %v.", ec))
	}
	// For some reason, here, the OpenGL error flag for me contains "Invalid enum".
	// This is weird since I have not done anything yet.  I imagine that something
	// goes wrong in gl.Init.  Reading the error flag clears it, so I do it.
	// Here's the reason:
	//     https://github.com/go-gl/glfw3/issues/50
	// Maybe I should not even ask for a core profile anyway.
	// What are the advantages are asking for a core profile?
	if err := glw.CheckGlError(); err != nil {
		err.Description = "OpenGL has this error right after init for some reason."
		//fmt.Println(err)
	}
	{
		// Assert OpenGL >= 3.3.
		major := programState.Gl.Window.GetAttribute(glfw.ContextVersionMajor)
		minor := programState.Gl.Window.GetAttribute(glfw.ContextVersionMinor)
		fmt.Printf("OpenGL version %v.%v.\n", major, minor)
		if (major < 3) || (major == 3 && minor < 3) {
			panic("OpenGL version 3.3 required, your video card/driver does not seem to support it.")
		}
	}

	programState.Gl.context = glw.NewGlContext()

	programState.Gl.Shapes[floorID] = sculpt.FloorInstNorm(programState.Gl.context.Programs)
	programState.Gl.Shapes[ceilingID] = sculpt.CeilingInstNorm(programState.Gl.context.Programs)
	programState.Gl.Shapes[wallID] = sculpt.WallInstNorm(programState.Gl.context.Programs)

	{
		// I do not like the default reference frame of OpenGl.
		// By default, we look in the direction -z, and y points up.
		// I want z to point up, and I want to look in the direction +x
		// by default.  That way, I move on an xy plane where z is the
		// altitude, instead of having the altitude stuffed between
		// the two things I use the most.  And my reason for pointing
		// toward +x is that I use the convention for trigonometry:
		// an angle of 0 points to the right (east) of the trigonometric
		// circle.  Bonus point: this matches Blender's reference frame.
		myFrame := glm.ZUP.Mult(glm.RotZ(90))
		eye_to_clip := glm.PerspectiveProj(110, 640./480., .1, 100).Mult(myFrame)
		programState.Gl.context.SetEyeToClp(eye_to_clip)
	}
	gl.Enable(gl.FRAMEBUFFER_SRGB)
	gl.Enable(gl.DEPTH_TEST)
	gl.Enable(gl.CULL_FACE)
	gl.CullFace(gl.BACK)
	gl.FrontFace(gl.CCW)
	gl.Enable(gl.TEXTURE_CUBE_MAP_SEAMLESS)

	// This needs a texture server attached to the context, like for programs.
	glw.LoadSkybox()

	programState.World = world.MakeWorld()
	mainLoop(programState)
}
Example #4
0
func render(programState programState) {
	actorID := programState.World.Player_id
	position, ok := programState.World.Level.ActorPosition(actorID)
	if !ok {
		panic("Could not find player's character position.")
	}
	worldToEye, eyeToWorld := viewMatrix(position)
	programState.Gl.context.SetEyeToWld(eyeToWorld)
	programState.Gl.context.UpdateCamera()

	// Lights.
	{
		const T = 3000000000
		t := programState.World.Time
		phase := (2 * math.Pi) * float64(t) / T
		lights := []glw.Light{
			glw.Light{
				Color: glm.Vector4{1, 0, 0, 0},
				Origin: worldToEye.MultV(glm.Vector4{
					3 + 2*math.Cos(phase),
					0 + 1*math.Sin(phase),
					.5 + .45*math.Cos(phase*6.321), 1,
				}),
			},
			glw.Light{
				Color:  glm.Vector4{0, .5, 0, 0},
				Origin: worldToEye.MultV(glm.Vector4{0, 0, .1, 1}),
			},
		}
		programState.Gl.context.SetLights(lights)
		programState.Gl.context.UpdateLights()
	}

	// Geometry.
	verticalPositions := make(map[world.ModelId]Positions)
	horizontalPositions := make(map[world.ModelId]Positions)

	gatherBuildingsPositions(
		horizontalPositions,
		programState.World.Level.Floors,
		0, 0,
		nil,
		worldToEye,
	)
	gatherBuildingsPositions(
		horizontalPositions,
		programState.World.Level.Ceilings,
		0, 0,
		nil,
		worldToEye,
	)
	for i := 0; i < 4; i++ {
		rot := glm.RotZ(180 + 90*float64(i))
		gatherBuildingsPositions(
			verticalPositions,
			programState.World.Level.Walls[i],
			0, 0,
			&rot,
			worldToEye,
		)
	}
	// Finally render all the things.
	// Reduce fill rate by drawing the closest objects first and making use of
	// the depth test to cull fragments before expensive lightings computations.
	gl.ClearColor(0.0, 0.0, 0.4, 0.0)
	gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
	for rendererID, pos := range verticalPositions {
		sort.Sort(pos)
		programState.Gl.Shapes[rendererID].Render(pos)
	}
	for rendererID, pos := range horizontalPositions {
		sort.Sort(pos)
		programState.Gl.Shapes[rendererID].Render(pos)
	}
}