diff --git a/src/client/public/.DS_Store b/src/client/public/.DS_Store new file mode 100644 index 0000000..84f5661 Binary files /dev/null and b/src/client/public/.DS_Store differ diff --git a/src/client/public/assets/besseggen.png b/src/client/public/assets/besseggen.png new file mode 100644 index 0000000..2ce70c2 Binary files /dev/null and b/src/client/public/assets/besseggen.png differ diff --git a/src/client/public/assets/besseggen10.png b/src/client/public/assets/besseggen10.png new file mode 100644 index 0000000..cccb412 Binary files /dev/null and b/src/client/public/assets/besseggen10.png differ diff --git a/src/client/public/assets/gora.png b/src/client/public/assets/gora.png new file mode 100644 index 0000000..c7bd7c2 Binary files /dev/null and b/src/client/public/assets/gora.png differ diff --git a/src/client/public/assets/img_the_scream.jpeg b/src/client/public/assets/img_the_scream.jpeg new file mode 100644 index 0000000..d2d4cef Binary files /dev/null and b/src/client/public/assets/img_the_scream.jpeg differ diff --git a/src/client/public/assets/jotunheimen.png b/src/client/public/assets/jotunheimen.png new file mode 100644 index 0000000..8bf1d7d Binary files /dev/null and b/src/client/public/assets/jotunheimen.png differ diff --git a/src/client/public/assets/new.png b/src/client/public/assets/new.png new file mode 100644 index 0000000..bac6a12 Binary files /dev/null and b/src/client/public/assets/new.png differ diff --git a/src/client/public/assets/raw.glb b/src/client/public/assets/raw.glb new file mode 100644 index 0000000..d26297d Binary files /dev/null and b/src/client/public/assets/raw.glb differ diff --git a/src/client/public/assets/rocks.jpeg b/src/client/public/assets/rocks.jpeg new file mode 100644 index 0000000..4c2f4f5 Binary files /dev/null and b/src/client/public/assets/rocks.jpeg differ diff --git a/src/client/public/index.html b/src/client/public/index.html index 6ac5819..5bb238c 100644 --- a/src/client/public/index.html +++ b/src/client/public/index.html @@ -17,9 +17,32 @@ height: 100vh; width: 100vw; } + + .test { + position: fixed; + top: 0; + left: 0; + /* width: 400px; */ + /* height: 400px; */ + padding: 15px; + outline: 1px solid red; + background-color: aqua; + } + + .img-canvas { + outline: 1px solid green; + /* width: 200px; + height: 200px; */ + } + +
+ + +
+ diff --git a/src/client/scene.ts b/src/client/scene.ts index fc93012..e271777 100644 --- a/src/client/scene.ts +++ b/src/client/scene.ts @@ -1,5 +1,6 @@ import * as THREE from 'three'; import { mergeBufferGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'; +// import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; // import { CMSMode, CSM } from 'three/examples/jsm/csm/CSM.js'; import RootThree from 'roothree'; import move from './move'; @@ -146,7 +147,7 @@ export default class Scene { createSkybox(_); _.scene.add(createLight(_)); _.scene.add(ground); - _.scene.add(boxes); + // _.scene.add(boxes); _.camera.position.y = 0.3; _.camera.position.z = 0.5; @@ -166,7 +167,9 @@ export default class Scene { _.observer.object.position.y = 8; _.observer.target.scale.set(0.5, 0.5, 0.5); - _.observer.addTeleportTargets([ground, boxes]); + _.observer.addTeleportTargets([ground]); + + // this.loadModel(_.scene); }; addUser(id, data?) { @@ -218,4 +221,24 @@ export default class Scene { removeUpdater(fn) { updaters.splice(updaters.indexOf(fn), 1); } + + // loadModel(scene) { + // const url = '/assets/raw.glb'; + + // const loader = new GLTFLoader(); + // loader.load(url, function (gltf) { + // // scene.add(gltf.scene); + // // render(); + // scene.add(gltf.scene); + + // gltf.scene.position.y = 8; + + // gltf.animations; // Array + // gltf.scene; // THREE.Group + // gltf.scenes; // Array + // gltf.cameras; // Array + // gltf.asset; // Object + // console.log(gltf); + // }); + // } } diff --git a/src/client/terrain.ts b/src/client/terrain.ts deleted file mode 100644 index 1eeac3f..0000000 --- a/src/client/terrain.ts +++ /dev/null @@ -1,60 +0,0 @@ -import * as THREE from 'three'; - -const textureLoader = new THREE.TextureLoader(); - -async function loadTerrain(url: RequestInfo | URL): Promise { - const res = await fetch(url); - const buffer = await res.arrayBuffer(); - - return new Uint16Array(buffer); -} - -export default async function getTerrain() { - const worldWidth = 200; - const worldDepth = 200; - - const geometry = new THREE.PlaneGeometry( - 32, - 32, - worldWidth - 1, - worldDepth - 1 - ); - const texture = textureLoader.load('/images/asphalt.jpg'); - const bumpMap = textureLoader.load('/images/asphalt-normals.jpg'); - const vertices = geometry.attributes.position.array; - - const data = Array.from( - new Uint16Array(await loadTerrain('/assets/terrain.bin')) - ).map(v => (v / 65535) * 10); - - console.log( - 'Verticies and binary data lenghts should be equal', - vertices.length / 3, - '===', - data.length - ); - - for (let i = 0; i < data.length; i++) { - //@ts-ignore - vertices[i * 3 + 2] = data[i]; - } - - const material = new THREE.MeshPhongMaterial({ - color: 0xdddddd, - map: texture, - bumpMap, - bumpScale: 1, - shininess: 0, - }); - const terrain = new THREE.Mesh(geometry, material); - - texture.anisotropy = 2; - texture.wrapS = texture.wrapT = THREE.RepeatWrapping; - texture.repeat.set(128, 128); - - terrain.rotation.x = -Math.PI / 2; - terrain.position.y = -5; - - // terrain.receiveShadow = true; - return terrain; -} diff --git a/src/client/terrain/drawImage.ts b/src/client/terrain/drawImage.ts new file mode 100644 index 0000000..2635954 --- /dev/null +++ b/src/client/terrain/drawImage.ts @@ -0,0 +1,23 @@ +export default function drawImage(inputImageData: ImageData) { + const canvas = document.querySelector('.img-canvas'); + const ctx = canvas.getContext('2d'); + + canvas.width = 200; + canvas.height = 200; + + const invertedImgData = invertImageData(inputImageData); + + ctx.putImageData(invertedImgData, 0, 0); +} + +function invertImageData(imgData: ImageData) { + var i; + for (i = 0; i < imgData.data.length; i += 4) { + imgData.data[i] = 255 - imgData.data[i]; + imgData.data[i + 1] = 255 - imgData.data[i + 1]; + imgData.data[i + 2] = 255 - imgData.data[i + 2]; + imgData.data[i + 3] = 255; + } + + return imgData; +} diff --git a/src/client/terrain/index.ts b/src/client/terrain/index.ts new file mode 100644 index 0000000..1fd7eaf --- /dev/null +++ b/src/client/terrain/index.ts @@ -0,0 +1,121 @@ +import * as THREE from 'three'; +import drawImage from './drawImage'; +const textureLoader = new THREE.TextureLoader(); + +async function blobToImageData(blob, width = 200, height = 200) { + let blobUrl = URL.createObjectURL(blob); + + function onLoad(img: HTMLImageElement) { + URL.revokeObjectURL(blobUrl); + // Limit to 256x256px while preserving aspect ratio + let [w, h] = [img.width, img.height]; + + let aspectRatio = w / h; + // Say the file is 1920x1080 + // divide max(w,h) by 256 to get factor + let factor = Math.max(w, h) / width; // TODO previosly we divided it to 256, divide to width is correct? + w = w / factor; + h = h / factor; + // REMINDER + // 256x256 = 65536 pixels with 4 channels (RGBA) = 262144 data points for each image + // Data is encoded as Uint8ClampedArray with BYTES_PER_ELEMENT = 1 + // So each images = 262144bytes + // 1000 images = 260Mb + let canvas = document.createElement('canvas'); + canvas.width = w; + canvas.height = h; + let ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0); + + return ctx.getImageData(0, 0, w, h); // some browsers synchronously decode image here + } + + return new Promise((resolve, reject) => { + let img = new Image(width, height); + + img.onload = () => resolve(img); + img.onerror = err => reject(err); + img.src = blobUrl; + }).then(onLoad); +} + +async function loadImage( + url: string, + width?: number, + height?: number +): Promise { + try { + const res = await fetch(url); + const imageBlob = await res.blob(); + + return await blobToImageData(imageBlob, width, height); + } catch (err) { + console.log('Error loading terrain', err); + } +} + +function getBinaryDataFromImageData(imageData: ImageData) { + const { data, width, height } = imageData; + const terrainData = new Array(width * height); + + for (let i = 0; i < data.length; i += 4) { + const x = i / 4; + // const y = Math.floor(i / 4 / width); + // const z = data[i] / 255; + // const index = y * width + x; + terrainData[x] = (data[i] / 255) * 10; + } + + return terrainData; +} + +export default async function getTerrain() { + const imageData = await loadImage('/assets/gora.png', 200, 200); + const data = getBinaryDataFromImageData(imageData); + const { width, height } = imageData; + console.log(width, height); + + const worldWidth = width; + const worldDepth = height; + + const geometry = new THREE.PlaneGeometry( + 20, + 20, + worldWidth - 1, + worldDepth - 1 + ); + const texture = textureLoader.load('/assets/rocks.jpeg'); + // const bumpMap = textureLoader.load('/images/asphalt-normals.jpg'); + const vertices = geometry.attributes.position.array; + + console.log( + 'Verticies and binary data lenghts should be equal', + vertices.length / 3, + '===', + data.length + ); + + for (let i = 0; i < data.length; i++) { + //@ts-ignore + vertices[i * 3 + 2] = data[i]; + } + + const material = new THREE.MeshPhongMaterial({ + color: 0xdddddd, + map: texture, + // bumpMap, + bumpScale: 1, + shininess: 0, + }); + const terrain = new THREE.Mesh(geometry, material); + + texture.anisotropy = 2; + texture.wrapS = texture.wrapT = THREE.RepeatWrapping; + texture.repeat.set(8, 8); + + terrain.rotation.x = -Math.PI / 2; + terrain.position.y = -5; + + // terrain.receiveShadow = true; + return terrain; +}