local state = lib "stager" local game = state:new("Game") local global = require "global" local config = require "conf" local util = include "util" local flux = lib "flux" local bounds local world local skybox local sandMaterial local mapMaterial local objects local groundOffset = -0.1 local fogScale = 2 local transitions function game:load() transitions = {} bounds = (class "Bounds"):new({ x = 0, y = 1, z = 0, width = 21, height = 0.9, depth = 21 }) world = lovr.physics.newWorld(0, -9.81, 0, true, {}) world:setSleepingAllowed(false) -- Create the ground lovr.graphics.setColor(1, 1, 1, 1) world:newBoxCollider(0, bounds:getHeight(), 0, 50, 0.05, 50):setKinematic(true) local mapTexture = lovr.graphics.newTexture("images/graph.jpg") local sandTexture = lovr.graphics.newTexture("images/sand.jpg") sandMaterial = lovr.graphics.newMaterial(sandTexture) sandMaterial:setTransform(0, 0, math.log(bounds:getWidth()) * 5, math.log(bounds:getDepth()) * 5, 0) mapMaterial = lovr.graphics.newMaterial(mapTexture) mapMaterial:setTransform(0, 0, 1, 1, 0) game.fishModel = (class "TransformedModel"):new({ model = lovr.graphics.newModel("models/cellulo/cellulo.glb"),--/Fish3_out/Fish3.gltf"), x = 0, y = 0, z = 0, scale = 0.025,---0.03, ax=0.9351131265201407,ay= -0.2505628071060503 ,az=0.2505628071060503, angle=1.6378367 --angle = 2.58, ax = 1, ay = 0, az = 0 }) game.shaders = { animatedFog = lovr.graphics.newShader("shaders/shader.vert", "shaders/shader.frag", { flags = { animated = true } }), regularFog = lovr.graphics.newShader("shaders/shader.vert", "shaders/shader.frag", { flags = { animated = false } }), } game.fogRatio = 0 game.shaders.animatedFog:send("CameraEye", { 0, 0, 0, 0 }) game.shaders.animatedFog:send("FogBounds", { 4, 22 * ((1 - game.fogRatio) * fogScale + 1) }) game.shaders.animatedFog:send("causticsScale", 1) game.shaders.regularFog:send("CameraEye", { 0, 0, 0, 0 }) game.shaders.regularFog:send("FogBounds", { 4, 20 * ((1 - game.fogRatio) * fogScale + 1) }) game.shaders.regularFog:send("causticsScale", 1) game.causticsTexture = lovr.graphics.newTexture("images/caustics.jpg") game.shaders.animatedFog:send("causticsTexture", game.causticsTexture) game.shaders.animatedFog:send("causticsAmount", 1.0) game.shaders.regularFog:send("causticsTexture", game.causticsTexture) game.shaders.regularFog:send("causticsAmount", 1.0) game.swarm = (class "boids.BoidsSwarm"):new({ bounds = bounds }) local x, y, z = -6, 1.42, 6 -- local boid = (class "boids.SelfControlledBoid"):new({ -- world = world, -- x = 0, y = y, z = 4, -- angle = 0, ax = 0, ay = 1, az = 0, -- model = game.fishModel -- }) -- game.swarm:addBoid(boid) for iz = 0, 1 do for i = 0, 1 do local boid = (class "boids.SelfControlledBoid"):new({ world = world, x = x +2.25*i, y = y, z = z+2.25*iz , --y + lovr.math.random() * 0.3, z = z - iz, angle = 0, ax = 0, ay = 1, az = 0, model = game.fishModel }) game.swarm:addBoid(boid) end end global.playerBoid = (class "boids.CelluloControlledBoid"):new({ world = world, cellulo = -1, -- will be later set by menu.lua controls = (class "boids.controls.AbsoluteControls"), -- static class celluloPlane = (class "CelluloPlane"):new(config.CELLULO_PLANE), hapticFeedbackEnabled = false, x = 0, y = 1.42, z = 10, angle = 0, ax = 0, ay = 1, az = 0, model = game.fishModel }) game.swarm:addBoid(global.playerBoid) -- local a = lovr.math.vec3(global.playerBoid:getPosition()) -- local b = lovr.math.vec3(game.swarm:getBoids()[1]:getPosition()) -- print(a) -- print(b) -- print(lovr.math.vec3(global.playerBoid:getCollider():getPosition()):sub(b)) -- print(lovr.math.vec3(global.playerBoid:getCollider():getPosition()):sub(b)) -- print(a:sub(b)) -- print(a:sub(b)) -- print(b:distance(a)) game.levelLabel = "LEVEL" game.levelOffset = 1 game.subLabel = "SUB" game.subOffset = 1 game.hasStarted = true game.currentLevel = -1 game.currentTargetCount = 0 -- game.grid = (lib "grid").new(16, 16, 1, { 1, 0.5 },nil) game.experiment = (class "Experiment"):new() game.experiment:setLogInterval(1/4) game.experiment.logger:log(game:getLogHeader()) game.experiment:setFunction(function() return game:getLogValues() end) game.experiment:start() end function game:unload() world:destroy() end function game:update(dt) flux.update(dt) -- game.shaders.regularFog:send("time", lovr.timer.getTime()) if game.currentLevel == -1 then global.playerBoid:update(dt) game.swarm:setTargetPosition({ -6,1.42,6})--math.cos(lovr.timer.getTime() / 8) * 6, 1.6, -10 + math.sin(lovr.timer.getTime() / 8) * 6 }) end if game.currentLevel == 1 then game.swarm:setTargetPosition({ 6,1.42,6})--math.cos(lovr.timer.getTime() / 8) * 6, 1.6, -10 + math.sin(lovr.timer.getTime() / 8) * 6 }) end if game.currentLevel == 2 then game.swarm:setTargetPosition({ 0,1.42,0})--math.cos(lovr.timer.getTime() / 8) * 6, 1.6, -10 + math.sin(lovr.timer.getTime() / 8) * 6 }) end if game.currentLevel == 3 then game.swarm:setTargetPosition({ -6,1.42,-6})--math.cos(lovr.timer.getTime() / 8) * 6, 1.6, -10 + math.sin(lovr.timer.getTime() / 8) * 6 }) end if game.currentLevel == 4 then game.swarm:setTargetPosition({ 6,1.42,-4})--math.cos(lovr.timer.getTime() / 8) * 6, 1.6, -10 + math.sin(lovr.timer.getTime() / 8) * 6 }) end if game.currentLevel == 5 then game.swarm:setTargetPosition({ -4,1.42,4})--math.cos(lovr.timer.getTime() / 8) * 6, 1.6, -10 + math.sin(lovr.timer.getTime() / 8) * 6 }) end world:update(dt) -- game.fishModel:animate(1, lovr.timer.getTime()) --game.swarm:setPosition(math.cos(lovr.timer.getTime()*0.1) * 5, 0, math.sin(lovr.timer.getTime()*0.1) * 5) if(game.hasStarted) then game.swarm:update(dt) end if global.cameraMode == "first" then local camx,camy,camz = global.playerBoid:getPosition() global.camera:setPosition(camx,camy-1.5,camz) global.camera:setAngle(global.playerBoid:getAngle()) else local x, y, z = global.playerBoid:getPosition() local CAMERA_Y_OFFSET = 10 y = y + CAMERA_Y_OFFSET global.camera:setPosition(0,y,0) -- global.camera:setPosition(x, y, z) -- global.camera:setAngle(global.playerBoid:getAngle()) end -- next target when reached local targetPosition = game.swarm:getTargetPosition() local x, y, z = targetPosition[1], targetPosition[2], targetPosition[3] local x1,y1,z1 = global.playerBoid:getPosition() local goal_distance = lovr.math.vec3(x, y, z) local d = goal_distance:distance(lovr.math.vec3(global.playerBoid:getPosition())) -- game.subLabel = x .. " " .. y .. " " .. z .. " " .. d .. " " .. x1 .. " " .. y1 .. " " .. z1 -- table.insert(transitions, flux.to(game, 1, { levelOffset = 0, subOffset = 0 })) if d < 3 then game:onReachedTarget() end game.experiment:update(dt) end function game:draw() --lovr.graphics.skybox(skybox) -- lovr.graphics.setColor(0, 0.35, 0.482, 1) -- lovr.graphics.fill() lovr.graphics.setColor(1, 1, 1, 1) global.camera:draw(function () -- floor lovr.graphics.translate(0, groundOffset, 0) lovr.graphics.plane(mapMaterial, -1.3, bounds:getHeight(), 1.3, bounds:getWidth(),bounds:getDepth(),-math.pi / 2,1,0,0)--, , math.pi / 2, 1, 0, 0,0.7,0.7) -- lovr.graphics.box("fill",0, bounds:getHeight(), 0, 5000, 0.05, 5000) lovr.graphics.plane(sandMaterial, 0, 100, 0, bounds:getWidth(), bounds:getDepth(), math.pi / 2, 1, 0, 0) game:drawScene() -- -- lovr.graphics.setColor(1, 1, 1, 1) -- -- game.shaders.regularFog:send("FogColor", { 0, 0.1, 0.2, 1 }) -- util.withShader(game.shaders.regularFog, function () -- -- game.shaders.regularFog:send("causticsScale", 8) -- game.shaders.regularFog:send("FogBounds", { 2, 12 * ((1 - game.fogRatio) * fogScale + 1) }) -- lovr.graphics.setColor(1,1,1,1)--(0.2, 0.6, 0.9, 1) -- lovr.graphics.push() -- -- floor -- lovr.graphics.translate(0, groundOffset, 0) -- lovr.graphics.plane(sandMaterial, -1,1,0, 1500,1500,math.pi / 2,1,0,0)--bounds:getWidth(), bounds:getDepth(), math.pi / 2, 1, 0, 0,0.7,0.7) -- game:drawScene() -- -- local target = game.swarm:getTargetPosition() -- -- if target then -- -- game.shaders.regularFog:send("causticsAmount", 0) -- -- lovr.graphics.setColor(0.2, 0.6, 0.9, 0.2) -- -- lovr.graphics.cube('fill', target[1], target[2], target[3], 0.2, lovr.timer.getTime()) -- -- lovr.graphics.setColor(1, 1, 1, 1) -- -- lovr.graphics.cube('line', target[1], target[2], target[3], 0.2, lovr.timer.getTime()) -- -- game.shaders.regularFog:send("causticsAmount", 1) -- -- end -- lovr.graphics.pop() -- -- roof -- -- game.shaders.regularFog:send("causticsScale", 4) -- --game.shaders.regularFog:send("FogBounds", { 2, 40 * ((1 - game.fogRatio) * fogScale + 1) }) -- lovr.graphics.setColor(0.3, 0.7, 1) -- lovr.graphics.plane("fill", 0, 6, 0, bounds:getWidth(), bounds:getDepth(), math.pi / 2, 1, 0, 0) -- end) -- --game.shaders.animatedFog:send("FogColor", { 0, 0.1, 0.3, 0 }) -- -- util.withShader(game.shaders.animatedFog, function () -- -- lovr.graphics.setColor(0.7, 0.9, 1, 1) -- -- game.swarm:draw() -- -- end) game.swarm:draw() -- game.grid:draw(0, 1.1, 0) end) global:drawHud(function () game:drawLevelAndObjective() game:drawCelluloPosition() end) end function game:drawScene() -- game.shaders.regularFog:send("causticsAmount", 0.1) lovr.graphics.setColor(0,1,0,0.8) --0.2, 0.6, 0.9, 1) -- game.shaders.regularFog:send("FogBounds", { 4, 22 * ((1 - game.fogRatio) * fogScale + 1) }) -- game.shaders.regularFog:send("causticsScale", 0.2) size = 1 lovr.graphics.cube("fill", -4,0.45,4, size) --game.shaders.regularFog:send("causticsAmount", 1) end function game:drawLevelAndObjective() lovr.graphics.push() lovr.graphics.transform(-0.175, 0.175, 0, 1, 1, 1, 0.6, 0.5, 1, 0) local s = 0.05 lovr.graphics.scale(s, s, s) lovr.graphics.setColor(1, 1, 1, 1-game.levelOffset) lovr.graphics.print(game.levelLabel, 0, game.levelOffset, 0, 1, 0, 0, 1, 0, 0, "left") lovr.graphics.setColor(1, 1, 1, 1-game.subOffset) lovr.graphics.print(game.subLabel, -math.pow(game.subOffset, 2) * 3, -1, 0, 1, 0, 0, 1, 0, 0, "left") lovr.graphics.pop() end function game:drawCelluloPosition() -- draw position of cellulo on paper lovr.graphics.push() lovr.graphics.transform(0.175, -0.175, 0, 1, 1, 1, -0.6, 0.5, 1, 0) local s = 0.05 lovr.graphics.scale(s, s, s) lovr.graphics.setColor(0, 0, 0, 0.5) lovr.graphics.plane("fill", 0, 0, 0, 2, 2) local sqrt2d2 = math.sqrt(2) / 2 local polygon = { 0, 1, 0, -- top pointing arrow 1, 0, 0, sqrt2d2, -sqrt2d2, 0, sqrt2d2, -sqrt2d2, 0, -sqrt2d2, -sqrt2d2, 0, 0, 1, 0, -- top pointing arrow -sqrt2d2, -sqrt2d2, 0, -1, 0, 0, 0, 1, 0 -- top pointing arrow } local x, y = global.playerBoid:getCelluloPlane():normalizedRelative(global.robot:getX(), global.robot:getY()) lovr.graphics.translate(x, -y, 0.1) local ps = 0.2 lovr.graphics.scale(ps, ps, ps) lovr.graphics.rotate(global.robot:getTheta(), 0, 0, -1) lovr.graphics.setColor(1, 1, 1, 0.75) lovr.graphics.triangle("fill", polygon) lovr.graphics.pop() end function game:buttonpressed(device, button) if button == "b" then local s = global.camera:getX() .. " " .. global.camera:getY() .. " " .. global.camera:getZ() global.toastManager:addToast(s) elseif button == "a" then game:startGame() elseif button == "x" then local s = global.playerBoid:getX() .." ".. global.playerBoid:getY() .. " " .. global.playerBoid:getZ() global.toastManager:addToast(s) end if button == "y" then state:push("scenes/menu") end return true end function game:stopGame() game.hasStarted = true for i = 1, #transitions do transitions[i]:stop() end table.insert(transitions, flux.to(game, 1, { levelOffset = 1, subOffset = 1, fogRatio = 0 })) end function game:spawnRandomTarget() game.swarm:setTargetPosition({0,1,42,0}) end function game:startGame() game.hasStarted = true game:levelOne() end function game:levelOne() game.currentLevel = 1 game.currentTargetCount = 0 game.levelLabel = "level 1" game.subLabel = "Reach the target (0/3)" table.insert(transitions, flux.to(game, 1, { levelOffset = 0, subOffset = 0 })) game:spawnRandomTarget() end function game:levelTwo() game.currentLevel = 2 game.currentTargetCount = 0 game.levelLabel = "level 2" game.subLabel = "reach the target (0/3)" table.insert(transitions, flux.to(game, 1, { levelOffset = 0, subOffset = 0 })) table.insert(transitions, flux.to(game, 3, { fogRatio = 1 })) game:spawnRandomTarget() end function game:youWin() game.currentLevel = -1 game.levelLabel = "you win!" game.subLabel = "" table.insert(transitions, flux.to(game, 1, { levelOffset = 0, subOffset = 0 })) table.insert(transitions, flux.to(game, 3, { fogRatio = 0 })) end function game:onReachedTarget() if game.currentLevel < 5 then game.currentLevel = game.currentLevel + 1 game.levelLabel = "Level" .. game.currentLevel local targetPosition = game.swarm:getTargetPosition() local x, y, z = targetPosition[1], targetPosition[2], targetPosition[3] game.subLabel = x .. " " .. y .. " " .. z else game.levelLabel = "YOU WIN! " end table.insert(transitions, flux.to(game, 1, { levelOffset = 0, subOffset = 0 })) -- if game.hasStarted and game.currentTargetCount < 3 then -- game.currentTargetCount = game.currentTargetCount + 1 -- game.subLabel = "reach the target (" .. game.currentTargetCount .. "/3)" -- if game.currentTargetCount == 3 then -- table.insert(transitions, flux.to(game, 1, {}):oncomplete(function() -- table.insert(transitions, flux.to(game, 1, { levelOffset = 1, subOffset = 1 }):oncomplete(function() -- game.currentLevel = game.currentLevel + 1 -- game:spawnRandomTarget() -- -- if game.currentLevel == 1 then -- -- game:levelTwo() -- -- else -- -- game:youWin() -- -- end -- end)) -- end)) -- else -- game:spawnRandomTarget() -- end -- end end --[[ Logging ]]-- function game:getLogHeader() local t = {} table.insert(t, "timestamp") table.insert(t, "frame_index") table.insert(t, "cellulo_x") table.insert(t, "cellulo_y") table.insert(t, "cellulo_theta") for i = 1, #game.swarm:getBoids() - 1 do -- exclude player boid table.insert(t, "fish_" .. i .. "_x") table.insert(t, "fish_" .. i .. "_y") table.insert(t, "fish_" .. i .. "_theta") table.insert(t, "fish_" .. i .. "_vx") table.insert(t, "fish_" .. i .. "_vy") table.insert(t, "fish_" .. i .. "_vz") table.insert(t, "fish_" .. i .. "_fx") table.insert(t, "fish_" .. i .. "_fy") table.insert(t, "fish_" .. i .. "_fz") end table.insert(t, "player_x") table.insert(t, "player_y") table.insert(t, "player_theta") table.insert(t, "player_vx") table.insert(t, "player_vy") table.insert(t, "player_vz") table.insert(t, "player_fx") table.insert(t, "player_fy") table.insert(t, "player_fz") table.insert(t, "player_rx") table.insert(t, "player_ry") table.insert(t, "player_rz") table.insert(t, "player_rw") return t end function game:getLogValues() local t = {} table.insert(t, lovr.timer.getTime() - game.experiment:getStartTime()) -- timestamp table.insert(t, game.experiment:getFrame()) -- frame_index local cellulo = global.playerBoid:getCellulo() table.insert(t, cellulo:getX()) -- cellulo_x table.insert(t, cellulo:getY()) -- celullo_y table.insert(t, cellulo:getTheta()) -- cellulo_theta util.foreachi(game.swarm:getBoids(), function (boid) -- last fish represents player local collider = boid:getCollider() local x, y, z = collider:getPosition() table.insert(t, x) -- fish_i_x table.insert(t, z) -- fish_i_y local a, ax, ay, az = boid:getAngle() -- FIXME: need to project boid rotation onto Y axis? table.insert(t, a) -- fish_i_theta local vx, vy , vz = collider:getLinearVelocity() table.insert(t, vx) -- fish_i_vx table.insert(t, vy) -- fish_i_vy table.insert(t, vz) -- fish_i_vy local fx,fy,fz=boid:getFeedback():unpack() table.insert(t,fx) table.insert(t,fy) table.insert(t,fz) end) local _, _, _, a, ax, ay, az = lovr.headset.getPose() local rx, ry, rz, rw = lovr.math.newQuat(a, ax, ay, az):unpack() table.insert(t, rx) -- player_rx table.insert(t, ry) -- player_ry table.insert(t, rz) -- player_rz table.insert(t, rw) -- player_rw return t end return game