├── .gitignore ├── .firebaserc ├── static ├── ui │ ├── crystal_icon.png │ └── shield_icon.png ├── models │ ├── cliffs │ │ ├── scene.bin │ │ ├── textures │ │ │ ├── cliff1_normal.png │ │ │ ├── cliff2_normal.png │ │ │ ├── cliff1_baseColor.png │ │ │ ├── cliff2_baseColor.png │ │ │ ├── cliff1_metallicRoughness.png │ │ │ └── cliff2_metallicRoughness.png │ │ └── scene.gltf │ ├── rocket │ │ ├── scene.bin │ │ ├── textures │ │ │ └── Texture_baseColor.jpg │ │ └── scene.gltf │ ├── start_bay │ │ ├── scene.bin │ │ └── textures │ │ │ ├── cliff1_normal.jpg │ │ │ ├── cliff2_normal.jpg │ │ │ ├── cliff1_baseColor.jpg │ │ │ ├── cliff2_baseColor.jpg │ │ │ ├── cliff1_metallicRoughness.jpg │ │ │ ├── cliff2_metallicRoughness.jpg │ │ │ └── Cube_Bake1_cyclesbake_COMBINED.png │ ├── glowing_rock │ │ ├── scene.bin │ │ ├── textures │ │ │ ├── lambert1_normal.png │ │ │ ├── lambert1_baseColor.jpeg │ │ │ ├── lambert1_emissive.jpeg │ │ │ └── lambert1_metallicRoughness.png │ │ └── scene.gltf │ ├── shield_item │ │ ├── scene.bin │ │ ├── license.txt │ │ └── scene.gltf │ ├── glowing_crystals │ │ ├── scene.bin │ │ ├── textures │ │ │ ├── crystals_normal.png │ │ │ ├── crystals_emissive.jpeg │ │ │ ├── crystals_baseColor.jpeg │ │ │ └── crystals_metallicRoughness.png │ │ └── scene.gltf │ └── spaceship_nortend │ │ ├── scene.bin │ │ └── textures │ │ ├── Osn2_normal.png │ │ ├── detal_normal.png │ │ ├── Osn2_emissive.jpeg │ │ ├── Osn2_baseColor.jpeg │ │ ├── detal_baseColor.jpeg │ │ ├── detal_emissive.jpeg │ │ ├── material_normal.png │ │ ├── material_baseColor.jpeg │ │ ├── material_emissive.jpeg │ │ ├── Osn2_metallicRoughness.png │ │ ├── detal_metallicRoughness.png │ │ └── material_metallicRoughness.png ├── normals │ └── waternormals.jpeg └── images │ └── rocket.svg ├── isTouchDevice.ts ├── rocketjourney ├── static │ ├── ui │ │ ├── shield_icon.png │ │ └── crystal_icon.png │ ├── models │ │ ├── cliffs │ │ │ ├── scene.bin │ │ │ └── textures │ │ │ │ ├── cliff1_normal.png │ │ │ │ ├── cliff2_normal.png │ │ │ │ ├── cliff1_baseColor.png │ │ │ │ ├── cliff2_baseColor.png │ │ │ │ ├── cliff1_metallicRoughness.png │ │ │ │ └── cliff2_metallicRoughness.png │ │ ├── rocket │ │ │ ├── scene.bin │ │ │ ├── textures │ │ │ │ └── Texture_baseColor.jpg │ │ │ └── scene.gltf │ │ ├── start_bay │ │ │ ├── scene.bin │ │ │ └── textures │ │ │ │ ├── cliff1_normal.jpg │ │ │ │ ├── cliff2_normal.jpg │ │ │ │ ├── cliff1_baseColor.jpg │ │ │ │ ├── cliff2_baseColor.jpg │ │ │ │ ├── cliff1_metallicRoughness.jpg │ │ │ │ ├── cliff2_metallicRoughness.jpg │ │ │ │ └── Cube_Bake1_cyclesbake_COMBINED.png │ │ ├── shield_item │ │ │ ├── scene.bin │ │ │ ├── license.txt │ │ │ └── scene.gltf │ │ ├── glowing_rock │ │ │ ├── scene.bin │ │ │ ├── textures │ │ │ │ ├── lambert1_normal.png │ │ │ │ ├── lambert1_emissive.jpeg │ │ │ │ ├── lambert1_baseColor.jpeg │ │ │ │ └── lambert1_metallicRoughness.png │ │ │ └── scene.gltf │ │ ├── glowing_crystals │ │ │ ├── scene.bin │ │ │ ├── textures │ │ │ │ ├── crystals_normal.png │ │ │ │ ├── crystals_baseColor.jpeg │ │ │ │ ├── crystals_emissive.jpeg │ │ │ │ └── crystals_metallicRoughness.png │ │ │ └── scene.gltf │ │ └── spaceship_nortend │ │ │ ├── scene.bin │ │ │ └── textures │ │ │ ├── Osn2_normal.png │ │ │ ├── detal_normal.png │ │ │ ├── Osn2_baseColor.jpeg │ │ │ ├── Osn2_emissive.jpeg │ │ │ ├── detal_emissive.jpeg │ │ │ ├── material_normal.png │ │ │ ├── detal_baseColor.jpeg │ │ │ ├── material_emissive.jpeg │ │ │ ├── material_baseColor.jpeg │ │ │ ├── Osn2_metallicRoughness.png │ │ │ ├── detal_metallicRoughness.png │ │ │ └── material_metallicRoughness.png │ ├── normals │ │ └── waternormals.jpeg │ └── images │ │ └── rocket.svg ├── webpack.dev.js ├── objects │ ├── shaders │ │ ├── waterVertexShader.glsl │ │ └── waterFragmentShader.glsl │ └── water.js ├── package.json ├── webpack.production.js ├── game │ ├── physics.ts │ ├── garbageCollector.ts │ ├── ui.ts │ ├── objects.ts │ └── collisionDetection.ts ├── webpack.common.js ├── html │ └── index.html └── game.ts ├── html ├── about.html └── index.html ├── firebase.json ├── tsconfig.json ├── release └── index.html ├── webpack.dev.js ├── objects ├── shaders │ ├── waterVertexShader.glsl │ ├── skyVertexShader.glsl │ ├── waterFragmentShader.glsl │ └── skyFragmentShader.glsl ├── sky.js └── water.js ├── postprocessing ├── copyShader.js ├── pass.js ├── convolutionShader.js └── bloomPass.js ├── package.json ├── webpack.production.js ├── game ├── physics.ts ├── garbageCollector.ts ├── ui.ts ├── objects.ts └── collisionDetection.ts ├── global.d.ts └── webpack.common.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .idea 4 | dist 5 | .firebase 6 | youtube video 7 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "awesome-rocket-journey" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /static/ui/crystal_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/ui/crystal_icon.png -------------------------------------------------------------------------------- /static/ui/shield_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/ui/shield_icon.png -------------------------------------------------------------------------------- /static/models/cliffs/scene.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/cliffs/scene.bin -------------------------------------------------------------------------------- /static/models/rocket/scene.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/rocket/scene.bin -------------------------------------------------------------------------------- /static/normals/waternormals.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/normals/waternormals.jpeg -------------------------------------------------------------------------------- /static/models/start_bay/scene.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/start_bay/scene.bin -------------------------------------------------------------------------------- /static/models/glowing_rock/scene.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/glowing_rock/scene.bin -------------------------------------------------------------------------------- /static/models/shield_item/scene.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/shield_item/scene.bin -------------------------------------------------------------------------------- /isTouchDevice.ts: -------------------------------------------------------------------------------- 1 | export function isTouchDevice() { 2 | return (('ontouchstart' in window) || 3 | (navigator.maxTouchPoints > 0)); 4 | } 5 | -------------------------------------------------------------------------------- /rocketjourney/static/ui/shield_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/ui/shield_icon.png -------------------------------------------------------------------------------- /rocketjourney/static/ui/crystal_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/ui/crystal_icon.png -------------------------------------------------------------------------------- /static/models/glowing_crystals/scene.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/glowing_crystals/scene.bin -------------------------------------------------------------------------------- /static/models/spaceship_nortend/scene.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/spaceship_nortend/scene.bin -------------------------------------------------------------------------------- /rocketjourney/static/models/cliffs/scene.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/cliffs/scene.bin -------------------------------------------------------------------------------- /rocketjourney/static/models/rocket/scene.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/rocket/scene.bin -------------------------------------------------------------------------------- /rocketjourney/static/models/start_bay/scene.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/start_bay/scene.bin -------------------------------------------------------------------------------- /rocketjourney/static/normals/waternormals.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/normals/waternormals.jpeg -------------------------------------------------------------------------------- /static/models/cliffs/textures/cliff1_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/cliffs/textures/cliff1_normal.png -------------------------------------------------------------------------------- /static/models/cliffs/textures/cliff2_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/cliffs/textures/cliff2_normal.png -------------------------------------------------------------------------------- /rocketjourney/static/models/shield_item/scene.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/shield_item/scene.bin -------------------------------------------------------------------------------- /rocketjourney/static/models/glowing_rock/scene.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/glowing_rock/scene.bin -------------------------------------------------------------------------------- /static/models/cliffs/textures/cliff1_baseColor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/cliffs/textures/cliff1_baseColor.png -------------------------------------------------------------------------------- /static/models/cliffs/textures/cliff2_baseColor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/cliffs/textures/cliff2_baseColor.png -------------------------------------------------------------------------------- /static/models/rocket/textures/Texture_baseColor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/rocket/textures/Texture_baseColor.jpg -------------------------------------------------------------------------------- /static/models/start_bay/textures/cliff1_normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/start_bay/textures/cliff1_normal.jpg -------------------------------------------------------------------------------- /static/models/start_bay/textures/cliff2_normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/start_bay/textures/cliff2_normal.jpg -------------------------------------------------------------------------------- /rocketjourney/static/models/glowing_crystals/scene.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/glowing_crystals/scene.bin -------------------------------------------------------------------------------- /static/models/start_bay/textures/cliff1_baseColor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/start_bay/textures/cliff1_baseColor.jpg -------------------------------------------------------------------------------- /static/models/start_bay/textures/cliff2_baseColor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/start_bay/textures/cliff2_baseColor.jpg -------------------------------------------------------------------------------- /rocketjourney/static/models/spaceship_nortend/scene.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/spaceship_nortend/scene.bin -------------------------------------------------------------------------------- /static/models/glowing_rock/textures/lambert1_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/glowing_rock/textures/lambert1_normal.png -------------------------------------------------------------------------------- /static/models/spaceship_nortend/textures/Osn2_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/spaceship_nortend/textures/Osn2_normal.png -------------------------------------------------------------------------------- /static/models/spaceship_nortend/textures/detal_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/spaceship_nortend/textures/detal_normal.png -------------------------------------------------------------------------------- /html/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /static/models/cliffs/textures/cliff1_metallicRoughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/cliffs/textures/cliff1_metallicRoughness.png -------------------------------------------------------------------------------- /static/models/cliffs/textures/cliff2_metallicRoughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/cliffs/textures/cliff2_metallicRoughness.png -------------------------------------------------------------------------------- /static/models/glowing_crystals/textures/crystals_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/glowing_crystals/textures/crystals_normal.png -------------------------------------------------------------------------------- /static/models/glowing_rock/textures/lambert1_baseColor.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/glowing_rock/textures/lambert1_baseColor.jpeg -------------------------------------------------------------------------------- /static/models/glowing_rock/textures/lambert1_emissive.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/glowing_rock/textures/lambert1_emissive.jpeg -------------------------------------------------------------------------------- /static/models/spaceship_nortend/textures/Osn2_emissive.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/spaceship_nortend/textures/Osn2_emissive.jpeg -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "dist", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /rocketjourney/static/models/cliffs/textures/cliff1_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/cliffs/textures/cliff1_normal.png -------------------------------------------------------------------------------- /rocketjourney/static/models/cliffs/textures/cliff2_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/cliffs/textures/cliff2_normal.png -------------------------------------------------------------------------------- /static/models/glowing_crystals/textures/crystals_emissive.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/glowing_crystals/textures/crystals_emissive.jpeg -------------------------------------------------------------------------------- /static/models/spaceship_nortend/textures/Osn2_baseColor.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/spaceship_nortend/textures/Osn2_baseColor.jpeg -------------------------------------------------------------------------------- /static/models/spaceship_nortend/textures/detal_baseColor.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/spaceship_nortend/textures/detal_baseColor.jpeg -------------------------------------------------------------------------------- /static/models/spaceship_nortend/textures/detal_emissive.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/spaceship_nortend/textures/detal_emissive.jpeg -------------------------------------------------------------------------------- /static/models/spaceship_nortend/textures/material_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/spaceship_nortend/textures/material_normal.png -------------------------------------------------------------------------------- /static/models/start_bay/textures/cliff1_metallicRoughness.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/start_bay/textures/cliff1_metallicRoughness.jpg -------------------------------------------------------------------------------- /static/models/start_bay/textures/cliff2_metallicRoughness.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/start_bay/textures/cliff2_metallicRoughness.jpg -------------------------------------------------------------------------------- /rocketjourney/static/models/cliffs/textures/cliff1_baseColor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/cliffs/textures/cliff1_baseColor.png -------------------------------------------------------------------------------- /rocketjourney/static/models/cliffs/textures/cliff2_baseColor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/cliffs/textures/cliff2_baseColor.png -------------------------------------------------------------------------------- /rocketjourney/static/models/start_bay/textures/cliff1_normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/start_bay/textures/cliff1_normal.jpg -------------------------------------------------------------------------------- /rocketjourney/static/models/start_bay/textures/cliff2_normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/start_bay/textures/cliff2_normal.jpg -------------------------------------------------------------------------------- /static/models/glowing_crystals/textures/crystals_baseColor.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/glowing_crystals/textures/crystals_baseColor.jpeg -------------------------------------------------------------------------------- /static/models/spaceship_nortend/textures/material_baseColor.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/spaceship_nortend/textures/material_baseColor.jpeg -------------------------------------------------------------------------------- /static/models/spaceship_nortend/textures/material_emissive.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/spaceship_nortend/textures/material_emissive.jpeg -------------------------------------------------------------------------------- /rocketjourney/static/models/rocket/textures/Texture_baseColor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/rocket/textures/Texture_baseColor.jpg -------------------------------------------------------------------------------- /rocketjourney/static/models/start_bay/textures/cliff1_baseColor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/start_bay/textures/cliff1_baseColor.jpg -------------------------------------------------------------------------------- /rocketjourney/static/models/start_bay/textures/cliff2_baseColor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/start_bay/textures/cliff2_baseColor.jpg -------------------------------------------------------------------------------- /static/models/glowing_rock/textures/lambert1_metallicRoughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/glowing_rock/textures/lambert1_metallicRoughness.png -------------------------------------------------------------------------------- /static/models/spaceship_nortend/textures/Osn2_metallicRoughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/spaceship_nortend/textures/Osn2_metallicRoughness.png -------------------------------------------------------------------------------- /static/models/start_bay/textures/Cube_Bake1_cyclesbake_COMBINED.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/start_bay/textures/Cube_Bake1_cyclesbake_COMBINED.png -------------------------------------------------------------------------------- /rocketjourney/static/models/glowing_rock/textures/lambert1_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/glowing_rock/textures/lambert1_normal.png -------------------------------------------------------------------------------- /static/models/spaceship_nortend/textures/detal_metallicRoughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/spaceship_nortend/textures/detal_metallicRoughness.png -------------------------------------------------------------------------------- /rocketjourney/static/models/cliffs/textures/cliff1_metallicRoughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/cliffs/textures/cliff1_metallicRoughness.png -------------------------------------------------------------------------------- /rocketjourney/static/models/cliffs/textures/cliff2_metallicRoughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/cliffs/textures/cliff2_metallicRoughness.png -------------------------------------------------------------------------------- /rocketjourney/static/models/glowing_rock/textures/lambert1_emissive.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/glowing_rock/textures/lambert1_emissive.jpeg -------------------------------------------------------------------------------- /rocketjourney/static/models/spaceship_nortend/textures/Osn2_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/spaceship_nortend/textures/Osn2_normal.png -------------------------------------------------------------------------------- /rocketjourney/static/models/spaceship_nortend/textures/detal_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/spaceship_nortend/textures/detal_normal.png -------------------------------------------------------------------------------- /static/models/glowing_crystals/textures/crystals_metallicRoughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/glowing_crystals/textures/crystals_metallicRoughness.png -------------------------------------------------------------------------------- /static/models/spaceship_nortend/textures/material_metallicRoughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/static/models/spaceship_nortend/textures/material_metallicRoughness.png -------------------------------------------------------------------------------- /rocketjourney/static/models/glowing_crystals/textures/crystals_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/glowing_crystals/textures/crystals_normal.png -------------------------------------------------------------------------------- /rocketjourney/static/models/glowing_rock/textures/lambert1_baseColor.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/glowing_rock/textures/lambert1_baseColor.jpeg -------------------------------------------------------------------------------- /rocketjourney/static/models/spaceship_nortend/textures/Osn2_baseColor.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/spaceship_nortend/textures/Osn2_baseColor.jpeg -------------------------------------------------------------------------------- /rocketjourney/static/models/spaceship_nortend/textures/Osn2_emissive.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/spaceship_nortend/textures/Osn2_emissive.jpeg -------------------------------------------------------------------------------- /rocketjourney/static/models/spaceship_nortend/textures/detal_emissive.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/spaceship_nortend/textures/detal_emissive.jpeg -------------------------------------------------------------------------------- /rocketjourney/static/models/spaceship_nortend/textures/material_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/spaceship_nortend/textures/material_normal.png -------------------------------------------------------------------------------- /rocketjourney/static/models/glowing_crystals/textures/crystals_baseColor.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/glowing_crystals/textures/crystals_baseColor.jpeg -------------------------------------------------------------------------------- /rocketjourney/static/models/glowing_crystals/textures/crystals_emissive.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/glowing_crystals/textures/crystals_emissive.jpeg -------------------------------------------------------------------------------- /rocketjourney/static/models/spaceship_nortend/textures/detal_baseColor.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/spaceship_nortend/textures/detal_baseColor.jpeg -------------------------------------------------------------------------------- /rocketjourney/static/models/spaceship_nortend/textures/material_emissive.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/spaceship_nortend/textures/material_emissive.jpeg -------------------------------------------------------------------------------- /rocketjourney/static/models/start_bay/textures/cliff1_metallicRoughness.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/start_bay/textures/cliff1_metallicRoughness.jpg -------------------------------------------------------------------------------- /rocketjourney/static/models/start_bay/textures/cliff2_metallicRoughness.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/start_bay/textures/cliff2_metallicRoughness.jpg -------------------------------------------------------------------------------- /rocketjourney/static/models/spaceship_nortend/textures/material_baseColor.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/spaceship_nortend/textures/material_baseColor.jpeg -------------------------------------------------------------------------------- /rocketjourney/static/models/glowing_rock/textures/lambert1_metallicRoughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/glowing_rock/textures/lambert1_metallicRoughness.png -------------------------------------------------------------------------------- /rocketjourney/static/models/spaceship_nortend/textures/Osn2_metallicRoughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/spaceship_nortend/textures/Osn2_metallicRoughness.png -------------------------------------------------------------------------------- /rocketjourney/static/models/spaceship_nortend/textures/detal_metallicRoughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/spaceship_nortend/textures/detal_metallicRoughness.png -------------------------------------------------------------------------------- /rocketjourney/static/models/start_bay/textures/Cube_Bake1_cyclesbake_COMBINED.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/start_bay/textures/Cube_Bake1_cyclesbake_COMBINED.png -------------------------------------------------------------------------------- /rocketjourney/static/models/glowing_crystals/textures/crystals_metallicRoughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/glowing_crystals/textures/crystals_metallicRoughness.png -------------------------------------------------------------------------------- /rocketjourney/static/models/spaceship_nortend/textures/material_metallicRoughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutterfromscratch/threejs-rocket-game/HEAD/rocketjourney/static/models/spaceship_nortend/textures/material_metallicRoughness.png -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "strict": true, 5 | "allowJs": true, 6 | "checkJs": false, 7 | "target": "es2017", 8 | "module": "commonjs" 9 | 10 | }, 11 | "include": ["**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /release/index.html: -------------------------------------------------------------------------------- 1 | My first three.js app
-------------------------------------------------------------------------------- /webpack.dev.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('webpack-merge') 2 | const common = require('./webpack.common.js') 3 | const path = require('path'); 4 | module.exports = merge(common, { 5 | mode: 'development', // Don't minify the source 6 | devtool: 'eval-source-map', // Source map for easier development 7 | devServer: { 8 | static: { 9 | directory: path.join(__dirname, './dist'), // Serve static files from here 10 | }, 11 | hot: true, // Reload our page when the code changes 12 | }, 13 | }) 14 | -------------------------------------------------------------------------------- /rocketjourney/webpack.dev.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('webpack-merge') 2 | const common = require('./webpack.common.js') 3 | const path = require('path'); 4 | module.exports = merge(common, { 5 | mode: 'development', // Don't minify the source 6 | devtool: 'eval-source-map', // Source map for easier development 7 | devServer: { 8 | static: { 9 | directory: path.join(__dirname, './dist'), // Serve static files from here 10 | }, 11 | hot: true, // Reload our page when the code changes 12 | }, 13 | }) 14 | -------------------------------------------------------------------------------- /static/models/shield_item/license.txt: -------------------------------------------------------------------------------- 1 | Model Information: 2 | * title: Shield Item 3 | * source: https://sketchfab.com/3d-models/shield-item-a72cd3f034ec4e6b88116b626e39401f 4 | * author: Reikan (https://sketchfab.com/jcremoux) 5 | 6 | Model License: 7 | * license type: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/) 8 | * requirements: Author must be credited. Commercial use is allowed. 9 | 10 | If you use this 3D model in your project be sure to copy paste this credit wherever you share it: 11 | This work is based on "Shield Item" (https://sketchfab.com/3d-models/shield-item-a72cd3f034ec4e6b88116b626e39401f) by Reikan (https://sketchfab.com/jcremoux) licensed under CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /rocketjourney/static/models/shield_item/license.txt: -------------------------------------------------------------------------------- 1 | Model Information: 2 | * title: Shield Item 3 | * source: https://sketchfab.com/3d-models/shield-item-a72cd3f034ec4e6b88116b626e39401f 4 | * author: Reikan (https://sketchfab.com/jcremoux) 5 | 6 | Model License: 7 | * license type: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/) 8 | * requirements: Author must be credited. Commercial use is allowed. 9 | 10 | If you use this 3D model in your project be sure to copy paste this credit wherever you share it: 11 | This work is based on "Shield Item" (https://sketchfab.com/3d-models/shield-item-a72cd3f034ec4e6b88116b626e39401f) by Reikan (https://sketchfab.com/jcremoux) licensed under CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /objects/shaders/waterVertexShader.glsl: -------------------------------------------------------------------------------- 1 | uniform mat4 textureMatrix; 2 | uniform float time; 3 | varying vec4 mirrorCoord; 4 | varying vec4 worldPosition; 5 | varying float speed; 6 | #include 7 | #include 8 | #include 9 | #include 10 | void main() { 11 | mirrorCoord = modelMatrix * vec4( position, 1.0 ); 12 | worldPosition = mirrorCoord.xyzw; 13 | mirrorCoord = textureMatrix * mirrorCoord; 14 | vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); 15 | gl_Position = projectionMatrix * mvPosition; 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | } 22 | -------------------------------------------------------------------------------- /postprocessing/copyShader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Full-screen textured quad shader 3 | */ 4 | 5 | var CopyShader = { 6 | 7 | uniforms: { 8 | 9 | 'tDiffuse': { value: null }, 10 | 'opacity': { value: 1.0 } 11 | 12 | }, 13 | 14 | vertexShader: /* glsl */` 15 | 16 | varying vec2 vUv; 17 | 18 | void main() { 19 | 20 | vUv = uv; 21 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 22 | 23 | }`, 24 | 25 | fragmentShader: /* glsl */` 26 | 27 | uniform float opacity; 28 | 29 | uniform sampler2D tDiffuse; 30 | 31 | varying vec2 vUv; 32 | 33 | void main() { 34 | 35 | vec4 texel = texture2D( tDiffuse, vUv ); 36 | gl_FragColor = opacity * texel; 37 | 38 | }` 39 | 40 | }; 41 | 42 | export { CopyShader }; 43 | -------------------------------------------------------------------------------- /rocketjourney/objects/shaders/waterVertexShader.glsl: -------------------------------------------------------------------------------- 1 | uniform mat4 textureMatrix; 2 | uniform float time; 3 | varying vec4 mirrorCoord; 4 | varying vec4 worldPosition; 5 | varying float speed; 6 | #include 7 | #include 8 | #include 9 | #include 10 | void main() { 11 | mirrorCoord = modelMatrix * vec4( position, 1.0 ); 12 | worldPosition = mirrorCoord.xyzw; 13 | mirrorCoord = textureMatrix * mirrorCoord; 14 | vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); 15 | gl_Position = projectionMatrix * mvPosition; 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "materialize-css": "^1.0.0", 4 | "nipplejs": "^0.9.0", 5 | "three": "^0.136.0" 6 | }, 7 | "devDependencies": { 8 | "@types/three": "^0.136.1", 9 | "@yushijinhun/three-minifier-webpack": "^0.3.0", 10 | "clean-webpack-plugin": "^4.0.0", 11 | "copy-webpack-plugin": "^9.1.0", 12 | "html-webpack-plugin": "^5.5.0", 13 | "raw-loader": "^4.0.2", 14 | "ts-loader": "^9.2.5", 15 | "typescript": "^4.5.4", 16 | "webpack": "^5.51.1", 17 | "webpack-bundle-analyzer": "^4.5.0", 18 | "webpack-cli": "^4.8.0", 19 | "webpack-dev-server": "^4.0.0", 20 | "webpack-glsl-loader": "git+https://github.com/grieve/webpack-glsl-loader.git", 21 | "webpack-merge": "^5.8.0" 22 | }, 23 | "scripts": { 24 | "dev": "webpack serve --config ./webpack.dev.js", 25 | "build": "webpack --config ./webpack.production.js" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /rocketjourney/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "materialize-css": "^1.0.0", 4 | "nipplejs": "^0.9.0", 5 | "three": "^0.136.0" 6 | }, 7 | "devDependencies": { 8 | "@types/three": "^0.136.1", 9 | "@yushijinhun/three-minifier-webpack": "^0.3.0", 10 | "clean-webpack-plugin": "^4.0.0", 11 | "copy-webpack-plugin": "^9.1.0", 12 | "html-webpack-plugin": "^5.5.0", 13 | "raw-loader": "^4.0.2", 14 | "ts-loader": "^9.2.5", 15 | "typescript": "^4.5.4", 16 | "webpack": "^5.51.1", 17 | "webpack-bundle-analyzer": "^4.5.0", 18 | "webpack-cli": "^4.8.0", 19 | "webpack-dev-server": "^4.0.0", 20 | "webpack-glsl-loader": "git+https://github.com/grieve/webpack-glsl-loader.git", 21 | "webpack-merge": "^5.8.0" 22 | }, 23 | "scripts": { 24 | "dev": "webpack serve --config ./webpack.dev.js", 25 | "build": "webpack --config ./webpack.production.js" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /webpack.production.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('webpack-merge') 2 | const common = require('./webpack.common.js') 3 | const path = require('path'); 4 | const ThreeMinifierPlugin = require("@yushijinhun/three-minifier-webpack"); 5 | const {CleanWebpackPlugin} = require("clean-webpack-plugin"); 6 | const threeMinifier = new ThreeMinifierPlugin(); 7 | 8 | 9 | module.exports = merge(common, { 10 | plugins: [ 11 | threeMinifier, 12 | new CleanWebpackPlugin() 13 | ], 14 | resolve: { 15 | plugins: [ 16 | threeMinifier.resolver, // <=== (2) Add resolver on the FIRST line 17 | ] 18 | }, 19 | 20 | mode: 'production', 21 | output: { 22 | path: path.resolve(__dirname, 'dist'), 23 | filename: '[name].[fullhash:8].js', 24 | sourceMapFilename: '[name].[fullhash:8].map', 25 | chunkFilename: '[id].[fullhash:8].js' 26 | }, 27 | optimization: { 28 | splitChunks: { 29 | chunks: 'all', 30 | }, 31 | }, 32 | }) 33 | -------------------------------------------------------------------------------- /game/physics.ts: -------------------------------------------------------------------------------- 1 | import {Vector3} from "three"; 2 | import {rocketModel} from "./objects"; 3 | import {destructionBits, scene} from "../game"; 4 | 5 | 6 | export const moveCollectedBits = () => { 7 | for (let i = 0; i < destructionBits.length; i++) { 8 | let bit = destructionBits[i]; 9 | if (bit.userData.lifetime > 500) { 10 | scene.remove(bit); 11 | destructionBits.splice(i, 1); 12 | } else { 13 | let target = rocketModel.position; 14 | let targetNormalizedVector = new Vector3(0, 0, 0); 15 | targetNormalizedVector.x = target.x - bit.position.x; 16 | targetNormalizedVector.y = target.y - bit.position.y; 17 | targetNormalizedVector.z = target.z - bit.position.z; 18 | targetNormalizedVector.normalize(); 19 | bit.translateOnAxis(targetNormalizedVector, 0.8); 20 | bit.userData.lifetime++; 21 | } 22 | 23 | 24 | } 25 | // console.log(`tracking ${destructionBits.length}`); 26 | } 27 | -------------------------------------------------------------------------------- /rocketjourney/webpack.production.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('webpack-merge') 2 | const common = require('./webpack.common.js') 3 | const path = require('path'); 4 | const ThreeMinifierPlugin = require("@yushijinhun/three-minifier-webpack"); 5 | const {CleanWebpackPlugin} = require("clean-webpack-plugin"); 6 | const threeMinifier = new ThreeMinifierPlugin(); 7 | 8 | 9 | module.exports = merge(common, { 10 | plugins: [ 11 | threeMinifier, 12 | new CleanWebpackPlugin() 13 | ], 14 | resolve: { 15 | plugins: [ 16 | threeMinifier.resolver, // <=== (2) Add resolver on the FIRST line 17 | ] 18 | }, 19 | 20 | mode: 'production', 21 | output: { 22 | path: path.resolve(__dirname, 'dist'), 23 | filename: '[name].[fullhash:8].js', 24 | sourceMapFilename: '[name].[fullhash:8].map', 25 | chunkFilename: '[id].[fullhash:8].js' 26 | }, 27 | optimization: { 28 | splitChunks: { 29 | chunks: 'all', 30 | }, 31 | }, 32 | }) 33 | -------------------------------------------------------------------------------- /rocketjourney/game/physics.ts: -------------------------------------------------------------------------------- 1 | import {Vector3} from "three"; 2 | import {rocketModel} from "./objects"; 3 | import {destructionBits, scene} from "../game"; 4 | 5 | 6 | export const moveCollectedBits = () => { 7 | for (let i = 0; i < destructionBits.length; i++) { 8 | let bit = destructionBits[i]; 9 | if (bit.userData.lifetime > 500) { 10 | scene.remove(bit); 11 | destructionBits.splice(i, 1); 12 | } else { 13 | let target = rocketModel.position; 14 | let targetNormalizedVector = new Vector3(0, 0, 0); 15 | targetNormalizedVector.x = target.x - bit.position.x; 16 | targetNormalizedVector.y = target.y - bit.position.y; 17 | targetNormalizedVector.z = target.z - bit.position.z; 18 | targetNormalizedVector.normalize(); 19 | bit.translateOnAxis(targetNormalizedVector, 0.8); 20 | bit.userData.lifetime++; 21 | } 22 | 23 | 24 | } 25 | // console.log(`tracking ${destructionBits.length}`); 26 | } 27 | -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.js'; 2 | 3 | declare module 'Mesh'{ 4 | import {BufferGeometry} from "three/src/core/BufferGeometry"; 5 | import {Material} from "three/src/materials/Material"; 6 | import {Object3D} from "three/src/core/Object3D"; 7 | import {Intersection, Raycaster} from "three/src/core/Raycaster"; 8 | import {ShaderMaterial} from "three"; 9 | 10 | export class Mesh< 11 | TGeometry extends BufferGeometry = BufferGeometry, 12 | TMaterial extends Material | Material[] = Material | Material[], 13 | > extends Object3D { 14 | constructor(geometry?: TGeometry, material?: TMaterial); 15 | 16 | geometry: TGeometry; 17 | material: ShaderMaterial; 18 | morphTargetInfluences?: number[] | undefined; 19 | morphTargetDictionary?: { [key: string]: number } | undefined; 20 | readonly isMesh: true; 21 | type: string; 22 | 23 | updateMorphTargets(): void; 24 | raycast(raycaster: Raycaster, intersects: Intersection[]): void; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /webpack.common.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 2 | const CopyPlugin = require("copy-webpack-plugin"); 3 | 4 | module.exports = { 5 | plugins: [ 6 | // Automatically creat an index.html with the right bundle name and references to our javascript. 7 | new HtmlWebpackPlugin({ 8 | template: 'html/index.html' 9 | }), 10 | // Copy game assets from our static directory, to the webpack output 11 | new CopyPlugin({ 12 | patterns: [ 13 | {from: 'static', to: 'static'} 14 | ] 15 | }), 16 | ], 17 | // Entrypoint for our game 18 | entry: './game.ts', 19 | module: { 20 | rules: [ 21 | { 22 | // Load our GLSL shaders in as text 23 | test: /\.(glsl|vs|fs|vert|frag)$/, exclude: /node_modules/, use: ['raw-loader'] 24 | }, 25 | { 26 | // Process our typescript and use ts-loader to transpile it to Javascript 27 | test: /\.tsx?$/, 28 | use: 'ts-loader', 29 | exclude: /node_modules/, 30 | } 31 | 32 | ], 33 | }, 34 | resolve: { 35 | extensions: ['.tsx', '.ts', '.js'], 36 | }, 37 | 38 | } 39 | -------------------------------------------------------------------------------- /rocketjourney/webpack.common.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 2 | const CopyPlugin = require("copy-webpack-plugin"); 3 | 4 | module.exports = { 5 | plugins: [ 6 | // Automatically creat an index.html with the right bundle name and references to our javascript. 7 | new HtmlWebpackPlugin({ 8 | template: 'html/index.html' 9 | }), 10 | // Copy game assets from our static directory, to the webpack output 11 | new CopyPlugin({ 12 | patterns: [ 13 | {from: 'static', to: 'static'} 14 | ] 15 | }), 16 | ], 17 | // Entrypoint for our game 18 | entry: './game.ts', 19 | module: { 20 | rules: [ 21 | { 22 | // Load our GLSL shaders in as text 23 | test: /\.(glsl|vs|fs|vert|frag)$/, exclude: /node_modules/, use: ['raw-loader'] 24 | }, 25 | { 26 | // Process our typescript and use ts-loader to transpile it to Javascript 27 | test: /\.tsx?$/, 28 | use: 'ts-loader', 29 | exclude: /node_modules/, 30 | } 31 | 32 | ], 33 | }, 34 | resolve: { 35 | extensions: ['.tsx', '.ts', '.js'], 36 | }, 37 | 38 | } 39 | -------------------------------------------------------------------------------- /game/garbageCollector.ts: -------------------------------------------------------------------------------- 1 | import {scene} from "../game"; 2 | import {challengeRows, environmentBits} from "./objects"; 3 | 4 | export const garbageCollector = () => { 5 | let environmentObjectsForCollection = environmentBits.filter(x => x.position.z > 100); 6 | let challengeRowsForCollection = challengeRows.filter(x => x.rowParent.position.z > 100); 7 | 8 | // if (environmentObjectsForCollection.length){ 9 | for (let i = 0; i < environmentObjectsForCollection.length - 1; i++) { 10 | let environmentObjectIndex = environmentBits.indexOf(environmentObjectsForCollection[i]); 11 | scene.remove(environmentBits[environmentObjectIndex]); 12 | environmentBits.splice(environmentObjectIndex, 1); 13 | 14 | console.log(`Removing environment object at index ${i} from scene`); 15 | } 16 | 17 | for (let i = 0; i < challengeRowsForCollection.length - 1; i++) { 18 | // debugger; 19 | let challengeRowIndex = challengeRows.indexOf(challengeRowsForCollection[i]); 20 | scene.remove(challengeRowsForCollection[i].rowParent); 21 | // challengeRowsForCollection[i].rowParent.remove(); 22 | // challengeRowsForCollection[i].rowObjects.forEach(x => { 23 | // scene.remove(x); 24 | // }); 25 | 26 | console.log(`Removing challenge line at index ${i} from scene`); 27 | 28 | challengeRows.splice(challengeRowIndex, 1); 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /rocketjourney/game/garbageCollector.ts: -------------------------------------------------------------------------------- 1 | import {scene} from "../game"; 2 | import {challengeRows, environmentBits} from "./objects"; 3 | 4 | export const garbageCollector = () => { 5 | let environmentObjectsForCollection = environmentBits.filter(x => x.position.z > 100); 6 | let challengeRowsForCollection = challengeRows.filter(x => x.rowParent.position.z > 100); 7 | 8 | // if (environmentObjectsForCollection.length){ 9 | for (let i = 0; i < environmentObjectsForCollection.length - 1; i++) { 10 | let environmentObjectIndex = environmentBits.indexOf(environmentObjectsForCollection[i]); 11 | scene.remove(environmentBits[environmentObjectIndex]); 12 | environmentBits.splice(environmentObjectIndex, 1); 13 | 14 | console.log(`Removing environment object at index ${i} from scene`); 15 | } 16 | 17 | for (let i = 0; i < challengeRowsForCollection.length - 1; i++) { 18 | // debugger; 19 | let challengeRowIndex = challengeRows.indexOf(challengeRowsForCollection[i]); 20 | scene.remove(challengeRowsForCollection[i].rowParent); 21 | // challengeRowsForCollection[i].rowParent.remove(); 22 | // challengeRowsForCollection[i].rowObjects.forEach(x => { 23 | // scene.remove(x); 24 | // }); 25 | 26 | console.log(`Removing challenge line at index ${i} from scene`); 27 | 28 | challengeRows.splice(challengeRowIndex, 1); 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /static/images/rocket.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /rocketjourney/static/images/rocket.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /objects/sky.js: -------------------------------------------------------------------------------- 1 | // import { 2 | // BackSide, 3 | // BoxGeometry, Scene, 4 | // ShaderMaterial, 5 | // UniformsUtils, 6 | // Vector3, 7 | // Mesh 8 | // } from 'three'; 9 | // // import {Mesh} from "../types/Mesh"; 10 | // 11 | // /** 12 | // * Based on "A Practical Analytic Model for Daylight" 13 | // * aka The Preetham Model, the de facto standard analytic skydome model 14 | // * https://www.researchgate.net/publication/220720443_A_Practical_Analytic_Model_for_Daylight 15 | // * 16 | // * First implemented by Simon Wallner 17 | // * http://simonwallner.at/project/atmospheric-scattering/ 18 | // * 19 | // * Improved by Martin Upitis 20 | // * http://blenderartists.org/forum/showthread.php?245954-preethams-sky-impementation-HDR 21 | // * 22 | // * Three.js integration by zz85 http://twitter.com/blurspline 23 | // */ 24 | // 25 | // class Sky extends Mesh { 26 | // 27 | // constructor() { 28 | // 29 | // const shader = Sky.SkyShader; 30 | // 31 | // const material = new ShaderMaterial( { 32 | // name: 'SkyShader', 33 | // fragmentShader: shader.fragmentShader, 34 | // vertexShader: shader.vertexShader, 35 | // uniforms: UniformsUtils.clone( shader.uniforms ), 36 | // side: BackSide, 37 | // depthWrite: false 38 | // } ); 39 | // 40 | // super( new BoxGeometry( 1, 1, 1 ), material ); 41 | // 42 | // } 43 | // 44 | // } 45 | // 46 | // Sky.prototype.isSky = true; 47 | // 48 | // Sky.SkyShader = { 49 | // 50 | // uniforms: { 51 | // 'turbidity': { value: 2 }, 52 | // 'rayleigh': { value: 1 }, 53 | // 'mieCoefficient': { value: 0.005 }, 54 | // 'mieDirectionalG': { value: 0.8 }, 55 | // 'sunPosition': { value: new Vector3() }, 56 | // 'up': { value: new Vector3( 0, 1, 0 ) } 57 | // }, 58 | // 59 | // vertexShader: /* glsl */ require('./shaders/skyVertexShader.glsl').default, 60 | // 61 | // fragmentShader: /* glsl */ require('./shaders/skyFragmentShader.glsl').default 62 | // 63 | // }; 64 | // 65 | // export { Sky }; 66 | -------------------------------------------------------------------------------- /postprocessing/pass.js: -------------------------------------------------------------------------------- 1 | import { 2 | BufferGeometry, 3 | Float32BufferAttribute, 4 | OrthographicCamera, 5 | Mesh 6 | } from 'three'; 7 | 8 | class Pass { 9 | 10 | constructor() { 11 | 12 | // if set to true, the pass is processed by the composer 13 | this.enabled = true; 14 | 15 | // if set to true, the pass indicates to swap read and write buffer after rendering 16 | this.needsSwap = true; 17 | 18 | // if set to true, the pass clears its buffer before rendering 19 | this.clear = false; 20 | 21 | // if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer. 22 | this.renderToScreen = false; 23 | 24 | } 25 | 26 | setSize( /* width, height */ ) {} 27 | 28 | render( /* renderer, writeBuffer, readBuffer, deltaTime, maskActive */ ) { 29 | 30 | console.error( 'THREE.Pass: .render() must be implemented in derived pass.' ); 31 | 32 | } 33 | 34 | } 35 | 36 | // Helper for passes that need to fill the viewport with a single quad. 37 | 38 | const _camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); 39 | 40 | // https://github.com/mrdoob/three.js/pull/21358 41 | 42 | const _geometry = new BufferGeometry(); 43 | _geometry.setAttribute( 'position', new Float32BufferAttribute( [ - 1, 3, 0, - 1, - 1, 0, 3, - 1, 0 ], 3 ) ); 44 | _geometry.setAttribute( 'uv', new Float32BufferAttribute( [ 0, 2, 0, 0, 2, 0 ], 2 ) ); 45 | 46 | class FullScreenQuad { 47 | 48 | constructor( material ) { 49 | 50 | this._mesh = new Mesh( _geometry, material ); 51 | 52 | } 53 | 54 | dispose() { 55 | 56 | this._mesh.geometry.dispose(); 57 | 58 | } 59 | 60 | render( renderer ) { 61 | 62 | renderer.render( this._mesh, _camera ); 63 | 64 | } 65 | 66 | get material() { 67 | 68 | return this._mesh.material; 69 | 70 | } 71 | 72 | set material( value ) { 73 | 74 | this._mesh.material = value; 75 | 76 | } 77 | 78 | } 79 | 80 | export { Pass, FullScreenQuad }; 81 | -------------------------------------------------------------------------------- /postprocessing/convolutionShader.js: -------------------------------------------------------------------------------- 1 | import { 2 | Vector2 3 | } from 'three' 4 | 5 | /** 6 | * Convolution shader 7 | * ported from o3d sample to WebGL / GLSL 8 | */ 9 | 10 | const ConvolutionShader = { 11 | 12 | defines: { 13 | 14 | 'KERNEL_SIZE_FLOAT': '25.0', 15 | 'KERNEL_SIZE_INT': '25' 16 | 17 | }, 18 | 19 | uniforms: { 20 | 21 | 'tDiffuse': { value: null }, 22 | 'uImageIncrement': { value: new Vector2( 0.001953125, 0.0 ) }, 23 | 'cKernel': { value: [] } 24 | 25 | }, 26 | 27 | vertexShader: /* glsl */` 28 | 29 | uniform vec2 uImageIncrement; 30 | 31 | varying vec2 vUv; 32 | 33 | void main() { 34 | 35 | vUv = uv - ( ( KERNEL_SIZE_FLOAT - 1.0 ) / 2.0 ) * uImageIncrement; 36 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 37 | 38 | }`, 39 | 40 | fragmentShader: /* glsl */` 41 | 42 | uniform float cKernel[ KERNEL_SIZE_INT ]; 43 | 44 | uniform sampler2D tDiffuse; 45 | uniform vec2 uImageIncrement; 46 | 47 | varying vec2 vUv; 48 | 49 | void main() { 50 | 51 | vec2 imageCoord = vUv; 52 | vec4 sum = vec4( 0.0, 0.0, 0.0, 0.0 ); 53 | 54 | for( int i = 0; i < KERNEL_SIZE_INT; i ++ ) { 55 | 56 | sum += texture2D( tDiffuse, imageCoord ) * cKernel[ i ]; 57 | imageCoord += uImageIncrement; 58 | 59 | } 60 | 61 | gl_FragColor = sum; 62 | 63 | }`, 64 | 65 | buildKernel: function ( sigma ) { 66 | 67 | // We lop off the sqrt(2 * pi) * sigma term, since we're going to normalize anyway. 68 | 69 | const kMaxKernelSize = 25; 70 | let kernelSize = 2 * Math.ceil( sigma * 3.0 ) + 1; 71 | 72 | if ( kernelSize > kMaxKernelSize ) kernelSize = kMaxKernelSize; 73 | 74 | const halfWidth = ( kernelSize - 1 ) * 0.5; 75 | 76 | const values = new Array( kernelSize ); 77 | let sum = 0.0; 78 | for ( let i = 0; i < kernelSize; ++ i ) { 79 | 80 | values[ i ] = gauss( i - halfWidth, sigma ); 81 | sum += values[ i ]; 82 | 83 | } 84 | 85 | // normalize the kernel 86 | 87 | for ( let i = 0; i < kernelSize; ++ i ) values[ i ] /= sum; 88 | 89 | return values; 90 | 91 | } 92 | 93 | }; 94 | 95 | function gauss( x, sigma ) { 96 | 97 | return Math.exp( - ( x * x ) / ( 2.0 * sigma * sigma ) ); 98 | 99 | } 100 | 101 | export { ConvolutionShader }; 102 | -------------------------------------------------------------------------------- /objects/shaders/skyVertexShader.glsl: -------------------------------------------------------------------------------- 1 | uniform vec3 sunPosition; 2 | uniform float rayleigh; 3 | uniform float turbidity; 4 | uniform float mieCoefficient; 5 | uniform vec3 up; 6 | 7 | varying vec3 vWorldPosition; 8 | varying vec3 vSunDirection; 9 | varying float vSunfade; 10 | varying vec3 vBetaR; 11 | varying vec3 vBetaM; 12 | varying float vSunE; 13 | 14 | // constants for atmospheric scattering 15 | const float e = 2.71828182845904523536028747135266249775724709369995957; 16 | const float pi = 3.141592653589793238462643383279502884197169; 17 | 18 | // wavelength of used primaries, according to preetham 19 | const vec3 lambda = vec3( 680E-9, 550E-9, 450E-9 ); 20 | // this pre-calcuation replaces older TotalRayleigh(vec3 lambda) function: 21 | // (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn)) 22 | const vec3 totalRayleigh = vec3( 5.804542996261093E-6, 1.3562911419845635E-5, 3.0265902468824876E-5 ); 23 | 24 | // mie stuff 25 | // K coefficient for the primaries 26 | const float v = 4.0; 27 | const vec3 K = vec3( 0.686, 0.678, 0.666 ); 28 | // MieConst = pi * pow( ( 2.0 * pi ) / lambda, vec3( v - 2.0 ) ) * K 29 | const vec3 MieConst = vec3( 1.8399918514433978E14, 2.7798023919660528E14, 4.0790479543861094E14 ); 30 | 31 | // earth shadow hack 32 | // cutoffAngle = pi / 1.95; 33 | const float cutoffAngle = 1.6110731556870734; 34 | const float steepness = 1.5; 35 | const float EE = 1000.0; 36 | 37 | float sunIntensity( float zenithAngleCos ) { 38 | zenithAngleCos = clamp( zenithAngleCos, -1.0, 1.0 ); 39 | return EE * max( 0.0, 1.0 - pow( e, -( ( cutoffAngle - acos( zenithAngleCos ) ) / steepness ) ) ); 40 | } 41 | 42 | vec3 totalMie( float T ) { 43 | float c = ( 0.2 * T ) * 10E-18; 44 | return 0.434 * c * MieConst; 45 | } 46 | 47 | void main() { 48 | 49 | vec4 worldPosition = modelMatrix * vec4( position, 1.0 ); 50 | vWorldPosition = worldPosition.xyz; 51 | 52 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 53 | gl_Position.z = gl_Position.w; // set z to camera.far 54 | 55 | vSunDirection = normalize( sunPosition ); 56 | 57 | vSunE = sunIntensity( dot( vSunDirection, up ) ); 58 | 59 | vSunfade = 1.0 - clamp( 1.0 - exp( ( sunPosition.y / 450000.0 ) ), 0.0, 1.0 ); 60 | 61 | float rayleighCoefficient = rayleigh - ( 1.0 * ( 1.0 - vSunfade ) ); 62 | 63 | // extinction (absorbtion + out scattering) 64 | // rayleigh coefficients 65 | vBetaR = totalRayleigh * rayleighCoefficient; 66 | 67 | // mie coefficients 68 | vBetaM = totalMie( turbidity ) * mieCoefficient; 69 | 70 | } 71 | -------------------------------------------------------------------------------- /objects/shaders/waterFragmentShader.glsl: -------------------------------------------------------------------------------- 1 | uniform sampler2D mirrorSampler; 2 | uniform float alpha; 3 | uniform float time; 4 | uniform float size; 5 | uniform float distortionScale; 6 | uniform sampler2D normalSampler; 7 | uniform vec3 sunColor; 8 | uniform vec3 sunDirection; 9 | uniform vec3 eye; 10 | uniform vec3 waterColor; 11 | varying vec4 mirrorCoord; 12 | varying vec4 worldPosition; 13 | uniform float speed; 14 | 15 | vec4 getNoise(vec2 uv) { 16 | float offset; 17 | if (speed == 0.0){ 18 | offset = time / 10.0; 19 | } 20 | else { 21 | offset = speed; 22 | } 23 | vec2 uv3 = uv / vec2(50.0, 50.0) - vec2(speed / 1000.0, offset); 24 | vec2 uv0 = vec2(0, 0); 25 | vec2 uv1 = vec2(0, 0); 26 | vec2 uv2 = vec2(0, 0); 27 | vec4 noise = texture2D(normalSampler, uv0) + 28 | texture2D(normalSampler, uv1) + 29 | texture2D(normalSampler, uv2) + 30 | texture2D(normalSampler, uv3); 31 | return noise * 0.5 - 1.0; 32 | } 33 | void sunLight(const vec3 surfaceNormal, const vec3 eyeDirection, float shiny, float spec, float diffuse, inout vec3 diffuseColor, inout vec3 specularColor) { 34 | vec3 reflection = normalize(reflect(-sunDirection, surfaceNormal)); 35 | float direction = max(0.0, dot(eyeDirection, reflection)); 36 | specularColor += pow(direction, shiny) * sunColor * spec; 37 | diffuseColor += max(dot(sunDirection, surfaceNormal), 0.0) * sunColor * diffuse; 38 | } 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | void main() { 48 | #include 49 | vec4 noise = getNoise(worldPosition.xz * size); 50 | vec3 surfaceNormal = normalize(noise.xzy * vec3(1.5, 1.0, 1.5)); 51 | vec3 diffuseLight = vec3(0.0); 52 | vec3 specularLight = vec3(0.0); 53 | vec3 worldToEye = eye-worldPosition.xyz; 54 | vec3 eyeDirection = normalize(worldToEye); 55 | sunLight(surfaceNormal, eyeDirection, 100.0, 2.0, 0.5, diffuseLight, specularLight); 56 | float distance = length(worldToEye); 57 | vec2 distortion = surfaceNormal.xz * (0.001 + 1.0 / distance) * distortionScale; 58 | vec3 reflectionSample = vec3(texture2D(mirrorSampler, mirrorCoord.xy / mirrorCoord.w + distortion)); 59 | float theta = max(dot(eyeDirection, surfaceNormal), 0.0); 60 | float rf0 = 0.3; 61 | float reflectance = rf0 + (1.0 - rf0) * pow((1.0 - theta), 5.0); 62 | vec3 scatter = max(0.0, dot(surfaceNormal, eyeDirection)) * waterColor; 63 | vec3 albedo = mix((sunColor * diffuseLight * 0.3 + scatter) * getShadowMask(), (vec3(0.1) + reflectionSample * 0.9 + reflectionSample * specularLight), reflectance); 64 | vec3 outgoingLight = albedo; 65 | gl_FragColor = vec4(outgoingLight, alpha); 66 | #include 67 | #include 68 | } 69 | -------------------------------------------------------------------------------- /rocketjourney/objects/shaders/waterFragmentShader.glsl: -------------------------------------------------------------------------------- 1 | uniform sampler2D mirrorSampler; 2 | uniform float alpha; 3 | uniform float time; 4 | uniform float size; 5 | uniform float distortionScale; 6 | uniform sampler2D normalSampler; 7 | uniform vec3 sunColor; 8 | uniform vec3 sunDirection; 9 | uniform vec3 eye; 10 | uniform vec3 waterColor; 11 | varying vec4 mirrorCoord; 12 | varying vec4 worldPosition; 13 | uniform float speed; 14 | 15 | vec4 getNoise(vec2 uv) { 16 | float offset; 17 | if (speed == 0.0){ 18 | offset = time / 10.0; 19 | } 20 | else { 21 | offset = speed; 22 | } 23 | vec2 uv3 = uv / vec2(50.0, 50.0) - vec2(speed / 1000.0, offset); 24 | vec2 uv0 = vec2(0, 0); 25 | vec2 uv1 = vec2(0, 0); 26 | vec2 uv2 = vec2(0, 0); 27 | vec4 noise = texture2D(normalSampler, uv0) + 28 | texture2D(normalSampler, uv1) + 29 | texture2D(normalSampler, uv2) + 30 | texture2D(normalSampler, uv3); 31 | return noise * 0.5 - 1.0; 32 | } 33 | void sunLight(const vec3 surfaceNormal, const vec3 eyeDirection, float shiny, float spec, float diffuse, inout vec3 diffuseColor, inout vec3 specularColor) { 34 | vec3 reflection = normalize(reflect(-sunDirection, surfaceNormal)); 35 | float direction = max(0.0, dot(eyeDirection, reflection)); 36 | specularColor += pow(direction, shiny) * sunColor * spec; 37 | diffuseColor += max(dot(sunDirection, surfaceNormal), 0.0) * sunColor * diffuse; 38 | } 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | void main() { 48 | #include 49 | vec4 noise = getNoise(worldPosition.xz * size); 50 | vec3 surfaceNormal = normalize(noise.xzy * vec3(1.5, 1.0, 1.5)); 51 | vec3 diffuseLight = vec3(0.0); 52 | vec3 specularLight = vec3(0.0); 53 | vec3 worldToEye = eye-worldPosition.xyz; 54 | vec3 eyeDirection = normalize(worldToEye); 55 | sunLight(surfaceNormal, eyeDirection, 100.0, 2.0, 0.5, diffuseLight, specularLight); 56 | float distance = length(worldToEye); 57 | vec2 distortion = surfaceNormal.xz * (0.001 + 1.0 / distance) * distortionScale; 58 | vec3 reflectionSample = vec3(texture2D(mirrorSampler, mirrorCoord.xy / mirrorCoord.w + distortion)); 59 | float theta = max(dot(eyeDirection, surfaceNormal), 0.0); 60 | float rf0 = 0.3; 61 | float reflectance = rf0 + (1.0 - rf0) * pow((1.0 - theta), 5.0); 62 | vec3 scatter = max(0.0, dot(surfaceNormal, eyeDirection)) * waterColor; 63 | vec3 albedo = mix((sunColor * diffuseLight * 0.3 + scatter) * getShadowMask(), (vec3(0.1) + reflectionSample * 0.9 + reflectionSample * specularLight), reflectance); 64 | vec3 outgoingLight = albedo; 65 | gl_FragColor = vec4(outgoingLight, alpha); 66 | #include 67 | #include 68 | } 69 | -------------------------------------------------------------------------------- /objects/shaders/skyFragmentShader.glsl: -------------------------------------------------------------------------------- 1 | varying vec3 vWorldPosition; 2 | varying vec3 vSunDirection; 3 | varying float vSunfade; 4 | varying vec3 vBetaR; 5 | varying vec3 vBetaM; 6 | varying float vSunE; 7 | 8 | uniform float mieDirectionalG; 9 | uniform vec3 up; 10 | 11 | const vec3 cameraPos = vec3( 0.0, 0.0, 0.0 ); 12 | 13 | // constants for atmospheric scattering 14 | const float pi = 3.141592653589793238462643383279502884197169; 15 | 16 | const float n = 1.0003; // refractive index of air 17 | const float N = 2.545E25; // number of molecules per unit volume for air at 288.15K and 1013mb (sea level -45 celsius) 18 | 19 | // optical length at zenith for molecules 20 | const float rayleighZenithLength = 8.4E3; 21 | const float mieZenithLength = 1.25E3; 22 | // 66 arc seconds -> degrees, and the cosine of that 23 | const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324; 24 | 25 | // 3.0 / ( 16.0 * pi ) 26 | const float THREE_OVER_SIXTEENPI = 0.05968310365946075; 27 | // 1.0 / ( 4.0 * pi ) 28 | const float ONE_OVER_FOURPI = 0.07957747154594767; 29 | 30 | float rayleighPhase( float cosTheta ) { 31 | return THREE_OVER_SIXTEENPI * ( 1.0 + pow( cosTheta, 2.0 ) ); 32 | } 33 | 34 | float hgPhase( float cosTheta, float g ) { 35 | float g2 = pow( g, 2.0 ); 36 | float inverse = 1.0 / pow( 1.0 - 2.0 * g * cosTheta + g2, 1.5 ); 37 | return ONE_OVER_FOURPI * ( ( 1.0 - g2 ) * inverse ); 38 | } 39 | 40 | void main() { 41 | 42 | vec3 direction = normalize( vWorldPosition - cameraPos ); 43 | 44 | // optical length 45 | // cutoff angle at 90 to avoid singularity in next formula. 46 | float zenithAngle = acos( max( 0.0, dot( up, direction ) ) ); 47 | float inverse = 1.0 / ( cos( zenithAngle ) + 0.15 * pow( 93.885 - ( ( zenithAngle * 180.0 ) / pi ), -1.253 ) ); 48 | float sR = rayleighZenithLength * inverse; 49 | float sM = mieZenithLength * inverse; 50 | 51 | // combined extinction factor 52 | vec3 Fex = exp( -( vBetaR * sR + vBetaM * sM ) ); 53 | 54 | // in scattering 55 | float cosTheta = dot( direction, vSunDirection ); 56 | 57 | float rPhase = rayleighPhase( cosTheta * 0.5 + 0.5 ); 58 | vec3 betaRTheta = vBetaR * rPhase; 59 | 60 | float mPhase = hgPhase( cosTheta, mieDirectionalG ); 61 | vec3 betaMTheta = vBetaM * mPhase; 62 | 63 | vec3 Lin = pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * ( 1.0 - Fex ), vec3( 1.5 ) ); 64 | Lin *= mix( vec3( 1.0 ), pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * Fex, vec3( 1.0 / 2.0 ) ), clamp( pow( 1.0 - dot( up, vSunDirection ), 5.0 ), 0.0, 1.0 ) ); 65 | 66 | // nightsky 67 | float theta = acos( direction.y ); // elevation --> y-axis, [-pi/2, pi/2] 68 | float phi = atan( direction.z, direction.x ); // azimuth --> x-axis [-pi/2, pi/2] 69 | vec2 uv = vec2( phi, theta ) / vec2( 2.0 * pi, pi ) + vec2( 0.5, 0.0 ); 70 | vec3 L0 = vec3( 0.1 ) * Fex; 71 | 72 | // composition + solar disc 73 | float sundisk = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta ); 74 | L0 += ( vSunE * 19000.0 * Fex ) * sundisk; 75 | 76 | vec3 texColor = ( Lin + L0 ) * 0.04 + vec3( 0.0, 0.0003, 0.00075 ); 77 | 78 | vec3 retColor = pow( texColor, vec3( 1.0 / ( 1.2 + ( 1.2 * vSunfade ) ) ) ); 79 | 80 | gl_FragColor = vec4( retColor, 1.0 ); 81 | 82 | #include 83 | #include 84 | 85 | } 86 | -------------------------------------------------------------------------------- /game/ui.ts: -------------------------------------------------------------------------------- 1 | import {sceneConfiguration, sceneSetup,} from "../game"; 2 | 3 | export const crystalUiElement = document.getElementById('crystalCount')!; 4 | export const shieldUiElement = document.getElementById('shieldCount')!; 5 | export const progressUiElement = document.getElementById('courseProgress')!; 6 | 7 | export const endLevelDescriptor = document.getElementById('levelDescriptor')!; 8 | export const endLevelShipStatus = document.getElementById('shipStatus')!; 9 | 10 | export const nextLevelButton = document.getElementById('nextLevel')!; 11 | 12 | export const startAgainButton = document.getElementById('startOver')!; 13 | export const startGameButton = document.getElementById('startGame')!; 14 | 15 | export const startPanel = document.getElementById('levelStartPanel')!; 16 | 17 | 18 | export const uiInit = () => { 19 | startAgainButton.onclick = () => { 20 | nextLevel(true); 21 | // sceneSetup(1); 22 | } 23 | } 24 | 25 | export const nextLevel = (reset: boolean = false) => { 26 | document.getElementById('endOfLevel')!.style!.display = ''; 27 | document.getElementById('endOfLevel')!.classList.remove('fadeOut'); 28 | document.getElementById('endOfLevel')!.classList.add('hidden'); 29 | document.getElementById('startGame')!.classList.remove('hidden'); 30 | document.getElementById('levelStartPanel')!.classList.remove('hidden'); 31 | 32 | // debugger; 33 | sceneConfiguration.cameraStartAnimationPlaying = false; 34 | sceneConfiguration.rocketMoving = false; 35 | sceneConfiguration.speed = 0.05; 36 | // sceneConfiguration.rocketMoving = false; 37 | sceneConfiguration.speed = sceneConfiguration.level * 0.1; 38 | if (reset) { 39 | sceneSetup(1) 40 | } else { 41 | sceneSetup(++sceneConfiguration.level); 42 | } 43 | } 44 | 45 | export const updateLevelEndUI = (damaged: boolean) => { 46 | endLevelDescriptor.innerText = `LEVEL ${sceneConfiguration.level}`; 47 | if (damaged) { 48 | endLevelShipStatus.innerText = 'Your ship has hit too many rocks and is too damaged to continue!\r\n\r\n' + 49 | 'We have another one you can use but you\'ll have to start over...'; 50 | nextLevelButton.classList.add('hidden'); 51 | startAgainButton.classList.remove('hidden'); 52 | } else { 53 | let shieldCount = sceneConfiguration.data.shieldsCollected; 54 | if (shieldCount == 5) { 55 | endLevelShipStatus.innerText = 'Your ship is in pristine condition!'; 56 | } else if (shieldCount > 1 && shieldCount < 5) { 57 | endLevelShipStatus.innerText = 'Your ship is in pretty good condition.'; 58 | } else if (shieldCount == 0) { 59 | endLevelShipStatus.innerText = 'Your ship is in the same condition as when you left.'; 60 | } else if (shieldCount >= -4 && shieldCount < 0) { 61 | endLevelShipStatus.innerText = 'Your ship is in pretty bad shape. We\'ll patch it up, but try to hit less rocks.'; 62 | } 63 | 64 | nextLevelButton.classList.remove('hidden'); 65 | startAgainButton.classList.add('hidden'); 66 | 67 | } 68 | } 69 | 70 | export const showLevelEndScreen = () => { 71 | // document. 72 | document.getElementById('endOfLevel')!.style!.display = 'flex'; 73 | document.getElementById('endOfLevel')!.classList.add('fadeOut'); 74 | document.getElementById('crystalCountLevelEnd')!.innerText = String(sceneConfiguration.data.crystalsCollected); 75 | 76 | 77 | } 78 | export const setProgress = (progress: string) => { 79 | let progressElement = document.getElementById('loadingProgress'); 80 | if (progressElement != null) { 81 | progressElement.innerText = progress; 82 | } 83 | } 84 | 85 | 86 | -------------------------------------------------------------------------------- /rocketjourney/game/ui.ts: -------------------------------------------------------------------------------- 1 | import {sceneConfiguration, sceneSetup,} from "../game"; 2 | 3 | export const crystalUiElement = document.getElementById('crystalCount')!; 4 | export const shieldUiElement = document.getElementById('shieldCount')!; 5 | export const progressUiElement = document.getElementById('courseProgress')!; 6 | 7 | export const endLevelDescriptor = document.getElementById('levelDescriptor')!; 8 | export const endLevelShipStatus = document.getElementById('shipStatus')!; 9 | 10 | export const nextLevelButton = document.getElementById('nextLevel')!; 11 | 12 | export const startAgainButton = document.getElementById('startOver')!; 13 | export const startGameButton = document.getElementById('startGame')!; 14 | 15 | export const startPanel = document.getElementById('levelStartPanel')!; 16 | export const levelIndicator = document.getElementById('levelIndicator')!; 17 | 18 | 19 | export const uiInit = () => { 20 | startAgainButton.onclick = () => { 21 | nextLevel(true); 22 | // sceneSetup(1); 23 | } 24 | } 25 | 26 | export const nextLevel = (reset: boolean = false) => { 27 | document.getElementById('endOfLevel')!.style!.display = ''; 28 | document.getElementById('endOfLevel')!.classList.remove('fadeOut'); 29 | document.getElementById('endOfLevel')!.classList.add('hidden'); 30 | document.getElementById('startGame')!.classList.remove('hidden'); 31 | document.getElementById('levelStartPanel')!.classList.remove('hidden'); 32 | 33 | // debugger; 34 | sceneConfiguration.cameraStartAnimationPlaying = false; 35 | sceneConfiguration.rocketMoving = false; 36 | sceneConfiguration.speed = 0.05; 37 | // sceneConfiguration.rocketMoving = false; 38 | sceneConfiguration.speed = sceneConfiguration.level * 0.1; 39 | if (reset) { 40 | sceneSetup(1) 41 | } else { 42 | sceneSetup(++sceneConfiguration.level); 43 | } 44 | } 45 | 46 | export const updateLevelEndUI = (damaged: boolean) => { 47 | endLevelDescriptor.innerText = `LEVEL ${sceneConfiguration.level}`; 48 | if (damaged) { 49 | endLevelShipStatus.innerText = 'Your ship has hit too many rocks and is too damaged to continue!\r\n\r\n' + 50 | 'We have another one you can use but you\'ll have to start over...'; 51 | nextLevelButton.classList.add('hidden'); 52 | startAgainButton.classList.remove('hidden'); 53 | } else { 54 | let shieldCount = sceneConfiguration.data.shieldsCollected; 55 | if (shieldCount == 5) { 56 | endLevelShipStatus.innerText = 'Your ship is in pristine condition!'; 57 | } else if (shieldCount > 1 && shieldCount < 5) { 58 | endLevelShipStatus.innerText = 'Your ship is in pretty good condition.'; 59 | } else if (shieldCount == 0) { 60 | endLevelShipStatus.innerText = 'Your ship is in the same condition as when you left.'; 61 | } else if (shieldCount >= -4 && shieldCount < 0) { 62 | endLevelShipStatus.innerText = 'Your ship is in pretty bad shape. We\'ll patch it up, but try to hit less rocks.'; 63 | } 64 | 65 | nextLevelButton.classList.remove('hidden'); 66 | startAgainButton.classList.add('hidden'); 67 | 68 | } 69 | } 70 | 71 | export const showLevelEndScreen = () => { 72 | // document. 73 | document.getElementById('endOfLevel')!.style!.display = 'flex'; 74 | document.getElementById('endOfLevel')!.classList.add('fadeOut'); 75 | document.getElementById('crystalCountLevelEnd')!.innerText = String(sceneConfiguration.data.crystalsCollected); 76 | 77 | 78 | } 79 | export const setProgress = (progress: string) => { 80 | let progressElement = document.getElementById('loadingProgress'); 81 | if (progressElement != null) { 82 | progressElement.innerText = progress; 83 | } 84 | } 85 | 86 | 87 | -------------------------------------------------------------------------------- /postprocessing/bloomPass.js: -------------------------------------------------------------------------------- 1 | import { 2 | AdditiveBlending, 3 | LinearFilter, 4 | RGBAFormat, 5 | ShaderMaterial, 6 | UniformsUtils, 7 | Vector2, 8 | WebGLRenderTarget 9 | } from 'three'; 10 | import { Pass, FullScreenQuad } from './pass.js'; 11 | import { CopyShader } from './copyShader.js'; 12 | import { ConvolutionShader } from './ConvolutionShader.js'; 13 | 14 | class BloomPass extends Pass { 15 | 16 | constructor( strength = 1, kernelSize = 25, sigma = 4, resolution = 256 ) { 17 | 18 | super(); 19 | 20 | // render targets 21 | 22 | const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; 23 | 24 | this.renderTargetX = new WebGLRenderTarget( resolution, resolution, pars ); 25 | this.renderTargetX.texture.name = 'BloomPass.x'; 26 | this.renderTargetY = new WebGLRenderTarget( resolution, resolution, pars ); 27 | this.renderTargetY.texture.name = 'BloomPass.y'; 28 | 29 | // copy material 30 | 31 | if ( CopyShader === undefined ) console.error( 'THREE.BloomPass relies on CopyShader' ); 32 | 33 | const copyShader = CopyShader; 34 | 35 | this.copyUniforms = UniformsUtils.clone( copyShader.uniforms ); 36 | 37 | this.copyUniforms[ 'opacity' ].value = strength; 38 | 39 | this.materialCopy = new ShaderMaterial( { 40 | 41 | uniforms: this.copyUniforms, 42 | vertexShader: copyShader.vertexShader, 43 | fragmentShader: copyShader.fragmentShader, 44 | blending: AdditiveBlending, 45 | transparent: true 46 | 47 | } ); 48 | 49 | // convolution material 50 | 51 | if ( ConvolutionShader === undefined ) console.error( 'THREE.BloomPass relies on ConvolutionShader' ); 52 | 53 | const convolutionShader = ConvolutionShader; 54 | 55 | this.convolutionUniforms = UniformsUtils.clone( convolutionShader.uniforms ); 56 | 57 | this.convolutionUniforms[ 'uImageIncrement' ].value = BloomPass.blurX; 58 | this.convolutionUniforms[ 'cKernel' ].value = ConvolutionShader.buildKernel( sigma ); 59 | 60 | this.materialConvolution = new ShaderMaterial( { 61 | 62 | uniforms: this.convolutionUniforms, 63 | vertexShader: convolutionShader.vertexShader, 64 | fragmentShader: convolutionShader.fragmentShader, 65 | defines: { 66 | 'KERNEL_SIZE_FLOAT': kernelSize.toFixed( 1 ), 67 | 'KERNEL_SIZE_INT': kernelSize.toFixed( 0 ) 68 | } 69 | 70 | } ); 71 | 72 | this.needsSwap = false; 73 | 74 | this.fsQuad = new FullScreenQuad( null ); 75 | 76 | } 77 | 78 | render( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) { 79 | 80 | if ( maskActive ) renderer.state.buffers.stencil.setTest( false ); 81 | 82 | // Render quad with blured scene into texture (convolution pass 1) 83 | 84 | this.fsQuad.material = this.materialConvolution; 85 | 86 | this.convolutionUniforms[ 'tDiffuse' ].value = readBuffer.texture; 87 | this.convolutionUniforms[ 'uImageIncrement' ].value = BloomPass.blurX; 88 | 89 | renderer.setRenderTarget( this.renderTargetX ); 90 | renderer.clear(); 91 | this.fsQuad.render( renderer ); 92 | 93 | 94 | // Render quad with blured scene into texture (convolution pass 2) 95 | 96 | this.convolutionUniforms[ 'tDiffuse' ].value = this.renderTargetX.texture; 97 | this.convolutionUniforms[ 'uImageIncrement' ].value = BloomPass.blurY; 98 | 99 | renderer.setRenderTarget( this.renderTargetY ); 100 | renderer.clear(); 101 | this.fsQuad.render( renderer ); 102 | 103 | // Render original scene with superimposed blur to texture 104 | 105 | this.fsQuad.material = this.materialCopy; 106 | 107 | this.copyUniforms[ 'tDiffuse' ].value = this.renderTargetY.texture; 108 | 109 | if ( maskActive ) renderer.state.buffers.stencil.setTest( true ); 110 | 111 | renderer.setRenderTarget( readBuffer ); 112 | if ( this.clear ) renderer.clear(); 113 | this.fsQuad.render( renderer ); 114 | 115 | } 116 | 117 | } 118 | 119 | BloomPass.blurX = new Vector2( 0.001953125, 0.0 ); 120 | BloomPass.blurY = new Vector2( 0.0, 0.001953125 ); 121 | 122 | export { BloomPass }; 123 | -------------------------------------------------------------------------------- /game/objects.ts: -------------------------------------------------------------------------------- 1 | import {Group, MathUtils, Object3D} from "three"; 2 | import {scene, sceneConfiguration} from "../game"; 3 | import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader"; 4 | import {setProgress} from "./ui"; 5 | 6 | export const environmentBits = new Array(); 7 | 8 | export const challengeRows = new Array(); 9 | export let rocketModel: Object3D; 10 | export let starterBay: Group; 11 | export let mothershipModel: Group; 12 | export let cliffsModel: Object3D; 13 | export let crystalModel: Object3D; 14 | export let rockModel: Object3D; 15 | export let shieldModel: Object3D; 16 | 17 | const gltfLoader = new GLTFLoader(); 18 | 19 | 20 | const rocketGLTF = 'static/models/rocket/scene.gltf'; 21 | const cliffsGLTF = 'static/models/cliffs/scene.gltf'; 22 | const crystalsGLTF = 'static/models/glowing_crystals/scene.gltf'; 23 | const rockGLTF = 'static/models/glowing_rock/scene.gltf'; 24 | const shieldGLTF = 'static/models/shield_item/scene.gltf'; 25 | const starterBayGLTF = 'static/models/start_bay/scene.gltf'; 26 | const mothershipGLTF = 'static/models/spaceship_nortend/scene.gltf'; 27 | 28 | export const objectsInit = async () => { 29 | cliffsModel = (await gltfLoader.loadAsync(cliffsGLTF)).scene.children[0]; 30 | setProgress('Loading energy crystal model...'); 31 | crystalModel = (await gltfLoader.loadAsync(crystalsGLTF)).scene.children[0]; 32 | setProgress('Loading rock model...'); 33 | rockModel = (await gltfLoader.loadAsync(rockGLTF)).scene.children[0]; 34 | setProgress('Loading shield model...'); 35 | shieldModel = (await gltfLoader.loadAsync(shieldGLTF)).scene.children[0]; 36 | setProgress('Loading rocket model ...'); 37 | rocketModel = (await gltfLoader.loadAsync(rocketGLTF)).scene.children[0]; 38 | setProgress('Loading starter bay...'); 39 | starterBay = (await gltfLoader.loadAsync(starterBayGLTF)).scene; 40 | setProgress('Loading mothership...'); 41 | mothershipModel = (await gltfLoader.loadAsync(mothershipGLTF)).scene; 42 | } 43 | 44 | export const addBackgroundBit = (count: number, horizonSpawn: boolean = false) => { 45 | // If we're spawning on the horizon, always spawn at a position far away from the player 46 | // Otherwise, place the rocks at certain intervals into the distance- 47 | let zOffset = (horizonSpawn ? -1400 : -(60 * count)); 48 | // Create a copy of our original rock model 49 | let thisRock = cliffsModel.clone(); 50 | // Set the scale appropriately for the scene 51 | thisRock.scale.set(0.02, 0.02, 0.02); 52 | // If the row that we're adding is divisble by two, place the rock to the left of the user 53 | // otherwise, place it to the right of the user. 54 | thisRock.position.set(count % 2 == 0 ? 60 - Math.random() : -60 - Math.random(), 0, zOffset); 55 | // Rotate the rock to a better angle 56 | thisRock.rotation.set(MathUtils.degToRad(-90), 0, Math.random()); 57 | // Finally, add the rock to the scene 58 | scene.add(thisRock); 59 | // Add the rock to the beginning of the environmentBits array to keep track of them (so we can clean up later) 60 | environmentBits.unshift(thisRock);// add to beginning of array 61 | } 62 | 63 | export const addChallengeRow = (count: number, horizonSpawn: boolean = false) => { 64 | // Work out how far away this challenge row should be 65 | let zOffset = (horizonSpawn ? -1400 : -(count * 60)); 66 | // Create a Group for the objects. This will be the parent for these objects. 67 | let rowGroup = new Group(); 68 | rowGroup.position.z = zOffset; 69 | for (let i = 0; i < 5; i++) { 70 | // Calculate a random number between 1 and 10 71 | const random = Math.random() * 10; 72 | 73 | // If it's less than 2, create a crystal 74 | if (random < 2) { 75 | let crystal = addCrystal(i); 76 | rowGroup.add(crystal); 77 | 78 | } 79 | // If it's less than 4, spawn a rock 80 | else if (random < 4) { 81 | let rock = addRock(i); 82 | rowGroup.add(rock); 83 | } 84 | // but if it's more than 9, spawn a shield 85 | else if (random > 9) { 86 | let shield = addShield(i); 87 | rowGroup.add(shield); 88 | } 89 | } 90 | // Add the row to the challengeRows array to keep track of it, and so we can clean them up later 91 | challengeRows.unshift({rowParent: rowGroup, index: sceneConfiguration.challengeRowCount++}); 92 | // Finally add the row to the scene 93 | scene.add(rowGroup); 94 | } 95 | const addCrystal = (rowCell: number) => { 96 | let crystal = crystalModel.clone(); 97 | // crystal.position.z = zOffset; 98 | crystal.position.x = rowCell * 11 - 20; 99 | crystal.scale.set(0.02, 0.02, 0.02); 100 | crystal.userData.objectType = ObjectType.CRYSTAL; 101 | return crystal; 102 | } 103 | const addRock = (rowCell: number) => { 104 | let rock = rockModel.clone(); 105 | rock.position.x = rowCell * 11 - 20; 106 | rock.scale.set(5, 5, 5); 107 | rock.position.setY(5); 108 | rock.userData.objectType = ObjectType.ROCK; 109 | return rock; 110 | } 111 | const addShield = (rowCell: number) => { 112 | let shield = shieldModel.clone(); 113 | shield.position.x = rowCell * 11 - 20; 114 | shield.position.y = 8; 115 | shield.userData.objectType = ObjectType.SHIELD_ITEM; 116 | return shield; 117 | } 118 | 119 | export enum ObjectType { 120 | ROCK, 121 | CRYSTAL, 122 | SHIELD_ITEM 123 | } 124 | 125 | interface ChallengeRow { 126 | index: number; 127 | rowParent: Group; 128 | } 129 | -------------------------------------------------------------------------------- /rocketjourney/game/objects.ts: -------------------------------------------------------------------------------- 1 | import {Group, MathUtils, Object3D} from "three"; 2 | import {scene, sceneConfiguration} from "../game"; 3 | import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader"; 4 | import {setProgress} from "./ui"; 5 | 6 | export const environmentBits = new Array(); 7 | 8 | export const challengeRows = new Array(); 9 | export let rocketModel: Object3D; 10 | export let starterBay: Group; 11 | export let mothershipModel: Group; 12 | export let cliffsModel: Object3D; 13 | export let crystalModel: Object3D; 14 | export let rockModel: Object3D; 15 | export let shieldModel: Object3D; 16 | 17 | const gltfLoader = new GLTFLoader(); 18 | 19 | 20 | const rocketGLTF = 'static/models/rocket/scene.gltf'; 21 | const cliffsGLTF = 'static/models/cliffs/scene.gltf'; 22 | const crystalsGLTF = 'static/models/glowing_crystals/scene.gltf'; 23 | const rockGLTF = 'static/models/glowing_rock/scene.gltf'; 24 | const shieldGLTF = 'static/models/shield_item/scene.gltf'; 25 | const starterBayGLTF = 'static/models/start_bay/scene.gltf'; 26 | const mothershipGLTF = 'static/models/spaceship_nortend/scene.gltf'; 27 | 28 | export const objectsInit = async () => { 29 | cliffsModel = (await gltfLoader.loadAsync(cliffsGLTF)).scene.children[0]; 30 | setProgress('Loading energy crystal model...'); 31 | crystalModel = (await gltfLoader.loadAsync(crystalsGLTF)).scene.children[0]; 32 | setProgress('Loading rock model...'); 33 | rockModel = (await gltfLoader.loadAsync(rockGLTF)).scene.children[0]; 34 | setProgress('Loading shield model...'); 35 | shieldModel = (await gltfLoader.loadAsync(shieldGLTF)).scene.children[0]; 36 | setProgress('Loading rocket model ...'); 37 | rocketModel = (await gltfLoader.loadAsync(rocketGLTF)).scene.children[0]; 38 | setProgress('Loading starter bay...'); 39 | starterBay = (await gltfLoader.loadAsync(starterBayGLTF)).scene; 40 | setProgress('Loading mothership...'); 41 | mothershipModel = (await gltfLoader.loadAsync(mothershipGLTF)).scene; 42 | } 43 | 44 | export const addBackgroundBit = (count: number, horizonSpawn: boolean = false) => { 45 | // If we're spawning on the horizon, always spawn at a position far away from the player 46 | // Otherwise, place the rocks at certain intervals into the distance- 47 | let zOffset = (horizonSpawn ? -1400 : -(60 * count)); 48 | // Create a copy of our original rock model 49 | let thisRock = cliffsModel.clone(); 50 | // Set the scale appropriately for the scene 51 | thisRock.scale.set(0.02, 0.02, 0.02); 52 | // If the row that we're adding is divisble by two, place the rock to the left of the user 53 | // otherwise, place it to the right of the user. 54 | thisRock.position.set(count % 2 == 0 ? 60 - Math.random() : -60 - Math.random(), 0, zOffset); 55 | // Rotate the rock to a better angle 56 | thisRock.rotation.set(MathUtils.degToRad(-90), 0, Math.random()); 57 | // Finally, add the rock to the scene 58 | scene.add(thisRock); 59 | // Add the rock to the beginning of the environmentBits array to keep track of them (so we can clean up later) 60 | environmentBits.unshift(thisRock);// add to beginning of array 61 | } 62 | 63 | export const addChallengeRow = (count: number, horizonSpawn: boolean = false) => { 64 | // Work out how far away this challenge row should be 65 | let zOffset = (horizonSpawn ? -1400 : -(count * 60)); 66 | // Create a Group for the objects. This will be the parent for these objects. 67 | let rowGroup = new Group(); 68 | rowGroup.position.z = zOffset; 69 | for (let i = 0; i < 5; i++) { 70 | // Calculate a random number between 1 and 10 71 | const random = Math.random() * 10; 72 | 73 | // If it's less than 2, create a crystal 74 | if (random < 2) { 75 | let crystal = addCrystal(i); 76 | rowGroup.add(crystal); 77 | 78 | } 79 | // If it's less than 4, spawn a rock 80 | else if (random < 4) { 81 | let rock = addRock(i); 82 | rowGroup.add(rock); 83 | } 84 | // but if it's more than 9, spawn a shield 85 | else if (random > 9) { 86 | let shield = addShield(i); 87 | rowGroup.add(shield); 88 | } 89 | } 90 | // Add the row to the challengeRows array to keep track of it, and so we can clean them up later 91 | challengeRows.unshift({rowParent: rowGroup, index: sceneConfiguration.challengeRowCount++}); 92 | // Finally add the row to the scene 93 | scene.add(rowGroup); 94 | } 95 | const addCrystal = (rowCell: number) => { 96 | let crystal = crystalModel.clone(); 97 | // crystal.position.z = zOffset; 98 | crystal.position.x = rowCell * 11 - 20; 99 | crystal.scale.set(0.02, 0.02, 0.02); 100 | crystal.userData.objectType = ObjectType.CRYSTAL; 101 | return crystal; 102 | } 103 | const addRock = (rowCell: number) => { 104 | let rock = rockModel.clone(); 105 | rock.position.x = rowCell * 11 - 20; 106 | rock.scale.set(5, 5, 5); 107 | rock.position.setY(5); 108 | rock.userData.objectType = ObjectType.ROCK; 109 | return rock; 110 | } 111 | const addShield = (rowCell: number) => { 112 | let shield = shieldModel.clone(); 113 | shield.position.x = rowCell * 11 - 20; 114 | shield.position.y = 8; 115 | shield.userData.objectType = ObjectType.SHIELD_ITEM; 116 | return shield; 117 | } 118 | 119 | export enum ObjectType { 120 | ROCK, 121 | CRYSTAL, 122 | SHIELD_ITEM 123 | } 124 | 125 | interface ChallengeRow { 126 | index: number; 127 | rowParent: Group; 128 | } 129 | -------------------------------------------------------------------------------- /static/models/rocket/scene.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "accessors": [ 3 | { 4 | "bufferView": 2, 5 | "componentType": 5126, 6 | "count": 2254, 7 | "max": [ 8 | 13.429120063781738, 9 | 8.1058225631713867, 10 | 30.018577575683594 11 | ], 12 | "min": [ 13 | -13.429120063781738, 14 | -8.1058225631713867, 15 | -3.871978759765625 16 | ], 17 | "type": "VEC3" 18 | }, 19 | { 20 | "bufferView": 2, 21 | "byteOffset": 27048, 22 | "componentType": 5126, 23 | "count": 2254, 24 | "max": [ 25 | 0.99999827146530151, 26 | 1, 27 | 1 28 | ], 29 | "min": [ 30 | -0.99999827146530151, 31 | -1, 32 | -1 33 | ], 34 | "type": "VEC3" 35 | }, 36 | { 37 | "bufferView": 3, 38 | "componentType": 5126, 39 | "count": 2254, 40 | "max": [ 41 | 1, 42 | 0.99999982118606567, 43 | 0.89828240871429443, 44 | 1 45 | ], 46 | "min": [ 47 | -1, 48 | -0.99997544288635254, 49 | -0.89781618118286133, 50 | 1 51 | ], 52 | "type": "VEC4" 53 | }, 54 | { 55 | "bufferView": 1, 56 | "componentType": 5126, 57 | "count": 2254, 58 | "max": [ 59 | 0.98976516723632812, 60 | 0.98559367656707764 61 | ], 62 | "min": [ 63 | 0.010633219964802265, 64 | 0.012985818088054657 65 | ], 66 | "type": "VEC2" 67 | }, 68 | { 69 | "bufferView": 0, 70 | "componentType": 5125, 71 | "count": 8268, 72 | "max": [ 73 | 2253 74 | ], 75 | "min": [ 76 | 0 77 | ], 78 | "type": "SCALAR" 79 | } 80 | ], 81 | "asset": { 82 | "extras": { 83 | "author": "Constantine Tvalashvili (https://sketchfab.com/marooned3d)", 84 | "license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)", 85 | "source": "https://sketchfab.com/models/ffedf6c2e1de40bcaef5c57301ca94fd", 86 | "title": "Rocket" 87 | }, 88 | "generator": "Sketchfab-3.18.5", 89 | "version": "2.0" 90 | }, 91 | "bufferViews": [ 92 | { 93 | "buffer": 0, 94 | "byteLength": 33072, 95 | "byteOffset": 0, 96 | "name": "floatBufferViews", 97 | "target": 34963 98 | }, 99 | { 100 | "buffer": 0, 101 | "byteLength": 18032, 102 | "byteOffset": 33072, 103 | "byteStride": 8, 104 | "name": "floatBufferViews", 105 | "target": 34962 106 | }, 107 | { 108 | "buffer": 0, 109 | "byteLength": 54096, 110 | "byteOffset": 51104, 111 | "byteStride": 12, 112 | "name": "floatBufferViews", 113 | "target": 34962 114 | }, 115 | { 116 | "buffer": 0, 117 | "byteLength": 36064, 118 | "byteOffset": 105200, 119 | "byteStride": 16, 120 | "name": "floatBufferViews", 121 | "target": 34962 122 | } 123 | ], 124 | "buffers": [ 125 | { 126 | "byteLength": 141264, 127 | "uri": "scene.bin" 128 | } 129 | ], 130 | "extensionsUsed": [ 131 | "KHR_materials_unlit" 132 | ], 133 | "images": [ 134 | { 135 | "uri": "textures/Texture_baseColor.jpg" 136 | } 137 | ], 138 | "materials": [ 139 | { 140 | "doubleSided": false, 141 | "extensions": { 142 | "KHR_materials_unlit": {} 143 | }, 144 | "name": "Texture", 145 | "pbrMetallicRoughness": { 146 | "baseColorFactor": [ 147 | 1, 148 | 1, 149 | 1, 150 | 1 151 | ], 152 | "baseColorTexture": { 153 | "index": 0, 154 | "texCoord": 0 155 | }, 156 | "metallicFactor": 0, 157 | "roughnessFactor": 1 158 | } 159 | } 160 | ], 161 | "meshes": [ 162 | { 163 | "name": "Cylinder001_Texture_0", 164 | "primitives": [ 165 | { 166 | "attributes": { 167 | "NORMAL": 1, 168 | "POSITION": 0, 169 | "TANGENT": 2, 170 | "TEXCOORD_0": 3 171 | }, 172 | "indices": 4, 173 | "material": 0, 174 | "mode": 4 175 | } 176 | ] 177 | } 178 | ], 179 | "nodes": [ 180 | { 181 | "children": [ 182 | 1 183 | ], 184 | "name": "RootNode (gltf orientation matrix)", 185 | "rotation": [ 186 | -0.70710678118654746, 187 | -0, 188 | -0, 189 | 0.70710678118654757 190 | ] 191 | }, 192 | { 193 | "children": [ 194 | 2 195 | ], 196 | "name": "RootNode (model correction matrix)" 197 | }, 198 | { 199 | "children": [ 200 | 3 201 | ], 202 | "matrix": [ 203 | 1, 204 | 0, 205 | 0, 206 | 0, 207 | 0, 208 | 0, 209 | 1, 210 | 0, 211 | 0, 212 | -1, 213 | 0, 214 | 0, 215 | 0, 216 | 0, 217 | 0, 218 | 1 219 | ], 220 | "name": "9d7ea369abab47818f8024e5905fa3a2.fbx" 221 | }, 222 | { 223 | "children": [ 224 | 4 225 | ], 226 | "name": "RootNode" 227 | }, 228 | { 229 | "children": [ 230 | 5 231 | ], 232 | "matrix": [ 233 | 1, 234 | 0, 235 | 0, 236 | 0, 237 | 0, 238 | 2.2204460492503131e-16, 239 | -1, 240 | 0, 241 | 0, 242 | 1, 243 | 2.2204460492503131e-16, 244 | 0, 245 | 0, 246 | -13.867504119873047, 247 | -6.06167873229424e-07, 248 | 1 249 | ], 250 | "name": "Cylinder001" 251 | }, 252 | { 253 | "mesh": 0, 254 | "name": "Cylinder001_Texture_0" 255 | } 256 | ], 257 | "samplers": [ 258 | { 259 | "magFilter": 9729, 260 | "minFilter": 9987, 261 | "wrapS": 10497, 262 | "wrapT": 10497 263 | } 264 | ], 265 | "scene": 0, 266 | "scenes": [ 267 | { 268 | "name": "OSG_Scene", 269 | "nodes": [ 270 | 0 271 | ] 272 | } 273 | ], 274 | "textures": [ 275 | { 276 | "sampler": 0, 277 | "source": 0 278 | } 279 | ] 280 | } 281 | 282 | -------------------------------------------------------------------------------- /rocketjourney/static/models/rocket/scene.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "accessors": [ 3 | { 4 | "bufferView": 2, 5 | "componentType": 5126, 6 | "count": 2254, 7 | "max": [ 8 | 13.429120063781738, 9 | 8.1058225631713867, 10 | 30.018577575683594 11 | ], 12 | "min": [ 13 | -13.429120063781738, 14 | -8.1058225631713867, 15 | -3.871978759765625 16 | ], 17 | "type": "VEC3" 18 | }, 19 | { 20 | "bufferView": 2, 21 | "byteOffset": 27048, 22 | "componentType": 5126, 23 | "count": 2254, 24 | "max": [ 25 | 0.99999827146530151, 26 | 1, 27 | 1 28 | ], 29 | "min": [ 30 | -0.99999827146530151, 31 | -1, 32 | -1 33 | ], 34 | "type": "VEC3" 35 | }, 36 | { 37 | "bufferView": 3, 38 | "componentType": 5126, 39 | "count": 2254, 40 | "max": [ 41 | 1, 42 | 0.99999982118606567, 43 | 0.89828240871429443, 44 | 1 45 | ], 46 | "min": [ 47 | -1, 48 | -0.99997544288635254, 49 | -0.89781618118286133, 50 | 1 51 | ], 52 | "type": "VEC4" 53 | }, 54 | { 55 | "bufferView": 1, 56 | "componentType": 5126, 57 | "count": 2254, 58 | "max": [ 59 | 0.98976516723632812, 60 | 0.98559367656707764 61 | ], 62 | "min": [ 63 | 0.010633219964802265, 64 | 0.012985818088054657 65 | ], 66 | "type": "VEC2" 67 | }, 68 | { 69 | "bufferView": 0, 70 | "componentType": 5125, 71 | "count": 8268, 72 | "max": [ 73 | 2253 74 | ], 75 | "min": [ 76 | 0 77 | ], 78 | "type": "SCALAR" 79 | } 80 | ], 81 | "asset": { 82 | "extras": { 83 | "author": "Constantine Tvalashvili (https://sketchfab.com/marooned3d)", 84 | "license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)", 85 | "source": "https://sketchfab.com/models/ffedf6c2e1de40bcaef5c57301ca94fd", 86 | "title": "Rocket" 87 | }, 88 | "generator": "Sketchfab-3.18.5", 89 | "version": "2.0" 90 | }, 91 | "bufferViews": [ 92 | { 93 | "buffer": 0, 94 | "byteLength": 33072, 95 | "byteOffset": 0, 96 | "name": "floatBufferViews", 97 | "target": 34963 98 | }, 99 | { 100 | "buffer": 0, 101 | "byteLength": 18032, 102 | "byteOffset": 33072, 103 | "byteStride": 8, 104 | "name": "floatBufferViews", 105 | "target": 34962 106 | }, 107 | { 108 | "buffer": 0, 109 | "byteLength": 54096, 110 | "byteOffset": 51104, 111 | "byteStride": 12, 112 | "name": "floatBufferViews", 113 | "target": 34962 114 | }, 115 | { 116 | "buffer": 0, 117 | "byteLength": 36064, 118 | "byteOffset": 105200, 119 | "byteStride": 16, 120 | "name": "floatBufferViews", 121 | "target": 34962 122 | } 123 | ], 124 | "buffers": [ 125 | { 126 | "byteLength": 141264, 127 | "uri": "scene.bin" 128 | } 129 | ], 130 | "extensionsUsed": [ 131 | "KHR_materials_unlit" 132 | ], 133 | "images": [ 134 | { 135 | "uri": "textures/Texture_baseColor.jpg" 136 | } 137 | ], 138 | "materials": [ 139 | { 140 | "doubleSided": false, 141 | "extensions": { 142 | "KHR_materials_unlit": {} 143 | }, 144 | "name": "Texture", 145 | "pbrMetallicRoughness": { 146 | "baseColorFactor": [ 147 | 1, 148 | 1, 149 | 1, 150 | 1 151 | ], 152 | "baseColorTexture": { 153 | "index": 0, 154 | "texCoord": 0 155 | }, 156 | "metallicFactor": 0, 157 | "roughnessFactor": 1 158 | } 159 | } 160 | ], 161 | "meshes": [ 162 | { 163 | "name": "Cylinder001_Texture_0", 164 | "primitives": [ 165 | { 166 | "attributes": { 167 | "NORMAL": 1, 168 | "POSITION": 0, 169 | "TANGENT": 2, 170 | "TEXCOORD_0": 3 171 | }, 172 | "indices": 4, 173 | "material": 0, 174 | "mode": 4 175 | } 176 | ] 177 | } 178 | ], 179 | "nodes": [ 180 | { 181 | "children": [ 182 | 1 183 | ], 184 | "name": "RootNode (gltf orientation matrix)", 185 | "rotation": [ 186 | -0.70710678118654746, 187 | -0, 188 | -0, 189 | 0.70710678118654757 190 | ] 191 | }, 192 | { 193 | "children": [ 194 | 2 195 | ], 196 | "name": "RootNode (model correction matrix)" 197 | }, 198 | { 199 | "children": [ 200 | 3 201 | ], 202 | "matrix": [ 203 | 1, 204 | 0, 205 | 0, 206 | 0, 207 | 0, 208 | 0, 209 | 1, 210 | 0, 211 | 0, 212 | -1, 213 | 0, 214 | 0, 215 | 0, 216 | 0, 217 | 0, 218 | 1 219 | ], 220 | "name": "9d7ea369abab47818f8024e5905fa3a2.fbx" 221 | }, 222 | { 223 | "children": [ 224 | 4 225 | ], 226 | "name": "RootNode" 227 | }, 228 | { 229 | "children": [ 230 | 5 231 | ], 232 | "matrix": [ 233 | 1, 234 | 0, 235 | 0, 236 | 0, 237 | 0, 238 | 2.2204460492503131e-16, 239 | -1, 240 | 0, 241 | 0, 242 | 1, 243 | 2.2204460492503131e-16, 244 | 0, 245 | 0, 246 | -13.867504119873047, 247 | -6.06167873229424e-07, 248 | 1 249 | ], 250 | "name": "Cylinder001" 251 | }, 252 | { 253 | "mesh": 0, 254 | "name": "Cylinder001_Texture_0" 255 | } 256 | ], 257 | "samplers": [ 258 | { 259 | "magFilter": 9729, 260 | "minFilter": 9987, 261 | "wrapS": 10497, 262 | "wrapT": 10497 263 | } 264 | ], 265 | "scene": 0, 266 | "scenes": [ 267 | { 268 | "name": "OSG_Scene", 269 | "nodes": [ 270 | 0 271 | ] 272 | } 273 | ], 274 | "textures": [ 275 | { 276 | "sampler": 0, 277 | "source": 0 278 | } 279 | ] 280 | } 281 | 282 | -------------------------------------------------------------------------------- /static/models/glowing_rock/scene.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "accessors": [ 3 | { 4 | "bufferView": 2, 5 | "componentType": 5126, 6 | "count": 1152, 7 | "max": [ 8 | 1, 9 | 0.8750920295715332, 10 | 0.96690100431442261 11 | ], 12 | "min": [ 13 | -1, 14 | -0.8750920295715332, 15 | -0.96690100431442261 16 | ], 17 | "type": "VEC3" 18 | }, 19 | { 20 | "bufferView": 2, 21 | "byteOffset": 13824, 22 | "componentType": 5126, 23 | "count": 1152, 24 | "max": [ 25 | 0.99950689077377319, 26 | 0.9927094578742981, 27 | 0.97859150171279907 28 | ], 29 | "min": [ 30 | -0.99143302440643311, 31 | -0.99999934434890747, 32 | -0.99826925992965698 33 | ], 34 | "type": "VEC3" 35 | }, 36 | { 37 | "bufferView": 3, 38 | "componentType": 5126, 39 | "count": 1152, 40 | "max": [ 41 | 0.9946320652961731, 42 | 0.99990278482437134, 43 | 0.99392831325531006, 44 | 1 45 | ], 46 | "min": [ 47 | -0.98853772878646851, 48 | -0.79022294282913208, 49 | -0.97787630558013916, 50 | 1 51 | ], 52 | "type": "VEC4" 53 | }, 54 | { 55 | "bufferView": 1, 56 | "componentType": 5126, 57 | "count": 1152, 58 | "max": [ 59 | 0.97502702474594116, 60 | 0.98724997043609619 61 | ], 62 | "min": [ 63 | 0.018356300890445709, 64 | 0.012071000412106514 65 | ], 66 | "type": "VEC2" 67 | }, 68 | { 69 | "bufferView": 0, 70 | "componentType": 5125, 71 | "count": 6648, 72 | "max": [ 73 | 1151 74 | ], 75 | "min": [ 76 | 0 77 | ], 78 | "type": "SCALAR" 79 | } 80 | ], 81 | "asset": { 82 | "extras": { 83 | "author": "JAY2.0 (https://sketchfab.com/JAY2.0)", 84 | "license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)", 85 | "source": "https://sketchfab.com/3d-models/glowing-rock-c1826814fe1641248fce16b79ac29e41", 86 | "title": "Glowing Rock" 87 | }, 88 | "generator": "Sketchfab-6.82.0", 89 | "version": "2.0" 90 | }, 91 | "bufferViews": [ 92 | { 93 | "buffer": 0, 94 | "byteLength": 26592, 95 | "byteOffset": 0, 96 | "name": "floatBufferViews", 97 | "target": 34963 98 | }, 99 | { 100 | "buffer": 0, 101 | "byteLength": 9216, 102 | "byteOffset": 26592, 103 | "byteStride": 8, 104 | "name": "floatBufferViews", 105 | "target": 34962 106 | }, 107 | { 108 | "buffer": 0, 109 | "byteLength": 27648, 110 | "byteOffset": 35808, 111 | "byteStride": 12, 112 | "name": "floatBufferViews", 113 | "target": 34962 114 | }, 115 | { 116 | "buffer": 0, 117 | "byteLength": 18432, 118 | "byteOffset": 63456, 119 | "byteStride": 16, 120 | "name": "floatBufferViews", 121 | "target": 34962 122 | } 123 | ], 124 | "buffers": [ 125 | { 126 | "byteLength": 81888, 127 | "uri": "scene.bin" 128 | } 129 | ], 130 | "images": [ 131 | { 132 | "uri": "textures/lambert1_baseColor.jpeg" 133 | }, 134 | { 135 | "uri": "textures/lambert1_metallicRoughness.png" 136 | }, 137 | { 138 | "uri": "textures/lambert1_emissive.jpeg" 139 | }, 140 | { 141 | "uri": "textures/lambert1_normal.png" 142 | } 143 | ], 144 | "materials": [ 145 | { 146 | "doubleSided": true, 147 | "emissiveFactor": [ 148 | 1, 149 | 1, 150 | 1 151 | ], 152 | "emissiveTexture": { 153 | "index": 2, 154 | "texCoord": 0 155 | }, 156 | "name": "lambert1", 157 | "normalTexture": { 158 | "index": 3, 159 | "scale": 1, 160 | "texCoord": 0 161 | }, 162 | "occlusionTexture": { 163 | "index": 1, 164 | "strength": 1, 165 | "texCoord": 0 166 | }, 167 | "pbrMetallicRoughness": { 168 | "baseColorFactor": [ 169 | 1, 170 | 1, 171 | 1, 172 | 1 173 | ], 174 | "baseColorTexture": { 175 | "index": 0, 176 | "texCoord": 0 177 | }, 178 | "metallicFactor": 1, 179 | "metallicRoughnessTexture": { 180 | "index": 1, 181 | "texCoord": 0 182 | }, 183 | "roughnessFactor": 1 184 | } 185 | } 186 | ], 187 | "meshes": [ 188 | { 189 | "name": "defaultMaterial", 190 | "primitives": [ 191 | { 192 | "attributes": { 193 | "NORMAL": 1, 194 | "POSITION": 0, 195 | "TANGENT": 2, 196 | "TEXCOORD_0": 3 197 | }, 198 | "indices": 4, 199 | "material": 0, 200 | "mode": 4 201 | } 202 | ] 203 | } 204 | ], 205 | "nodes": [ 206 | { 207 | "children": [ 208 | 1 209 | ], 210 | "name": "RootNode (gltf orientation matrix)", 211 | "rotation": [ 212 | -0.70710678118654746, 213 | -0, 214 | -0, 215 | 0.70710678118654757 216 | ] 217 | }, 218 | { 219 | "children": [ 220 | 2 221 | ], 222 | "name": "RootNode (model correction matrix)" 223 | }, 224 | { 225 | "children": [ 226 | 3 227 | ], 228 | "matrix": [ 229 | 1, 230 | 0, 231 | 0, 232 | 0, 233 | 0, 234 | -4.3711390063094768e-08, 235 | 0.999999999999999, 236 | 0, 237 | 0, 238 | -0.999999999999999, 239 | -4.3711390063094768e-08, 240 | 0, 241 | 0, 242 | 0, 243 | 0, 244 | 1 245 | ], 246 | "name": "Collada visual scene group" 247 | }, 248 | { 249 | "children": [ 250 | 4 251 | ], 252 | "name": "Rock_Low1:Group31467" 253 | }, 254 | { 255 | "mesh": 0, 256 | "name": "defaultMaterial" 257 | } 258 | ], 259 | "samplers": [ 260 | { 261 | "magFilter": 9729, 262 | "minFilter": 9987, 263 | "wrapS": 10497, 264 | "wrapT": 10497 265 | } 266 | ], 267 | "scene": 0, 268 | "scenes": [ 269 | { 270 | "name": "OSG_Scene", 271 | "nodes": [ 272 | 0 273 | ] 274 | } 275 | ], 276 | "textures": [ 277 | { 278 | "sampler": 0, 279 | "source": 0 280 | }, 281 | { 282 | "sampler": 0, 283 | "source": 1 284 | }, 285 | { 286 | "sampler": 0, 287 | "source": 2 288 | }, 289 | { 290 | "sampler": 0, 291 | "source": 3 292 | } 293 | ] 294 | } 295 | 296 | -------------------------------------------------------------------------------- /rocketjourney/static/models/glowing_rock/scene.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "accessors": [ 3 | { 4 | "bufferView": 2, 5 | "componentType": 5126, 6 | "count": 1152, 7 | "max": [ 8 | 1, 9 | 0.8750920295715332, 10 | 0.96690100431442261 11 | ], 12 | "min": [ 13 | -1, 14 | -0.8750920295715332, 15 | -0.96690100431442261 16 | ], 17 | "type": "VEC3" 18 | }, 19 | { 20 | "bufferView": 2, 21 | "byteOffset": 13824, 22 | "componentType": 5126, 23 | "count": 1152, 24 | "max": [ 25 | 0.99950689077377319, 26 | 0.9927094578742981, 27 | 0.97859150171279907 28 | ], 29 | "min": [ 30 | -0.99143302440643311, 31 | -0.99999934434890747, 32 | -0.99826925992965698 33 | ], 34 | "type": "VEC3" 35 | }, 36 | { 37 | "bufferView": 3, 38 | "componentType": 5126, 39 | "count": 1152, 40 | "max": [ 41 | 0.9946320652961731, 42 | 0.99990278482437134, 43 | 0.99392831325531006, 44 | 1 45 | ], 46 | "min": [ 47 | -0.98853772878646851, 48 | -0.79022294282913208, 49 | -0.97787630558013916, 50 | 1 51 | ], 52 | "type": "VEC4" 53 | }, 54 | { 55 | "bufferView": 1, 56 | "componentType": 5126, 57 | "count": 1152, 58 | "max": [ 59 | 0.97502702474594116, 60 | 0.98724997043609619 61 | ], 62 | "min": [ 63 | 0.018356300890445709, 64 | 0.012071000412106514 65 | ], 66 | "type": "VEC2" 67 | }, 68 | { 69 | "bufferView": 0, 70 | "componentType": 5125, 71 | "count": 6648, 72 | "max": [ 73 | 1151 74 | ], 75 | "min": [ 76 | 0 77 | ], 78 | "type": "SCALAR" 79 | } 80 | ], 81 | "asset": { 82 | "extras": { 83 | "author": "JAY2.0 (https://sketchfab.com/JAY2.0)", 84 | "license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)", 85 | "source": "https://sketchfab.com/3d-models/glowing-rock-c1826814fe1641248fce16b79ac29e41", 86 | "title": "Glowing Rock" 87 | }, 88 | "generator": "Sketchfab-6.82.0", 89 | "version": "2.0" 90 | }, 91 | "bufferViews": [ 92 | { 93 | "buffer": 0, 94 | "byteLength": 26592, 95 | "byteOffset": 0, 96 | "name": "floatBufferViews", 97 | "target": 34963 98 | }, 99 | { 100 | "buffer": 0, 101 | "byteLength": 9216, 102 | "byteOffset": 26592, 103 | "byteStride": 8, 104 | "name": "floatBufferViews", 105 | "target": 34962 106 | }, 107 | { 108 | "buffer": 0, 109 | "byteLength": 27648, 110 | "byteOffset": 35808, 111 | "byteStride": 12, 112 | "name": "floatBufferViews", 113 | "target": 34962 114 | }, 115 | { 116 | "buffer": 0, 117 | "byteLength": 18432, 118 | "byteOffset": 63456, 119 | "byteStride": 16, 120 | "name": "floatBufferViews", 121 | "target": 34962 122 | } 123 | ], 124 | "buffers": [ 125 | { 126 | "byteLength": 81888, 127 | "uri": "scene.bin" 128 | } 129 | ], 130 | "images": [ 131 | { 132 | "uri": "textures/lambert1_baseColor.jpeg" 133 | }, 134 | { 135 | "uri": "textures/lambert1_metallicRoughness.png" 136 | }, 137 | { 138 | "uri": "textures/lambert1_emissive.jpeg" 139 | }, 140 | { 141 | "uri": "textures/lambert1_normal.png" 142 | } 143 | ], 144 | "materials": [ 145 | { 146 | "doubleSided": true, 147 | "emissiveFactor": [ 148 | 1, 149 | 1, 150 | 1 151 | ], 152 | "emissiveTexture": { 153 | "index": 2, 154 | "texCoord": 0 155 | }, 156 | "name": "lambert1", 157 | "normalTexture": { 158 | "index": 3, 159 | "scale": 1, 160 | "texCoord": 0 161 | }, 162 | "occlusionTexture": { 163 | "index": 1, 164 | "strength": 1, 165 | "texCoord": 0 166 | }, 167 | "pbrMetallicRoughness": { 168 | "baseColorFactor": [ 169 | 1, 170 | 1, 171 | 1, 172 | 1 173 | ], 174 | "baseColorTexture": { 175 | "index": 0, 176 | "texCoord": 0 177 | }, 178 | "metallicFactor": 1, 179 | "metallicRoughnessTexture": { 180 | "index": 1, 181 | "texCoord": 0 182 | }, 183 | "roughnessFactor": 1 184 | } 185 | } 186 | ], 187 | "meshes": [ 188 | { 189 | "name": "defaultMaterial", 190 | "primitives": [ 191 | { 192 | "attributes": { 193 | "NORMAL": 1, 194 | "POSITION": 0, 195 | "TANGENT": 2, 196 | "TEXCOORD_0": 3 197 | }, 198 | "indices": 4, 199 | "material": 0, 200 | "mode": 4 201 | } 202 | ] 203 | } 204 | ], 205 | "nodes": [ 206 | { 207 | "children": [ 208 | 1 209 | ], 210 | "name": "RootNode (gltf orientation matrix)", 211 | "rotation": [ 212 | -0.70710678118654746, 213 | -0, 214 | -0, 215 | 0.70710678118654757 216 | ] 217 | }, 218 | { 219 | "children": [ 220 | 2 221 | ], 222 | "name": "RootNode (model correction matrix)" 223 | }, 224 | { 225 | "children": [ 226 | 3 227 | ], 228 | "matrix": [ 229 | 1, 230 | 0, 231 | 0, 232 | 0, 233 | 0, 234 | -4.3711390063094768e-08, 235 | 0.999999999999999, 236 | 0, 237 | 0, 238 | -0.999999999999999, 239 | -4.3711390063094768e-08, 240 | 0, 241 | 0, 242 | 0, 243 | 0, 244 | 1 245 | ], 246 | "name": "Collada visual scene group" 247 | }, 248 | { 249 | "children": [ 250 | 4 251 | ], 252 | "name": "Rock_Low1:Group31467" 253 | }, 254 | { 255 | "mesh": 0, 256 | "name": "defaultMaterial" 257 | } 258 | ], 259 | "samplers": [ 260 | { 261 | "magFilter": 9729, 262 | "minFilter": 9987, 263 | "wrapS": 10497, 264 | "wrapT": 10497 265 | } 266 | ], 267 | "scene": 0, 268 | "scenes": [ 269 | { 270 | "name": "OSG_Scene", 271 | "nodes": [ 272 | 0 273 | ] 274 | } 275 | ], 276 | "textures": [ 277 | { 278 | "sampler": 0, 279 | "source": 0 280 | }, 281 | { 282 | "sampler": 0, 283 | "source": 1 284 | }, 285 | { 286 | "sampler": 0, 287 | "source": 2 288 | }, 289 | { 290 | "sampler": 0, 291 | "source": 3 292 | } 293 | ] 294 | } 295 | 296 | -------------------------------------------------------------------------------- /static/models/glowing_crystals/scene.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "accessors": [ 3 | { 4 | "bufferView": 2, 5 | "componentType": 5126, 6 | "count": 825, 7 | "max": [ 8 | 4.0185732841491699, 9 | 1.1783764362335205, 10 | 1.612663745880127 11 | ], 12 | "min": [ 13 | -4.7225213050842285, 14 | -4.5772604942321777, 15 | -0.27445262670516968 16 | ], 17 | "type": "VEC3" 18 | }, 19 | { 20 | "bufferView": 2, 21 | "byteOffset": 9900, 22 | "componentType": 5126, 23 | "count": 825, 24 | "max": [ 25 | 0.94840466976165771, 26 | 0.97833031415939331, 27 | 0.99306124448776245 28 | ], 29 | "min": [ 30 | -0.98145860433578491, 31 | -0.98187214136123657, 32 | -0.99540239572525024 33 | ], 34 | "type": "VEC3" 35 | }, 36 | { 37 | "bufferView": 3, 38 | "componentType": 5126, 39 | "count": 825, 40 | "max": [ 41 | 0.97197461128234863, 42 | 0.77059656381607056, 43 | 0.9888579249382019, 44 | 1 45 | ], 46 | "min": [ 47 | -0.99774086475372314, 48 | -0.96808922290802002, 49 | -0.99106955528259277, 50 | -1 51 | ], 52 | "type": "VEC4" 53 | }, 54 | { 55 | "bufferView": 1, 56 | "componentType": 5126, 57 | "count": 825, 58 | "max": [ 59 | 1, 60 | 1 61 | ], 62 | "min": [ 63 | 0, 64 | 0 65 | ], 66 | "type": "VEC2" 67 | }, 68 | { 69 | "bufferView": 0, 70 | "componentType": 5125, 71 | "count": 1197, 72 | "max": [ 73 | 824 74 | ], 75 | "min": [ 76 | 0 77 | ], 78 | "type": "SCALAR" 79 | } 80 | ], 81 | "asset": { 82 | "extras": { 83 | "author": "SusanKing (https://sketchfab.com/krolzuzannapl)", 84 | "license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)", 85 | "source": "https://sketchfab.com/3d-models/glowing-crystals-1c2e277c8c6b4c379ae55099c3122db1", 86 | "title": "Glowing crystals" 87 | }, 88 | "generator": "Sketchfab-8.89.2", 89 | "version": "2.0" 90 | }, 91 | "bufferViews": [ 92 | { 93 | "buffer": 0, 94 | "byteLength": 4788, 95 | "byteOffset": 0, 96 | "name": "floatBufferViews", 97 | "target": 34963 98 | }, 99 | { 100 | "buffer": 0, 101 | "byteLength": 6600, 102 | "byteOffset": 4788, 103 | "byteStride": 8, 104 | "name": "floatBufferViews", 105 | "target": 34962 106 | }, 107 | { 108 | "buffer": 0, 109 | "byteLength": 19800, 110 | "byteOffset": 11388, 111 | "byteStride": 12, 112 | "name": "floatBufferViews", 113 | "target": 34962 114 | }, 115 | { 116 | "buffer": 0, 117 | "byteLength": 13200, 118 | "byteOffset": 31188, 119 | "byteStride": 16, 120 | "name": "floatBufferViews", 121 | "target": 34962 122 | } 123 | ], 124 | "buffers": [ 125 | { 126 | "byteLength": 44388, 127 | "uri": "scene.bin" 128 | } 129 | ], 130 | "images": [ 131 | { 132 | "uri": "textures/crystals_baseColor.jpeg" 133 | }, 134 | { 135 | "uri": "textures/crystals_metallicRoughness.png" 136 | }, 137 | { 138 | "uri": "textures/crystals_emissive.jpeg" 139 | }, 140 | { 141 | "uri": "textures/crystals_normal.png" 142 | } 143 | ], 144 | "materials": [ 145 | { 146 | "doubleSided": true, 147 | "emissiveFactor": [ 148 | 1, 149 | 1, 150 | 1 151 | ], 152 | "emissiveTexture": { 153 | "index": 2, 154 | "texCoord": 0 155 | }, 156 | "name": "crystals", 157 | "normalTexture": { 158 | "index": 3, 159 | "scale": 1, 160 | "texCoord": 0 161 | }, 162 | "occlusionTexture": { 163 | "index": 1, 164 | "strength": 1, 165 | "texCoord": 0 166 | }, 167 | "pbrMetallicRoughness": { 168 | "baseColorFactor": [ 169 | 1, 170 | 1, 171 | 1, 172 | 1 173 | ], 174 | "baseColorTexture": { 175 | "index": 0, 176 | "texCoord": 0 177 | }, 178 | "metallicFactor": 1, 179 | "metallicRoughnessTexture": { 180 | "index": 1, 181 | "texCoord": 0 182 | }, 183 | "roughnessFactor": 1 184 | } 185 | } 186 | ], 187 | "meshes": [ 188 | { 189 | "name": "crystals_crystals_0", 190 | "primitives": [ 191 | { 192 | "attributes": { 193 | "NORMAL": 1, 194 | "POSITION": 0, 195 | "TANGENT": 2, 196 | "TEXCOORD_0": 3 197 | }, 198 | "indices": 4, 199 | "material": 0, 200 | "mode": 4 201 | } 202 | ] 203 | } 204 | ], 205 | "nodes": [ 206 | { 207 | "children": [ 208 | 1 209 | ], 210 | "name": "RootNode (gltf orientation matrix)", 211 | "rotation": [ 212 | -0.70710678118654746, 213 | -0, 214 | -0, 215 | 0.70710678118654757 216 | ] 217 | }, 218 | { 219 | "children": [ 220 | 2 221 | ], 222 | "name": "RootNode (model correction matrix)" 223 | }, 224 | { 225 | "children": [ 226 | 3 227 | ], 228 | "matrix": [ 229 | 1, 230 | 0, 231 | 0, 232 | 0, 233 | 0, 234 | 0, 235 | 1, 236 | 0, 237 | 0, 238 | -1, 239 | 0, 240 | 0, 241 | 0, 242 | 0, 243 | 0, 244 | 1 245 | ], 246 | "name": "8a105cb18e6547a7ba572f6c6501e940.fbx" 247 | }, 248 | { 249 | "children": [ 250 | 4 251 | ], 252 | "name": "RootNode" 253 | }, 254 | { 255 | "children": [ 256 | 5 257 | ], 258 | "matrix": [ 259 | 57.823459625244141, 260 | 7.3041344133806396e-15, 261 | -2.2800551080369752e-15, 262 | 0, 263 | 2.7623628878390726e-15, 264 | -39.853250332486432, 265 | -57.614448817621188, 266 | 0, 267 | -3.2142495496944071e-14, 268 | 209.27003888148926, 269 | -144.75693888930115, 270 | 0, 271 | -7.4505805969238281e-07, 272 | -1.1920928955078125e-05, 273 | 0, 274 | 1 275 | ], 276 | "name": "crystals" 277 | }, 278 | { 279 | "mesh": 0, 280 | "name": "crystals_crystals_0" 281 | } 282 | ], 283 | "samplers": [ 284 | { 285 | "magFilter": 9729, 286 | "minFilter": 9987, 287 | "wrapS": 10497, 288 | "wrapT": 10497 289 | } 290 | ], 291 | "scene": 0, 292 | "scenes": [ 293 | { 294 | "name": "OSG_Scene", 295 | "nodes": [ 296 | 0 297 | ] 298 | } 299 | ], 300 | "textures": [ 301 | { 302 | "sampler": 0, 303 | "source": 0 304 | }, 305 | { 306 | "sampler": 0, 307 | "source": 1 308 | }, 309 | { 310 | "sampler": 0, 311 | "source": 2 312 | }, 313 | { 314 | "sampler": 0, 315 | "source": 3 316 | } 317 | ] 318 | } 319 | 320 | -------------------------------------------------------------------------------- /rocketjourney/static/models/glowing_crystals/scene.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "accessors": [ 3 | { 4 | "bufferView": 2, 5 | "componentType": 5126, 6 | "count": 825, 7 | "max": [ 8 | 4.0185732841491699, 9 | 1.1783764362335205, 10 | 1.612663745880127 11 | ], 12 | "min": [ 13 | -4.7225213050842285, 14 | -4.5772604942321777, 15 | -0.27445262670516968 16 | ], 17 | "type": "VEC3" 18 | }, 19 | { 20 | "bufferView": 2, 21 | "byteOffset": 9900, 22 | "componentType": 5126, 23 | "count": 825, 24 | "max": [ 25 | 0.94840466976165771, 26 | 0.97833031415939331, 27 | 0.99306124448776245 28 | ], 29 | "min": [ 30 | -0.98145860433578491, 31 | -0.98187214136123657, 32 | -0.99540239572525024 33 | ], 34 | "type": "VEC3" 35 | }, 36 | { 37 | "bufferView": 3, 38 | "componentType": 5126, 39 | "count": 825, 40 | "max": [ 41 | 0.97197461128234863, 42 | 0.77059656381607056, 43 | 0.9888579249382019, 44 | 1 45 | ], 46 | "min": [ 47 | -0.99774086475372314, 48 | -0.96808922290802002, 49 | -0.99106955528259277, 50 | -1 51 | ], 52 | "type": "VEC4" 53 | }, 54 | { 55 | "bufferView": 1, 56 | "componentType": 5126, 57 | "count": 825, 58 | "max": [ 59 | 1, 60 | 1 61 | ], 62 | "min": [ 63 | 0, 64 | 0 65 | ], 66 | "type": "VEC2" 67 | }, 68 | { 69 | "bufferView": 0, 70 | "componentType": 5125, 71 | "count": 1197, 72 | "max": [ 73 | 824 74 | ], 75 | "min": [ 76 | 0 77 | ], 78 | "type": "SCALAR" 79 | } 80 | ], 81 | "asset": { 82 | "extras": { 83 | "author": "SusanKing (https://sketchfab.com/krolzuzannapl)", 84 | "license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)", 85 | "source": "https://sketchfab.com/3d-models/glowing-crystals-1c2e277c8c6b4c379ae55099c3122db1", 86 | "title": "Glowing crystals" 87 | }, 88 | "generator": "Sketchfab-8.89.2", 89 | "version": "2.0" 90 | }, 91 | "bufferViews": [ 92 | { 93 | "buffer": 0, 94 | "byteLength": 4788, 95 | "byteOffset": 0, 96 | "name": "floatBufferViews", 97 | "target": 34963 98 | }, 99 | { 100 | "buffer": 0, 101 | "byteLength": 6600, 102 | "byteOffset": 4788, 103 | "byteStride": 8, 104 | "name": "floatBufferViews", 105 | "target": 34962 106 | }, 107 | { 108 | "buffer": 0, 109 | "byteLength": 19800, 110 | "byteOffset": 11388, 111 | "byteStride": 12, 112 | "name": "floatBufferViews", 113 | "target": 34962 114 | }, 115 | { 116 | "buffer": 0, 117 | "byteLength": 13200, 118 | "byteOffset": 31188, 119 | "byteStride": 16, 120 | "name": "floatBufferViews", 121 | "target": 34962 122 | } 123 | ], 124 | "buffers": [ 125 | { 126 | "byteLength": 44388, 127 | "uri": "scene.bin" 128 | } 129 | ], 130 | "images": [ 131 | { 132 | "uri": "textures/crystals_baseColor.jpeg" 133 | }, 134 | { 135 | "uri": "textures/crystals_metallicRoughness.png" 136 | }, 137 | { 138 | "uri": "textures/crystals_emissive.jpeg" 139 | }, 140 | { 141 | "uri": "textures/crystals_normal.png" 142 | } 143 | ], 144 | "materials": [ 145 | { 146 | "doubleSided": true, 147 | "emissiveFactor": [ 148 | 1, 149 | 1, 150 | 1 151 | ], 152 | "emissiveTexture": { 153 | "index": 2, 154 | "texCoord": 0 155 | }, 156 | "name": "crystals", 157 | "normalTexture": { 158 | "index": 3, 159 | "scale": 1, 160 | "texCoord": 0 161 | }, 162 | "occlusionTexture": { 163 | "index": 1, 164 | "strength": 1, 165 | "texCoord": 0 166 | }, 167 | "pbrMetallicRoughness": { 168 | "baseColorFactor": [ 169 | 1, 170 | 1, 171 | 1, 172 | 1 173 | ], 174 | "baseColorTexture": { 175 | "index": 0, 176 | "texCoord": 0 177 | }, 178 | "metallicFactor": 1, 179 | "metallicRoughnessTexture": { 180 | "index": 1, 181 | "texCoord": 0 182 | }, 183 | "roughnessFactor": 1 184 | } 185 | } 186 | ], 187 | "meshes": [ 188 | { 189 | "name": "crystals_crystals_0", 190 | "primitives": [ 191 | { 192 | "attributes": { 193 | "NORMAL": 1, 194 | "POSITION": 0, 195 | "TANGENT": 2, 196 | "TEXCOORD_0": 3 197 | }, 198 | "indices": 4, 199 | "material": 0, 200 | "mode": 4 201 | } 202 | ] 203 | } 204 | ], 205 | "nodes": [ 206 | { 207 | "children": [ 208 | 1 209 | ], 210 | "name": "RootNode (gltf orientation matrix)", 211 | "rotation": [ 212 | -0.70710678118654746, 213 | -0, 214 | -0, 215 | 0.70710678118654757 216 | ] 217 | }, 218 | { 219 | "children": [ 220 | 2 221 | ], 222 | "name": "RootNode (model correction matrix)" 223 | }, 224 | { 225 | "children": [ 226 | 3 227 | ], 228 | "matrix": [ 229 | 1, 230 | 0, 231 | 0, 232 | 0, 233 | 0, 234 | 0, 235 | 1, 236 | 0, 237 | 0, 238 | -1, 239 | 0, 240 | 0, 241 | 0, 242 | 0, 243 | 0, 244 | 1 245 | ], 246 | "name": "8a105cb18e6547a7ba572f6c6501e940.fbx" 247 | }, 248 | { 249 | "children": [ 250 | 4 251 | ], 252 | "name": "RootNode" 253 | }, 254 | { 255 | "children": [ 256 | 5 257 | ], 258 | "matrix": [ 259 | 57.823459625244141, 260 | 7.3041344133806396e-15, 261 | -2.2800551080369752e-15, 262 | 0, 263 | 2.7623628878390726e-15, 264 | -39.853250332486432, 265 | -57.614448817621188, 266 | 0, 267 | -3.2142495496944071e-14, 268 | 209.27003888148926, 269 | -144.75693888930115, 270 | 0, 271 | -7.4505805969238281e-07, 272 | -1.1920928955078125e-05, 273 | 0, 274 | 1 275 | ], 276 | "name": "crystals" 277 | }, 278 | { 279 | "mesh": 0, 280 | "name": "crystals_crystals_0" 281 | } 282 | ], 283 | "samplers": [ 284 | { 285 | "magFilter": 9729, 286 | "minFilter": 9987, 287 | "wrapS": 10497, 288 | "wrapT": 10497 289 | } 290 | ], 291 | "scene": 0, 292 | "scenes": [ 293 | { 294 | "name": "OSG_Scene", 295 | "nodes": [ 296 | 0 297 | ] 298 | } 299 | ], 300 | "textures": [ 301 | { 302 | "sampler": 0, 303 | "source": 0 304 | }, 305 | { 306 | "sampler": 0, 307 | "source": 1 308 | }, 309 | { 310 | "sampler": 0, 311 | "source": 2 312 | }, 313 | { 314 | "sampler": 0, 315 | "source": 3 316 | } 317 | ] 318 | } 319 | 320 | -------------------------------------------------------------------------------- /static/models/shield_item/scene.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "accessors": [ 3 | { 4 | "bufferView": 2, 5 | "componentType": 5126, 6 | "count": 928, 7 | "max": [ 8 | 5.315570831298828, 9 | 4.4732160568237305, 10 | 5.3155694007873535 11 | ], 12 | "min": [ 13 | -5.315568923950195, 14 | -4.296392440795898, 15 | -5.31557035446167 16 | ], 17 | "type": "VEC3" 18 | }, 19 | { 20 | "bufferView": 2, 21 | "byteOffset": 11136, 22 | "componentType": 5126, 23 | "count": 928, 24 | "max": [ 25 | 0.951056957244873, 26 | 0.9510565996170044, 27 | 0.9510565996170044 28 | ], 29 | "min": [ 30 | -0.9510568976402283, 31 | -1.0, 32 | -0.9510565996170044 33 | ], 34 | "type": "VEC3" 35 | }, 36 | { 37 | "bufferView": 1, 38 | "componentType": 5126, 39 | "count": 928, 40 | "max": [ 41 | 0.9934467077255249, 42 | 0.9890891313552856 43 | ], 44 | "min": [ 45 | 0.006553361192345619, 46 | 0.006553361192345619 47 | ], 48 | "type": "VEC2" 49 | }, 50 | { 51 | "bufferView": 0, 52 | "componentType": 5125, 53 | "count": 1524, 54 | "type": "SCALAR" 55 | }, 56 | { 57 | "bufferView": 2, 58 | "byteOffset": 22272, 59 | "componentType": 5126, 60 | "count": 408, 61 | "max": [ 62 | 1.9871898889541626, 63 | 2.548590660095215, 64 | 0.1950904279947281 65 | ], 66 | "min": [ 67 | -1.9871898889541626, 68 | -2.2827541828155518, 69 | -0.1950904279947281 70 | ], 71 | "type": "VEC3" 72 | }, 73 | { 74 | "bufferView": 2, 75 | "byteOffset": 27168, 76 | "componentType": 5126, 77 | "count": 408, 78 | "max": [ 79 | 1.0, 80 | 0.9573110938072205, 81 | 1.0 82 | ], 83 | "min": [ 84 | -1.0, 85 | -0.95731520652771, 86 | -1.0 87 | ], 88 | "type": "VEC3" 89 | }, 90 | { 91 | "bufferView": 1, 92 | "byteOffset": 7424, 93 | "componentType": 5126, 94 | "count": 408, 95 | "max": [ 96 | 0.0, 97 | 0.0 98 | ], 99 | "min": [ 100 | 0.0, 101 | 0.0 102 | ], 103 | "type": "VEC2" 104 | }, 105 | { 106 | "bufferView": 0, 107 | "byteOffset": 6096, 108 | "componentType": 5125, 109 | "count": 1320, 110 | "type": "SCALAR" 111 | } 112 | ], 113 | "asset": { 114 | "extras": { 115 | "author": "Reikan (https://sketchfab.com/jcremoux)", 116 | "license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)", 117 | "source": "https://sketchfab.com/3d-models/shield-item-a72cd3f034ec4e6b88116b626e39401f", 118 | "title": "Shield Item" 119 | }, 120 | "generator": "Sketchfab-11.62.0", 121 | "version": "2.0" 122 | }, 123 | "bufferViews": [ 124 | { 125 | "buffer": 0, 126 | "byteLength": 11376, 127 | "name": "floatBufferViews", 128 | "target": 34963 129 | }, 130 | { 131 | "buffer": 0, 132 | "byteLength": 10688, 133 | "byteOffset": 11376, 134 | "byteStride": 8, 135 | "name": "floatBufferViews", 136 | "target": 34962 137 | }, 138 | { 139 | "buffer": 0, 140 | "byteLength": 32064, 141 | "byteOffset": 22064, 142 | "byteStride": 12, 143 | "name": "floatBufferViews", 144 | "target": 34962 145 | } 146 | ], 147 | "buffers": [ 148 | { 149 | "byteLength": 54128, 150 | "uri": "scene.bin" 151 | } 152 | ], 153 | "extensionsUsed": [ 154 | "KHR_materials_transmission" 155 | ], 156 | "materials": [ 157 | { 158 | "alphaMode": "BLEND", 159 | "doubleSided": true, 160 | "extensions": { 161 | "KHR_materials_transmission": { 162 | "transmissionFactor": 0.39635280812797546 163 | } 164 | }, 165 | "name": "Material.002", 166 | "pbrMetallicRoughness": { 167 | "baseColorFactor": [ 168 | 0.02393077144547778, 169 | 0.22735972374595217, 170 | 0.512241594656483, 171 | 0.6036471918720245 172 | ], 173 | "metallicFactor": 0.21451610941879626, 174 | "roughnessFactor": 0.03819108768217719 175 | } 176 | }, 177 | { 178 | "doubleSided": true, 179 | "name": "Material.001", 180 | "pbrMetallicRoughness": { 181 | "baseColorFactor": [ 182 | 0.12918817636889204, 183 | 0.43104767876560146, 184 | 0.8464433773127846, 185 | 1.0 186 | ], 187 | "metallicFactor": 0.6036471918720245, 188 | "roughnessFactor": 0.0321109145188455 189 | } 190 | } 191 | ], 192 | "meshes": [ 193 | { 194 | "name": "Shield_Material.002_0", 195 | "primitives": [ 196 | { 197 | "attributes": { 198 | "NORMAL": 1, 199 | "POSITION": 0, 200 | "TEXCOORD_0": 2 201 | }, 202 | "indices": 3, 203 | "material": 0, 204 | "mode": 4 205 | } 206 | ] 207 | }, 208 | { 209 | "name": "Shield.001_Material.001_0", 210 | "primitives": [ 211 | { 212 | "attributes": { 213 | "NORMAL": 5, 214 | "POSITION": 4, 215 | "TEXCOORD_0": 6 216 | }, 217 | "indices": 7, 218 | "material": 1, 219 | "mode": 4 220 | } 221 | ] 222 | } 223 | ], 224 | "nodes": [ 225 | { 226 | "children": [ 227 | 1 228 | ], 229 | "matrix": [ 230 | 1.0, 231 | 0.0, 232 | 0.0, 233 | 0.0, 234 | 0.0, 235 | 2.220446049250313e-16, 236 | -1.0, 237 | 0.0, 238 | 0.0, 239 | 1.0, 240 | 2.220446049250313e-16, 241 | 0.0, 242 | 0.0, 243 | 0.0, 244 | 0.0, 245 | 1.0 246 | ], 247 | "name": "Sketchfab_model" 248 | }, 249 | { 250 | "children": [ 251 | 2 252 | ], 253 | "matrix": [ 254 | 0.009999999776482582, 255 | 0.0, 256 | 0.0, 257 | 0.0, 258 | 0.0, 259 | 0.0, 260 | 0.009999999776482582, 261 | 0.0, 262 | 0.0, 263 | -0.009999999776482582, 264 | 0.0, 265 | 0.0, 266 | 0.0, 267 | 0.0, 268 | 0.0, 269 | 1.0 270 | ], 271 | "name": "722608e63c1d4ebca51744a16a789edc.fbx" 272 | }, 273 | { 274 | "children": [ 275 | 3, 276 | 5 277 | ], 278 | "name": "RootNode" 279 | }, 280 | { 281 | "children": [ 282 | 4 283 | ], 284 | "matrix": [ 285 | 100.0, 286 | 0.0, 287 | 0.0, 288 | 0.0, 289 | 0.0, 290 | 99.99999999999866, 291 | 1.6292067073209114e-05, 292 | 0.0, 293 | 0.0, 294 | -1.6292067073209114e-05, 295 | 99.99999999999866, 296 | 0.0, 297 | 0.0, 298 | 0.0, 299 | 0.0, 300 | 1.0 301 | ], 302 | "name": "Shield" 303 | }, 304 | { 305 | "mesh": 0, 306 | "name": "Shield_Material.002_0" 307 | }, 308 | { 309 | "children": [ 310 | 6 311 | ], 312 | "matrix": [ 313 | 100.0, 314 | 0.0, 315 | 0.0, 316 | 0.0, 317 | 0.0, 318 | 99.99999999999866, 319 | 1.6292067073209114e-05, 320 | 0.0, 321 | 0.0, 322 | -1.6292067073209114e-05, 323 | 99.99999999999866, 324 | 0.0, 325 | 0.0, 326 | 0.0, 327 | 0.0, 328 | 1.0 329 | ], 330 | "name": "Shield.001" 331 | }, 332 | { 333 | "mesh": 1, 334 | "name": "Shield.001_Material.001_0" 335 | } 336 | ], 337 | "scene": 0, 338 | "scenes": [ 339 | { 340 | "name": "Sketchfab_Scene", 341 | "nodes": [ 342 | 0 343 | ] 344 | } 345 | ] 346 | } 347 | -------------------------------------------------------------------------------- /rocketjourney/static/models/shield_item/scene.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "accessors": [ 3 | { 4 | "bufferView": 2, 5 | "componentType": 5126, 6 | "count": 928, 7 | "max": [ 8 | 5.315570831298828, 9 | 4.4732160568237305, 10 | 5.3155694007873535 11 | ], 12 | "min": [ 13 | -5.315568923950195, 14 | -4.296392440795898, 15 | -5.31557035446167 16 | ], 17 | "type": "VEC3" 18 | }, 19 | { 20 | "bufferView": 2, 21 | "byteOffset": 11136, 22 | "componentType": 5126, 23 | "count": 928, 24 | "max": [ 25 | 0.951056957244873, 26 | 0.9510565996170044, 27 | 0.9510565996170044 28 | ], 29 | "min": [ 30 | -0.9510568976402283, 31 | -1.0, 32 | -0.9510565996170044 33 | ], 34 | "type": "VEC3" 35 | }, 36 | { 37 | "bufferView": 1, 38 | "componentType": 5126, 39 | "count": 928, 40 | "max": [ 41 | 0.9934467077255249, 42 | 0.9890891313552856 43 | ], 44 | "min": [ 45 | 0.006553361192345619, 46 | 0.006553361192345619 47 | ], 48 | "type": "VEC2" 49 | }, 50 | { 51 | "bufferView": 0, 52 | "componentType": 5125, 53 | "count": 1524, 54 | "type": "SCALAR" 55 | }, 56 | { 57 | "bufferView": 2, 58 | "byteOffset": 22272, 59 | "componentType": 5126, 60 | "count": 408, 61 | "max": [ 62 | 1.9871898889541626, 63 | 2.548590660095215, 64 | 0.1950904279947281 65 | ], 66 | "min": [ 67 | -1.9871898889541626, 68 | -2.2827541828155518, 69 | -0.1950904279947281 70 | ], 71 | "type": "VEC3" 72 | }, 73 | { 74 | "bufferView": 2, 75 | "byteOffset": 27168, 76 | "componentType": 5126, 77 | "count": 408, 78 | "max": [ 79 | 1.0, 80 | 0.9573110938072205, 81 | 1.0 82 | ], 83 | "min": [ 84 | -1.0, 85 | -0.95731520652771, 86 | -1.0 87 | ], 88 | "type": "VEC3" 89 | }, 90 | { 91 | "bufferView": 1, 92 | "byteOffset": 7424, 93 | "componentType": 5126, 94 | "count": 408, 95 | "max": [ 96 | 0.0, 97 | 0.0 98 | ], 99 | "min": [ 100 | 0.0, 101 | 0.0 102 | ], 103 | "type": "VEC2" 104 | }, 105 | { 106 | "bufferView": 0, 107 | "byteOffset": 6096, 108 | "componentType": 5125, 109 | "count": 1320, 110 | "type": "SCALAR" 111 | } 112 | ], 113 | "asset": { 114 | "extras": { 115 | "author": "Reikan (https://sketchfab.com/jcremoux)", 116 | "license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)", 117 | "source": "https://sketchfab.com/3d-models/shield-item-a72cd3f034ec4e6b88116b626e39401f", 118 | "title": "Shield Item" 119 | }, 120 | "generator": "Sketchfab-11.62.0", 121 | "version": "2.0" 122 | }, 123 | "bufferViews": [ 124 | { 125 | "buffer": 0, 126 | "byteLength": 11376, 127 | "name": "floatBufferViews", 128 | "target": 34963 129 | }, 130 | { 131 | "buffer": 0, 132 | "byteLength": 10688, 133 | "byteOffset": 11376, 134 | "byteStride": 8, 135 | "name": "floatBufferViews", 136 | "target": 34962 137 | }, 138 | { 139 | "buffer": 0, 140 | "byteLength": 32064, 141 | "byteOffset": 22064, 142 | "byteStride": 12, 143 | "name": "floatBufferViews", 144 | "target": 34962 145 | } 146 | ], 147 | "buffers": [ 148 | { 149 | "byteLength": 54128, 150 | "uri": "scene.bin" 151 | } 152 | ], 153 | "extensionsUsed": [ 154 | "KHR_materials_transmission" 155 | ], 156 | "materials": [ 157 | { 158 | "alphaMode": "BLEND", 159 | "doubleSided": true, 160 | "extensions": { 161 | "KHR_materials_transmission": { 162 | "transmissionFactor": 0.39635280812797546 163 | } 164 | }, 165 | "name": "Material.002", 166 | "pbrMetallicRoughness": { 167 | "baseColorFactor": [ 168 | 0.02393077144547778, 169 | 0.22735972374595217, 170 | 0.512241594656483, 171 | 0.6036471918720245 172 | ], 173 | "metallicFactor": 0.21451610941879626, 174 | "roughnessFactor": 0.03819108768217719 175 | } 176 | }, 177 | { 178 | "doubleSided": true, 179 | "name": "Material.001", 180 | "pbrMetallicRoughness": { 181 | "baseColorFactor": [ 182 | 0.12918817636889204, 183 | 0.43104767876560146, 184 | 0.8464433773127846, 185 | 1.0 186 | ], 187 | "metallicFactor": 0.6036471918720245, 188 | "roughnessFactor": 0.0321109145188455 189 | } 190 | } 191 | ], 192 | "meshes": [ 193 | { 194 | "name": "Shield_Material.002_0", 195 | "primitives": [ 196 | { 197 | "attributes": { 198 | "NORMAL": 1, 199 | "POSITION": 0, 200 | "TEXCOORD_0": 2 201 | }, 202 | "indices": 3, 203 | "material": 0, 204 | "mode": 4 205 | } 206 | ] 207 | }, 208 | { 209 | "name": "Shield.001_Material.001_0", 210 | "primitives": [ 211 | { 212 | "attributes": { 213 | "NORMAL": 5, 214 | "POSITION": 4, 215 | "TEXCOORD_0": 6 216 | }, 217 | "indices": 7, 218 | "material": 1, 219 | "mode": 4 220 | } 221 | ] 222 | } 223 | ], 224 | "nodes": [ 225 | { 226 | "children": [ 227 | 1 228 | ], 229 | "matrix": [ 230 | 1.0, 231 | 0.0, 232 | 0.0, 233 | 0.0, 234 | 0.0, 235 | 2.220446049250313e-16, 236 | -1.0, 237 | 0.0, 238 | 0.0, 239 | 1.0, 240 | 2.220446049250313e-16, 241 | 0.0, 242 | 0.0, 243 | 0.0, 244 | 0.0, 245 | 1.0 246 | ], 247 | "name": "Sketchfab_model" 248 | }, 249 | { 250 | "children": [ 251 | 2 252 | ], 253 | "matrix": [ 254 | 0.009999999776482582, 255 | 0.0, 256 | 0.0, 257 | 0.0, 258 | 0.0, 259 | 0.0, 260 | 0.009999999776482582, 261 | 0.0, 262 | 0.0, 263 | -0.009999999776482582, 264 | 0.0, 265 | 0.0, 266 | 0.0, 267 | 0.0, 268 | 0.0, 269 | 1.0 270 | ], 271 | "name": "722608e63c1d4ebca51744a16a789edc.fbx" 272 | }, 273 | { 274 | "children": [ 275 | 3, 276 | 5 277 | ], 278 | "name": "RootNode" 279 | }, 280 | { 281 | "children": [ 282 | 4 283 | ], 284 | "matrix": [ 285 | 100.0, 286 | 0.0, 287 | 0.0, 288 | 0.0, 289 | 0.0, 290 | 99.99999999999866, 291 | 1.6292067073209114e-05, 292 | 0.0, 293 | 0.0, 294 | -1.6292067073209114e-05, 295 | 99.99999999999866, 296 | 0.0, 297 | 0.0, 298 | 0.0, 299 | 0.0, 300 | 1.0 301 | ], 302 | "name": "Shield" 303 | }, 304 | { 305 | "mesh": 0, 306 | "name": "Shield_Material.002_0" 307 | }, 308 | { 309 | "children": [ 310 | 6 311 | ], 312 | "matrix": [ 313 | 100.0, 314 | 0.0, 315 | 0.0, 316 | 0.0, 317 | 0.0, 318 | 99.99999999999866, 319 | 1.6292067073209114e-05, 320 | 0.0, 321 | 0.0, 322 | -1.6292067073209114e-05, 323 | 99.99999999999866, 324 | 0.0, 325 | 0.0, 326 | 0.0, 327 | 0.0, 328 | 1.0 329 | ], 330 | "name": "Shield.001" 331 | }, 332 | { 333 | "mesh": 1, 334 | "name": "Shield.001_Material.001_0" 335 | } 336 | ], 337 | "scene": 0, 338 | "scenes": [ 339 | { 340 | "name": "Sketchfab_Scene", 341 | "nodes": [ 342 | 0 343 | ] 344 | } 345 | ] 346 | } 347 | -------------------------------------------------------------------------------- /game/collisionDetection.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AnimationClip, 3 | AnimationMixer, 4 | Box3, 5 | BoxGeometry, 6 | Clock, 7 | LoopOnce, 8 | Mesh, 9 | MeshBasicMaterial, 10 | Vector3, 11 | VectorKeyframeTrack 12 | } from "three"; 13 | import {challengeRows, ObjectType, rocketModel} from "./objects"; 14 | import {crystalUiElement, shieldUiElement} from "./ui"; 15 | import {radToDeg} from "three/src/math/MathUtils"; 16 | import {destructionBits, endLevel, scene, sceneConfiguration} from "../game"; 17 | 18 | export const detectCollisions = () => { 19 | // If the level is over, don't detect collisions 20 | if (sceneConfiguration.levelOver) return; 21 | // Using the dimensions of our rocket, create a box that is the width and height of our model 22 | // This box doesn't appear in the world, it's merely a set of coordinates that describe the box 23 | // in world space. 24 | const rocketBox = new Box3().setFromObject(rocketModel); 25 | // For every challange row that we have on the screen... 26 | challengeRows.forEach(x => { 27 | // ...update the global position matrix of the row, and its children. 28 | x.rowParent.updateMatrixWorld(); 29 | // Next, for each object within each challenge row... 30 | x.rowParent.children.forEach(y => { 31 | y.children.forEach(z => { 32 | // ...create a box that is the width and height of the object 33 | const box = new Box3().setFromObject(z); 34 | // Check if the box with the obstacle overlaps (or intersects with) our rocket 35 | if (box.intersectsBox(rocketBox)) { 36 | // If it does, get the center position of that box 37 | let destructionPosition = box.getCenter(z.position); 38 | // Queue up the destruction animation to play (the boxes flying out from the rocket) 39 | playDestructionAnimation(destructionPosition); 40 | // Remove the object that has been hit from the parent 41 | // This removes the object from the scene 42 | y.remove(z); 43 | // Now, we check what it was that we hit, whether it was a rock, shield, or crystal 44 | if (y.userData.objectType !== undefined) { 45 | let type = y.userData.objectType as ObjectType; 46 | switch (type) { 47 | // If it was a rock... 48 | case ObjectType.ROCK: 49 | // ...remove one shield from the players' score 50 | sceneConfiguration.data.shieldsCollected--; 51 | // Update the UI with the new count of shields 52 | shieldUiElement.innerText = String(sceneConfiguration.data.shieldsCollected); 53 | // If the player has less than 0 shields... 54 | if (sceneConfiguration.data.shieldsCollected <= 0) { 55 | // ...add the 'danger' CSS class to make the text red (if it's not already there) 56 | if (!shieldUiElement.classList.contains('danger')) { 57 | shieldUiElement.classList.add('danger'); 58 | } 59 | } else { //Otherwise, if it's more than 0 shields, remove the danger CSS class 60 | // so the text goes back to being white 61 | shieldUiElement.classList.remove('danger'); 62 | } 63 | 64 | // If the ship has sustained too much damage, and has less than -5 shields... 65 | if (sceneConfiguration.data.shieldsCollected <= -5) { 66 | // ...end the scene 67 | endLevel(true); 68 | } 69 | break; 70 | // If it's a crystal... 71 | case ObjectType.CRYSTAL: 72 | // Update the UI with the new count of crystals, and increment the count of 73 | // currently collected crystals 74 | crystalUiElement.innerText = String(++sceneConfiguration.data.crystalsCollected); 75 | break; 76 | // If it's a shield... 77 | case ObjectType.SHIELD_ITEM: 78 | // Update the UI with the new count of shields, and increment the count of 79 | // currently collected shields 80 | shieldUiElement.innerText = String(++sceneConfiguration.data.shieldsCollected); 81 | break; 82 | } 83 | } 84 | } 85 | }); 86 | }) 87 | }); 88 | } 89 | const playDestructionAnimation = (spawnPosition: Vector3) => { 90 | 91 | // Create six boxes 92 | for (let i = 0; i < 6; i++) { 93 | // Our destruction 'bits' will be black, but have some transparency to them 94 | let destructionBit = new Mesh(new BoxGeometry(1, 1, 1), new MeshBasicMaterial({ 95 | color: 'black', 96 | transparent: true, 97 | opacity: 0.4 98 | })); 99 | 100 | // Each destruction bit object within the scene will have a 'lifetime' property associated to it 101 | // This property is incremented every time a frame is drawn to the screen 102 | // Within our animate loop, we check if this is more than 500, and if it is, we remove the object 103 | destructionBit.userData.lifetime = 0; 104 | // Set the spawn position of the box 105 | destructionBit.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z); 106 | // Create an animation mixer for the object 107 | destructionBit.userData.mixer = new AnimationMixer(destructionBit); 108 | 109 | // Spawn the objects in a circle around the rocket 110 | let degrees = i / 45; 111 | 112 | // Work out where on the circle we should spawn this specific destruction bit 113 | let spawnX = Math.cos(radToDeg(degrees)) * 15; 114 | let spawnY = Math.sin(radToDeg(degrees)) * 15; 115 | 116 | // Create a VectorKeyFrameTrack that will animate this box from its starting position to the final 117 | // 'outward' position (so it looks like the boxes are exploding from the ship) 118 | let track = new VectorKeyframeTrack('.position', [0, 0.3], [ 119 | rocketModel.position.x, // x 1 120 | rocketModel.position.y, // y 1 121 | rocketModel.position.z, // z 1 122 | rocketModel.position.x + spawnX, // x 2 123 | rocketModel.position.y, // y 2 124 | rocketModel.position.z + spawnY, // z 2 125 | ]); 126 | 127 | // Create an animation clip with our VectorKeyFrameTrack 128 | const animationClip = new AnimationClip('animateIn', 10, [track]); 129 | const animationAction = destructionBit.userData.mixer.clipAction(animationClip); 130 | 131 | // Only play the animation once 132 | animationAction.setLoop(LoopOnce, 1); 133 | 134 | // When complete, leave the objects in their final position (don't reset them to the starting position) 135 | animationAction.clampWhenFinished = true; 136 | // Play the animation 137 | animationAction.play(); 138 | // Associate a Clock to the destruction bit. We use this within the render loop so ThreeJS knows how far 139 | // to move this object for this frame 140 | destructionBit.userData.clock = new Clock(); 141 | // Add the destruction bit to the scene 142 | scene.add(destructionBit); 143 | 144 | // Add the destruction bit to an array, to keep track of them 145 | destructionBits.push(destructionBit); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /rocketjourney/game/collisionDetection.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AnimationClip, 3 | AnimationMixer, 4 | Box3, 5 | BoxGeometry, 6 | Clock, 7 | LoopOnce, 8 | Mesh, 9 | MeshBasicMaterial, 10 | Vector3, 11 | VectorKeyframeTrack 12 | } from "three"; 13 | import {challengeRows, ObjectType, rocketModel} from "./objects"; 14 | import {crystalUiElement, shieldUiElement} from "./ui"; 15 | import {radToDeg} from "three/src/math/MathUtils"; 16 | import {destructionBits, endLevel, scene, sceneConfiguration} from "../game"; 17 | 18 | export const detectCollisions = () => { 19 | // If the level is over, don't detect collisions 20 | if (sceneConfiguration.levelOver) return; 21 | // Using the dimensions of our rocket, create a box that is the width and height of our model 22 | // This box doesn't appear in the world, it's merely a set of coordinates that describe the box 23 | // in world space. 24 | const rocketBox = new Box3().setFromObject(rocketModel); 25 | // For every challange row that we have on the screen... 26 | challengeRows.forEach(x => { 27 | // ...update the global position matrix of the row, and its children. 28 | x.rowParent.updateMatrixWorld(); 29 | // Next, for each object within each challenge row... 30 | x.rowParent.children.forEach(y => { 31 | y.children.forEach(z => { 32 | // ...create a box that is the width and height of the object 33 | const box = new Box3().setFromObject(z); 34 | // Check if the box with the obstacle overlaps (or intersects with) our rocket 35 | if (box.intersectsBox(rocketBox)) { 36 | // If it does, get the center position of that box 37 | let destructionPosition = box.getCenter(z.position); 38 | // Queue up the destruction animation to play (the boxes flying out from the rocket) 39 | playDestructionAnimation(destructionPosition); 40 | // Remove the object that has been hit from the parent 41 | // This removes the object from the scene 42 | y.remove(z); 43 | // Now, we check what it was that we hit, whether it was a rock, shield, or crystal 44 | if (y.userData.objectType !== undefined) { 45 | let type = y.userData.objectType as ObjectType; 46 | switch (type) { 47 | // If it was a rock... 48 | case ObjectType.ROCK: 49 | // ...remove one shield from the players' score 50 | sceneConfiguration.data.shieldsCollected--; 51 | // Update the UI with the new count of shields 52 | shieldUiElement.innerText = String(sceneConfiguration.data.shieldsCollected); 53 | // If the player has less than 0 shields... 54 | if (sceneConfiguration.data.shieldsCollected <= 0) { 55 | // ...add the 'danger' CSS class to make the text red (if it's not already there) 56 | if (!shieldUiElement.classList.contains('danger')) { 57 | shieldUiElement.classList.add('danger'); 58 | } 59 | } else { //Otherwise, if it's more than 0 shields, remove the danger CSS class 60 | // so the text goes back to being white 61 | shieldUiElement.classList.remove('danger'); 62 | } 63 | 64 | // If the ship has sustained too much damage, and has less than -5 shields... 65 | if (sceneConfiguration.data.shieldsCollected <= -5) { 66 | // ...end the scene 67 | endLevel(true); 68 | } 69 | break; 70 | // If it's a crystal... 71 | case ObjectType.CRYSTAL: 72 | // Update the UI with the new count of crystals, and increment the count of 73 | // currently collected crystals 74 | crystalUiElement.innerText = String(++sceneConfiguration.data.crystalsCollected); 75 | break; 76 | // If it's a shield... 77 | case ObjectType.SHIELD_ITEM: 78 | // Update the UI with the new count of shields, and increment the count of 79 | // currently collected shields 80 | shieldUiElement.innerText = String(++sceneConfiguration.data.shieldsCollected); 81 | break; 82 | } 83 | } 84 | } 85 | }); 86 | }) 87 | }); 88 | } 89 | const playDestructionAnimation = (spawnPosition: Vector3) => { 90 | 91 | // Create six boxes 92 | for (let i = 0; i < 6; i++) { 93 | // Our destruction 'bits' will be black, but have some transparency to them 94 | let destructionBit = new Mesh(new BoxGeometry(1, 1, 1), new MeshBasicMaterial({ 95 | color: 'black', 96 | transparent: true, 97 | opacity: 0.4 98 | })); 99 | 100 | // Each destruction bit object within the scene will have a 'lifetime' property associated to it 101 | // This property is incremented every time a frame is drawn to the screen 102 | // Within our animate loop, we check if this is more than 500, and if it is, we remove the object 103 | destructionBit.userData.lifetime = 0; 104 | // Set the spawn position of the box 105 | destructionBit.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z); 106 | // Create an animation mixer for the object 107 | destructionBit.userData.mixer = new AnimationMixer(destructionBit); 108 | 109 | // Spawn the objects in a circle around the rocket 110 | let degrees = i / 45; 111 | 112 | // Work out where on the circle we should spawn this specific destruction bit 113 | let spawnX = Math.cos(radToDeg(degrees)) * 15; 114 | let spawnY = Math.sin(radToDeg(degrees)) * 15; 115 | 116 | // Create a VectorKeyFrameTrack that will animate this box from its starting position to the final 117 | // 'outward' position (so it looks like the boxes are exploding from the ship) 118 | let track = new VectorKeyframeTrack('.position', [0, 0.3], [ 119 | rocketModel.position.x, // x 1 120 | rocketModel.position.y, // y 1 121 | rocketModel.position.z, // z 1 122 | rocketModel.position.x + spawnX, // x 2 123 | rocketModel.position.y, // y 2 124 | rocketModel.position.z + spawnY, // z 2 125 | ]); 126 | 127 | // Create an animation clip with our VectorKeyFrameTrack 128 | const animationClip = new AnimationClip('animateIn', 10, [track]); 129 | const animationAction = destructionBit.userData.mixer.clipAction(animationClip); 130 | 131 | // Only play the animation once 132 | animationAction.setLoop(LoopOnce, 1); 133 | 134 | // When complete, leave the objects in their final position (don't reset them to the starting position) 135 | animationAction.clampWhenFinished = true; 136 | // Play the animation 137 | animationAction.play(); 138 | // Associate a Clock to the destruction bit. We use this within the render loop so ThreeJS knows how far 139 | // to move this object for this frame 140 | destructionBit.userData.clock = new Clock(); 141 | // Add the destruction bit to the scene 142 | scene.add(destructionBit); 143 | 144 | // Add the destruction bit to an array, to keep track of them 145 | destructionBits.push(destructionBit); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | My first three.js app 12 | 17 | 18 | 19 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
39 | 40 |
41 |
44 | 45 | Lighting the engines... 46 | 47 |
48 | 54 |
55 | 73 | 74 | 86 | 87 | 88 | 89 | 90 | 91 | 135 | 136 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 209 | 210 | 211 | -------------------------------------------------------------------------------- /rocketjourney/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | My first three.js app 12 | 17 | 18 | 19 | 37 | 48 | 54 |
55 | 73 | 74 | 86 | 87 | 88 | 89 | 90 | 91 | 135 | 136 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 209 | 210 | 211 | -------------------------------------------------------------------------------- /rocketjourney/objects/water.js: -------------------------------------------------------------------------------- 1 | import { 2 | Color, 3 | FrontSide, 4 | LinearFilter, 5 | MathUtils, 6 | Matrix4, 7 | PerspectiveCamera, 8 | Plane, 9 | RGBFormat, 10 | ShaderMaterial, 11 | UniformsLib, 12 | UniformsUtils, 13 | Vector3, 14 | Vector4, 15 | WebGLRenderTarget, 16 | Mesh 17 | } from 'three'; 18 | 19 | /** 20 | * Work based on : 21 | * https://github.com/Slayvin: Flat mirror for three.js 22 | * https://home.adelphi.edu/~stemkoski/ : An implementation of water shader based on the flat mirror 23 | * http://29a.ch/ && http://29a.ch/slides/2012/webglwater/ : Water shader explanations in WebGL 24 | */ 25 | 26 | class Water extends Mesh { 27 | 28 | constructor( geometry, options = {} ) { 29 | 30 | super( geometry ); 31 | 32 | const scope = this; 33 | 34 | const textureWidth = options.textureWidth !== undefined ? options.textureWidth : 512; 35 | const textureHeight = options.textureHeight !== undefined ? options.textureHeight : 512; 36 | 37 | const clipBias = options.clipBias !== undefined ? options.clipBias : 0.0; 38 | const alpha = options.alpha !== undefined ? options.alpha : 1.0; 39 | const time = options.time !== undefined ? options.time : 0.0; 40 | const normalSampler = options.waterNormals !== undefined ? options.waterNormals : null; 41 | const sunDirection = options.sunDirection !== undefined ? options.sunDirection : new Vector3( 0.70707, 0.70707, 0.0 ); 42 | const sunColor = new Color( options.sunColor !== undefined ? options.sunColor : 0xffffff ); 43 | const waterColor = new Color( options.waterColor !== undefined ? options.waterColor : 0x7F7F7F ); 44 | const eye = options.eye !== undefined ? options.eye : new Vector3( 0, 0, 0 ); 45 | const distortionScale = options.distortionScale !== undefined ? options.distortionScale : 20.0; 46 | const side = options.side !== undefined ? options.side : FrontSide; 47 | const fog = options.fog !== undefined ? options.fog : false; 48 | const speed = options.speed !== undefined ? options.speed : 5.0; 49 | 50 | // 51 | 52 | const mirrorPlane = new Plane(); 53 | const normal = new Vector3(); 54 | const mirrorWorldPosition = new Vector3(); 55 | const cameraWorldPosition = new Vector3(); 56 | const rotationMatrix = new Matrix4(); 57 | const lookAtPosition = new Vector3( 0, 0, - 1 ); 58 | const clipPlane = new Vector4(); 59 | 60 | const view = new Vector3(); 61 | const target = new Vector3(); 62 | const q = new Vector4(); 63 | 64 | const textureMatrix = new Matrix4(); 65 | 66 | const mirrorCamera = new PerspectiveCamera(); 67 | 68 | var accumulator = 0.0; 69 | 70 | const parameters = { 71 | minFilter: LinearFilter, 72 | magFilter: LinearFilter, 73 | format: RGBFormat 74 | }; 75 | 76 | const renderTarget = new WebGLRenderTarget( textureWidth, textureHeight, parameters ); 77 | 78 | if ( ! MathUtils.isPowerOfTwo( textureWidth ) || ! MathUtils.isPowerOfTwo( textureHeight ) ) { 79 | 80 | renderTarget.texture.generateMipmaps = false; 81 | 82 | } 83 | 84 | const mirrorShader = { 85 | 86 | uniforms: UniformsUtils.merge( [ 87 | UniformsLib[ 'fog' ], 88 | UniformsLib[ 'lights' ], 89 | { 90 | 'normalSampler': { value: null }, 91 | 'mirrorSampler': { value: null }, 92 | 'alpha': { value: 1.0 }, 93 | 'time': { value: 0.0 }, 94 | 'size': { value: 1.0 }, 95 | 'distortionScale': { value: 20.0 }, 96 | 'textureMatrix': { value: new Matrix4() }, 97 | 'sunColor': { value: new Color( 0x7F7F7F ) }, 98 | 'sunDirection': { value: new Vector3( 0.70707, 0.70707, 0 ) }, 99 | 'eye': { value: new Vector3(0,0,0) }, 100 | 'waterColor': { value: new Color( 0x555555 ) }, 101 | 'speed' : {value: 5.0} 102 | } 103 | ] ), 104 | 105 | vertexShader: require('./shaders/waterVertexShader.glsl').default, 106 | 107 | fragmentShader: require('./shaders/waterFragmentShader.glsl').default 108 | 109 | }; 110 | // debugger; 111 | 112 | const material = new ShaderMaterial( { 113 | fragmentShader: mirrorShader.fragmentShader, 114 | vertexShader: mirrorShader.vertexShader, 115 | uniforms: UniformsUtils.clone( mirrorShader.uniforms ), 116 | lights: true, 117 | side: side, 118 | fog: fog 119 | } ); 120 | 121 | material.uniforms[ 'mirrorSampler' ].value = renderTarget.texture; 122 | material.uniforms[ 'textureMatrix' ].value = textureMatrix; 123 | material.uniforms[ 'alpha' ].value = alpha; 124 | material.uniforms[ 'time' ].value = time; 125 | material.uniforms[ 'normalSampler' ].value = normalSampler; 126 | material.uniforms[ 'sunColor' ].value = sunColor; 127 | material.uniforms[ 'waterColor' ].value = waterColor; 128 | material.uniforms[ 'sunDirection' ].value = sunDirection; 129 | material.uniforms[ 'distortionScale' ].value = distortionScale; 130 | material.uniforms['speed'].value = speed; 131 | 132 | material.uniforms[ 'eye' ].value = eye; 133 | 134 | scope.material = material; 135 | 136 | scope.onBeforeRender = function ( renderer, scene, camera ) { 137 | 138 | accumulator += 1; 139 | // console.log(accumulator); 140 | 141 | mirrorWorldPosition.setFromMatrixPosition( scope.matrixWorld ); 142 | cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld ); 143 | 144 | rotationMatrix.extractRotation( scope.matrixWorld ); 145 | 146 | normal.set( 0, 0, 1 ); 147 | normal.applyMatrix4( rotationMatrix ); 148 | 149 | view.subVectors( mirrorWorldPosition, cameraWorldPosition ); 150 | // view.negate(); 151 | 152 | // Avoid rendering when mirror is facing away 153 | 154 | if ( view.dot( normal ) > 0 ) return; 155 | 156 | view.reflect( normal ).negate(); 157 | view.add( mirrorWorldPosition ); 158 | 159 | rotationMatrix.extractRotation( camera.matrixWorld ); 160 | 161 | lookAtPosition.set( 0, 0, - 1 ); 162 | lookAtPosition.applyMatrix4( rotationMatrix ); 163 | lookAtPosition.add( cameraWorldPosition ); 164 | // lookAtPosition.add(new Vector3(0,0,-accumulator)); 165 | 166 | target.subVectors( mirrorWorldPosition, lookAtPosition ); 167 | target.reflect( normal ).negate(); 168 | target.add( mirrorWorldPosition ); 169 | 170 | mirrorCamera.position.copy( view ); 171 | mirrorCamera.up.set( 0, 1, 0 ); 172 | mirrorCamera.up.applyMatrix4( rotationMatrix ); 173 | mirrorCamera.up.reflect( normal ); 174 | mirrorCamera.lookAt( target ); 175 | 176 | mirrorCamera.far = camera.far; // Used in WebGLBackground 177 | 178 | mirrorCamera.updateMatrixWorld(); 179 | mirrorCamera.projectionMatrix.copy( camera.projectionMatrix ); 180 | 181 | // Update the texture matrix 182 | textureMatrix.set( 183 | 0.5, 0.0, 0.0, 0.5, 184 | 0.0, 0.5, 0.0, 0.5, 185 | 0.0, 0.0, 0.5, 0.5, 186 | 0.0, 0.0, 0.0, 1.0 187 | ); 188 | textureMatrix.multiply( mirrorCamera.projectionMatrix ); 189 | textureMatrix.multiply( mirrorCamera.matrixWorldInverse ); 190 | 191 | // Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html 192 | // Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf 193 | mirrorPlane.setFromNormalAndCoplanarPoint( normal, mirrorWorldPosition ); 194 | mirrorPlane.applyMatrix4( mirrorCamera.matrixWorldInverse ); 195 | 196 | clipPlane.set( mirrorPlane.normal.x, mirrorPlane.normal.y, mirrorPlane.normal.z, mirrorPlane.constant ); 197 | 198 | const projectionMatrix = mirrorCamera.projectionMatrix; 199 | 200 | q.x = ( Math.sign( clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ]; 201 | q.y = ( Math.sign( clipPlane.y ) + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ]; 202 | q.z = - 1.0; 203 | q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ]; 204 | 205 | // Calculate the scaled plane vector 206 | clipPlane.multiplyScalar( 2.0 / clipPlane.dot( q ) ); 207 | 208 | // Replacing the third row of the projection matrix 209 | projectionMatrix.elements[ 2 ] = clipPlane.x; 210 | projectionMatrix.elements[ 6 ] = clipPlane.y; 211 | projectionMatrix.elements[ 10 ] = clipPlane.z + 1.0 - clipBias; 212 | projectionMatrix.elements[ 14 ] = clipPlane.w; 213 | 214 | eye.setFromMatrixPosition( camera.matrixWorld ); 215 | 216 | // Render 217 | 218 | const currentRenderTarget = renderer.getRenderTarget(); 219 | 220 | const currentXrEnabled = renderer.xr.enabled; 221 | const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate; 222 | 223 | scope.visible = false; 224 | 225 | renderer.xr.enabled = false; // Avoid camera modification and recursion 226 | renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows 227 | 228 | renderer.setRenderTarget( renderTarget ); 229 | 230 | renderer.state.buffers.depth.setMask( true ); // make sure the depth buffer is writable so it can be properly cleared, see #18897 231 | 232 | if ( renderer.autoClear === false ) renderer.clear(); 233 | renderer.render( scene, mirrorCamera ); 234 | 235 | scope.visible = true; 236 | 237 | renderer.xr.enabled = currentXrEnabled; 238 | renderer.shadowMap.autoUpdate = currentShadowAutoUpdate; 239 | 240 | renderer.setRenderTarget( currentRenderTarget ); 241 | 242 | // Restore viewport 243 | 244 | const viewport = camera.viewport; 245 | 246 | if ( viewport !== undefined ) { 247 | 248 | renderer.state.viewport( viewport ); 249 | 250 | } 251 | 252 | }; 253 | } 254 | } 255 | 256 | Water.prototype.isWater = true; 257 | 258 | export { Water }; 259 | -------------------------------------------------------------------------------- /objects/water.js: -------------------------------------------------------------------------------- 1 | import { 2 | Color, 3 | FrontSide, 4 | LinearFilter, 5 | MathUtils, 6 | Matrix4, 7 | PerspectiveCamera, 8 | Plane, 9 | RGBFormat, 10 | ShaderMaterial, 11 | UniformsLib, 12 | UniformsUtils, 13 | Vector3, 14 | Vector4, 15 | WebGLRenderTarget, 16 | Mesh 17 | } from 'three'; 18 | 19 | /** 20 | * Work based on : 21 | * https://github.com/Slayvin: Flat mirror for three.js 22 | * https://home.adelphi.edu/~stemkoski/ : An implementation of water shader based on the flat mirror 23 | * http://29a.ch/ && http://29a.ch/slides/2012/webglwater/ : Water shader explanations in WebGL 24 | */ 25 | 26 | class Water extends Mesh { 27 | 28 | constructor( geometry, options = {} ) { 29 | 30 | super( geometry ); 31 | 32 | const scope = this; 33 | 34 | const textureWidth = options.textureWidth !== undefined ? options.textureWidth : 512; 35 | const textureHeight = options.textureHeight !== undefined ? options.textureHeight : 512; 36 | 37 | const clipBias = options.clipBias !== undefined ? options.clipBias : 0.0; 38 | const alpha = options.alpha !== undefined ? options.alpha : 1.0; 39 | const time = options.time !== undefined ? options.time : 0.0; 40 | const normalSampler = options.waterNormals !== undefined ? options.waterNormals : null; 41 | const sunDirection = options.sunDirection !== undefined ? options.sunDirection : new Vector3( 0.70707, 0.70707, 0.0 ); 42 | const sunColor = new Color( options.sunColor !== undefined ? options.sunColor : 0xffffff ); 43 | const waterColor = new Color( options.waterColor !== undefined ? options.waterColor : 0x7F7F7F ); 44 | const eye = options.eye !== undefined ? options.eye : new Vector3( 0, 0, 0 ); 45 | const distortionScale = options.distortionScale !== undefined ? options.distortionScale : 20.0; 46 | const side = options.side !== undefined ? options.side : FrontSide; 47 | const fog = options.fog !== undefined ? options.fog : false; 48 | const speed = options.speed !== undefined ? options.speed : 5.0; 49 | 50 | // 51 | 52 | const mirrorPlane = new Plane(); 53 | const normal = new Vector3(); 54 | const mirrorWorldPosition = new Vector3(); 55 | const cameraWorldPosition = new Vector3(); 56 | const rotationMatrix = new Matrix4(); 57 | const lookAtPosition = new Vector3( 0, 0, - 1 ); 58 | const clipPlane = new Vector4(); 59 | 60 | const view = new Vector3(); 61 | const target = new Vector3(); 62 | const q = new Vector4(); 63 | 64 | const textureMatrix = new Matrix4(); 65 | 66 | const mirrorCamera = new PerspectiveCamera(); 67 | 68 | var accumulator = 0.0; 69 | 70 | const parameters = { 71 | minFilter: LinearFilter, 72 | magFilter: LinearFilter, 73 | format: RGBFormat 74 | }; 75 | 76 | const renderTarget = new WebGLRenderTarget( textureWidth, textureHeight, parameters ); 77 | 78 | if ( ! MathUtils.isPowerOfTwo( textureWidth ) || ! MathUtils.isPowerOfTwo( textureHeight ) ) { 79 | 80 | renderTarget.texture.generateMipmaps = false; 81 | 82 | } 83 | 84 | const mirrorShader = { 85 | 86 | uniforms: UniformsUtils.merge( [ 87 | UniformsLib[ 'fog' ], 88 | UniformsLib[ 'lights' ], 89 | { 90 | 'normalSampler': { value: null }, 91 | 'mirrorSampler': { value: null }, 92 | 'alpha': { value: 1.0 }, 93 | 'time': { value: 0.0 }, 94 | 'size': { value: 1.0 }, 95 | 'distortionScale': { value: 20.0 }, 96 | 'textureMatrix': { value: new Matrix4() }, 97 | 'sunColor': { value: new Color( 0x7F7F7F ) }, 98 | 'sunDirection': { value: new Vector3( 0.70707, 0.70707, 0 ) }, 99 | 'eye': { value: new Vector3(0,0,0) }, 100 | 'waterColor': { value: new Color( 0x555555 ) }, 101 | 'speed' : {value: 5.0} 102 | } 103 | ] ), 104 | 105 | vertexShader: require('./shaders/waterVertexShader.glsl').default, 106 | 107 | fragmentShader: require('./shaders/waterFragmentShader.glsl').default 108 | 109 | }; 110 | // debugger; 111 | 112 | const material = new ShaderMaterial( { 113 | fragmentShader: mirrorShader.fragmentShader, 114 | vertexShader: mirrorShader.vertexShader, 115 | uniforms: UniformsUtils.clone( mirrorShader.uniforms ), 116 | lights: true, 117 | side: side, 118 | fog: fog 119 | } ); 120 | 121 | material.uniforms[ 'mirrorSampler' ].value = renderTarget.texture; 122 | material.uniforms[ 'textureMatrix' ].value = textureMatrix; 123 | material.uniforms[ 'alpha' ].value = alpha; 124 | material.uniforms[ 'time' ].value = time; 125 | material.uniforms[ 'normalSampler' ].value = normalSampler; 126 | material.uniforms[ 'sunColor' ].value = sunColor; 127 | material.uniforms[ 'waterColor' ].value = waterColor; 128 | material.uniforms[ 'sunDirection' ].value = sunDirection; 129 | material.uniforms[ 'distortionScale' ].value = distortionScale; 130 | material.uniforms['speed'].value = speed; 131 | 132 | material.uniforms[ 'eye' ].value = eye; 133 | 134 | scope.material = material; 135 | 136 | scope.onBeforeRender = function ( renderer, scene, camera ) { 137 | 138 | accumulator += 1; 139 | // console.log(accumulator); 140 | 141 | mirrorWorldPosition.setFromMatrixPosition( scope.matrixWorld ); 142 | cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld ); 143 | 144 | rotationMatrix.extractRotation( scope.matrixWorld ); 145 | 146 | normal.set( 0, 0, 1 ); 147 | normal.applyMatrix4( rotationMatrix ); 148 | 149 | view.subVectors( mirrorWorldPosition, cameraWorldPosition ); 150 | // view.negate(); 151 | 152 | // Avoid rendering when mirror is facing away 153 | 154 | if ( view.dot( normal ) > 0 ) return; 155 | 156 | view.reflect( normal ).negate(); 157 | view.add( mirrorWorldPosition ); 158 | 159 | rotationMatrix.extractRotation( camera.matrixWorld ); 160 | 161 | lookAtPosition.set( 0, 0, - 1 ); 162 | lookAtPosition.applyMatrix4( rotationMatrix ); 163 | lookAtPosition.add( cameraWorldPosition ); 164 | // lookAtPosition.add(new Vector3(0,0,-accumulator)); 165 | 166 | target.subVectors( mirrorWorldPosition, lookAtPosition ); 167 | target.reflect( normal ).negate(); 168 | target.add( mirrorWorldPosition ); 169 | 170 | mirrorCamera.position.copy( view ); 171 | mirrorCamera.up.set( 0, 1, 0 ); 172 | mirrorCamera.up.applyMatrix4( rotationMatrix ); 173 | mirrorCamera.up.reflect( normal ); 174 | mirrorCamera.lookAt( target ); 175 | 176 | mirrorCamera.far = camera.far; // Used in WebGLBackground 177 | 178 | mirrorCamera.updateMatrixWorld(); 179 | mirrorCamera.projectionMatrix.copy( camera.projectionMatrix ); 180 | 181 | // Update the texture matrix 182 | textureMatrix.set( 183 | 0.5, 0.0, 0.0, 0.5, 184 | 0.0, 0.5, 0.0, 0.5, 185 | 0.0, 0.0, 0.5, 0.5, 186 | 0.0, 0.0, 0.0, 1.0 187 | ); 188 | textureMatrix.multiply( mirrorCamera.projectionMatrix ); 189 | textureMatrix.multiply( mirrorCamera.matrixWorldInverse ); 190 | 191 | // Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html 192 | // Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf 193 | mirrorPlane.setFromNormalAndCoplanarPoint( normal, mirrorWorldPosition ); 194 | mirrorPlane.applyMatrix4( mirrorCamera.matrixWorldInverse ); 195 | 196 | clipPlane.set( mirrorPlane.normal.x, mirrorPlane.normal.y, mirrorPlane.normal.z, mirrorPlane.constant ); 197 | 198 | const projectionMatrix = mirrorCamera.projectionMatrix; 199 | 200 | q.x = ( Math.sign( clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ]; 201 | q.y = ( Math.sign( clipPlane.y ) + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ]; 202 | q.z = - 1.0; 203 | q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ]; 204 | 205 | // Calculate the scaled plane vector 206 | clipPlane.multiplyScalar( 2.0 / clipPlane.dot( q ) ); 207 | 208 | // Replacing the third row of the projection matrix 209 | projectionMatrix.elements[ 2 ] = clipPlane.x; 210 | projectionMatrix.elements[ 6 ] = clipPlane.y; 211 | projectionMatrix.elements[ 10 ] = clipPlane.z + 1.0 - clipBias; 212 | projectionMatrix.elements[ 14 ] = clipPlane.w; 213 | 214 | eye.setFromMatrixPosition( camera.matrixWorld ); 215 | 216 | // Render 217 | 218 | const currentRenderTarget = renderer.getRenderTarget(); 219 | 220 | const currentXrEnabled = renderer.xr.enabled; 221 | const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate; 222 | 223 | scope.visible = false; 224 | 225 | renderer.xr.enabled = false; // Avoid camera modification and recursion 226 | renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows 227 | 228 | renderer.setRenderTarget( renderTarget ); 229 | 230 | renderer.state.buffers.depth.setMask( true ); // make sure the depth buffer is writable so it can be properly cleared, see #18897 231 | 232 | if ( renderer.autoClear === false ) renderer.clear(); 233 | renderer.render( scene, mirrorCamera ); 234 | 235 | scope.visible = true; 236 | 237 | renderer.xr.enabled = currentXrEnabled; 238 | renderer.shadowMap.autoUpdate = currentShadowAutoUpdate; 239 | 240 | renderer.setRenderTarget( currentRenderTarget ); 241 | 242 | // Restore viewport 243 | 244 | const viewport = camera.viewport; 245 | 246 | if ( viewport !== undefined ) { 247 | 248 | renderer.state.viewport( viewport ); 249 | 250 | } 251 | 252 | }; 253 | // this.rotation = undefined; 254 | 255 | } 256 | 257 | } 258 | 259 | Water.prototype.isWater = true; 260 | 261 | export { Water }; 262 | -------------------------------------------------------------------------------- /rocketjourney/game.ts: -------------------------------------------------------------------------------- 1 | import { AnimationClip, AnimationMixer, Clock, Euler, Group, LoopOnce, MathUtils, Mesh, MirroredRepeatWrapping, PerspectiveCamera, PlaneGeometry, PMREMGenerator, Quaternion, QuaternionKeyframeTrack, Scene, ShaderMaterial, TextureLoader, Vector3, VectorKeyframeTrack, WebGLRenderer } from "three"; 2 | import { Water } from "./objects/water"; 3 | import { Sky } from "three/examples/jsm/objects/Sky"; 4 | import { addBackgroundBit, addChallengeRow, challengeRows, environmentBits, mothershipModel, objectsInit, rocketModel, starterBay } from "./game/objects"; 5 | import { crystalUiElement, levelIndicator, shieldUiElement, showLevelEndScreen, uiInit, updateLevelEndUI } from "./game/ui"; 6 | 7 | export const scene = new Scene(); 8 | export const camera = new PerspectiveCamera( 9 | 75, 10 | window.innerWidth / window.innerHeight, 11 | 0.1, 12 | 2000 13 | ); 14 | 15 | export const destructionBits = new Array(); 16 | const sun = new Vector3(); 17 | 18 | const waterGeometry = new PlaneGeometry(10000, 10000); 19 | const water = new Water( 20 | waterGeometry, { 21 | 22 | textureWidth: 512, 23 | textureHeight: 512, 24 | waterNormals: new TextureLoader().load('static/normals/waternormals.jpeg', function (texture) { 25 | texture.wrapS = texture.wrapT = MirroredRepeatWrapping; 26 | }), 27 | sunDirection: new Vector3(), 28 | sunColor: 0xffffff, 29 | waterColor: 0x001e0f, 30 | distortionScale: 3.7, 31 | fog: scene.fog !== undefined 32 | } 33 | ); 34 | 35 | export const sceneConfiguration = { 36 | /// Whether the scene is ready (i.e.: All models have been loaded and can be used) 37 | ready: false, 38 | /// Whether the camera is moving from the beginning circular pattern to behind the ship 39 | cameraMovingToStartPosition: false, 40 | /// Whether the rocket is moving forward 41 | rocketMoving: false, 42 | // backgroundMoving: false, 43 | /// Collected game data 44 | data: { 45 | /// How many crystals the player has collected on this run 46 | crystalsCollected: 0, 47 | /// How many shields the player has collected on this run (can be as low as -5 if player hits rocks) 48 | shieldsCollected: 0, 49 | }, 50 | /// The length of the current level, increases as levels go up 51 | courseLength: 500, 52 | /// How far the player is through the current level, initialises to zero. 53 | courseProgress: 0, 54 | /// Whether the level has finished 55 | levelOver: false, 56 | /// The current level, initialises to one. 57 | level: 1, 58 | /// Gives the completion amount of the course thus far, from 0.0 to 1.0. 59 | coursePercentComplete: () => (sceneConfiguration.courseProgress / sceneConfiguration.courseLength), 60 | /// Whether the start animation is playing (the circular camera movement while looking at the ship) 61 | cameraStartAnimationPlaying: false, 62 | /// How many 'background bits' are in the scene (the cliffs) 63 | backgroundBitCount: 0, 64 | /// How many 'challenge rows' are in the scene (the rows that have rocks, shields, or crystals in them). 65 | challengeRowCount: 0, 66 | /// The current speed of the ship 67 | speed: 0.0 68 | } 69 | 70 | 71 | let renderer: WebGLRenderer; 72 | 73 | 74 | const init = () => { 75 | renderer = new WebGLRenderer(); 76 | renderer.setSize(window.innerWidth, window.innerHeight); 77 | document.body.appendChild(renderer.domElement); 78 | 79 | water.rotation.x = -Math.PI / 2; 80 | scene.add(water); 81 | 82 | const sky = new Sky(); 83 | sky.scale.setScalar(10000); 84 | scene.add(sky); 85 | 86 | // Set up variables to control the look of the sky 87 | const skyUniforms = sky.material.uniforms; 88 | skyUniforms['turbidity'].value = 10; 89 | skyUniforms['rayleigh'].value = 2; 90 | skyUniforms['mieCoefficient'].value = 0.005; 91 | skyUniforms['mieDirectionalG'].value = 0.8; 92 | 93 | const parameters = { 94 | elevation: 3, 95 | azimuth: 115 96 | }; 97 | 98 | const pmremGenerator = new PMREMGenerator(renderer); 99 | 100 | const phi = MathUtils.degToRad(90 - parameters.elevation); 101 | const theta = MathUtils.degToRad(parameters.azimuth); 102 | 103 | sun.setFromSphericalCoords(1, phi, theta); 104 | 105 | sky.material.uniforms['sunPosition'].value.copy(sun); 106 | (water.material as ShaderMaterial).uniforms['sunDirection'].value.copy(sun).normalize(); 107 | scene.environment = pmremGenerator.fromScene(sky as any).texture; 108 | 109 | (water.material as ShaderMaterial).uniforms['speed'].value = 0.0; 110 | 111 | rocketModel.scale.setScalar(0.3); 112 | scene.add(rocketModel); 113 | scene.add(mothershipModel); 114 | 115 | mothershipModel.position.y = 200; 116 | mothershipModel.position.z = 100; 117 | mothershipModel.scale.setScalar(15); 118 | sceneConfiguration.ready = true; 119 | } 120 | 121 | export const sceneSetup = (level: number) => { 122 | sceneConfiguration.challengeRowCount = 0; 123 | sceneConfiguration.backgroundBitCount = 0; 124 | 125 | camera.position.x = 15; 126 | camera.position.y = 12; 127 | camera.position.z = 50; 128 | camera.rotation.y = 2.5; 129 | 130 | scene.add(starterBay); 131 | starterBay.position.x = 10; 132 | starterBay.position.z = 120; 133 | 134 | rocketModel.rotation.x = Math.PI; 135 | rocketModel.rotation.z = Math.PI; 136 | 137 | rocketModel.position.y = 10; 138 | rocketModel.position.z = 70; 139 | 140 | challengeRows.forEach(x => { 141 | scene.remove(x.rowParent); 142 | }); 143 | 144 | environmentBits.forEach(x => { 145 | scene.remove(x); 146 | }); 147 | 148 | environmentBits.length = 0; 149 | challengeRows.length = 0; 150 | 151 | for (let i=0; i< 60; i++){ 152 | addChallengeRow(i); 153 | addBackgroundBit(i); 154 | } 155 | 156 | sceneConfiguration.cameraStartAnimationPlaying = false; 157 | sceneConfiguration.levelOver = false; 158 | rocketModel.userData.flyingAway = false; 159 | 160 | sceneConfiguration.courseProgress = 0; 161 | sceneConfiguration.courseLength = 1000 * level; 162 | 163 | sceneConfiguration.data.shieldsCollected = 0; 164 | sceneConfiguration.data.crystalsCollected = 0; 165 | 166 | crystalUiElement.innerText = String(sceneConfiguration.data.crystalsCollected); 167 | shieldUiElement.innerText = String(sceneConfiguration.data.shieldsCollected); 168 | 169 | levelIndicator.innerText = `LEVEL ${sceneConfiguration.level}`; 170 | 171 | sceneConfiguration.ready = true; 172 | } 173 | 174 | export const endLevel = (damaged: boolean) => { 175 | updateLevelEndUI(damaged); 176 | sceneConfiguration.rocketMoving = false; 177 | sceneConfiguration.levelOver = true; 178 | rocketModel.userData.flyingAway = true; 179 | destructionBits.forEach(x => { 180 | scene.remove(x); 181 | }); 182 | destructionBits.length = 0; 183 | 184 | // make the camera look at the rocket before it flies away 185 | 186 | let cubeLook = new Group(); 187 | let rocketPositionCopy = rocketModel.position.clone(); 188 | cubeLook.position.copy(rocketPositionCopy); 189 | cubeLook.lookAt(rocketModel.position); 190 | let lookAtRocketQuaternion = cubeLook.quaternion; 191 | 192 | let cameraRotationTrack = new QuaternionKeyframeTrack('.quaternion', [0, 2], [ 193 | camera.quaternion.x, 194 | camera.quaternion.y, 195 | camera.quaternion.z, 196 | camera.quaternion.w, 197 | lookAtRocketQuaternion.x, 198 | lookAtRocketQuaternion.y, 199 | lookAtRocketQuaternion.z, 200 | lookAtRocketQuaternion.w, 201 | ]) 202 | 203 | const lookAtRocketAnimationClip = new AnimationClip('lookAtRocket', 2, [cameraRotationTrack]); 204 | const lookAtRocketAnimationAction = camera.userData.mixer.clipAction(lookAtRocketAnimationClip); 205 | lookAtRocketAnimationAction.setLoop(LoopOnce, 1); 206 | lookAtRocketAnimationAction.clampWhenFinished = true; 207 | lookAtRocketAnimationAction.play(); 208 | 209 | rocketModel.userData.mixer = new AnimationMixer(rocketModel); 210 | let track = new VectorKeyframeTrack('.position', [2, 3, 5], [ 211 | rocketModel.position.x, rocketModel.position.y, rocketModel.position.z, 212 | 20, 100, 20, 213 | 40, 400, 100 214 | ]); 215 | 216 | let destinationQuaternion = new Quaternion().setFromEuler(new Euler(-90, 0, -90)) 217 | 218 | let rotationTrack = new QuaternionKeyframeTrack('.quaternion', [0, 2], [ 219 | rocketModel.quaternion.x, 220 | rocketModel.quaternion.y, 221 | rocketModel.quaternion.z, 222 | rocketModel.quaternion.w, 223 | destinationQuaternion.x, 224 | destinationQuaternion.y, 225 | destinationQuaternion.z, 226 | destinationQuaternion.w 227 | ]); 228 | 229 | rocketModel.userData.clock = new Clock(); 230 | 231 | const animationClip = new AnimationClip('flyAway', 6, [track, rotationTrack]); 232 | const animationAction = rocketModel.userData.mixer.clipAction(animationClip); 233 | animationAction.setLoop(LoopOnce, 1); 234 | animationAction.clampWhenFinished = true; 235 | 236 | rocketModel.userData.mixer.addEventListener('finished', function () { 237 | showLevelEndScreen(); 238 | }); 239 | animationAction.play(); 240 | } 241 | 242 | const animate = () => { 243 | requestAnimationFrame(animate); 244 | renderer.render(scene, camera); 245 | } 246 | 247 | objectsInit().then(x => { 248 | uiInit(); 249 | init(); 250 | }) 251 | 252 | animate() 253 | -------------------------------------------------------------------------------- /static/models/cliffs/scene.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "accessors": [ 3 | { 4 | "bufferView": 2, 5 | "componentType": 5126, 6 | "count": 728, 7 | "max": [ 8 | 1196.5694580078125, 9 | 1109.1278076171875, 10 | 672.35418701171875 11 | ], 12 | "min": [ 13 | -1200.1798095703125, 14 | -29.652145385742188, 15 | -644.428955078125 16 | ], 17 | "type": "VEC3" 18 | }, 19 | { 20 | "bufferView": 2, 21 | "byteOffset": 8736, 22 | "componentType": 5126, 23 | "count": 728, 24 | "max": [ 25 | 0.99275422096252441, 26 | 0.99804294109344482, 27 | 0.99908632040023804 28 | ], 29 | "min": [ 30 | -0.99455183744430542, 31 | -0.99999839067459106, 32 | -0.99863916635513306 33 | ], 34 | "type": "VEC3" 35 | }, 36 | { 37 | "bufferView": 3, 38 | "componentType": 5126, 39 | "count": 728, 40 | "max": [ 41 | 0.99963480234146118, 42 | 0.99959373474121094, 43 | 0.98715931177139282, 44 | 1 45 | ], 46 | "min": [ 47 | -0.99832171201705933, 48 | -0.9998171329498291, 49 | -0.98771309852600098, 50 | -1 51 | ], 52 | "type": "VEC4" 53 | }, 54 | { 55 | "bufferView": 1, 56 | "componentType": 5126, 57 | "count": 728, 58 | "max": [ 59 | 0.99689203500747681, 60 | 0.98296123743057251 61 | ], 62 | "min": [ 63 | 0.0031479585450142622, 64 | 0.0030837059020996094 65 | ], 66 | "type": "VEC2" 67 | }, 68 | { 69 | "bufferView": 1, 70 | "byteOffset": 5824, 71 | "componentType": 5126, 72 | "count": 728, 73 | "max": [ 74 | 0.99689203500747681, 75 | 0.98296123743057251 76 | ], 77 | "min": [ 78 | 0.0031479585450142622, 79 | 0.0030837059020996094 80 | ], 81 | "type": "VEC2" 82 | }, 83 | { 84 | "bufferView": 0, 85 | "componentType": 5125, 86 | "count": 3294, 87 | "max": [ 88 | 727 89 | ], 90 | "min": [ 91 | 0 92 | ], 93 | "type": "SCALAR" 94 | }, 95 | { 96 | "bufferView": 2, 97 | "byteOffset": 17472, 98 | "componentType": 5126, 99 | "count": 839, 100 | "max": [ 101 | 695.2216796875, 102 | 1555.0494384765625, 103 | 617.69775390625 104 | ], 105 | "min": [ 106 | -717.26708984375, 107 | -8.4538469314575195, 108 | -454.28326416015625 109 | ], 110 | "type": "VEC3" 111 | }, 112 | { 113 | "bufferView": 2, 114 | "byteOffset": 27540, 115 | "componentType": 5126, 116 | "count": 839, 117 | "max": [ 118 | 0.99945312738418579, 119 | 0.98819422721862793, 120 | 0.99945271015167236 121 | ], 122 | "min": [ 123 | -0.99916368722915649, 124 | -0.99999839067459106, 125 | -0.99971795082092285 126 | ], 127 | "type": "VEC3" 128 | }, 129 | { 130 | "bufferView": 3, 131 | "byteOffset": 11648, 132 | "componentType": 5126, 133 | "count": 839, 134 | "max": [ 135 | 0.99157595634460449, 136 | 0.9998202919960022, 137 | 0.99325472116470337, 138 | 1 139 | ], 140 | "min": [ 141 | -0.99824225902557373, 142 | -0.92144292593002319, 143 | -0.9990125298500061, 144 | -1 145 | ], 146 | "type": "VEC4" 147 | }, 148 | { 149 | "bufferView": 1, 150 | "byteOffset": 11648, 151 | "componentType": 5126, 152 | "count": 839, 153 | "max": [ 154 | 0.99683141708374023, 155 | 0.99263864755630493 156 | ], 157 | "min": [ 158 | 0.0028030585963279009, 159 | 0.0027884244918823242 160 | ], 161 | "type": "VEC2" 162 | }, 163 | { 164 | "bufferView": 1, 165 | "byteOffset": 18360, 166 | "componentType": 5126, 167 | "count": 839, 168 | "max": [ 169 | 0.99683141708374023, 170 | 0.99263864755630493 171 | ], 172 | "min": [ 173 | 0.0028030585963279009, 174 | 0.0027884244918823242 175 | ], 176 | "type": "VEC2" 177 | }, 178 | { 179 | "bufferView": 0, 180 | "byteOffset": 13176, 181 | "componentType": 5125, 182 | "count": 4014, 183 | "max": [ 184 | 838 185 | ], 186 | "min": [ 187 | 0 188 | ], 189 | "type": "SCALAR" 190 | } 191 | ], 192 | "asset": { 193 | "extras": { 194 | "author": "3DMaesen (https://sketchfab.com/bumstrum)", 195 | "license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)", 196 | "source": "https://sketchfab.com/3d-models/cliffs-14fe5023840146179c8515093e88fae0", 197 | "title": "Cliffs" 198 | }, 199 | "generator": "Sketchfab-7.47.0", 200 | "version": "2.0" 201 | }, 202 | "bufferViews": [ 203 | { 204 | "buffer": 0, 205 | "byteLength": 29232, 206 | "byteOffset": 0, 207 | "name": "floatBufferViews", 208 | "target": 34963 209 | }, 210 | { 211 | "buffer": 0, 212 | "byteLength": 25072, 213 | "byteOffset": 29232, 214 | "byteStride": 8, 215 | "name": "floatBufferViews", 216 | "target": 34962 217 | }, 218 | { 219 | "buffer": 0, 220 | "byteLength": 37608, 221 | "byteOffset": 54304, 222 | "byteStride": 12, 223 | "name": "floatBufferViews", 224 | "target": 34962 225 | }, 226 | { 227 | "buffer": 0, 228 | "byteLength": 25072, 229 | "byteOffset": 91912, 230 | "byteStride": 16, 231 | "name": "floatBufferViews", 232 | "target": 34962 233 | } 234 | ], 235 | "buffers": [ 236 | { 237 | "byteLength": 116984, 238 | "uri": "scene.bin" 239 | } 240 | ], 241 | "images": [ 242 | { 243 | "uri": "textures/cliff1_baseColor.png" 244 | }, 245 | { 246 | "uri": "textures/cliff1_metallicRoughness.png" 247 | }, 248 | { 249 | "uri": "textures/cliff1_normal.png" 250 | }, 251 | { 252 | "uri": "textures/cliff2_baseColor.png" 253 | }, 254 | { 255 | "uri": "textures/cliff2_metallicRoughness.png" 256 | }, 257 | { 258 | "uri": "textures/cliff2_normal.png" 259 | } 260 | ], 261 | "materials": [ 262 | { 263 | "doubleSided": false, 264 | "name": "cliff1", 265 | "normalTexture": { 266 | "index": 2, 267 | "scale": 1, 268 | "texCoord": 0 269 | }, 270 | "pbrMetallicRoughness": { 271 | "baseColorFactor": [ 272 | 1, 273 | 1, 274 | 1, 275 | 1 276 | ], 277 | "baseColorTexture": { 278 | "index": 0, 279 | "texCoord": 0 280 | }, 281 | "metallicFactor": 0, 282 | "metallicRoughnessTexture": { 283 | "index": 1, 284 | "texCoord": 0 285 | }, 286 | "roughnessFactor": 1 287 | } 288 | }, 289 | { 290 | "doubleSided": false, 291 | "name": "cliff2", 292 | "normalTexture": { 293 | "index": 5, 294 | "scale": 1, 295 | "texCoord": 0 296 | }, 297 | "pbrMetallicRoughness": { 298 | "baseColorFactor": [ 299 | 1, 300 | 1, 301 | 1, 302 | 1 303 | ], 304 | "baseColorTexture": { 305 | "index": 3, 306 | "texCoord": 0 307 | }, 308 | "metallicFactor": 0, 309 | "metallicRoughnessTexture": { 310 | "index": 4, 311 | "texCoord": 0 312 | }, 313 | "roughnessFactor": 1 314 | } 315 | } 316 | ], 317 | "meshes": [ 318 | { 319 | "name": "cliff2_cliff1_0", 320 | "primitives": [ 321 | { 322 | "attributes": { 323 | "NORMAL": 1, 324 | "POSITION": 0, 325 | "TANGENT": 2, 326 | "TEXCOORD_0": 3, 327 | "TEXCOORD_1": 4 328 | }, 329 | "indices": 5, 330 | "material": 0, 331 | "mode": 4 332 | } 333 | ] 334 | }, 335 | { 336 | "name": "cliff3_cliff2_0", 337 | "primitives": [ 338 | { 339 | "attributes": { 340 | "NORMAL": 7, 341 | "POSITION": 6, 342 | "TANGENT": 8, 343 | "TEXCOORD_0": 9, 344 | "TEXCOORD_1": 10 345 | }, 346 | "indices": 11, 347 | "material": 1, 348 | "mode": 4 349 | } 350 | ] 351 | } 352 | ], 353 | "nodes": [ 354 | { 355 | "children": [ 356 | 1 357 | ], 358 | "name": "RootNode (gltf orientation matrix)", 359 | "rotation": [ 360 | -0.70710678118654746, 361 | -0, 362 | -0, 363 | 0.70710678118654757 364 | ] 365 | }, 366 | { 367 | "children": [ 368 | 2 369 | ], 370 | "name": "RootNode (model correction matrix)" 371 | }, 372 | { 373 | "children": [ 374 | 3 375 | ], 376 | "matrix": [ 377 | 1, 378 | 0, 379 | 0, 380 | 0, 381 | 0, 382 | 0, 383 | 1, 384 | 0, 385 | 0, 386 | -1, 387 | 0, 388 | 0, 389 | 0, 390 | 0, 391 | 0, 392 | 1 393 | ], 394 | "name": "672308ab37df4983b8c49390a55f5219.fbx" 395 | }, 396 | { 397 | "children": [ 398 | 4, 399 | 6 400 | ], 401 | "name": "RootNode" 402 | }, 403 | { 404 | "children": [ 405 | 5 406 | ], 407 | "matrix": [ 408 | 1, 409 | 0, 410 | 0, 411 | 0, 412 | 0, 413 | 1, 414 | 0, 415 | 0, 416 | 0, 417 | 0, 418 | 1, 419 | 0, 420 | 0, 421 | 0, 422 | 1084.8150634765625, 423 | 1 424 | ], 425 | "name": "cliff2" 426 | }, 427 | { 428 | "mesh": 0, 429 | "name": "cliff2_cliff1_0" 430 | }, 431 | { 432 | "children": [ 433 | 7 434 | ], 435 | "matrix": [ 436 | 1, 437 | 0, 438 | 0, 439 | 0, 440 | 0, 441 | 0.99999999999998668, 442 | 1.6292067073209129e-07, 443 | 0, 444 | 0, 445 | -1.6292067073209129e-07, 446 | 0.99999999999998668, 447 | 0, 448 | 0, 449 | -3.1249796221355945e-29, 450 | 7.2676471063459758e-07, 451 | 1 452 | ], 453 | "name": "cliff3" 454 | }, 455 | { 456 | "mesh": 1, 457 | "name": "cliff3_cliff2_0" 458 | } 459 | ], 460 | "samplers": [ 461 | { 462 | "magFilter": 9729, 463 | "minFilter": 9987, 464 | "wrapS": 10497, 465 | "wrapT": 10497 466 | } 467 | ], 468 | "scene": 0, 469 | "scenes": [ 470 | { 471 | "name": "OSG_Scene", 472 | "nodes": [ 473 | 0 474 | ] 475 | } 476 | ], 477 | "textures": [ 478 | { 479 | "sampler": 0, 480 | "source": 0 481 | }, 482 | { 483 | "sampler": 0, 484 | "source": 1 485 | }, 486 | { 487 | "sampler": 0, 488 | "source": 2 489 | }, 490 | { 491 | "sampler": 0, 492 | "source": 3 493 | }, 494 | { 495 | "sampler": 0, 496 | "source": 4 497 | }, 498 | { 499 | "sampler": 0, 500 | "source": 5 501 | } 502 | ] 503 | } 504 | 505 | --------------------------------------------------------------------------------