diff --git a/.vscode/settings.json b/.vscode/settings.json
index 15d3670..1f17729 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -3,4 +3,7 @@
"editor.codeActionsOnSave": {
"source.fixAll": true
},
+ "[javascript]": {
+ "editor.defaultFormatter": "vscode.typescript-language-features"
+ },
}
diff --git a/blocks/environment/components/Controls.js b/blocks/environment/components/Controls.js
index 7288f06..1155511 100644
--- a/blocks/environment/components/Controls.js
+++ b/blocks/environment/components/Controls.js
@@ -1,15 +1,15 @@
import React, { useEffect, useRef, useState } from "react";
// import { Raycaster, Vector3, Math, Euler } from 'three';
-import * as THREE from "three";
+import { Euler, Raycaster, MathUtils } from "three";
import { useFrame, useThree } from "@react-three/fiber";
import { PointerLockControls } from "@react-three/drei";
// import previewOptions from "@wordpress/block-editor/build/components/preview-options";
import { useRapier, useRigidBody } from "@react-three/rapier";
-function touchStarted() {
- getAudioContext().resume();
-}
+// function touchStarted() {
+// getAudioContext().resume();
+// }
const Controls = (props) => {
const p2pcf = window.p2pcf;
@@ -21,8 +21,9 @@ const Controls = (props) => {
const [moveBackward, setMoveBackward] = useState(false);
const [moveLeft, setMoveLeft] = useState(false);
const [moveRight, setMoveRight] = useState(false);
- const [spawnPos, setSpawnPos] = useState();
+ const [spawnPos, setSpawnPos] = useState(props.spawnPoint);
const [jump, setJump] = useState(false);
+ const [thirdPerson, setThirdPerson] = useState(false); // Add this line
const currentRigidbody = useRigidBody();
const { world, rapier } = useRapier();
const ray = new rapier.Ray({ x: 0, y: 0, z: 0 }, { x: 0, y: -1, z: 0 });
@@ -34,27 +35,39 @@ const Controls = (props) => {
const { camera, scene } = useThree();
useEffect(() => {
- setSpawnPos(props.spawnPoint);
const playerThing = world.getRigidBody(props.something.current.handle);
- // if (playerThing) {
- // playerThing.setTranslation({
- // x: props.spawnPoint[0],
- // y: props.spawnPoint[1],
- // z: props.spawnPoint[2]
- // });
- // }
-
- // controlsRef.current
- // .getObject()
- // .parent.position.set(
- // props.spawnPoint[0],
- // props.spawnPoint[1],
- // props.spawnPoint[2]
- // );
+ const x = Number(spawnPos[0]);
+ const y = Number(spawnPos[1]);
+ const z = Number(spawnPos[2]);
+
+ setTimeout(() => {
+ let finalPoints = [];
+ if (props.spawnPointsToAdd) {
+ props.spawnPointsToAdd.forEach((point) => {
+ finalPoints.push([Number(point[0]), Number(point[1]), Number(point[2])]);
+ });
+ }
+ finalPoints.push([x, y, z]);
+ //pick a random point
+ let randomPoint = finalPoints[Math.floor(Math.random() * finalPoints.length)];
+ // Check if the converted values are valid and finite
+ // Set the camera's position
+ camera.position.set(randomPoint[0], randomPoint[1], randomPoint[2]);
+
+ playerThing.setTranslation({
+ x: randomPoint[0],
+ y: randomPoint[1],
+ z: randomPoint[2]
+ });
+ }, 20);
}, []);
- const raycaster = new THREE.Raycaster();
+
+ const raycaster = new Raycaster();
useFrame(() => {
+
+ const playerThing = world.getRigidBody(props.something.current.handle);
+
// raycaster.set( camera.position, camera.getWorldDirection() );
// raycast forward from the camera and log hitting any objects
// var intersects = raycaster.intersectObjects( scene.children );
@@ -62,7 +75,6 @@ const Controls = (props) => {
// console.log(intersects[0].object);
// }
- const playerThing = world.getRigidBody(props.something.current.handle);
const playerThingColliders = world.getCollider(
props.something.current.handle
);
@@ -98,11 +110,11 @@ const Controls = (props) => {
true
);
if (intersects.length > 0) {
- console.log(intersects[0].object.name);
+ // console.log(intersects[0].object.name);
const pointHitObject = scene.getObjectByName(
intersects[0].object.name
);
- console.log(pointHitObject);
+ // console.log(pointHitObject);
// add a rigidbody at the point of intersection
if (intersects[0].point) {
const rigidBodyDesc = new rapier.RigidBodyDesc(
@@ -142,64 +154,68 @@ const Controls = (props) => {
setClick(false);
}
if (moveForward) {
- // playerThing.applyImpulse({x:0, y:0, z:0.1}, true);
- controlsRef.current.moveForward(velocity);
- const hit = world
- .raw()
- .queryPipeline.castRay(
- world.raw().colliders,
- ray,
- maxToi,
- solid,
- 0xfffffffff
- );
- // const pointerHit = world
- // .raw()
- // .queryPipeline.castRay(
- // world.raw().colliders,
- // pointerRay,
- // maxToi,
- // solid,
- // 0xfffffffff
- // );
+ if (playerThing) {
+ controlsRef.current.moveForward(velocity);
+ const hit = world
+ .raw()
+ .queryPipeline.castRay(
+ world.raw().colliders,
+ ray,
+ maxToi,
+ solid,
+ 0xfffffffff
+ );
- playerThing.lockRotations(true, true);
- // playerThing.setRotation({x: 0, y: 1, z: 0, w: 0}, true);
- // if (pointerHit){
- // console.log(pointerHit);
- // const pointerHitPoint = pointerRay.pointAt(hit.toi);
- // console.log(pointerHitPoint);
+ playerThing.lockRotations(true, true);
+ if (hit) {
+ const hitPoint = ray.pointAt(hit.toi);
+ playerThing.setTranslation({
+ x: controlsRef.current.camera.position.x,
+ y: hitPoint.y,
+ z: controlsRef.current.camera.position.z
+ });
+ camera.position.setY(hitPoint.y + 0.001);
+ }
+ if (p2pcf) {
+ const position = [
+ controlsRef.current.camera.position.x,
+ controlsRef.current.camera.position.y,
+ controlsRef.current.camera.position.z
+ ];
+ const rotation = [
+ controlsRef.current.camera.rotation.x,
+ controlsRef.current.camera.rotation.y,
+ controlsRef.current.camera.rotation.z
+ ];
+ const message =
+ `{ "${p2pcf.clientId}": [{ "position" : [` +
+ position +
+ `]},{ "rotation" : [` +
+ rotation +
+ `]},{ "profileImage" : ["` +
+ userData.profileImage +
+ `"]}]}`;
+ p2pcf.broadcast(new TextEncoder().encode(message));
+ }
- // }
- if (hit) {
- const hitPoint = ray.pointAt(hit.toi);
- playerThing.setTranslation({
- x: controlsRef.current.camera.position.x,
- y: hitPoint.y,
- z: controlsRef.current.camera.position.z
- });
- camera.position.setY(hitPoint.y + 0.001);
- }
- if (p2pcf) {
- const position = [
- controlsRef.current.camera.position.x,
- controlsRef.current.camera.position.y,
- controlsRef.current.camera.position.z
- ];
- const rotation = [
- controlsRef.current.camera.rotation.x,
- controlsRef.current.camera.rotation.y,
- controlsRef.current.camera.rotation.z
- ];
- const message =
- `{ "${p2pcf.clientId}": [{ "position" : [` +
- position +
- `]},{ "rotation" : [` +
- rotation +
- `]},{ "profileImage" : ["` +
- userData.profileImage +
- `"]}]}`;
- p2pcf.broadcast(new TextEncoder().encode(message));
+ // playerThing.applyImpulse({x:0, y:0, z:0.1}, true);
+ // const pointerHit = world
+ // .raw()
+ // .queryPipeline.castRay(
+ // world.raw().colliders,
+ // pointerRay,
+ // maxToi,
+ // solid,
+ // 0xfffffffff
+ // );
+
+ // playerThing.setRotation({x: 0, y: 1, z: 0, w: 0}, true);
+ // if (pointerHit){
+ // console.log(pointerHit);
+ // const pointerHitPoint = pointerRay.pointAt(hit.toi);
+ // console.log(pointerHitPoint);
+
+ // }
}
} else if (moveLeft) {
playerThing.lockRotations(true, true);
@@ -336,7 +352,6 @@ const Controls = (props) => {
} else if (jump) {
}
});
-
const onKeyDown = function (event) {
switch (event.code) {
case "ArrowUp":
@@ -363,30 +378,42 @@ const Controls = (props) => {
setLock(false);
break;
case "KeyR":
- // @todo revisit the respawn logic
- // console.log(props);
- // if (props.something.current) {
- // const playerThing = world.getRigidBody(
- // props.something.current.handle
- // );
- // if (playerThing) {
- // playerThing.setTranslation({
- // x: props.spawnPoint[0],
- // y: props.spawnPoint[1],
- // z: props.spawnPoint[2]
- // });
- // if (controlsRef.current) {
- // console.log(controlsRef.current.getObject());
- // controlsRef.current
- // .getObject()
- // .parent.position.set(
- // props.spawnPoint[0],
- // props.spawnPoint[1],
- // props.spawnPoint[2]
- // );
- // }
- // }
- // }
+ if (props.something.current) {
+ const playerThing = world.getRigidBody(props.something.current.handle);
+
+ const x = Number(spawnPos[0]);
+ const y = Number(spawnPos[1]);
+ const z = Number(spawnPos[2]);
+ if (props.spawnPointsToAdd) {
+ let finalPoints = [];
+ props.spawnPointsToAdd.forEach((point) => {
+ finalPoints.push([Number(point.position.x), Number(point.position.y), Number(point.position.z)]);
+ });
+ finalPoints.push([x, y, z]);
+ //pick a random point
+ let randomPoint = finalPoints[Math.floor(Math.random() * finalPoints.length)];
+ // Check if the converted values are valid and finite
+ // Set the camera's position
+ camera.position.set(randomPoint[0], randomPoint[1], randomPoint[2]);
+
+ playerThing.setTranslation({
+ x: randomPoint[0],
+ y: randomPoint[1],
+ z: randomPoint[2]
+ });
+
+ } else {
+ // Check if the converted values are valid and finite
+ // Set the camera's position
+ camera.position.set(x, y, z);
+
+ playerThing.setTranslation({
+ x: x,
+ y: y,
+ z: z
+ });
+ }
+ }
setLock(false);
break;
case "Space":
@@ -407,7 +434,7 @@ const Controls = (props) => {
setClick(true);
});
- const onKeyUp = function (event, props) {
+ const onKeyUp = function (event) {
switch (event.code) {
case "ArrowUp":
case "KeyW":
@@ -427,10 +454,9 @@ const Controls = (props) => {
setLock(true);
break;
- // case "KeyR":
- // setLock(true);
- // break;
-
+ case "KeyR":
+ setLock(true);
+ break;
case "Space":
setJump(false);
setLock(true);
@@ -450,6 +476,7 @@ const Controls = (props) => {
document.addEventListener("keyup", onKeyUp);
return (
{
if (controlsRef.current) {
controlsRef.current.addEventListener("lock", () => {
@@ -485,13 +512,13 @@ const Controls = (props) => {
p2pcf.broadcast(new TextEncoder().encode(message));
}
const rotatingPlayer = scene.getObjectByName("playerOne");
- const euler = new THREE.Euler();
+ const euler = new Euler();
const rotation = euler.setFromQuaternion(
controlsRef.current.camera.quaternion
);
const radians =
rotation.z > 0 ? rotation.z : 2 * Math.PI + rotation.z;
- const degrees = THREE.MathUtils.radToDeg(radians);
+ const degrees = MathUtils.radToDeg(radians);
rotatingPlayer.rotation.set(0, radians, 0);
}}
ref={controlsRef}
diff --git a/blocks/environment/components/EnvironmentFront.js b/blocks/environment/components/EnvironmentFront.js
index a6c1820..8bfcea8 100644
--- a/blocks/environment/components/EnvironmentFront.js
+++ b/blocks/environment/components/EnvironmentFront.js
@@ -1,4 +1,5 @@
import * as THREE from "three";
+// import { Reflector } from 'three/examples/jsm/objects/Reflector';
import React, { Suspense, useRef, useState, useEffect, useMemo } from "react";
import { useLoader, useThree } from "@react-three/fiber";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
@@ -30,79 +31,20 @@ import defaultFont from "../../../inc/fonts/roboto.woff";
import { ItemBaseUI } from "@wordpress/components/build/navigation/styles/navigation-styles";
import { BoxGeometry } from "three";
-function parseMatrixUri(uri) {
- const SegmentToSigil = {
- u: "@",
- user: "@",
- r: "#",
- room: "#",
- roomid: "!"
- };
-
- const url = new URL(uri, window.location.href);
-
- if (url.protocol === "matrix:") {
- const matches = url.pathname.match(/^(\/\/.+\/)?(.+)$/);
-
- let authority;
- let path;
-
- if (matches) {
- if (matches.length === 3) {
- authority = matches[1];
- path = matches[2];
- } else if (matches.length === 2) {
- path = matches[1];
- }
- }
-
- if (!path) {
- throw new Error(`Invalid matrix uri "${uri}": No path provided`);
- }
-
- const segments = path.split("/");
-
- if (segments.length !== 2 && segments.length !== 4) {
- throw new Error(
- `Invalid matrix uri "${uri}": Invalid number of segments`
- );
- }
-
- const sigil1 = SegmentToSigil[segments[0]];
-
- if (!sigil1) {
- throw new Error(
- `Invalid matrix uri "${uri}": Invalid segment ${segments[0]}`
- );
- }
-
- if (!segments[1]) {
- throw new Error(`Invalid matrix uri "${uri}": Empty segment`);
- }
-
- const mxid1 = `${sigil1}${segments[1]}`;
-
- let mxid2;
-
- if (segments.length === 4) {
- if (
- (sigil1 === "!" || sigil1 === "#") &&
- (segments[2] === "e" || segments[2] === "event") &&
- segments[3]
- ) {
- mxid2 = `$${segments[3]}`;
- } else {
- throw new Error(
- `Invalid matrix uri "${uri}": Invalid segment ${segments[2]}`
- );
- }
- }
- return { protocol: "matrix:", authority, mxid1, mxid2 };
- }
-
- return url;
-}
-
+import { ThreeImage } from "./core/front/ThreeImage";
+import { ThreeVideo } from "./core/front/ThreeVideo";
+import { ModelObject } from "./core/front/ModelObject";
+import { Portal } from "./core/front/Portal";
+import { Sky } from "./core/front/Sky";
+import { TextObject } from "./core/front/TextObject";
+
+/**
+ * Represents a participant in a virtual reality scene.
+ *
+ * @param {Object} participant - The props for the participant.
+ *
+ * @return {JSX.Element} The participant.
+ */
function Participant(participant) {
// Participant VRM.
const fallbackURL = threeObjectPlugin + defaultVRM;
@@ -179,566 +121,6 @@ function Participant(participant) {
}
}
-function ModelObject(model) {
- const [clicked, setClickEvent] = useState();
- const [url, set] = useState(model.url);
- useEffect(() => {
- setTimeout(() => set(model.url), 2000);
- }, []);
- const [listener] = useState(() => new THREE.AudioListener());
-
- useThree(({ camera }) => {
- camera.add(listener);
- });
-
- const gltf = useLoader(GLTFLoader, url, (loader) => {
- // const dracoLoader = new DRACOLoader();
- // dracoLoader.setDecoderPath(
- // "https://www.gstatic.com/draco/v1/decoders/"
- // );
- // loader.setDRACOLoader(dracoLoader);
-
- loader.register(
- (parser) => new GLTFAudioEmitterExtension(parser, listener)
- );
- if (openbrushEnabled === true) {
- loader.register(
- (parser) =>
- new GLTFGoogleTiltBrushMaterialExtension(
- parser,
- openbrushDirectory
- )
- );
- }
- loader.register((parser) => {
- return new VRMLoaderPlugin(parser);
- });
- });
-
- const audioObject = gltf.scene.getObjectByProperty('type', 'Audio');
-
- const { actions } = useAnimations(gltf.animations, gltf.scene);
- const animationClips = gltf.animations;
- const animationList = model.animations ? model.animations.split(",") : "";
- useEffect(() => {
- if (animationList) {
- animationList.forEach((name) => {
- if (Object.keys(actions).includes(name)) {
- console.log(actions[name].play());
- }
- });
- }
- }, []);
-
- const generator = gltf.asset.generator;
-
- // return tilt brush if tilt brush
- if (String(generator).includes("Tilt Brush")) {
- return (
-
- );
- }
-
- if (gltf?.userData?.gltfExtensions?.VRM) {
- const vrm = gltf.userData.vrm;
- vrm.scene.position.set(
- model.positionX,
- model.positionY,
- model.positionZ
- );
- VRMUtils.rotateVRM0(vrm);
- const rotationVRM = vrm.scene.rotation.y + parseFloat(0);
- vrm.scene.rotation.set(0, rotationVRM, 0);
- vrm.scene.scale.set(1, 1, 1);
- vrm.scene.scale.set(model.scaleX, model.scaleY, model.scaleZ);
- return (
- //
-
- //
- );
- }
- // gltf.scene.castShadow = true;
- // enable shadows @todo figure this out
- // gltf.scene.traverse(function (node) {
- // if (node.isMesh) {
- // node.castShadow = true;
- // node.receiveShadow = true;
- // }
- // });
-
- // @todo figure out how to clone gltf proper with extensions and animations
- // const copyGltf = useMemo(() => gltf.scene.clone(), [gltf.scene]);
- // const modelClone = SkeletonUtils.clone(gltf.scene);
- // modelClone.scene.castShadow = true;
-
- //audioObject
- // Add a triangle mesh on top of the video
- const [triangle] = useState(() => {
- const points = [];
- points.push(
- new THREE.Vector3(0, -3, 0),
- new THREE.Vector3(0, 3, 0),
- new THREE.Vector3(4, 0, 0)
- );
- const geometry = new THREE.BufferGeometry().setFromPoints(points);
- const material = new THREE.MeshBasicMaterial({
- color: 0x00000,
- side: THREE.DoubleSide
- });
- const triangle = new THREE.Mesh(geometry, material);
- return triangle;
- });
-
- const [circle] = useState(() => {
- const geometryCircle = new THREE.CircleGeometry(5, 32);
- const materialCircle = new THREE.MeshBasicMaterial({
- color: 0xfffff,
- side: THREE.DoubleSide
- });
- const circle = new THREE.Mesh(geometryCircle, materialCircle);
- return circle;
- });
-
- if (model.collidable === "1") {
- return (
- <>
- {
- setClickEvent(!clicked);
- if(audioObject){
- if (clicked) {
- audioObject.play();
- triangle.material.visible = false;
- circle.material.visible = false;
- } else {
- audioObject.pause();
- triangle.material.visible = true;
- circle.material.visible = true;
- }
- }
- }}
- // onCollisionEnter={ ( props ) =>(
- // // window.location.href = model.destinationUrl
- // )
- // }
- >
-
-
- >
- );
- }
- return (
- <>
-
- >
- );
-}
-
-function Portal(model) {
-
- if (model.object) {
- return (
- <>
-
-
- {model.label
- ? model.label + ": "
- : "" + model.destinationUrl}
-
-
- {
- const url = new URL(
- model.destinationUrl,
- window.location.href
- );
- if (url.protocol === "matrix:") {
- const destination = parseMatrixUri(
- model.destinationUrl
- );
- window.location.href =
- "https://thirdroom.io/world/" +
- destination.mxid1;
- } else {
- window.location.href = model.destinationUrl;
- }
- }}
- >
-
-
- >
- );
- }
- const [url, set] = useState(model.url);
-
- useEffect(() => {
- setTimeout(() => set(model.url), 2000);
- }, []);
- const [listener] = useState(() => new THREE.AudioListener());
-
- useThree(({ camera }) => {
- camera.add(listener);
- });
-
- const gltf = useLoader(GLTFLoader, url, (loader) => {
- loader.register(
- (parser) => new GLTFAudioEmitterExtension(parser, listener)
- );
- loader.register((parser) => {
- return new VRMLoaderPlugin(parser);
- });
- });
-
- const { actions } = useAnimations(gltf.animations, gltf.scene);
-
- const animationList = model.animations ? model.animations.split(",") : "";
- useEffect(() => {
- if (animationList) {
- animationList.forEach((name) => {
- if (Object.keys(actions).includes(name)) {
- actions[name].play();
- }
- });
- }
- }, []);
- // gltf.scene.position.set( model.positionX, model.positionY, model.positionZ );
- // gltf.scene.rotation.set( 0, 0, 0 );
- // gltf.scene.scale.set(model.scaleX, model.scaleY, model.scaleZ);
- // gltf.scene.rotation.set(model.rotationX , model.rotationY, model.rotationZ );
- const copyGltf = useMemo(() => gltf.scene.clone(), [gltf.scene]);
-
- return (
- <>
-
- (window.location.href = model.destinationUrl)
- }
- rotation={[model.rotationX, model.rotationY, model.rotationZ]}
- position={[model.positionX, model.positionY, model.positionZ]}
- scale={[model.scaleX, model.scaleY, model.scaleZ]}
- >
-
-
- {model.label + ": " + model.destinationUrl}
-
-
-
-
- >
- );
-}
-
-function Sky(sky) {
- const skyUrl = sky.src[0].querySelector("p.sky-block-url")
- ? sky.src[0].querySelector("p.sky-block-url").innerText
- : "";
-
- const texture1 = useLoader(THREE.TextureLoader, skyUrl);
-
- return (
-
-
-
-
- );
-}
-
-function ThreeImage(threeImage) {
- const texture2 = useLoader(THREE.TextureLoader, threeImage.url);
-
- return (
-
-
- {threeImage.transparent ? (
-
- ) : (
-
- )}
-
- );
-}
-
-function ThreeVideo(threeVideo) {
- const play = threeVideo.autoPlay === "1" ? true : false;
- const { scene } = useThree();
- const [clicked, setClickEvent] = useState();
- const [video] = useState(() =>
- Object.assign(document.createElement("video"), {
- src: threeVideo.url,
- crossOrigin: "Anonymous",
- loop: true,
- muted: true
- })
- );
- // Add a triangle mesh on top of the video
- const [triangle] = useState(() => {
- const points = [];
- points.push(
- new THREE.Vector3(0, -3, 0),
- new THREE.Vector3(0, 3, 0),
- new THREE.Vector3(4, 0, 0)
- );
- const geometry = new THREE.BufferGeometry().setFromPoints(points);
- const material = new THREE.MeshBasicMaterial({
- color: 0x00000,
- side: THREE.DoubleSide
- });
- const triangle = new THREE.Mesh(geometry, material);
- return triangle;
- });
-
- const [circle] = useState(() => {
- const geometryCircle = new THREE.CircleGeometry(5, 32);
- const materialCircle = new THREE.MeshBasicMaterial({
- color: 0xfffff,
- side: THREE.DoubleSide
- });
- const circle = new THREE.Mesh(geometryCircle, materialCircle);
- return circle;
- });
-
- useEffect(() => {
- if (play) {
- triangle.material.visible = false;
- circle.material.visible = false;
- video.play();
- } else {
- triangle.material.visible = true;
- circle.material.visible = true;
- }
- }, [video, play]);
-
- return (
- // {
- // if (e.length !== 0) {
- // setClickEvent(!clicked);
- // if (clicked) {
- // video.play();
- // triangle.material.visible = false;
- // circle.material.visible = false;
- // } else {
- // video.pause();
- // triangle.material.visible = true;
- // circle.material.visible = true;
- // }
- // }
- // }}
- // filter={(items) => items}
- // >
-
- {
- setClickEvent(!clicked);
- if (clicked) {
- video.play();
- triangle.material.visible = false;
- circle.material.visible = false;
- } else {
- video.pause();
- triangle.material.visible = true;
- circle.material.visible = true;
- }
- }}
- >
-
-
-
-
-
-
-
-
-
-
-
-
- //
- );
-}
-
-function Floor(props) {
- return (
-
-
-
-
- );
-}
-
-function TextObject(model) {
- const htmlObj = useRef();
- return (
- <>
-
-
- {model.textContent}
-
-
- >
- );
-}
-
function Participants(props) {
const [participants, setParticipant] = useState([]);
const p2pcf = window.p2pcf;
@@ -766,6 +148,13 @@ function Participants(props) {
);
}
+/**
+ * Represents a saved object in a virtual reality world.
+ *
+ * @param {Object} props - The props for the saved object.
+ *
+ * @return {JSX.Element} The saved object.
+ */
function SavedObject(props) {
const meshRef = useRef();
const [url, set] = useState(props.url);
@@ -804,6 +193,7 @@ function SavedObject(props) {
const collidersToAdd = [];
const meshesToAdd = [];
const portalsToAdd = [];
+ const spawnPointsToAdd = [];
let omiColliders;
gltf.scene.scale.set(props.scale, props.scale, props.scale);
@@ -834,11 +224,24 @@ function SavedObject(props) {
}
if (child.userData.gltfExtensions?.OMI_link) {
portalsToAdd.push(child);
+ } else if (child.userData.gltfExtensions?.OMI_spawn_point) {
+ spawnPointsToAdd.push(child);
} else {
meshesToAdd.push(child);
}
});
+ // Mirror logic.
+ // const mirror = new Reflector(
+ // new THREE.PlaneGeometry(10, 10),
+ // {
+ // color: new THREE.Color(0x7f7f7f),
+ // textureWidth: window.innerWidth * window.devicePixelRatio,
+ // textureHeight: window.innerHeight * window.devicePixelRatio
+ // }
+ // )
+ // gltf.scene.add(mirror);
+
meshesToAdd.forEach((mesh) => {
meshesScene.attach(mesh);
});
@@ -851,6 +254,7 @@ function SavedObject(props) {
setColliders(collidersToAdd);
setMeshes(meshesScene);
setPortals(portalsToAdd);
+ props.setSpawnPoints(spawnPointsToAdd);
// End OMI_collider logic.
}, []);
@@ -866,11 +270,6 @@ function SavedObject(props) {
});
}
}, []);
- // Always seem to need to log this...
- // console.log(gltf);
- // const loader = new THREE.ObjectLoader();
- // const object = await loader.loadAsync("models/json/lightmap/lightmap.json");
- // scene.add(object);
return (
<>
@@ -908,6 +307,8 @@ function SavedObject(props) {
rotationZ={finalRotation.z}
object={item.parent}
label={props.label}
+ defaultFont={defaultFont}
+ threeObjectPlugin={threeObjectPlugin}
destinationUrl={
item.userData.gltfExtensions.OMI_link.uri
}
@@ -974,6 +375,8 @@ function SavedObject(props) {
export default function EnvironmentFront(props) {
const [loaded, setLoaded] = useState(false);
+ const [spawnPoints, setSpawnPoints] = useState();
+
if (loaded === true) {
if (props.deviceTarget === "vr") {
return (
@@ -1003,18 +406,18 @@ export default function EnvironmentFront(props) {
*/}
{props.threeUrl && (
<>
-
+
@@ -1039,6 +447,7 @@ export default function EnvironmentFront(props) {
hasTip={props.hasTip}
animations={props.animations}
playerData={props.userData}
+ setSpawnPoints={setSpawnPoints}
/>
{Object.values(props.sky).map(
(item, index) => {
@@ -1059,7 +468,7 @@ export default function EnvironmentFront(props) {
"p.image-block-positionX"
)
? item.querySelector(
- "p.image-block-positionX"
+ "p.image-block-positionX"
).innerText
: "";
@@ -1068,7 +477,7 @@ export default function EnvironmentFront(props) {
"p.image-block-positionY"
)
? item.querySelector(
- "p.image-block-positionY"
+ "p.image-block-positionY"
).innerText
: "";
@@ -1077,7 +486,7 @@ export default function EnvironmentFront(props) {
"p.image-block-positionZ"
)
? item.querySelector(
- "p.image-block-positionZ"
+ "p.image-block-positionZ"
).innerText
: "";
@@ -1086,7 +495,7 @@ export default function EnvironmentFront(props) {
"p.image-block-scaleX"
)
? item.querySelector(
- "p.image-block-scaleX"
+ "p.image-block-scaleX"
).innerText
: "";
@@ -1095,7 +504,7 @@ export default function EnvironmentFront(props) {
"p.image-block-scaleY"
)
? item.querySelector(
- "p.image-block-scaleY"
+ "p.image-block-scaleY"
).innerText
: "";
@@ -1104,7 +513,7 @@ export default function EnvironmentFront(props) {
"p.image-block-scaleZ"
)
? item.querySelector(
- "p.image-block-scaleZ"
+ "p.image-block-scaleZ"
).innerText
: "";
@@ -1113,7 +522,7 @@ export default function EnvironmentFront(props) {
"p.image-block-rotationX"
)
? item.querySelector(
- "p.image-block-rotationX"
+ "p.image-block-rotationX"
).innerText
: "";
@@ -1122,7 +531,7 @@ export default function EnvironmentFront(props) {
"p.image-block-rotationY"
)
? item.querySelector(
- "p.image-block-rotationY"
+ "p.image-block-rotationY"
).innerText
: "";
@@ -1131,7 +540,7 @@ export default function EnvironmentFront(props) {
"p.image-block-rotationZ"
)
? item.querySelector(
- "p.image-block-rotationZ"
+ "p.image-block-rotationZ"
).innerText
: "";
@@ -1140,7 +549,7 @@ export default function EnvironmentFront(props) {
"p.image-block-url"
)
? item.querySelector(
- "p.image-block-url"
+ "p.image-block-url"
).innerText
: "";
@@ -1149,7 +558,7 @@ export default function EnvironmentFront(props) {
"p.image-block-aspect-height"
)
? item.querySelector(
- "p.image-block-aspect-height"
+ "p.image-block-aspect-height"
).innerText
: "";
@@ -1158,7 +567,7 @@ export default function EnvironmentFront(props) {
"p.image-block-aspect-width"
)
? item.querySelector(
- "p.image-block-aspect-width"
+ "p.image-block-aspect-width"
).innerText
: "";
@@ -1167,7 +576,7 @@ export default function EnvironmentFront(props) {
"p.image-block-transparent"
)
? item.querySelector(
- "p.image-block-transparent"
+ "p.image-block-transparent"
).innerText
: false;
return (
@@ -1209,7 +618,7 @@ export default function EnvironmentFront(props) {
"p.video-block-positionX"
)
? item.querySelector(
- "p.video-block-positionX"
+ "p.video-block-positionX"
).innerText
: "";
@@ -1218,7 +627,7 @@ export default function EnvironmentFront(props) {
"p.video-block-positionY"
)
? item.querySelector(
- "p.video-block-positionY"
+ "p.video-block-positionY"
).innerText
: "";
@@ -1227,7 +636,7 @@ export default function EnvironmentFront(props) {
"p.video-block-positionZ"
)
? item.querySelector(
- "p.video-block-positionZ"
+ "p.video-block-positionZ"
).innerText
: "";
@@ -1236,7 +645,7 @@ export default function EnvironmentFront(props) {
"p.video-block-scaleX"
)
? item.querySelector(
- "p.video-block-scaleX"
+ "p.video-block-scaleX"
).innerText
: "";
@@ -1245,7 +654,7 @@ export default function EnvironmentFront(props) {
"p.video-block-scaleY"
)
? item.querySelector(
- "p.video-block-scaleY"
+ "p.video-block-scaleY"
).innerText
: "";
@@ -1254,7 +663,7 @@ export default function EnvironmentFront(props) {
"p.video-block-scaleZ"
)
? item.querySelector(
- "p.video-block-scaleZ"
+ "p.video-block-scaleZ"
).innerText
: "";
@@ -1263,7 +672,7 @@ export default function EnvironmentFront(props) {
"p.video-block-rotationX"
)
? item.querySelector(
- "p.video-block-rotationX"
+ "p.video-block-rotationX"
).innerText
: "";
@@ -1272,7 +681,7 @@ export default function EnvironmentFront(props) {
"p.video-block-rotationY"
)
? item.querySelector(
- "p.video-block-rotationY"
+ "p.video-block-rotationY"
).innerText
: "";
@@ -1281,7 +690,7 @@ export default function EnvironmentFront(props) {
"p.video-block-rotationZ"
)
? item.querySelector(
- "p.video-block-rotationZ"
+ "p.video-block-rotationZ"
).innerText
: "";
@@ -1290,7 +699,7 @@ export default function EnvironmentFront(props) {
"div.video-block-url"
)
? item.querySelector(
- "div.video-block-url"
+ "div.video-block-url"
).innerText
: "";
@@ -1299,7 +708,7 @@ export default function EnvironmentFront(props) {
"p.video-block-aspect-height"
)
? item.querySelector(
- "p.video-block-aspect-height"
+ "p.video-block-aspect-height"
).innerText
: "";
@@ -1308,7 +717,7 @@ export default function EnvironmentFront(props) {
"p.video-block-aspect-width"
)
? item.querySelector(
- "p.video-block-aspect-width"
+ "p.video-block-aspect-width"
).innerText
: "";
@@ -1317,7 +726,7 @@ export default function EnvironmentFront(props) {
"p.video-block-autoplay"
)
? item.querySelector(
- "p.video-block-autoplay"
+ "p.video-block-autoplay"
).innerText
: false;
@@ -1359,7 +768,7 @@ export default function EnvironmentFront(props) {
"p.model-block-position-x"
)
? model.querySelector(
- "p.model-block-position-x"
+ "p.model-block-position-x"
).innerText
: "";
@@ -1368,7 +777,7 @@ export default function EnvironmentFront(props) {
"p.model-block-position-y"
)
? model.querySelector(
- "p.model-block-position-y"
+ "p.model-block-position-y"
).innerText
: "";
@@ -1377,7 +786,7 @@ export default function EnvironmentFront(props) {
"p.model-block-position-z"
)
? model.querySelector(
- "p.model-block-position-z"
+ "p.model-block-position-z"
).innerText
: "";
@@ -1386,7 +795,7 @@ export default function EnvironmentFront(props) {
"p.model-block-scale-x"
)
? model.querySelector(
- "p.model-block-scale-x"
+ "p.model-block-scale-x"
).innerText
: "";
@@ -1395,7 +804,7 @@ export default function EnvironmentFront(props) {
"p.model-block-scale-y"
)
? model.querySelector(
- "p.model-block-scale-y"
+ "p.model-block-scale-y"
).innerText
: "";
@@ -1404,7 +813,7 @@ export default function EnvironmentFront(props) {
"p.model-block-scale-z"
)
? model.querySelector(
- "p.model-block-scale-z"
+ "p.model-block-scale-z"
).innerText
: "";
@@ -1413,7 +822,7 @@ export default function EnvironmentFront(props) {
"p.model-block-rotation-x"
)
? model.querySelector(
- "p.model-block-rotation-x"
+ "p.model-block-rotation-x"
).innerText
: "";
@@ -1422,7 +831,7 @@ export default function EnvironmentFront(props) {
"p.model-block-rotation-y"
)
? model.querySelector(
- "p.model-block-rotation-y"
+ "p.model-block-rotation-y"
).innerText
: "";
@@ -1431,7 +840,7 @@ export default function EnvironmentFront(props) {
"p.model-block-rotation-z"
)
? model.querySelector(
- "p.model-block-rotation-z"
+ "p.model-block-rotation-z"
).innerText
: "";
@@ -1439,7 +848,7 @@ export default function EnvironmentFront(props) {
"p.model-block-url"
)
? model.querySelector(
- "p.model-block-url"
+ "p.model-block-url"
).innerText
: "";
@@ -1448,7 +857,7 @@ export default function EnvironmentFront(props) {
"p.model-block-animations"
)
? model.querySelector(
- "p.model-block-animations"
+ "p.model-block-animations"
).innerText
: "";
@@ -1456,7 +865,7 @@ export default function EnvironmentFront(props) {
"p.model-block-alt"
)
? model.querySelector(
- "p.model-block-alt"
+ "p.model-block-alt"
).innerText
: "";
@@ -1465,7 +874,7 @@ export default function EnvironmentFront(props) {
"p.model-block-collidable"
)
? model.querySelector(
- "p.model-block-collidable"
+ "p.model-block-collidable"
).innerText
: false;
@@ -1501,7 +910,7 @@ export default function EnvironmentFront(props) {
"p.three-text-content"
)
? model.querySelector(
- "p.three-text-content"
+ "p.three-text-content"
).innerText
: "";
const rotationX =
@@ -1509,7 +918,7 @@ export default function EnvironmentFront(props) {
"p.three-text-rotationX"
)
? model.querySelector(
- "p.three-text-rotationX"
+ "p.three-text-rotationX"
).innerText
: "";
const rotationY =
@@ -1517,7 +926,7 @@ export default function EnvironmentFront(props) {
"p.three-text-rotationY"
)
? model.querySelector(
- "p.three-text-rotationY"
+ "p.three-text-rotationY"
).innerText
: "";
const rotationZ =
@@ -1525,7 +934,7 @@ export default function EnvironmentFront(props) {
"p.three-text-rotationZ"
)
? model.querySelector(
- "p.three-text-rotationZ"
+ "p.three-text-rotationZ"
).innerText
: "";
const positionX =
@@ -1533,7 +942,7 @@ export default function EnvironmentFront(props) {
"p.three-text-positionX"
)
? model.querySelector(
- "p.three-text-positionX"
+ "p.three-text-positionX"
).innerText
: "";
const positionY =
@@ -1541,7 +950,7 @@ export default function EnvironmentFront(props) {
"p.three-text-positionY"
)
? model.querySelector(
- "p.three-text-positionY"
+ "p.three-text-positionY"
).innerText
: "";
const positionZ =
@@ -1549,7 +958,7 @@ export default function EnvironmentFront(props) {
"p.three-text-positionZ"
)
? model.querySelector(
- "p.three-text-positionZ"
+ "p.three-text-positionZ"
).innerText
: "";
const scaleX =
@@ -1557,7 +966,7 @@ export default function EnvironmentFront(props) {
"p.three-text-scaleX"
)
? model.querySelector(
- "p.three-text-scaleX"
+ "p.three-text-scaleX"
).innerText
: "";
const scaleY =
@@ -1565,7 +974,7 @@ export default function EnvironmentFront(props) {
"p.three-text-scaleY"
)
? model.querySelector(
- "p.three-text-scaleY"
+ "p.three-text-scaleY"
).innerText
: "";
const scaleZ =
@@ -1573,7 +982,7 @@ export default function EnvironmentFront(props) {
"p.three-text-scaleZ"
)
? model.querySelector(
- "p.three-text-scaleZ"
+ "p.three-text-scaleZ"
).innerText
: "";
@@ -1582,7 +991,7 @@ export default function EnvironmentFront(props) {
"p.three-text-color"
)
? model.querySelector(
- "p.three-text-color"
+ "p.three-text-color"
).innerText
: "";
@@ -1604,6 +1013,8 @@ export default function EnvironmentFront(props) {
scaleX={scaleX}
scaleY={scaleY}
scaleZ={scaleZ}
+ defaultFont={defaultFont}
+ threeObjectPlugin={threeObjectPlugin}
textColor={
textColor
}
@@ -1616,8 +1027,8 @@ export default function EnvironmentFront(props) {
rotationZ={
rotationZ
}
- // alt={alt}
- // animations={animations}
+ // alt={alt}
+ // animations={animations}
/>
);
}
@@ -1630,7 +1041,7 @@ export default function EnvironmentFront(props) {
"p.three-portal-block-position-x"
)
? model.querySelector(
- "p.three-portal-block-position-x"
+ "p.three-portal-block-position-x"
).innerText
: "";
@@ -1639,7 +1050,7 @@ export default function EnvironmentFront(props) {
"p.three-portal-block-position-y"
)
? model.querySelector(
- "p.three-portal-block-position-y"
+ "p.three-portal-block-position-y"
).innerText
: "";
@@ -1648,7 +1059,7 @@ export default function EnvironmentFront(props) {
"p.three-portal-block-position-z"
)
? model.querySelector(
- "p.three-portal-block-position-z"
+ "p.three-portal-block-position-z"
).innerText
: "";
@@ -1657,7 +1068,7 @@ export default function EnvironmentFront(props) {
"p.three-portal-block-scale-x"
)
? model.querySelector(
- "p.three-portal-block-scale-x"
+ "p.three-portal-block-scale-x"
).innerText
: "";
@@ -1666,7 +1077,7 @@ export default function EnvironmentFront(props) {
"p.three-portal-block-scale-y"
)
? model.querySelector(
- "p.three-portal-block-scale-y"
+ "p.three-portal-block-scale-y"
).innerText
: "";
@@ -1675,7 +1086,7 @@ export default function EnvironmentFront(props) {
"p.three-portal-block-scale-z"
)
? model.querySelector(
- "p.three-portal-block-scale-z"
+ "p.three-portal-block-scale-z"
).innerText
: "";
@@ -1684,7 +1095,7 @@ export default function EnvironmentFront(props) {
"p.three-portal-block-rotation-x"
)
? model.querySelector(
- "p.three-portal-block-rotation-x"
+ "p.three-portal-block-rotation-x"
).innerText
: "";
@@ -1693,7 +1104,7 @@ export default function EnvironmentFront(props) {
"p.three-portal-block-rotation-y"
)
? model.querySelector(
- "p.three-portal-block-rotation-y"
+ "p.three-portal-block-rotation-y"
).innerText
: "";
@@ -1702,7 +1113,7 @@ export default function EnvironmentFront(props) {
"p.three-portal-block-rotation-z"
)
? model.querySelector(
- "p.three-portal-block-rotation-z"
+ "p.three-portal-block-rotation-z"
).innerText
: "";
@@ -1710,7 +1121,7 @@ export default function EnvironmentFront(props) {
"p.three-portal-block-url"
)
? model.querySelector(
- "p.three-portal-block-url"
+ "p.three-portal-block-url"
).innerText
: "";
@@ -1719,7 +1130,7 @@ export default function EnvironmentFront(props) {
"p.three-portal-block-destination-url"
)
? model.querySelector(
- "p.three-portal-block-destination-url"
+ "p.three-portal-block-destination-url"
).innerText
: "";
@@ -1728,7 +1139,7 @@ export default function EnvironmentFront(props) {
"p.three-portal-block-animations"
)
? model.querySelector(
- "p.three-portal-block-animations"
+ "p.three-portal-block-animations"
).innerText
: "";
@@ -1737,7 +1148,7 @@ export default function EnvironmentFront(props) {
"p.three-portal-block-label"
)
? model.querySelector(
- "p.three-portal-block-label"
+ "p.three-portal-block-label"
).innerText
: "";
@@ -1746,7 +1157,7 @@ export default function EnvironmentFront(props) {
"p.three-portal-block-label-offset-x"
)
? model.querySelector(
- "p.three-portal-block-label-offset-x"
+ "p.three-portal-block-label-offset-x"
).innerText
: "";
@@ -1755,7 +1166,7 @@ export default function EnvironmentFront(props) {
"p.three-portal-block-label-offset-y"
)
? model.querySelector(
- "p.three-portal-block-label-offset-y"
+ "p.three-portal-block-label-offset-y"
).innerText
: "";
@@ -1764,7 +1175,7 @@ export default function EnvironmentFront(props) {
"p.three-portal-block-label-offset-z"
)
? model.querySelector(
- "p.three-portal-block-label-offset-z"
+ "p.three-portal-block-label-offset-z"
).innerText
: "";
const labelTextColor =
@@ -1772,7 +1183,7 @@ export default function EnvironmentFront(props) {
"p.three-portal-block-label-text-color"
)
? model.querySelector(
- "p.three-portal-block-label-text-color"
+ "p.three-portal-block-label-text-color"
).innerText
: "";
@@ -1783,6 +1194,8 @@ export default function EnvironmentFront(props) {
destinationUrl={
destinationUrl
}
+ defaultFont={defaultFont}
+ threeObjectPlugin={threeObjectPlugin}
positionX={modelPosX}
positionY={modelPosY}
animations={animations}
@@ -1815,12 +1228,6 @@ export default function EnvironmentFront(props) {
/>
);
})}
- {/*
-
- */}
>
)}
diff --git a/blocks/environment/components/Player.js b/blocks/environment/components/Player.js
index 81fc348..725407f 100644
--- a/blocks/environment/components/Player.js
+++ b/blocks/environment/components/Player.js
@@ -116,7 +116,7 @@ export default function Player(props) {
friction={0}
ref={rigidRef}
mass={0}
- type={"dynamic"}
+ type={"static"}
onCollisionEnter={({ manifold, target }) => {
setRapierId(target.colliderSet.map.data[1]);
setContactPoint(manifold.solverContactPoint(0));
@@ -134,6 +134,7 @@ export default function Player(props) {
point={contactPoint}
something={rigidRef}
spawnPoint={props.spawnPoint}
+ spawnPointsToAdd={props.spawnPointsToAdd}
/>
{
+ const x = Number(spawnPos[0]);
+ const y = Number(spawnPos[1]) + 1.1;
+ const z = Number(spawnPos[2]);
+
+ if (isPresenting) {
+ player.position.x = x
+ player.position.y = y
+ player.position.z = z
+ }
+ }, [isPresenting])
+
+
// Set a variable finding an object in the three.js scene that is named reticle.
useEffect(() => {
+
// Remove the reticle when the controllers are registered.
const reticle = scene.getObjectByName("reticle");
if (controllers.length > 0 && reticle) {
diff --git a/blocks/environment/components/ThreeObjectEdit.js b/blocks/environment/components/ThreeObjectEdit.js
index 5b77d6c..aabcee3 100644
--- a/blocks/environment/components/ThreeObjectEdit.js
+++ b/blocks/environment/components/ThreeObjectEdit.js
@@ -183,7 +183,7 @@ function Spawn(spawn) {
scale={[1, 1, 1]}
rotation={[0, 0, 0]}
>
-
+
{
+ setTimeout(() => set(model.url), 2000);
+ }, []);
+ const [listener] = useState(() => new AudioListener());
+
+ useThree(({ camera }) => {
+ camera.add(listener);
+ });
+
+ const gltf = useLoader(GLTFLoader, url, (loader) => {
+ // const dracoLoader = new DRACOLoader();
+ // dracoLoader.setDecoderPath(
+ // "https://www.gstatic.com/draco/v1/decoders/"
+ // );
+ // loader.setDRACOLoader(dracoLoader);
+
+ loader.register(
+ (parser) => new GLTFAudioEmitterExtension(parser, listener)
+ );
+ if (openbrushEnabled === true) {
+ loader.register(
+ (parser) =>
+ new GLTFGoogleTiltBrushMaterialExtension(
+ parser,
+ openbrushDirectory
+ )
+ );
+ }
+ loader.register((parser) => {
+ return new VRMLoaderPlugin(parser);
+ });
+ });
+
+ const audioObject = gltf.scene.getObjectByProperty('type', 'Audio');
+
+ const { actions } = useAnimations(gltf.animations, gltf.scene);
+ const animationClips = gltf.animations;
+ const animationList = model.animations ? model.animations.split(",") : "";
+ useEffect(() => {
+ if (animationList) {
+ animationList.forEach((name) => {
+ if (Object.keys(actions).includes(name)) {
+ console.log(actions[name].play());
+ }
+ });
+ }
+ }, []);
+
+ const generator = gltf.asset.generator;
+
+ // return tilt brush if tilt brush
+ if (String(generator).includes("Tilt Brush")) {
+ return (
+
+ );
+ }
+
+ if (gltf?.userData?.gltfExtensions?.VRM) {
+ const vrm = gltf.userData.vrm;
+ vrm.scene.position.set(
+ model.positionX,
+ model.positionY,
+ model.positionZ
+ );
+ VRMUtils.rotateVRM0(vrm);
+ const rotationVRM = vrm.scene.rotation.y + parseFloat(0);
+ vrm.scene.rotation.set(0, rotationVRM, 0);
+ vrm.scene.scale.set(1, 1, 1);
+ vrm.scene.scale.set(model.scaleX, model.scaleY, model.scaleZ);
+ return (
+ //
+
+ //
+ );
+ }
+ // gltf.scene.castShadow = true;
+ // enable shadows @todo figure this out
+ // gltf.scene.traverse(function (node) {
+ // if (node.isMesh) {
+ // node.castShadow = true;
+ // node.receiveShadow = true;
+ // }
+ // });
+
+ // @todo figure out how to clone gltf proper with extensions and animations
+ // const copyGltf = useMemo(() => gltf.scene.clone(), [gltf.scene]);
+ // const modelClone = SkeletonUtils.clone(gltf.scene);
+ // modelClone.scene.castShadow = true;
+
+ //audioObject
+ // Add a triangle mesh on top of the video
+ const [triangle] = useState(() => {
+ const points = [];
+ points.push(
+ new Vector3(0, -3, 0),
+ new Vector3(0, 3, 0),
+ new Vector3(4, 0, 0)
+ );
+ const geometry = new BufferGeometry().setFromPoints(points);
+ const material = new MeshBasicMaterial({
+ color: 0x00000,
+ side: DoubleSide
+ });
+ const triangle = new Mesh(geometry, material);
+ return triangle;
+ });
+
+ const [circle] = useState(() => {
+ const geometryCircle = new CircleGeometry(5, 32);
+ const materialCircle = new MeshBasicMaterial({
+ color: 0xfffff,
+ side: DoubleSide
+ });
+ const circle = new Mesh(geometryCircle, materialCircle);
+ return circle;
+ });
+
+ if (model.collidable === "1") {
+ return (
+ <>
+ {
+ setClickEvent(!clicked);
+ if(audioObject){
+ if (clicked) {
+ audioObject.play();
+ triangle.material.visible = false;
+ circle.material.visible = false;
+ } else {
+ audioObject.pause();
+ triangle.material.visible = true;
+ circle.material.visible = true;
+ }
+ }
+ }}
+ // onCollisionEnter={ ( props ) =>(
+ // // window.location.href = model.destinationUrl
+ // )
+ // }
+ >
+
+
+ >
+ );
+ }
+ return (
+ <>
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/blocks/environment/components/core/front/Portal.js b/blocks/environment/components/core/front/Portal.js
new file mode 100644
index 0000000..4c2c283
--- /dev/null
+++ b/blocks/environment/components/core/front/Portal.js
@@ -0,0 +1,244 @@
+import React, { useState, useEffect, useMemo } from "react";
+import { useLoader, useThree } from "@react-three/fiber";
+import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
+import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
+import { AudioListener, Vector3, BufferGeometry, MeshBasicMaterial, DoubleSide, Mesh, CircleGeometry, sRGBEncoding } from "three";
+import { RigidBody } from "@react-three/rapier";
+import {
+ useAnimations,
+ Billboard,
+ Text
+} from "@react-three/drei";
+import { GLTFAudioEmitterExtension } from "three-omi";
+import { GLTFGoogleTiltBrushMaterialExtension } from "three-icosa";
+import { VRMUtils, VRMLoaderPlugin } from "@pixiv/three-vrm";
+
+/**
+ * Parses a Matrix URI and returns a matrix ID.
+ *
+ * @param {string} uri - The Matrix URI to parse.
+ * @return {string} The matrix ID extracted from the URI.
+ *
+ * @throws {Error} If the provided URI is invalid or has an unsupported format.
+ */
+ function parseMatrixUri(uri) {
+ const SegmentToSigil = {
+ u: "@",
+ user: "@",
+ r: "#",
+ room: "#",
+ roomid: "!"
+ };
+
+ const url = new URL(uri, window.location.href);
+
+ if (url.protocol === "matrix:") {
+ const matches = url.pathname.match(/^(\/\/.+\/)?(.+)$/);
+
+ let authority;
+ let path;
+
+ if (matches) {
+ if (matches.length === 3) {
+ authority = matches[1];
+ path = matches[2];
+ } else if (matches.length === 2) {
+ path = matches[1];
+ }
+ }
+
+ if (!path) {
+ throw new Error(`Invalid matrix uri "${uri}": No path provided`);
+ }
+
+ const segments = path.split("/");
+
+ if (segments.length !== 2 && segments.length !== 4) {
+ throw new Error(
+ `Invalid matrix uri "${uri}": Invalid number of segments`
+ );
+ }
+
+ const sigil1 = SegmentToSigil[segments[0]];
+
+ if (!sigil1) {
+ throw new Error(
+ `Invalid matrix uri "${uri}": Invalid segment ${segments[0]}`
+ );
+ }
+
+ if (!segments[1]) {
+ throw new Error(`Invalid matrix uri "${uri}": Empty segment`);
+ }
+
+ const mxid1 = `${sigil1}${segments[1]}`;
+
+ let mxid2;
+
+ if (segments.length === 4) {
+ if (
+ (sigil1 === "!" || sigil1 === "#") &&
+ (segments[2] === "e" || segments[2] === "event") &&
+ segments[3]
+ ) {
+ mxid2 = `$${segments[3]}`;
+ } else {
+ throw new Error(
+ `Invalid matrix uri "${uri}": Invalid segment ${segments[2]}`
+ );
+ }
+ }
+ return { protocol: "matrix:", authority, mxid1, mxid2 };
+ }
+
+ return url;
+}
+
+
+/**
+ * Represents a portal in a virtual reality scene.
+ *
+ * @param {Object} model - The props for the portal.
+ *
+ * @return {JSX.Element} The portal.
+ */
+export function Portal(model) {
+ if (model.object && model.defaultFont) {
+ return (
+ <>
+
+
+ {model.label
+ ? model.label + ": "
+ : "" + model.destinationUrl}
+
+
+ {
+ const url = new URL(
+ model.destinationUrl,
+ window.location.href
+ );
+ if (url.protocol === "matrix:") {
+ const destination = parseMatrixUri(
+ model.destinationUrl
+ );
+ window.location.href =
+ "https://thirdroom.io/world/" +
+ destination.mxid1;
+ } else {
+ window.location.href = model.destinationUrl;
+ }
+ }}
+ >
+
+
+ >
+ );
+ }
+ const [url, set] = useState(model.url);
+
+ useEffect(() => {
+ setTimeout(() => set(model.url), 2000);
+ }, []);
+ const [listener] = useState(() => new AudioListener());
+
+ useThree(({ camera }) => {
+ camera.add(listener);
+ });
+
+ const gltf = useLoader(GLTFLoader, url, (loader) => {
+ loader.register(
+ (parser) => new GLTFAudioEmitterExtension(parser, listener)
+ );
+ loader.register((parser) => {
+ return new VRMLoaderPlugin(parser);
+ });
+ });
+
+ const { actions } = useAnimations(gltf.animations, gltf.scene);
+
+ const animationList = model.animations ? model.animations.split(",") : "";
+ useEffect(() => {
+ if (animationList) {
+ animationList.forEach((name) => {
+ if (Object.keys(actions).includes(name)) {
+ actions[name].play();
+ }
+ });
+ }
+ }, []);
+ // gltf.scene.position.set( model.positionX, model.positionY, model.positionZ );
+ // gltf.scene.rotation.set( 0, 0, 0 );
+ // gltf.scene.scale.set(model.scaleX, model.scaleY, model.scaleZ);
+ // gltf.scene.rotation.set(model.rotationX , model.rotationY, model.rotationZ );
+ const copyGltf = useMemo(() => gltf.scene.clone(), [gltf.scene]);
+
+ return (
+ <>
+
+ (window.location.href = model.destinationUrl)
+ }
+ rotation={[model.rotationX, model.rotationY, model.rotationZ]}
+ position={[model.positionX, model.positionY, model.positionZ]}
+ scale={[model.scaleX, model.scaleY, model.scaleZ]}
+ >
+
+
+ {model.label + ": " + model.destinationUrl}
+
+
+
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/blocks/environment/components/core/front/Sky.js b/blocks/environment/components/core/front/Sky.js
new file mode 100644
index 0000000..af49c56
--- /dev/null
+++ b/blocks/environment/components/core/front/Sky.js
@@ -0,0 +1,30 @@
+import React from "react";
+import { useLoader } from "@react-three/fiber";
+import { TextureLoader, DoubleSide } from "three";
+
+/**
+ * Represents a sky in a virtual reality scene.
+ *
+ * @param {Object} sky - The props for the sky.
+ *
+ * @return {JSX.Element} The sky.
+ */
+export function Sky(sky) {
+ const skyUrl = sky.src[0].querySelector("p.sky-block-url")
+ ? sky.src[0].querySelector("p.sky-block-url").innerText
+ : "";
+
+const texture1 = useLoader(TextureLoader, skyUrl);
+
+return (
+
+
+
+
+);
+}
diff --git a/blocks/environment/components/core/front/TextObject.js b/blocks/environment/components/core/front/TextObject.js
new file mode 100644
index 0000000..0711ab1
--- /dev/null
+++ b/blocks/environment/components/core/front/TextObject.js
@@ -0,0 +1,38 @@
+import React, { useRef } from "react";
+import {
+ Text,
+} from "@react-three/drei";
+
+/**
+ * Represents a text object in a virtual reality scene.
+ *
+ * @param {Object} model - The props for the text object.
+ *
+ * @return {JSX.Element} The text object.
+ */
+export function TextObject(model) {
+ const htmlObj = useRef();
+ return (
+ <>
+
+
+ {model.textContent}
+
+
+ >
+ );
+}
diff --git a/blocks/environment/components/core/front/ThreeImage.js b/blocks/environment/components/core/front/ThreeImage.js
new file mode 100644
index 0000000..f3bd59c
--- /dev/null
+++ b/blocks/environment/components/core/front/ThreeImage.js
@@ -0,0 +1,46 @@
+import React from "react";
+import { useLoader, useThree } from "@react-three/fiber";
+import { TextureLoader, DoubleSide } from "three";
+
+/**
+ * Renders an image in a three.js scene.
+ *
+ * @param {Object} threeImage - The props for the image.
+ *
+ * @return {JSX.Element} The image.
+ */
+export function ThreeImage(threeImage) {
+ const texture2 = useLoader(TextureLoader, threeImage.url);
+ return (
+
+
+ {threeImage.transparent == "1" ? (
+
+ ) : (
+
+ )}
+
+ );
+}
\ No newline at end of file
diff --git a/blocks/environment/components/core/front/ThreeVideo.js b/blocks/environment/components/core/front/ThreeVideo.js
new file mode 100644
index 0000000..39afcb2
--- /dev/null
+++ b/blocks/environment/components/core/front/ThreeVideo.js
@@ -0,0 +1,137 @@
+import React, { useState, useEffect } from "react";
+import { useLoader, useThree } from "@react-three/fiber";
+import { Vector3, BufferGeometry, MeshBasicMaterial, DoubleSide, Mesh, CircleGeometry, sRGBEncoding } from "three";
+import { RigidBody } from "@react-three/rapier";
+
+/**
+ * Renders a video in a three.js scene.
+ *
+ * @param {Object} threeVideo - The props for the video.
+ *
+ * @return {JSX.Element} The video.
+ */
+export function ThreeVideo(threeVideo) {
+ const play = threeVideo.autoPlay === "1" ? true : false;
+ const { scene } = useThree();
+ const [clicked, setClickEvent] = useState();
+ const [video] = useState(() =>
+ Object.assign(document.createElement("video"), {
+ src: threeVideo.url,
+ crossOrigin: "Anonymous",
+ loop: true,
+ muted: true
+ })
+ );
+ // Add a triangle mesh on top of the video
+ const [triangle] = useState(() => {
+ const points = [];
+ points.push(
+ new Vector3(0, -3, 0),
+ new Vector3(0, 3, 0),
+ new Vector3(4, 0, 0)
+ );
+ const geometry = new BufferGeometry().setFromPoints(points);
+ const material = new MeshBasicMaterial({
+ color: 0x00000,
+ side: DoubleSide
+ });
+ const triangle = new Mesh(geometry, material);
+ return triangle;
+ });
+
+ const [circle] = useState(() => {
+ const geometryCircle = new CircleGeometry(5, 32);
+ const materialCircle = new MeshBasicMaterial({
+ color: 0xfffff,
+ side: DoubleSide
+ });
+ const circle = new Mesh(geometryCircle, materialCircle);
+ return circle;
+ });
+
+ useEffect(() => {
+ if (play) {
+ triangle.material.visible = false;
+ circle.material.visible = false;
+ video.play();
+ } else {
+ triangle.material.visible = true;
+ circle.material.visible = true;
+ }
+ }, [video, play]);
+
+ return (
+ // {
+ // if (e.length !== 0) {
+ // setClickEvent(!clicked);
+ // if (clicked) {
+ // video.play();
+ // triangle.material.visible = false;
+ // circle.material.visible = false;
+ // } else {
+ // video.pause();
+ // triangle.material.visible = true;
+ // circle.material.visible = true;
+ // }
+ // }
+ // }}
+ // filter={(items) => items}
+ // >
+
+ {
+ setClickEvent(!clicked);
+ if (clicked) {
+ video.play();
+ triangle.material.visible = false;
+ circle.material.visible = false;
+ } else {
+ video.pause();
+ triangle.material.visible = true;
+ circle.material.visible = true;
+ }
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+ //
+ );
+}
\ No newline at end of file
diff --git a/blocks/three-audio-block/Edit.js b/blocks/three-audio-block/Edit.js
index 50d682a..8739332 100644
--- a/blocks/three-audio-block/Edit.js
+++ b/blocks/three-audio-block/Edit.js
@@ -21,7 +21,6 @@ import { more } from "@wordpress/icons";
export default function Edit({ attributes, setAttributes, isSelected }) {
const onImageSelect = (imageObject) => {
- console.log(imageObject);
setAttributes({ videoUrl: null });
setAttributes({
videoUrl: imageObject.url,
@@ -110,7 +109,6 @@ export default function Edit({ attributes, setAttributes, isSelected }) {
function handleClick(objectURL) {
if (objectURL) {
- console.log("success good job", objectURL);
onImageSelect(objectURL);
}
console.log("fail", objectURL);
diff --git a/blocks/three-image-block/Edit.js b/blocks/three-image-block/Edit.js
index e0635a3..bd7b947 100644
--- a/blocks/three-image-block/Edit.js
+++ b/blocks/three-image-block/Edit.js
@@ -21,7 +21,6 @@ import { more } from "@wordpress/icons";
export default function Edit({ attributes, setAttributes, isSelected }) {
const onImageSelect = (imageObject) => {
- console.log(imageObject);
setAttributes({ imageUrl: null });
setAttributes({
imageUrl: imageObject.url,
@@ -63,8 +62,8 @@ export default function Edit({ attributes, setAttributes, isSelected }) {
setAttributes({ collidable: setting });
};
- const onChangeTransparent = (transparent) => {
- setAttributes({ transparent });
+ const onChangeTransparent = (transparentSetting) => {
+ setAttributes({ transparent: transparentSetting });
};
const { mediaUpload } = wp.editor;
@@ -93,7 +92,6 @@ export default function Edit({ attributes, setAttributes, isSelected }) {
function handleClick(objectURL) {
if (objectURL) {
- console.log("success good job", objectURL);
onImageSelect(objectURL);
}
console.log("fail", objectURL);
diff --git a/blocks/three-image-block/Save.js b/blocks/three-image-block/Save.js
index 3f75665..776c9f9 100644
--- a/blocks/three-image-block/Save.js
+++ b/blocks/three-image-block/Save.js
@@ -35,7 +35,7 @@ export default function save({ attributes }) {
{attributes.aspectWidth}
- {attributes.transparent}
+ {attributes.transparent ? 1 : 0}
>
diff --git a/blocks/three-object-block/Edit.js b/blocks/three-object-block/Edit.js
index 004aa28..2d0116f 100644
--- a/blocks/three-object-block/Edit.js
+++ b/blocks/three-object-block/Edit.js
@@ -93,7 +93,6 @@ export default function Edit( { attributes, setAttributes, isSelected } ) {
function handleClick(objectURL){
if(objectURL){
- console.log("success good job", objectURL);
onImageSelect(objectURL);
}
console.log("fail", objectURL);
diff --git a/blocks/three-portal-block/Edit.js b/blocks/three-portal-block/Edit.js
index 12a0dfa..3c0f8dc 100644
--- a/blocks/three-portal-block/Edit.js
+++ b/blocks/three-portal-block/Edit.js
@@ -121,7 +121,6 @@ export default function Edit({ attributes, setAttributes, isSelected }) {
function handleClick(objectURL) {
if (objectURL) {
- console.log("success good job", objectURL);
onImageSelect(objectURL);
}
console.log("fail", objectURL);
diff --git a/blocks/three-text-block/Edit.js b/blocks/three-text-block/Edit.js
index c4701f1..9031b20 100644
--- a/blocks/three-text-block/Edit.js
+++ b/blocks/three-text-block/Edit.js
@@ -106,7 +106,6 @@ export default function Edit({ attributes, setAttributes, isSelected }) {
function handleClick(objectURL) {
if (objectURL) {
- console.log("success good job", objectURL);
onImageSelect(objectURL);
}
console.log("fail", objectURL);
diff --git a/blocks/three-video-block/Edit.js b/blocks/three-video-block/Edit.js
index 319fe5c..4d4ff83 100644
--- a/blocks/three-video-block/Edit.js
+++ b/blocks/three-video-block/Edit.js
@@ -21,7 +21,6 @@ import { more } from "@wordpress/icons";
export default function Edit({ attributes, setAttributes, isSelected }) {
const onImageSelect = (imageObject) => {
- console.log(imageObject);
setAttributes({ videoUrl: null });
setAttributes({
videoUrl: imageObject.url,
@@ -89,7 +88,6 @@ export default function Edit({ attributes, setAttributes, isSelected }) {
function handleClick(objectURL) {
if (objectURL) {
- console.log("success good job", objectURL);
onImageSelect(objectURL);
}
console.log("fail", objectURL);
diff --git a/readme.txt b/readme.txt
index ba15c51..3e27596 100644
--- a/readme.txt
+++ b/readme.txt
@@ -2,7 +2,7 @@
Requires at least: 5.7
Tested up to: 6.1
Requires PHP: 7.2
-Stable tag: 1.0.8
+Stable tag: 1.0.9
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Author: antpb
@@ -46,6 +46,10 @@ It can also be installed manually using a zip file.
== Changelog ==
+= 1.0.9 =
+Fixed: Spawn - Spawn block now properly loads a player in the spawn location.
+Added: Initial support for OMI_spawn_point.
+
= 1.0.8 =
Added: Click event for audio objects that contain KHR_audio. Improvements to come. To set a KHR_audio object to be interactable simply make the object collibable.
Added: Video - Interactions to play/pause a video. A paused video will now show a play icon to resume the video source. Video audio to come soon!
diff --git a/three-object-viewer.php b/three-object-viewer.php
index ee4b38c..f45912c 100644
--- a/three-object-viewer.php
+++ b/three-object-viewer.php
@@ -3,7 +3,7 @@
* Plugin Name: Three Object Viewer
* Plugin URI: https://3ov.xyz/
* Description: A plugin for viewing 3D files with support for WebXR and Open Metaverse Interoperability GLTF Extensions.
-* Version: 1.0.8
+* Version: 1.0.9
* Requires at least: 5.7
* Requires PHP: 7.1.0
* Author: antpb