import { difAngle } from './gmath' ; import { Assets } from './loader' ; import Player from './player' ; import * as grass from './grass' ; import * as terrain from './terrain' ; import * as skydome from './skydome' ; const VIEW_DEPTH = 1000.0 ; const MAX_TIMESTEP = 67 ; const FOG_COLOR = new THREE . Color ( 0.92 , 0.94 , 0.98 ) ; const GRASS_FOG_COLOR = new THREE . Color ( 0.46 , 0.56 , 0.38 ) ; const MAX_GLARE = 0.25 ; const GLARE_RANGE = 1.1 ; const GLARE_YAW = Math . PI * 1.5 ; const GLARE_PITCH = 0.2 ; const INTRO_FADE_DUR = 2000 ; export interface IWorld { doFrame ( ) : void ; resize ( w , h ) : void ; } interface MeshSet { ground : THREE . Mesh ; grass : THREE . Mesh ; sky : THREE . Mesh ; sunFlare : THREE . Mesh ; fade : THREE . Mesh ; } /**
 * Create a World instance
 */ export function World ( renderer , assets , numGrassBlades , grassPatchRadius , displayWidth , displayHeight ) { const fogDist = grassPatchRadius * 10.0 ; const grassFogDist = grassPatchRadius * 2.0 ; const camera = new THREE . PerspectiveCamera ( 45 , displayWidth / displayHeight , 1 , VIEW_DEPTH ) ; const meshes = { ground : null , grass : null , sky : null , sunFlare : null , fade : null } ; const scene = new THREE . Scene ( ) ; camera . rotation . order = "ZXY" ; camera . rotation . x = Math . PI * 0.5 ; camera . rotation . y = Math . PI * 0.5 ; camera . rotation . z = Math . PI ; camera . up . set ( 0.0 , 0.0 , 1.0 ) ; const camHolder = new THREE . Object3D ( ) ; camHolder . rotation . order = "ZYX" ; camHolder . add ( camera ) ; scene . add ( camHolder ) ; renderer . sortObjects = false ; meshes . grass = grass . createMesh ( { numBlades : numGrassBlades , radius : grassPatchRadius , texture : assets . textures [ 'grass' ] , fogColor : FOG_COLOR , fogFar : fogDist , grassFogColor : GRASS_FOG_COLOR , grassFogFar : grassFogDist } ) ; scene . add ( meshes . grass ) ; meshes . ground = terrain . createMesh ( { texture : assets . textures [ 'ground' ] , fogColor : FOG_COLOR , fogFar : fogDist , grassFogColor : GRASS_FOG_COLOR , grassFogFar : grassFogDist } ) ; scene . add ( meshes . ground ) ; meshes . sky = skydome . createMesh ( assets . textures [ 'skydome' ] , VIEW_DEPTH * 0.95 , VIEW_DEPTH * 0.95 ) ; scene . add ( meshes . sky ) ; meshes . sky . position . z = - 25.0 ; meshes . fade = new THREE . Mesh ( new THREE . PlaneBufferGeometry ( 6.0 , 4.0 , 1 , 1 ) , new THREE . MeshBasicMaterial ( { color : 0xFFFFFF , fog : false , transparent : true , opacity : 1.0 , depthTest : false , depthWrite : false } ) ) ; meshes . fade . position . x = 2.0 ; meshes . fade . rotation . y = Math . PI * 1.5 ; camHolder . add ( meshes . fade ) ; meshes . sunFlare = new THREE . Mesh ( new THREE . PlaneBufferGeometry ( 6.0 , 4.0 , 1 , 1 ) , new THREE . MeshBasicMaterial ( { color : 0xFFF844 , fog : false , transparent : true , opacity : 0.0 , depthTest : false , depthWrite : false , blending : THREE . AdditiveBlending } ) ) ; meshes . sunFlare . position . x = 2.05 ; meshes . sunFlare . rotation . y = Math . PI * 1.5 ; meshes . sunFlare . visible = false ; camHolder . add ( meshes . sunFlare ) ; const player = Player ( ) ; let prevT = Date . now ( ) ; let simT = 0 ; /**
     * Call every frame
     */ function doFrame ( ) { const curT = Date . now ( ) ; let dt = curT - prevT ; if ( dt > 0 ) { if ( dt > MAX_TIMESTEP ) { dt = MAX_TIMESTEP ; prevT = curT - MAX_TIMESTEP ; } update ( dt ) ; render ( ) ; prevT = curT ; } } /** Handle window resize events */ function resize ( w , h ) { displayWidth = w ; displayHeight = h ; camera . aspect = displayWidth / displayHeight ; camera . updateProjectionMatrix ( ) ; } /**
     * Logic update
     */ function update ( dt ) { if ( simT < INTRO_FADE_DUR ) { updateFade ( dt ) ; } simT += dt ; const t = simT * 0.001 ; player . update ( dt ) ; const ppos = player . state . pos ; const pyaw = player . state . yaw ; const ppitch = player . state . pitch ; const proll = player . state . roll ; meshes . sky . position . x = ppos . x ; meshes . sky . position . y = ppos . y ; grass . update ( meshes . grass , t , ppos . x + Math . cos ( pyaw ) * grassPatchRadius , ppos . y + Math . sin ( pyaw ) * grassPatchRadius ) ; camHolder . position . copy ( ppos ) ; camHolder . rotation . z = pyaw ; camHolder . rotation . y = - ppitch ; camHolder . rotation . x = proll ; updateGlare ( ) ; } /** Update how much glare effect by how much we're looking at the sun */ function updateGlare ( ) { const dy = Math . abs ( difAngle ( GLARE_YAW , player . state . yaw ) ) ; const dp = Math . abs ( difAngle ( GLARE_PITCH , player . state . pitch ) ) * 1.375 ; const sunVisAngle = Math . sqrt ( dy * dy + dp * dp ) ; if ( sunVisAngle < GLARE_RANGE ) { const glare = MAX_GLARE * Math . pow ( ( GLARE_RANGE - sunVisAngle ) / ( 1.0 + MAX_GLARE ) , 0.75 ) ; meshes . sunFlare . material . opacity = Math . max ( 0.0 , glare ) ; meshes . sunFlare . visible = true ; } else { meshes . sunFlare . visible = false ; } } /** Update intro fullscreen fade from white */ function updateFade ( dt ) { if ( simT + dt >= INTRO_FADE_DUR ) { meshes . fade . material . opacity = 0.0 ; meshes . fade . visible = false ; } else { meshes . fade . material . opacity = 1.0 - Math . pow ( simT / INTRO_FADE_DUR , 2.0 ) ; } } function render ( ) { renderer . render ( scene , camera ) ; } return { doFrame : doFrame , resize : resize } ; }