示例#1
0
文件: maps.go 项目: walesey/go-engine
func loadMapToNode(model *editorModels.NodeModel) chan MapLoadUpdate {

	out := make(chan MapLoadUpdate)
	geomsLoaded := 0

	var updateNode func(srcModel *editorModels.NodeModel, destNode *renderer.Node)
	updateNode = func(srcModel *editorModels.NodeModel, destNode *renderer.Node) {
		srcModel.SetNode(destNode)
		if srcModel.Geometry != nil {
			geometry, material, err := assets.ImportObjCached(*srcModel.Geometry)
			if err == nil {
				destNode.Material = material
				destNode.Add(geometry)
			}
			geomsLoaded++
			out <- MapLoadUpdate{nil, geomsLoaded}
		}
		destNode.SetScale(srcModel.Scale)
		destNode.SetTranslation(srcModel.Translation)
		destNode.SetOrientation(srcModel.Orientation)
		if srcModel.Reference != nil {
			if refModel, _ := findNodeById(*srcModel.Reference, model); refModel != nil {
				for _, childModel := range refModel.Children {
					refNode := childModel.GetNode()
					if refNode != nil {
						destNode.Add(refNode)
					}
				}
			}
		}
		for _, childModel := range srcModel.Children {
			newNode := renderer.NewNode()
			destNode.Add(newNode)
			updateNode(childModel, newNode)
		}
	}

	node := renderer.NewNode()
	go func() {
		updateNode(model, node)
		out <- MapLoadUpdate{node, geomsLoaded}
	}()
	return out
}
示例#2
0
func (e *Editor) initSelectSprite() {
	img, _ := assets.DecodeImage(bytes.NewBuffer(util.Base64ToBytes(GeometryIconData)))
	mat := renderer.NewMaterial(renderer.NewTexture("diffuseMap", img, false))
	selectSprite := effects.CreateSprite(1, 1, 1, mat)
	spriteNode := renderer.NewNode()
	spriteNode.RendererParams = &renderer.RendererParams{Unlit: true}
	spriteNode.Add(selectSprite)
	e.selectSprite = selectSprite
	e.gameEngine.AddSpatialTransparent(spriteNode)
}
示例#3
0
func NewWindow() *Window {
	node := renderer.NewNode()
	elementNode := renderer.NewNode()
	background := renderer.NewNode()
	background.Material = renderer.NewMaterial()
	box := renderer.CreateBoxWithOffset(1, 1, 0, 0)
	box.SetColor(color.NRGBA{255, 255, 255, 255})
	background.Add(box)
	node.Add(background)
	node.Add(elementNode)
	return &Window{
		node:          node,
		backgroundBox: box,
		background:    background,
		elementNode:   elementNode,
		size:          mgl32.Vec2{500, 1},
		Tabs:          []Activatable{},
	}
}
示例#4
0
func NewParticleGroup(camera *renderer.Camera, particles ...*ParticleSystem) *ParticleGroup {
	node := renderer.NewNode()
	for _, particle := range particles {
		node.Add(particle)
	}
	return &ParticleGroup{
		Node:      node,
		camera:    camera,
		particles: particles,
	}
}
示例#5
0
func NewImageElement(img image.Image) *ImageElement {
	imageElement := &ImageElement{
		rotation: 0,
		Hitbox:   NewHitbox(),
		node:     renderer.NewNode(),
	}
	box := renderer.CreateBoxWithOffset(1, 1, 0, 0)
	imageElement.node.Add(box)
	imageElement.SetImage(img)
	return imageElement
}
示例#6
0
func NewContainer() *Container {
	node := renderer.NewNode()
	elementsNode := renderer.NewNode()
	background := renderer.NewNode()
	box := renderer.CreateBoxWithOffset(1, 1, 0, 0)
	box.SetColor(color.NRGBA{0, 0, 0, 0})
	background.Material = renderer.NewMaterial()
	background.Add(box)
	node.Add(background)
	node.Add(elementsNode)
	return &Container{
		node:          node,
		elementsNode:  elementsNode,
		background:    background,
		backgroundBox: box,
		children:      make([]Element, 0),
		Hitbox:        NewHitbox(),
		padding:       NewMargin(0),
		margin:        NewMargin(0),
	}
}
示例#7
0
func New(assetDir string) *Editor {
	return &Editor{
		assetDir:    assetDir,
		uiAssets:    ui.NewHtmlAssets(),
		rootMapNode: renderer.NewNode(),
		currentMap: &editorModels.MapModel{
			Name: "default",
			Root: editorModels.NewNodeModel("root"),
		},
		mouseMode: "scale",
	}
}
示例#8
0
func (loader *Loader) LoadMap(path string, callback func(node *renderer.Node, model *editorModels.NodeModel)) {
	go func() {
		srcModel := LoadMap(path)
		destNode := renderer.NewNode()
		loadedModel := LoadMapToNode(srcModel.Root, destNode)
		loader.maps <- mapImport{
			node:     destNode,
			model:    loadedModel,
			callback: callback,
		}
	}()
}
示例#9
0
func CreateSprite(totalFrames, framesX, framesY int, material *renderer.Material) *Sprite {
	sprite := Sprite{
		frame:       0,
		FaceCamera:  true,
		totalFrames: totalFrames,
		framesX:     framesX,
		framesY:     framesY,
	}
	geometry := renderer.CreateBox(1, 1)
	sprite.geometry = geometry
	sprite.Node = renderer.NewNode()
	sprite.Node.Material = material
	sprite.Node.Add(sprite.geometry)
	return &sprite
}
示例#10
0
func CreateParticleSystem(settings ParticleSettings) *ParticleSystem {
	geometry := renderer.CreateGeometry(make([]uint32, 0, 0), make([]float32, 0, 0))
	node := renderer.NewNode()
	node.Add(geometry)

	ps := ParticleSystem{
		Settings:   settings,
		FaceCamera: true,
		Node:       node,
		geometry:   geometry,
		particles:  make([]*Particle, settings.MaxParticles),
	}
	ps.initParitcles()
	return &ps
}
示例#11
0
文件: text.go 项目: walesey/go-engine
func NewTextElement(text string, textColor color.Color, textSize float32, textFont *truetype.Font) *TextElement {
	img := NewImageElement(image.NewAlpha(image.Rect(0, 0, 1, 1)))
	node := renderer.NewNode()
	node.Add(img.Spatial())
	textElem := &TextElement{
		img:  img,
		node: node,
		props: textProps{
			text:      text,
			textColor: textColor,
			textSize:  textSize,
			textFont:  textFont,
		},
	}
	if textFont == nil {
		defaultFont, _ := LoadFont(getDefaultFont())
		textElem.SetFont(defaultFont)
	}
	return textElem
}
示例#12
0
文件: maps.go 项目: walesey/go-engine
func loadMapRecursive(model, srcModel *editorModels.NodeModel, destNode *renderer.Node) {
	model.SetNode(destNode)
	if model.Geometry != nil {
		geometry, material, err := ImportObjCached(*model.Geometry)
		if err == nil {
			destNode.Add(geometry)
			destNode.Material = material
		}
	}
	destNode.SetScale(model.Scale)
	destNode.SetTranslation(model.Translation)
	destNode.SetOrientation(model.Orientation)
	if model.Reference != nil {
		copyRef(model, srcModel)
	}
	for _, childModel := range model.Children {
		newNode := renderer.NewNode()
		destNode.Add(newNode)
		loadMapRecursive(childModel, srcModel, newNode)
	}
}
示例#13
0
func NewTextField(text string, textColor color.Color, textSize float32, textFont *truetype.Font) *TextField {
	cursor := renderer.CreateBoxWithOffset(0.07, 1.1, 0, 0.1)
	cursor.SetColor(color.NRGBA{0, 0, 0, 255})
	cursorNode := renderer.NewNode()
	cursorNode.Material = renderer.NewMaterial()
	cursorNode.Add(cursor)
	tf := &TextField{
		container: NewContainer(),
		text:      NewTextElement(text, textColor, textSize, textFont),
		cursor:    cursorNode,
		cursorPos: len(text),
	}
	tf.text.node.Add(cursorNode)
	tf.container.AddChildren(tf.text)
	tf.GetHitbox().AddOnClick(func(button int, release bool, position mgl32.Vec2) {
		if !release {
			tf.Activate()
		}
	})
	tf.SetBackgroundColor(0, 0, 0, 0)
	tf.SetHeight(textSize * 1.5)
	tf.SetPadding(NewMargin(2))
	return tf
}
示例#14
0
文件: main.go 项目: walesey/go-engine
func main() {
	server := len(os.Args) > 1 && os.Args[1] == "server"
	var gameEngine engine.Engine
	var glRenderer *opengl.OpenglRenderer
	network := networking.NewNetwork()

	// Start server or connect to server
	if server {
		gameEngine = engine.NewHeadlessEngine()
		network.StartServer(serverPort)
	} else {
		glRenderer = opengl.NewOpenglRenderer("Networking example", 800, 800, false)
		gameEngine = engine.NewEngine(glRenderer)
		network.ConnectClient(fmt.Sprintf("%v:%v", serverAddr, serverPort))
	}
	gameEngine.AddUpdatable(network)

	// map containing each player's entity
	players := make(map[string]*Player)

	//Networked Game events
	network.ClientJoinedEvent(func(clientId string) {
		fmt.Println("client joined, clientId: ", clientId)
		network.TriggerOnServerAndClients("spawn", util.SerializeArgs(clientId, startingPosition))
		for _, player := range players {
			network.TriggerEvent("spawn", clientId, util.SerializeArgs(player.clientId, player.position))
		}
	})

	network.RegisterEvent("spawn", func(clientId string, data []byte) {
		buf := bytes.NewBuffer(data)
		playerID := util.Stringfrombytes(buf)
		position := util.Vector2frombytes(buf)
		if _, ok := players[playerID]; !ok {
			player := &Player{clientId: playerID}
			players[player.clientId] = player
			player.node = renderer.NewNode()
			player.position = position
			gameEngine.AddUpdatable(player)
			if network.IsClient() {
				boxGeometry := renderer.CreateBox(30, 30)
				boxGeometry.SetColor(color.NRGBA{254, 0, 0, 254})
				player.node.Add(boxGeometry)
				gameEngine.AddOrtho(player.node)
			}
		}
	})

	network.RegisterEvent("move", func(clientId string, data []byte) {
		buf := bytes.NewBuffer(data)
		playerID := util.Stringfrombytes(buf)
		velocity := util.Vector2frombytes(buf)
		if network.IsServer() && clientId != playerID {
			return // client is only allowed to control the player assigned to them.
		}
		if player, ok := players[playerID]; ok {
			player.velocity = velocity
			if network.IsServer() {
				network.BroadcastEvent("updatePlayer", util.SerializeArgs(playerID, player.position, player.velocity))
				network.FlushAllWriteBuffers()
			}
		}
	})

	network.RegisterEvent("updatePlayer", func(clientId string, data []byte) {
		if network.IsClient() { // This is a server to client update only
			buf := bytes.NewBuffer(data)
			playerID := util.Stringfrombytes(buf)
			position := util.Vector2frombytes(buf)
			velocity := util.Vector2frombytes(buf)
			if player, ok := players[playerID]; ok {
				player.position = position
				player.velocity = velocity
			}
		}
	})

	// client setup
	gameEngine.Start(func() {
		if network.IsClient() {
			gameEngine.InitFpsDial()

			if shader, err := assets.ImportShader("shaders/build/basic.vert", "shaders/build/basic.frag"); err == nil {
				gameEngine.DefaultShader(shader)
			}

			glRenderer.BackGroundColor(0, 0.4, 0, 0)

			// input/controller manager
			controllerManager := glfwController.NewControllerManager(glRenderer.Window)

			// networked movement controls
			move := func(velocity mgl32.Vec2) {
				network.TriggerOnServerAndClients("move", util.SerializeArgs(network.ClientToken(), velocity))
			}

			customController := controller.CreateController()
			controllerManager.AddController(customController.(glfwController.Controller))
			customController.BindKeyAction(func() { move(mgl32.Vec2{0, -100}) }, controller.KeyW, controller.Press)
			customController.BindKeyAction(func() { move(mgl32.Vec2{-100, 0}) }, controller.KeyA, controller.Press)
			customController.BindKeyAction(func() { move(mgl32.Vec2{0, 100}) }, controller.KeyS, controller.Press)
			customController.BindKeyAction(func() { move(mgl32.Vec2{100, 0}) }, controller.KeyD, controller.Press)
			customController.BindKeyAction(func() { move(mgl32.Vec2{}) }, controller.KeyW, controller.Release)
			customController.BindKeyAction(func() { move(mgl32.Vec2{}) }, controller.KeyA, controller.Release)
			customController.BindKeyAction(func() { move(mgl32.Vec2{}) }, controller.KeyS, controller.Release)
			customController.BindKeyAction(func() { move(mgl32.Vec2{}) }, controller.KeyD, controller.Release)
		}
	})
}
示例#15
0
func (e *Editor) Start() {

	glRenderer := opengl.NewOpenglRenderer("GoEngine Editor", 1800, 900, false)
	e.renderer = glRenderer
	e.gameEngine = engine.NewEngine(e.renderer)

	e.gameEngine.Start(func() {

		shader, err := assets.ImportShader(
			filepath.Join(e.assetDir, "shaders/build/pbr.vert"),
			filepath.Join(e.assetDir, "shaders/build/pbr.frag"),
		)
		if err != nil {
			panic("error importing shader")
		}

		e.gameEngine.DefaultShader(shader)

		// Sky cubemap
		skyImg, err := assets.ImportImage(filepath.Join(e.assetDir, "resources/cubemap.png"))
		if err == nil {
			geom := renderer.CreateSkyBox()
			geom.Transform(mgl32.Scale3D(10000, 10000, 10000))
			skyNode := renderer.NewNode()
			skyNode.SetOrientation(mgl32.QuatRotate(1.57, mgl32.Vec3{0, 1, 0}))
			skyNode.Material = renderer.NewMaterial(renderer.NewTexture("diffuseMap", skyImg, false))
			skyNode.RendererParams = renderer.NewRendererParams()
			skyNode.RendererParams.CullBackface = false
			skyNode.RendererParams.Unlit = true
			skyNode.Add(geom)
			e.gameEngine.AddSpatial(skyNode)
			// create an environmentMap using the skybox texture
			envCubeMap := renderer.NewCubemap("environmentMap", skyImg, true)
			e.gameEngine.DefaultCubeMap(envCubeMap)
		}

		l := renderer.NewLight(renderer.DIRECTIONAL)
		l.Color = [3]float32{0.7, 0.7, 0.8}
		e.gameEngine.AddLight(l)

		//root node
		e.gameEngine.AddSpatial(e.rootMapNode)

		//input/controller manager
		e.controllerManager = glfwController.NewControllerManager(glRenderer.Window)

		//camera + player
		camera := e.gameEngine.Camera()
		freeMoveActor := actor.NewFreeMoveActor(camera)
		freeMoveActor.MoveSpeed = 20.0
		freeMoveActor.LookSpeed = 0.002
		mainController := controller.NewBasicMovementController(freeMoveActor, true)
		e.controllerManager.AddController(mainController.(glfwController.Controller))
		e.gameEngine.AddUpdatable(freeMoveActor)

		e.initSelectSprite()
		e.gameEngine.AddUpdatable(engine.UpdatableFunc(e.updateSelectSprite))

		//editor controller
		e.controllerManager.AddController(NewEditorController(e).(glfwController.Controller))

		//custom controller
		e.customController = controller.CreateController()
		e.controllerManager.AddController(e.customController.(glfwController.Controller))

		e.setupUI()
	})
}
示例#16
0
文件: main.go 项目: walesey/go-engine
func main() {

	//renderer and game engine
	glRenderer := opengl.NewOpenglRenderer("Platformer", 800, 800, false)
	gameEngine := engine.NewEngine(glRenderer)
	gameEngine.InitFpsDial()

	// physics engine (Chipmonk)
	physicsSpace := chipmunkPhysics.NewChipmonkSpace()
	physicsSpace.SetGravity(mgl32.Vec2{0, 400})
	gameEngine.AddUpdatable(physicsSpace)

	gameEngine.Start(func() {
		glRenderer.BackGroundColor(0.7, 0.7, 0.9, 0.0)

		// load in default shader
		shader := renderer.NewShader()
		shader.FragSrc = fragShader
		shader.VertSrc = vertShader
		gameEngine.DefaultShader(shader)

		// The player object
		character := NewCharacter()
		character.body.SetPosition(mgl32.Vec2{400, 400})

		// Add the character to all the things
		physicsSpace.AddBody(character.body)
		gameEngine.AddOrtho(character.sprite)
		gameEngine.AddUpdatable(character)

		// terrain
		terrainGeometry := renderer.CreateBoxWithOffset(800, 800-floorHeight, 0, floorHeight)
		terrainGeometry.SetColor(color.NRGBA{0, 80, 0, 254})
		gameEngine.AddOrtho(terrainGeometry)

		// terrain physics body
		terrainBody := chipmunkPhysics.NewChipmunkBodyStatic()
		segment := chipmunk.NewSegment(vect.Vect{0, floorHeight}, vect.Vect{4000, floorHeight}, 0)
		terrainBody.Body.AddShape(segment)
		physicsSpace.AddBody(terrainBody)

		particleNode := renderer.NewNode()
		gameEngine.AddOrtho(particleNode)

		// create a new timed particle system
		spawnParticles := func(load func() *effects.ParticleGroup, position mgl32.Vec2) {
			particleGroup := load()
			particleGroup.SetTranslation(position.Vec3(0))
			effects.TriggerTimedParticleGroup(
				effects.TimedParticleGroup{
					Particles:  particleGroup,
					GameEngine: gameEngine,
					TargetNode: particleNode,
					Life:       0.2,
					Cleanup:    10,
				},
			)
		}

		// input/controller manager
		controllerManager := glfwController.NewControllerManager(glRenderer.Window)

		// key bindings
		customController := controller.CreateController()
		controllerManager.AddController(customController.(glfwController.Controller))

		// Walk
		customController.BindKeyAction(func() {
			character.body.SetVelocity(mgl32.Vec2{-200, character.body.GetVelocity().Y()})
		}, controller.KeyA, controller.Press)
		customController.BindKeyAction(func() {
			character.body.SetVelocity(mgl32.Vec2{200, character.body.GetVelocity().Y()})
		}, controller.KeyD, controller.Press)

		//Stop walking
		customController.BindKeyAction(func() {
			character.body.SetVelocity(mgl32.Vec2{0, character.body.GetVelocity().Y()})
		}, controller.KeyA, controller.Release)
		customController.BindKeyAction(func() {
			character.body.SetVelocity(mgl32.Vec2{0, character.body.GetVelocity().Y()})
		}, controller.KeyD, controller.Release)

		// Jump
		customController.BindKeyAction(func() {
			character.body.SetVelocity(character.body.GetVelocity().Add(mgl32.Vec2{0, -300}))
			spawnParticles(dustParticles, character.body.GetPosition().Add(mgl32.Vec2{0, 0.5 * characterSize}))
		}, controller.KeySpace, controller.Press)

		// shoot
		customController.BindMouseAction(func() {
			spawnParticles(majicParticles, character.body.GetPosition())
		}, controller.MouseButtonLeft, controller.Press)

		// create sparks when player collides with somthing
		var collisionTimer time.Time
		physicsSpace.SetOnCollision(func(shapeA, shapeB *chipmunk.Shape) {
			if time.Since(collisionTimer) > 200*time.Millisecond {
				spawnParticles(sparkParticles, character.body.GetPosition().Add(mgl32.Vec2{0, 0.5 * characterSize}))
			}
			collisionTimer = time.Now()
		})
	})
}
示例#17
0
文件: main.go 项目: walesey/go-engine
func setupScene(gameEngine engine.Engine, shader *renderer.Shader) {
	camera := gameEngine.Camera()

	transparentNode := renderer.NewNode()
	gameEngine.AddSpatialTransparent(transparentNode)
	transparentNode.RendererParams = &renderer.RendererParams{
		DepthTest:    true,
		Unlit:        true,
		Transparency: renderer.EMISSIVE,
	}

	// Sky cubemap
	skyImg, err := assets.ImportImage("TestAssets/cloudSky.jpg")
	if err == nil {
		skyImg = imaging.AdjustBrightness(skyImg, -30)
		skyImg = imaging.AdjustContrast(skyImg, 30)
		geom := renderer.CreateSkyBox()
		geom.Transform(mgl32.Scale3D(10000, 10000, 10000))
		skyNode := renderer.NewNode()
		skyNode.SetOrientation(mgl32.QuatRotate(1.57, mgl32.Vec3{0, 1, 0}))
		skyNode.Material = renderer.NewMaterial(renderer.NewTexture("diffuseMap", skyImg, false))
		skyNode.RendererParams = renderer.NewRendererParams()
		skyNode.RendererParams.CullBackface = false
		skyNode.RendererParams.Unlit = true
		skyNode.Add(geom)
		gameEngine.AddSpatial(skyNode)
		// create an environmentMap using the skybox texture
		envCubeMap := renderer.NewCubemap("environmentMap", skyImg, true)
		gameEngine.DefaultCubeMap(envCubeMap)
	}

	// load scene objs
	objs := []string{
		"TestAssets/wellScene/floor.obj",
		"TestAssets/wellScene/frame1.obj",
		"TestAssets/wellScene/frame2.obj",
		"TestAssets/wellScene/well.obj",
		"TestAssets/wellScene/torches.obj",
	}
	for _, objFile := range objs {
		if geom, mat, err := assets.ImportObjCached(objFile); err == nil {
			sceneNode := renderer.NewNode()
			sceneNode.Add(geom)
			sceneNode.Material = mat
			sceneNode.RendererParams = renderer.NewRendererParams()
			sceneNode.RendererParams.CullBackface = false
			gameEngine.AddSpatial(sceneNode)
		}
	}

	for i := 0; i < 2; i++ {
		torchLocation := mgl32.Vec3{0.86, 1.76, 1.05}
		if i == 1 {
			torchLocation = mgl32.Vec3{0.86, 1.76, -1.05}
		}

		fire := fireParticles()
		spark := sparkParticles()
		torchParticles := effects.NewParticleGroup(camera, fire, spark)
		torchParticles.SetTranslation(torchLocation)
		transparentNode.Add(torchParticles)
		gameEngine.AddUpdatable(torchParticles)

		light := renderer.NewLight(renderer.POINT)
		light.SetTranslation(torchLocation.Add(mgl32.Vec3{0, 0.05, 0}))
		gameEngine.AddLight(light)

		var x float64
		gameEngine.AddUpdatable(engine.UpdatableFunc(func(dt float64) {
			x += dt
			mag := float32(math.Abs(0.6*math.Sin(3*x)+0.3*math.Sin(4*x)+0.15*math.Sin(7*x)+0.1*math.Sin(15*x))) + 0.5
			mag *= 0.05
			light.Color = [3]float32{1 * mag, 0.6 * mag, 0.4 * mag}
		}))
	}
}
示例#18
0
func (engine *EngineImpl) initNodes() {
	engine.opaqueNode, engine.transparentNode, engine.orthoNode = renderer.NewNode(), renderer.NewNode(), renderer.NewNode()
}
示例#19
0
文件: main.go 项目: walesey/go-engine
func main() {

	glRenderer := opengl.NewOpenglRenderer("Simple", 800, 800, false)
	gameEngine := engine.NewEngine(glRenderer)
	gameEngine.InitFpsDial()

	gameEngine.Start(func() {

		if shader, err := assets.ImportShader("shaders/build/basic.vert", "shaders/build/basic.frag"); err == nil {
			gameEngine.DefaultShader(shader)
		}

		// sky cube
		skyImg, err := assets.ImportImage("resources/cubemap.png")
		if err == nil {
			geom := renderer.CreateSkyBox()
			geom.Transform(mgl32.Scale3D(10000, 10000, 10000))
			skyNode := renderer.NewNode()
			skyNode.SetOrientation(mgl32.QuatRotate(1.57, mgl32.Vec3{0, 1, 0}))
			skyNode.Material = renderer.NewMaterial(renderer.NewTexture("diffuseMap", skyImg, false))
			skyNode.RendererParams = renderer.NewRendererParams()
			skyNode.RendererParams.CullBackface = false
			skyNode.RendererParams.Unlit = true
			skyNode.Add(geom)
			gameEngine.AddSpatial(skyNode)
		}

		// Add some light to the scene
		ambientLight := renderer.NewLight(renderer.AMBIENT)
		ambientLight.Color = [3]float32{0.3, 0.3, 0.3}
		gameEngine.AddLight(ambientLight)

		// Create a red box geometry, attach to a node, add the node to the scenegraph
		boxGeometry := renderer.CreateBox(10, 10)
		boxGeometry.SetColor(color.NRGBA{254, 0, 0, 254})
		boxNode := renderer.NewNode()
		boxNode.RendererParams = renderer.NewRendererParams()
		boxNode.RendererParams.CullBackface = false
		boxNode.Material = renderer.NewMaterial()
		boxNode.SetTranslation(mgl32.Vec3{30, 0})
		boxNode.Add(boxGeometry)
		gameEngine.AddSpatial(boxNode)

		// make the box spin
		var angle float64
		gameEngine.AddUpdatable(engine.UpdatableFunc(func(dt float64) {
			angle += dt
			q := mgl32.QuatRotate(float32(angle), mgl32.Vec3{0, 1, 0})
			boxNode.SetOrientation(q)
		}))

		// input/controller manager
		controllerManager := glfwController.NewControllerManager(glRenderer.Window)

		// camera + wasd controls
		camera := gameEngine.Camera()
		freeMoveActor := actor.NewFreeMoveActor(camera)
		freeMoveActor.Location = mgl32.Vec3{}
		mainController := controller.NewBasicMovementController(freeMoveActor, false)
		controllerManager.AddController(mainController.(glfwController.Controller))
		gameEngine.AddUpdatable(freeMoveActor)

		//lock the cursor
		glRenderer.LockCursor(true)

		// custom key bindings
		customController := controller.CreateController()
		controllerManager.AddController(customController.(glfwController.Controller))

		// close window and exit on escape
		customController.BindKeyAction(func() {
			glRenderer.Window.SetShouldClose(true)
		}, controller.KeyEscape, controller.Press)
	})
}