diff --git a/plugins/rollup.js b/plugins/rollup.js index a39f584..9f85821 100644 --- a/plugins/rollup.js +++ b/plugins/rollup.js @@ -45,6 +45,8 @@ const mob = require('../types/mob.js'); const react = require('../types/react.js'); const group = require('../types/group.js'); const directory = require('../types/directory.js'); +const sprite = require('../types/sprite.js'); +const scene2D = require('../types/scene2D.js'); const loaders = { js: jsx, jsx, @@ -74,6 +76,8 @@ const loaders = { react, group, wind, + sprite, + scene2D, '': directory, }; const upath = require('unix-path'); @@ -92,7 +96,7 @@ const _getType = id => { } let extension; let match2; - if (match2 = type.match(/^application\/(light|text|rendersettings|spawnpoint|lore|quest|npc|mob|react|group|wind)$/)) { + if (match2 = type.match(/^application\/(light|text|rendersettings|spawnpoint|lore|quest|npc|mob|react|group|wind|sprite|scene2D)$/)) { extension = match2[1]; } else if (match2 = type.match(/^application\/(javascript)$/)) { extension = 'js'; diff --git a/type_templates/gif.js b/type_templates/gif.js index ed2842e..f1b1b26 100644 --- a/type_templates/gif.js +++ b/type_templates/gif.js @@ -50,6 +50,7 @@ export default e => { textures = frames.map(frame => { const t = new THREE.Texture(frame); t.anisotropy = 16; + // t.encoding = THREE.sRGBEncoding; t.needsUpdate = true; return t; }); @@ -91,7 +92,7 @@ export default e => { useFrame(() => { if (textures) { const now = Date.now(); - const f = (now % 2000) / 2000; + const f = (now % 1000) / 1000; const frameIndex = Math.floor(f * textures.length); material.map = textures[frameIndex]; } @@ -103,4 +104,4 @@ export const contentId = ${this.contentId}; export const name = ${this.name}; export const description = ${this.description}; export const type = 'gif'; -export const components = ${this.components}; \ No newline at end of file +export const components = ${this.components}; diff --git a/type_templates/glb.js b/type_templates/glb.js index b320f31..d60d82d 100644 --- a/type_templates/glb.js +++ b/type_templates/glb.js @@ -1,7 +1,7 @@ import * as THREE from 'three'; import metaversefile from 'metaversefile'; -const {useApp, useFrame, useCleanup, useLocalPlayer, usePhysics, useLoaders, useActivate, useAvatarInternal, useInternals} = metaversefile; +const {useApp, useFrame, useCleanup, useLocalPlayer, usePhysics, useLoaders, useActivate, useAvatarInternal, useInternals,useCameraManager, useScene} = metaversefile; // const wearableScale = 1; @@ -20,6 +20,9 @@ export default e => { const physics = usePhysics(); const localPlayer = useLocalPlayer(); + const cameraManager = useCameraManager(); + + let scene = useScene(); const srcUrl = ${this.srcUrl}; for (const {key, value} of components) { @@ -197,7 +200,35 @@ export default e => { let physicsId; switch (physicsComponent.type) { case 'triangleMesh': { - physicsId = physics.addGeometry(o); + let worldPos = new THREE.Vector3(); + o.getWorldPosition(worldPos); + if(cameraManager.scene2D) { + switch (cameraManager.scene2D.perspective) { + case 'side-scroll': { + if(worldPos.z === 0) { + physicsId = physics.addGeometry2D(o, scene, 0); + console.log("2D-Geom", "perspective:", cameraManager.scene2D.perspective); + } + else { + physicsId = physics.addGeometry(o); + console.log("3D-Geom", "perspective:", cameraManager.scene2D.perspective); + } + } + case 'isometric': { + physicsId = physics.addGeometry(o); + console.log("3D-Geom", "perspective:", cameraManager.scene2D.perspective); + } + default: { + console.log("invalid perspective:", cameraManager.scene2D.perspective); + physicsId = physics.addGeometry(o); + break; + } + } + } + else { + physicsId = physics.addGeometry(o); + console.log("3D-Geom"); + } break; } case 'convexMesh': { @@ -226,6 +257,25 @@ export default e => { } o.traverse(o => { if (o.isMesh) { + //console.log(o); + // if(o.name === "body" || o.name === "rims") { + // const textureLoader = new THREE.TextureLoader(); + // o.material = new THREE.MeshPhysicalMaterial({color: 0x000000}); + // let envMapTexture = textureLoader.load('https://i.ibb.co/svQWWxz/full-spherical-seamless-hdri-panorama-360-degrees-angle-view-no-traffic-asphalt-road-among-fields-wi.jpg'); + // envMapTexture.mapping = THREE.EquirectangularReflectionMapping; + // envMapTexture.encoding = THREE.sRGBEncoding; + + // o.material.envMap = envMapTexture; + + // o.material.roughness = 0; + // o.material.clearcoat = 1; + // o.material.reflectivity = 1; + // o.material.metalness = 0; + // o.material.envMapIntensity = 5; + // o.material.needsUpdate = true + + // console.log(o, envMapTexture); + // } o.frustumCulled = false; o.castShadow = true; o.receiveShadow = true; diff --git a/type_templates/image.js b/type_templates/image.js index 2af1556..ce0555d 100644 --- a/type_templates/image.js +++ b/type_templates/image.js @@ -19,6 +19,8 @@ export default e => { app.image = null; const srcUrl = ${this.srcUrl}; + + console.log(srcUrl); // console.log('got gif 1'); const physicsIds = []; @@ -73,18 +75,10 @@ export default e => { const texture = new THREE.Texture(img); texture.needsUpdate = true; - const material = new THREE.MeshBasicMaterial({ - map: texture, - side: THREE.DoubleSide, - vertexColors: true, - transparent: true, - alphaTest: 0.5, - }); - /* const material = meshComposer.material.clone(); - material.uniforms.map.value = texture; - material.uniforms.map.needsUpdate = true; */ + + const material = new THREE.SpriteMaterial( { map: texture } ); - const mesh = new THREE.Mesh(geometry, material); + const mesh = new THREE.Sprite( material ); mesh.frustumCulled = false; // mesh.contentId = contentId; app.add(mesh); diff --git a/type_templates/scene2D.js b/type_templates/scene2D.js new file mode 100644 index 0000000..7074a16 --- /dev/null +++ b/type_templates/scene2D.js @@ -0,0 +1,39 @@ +import * as THREE from 'three'; +import metaversefile from 'metaversefile'; +const {useApp, useCameraManager, useCamera, useCleanup, useScene2DManager} = metaversefile; + +export default e => { + const app = useApp(); + const cameraManager = useCameraManager(); + const camera = useCamera(); + const scene2DManager = useScene2DManager(); + + const srcUrl = ${this.srcUrl}; + const mode = app.getComponent('mode') ?? 'attached'; + if (mode === 'attached') { + (async () => { + const res = await fetch(srcUrl); + const scene2D = await res.json(); + if (scene2D) { + let cameraType = "orthographic"; + let perspective = scene2D.perspective ? scene2D.perspective : "side-scroll"; + let cameraMode = scene2D.cameraMode ? scene2D.cameraMode : "follow"; + let scrollDirection = scene2D.scrollDirection ? scene2D.scrollDirection : "both"; + let controls = scene2D.controls ? scene2D.controls : "default"; + let viewS = scene2D.viewSize ? scene2D.viewSize : 15; + scene2DManager.setMode(perspective, cameraMode, viewS, scrollDirection, controls); + } + })(); + + useCleanup(() => { + scene2DManager.reset(); + }); + } + + return app; +}; +export const contentId = ${this.contentId}; +export const name = ${this.name}; +export const description = ${this.description}; +export const type = 'scene2D'; +export const components = ${this.components}; diff --git a/type_templates/sprite.js b/type_templates/sprite.js new file mode 100644 index 0000000..7034946 --- /dev/null +++ b/type_templates/sprite.js @@ -0,0 +1,108 @@ +import * as THREE from 'three'; + +import metaversefile from 'metaversefile'; +const {useApp, useFrame, useCleanup, usePhysics} = metaversefile; + +/* const flipGeomeryUvs = geometry => { + for (let i = 0; i < geometry.attributes.uv.array.length; i += 2) { + const j = i + 1; + geometry.attributes.uv.array[j] = 1 - geometry.attributes.uv.array[j]; + } +}; */ +// console.log('got gif 0'); + +export default e => { + const app = useApp(); + // const {gifLoader} = useLoaders(); + const physics = usePhysics(); + + app.image = null; + + const srcUrl = ${this.srcUrl}; + // console.log('got gif 1'); + + const physicsIds = []; + // const staticPhysicsIds = []; + e.waitUntil((async () => { + const img = await (async() => { + for (let i = 0; i < 10; i++) { // hack: give it a few tries, sometimes images fail for some reason + try { + const img = await new Promise((accept, reject) => { + const img = new Image(); + img.onload = () => { + accept(img); + // startMonetization(instanceId, monetizationPointer, ownerAddress); + // _cleanup(); + }; + img.onerror = err => { + const err2 = new Error('failed to load image: ' + srcUrl + ': ' + err); + reject(err2); + // _cleanup(); + } + /* const _cleanup = () => { + gcFiles && URL.revokeObjectURL(u); + }; */ + img.crossOrigin = 'Anonymous'; + img.src = srcUrl; + }); + return img; + } catch(err) { + console.warn(err); + } + } + throw new Error('failed to load image: ' + srcUrl); + })(); + app.image = img; + let {width, height} = img; + if (width >= height) { + height /= width; + width = 1; + } + if (height >= width) { + width /= height; + height = 1; + } + const geometry = new THREE.PlaneBufferGeometry(width, height); + geometry.boundingBox = new THREE.Box3( + new THREE.Vector3(-width/2, -height/2, -0.1), + new THREE.Vector3(width/2, height/2, 0.1), + ); + const colors = new Float32Array(geometry.attributes.position.array.length); + colors.fill(1, 0, colors.length); + geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); + + const texture = new THREE.Texture(img); + texture.needsUpdate = true; + + const material = new THREE.SpriteMaterial( { map: texture } ); + + const mesh = new THREE.Sprite( material ); + mesh.frustumCulled = false; + // mesh.contentId = contentId; + app.add(mesh); + mesh.updateMatrixWorld(); + + const physicsId = physics.addBoxGeometry( + app.position, + app.quaternion, + new THREE.Vector3(width/2, height/2, 0.01), + false + ); + physicsIds.push(physicsId); + // staticPhysicsIds.push(physicsId); + })()); + useCleanup(() => { + for (const physicsId of physicsIds) { + physics.removeGeometry(physicsId); + } + physicsIds.length = 0; + // staticPhysicsIds.length = 0; + }); + + return app; +}; +export const contentId = ${this.contentId}; +export const name = ${this.name}; +export const description = ${this.description}; +export const type = 'image'; +export const components = ${this.components}; \ No newline at end of file diff --git a/type_templates/spriteanimation.js b/type_templates/spriteanimation.js new file mode 100644 index 0000000..7034946 --- /dev/null +++ b/type_templates/spriteanimation.js @@ -0,0 +1,108 @@ +import * as THREE from 'three'; + +import metaversefile from 'metaversefile'; +const {useApp, useFrame, useCleanup, usePhysics} = metaversefile; + +/* const flipGeomeryUvs = geometry => { + for (let i = 0; i < geometry.attributes.uv.array.length; i += 2) { + const j = i + 1; + geometry.attributes.uv.array[j] = 1 - geometry.attributes.uv.array[j]; + } +}; */ +// console.log('got gif 0'); + +export default e => { + const app = useApp(); + // const {gifLoader} = useLoaders(); + const physics = usePhysics(); + + app.image = null; + + const srcUrl = ${this.srcUrl}; + // console.log('got gif 1'); + + const physicsIds = []; + // const staticPhysicsIds = []; + e.waitUntil((async () => { + const img = await (async() => { + for (let i = 0; i < 10; i++) { // hack: give it a few tries, sometimes images fail for some reason + try { + const img = await new Promise((accept, reject) => { + const img = new Image(); + img.onload = () => { + accept(img); + // startMonetization(instanceId, monetizationPointer, ownerAddress); + // _cleanup(); + }; + img.onerror = err => { + const err2 = new Error('failed to load image: ' + srcUrl + ': ' + err); + reject(err2); + // _cleanup(); + } + /* const _cleanup = () => { + gcFiles && URL.revokeObjectURL(u); + }; */ + img.crossOrigin = 'Anonymous'; + img.src = srcUrl; + }); + return img; + } catch(err) { + console.warn(err); + } + } + throw new Error('failed to load image: ' + srcUrl); + })(); + app.image = img; + let {width, height} = img; + if (width >= height) { + height /= width; + width = 1; + } + if (height >= width) { + width /= height; + height = 1; + } + const geometry = new THREE.PlaneBufferGeometry(width, height); + geometry.boundingBox = new THREE.Box3( + new THREE.Vector3(-width/2, -height/2, -0.1), + new THREE.Vector3(width/2, height/2, 0.1), + ); + const colors = new Float32Array(geometry.attributes.position.array.length); + colors.fill(1, 0, colors.length); + geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); + + const texture = new THREE.Texture(img); + texture.needsUpdate = true; + + const material = new THREE.SpriteMaterial( { map: texture } ); + + const mesh = new THREE.Sprite( material ); + mesh.frustumCulled = false; + // mesh.contentId = contentId; + app.add(mesh); + mesh.updateMatrixWorld(); + + const physicsId = physics.addBoxGeometry( + app.position, + app.quaternion, + new THREE.Vector3(width/2, height/2, 0.01), + false + ); + physicsIds.push(physicsId); + // staticPhysicsIds.push(physicsId); + })()); + useCleanup(() => { + for (const physicsId of physicsIds) { + physics.removeGeometry(physicsId); + } + physicsIds.length = 0; + // staticPhysicsIds.length = 0; + }); + + return app; +}; +export const contentId = ${this.contentId}; +export const name = ${this.name}; +export const description = ${this.description}; +export const type = 'image'; +export const components = ${this.components}; \ No newline at end of file diff --git a/types/scene2D.js b/types/scene2D.js new file mode 100644 index 0000000..ed79570 --- /dev/null +++ b/types/scene2D.js @@ -0,0 +1,33 @@ +const path = require('path'); +const fs = require('fs'); +const {fillTemplate, createRelativeFromAbsolutePath, parseIdHash} = require('../util.js'); + +const templateString = fs.readFileSync(path.join(__dirname, '..', 'type_templates', 'scene2D.js'), 'utf8'); +// const cwd = process.cwd(); + +module.exports = { + load(id) { + + id = createRelativeFromAbsolutePath(id); + + const { + contentId, + name, + description, + components, + } = parseIdHash(id); + + const code = fillTemplate(templateString, { + srcUrl: JSON.stringify(id), + contentId: JSON.stringify(contentId), + name: JSON.stringify(name), + description: JSON.stringify(description), + components: JSON.stringify(components), + }); + + return { + code, + map: null, + }; + }, +}; \ No newline at end of file diff --git a/types/sprite.js b/types/sprite.js new file mode 100644 index 0000000..2d3082d --- /dev/null +++ b/types/sprite.js @@ -0,0 +1,33 @@ +const path = require('path'); +const fs = require('fs'); +const {fillTemplate, createRelativeFromAbsolutePath, parseIdHash} = require('../util.js'); + +const templateString = fs.readFileSync(path.join(__dirname, '..', 'type_templates', 'sprite.js'), 'utf8'); +// const cwd = process.cwd(); + +module.exports = { + load(id) { + + id = createRelativeFromAbsolutePath(id); + + const { + contentId, + name, + description, + components, + } = parseIdHash(id); + + const code = fillTemplate(templateString, { + srcUrl: JSON.stringify(id), + contentId: JSON.stringify(contentId), + name: JSON.stringify(name), + description: JSON.stringify(description), + components: JSON.stringify(components), + }); + + return { + code, + map: null, + }; + }, +}; \ No newline at end of file diff --git a/types/spriteanimation.js b/types/spriteanimation.js new file mode 100644 index 0000000..ffefad0 --- /dev/null +++ b/types/spriteanimation.js @@ -0,0 +1,33 @@ +const path = require('path'); +const fs = require('fs'); +const {fillTemplate, createRelativeFromAbsolutePath, parseIdHash} = require('../util.js'); + +const templateString = fs.readFileSync(path.join(__dirname, '..', 'type_templates', 'spriteanimation.js'), 'utf8'); +// const cwd = process.cwd(); + +module.exports = { + load(id) { + + id = createRelativeFromAbsolutePath(id); + + const { + contentId, + name, + description, + components, + } = parseIdHash(id); + + const code = fillTemplate(templateString, { + srcUrl: JSON.stringify(id), + contentId: JSON.stringify(contentId), + name: JSON.stringify(name), + description: JSON.stringify(description), + components: JSON.stringify(components), + }); + + return { + code, + map: null, + }; + }, +}; \ No newline at end of file