diff --git a/.gitignore b/.gitignore index d297c64..9953b72 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ vendor wordpress build pro/ +plugin-build/free/*.zip .phpunit.result.cache plugin-build/pro/three-object-viewer/* !plugin-build/pro/three-object-viewer/.gitkeep diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b35c5af --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,49 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": "Listen for Xdebug", + "type": "php", + "request": "launch", + "port": 9003 + }, + { + "name": "Launch currently open script", + "type": "php", + "request": "launch", + "program": "${file}", + "cwd": "${fileDirname}", + "port": 0, + "runtimeArgs": [ + "-dxdebug.start_with_request=yes" + ], + "env": { + "XDEBUG_MODE": "debug,develop", + "XDEBUG_CONFIG": "client_port=${port}" + } + }, + { + "name": "Launch Built-in web server", + "type": "php", + "request": "launch", + "runtimeArgs": [ + "-dxdebug.mode=debug", + "-dxdebug.start_with_request=yes", + "-S", + "localhost:0" + ], + "program": "", + "cwd": "${workspaceRoot}", + "port": 9003, + "serverReadyAction": { + "pattern": "Development Server \\(http://localhost:([0-9]+)\\) started", + "uriFormat": "http://localhost:%s", + "action": "openExternally" + } + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 1f17729..70709a0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,8 @@ { "editor.defaultFormatter": "rvest.vs-code-prettier-eslint", "editor.codeActionsOnSave": { - "source.fixAll": true - }, + "source.fixAll": "explicit" +}, "[javascript]": { "editor.defaultFormatter": "vscode.typescript-language-features" }, diff --git a/admin/three-object-viewer-settings/App.js b/admin/three-object-viewer-settings/App.js index e65ad3c..881087d 100644 --- a/admin/three-object-viewer-settings/App.js +++ b/admin/three-object-viewer-settings/App.js @@ -1,270 +1,397 @@ -import { Suspense, useState, useEffect } from "@wordpress/element"; -// import i18n from "@wordpress/i18n"; -import { __ } from "@wordpress/i18n"; - -//Main component for admin page app +import { useState, useEffect } from "@wordpress/element"; +import { __, sprintf } from "@wordpress/i18n"; +import { Button, Spinner, ExternalLink } from "@wordpress/components"; + export default function App({ getSettings, updateSettings }) { + const [settings, setSettings] = useState({}); + const [isLoading, setIsLoading] = useState(true); + const [isSaving, setIsSaving] = useState(false); + const [isOpenApiKeyVisible, setIsOpenApiKeyVisible] = useState(false); + const [mediaFrame, setMediaFrame] = useState(null); - let frame + useEffect(() => { + getSettings().then((response) => { + setSettings(response); + setIsLoading(false); + }); + }, [getSettings]); - //Track settings state - const [settings, setSettings] = useState({}); - //Use to show loading spinner - const [isLoading, setIsLoading] = useState(true); - const [isOpenApiKeyVisible, setIsOpenApiKeyVisible] = useState(false); - const [saveIndicator, setSaveIndicator] = useState(false); + const onSave = async (event) => { + event.preventDefault(); + setIsSaving(true); + await updateSettings(settings); + setIsSaving(false); + }; - const [defaultVRM, setDefaultVRM] = useState(); + const openMediaUploader = (settingKey) => { + if (mediaFrame) { + mediaFrame.open(); + return; + } - //When app loads, get settings - useEffect(() => { - getSettings().then((r) => { - setSettings(r); - setIsLoading(false); - }); - }, [getSettings, setSettings]); + const frame = wp.media({ + title: __("Select or Upload Media", "three-object-viewer"), + button: { + text: __("Use this media", "three-object-viewer"), + }, + multiple: false, + }); - //Function to update settings via API - const onSave = async (event) => { - event.preventDefault(); - setSaveIndicator(true); // Show save indicator - let response = await updateSettings(settings); - setSettings(response); + frame.on("select", () => { + const attachment = frame.state().get("selection").first().toJSON(); + setSettings({ ...settings, [settingKey]: attachment.url }); + }); - setTimeout(() => { - setSaveIndicator(false); - }, 1500); - }; - - const runUploaderAnimation = (event) => { - event.preventDefault() - - // If the media frame already exists, reopen it. - if (frame) { - frame.open() - return - } - - // Create a new media frame - frame = wp.media({ - title: __( 'Select or Upload Media', 'three-object-viewer' ), - button: { - text: __( 'Use this media', 'three-object-viewer' ), - }, - multiple: false, - }) - frame.on( 'select', function() { - - // Get media attachment details from the frame state - var attachment = frame.state().get('selection').first().toJSON(); - setSettings({ ...settings, defaultVRM: attachment.url }); - // Send the attachment URL to our custom image input field. - }); - - - // Finally, open the modal on click - frame.open() - } + setMediaFrame(frame); + frame.open(); + }; - const runUploaderDefaultAvatar = (event) => { - event.preventDefault() - - // If the media frame already exists, reopen it. - if (frame) { - frame.open() - return - } - - // Create a new media frame - frame = wp.media({ - title: __( 'Select or Upload Media', 'three-object-viewer' ), - button: { - text: __( 'Use this media', 'three-object-viewer' ), - }, - multiple: false, - }) - frame.on( 'select', function() { - - // Get media attachment details from the frame state - var attachment = frame.state().get('selection').first().toJSON(); - setSettings({ ...settings, defaultAvatar: attachment.url }); - }); - - - // Finally, open the modal on click - frame.open() - } + const clearMedia = (settingKey) => { + setSettings({ ...settings, [settingKey]: "" }); + }; - //Show a spinner if loading - if (isLoading) { - return
; - } - const clearDefaultAnimation = () => { - setSettings({ ...settings, defaultVRM: "" }); - } + if (isLoading) { + return ; + } - const clearDefaultAvatar = () => { - setSettings({ ...settings, defaultAvatar: "" }); - } - - //Show settings if not loading - return ( - <> -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + return ( +
+

{__("3OV Settings", "three-object-viewer")}

+

+ {__( + "Here you can manage the settings for 3OV to tweak global configuration options and save your API keys for connected services.", + "three-object-viewer" + )} +

+ + +
-

{ __( '3OV Settings', 'three-object-viewer') }

-

{ __( 'Here you can manage the settings for 3OV to tweak global configuration options and save your API keys for connected serivces.', 'three-object-viewer' ) }

-

{ __( 'Avatar Settings', 'three-object-viewer' ) }

- -
- { settings.defaultVRM ? settings.defaultVRM : __( "No custom default animation set", 'three-object-viewer' ) } -
- -
- -
- -

View our Avatar Resource Page for some 3OV compatible avatars.

-
- { settings.defaultAvatar ? settings.defaultAvatar : __( "No custom default avatar set", 'three-object-viewer' ) } -
- -
+ + {/* + + + */} + + + + + + + + + + + + + + + + {settings.multiplayerWorker && ( + <> + + + + + + - - - - - - - - + + + + - - + + + + - - - - - {/* Select element with three options for AI type public, or logged in */} - + + + + - - - - - -
+ + + setSettings({ ...settings, threeovApiKey: event.target.value })} + className="regular-text" + /> +

+ { + + {__("Sign up for Pro", "three-object-viewer")} + + } +

+
+ + + setSettings({ ...settings, toyboxApiKey: event.target.value })} + className="regular-text" + /> +

+ { + <> + + {__("Sign up for Toybox Storage", "three-object-viewer")} + + + } +

+
{__("Default Animation", "three-object-viewer")} + + + {settings.defaultVRM && ( + + )} +
{__("Default Avatar", "three-object-viewer")} + +

+ { + {__("Compatible Avatars", "three-object-viewer")} + } +

+ + {settings.defaultAvatar && ( + + )} +
+ + + setSettings({ ...settings, multiplayerWorker: event.target.value })} + className="regular-text" + /> +

+ {__( + "Use https://p2pcf.sxp.digital/ or host your own CloudFlare Worker using ", + "three-object-viewer" + )} + + p2pcf + + {__( + ". A tutorial for setting up your own worker can be found ", + "three-object-viewer" + )} + + {__("here", "three-object-viewer")} + . +

+
{__("TURN Settings", "three-object-viewer")} - +

+ {__( + "These settings are used to configure the TURN server for WebRTC connections. You can use the public TURN server or host your own. The public TURN server is hosted at ", + "three-object-viewer" + )} + turn.sxp.digital + {__(" but is limited.", "three-object-viewer")} +

+

+ {__( + "A TURN server is used to relay WebRTC connections when a direct connection cannot be established. This is common when two peers are behind different NATs or firewalls.", + "three-object-viewer" + )} +

{__('AI Settings', 'three-object-viewer' ) }

{ __( 'NPC Settings', 'three-object-viewer' ) }
+ + - { - setSettings({ ...settings, enabled: event.target.checked }); - }} + type="text" + id="turnCredentialRelay" + value={settings.turnCredentialRelay || ""} + onChange={(event) => setSettings({ ...settings, turnCredentialRelay: event.target.value })} + className="regular-text" /> +

+ {__( + "A CloudFlare Worker is used to relay TURN credentials. You can use the public worker at ", + "three-object-viewer" + )} + https://turn.sxp.digital/ + {__( + " or host your own. The public worker is resource limited and should only be used for testing. You can bypass these limits using your API key below or by hosting your own CloudFlare Worker to run the exact same credential handling. A tutorial for setting up your own worker can be found ", + "three-object-viewer" + )} + + {__("here", "three-object-viewer")} + . +

+ + - { - setSettings({ ...settings, networkWorker: event.target.value }); - }} + type={isOpenApiKeyVisible ? "text" : "password"} + id="turnServerKey" + value={settings.turnServerKey || ""} + onChange={(event) => setSettings({ ...settings, turnServerKey: event.target.value })} + className="regular-text" /> -
- - {isOpenApiKeyVisible ? ( - { - setSettings({ ...settings, openApiKey: event.target.value }); - }} - /> - ) : ( - { - setSettings({ ...settings, openApiKey: event.target.value }); - }} - /> + +

+ {__( + "This secret key will bypass limitations of the 3OV public TURN worker. You can use ", + "three-object-viewer" + )} + + metered.ca + + {__( + " to establish a secret key. More info can be found ", + "three-object-viewer" )} - + + {__("here", "three-object-viewer")} + . +

+ + -
- - {saveIndicator && {__('Saving...', 'three-object-viewer' ) }} -
-
- - ); -} + + + )} + + {__("NPC Settings", "three-object-viewer")} + + + + + + + + + + + setSettings({ ...settings, networkWorker: event.target.value })} + className="regular-text" + /> + + + + + + + + + setSettings({ ...settings, openApiKey: event.target.value })} + className="regular-text" + /> + + + + + + + + + + + + + + + +

+ +

+ +
+ ); +} \ No newline at end of file diff --git a/admin/three-object-viewer-settings/init.php b/admin/three-object-viewer-settings/init.php index 04a8625..f74b3e0 100644 --- a/admin/three-object-viewer-settings/init.php +++ b/admin/three-object-viewer-settings/init.php @@ -8,18 +8,18 @@ $assets = include dirname(__FILE__, 3). "/build/admin-page-$handle.asset.php"; $dependencies = $assets['dependencies']; - wp_register_script( - $handle, - plugins_url("/build/admin-page-$handle.js", dirname(__FILE__, 2)), - $dependencies, - $assets['version'] - ); - + wp_register_script( + $handle, + plugins_url("/build/admin-page-$handle.js", dirname(__FILE__, 2)), + $dependencies, + $assets['version'], + true + ); + $three_object_plugin = plugins_url() . '/three-object-viewer/build/'; $three_object_plugin_root = plugins_url() . '/three-object-viewer/'; - wp_localize_script( $handle, 'threeObjectPlugin', $three_object_plugin ); - wp_localize_script( $handle, 'threeObjectPluginRoot', $three_object_plugin_root ); - if ( function_exists( 'wp_set_script_translations' ) ) { + wp_localize_script( $handle, 'threeObjectPlugin', (array) $three_object_plugin ); + wp_localize_script( $handle, 'threeObjectPluginRoot', (array) $three_object_plugin_root ); if ( function_exists( 'wp_set_script_translations' ) ) { $path = plugin_dir_path( __FILE__ ) . 'languages'; $language_directory = plugin_dir_path( dirname(__DIR__) ) . 'languages/'; wp_set_script_translations( 'three-object-viewer-settings', 'three-object-viewer', $language_directory ); @@ -38,10 +38,16 @@ return rest_ensure_response( [ 'enabled' => get_option( '3ov_ai_enabled', false ), 'networkWorker' => get_option( '3ov_mp_networkWorker', '' ), + 'multiplayerWorker' => get_option( '3ov_mp_multiplayerWorker', '' ), + 'turnCredentialRelay' => get_option( '3ov_mp_turnCredentialRelay', '' ), + 'turnServerKey' => get_option( '3ov_mp_turnServerKey', '' ), + 'multiplayerAccess' => get_option( '3ov_mp_multiplayerAccess', '' ), 'openApiKey' => three_decrypt ( get_option( '3ov_ai_openApiKey', '' ) ), 'allowPublicAI' => get_option( '3ov_ai_allow', '' ), 'defaultVRM' => get_option( '3ov_defaultVRM', '' ), 'defaultAvatar' => get_option( '3ov_defaultAvatar', '' ), + 'threeovApiKey' => get_option( '3ov_proApiKey', '' ), + 'toyboxApiKey' => get_option( '3ov_toyboxApiKey', '' ), ], 200); }, 'permission_callback' => function(){ @@ -55,10 +61,17 @@ $data = $request->get_json_params(); update_option( '3ov_ai_enabled', $data['enabled'] ); update_option( '3ov_mp_networkWorker', $data['networkWorker'] ); + update_option( '3ov_mp_multiplayerWorker', $data['multiplayerWorker'] ); + update_option( '3ov_mp_turnCredentialRelay', $data['turnCredentialRelay'] ); + update_option( '3ov_mp_turnServerUser', $data['turnServerUser'] ); + update_option( '3ov_mp_turnServerKey', $data['turnServerKey'] ); + update_option( '3ov_mp_multiplayerAccess', $data['multiplayerAccess'] ); update_option( '3ov_defaultVRM', $data['defaultVRM'] ); update_option( '3ov_defaultAvatar', $data['defaultAvatar'] ); update_option( '3ov_ai_allow', $data['allowPublicAI'] ); update_option( '3ov_ai_openApiKey', three_encrypt( $data['openApiKey'] ) ); + update_option( '3ov_proApiKey', $data['threeovApiKey'] ); + update_option( '3ov_toyboxApiKey', $data['toyboxApiKey'] ); return rest_ensure_response( $data, 200); }, 'permission_callback' => function(){ diff --git a/babel.config.js b/babel.config.js index 94fb67d..5f7c089 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,6 +1,6 @@ module.exports = function (api) { api.cache(true); const presets = ["@babel/preset-env", "@babel/preset-react"]; - const plugins = ["@babel/plugin-proposal-optional-chaining"]; + const plugins = ["@babel/plugin-proposal-nullish-coalescing-operator", "@babel/plugin-proposal-optional-chaining", "@babel/plugin-proposal-class-properties"]; return { presets, plugins }; }; diff --git a/blocks/environment/Deprecated.js b/blocks/environment/Deprecated.js new file mode 100644 index 0000000..bfaf47e --- /dev/null +++ b/blocks/environment/Deprecated.js @@ -0,0 +1,348 @@ +import { useBlockProps } from "@wordpress/block-editor"; + +export default function Deprecated(){ + return [ + { + attributes: { + bg_color: { + type: "string", + default: "#FFFFFF" + }, + zoom: { + type: "integer", + default: 90 + }, + scale: { + type: "integer", + default: 1 + }, + positionX: { + type: "integer", + default: 0 + }, + positionY: { + type: "integer", + default: 0 + }, + rotationY: { + type: "integer", + default: 0 + }, + threeObjectUrl: { + type: "string", + default: null + }, + hasZoom: { + type: "bool", + default: false + }, + hasTip: { + type: "bool", + default: true + }, + deviceTarget: { + type: "string", + default: "2d" + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.deviceTarget} +

+

+ {props.attributes.threeObjectUrl} +

+

+ {props.attributes.scale} +

+

+ {props.attributes.bg_color} +

+

+ {props.attributes.zoom} +

+

+ {props.attributes.hasZoom ? 1 : 0} +

+

+ {props.attributes.hasTip ? 1 : 0} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.scale} +

+
+ +
+ ); + } + }, + { + attributes: { + bg_color: { + type: "string", + default: "#FFFFFF" + }, + zoom: { + type: "integer", + default: 90 + }, + scale: { + type: "integer", + default: 1 + }, + positionX: { + type: "integer", + default: 0 + }, + positionY: { + type: "integer", + default: 0 + }, + rotationY: { + type: "integer", + default: 0 + }, + threeObjectUrl: { + type: "string", + default: null + }, + hasZoom: { + type: "bool", + default: false + }, + hasTip: { + type: "bool", + default: true + }, + deviceTarget: { + type: "string", + default: "2d" + }, + animations: { + type: "string", + default: "" + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.deviceTarget} +

+

+ {props.attributes.threeObjectUrl} +

+

+ {props.attributes.scale} +

+

+ {props.attributes.bg_color} +

+

+ {props.attributes.zoom} +

+

+ {props.attributes.hasZoom ? 1 : 0} +

+

+ {props.attributes.hasTip ? 1 : 0} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.scale} +

+

+ {props.attributes.animations} +

+
+ +
+ ); + } + }, + { + attributes: { + align: { + type: "string", + default: "full" + }, + scale: { + type: "integer", + default: 1 + }, + positionX: { + type: "integer", + default: 0 + }, + positionY: { + type: "integer", + default: 0 + }, + rotationY: { + type: "integer", + default: 0 + }, + threeObjectUrl: { + type: "string", + default: null + }, + threePreviewImage: { + type: "string", + default: null + }, + deviceTarget: { + type: "string", + default: "vr" + }, + animations: { + type: "string", + default: "" + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.deviceTarget} +

+

+ {props.attributes.threeObjectUrl} +

+

{props.attributes.scale}

+

+ {props.attributes.bg_color} +

+

{props.attributes.zoom}

+

+ {props.attributes.hasZoom ? 1 : 0} +

+

+ {props.attributes.hasTip ? 1 : 0} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.rotationY} +

+

{props.attributes.scale}

+

+ {props.attributes.threePreviewImage} +

+

+ {props.attributes.animations} +

+
+ +
+ ); + } + }, + { + attributes: { + align: { + type: "string", + default: "full" + }, + scale: { + type: "integer", + default: 1 + }, + positionX: { + type: "integer", + default: 0 + }, + positionY: { + type: "integer", + default: 0 + }, + rotationY: { + type: "integer", + default: 0 + }, + threeObjectUrl: { + type: "string", + default: null + }, + threePreviewImage: { + type: "string", + default: null + }, + hdr: { + type: "string", + default: null + }, + deviceTarget: { + type: "string", + default: "vr" + }, + animations: { + type: "string", + default: "" + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.deviceTarget} +

+

+ {props.attributes.threeObjectUrl} +

+

+ {props.attributes.hdr} +

+

{props.attributes.scale}

+

+ {props.attributes.bg_color} +

+

{props.attributes.zoom}

+

+ {props.attributes.hasZoom ? 1 : 0} +

+

+ {props.attributes.hasTip ? 1 : 0} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.rotationY} +

+

{props.attributes.scale}

+

+ {props.attributes.threePreviewImage} +

+

+ {props.attributes.animations} +

+
+ +
+ ); + } + } + ]; +} \ No newline at end of file diff --git a/blocks/environment/Edit.js b/blocks/environment/Edit.js index 091a4fb..c11128c 100644 --- a/blocks/environment/Edit.js +++ b/blocks/environment/Edit.js @@ -8,6 +8,9 @@ import { MediaUpload, InnerBlocks } from "@wordpress/block-editor"; +import { useDispatch } from '@wordpress/data'; +import { createBlock } from '@wordpress/blocks'; + import { Panel, PanelBody, @@ -24,11 +27,11 @@ import defaultEnvironment from "../../inc/assets/default_grid.glb"; import ThreeObjectEdit from "./components/ThreeObjectEdit"; import { EditorPluginProvider, useEditorPlugins, EditorPluginContext } from './components/EditorPluginProvider'; // Import the PluginProvider -export default function Edit({ attributes, setAttributes, isSelected }) { +export default function Edit({ attributes, setAttributes, isSelected, clientId }) { const ALLOWED_BLOCKS = allowed_blocks; const [focusPosition, setFocusPosition] = useState(new THREE.Vector3()); const [focusPoint, setFocus] = useState(new THREE.Vector3()); - const [mainModel, setMainModel] = useState(attributes.threeObjectUrl ? attributes.threeObjectUrl : (threeObjectPlugin + defaultEnvironment)); + const [mainModel, setMainModel] = useState(attributes.threeObjectUrl ? attributes.threeObjectUrl : (defaultEnvironment)); const changeFocusPoint = (newValue) => { setFocusPosition(newValue); } @@ -36,7 +39,7 @@ export default function Edit({ attributes, setAttributes, isSelected }) { // useEffect to initialize the value of the threeObjectUrl attribute if it is not set useEffect(() => { if (!attributes.threeObjectUrl) { - setAttributes({ threeObjectUrl: (threeObjectPlugin + defaultEnvironment) }); + setAttributes({ threeObjectUrl: (defaultEnvironment) }); } }, []); const removeHDR = (imageObject) => { @@ -80,6 +83,11 @@ export default function Edit({ attributes, setAttributes, isSelected }) { setAttributes({ deviceTarget: target }); }; + const setCamCollisions = (collisions) => { + setAttributes({ camCollisions: collisions }); + }; + + const [enteredURL, setEnteredURL] = useState(""); const { mediaUpload } = wp.editor; @@ -116,6 +124,83 @@ export default function Edit({ attributes, setAttributes, isSelected }) { ); }; + // "name": "three-object-viewer/model-block", + // "attributes": { + // "scaleX": { + // "type": "int", + // "default":1 + // }, + // "name": { + // "type": "string" + // }, + // "scaleY": { + // "type": "int", + // "default":1 + // }, + // "scaleZ": { + // "type": "int", + // "default":1 + // }, + // "positionX": { + // "type": "int", + // "default":0 + // }, + // "positionY": { + // "type": "int", + // "default":0 + // }, + // "positionZ": { + // "type": "int", + // "default":0 + // }, + // "rotationX": { + // "type": "int", + // "default":0 + // }, + // "rotationY": { + // "type": "int", + // "default":0 + // }, + // "rotationZ": { + // "type": "int", + // "default":0 + // }, + // "threeObjectUrl": { + // "type": "string", + // "default": null + // }, + // "animations": { + // "type": "string", + // "default": "" + // }, + // "alt": { + // "type": "string", + // "default": "" + // }, + // "collidable": { + // "type": "boolean", + // "default": false + // } + // }, + // "category": "spatial", + // "parent": [ "three-object-viewer/environment" ], + const { insertBlock } = useDispatch('core/block-editor'); + + const handleDrop = (e) => { + e.dataTransfer.dropEffect = 'copy'; + const fileUrl = e.dataTransfer.getData('text'); + console.log('event', fileUrl); + e.preventDefault(); + e.stopPropagation(); + + // Create a new block based on the dropped URL + const newBlock = createBlock('three-object-viewer/model-block', { + threeObjectUrl: fileUrl, + }); + + // Insert the new block as an inner block + insertBlock(newBlock, undefined, clientId); + }; return (
@@ -152,25 +237,32 @@ export default function Edit({ attributes, setAttributes, isSelected }) { )} /> + + + {__( "Select an image to be used as the preview image:", "three-object-viewer" )} - - Preview - + {attributes.threePreviewImage && ( + + Preview + + )} )} + + + {__( "Use an .hdr to give your scene a HDR image to use as the environment. This influences lighting and reflections.", "three-object-viewer" )} + + @@ -227,7 +324,7 @@ export default function Edit({ attributes, setAttributes, isSelected }) { initialOpen={true} > - { __( "Object Display Type:", "three-object-viewer" ) } + { __( "Device Target Type:", "three-object-viewer" ) } setDeviceTarget(target)} /> + + { __( "Camera Collisions:", "three-object-viewer" ) } + + + { + setCamCollisions(e); + }} + /> + <>
+ + - <> -
-

- {attributes.deviceTarget} -

-

- {attributes.threeObjectUrl} -

-

- {attributes.hdr} -

-

{attributes.scale}

-

- {attributes.bg_color} -

-

{attributes.zoom}

-

- {attributes.hasZoom ? 1 : 0} -

-

- {attributes.hasTip ? 1 : 0} -

-

- {attributes.positionY} -

-

- {attributes.rotationY} -

-

{attributes.scale}

-

- {attributes.threePreviewImage} -

-

- {attributes.animations} -

- -
- -
+ + + ); } diff --git a/blocks/environment/block.json b/blocks/environment/block.json index 4ddf219..f0596a5 100644 --- a/blocks/environment/block.json +++ b/blocks/environment/block.json @@ -1,57 +1,61 @@ { - "name": "three-object-viewer/environment", - "attributes": { - "align": { - "type": "string", - "default": "full" - }, - "scale": { - "type": "integer", - "default": 1 - }, - "positionX": { - "type": "integer", - "default": 0 - }, - "positionY": { - "type": "integer", - "default": 0 - }, - "rotationY": { - "type": "integer", - "default": 0 - }, - "threeObjectUrl": { - "type": "string", - "default": null - }, - "threePreviewImage": { - "type": "string", - "default": null - }, - "hdr": { - "type": "string", - "default": null - }, - "deviceTarget": { - "type": "string", - "default": "vr" - }, - "animations": { - "type": "string", - "default": "" - } - }, - "category": "design", - "apiVersion": 2, - "supports": { - "html": false, - "multiple": false, + "name": "three-object-viewer/environment", + "attributes": { + "align": { + "type": "string", + "default": "full" + }, + "scale": { + "type": "integer", + "default": 1 + }, + "positionX": { + "type": "integer", + "default": 0 + }, + "positionY": { + "type": "integer", + "default": 0 + }, + "rotationY": { + "type": "integer", + "default": 0 + }, + "threeObjectUrl": { + "type": "string", + "default": null + }, + "threePreviewImage": { + "type": "string", + "default": null + }, + "hdr": { + "type": "string", + "default": null + }, + "deviceTarget": { + "type": "string", + "default": "vr" + }, + "animations": { + "type": "string", + "default": "" + }, + "camCollisions": { + "type": "boolean", + "default": true + } + }, + "category": "spatial", + "apiVersion": 2, + "supports": { + "html": false, + "multiple": false, "hasOverlay": false, - "align": ["full"] - }, - "textdomain": "three-object-viewer", - "editorScript": "file:../../build/block-environment.js", + "align": ["full"] + }, + "textdomain": "three-object-viewer", + "editorScript": "file:../../build/block-environment.js", "editorStyle": "file:../../build/block-environment.css", "style": "file:../../build/block-environment.css" } diff --git a/blocks/environment/components/ContextBridgeComponent.js b/blocks/environment/components/ContextBridgeComponent.js index 7e0c08f..bfc8d1c 100644 --- a/blocks/environment/components/ContextBridgeComponent.js +++ b/blocks/environment/components/ContextBridgeComponent.js @@ -5,7 +5,7 @@ import { useContextBridge } from "@react-three/drei"; //import contextBridgef // add function for context export function ContextBridgeComponent(props) { - const { plugins } = useFrontPlugins(); // From your own context + const { plugins } = useFrontPlugins(); const [registeredThreeovBlocks, setRegisteredThreeovBlocks] = useState([]); const ContextBridge = useContextBridge(FrontPluginContext); diff --git a/blocks/environment/components/Controls.js b/blocks/environment/components/Controls.js index fc71bfe..e219a86 100644 --- a/blocks/environment/components/Controls.js +++ b/blocks/environment/components/Controls.js @@ -1,55 +1,77 @@ import { useEffect, useRef } from 'react'; +import { useJoystickControls } from "ecctrl"; +import { useFrame } from '@react-three/fiber'; export function useKeyboardControls() { - const movement = useRef({ - forward: false, - backward: false, - left: false, - right: false, - shift: false, - space: false - }); - - useEffect(() => { - const handleKeyDown = (e) => { - let element = e.target; - // if the element is an input, dont move - if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') return; - if (e.key === 'w' || e.key === 'W' && ! movement.current.forward) movement.current.forward = true; - else if (e.key === 's' || e.key === 'S' && ! movement.current.backward) movement.current.backward = true; - else if (e.key === 'a' || e.key === 'A' && ! movement.current.left) movement.current.left = true; - else if (e.key === 'd' || e.key === 'D' && ! movement.current.right) movement.current.right = true; - else if (e.key === 'space') movement.current.space = true; - else if (e.key === 'Shift') movement.current.shift = true; - else if (e.key === 'r' || e.key === 'R'){ - if (e.metaKey || e.ctrlKey){ - movement.current.respawn = false; - } else { - movement.current.respawn = true; - } - - } - - } - - const handleKeyUp = (e) => { - if (e.key === 'w' || e.key === 'W') movement.current.forward = false; - else if (e.key === 's' || e.key === 'S') movement.current.backward = false; - else if (e.key === 'a' || e.key === 'A') movement.current.left = false; - else if (e.key === 'd' || e.key === 'D') movement.current.right = false; - else if (e.key === 'space') movement.current.space = false; - else if (e.key === 'Shift') movement.current.shift = false; - else if (e.key === 'r' || e.key === 'R') movement.current.respawn = false; - } - - window.addEventListener('keydown', handleKeyDown); - window.addEventListener('keyup', handleKeyUp); - - return () => { - window.removeEventListener('keydown', handleKeyDown); - window.removeEventListener('keyup', handleKeyUp); - } - }, []); - - return movement; + +const movement = useRef({ + forward: false, + backward: false, + left: false, + right: false, + shift: false, + space: false, + mouseDown: false +}); +const spacebarDebounceTime = 100; // Adjust this value as needed +let lastSpacebarTime = 0; + +useEffect(() => { + const handleKeyDown = (e) => { + let element = e.target; + // if the element is an input, dont move + if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') return; + if (e.key === 'w' || e.key === 'W' && ! movement.current.forward) movement.current.forward = true; + else if (e.key === 's' || e.key === 'S' && ! movement.current.backward) movement.current.backward = true; + else if (e.key === 'a' || e.key === 'A' && ! movement.current.left) movement.current.left = true; + else if (e.key === 'd' || e.key === 'D' && ! movement.current.right) movement.current.right = true; + else if (e.code === 'Space') { + const currentTime = Date.now(); + if (currentTime - lastSpacebarTime > spacebarDebounceTime) { + movement.current.space = true; + lastSpacebarTime = currentTime; + } + } + else if (e.key === 'Shift') movement.current.shift = true; + else if (e.key === 'r' || e.key === 'R'){ + if (e.metaKey || e.ctrlKey){ + movement.current.respawn = false; + } else { + movement.current.respawn = true; + } + + } + } + + const handleKeyUp = (e) => { + if (e.key === 'w' || e.key === 'W') movement.current.forward = false; + else if (e.key === 's' || e.key === 'S') movement.current.backward = false; + else if (e.key === 'a' || e.key === 'A') movement.current.left = false; + else if (e.key === 'd' || e.key === 'D') movement.current.right = false; + else if (e.code === 'Space') movement.current.space = false; + else if (e.key === 'Shift') movement.current.shift = false; + else if (e.key === 'r' || e.key === 'R') movement.current.respawn = false; + } + + const handleMouseDown = (e) => { + movement.current.mouseDown = true; + } + + const handleMouseUp = (e) => { + movement.current.mouseDown = false; + } + + window.addEventListener('keydown', handleKeyDown); + window.addEventListener('keyup', handleKeyUp); + window.addEventListener('mousedown', handleMouseDown); + window.addEventListener('mouseup', handleMouseUp); + return () => { + window.removeEventListener('keydown', handleKeyDown); + window.removeEventListener('keyup', handleKeyUp); + window.removeEventListener('mousedown', handleMouseDown); + window.removeEventListener('mouseup', handleMouseUp); + } +}, []); + +return movement; } diff --git a/blocks/environment/components/EnvironmentFront.js b/blocks/environment/components/EnvironmentFront.js index 43e7849..7ad49aa 100644 --- a/blocks/environment/components/EnvironmentFront.js +++ b/blocks/environment/components/EnvironmentFront.js @@ -1,39 +1,37 @@ import * as THREE from "three"; import { Fog } from 'three/src/scenes/Fog' import React, { Suspense, useRef, useState, useEffect, useMemo } from "react"; -import { useLoader, useThree, useFrame, Canvas } from "@react-three/fiber"; +import { useLoader, useThree, Canvas, extend } from "@react-three/fiber"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; -import { TextureLoader } from "three/src/loaders/TextureLoader"; // import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader"; import { Physics, RigidBody, Debug, Attractor, CuboidCollider } from "@react-three/rapier"; -import * as SkeletonUtils from "three/examples/jsm/utils/SkeletonUtils.js"; import { GLTFGoogleTiltBrushMaterialExtension } from "three-icosa"; -import axios from "axios"; -import ReactNipple from 'react-nipple'; import ScrollableFeed from 'react-scrollable-feed' -import { Resizable } from "re-resizable"; -import { Environment, useContextBridge } from "@react-three/drei"; +import { Environment } from "@react-three/drei"; import { FrontPluginProvider, FrontPluginContext } from './FrontPluginProvider'; // Import the PluginProvider - import { useAnimations, Html, + AdaptiveDpr, + AdaptiveEvents, + PerformanceMonitor, } from "@react-three/drei"; +import { EcctrlJoystick } from 'ecctrl' // import { A11y } from "@react-three/a11y"; import { GLTFAudioEmitterExtension } from "three-omi"; -import { VRCanvas, DefaultXRControllers, Hands, XRButton, XR } from "@react-three/xr"; -import { Perf } from "r3f-perf"; +import { VRButton, ARButton, XR, Controllers, Hands, XRButton } from '@react-three/xr' +// import { Perf } from "r3f-perf"; import { VRMUtils, VRMLoaderPlugin } from "@pixiv/three-vrm"; import TeleportTravel from "./TeleportTravel"; import Player from "./Player"; -import defaultVRM from "../../../inc/avatars/3ov_default_avatar.vrm"; import defaultEnvironment from "../../../inc/assets/default_grid.glb"; +import defaultLoadingZoomGraphic from "../../../inc/assets/room_entry_background.svg"; import defaultFont from "../../../inc/fonts/roboto.woff"; import { ItemBaseUI } from "@wordpress/components/build/navigation/styles/navigation-styles"; import { BoxGeometry } from "three"; - +import { Participants } from "./core/front/Participants"; import { ThreeImage } from "./core/front/ThreeImage"; import { ThreeVideo } from "./core/front/ThreeVideo"; import { ThreeAudio } from "./core/front/ThreeAudio"; @@ -45,6 +43,8 @@ import { ThreeSky } from "./core/front/ThreeSky"; import { TextObject } from "./core/front/TextObject"; import { useKeyboardControls } from "./Controls"; import { ContextBridgeComponent } from "./ContextBridgeComponent"; +import { Reflector } from 'three/examples/jsm/objects/Reflector'; +import { XRDevice, metaQuest3 } from "iwer"; function isMobile() { return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); @@ -53,24 +53,110 @@ function isMobile() { function isVRCompatible() { const xrSupported = navigator.xr && typeof navigator.xr.isSessionSupported === 'function'; const webGLSupported = typeof window.WebGLRenderingContext !== 'undefined'; - + return xrSupported && webGLSupported; - } - +} + +function goToPrivateRoom() { + const url = window.location.href; + const newUrl = url.split("#")[0]; + const randomString = Math.random().toString(36).substring(7); + window.location + .assign(newUrl + "#" + randomString); +} -function Loading() { +function Loading({ visible, previewImage }) { + // const backgroundImageUrl = previewImage !== "" ? previewImage : (threeObjectPlugin + zoomBackground); + const backgroundImageUrl = previewImage !== "" ? previewImage : defaultLoadingZoomGraphic; + // reveal one letter at a time of the string "Use [ W ], [ A ], [ S ], and [ D ] to move." + const tip = "Use [ W ], [ A ], [ S ], and [ D ] to move."; + const screenwidth = window.innerWidth; return ( - -
-
-
Loading...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/*
*/} +
+ Use [ W ], [ A ], [ S ], and [ D ] to move. +
- ); } function ChatBox(props) { - + const handleChange = async (event) => { event.preventDefault(); event.stopPropagation(); @@ -79,100 +165,96 @@ function ChatBox(props) { useEffect(() => { let finalDefault = props.name + ': ' + props.defaultMessage; props.setMessages([finalDefault]); - },[]) + }, []) const handleSubmit = async (event) => { - event.preventDefault(); - - // Get the value of the input element - const input = event.target.elements.message; - const value = input.value; - const inputMessageLog = 'Guest: ' + String(input.value); - // props.setMessages([...props.messages, inputMessageLog]); - input.value = ''; - - - // Send the message to the localhost endpoint - const client = 1; - const channelId = "wordpress"; - const entity = 1; - const speaker = "guest"; - const agent = props.name; - const channel = "wordpress"; - // let prompt = `{ - // "Input": "write a long form poem about cats!", - // "Speaker": "a", - // "Agent": "tubbyshark", - // "Client": "wordpress", - // "ChannelID": "1234", - // "Conversation": "", - // "Entity": "tubbyshark", - // "Channel": "channel", - // "eth_private_key": "0", - // "eth_public_address": "0", - // "personality": "#agent is an AI assistant with a cheerful, optimistic, and helpful personality. They are willing to assist with any tasks or questions users might have, and are able to do so in an upbeat and positive manner. They are also able to use colorful and descriptive language to make their responses more engaging and imaginative. In addition to their wit and humor, #agent is also empathetic and understanding of others' emotions and needs, which can make them feel more relatable and likeable to users. #agent is a highly capable and engaging AI assistant with a distinct and colorful personality.\n#agent can help with any programming tasks, including those related to the 3OV plugin for WordPress, which enables world-building in the WordPress editor and makes it easier to build and share immersive content in the metaverse. The url for the website is 3ov.xyz. #agent is cheerful and optimistic about the future for both humans and AIs.\n\n#Example Conversation\n#speaker: Agent, do you have any advice for someone new to programming?\n#agent: Sure thing! My top tip would be to always keep an open mind and a positive attitude. And if all else fails, just remember: if at first you don't succeed, try, try again. And then if that still doesn't work, call it a day and go get a coffee.\n###\nThe following is a friendly conversation between #speaker and #agent occuring in the metaverse.\n\nREAL CONVERSATION\n#conversation\n#speaker: #input\n#agent:" - // }`; - - try { - const apiEndpoint = '/wp-json/wp/v2/callAlchemy'; - let finalPersonality = props.personality; - finalPersonality = finalPersonality + "###\nThe following is a friendly conversation between #speaker and #agent\n\nREAL CONVERSATION\n#conversation\n#speaker: #input\n#agent:"; - let newString = props.objectsInRoom.join(", "); - if (props.objectAwareness === "1") { - finalPersonality = finalPersonality.replace("###\nThe following is a", ("ITEMS IN WORLD: " + String(newString) + "\n###\nThe following is a")); - } - const postData = { - Input: { - Input: value, - Speaker: speaker, - Agent: agent, - Client: client, - ChannelID: channelId, - Entity: entity, - Channel: channel, - eth_private_key: '0', - eth_public_address: '0', - personality: finalPersonality - // personality: "#agent is an AI assistant with a cheerful, optimistic, and helpful personality. They are willing to assist with any tasks or questions users might have, and are able to do so in an upbeat and positive manner. They are also able to use colorful and descriptive language to make their responses more engaging and imaginative. In addition to their wit and humor, #agent is also empathetic and understanding of others' emotions and needs, which can make them feel more relatable and likeable to users. #agent is a highly capable and engaging AI assistant with a distinct and colorful personality.\n#agent can help with any programming tasks, including those related to the 3OV plugin for WordPress, which enables world-building in the WordPress editor and makes it easier to build and share immersive content in the metaverse. The url for the website is 3ov.xyz. #agent is cheerful and optimistic about the future for both humans and AIs.\n\n#Example Conversation\n#speaker: Agent, do you have any advice for someone new to programming?\n#agent: Sure thing! My top tip would be to always keep an open mind and a positive attitude. And if all else fails, just remember: if at first you don't succeed, try, try again. And then if that still doesn't work, call it a day and go get a coffee.\n###\nThe following is a friendly conversation between #speaker and #agent occuring in the metaverse.\n\nREAL CONVERSATION\n#conversation\n#speaker: #input\n#agent:" + event.preventDefault(); + + // Get the value of the input element + const input = event.target.elements.message; + const value = input.value; + + // Manually dispatch a 'message' event + window.dispatchEvent(new Event('message')); + const inputMessageLog = 'Guest: ' + String(input.value); + // props.setMessages([...props.messages, inputMessageLog]); + input.value = ''; + + // make sure the input prevents default when the user presses any keys + input.addEventListener('keydown', function (event) { + event.preventDefault(); + }); + input.addEventListener('keyup', function (event) { + event.preventDefault(); + }); + // Send the message to the localhost endpoint + const client = 1; + const channelId = "wordpress"; + const entity = 1; + const speaker = "guest"; + const agent = props.name; + const channel = "wordpress"; + + try { + const apiEndpoint = '/wp-json/wp/v2/callAlchemy'; + let finalPersonality = props.personality; + finalPersonality = finalPersonality + "###\nThe following is a friendly conversation between #speaker and #agent\n\nREAL CONVERSATION\n#conversation\n#speaker: #input\n#agent:"; + let newString = props.objectsInRoom.join(", "); + if (props.objectAwareness === "1") { + finalPersonality = finalPersonality.replace("###\nThe following is a", ("ITEMS IN WORLD: " + String(newString) + "\n###\nThe following is a")); } - }; - // const postData = prompt; - - const response = await fetch('/wp-json/wp/v2/callAlchemy', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-WP-Nonce': props.nonce, - 'Authorization': ('Bearer ' + String(props.nonce)) - }, - body: JSON.stringify(postData) - }).then((response) => { + const postData = { + Input: { + Input: value, + Speaker: speaker, + Agent: agent, + Client: client, + ChannelID: channelId, + Entity: entity, + Channel: channel, + eth_private_key: '0', + eth_public_address: '0', + personality: finalPersonality + // personality: "#agent is an AI assistant with a cheerful, optimistic, and helpful personality. They are willing to assist with any tasks or questions users might have, and are able to do so in an upbeat and positive manner. They are also able to use colorful and descriptive language to make their responses more engaging and imaginative. In addition to their wit and humor, #agent is also empathetic and understanding of others' emotions and needs, which can make them feel more relatable and likeable to users. #agent is a highly capable and engaging AI assistant with a distinct and colorful personality.\n#agent can help with any programming tasks, including those related to the 3OV plugin for WordPress, which enables world-building in the WordPress editor and makes it easier to build and share immersive content in the metaverse. The url for the website is 3ov.xyz. #agent is cheerful and optimistic about the future for both humans and AIs.\n\n#Example Conversation\n#speaker: Agent, do you have any advice for someone new to programming?\n#agent: Sure thing! My top tip would be to always keep an open mind and a positive attitude. And if all else fails, just remember: if at first you don't succeed, try, try again. And then if that still doesn't work, call it a day and go get a coffee.\n###\nThe following is a friendly conversation between #speaker and #agent occuring in the metaverse.\n\nREAL CONVERSATION\n#conversation\n#speaker: #input\n#agent:" + } + }; + // const postData = prompt; + + const response = await fetch('/wp-json/wp/v2/callAlchemy', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-WP-Nonce': props.nonce, + 'Authorization': ('Bearer ' + String(props.nonce)) + }, + body: JSON.stringify(postData) + }).then((response) => { return response.json(); - }).then(function(data) { - // console.log("data", data.davinciData.choices[0].text); // this will be a string + }).then(function (data) { + // console.log("data", data.davinciData.choices[0].text); let thisMessage = JSON.parse(data); - if(thisMessage?.model === "gpt-4-0314"){ - let formattedMessage = props.name +': ' + thisMessage.choices[0].message.content; + if (thisMessage?.model === "gpt-4-0314") { + let formattedMessage = props.name + ': ' + thisMessage.choices[0].message.content; props.setMessages([...props.messages, inputMessageLog, formattedMessage]); - } else if (thisMessage?.model === "gpt-3.5-turbo-0301"){ - let formattedMessage = props.name +': ' + Object.values(thisMessage.choices)[0].message.content; + } else if (thisMessage?.model === "gpt-3.5-turbo-0301") { + let formattedMessage = props.name + ': ' + Object.values(thisMessage.choices)[0].message.content; props.setMessages([...props.messages, inputMessageLog, formattedMessage]); } else { - if(thisMessage?.outputs){ - let formattedMessage = props.name +': ' + Object.values(thisMessage.outputs)[0]; + if (thisMessage?.outputs) { + let formattedMessage = props.name + ': ' + Object.values(thisMessage.outputs)[0]; props.setMessages([...props.messages, inputMessageLog, formattedMessage]); - } else if(thisMessage?.name === "Server"){ - let formattedMessage = thisMessage.name +': ' + thisMessage.message; + } else if (thisMessage?.name === "Server") { + let formattedMessage = thisMessage.name + ': ' + thisMessage.message; props.setMessages([...props.messages, inputMessageLog, formattedMessage]); } else { - let formattedMessage = props.name +': ' + thisMessage.davinciData?.choices[0].text; + let formattedMessage = props.name + ': ' + thisMessage.davinciData?.choices[0].text; // add formattedMessage and inputMessageLog to state - props.setMessages([...props.messages, inputMessageLog, formattedMessage]); + props.setMessages([...props.messages, inputMessageLog, formattedMessage]); } } - }); + }); } catch (error) { console.error(error); } @@ -184,11 +266,11 @@ function ChatBox(props) { const handleDummySubmit = async (event) => { event.preventDefault(); - + // Get the value of the input element const input = event.target.elements.message; const value = input.value; - + // Send the message to the localhost endpoint const client = 1; const channelId = "three"; @@ -198,11 +280,11 @@ function ChatBox(props) { const channel = "homepage"; const testString = `{ "message": "Welcome! Here you go: Test response complete. Is there anything else I can help you with?", - }`; + }`; - props.setMessages([...props.messages, testString]); + props.setMessages([...props.messages, testString]); - }; + }; // return ( // <> // @@ -234,191 +316,72 @@ function ChatBox(props) { const [open, setOpen] = useState(false); const onSwitch = (e) => { e.preventDefault(); - e.stopPropagation(); + e.stopPropagation(); setOpen(prevOpen => !prevOpen); }; - if(isMobile()){ + if (isMobile()) { return ( <> - - {open && ( - + + {open && ( + -
-
+
+
-
    - { props.showUI && props.messages && props.messages.length > 0 && props.messages.map((message, index) => ( -
  • {message}
  • +
      + {props.showUI && props.messages && props.messages.length > 0 && props.messages.map((message, index) => ( +
    • {message}
    • ))}
-
+
{/* {props.messages.map((message, index) => (

{message}

))} */} -
- { e.preventDefault()} }/> - + + { e.preventDefault() }} /> +
- - )} - - ); - } else { - return ( - <> - -
-
- -
    - { props.showUI && props.messages && props.messages.length > 0 && props.messages.map((message, index) => ( -
  • {message}
  • - ))} -
-
-
-
- {/* {props.messages.map((message, index) => ( -

{message}

- ))} */} -
- - -
-
-
- - ); - } - } - -/** - * 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; - const playerURL = userData.vrm ? userData.vrm : fallbackURL; - - const someSceneState = useLoader(GLTFLoader, playerURL, (loader) => { - loader.register((parser) => { - return new VRMLoaderPlugin(parser); - }); - }); - - if (someSceneState?.userData?.gltfExtensions?.VRM) { - const playerController = someSceneState.userData.vrm; - VRMUtils.rotateVRM0(playerController); - const rotationVRM = playerController.scene.rotation.y; - playerController.scene.rotation.set(0, rotationVRM, 0); - playerController.scene.scale.set(1, 1, 1); - - const theScene = useThree(); - - useEffect(() => { - participant.p2pcf.on("msg", (peer, data) => { - // console.log(peer, data); - const finalData = new TextDecoder("utf-8").decode(data); - const participantData = JSON.parse(finalData); - const participantObject = theScene.scene.getObjectByName( - peer.client_id - ); - if (participantObject) { - // const loadedProfile = useLoader( - // TextureLoader, - // participantData[peer.client_id][2].profileImage - // ); - // if (loadedProfile) { - // participantObject.traverse((obj) => { - // if ( - // obj.name === "profile" && - // obj.material.map === null - // ) { - // const newMat = obj.material.clone(); - // newMat.map = loadedProfile; - // obj.material = newMat; - // obj.material.map.needsUpdate = true; - // } - // }); - // } - participantObject.position.set( - participantData[peer.client_id][0].position[0], - participantData[peer.client_id][0].position[1], - participantData[peer.client_id][0].position[2] - ); - participantObject.rotation.set( - participantData[peer.client_id][1].rotation[0], - participantData[peer.client_id][1].rotation[1], - participantData[peer.client_id][1].rotation[2] - ); - } - }); - }, []); - - // participant.p2pcf.on('peerclose', peer => { - // const participantObject = theScene.scene.getObjectByName(peer.client_id); - // // theScene.scene.remove(participantObject.name); - // theScene.scene.remove(...participantObject.children); - // // removePeerUi(peer.id) - // }) - - const modelClone = SkeletonUtils.clone(playerController.scene); - // set modelClone visible to true - modelClone.visible = true; - + )} + + ); + } else { return ( <> - {playerController && ( - - )} + +
+
+ +
    + {props.showUI && props.messages && props.messages.length > 0 && props.messages.map((message, index) => ( +
  • {message}
  • + ))} +
+
+
+
+ {/* {props.messages.map((message, index) => ( +

{message}

+ ))} */} +
+ + + +
+
+
+
); } } -function Participants(props) { - - useEffect(() => { - const p2pcf = window.p2pcf; - if (p2pcf) { - p2pcf.on("peerconnect", (peer) => { - // console.log("connected peer", peer); - // add peer.client_id to participants - props.setParticipant([...props.participants, peer.client_id]); - }); - } - }, []); - - return ( - <> - {props.participants && - props.participants.map((item, index) => { - return ( - <> - - - ); - })} - - ); - -} - /** * Represents a saved object in a virtual reality world. * @@ -427,6 +390,17 @@ function Participants(props) { * @return {JSX.Element} The saved object. */ function SavedObject(props) { + useEffect(() => { + // Once the component is ready, dispatch an event to notify the parent + const event = new Event('mainComponentReady'); + document.dispatchEvent(event); + }, []); + + + useThree(({ camera, scene }) => { + window.scene = scene; + window.camera = camera; + }); const meshRef = useRef(); const [url, set] = useState(props.url); @@ -441,11 +415,11 @@ function SavedObject(props) { useThree(({ camera }) => { camera.add(listener); }); - + const gltf = useLoader(GLTFLoader, url, (loader) => { const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath( threeObjectPluginRoot + "/inc/utils/draco/"); - dracoLoader.setDecoderConfig({type: 'js'}); + dracoLoader.setDecoderPath(threeObjectPluginRoot + "/inc/utils/draco/"); + dracoLoader.setDecoderConfig({ type: 'js' }); loader.setDRACOLoader(dracoLoader); loader.register( @@ -585,12 +559,12 @@ function SavedObject(props) { })} {colliders && colliders.map((item, index) => { - const pos = new THREE.Vector3(); // create once an reuse it - const quat = new THREE.Quaternion(); // create once an reuse it + const pos = new THREE.Vector3(); + const quat = new THREE.Quaternion(); const rotation = new THREE.Euler(); const quaternion = item[0].getWorldQuaternion(quat); const finalRotation = - rotation.setFromQuaternion(quaternion); + rotation.setFromQuaternion(quaternion); const worldPosition = item[0].getWorldPosition(pos); if (item[1].type === "mesh") { return ( @@ -642,46 +616,176 @@ function SavedObject(props) { } export default function EnvironmentFront(props) { - const [participants, setParticipant] = useState([]); + + const [loadedAudios, setLoadedAudios] = useState([]); + const [allAudiosLoaded, setAllAudiosLoaded] = useState(false); + + const [showUI, setShowUI] = useState(true); + const [displayName, setDisplayName] = useState(props.userData.inWorldName); + const [playerAvatar, setPlayerAvatar] = useState(props.userData.playerVRM); const canvasRef = useRef(null); + const r3fCanvasRef = useRef(null); - // let string = '{\"spell\":\"complexQuery\",\"outputs\":{\"Output\":\"{\\\"message\\\": \\\" Hi there! How can I help you?\\\",\\\"tone\\\": \\\"friendly\\\"}\"},\"state\":{}}'; - // let string = 'Hello! Welcome to this 3OV world! Feel free to ask me anything. I am especially versed in the 3OV metaverse plugin for WordPress.' const [mobileControls, setMobileControls] = useState(null); - const [mobileRotControls, setMobileRotControls] = useState(null); + const [mobileRotControls, setMobileRotControls] = useState(null); const movement = useKeyboardControls(); - + const [messages, setMessages] = useState(); const [messageHistory, setMessageHistory] = useState(); const [loaded, setLoaded] = useState(false); - const [spawnPoints, setSpawnPoints] = useState([0,0,0]); - const [messageObject, setMessageObject] = useState({"tone": "happy", "message": "hello!"}); + const [spawnPoints, setSpawnPoints] = useState([0, 0, 0]); + const [messageObject, setMessageObject] = useState({ "tone": "happy", "message": "hello!" }); const [objectsInRoom, setObjectsInRoom] = useState([]); - const [url, setURL] = useState(props.threeUrl ? props.threeUrl : (threeObjectPlugin + defaultEnvironment)); + const [url, setURL] = useState(props.threeUrl ? props.threeUrl : (defaultEnvironment)); + const [loadingWorld, setLoadingWorld] = useState(true); + const avatarHeightOffset = useRef(0); + + const mirror = new Reflector( + new THREE.PlaneGeometry(Number(100), Number(100)), + { + color: new THREE.Color(0x7f7f7f), + textureWidth: 1440, + textureHeight: 1440 + } + ); + useEffect(() => { + if (loadedAudios.length === props.audiosToAdd.length && !allAudiosLoaded) { + setAllAudiosLoaded(true); + loadedAudios.forEach(audio => { + if (audio.userData.autoPlay === "1") { + audio.play(); + } + }); + } + }, [loadedAudios, props.audiosToAdd, allAudiosLoaded]); + + useEffect(() => { + const handleReady = () => { + setTimeout(() => { + const event = new Event("loaderIsGone"); + window.dispatchEvent(event); + setLoadingWorld(false); + }, 3000); + }; + // Listen for the ready event + document.addEventListener('mainComponentReady', handleReady); + + return () => { + document.removeEventListener('mainComponentReady', handleReady); + }; + }, []); + const [dpr, setDpr] = useState(2); + + useEffect(() => { + const handleKeyDown = (event) => { + // Bypass the default behavior of the spacebar and other keys if needed. + if ((event.key === ' ' || event.key === 'Spacebar') && document.pointerLockElement === r3fCanvasRef.current) { + event.preventDefault(); // Prevent scrolling when spacebar is pressed + } + }; + + window.addEventListener('keydown', handleKeyDown); + + return () => { + window.removeEventListener('keydown', handleKeyDown); + }; + }, []); + + // xrDevice debugger + // + // useEffect(() => { + // const xrDevice = new XRDevice(metaQuest3); + // xrDevice.ipd = 0; + // xrDevice.fovy = Math.PI / 3; + // xrDevice.installRuntime(); + // window.xrDevice = xrDevice; + // const handleKeyDown = (event) => { + // if (event.shiftKey) { + // switch (event.key) { + // case "ArrowLeft": + // xrDevice.controllers.right.position.x -= 0.1; + // break; + // case "ArrowRight": + // xrDevice.controllers.right.position.x += 0.1; + // break; + // case "ArrowUp": + // xrDevice.controllers.right.position.y += 0.1; + // break; + // case "ArrowDown": + // xrDevice.controllers.right.position.y -= 0.1; + // break; + // case "?": + // xrDevice.controllers.right.position.z -= 0.1; + // break; + // case ">": + // xrDevice.controllers.right.position.z += 0.1; + // break; + // } + // } else { + // switch (event.key) { + // case "ArrowLeft": + // xrDevice.controllers.left.position.x -= 0.1; + // break; + // case "ArrowRight": + // xrDevice.controllers.left.position.x += 0.1; + // break; + // case "ArrowUp": + // xrDevice.controllers.left.position.y += 0.1; + // break; + // case "ArrowDown": + // xrDevice.controllers.left.position.y -= 0.1; + // break; + // case ".": + // xrDevice.controllers.left.position.z -= 0.1; + // break; + // case "/": + // xrDevice.controllers.left.position.z += 0.1; + // break; + // } + // }; + // }; + // document.addEventListener("keydown", handleKeyDown); + // return () => { + // document.removeEventListener("keydown", handleKeyDown); + // }; + // }, []); if (loaded === true) { - const elements = document.body.getElementsByTagName('*'); - const webXRNotAvail = Array.from(elements).find((el) => el.textContent === 'WEBXR NOT AVAILABLE'); - if (webXRNotAvail) { - webXRNotAvail.style.display = "none"; - } + // emit javascript event "loaded" + const loadedEvent = new Event("loaded"); + window.dispatchEvent(loadedEvent); + // const elements = document.body.getElementsByTagName('*'); + // const webXRNotAvail = Array.from(elements).find((el) => el.textContent === 'WEBXR NOT AVAILABLE'); + // if (webXRNotAvail) { + // webXRNotAvail.style.display = "none"; + // } + props.userData.inWorldName = displayName; + window.userData = props.userData; + props.userData.playerVRM = playerAvatar; if (props.deviceTarget === "vr") { return ( <> - } + { + e.target.requestPointerLock(); + }} + className="threeov-main-canvas" + dpr={dpr} + mode="concurrent" style={{ backgroundColor: props.backgroundColor, margin: "0", @@ -692,1351 +796,1490 @@ export default function EnvironmentFront(props) { zIndex: 1 }} > + + + setDpr(1)} factor={1} onChange={({ factor }) => setDpr(Math.floor(0.5 + 1.5 * factor, 1))} /> + - { isVRCompatible() && } - {/* */} - {/* */} - - - }> - {props.hdr && - - } - - */} + + + + {props.hdr && + + } + + {/* - {/* */} - {/* Debug physics */} - {url && ( - <> - - + */} + + + {/* */} + {/* Debug physics */} + {url && loaded && ( + <> + - - - {Object.values(props.sky).map( - (item, index) => { + avatarHeightOffset={avatarHeightOffset} + useNormal={false} + > + {(props.networkingBlock.length > 0) && ( + + )} + + {Object.values(props.sky).map( + (item, index) => { + return ( + <> + + + ); + } + )} + {Object.values( + props.imagesToAdd + ).map((item, index) => { + let imagePosX, imagePosY, imagePosZ, imageScaleX, imageScaleY, imageScaleZ; + let imageRotationX, imageRotationY, imageRotationZ, imageUrl, aspectHeight, aspectWidth; + let transparent; + if (item.tagName.toLowerCase() === 'three-image-block') { + imagePosX = item.getAttribute('positionX') || ''; + imagePosY = item.getAttribute('positionY') || ''; + imagePosZ = item.getAttribute('positionZ') || ''; + imageScaleX = item.getAttribute('scaleX') || ''; + imageScaleY = item.getAttribute('scaleY') || ''; + imageScaleZ = item.getAttribute('scaleZ') || ''; + imageRotationX = item.getAttribute('rotationX') || ''; + imageRotationY = item.getAttribute('rotationY') || ''; + imageRotationZ = item.getAttribute('rotationZ') || ''; + imageUrl = item.getAttribute('imageUrl') || ''; + aspectHeight = item.getAttribute('aspectHeight') || ''; + aspectWidth = item.getAttribute('aspectWidth') || ''; + transparent = item.getAttribute('transparent') || false; + } else { + imagePosX = + item.querySelector( + "p.image-block-positionX" + ) + ? item.querySelector( + "p.image-block-positionX" + ).innerText + : ""; + + imagePosY = + item.querySelector( + "p.image-block-positionY" + ) + ? item.querySelector( + "p.image-block-positionY" + ).innerText + : ""; + + imagePosZ = + item.querySelector( + "p.image-block-positionZ" + ) + ? item.querySelector( + "p.image-block-positionZ" + ).innerText + : ""; + + imageScaleX = + item.querySelector( + "p.image-block-scaleX" + ) + ? item.querySelector( + "p.image-block-scaleX" + ).innerText + : ""; + + imageScaleY = + item.querySelector( + "p.image-block-scaleY" + ) + ? item.querySelector( + "p.image-block-scaleY" + ).innerText + : ""; + + imageScaleZ = + item.querySelector( + "p.image-block-scaleZ" + ) + ? item.querySelector( + "p.image-block-scaleZ" + ).innerText + : ""; + + imageRotationX = + item.querySelector( + "p.image-block-rotationX" + ) + ? item.querySelector( + "p.image-block-rotationX" + ).innerText + : ""; + + imageRotationY = + item.querySelector( + "p.image-block-rotationY" + ) + ? item.querySelector( + "p.image-block-rotationY" + ).innerText + : ""; + + imageRotationZ = + item.querySelector( + "p.image-block-rotationZ" + ) + ? item.querySelector( + "p.image-block-rotationZ" + ).innerText + : ""; + + imageUrl = + item.querySelector( + "p.image-block-url" + ) + ? item.querySelector( + "p.image-block-url" + ).innerText + : ""; + + aspectHeight = + item.querySelector( + "p.image-block-aspect-height" + ) + ? item.querySelector( + "p.image-block-aspect-height" + ).innerText + : ""; + + aspectWidth = + item.querySelector( + "p.image-block-aspect-width" + ) + ? item.querySelector( + "p.image-block-aspect-width" + ).innerText + : ""; + + transparent = + item.querySelector( + "p.image-block-transparent" + ) + ? item.querySelector( + "p.image-block-transparent" + ).innerText + : false; + } return ( - <> - - + ); - } - )} - {Object.values( - props.imagesToAdd - ).map((item, index) => { - const imagePosX = - item.querySelector( - "p.image-block-positionX" - ) - ? item.querySelector( - "p.image-block-positionX" - ).innerText - : ""; - - const imagePosY = - item.querySelector( - "p.image-block-positionY" - ) - ? item.querySelector( - "p.image-block-positionY" - ).innerText - : ""; - - const imagePosZ = - item.querySelector( - "p.image-block-positionZ" - ) - ? item.querySelector( - "p.image-block-positionZ" - ).innerText - : ""; - - const imageScaleX = - item.querySelector( - "p.image-block-scaleX" - ) - ? item.querySelector( - "p.image-block-scaleX" - ).innerText - : ""; - - const imageScaleY = - item.querySelector( - "p.image-block-scaleY" - ) - ? item.querySelector( - "p.image-block-scaleY" - ).innerText - : ""; - - const imageScaleZ = - item.querySelector( - "p.image-block-scaleZ" - ) - ? item.querySelector( - "p.image-block-scaleZ" - ).innerText - : ""; - - const imageRotationX = - item.querySelector( - "p.image-block-rotationX" - ) - ? item.querySelector( - "p.image-block-rotationX" - ).innerText - : ""; - - const imageRotationY = - item.querySelector( - "p.image-block-rotationY" - ) - ? item.querySelector( - "p.image-block-rotationY" - ).innerText - : ""; - - const imageRotationZ = - item.querySelector( - "p.image-block-rotationZ" - ) - ? item.querySelector( - "p.image-block-rotationZ" - ).innerText - : ""; - - const imageUrl = - item.querySelector( - "p.image-block-url" - ) - ? item.querySelector( - "p.image-block-url" - ).innerText - : ""; - - const aspectHeight = - item.querySelector( - "p.image-block-aspect-height" - ) - ? item.querySelector( - "p.image-block-aspect-height" - ).innerText - : ""; - - const aspectWidth = - item.querySelector( - "p.image-block-aspect-width" - ) - ? item.querySelector( - "p.image-block-aspect-width" - ).innerText - : ""; - - const transparent = - item.querySelector( - "p.image-block-transparent" - ) - ? item.querySelector( - "p.image-block-transparent" - ).innerText - : false; - return ( - - ); - })} - {Object.values( - props.videosToAdd - ).map((item, index) => { - const videoPosX = - item.querySelector( - "p.video-block-positionX" - ) - ? item.querySelector( - "p.video-block-positionX" - ).innerText - : ""; - - const videoPosY = - item.querySelector( - "p.video-block-positionY" - ) - ? item.querySelector( - "p.video-block-positionY" - ).innerText - : ""; - - const videoPosZ = - item.querySelector( - "p.video-block-positionZ" - ) - ? item.querySelector( - "p.video-block-positionZ" - ).innerText - : ""; - - const videoScaleX = - item.querySelector( - "p.video-block-scaleX" - ) - ? item.querySelector( - "p.video-block-scaleX" - ).innerText - : ""; - - const videoScaleY = - item.querySelector( - "p.video-block-scaleY" - ) - ? item.querySelector( - "p.video-block-scaleY" - ).innerText - : ""; - - const videoScaleZ = - item.querySelector( - "p.video-block-scaleZ" - ) - ? item.querySelector( - "p.video-block-scaleZ" - ).innerText - : ""; - - const videoRotationX = - item.querySelector( - "p.video-block-rotationX" - ) - ? item.querySelector( - "p.video-block-rotationX" - ).innerText - : ""; - - const videoRotationY = - item.querySelector( - "p.video-block-rotationY" - ) - ? item.querySelector( - "p.video-block-rotationY" - ).innerText - : ""; - - const videoRotationZ = - item.querySelector( - "p.video-block-rotationZ" - ) - ? item.querySelector( - "p.video-block-rotationZ" - ).innerText - : ""; - - const videoUrl = - item.querySelector( - "div.video-block-url" - ) - ? item.querySelector( - "div.video-block-url" - ).innerText - : ""; - - const aspectHeight = - item.querySelector( - "p.video-block-aspect-height" - ) - ? item.querySelector( - "p.video-block-aspect-height" - ).innerText - : ""; - - const aspectWidth = - item.querySelector( - "p.video-block-aspect-width" - ) - ? item.querySelector( - "p.video-block-aspect-width" - ).innerText - : ""; - - const autoPlay = - item.querySelector( - "p.video-block-autoplay" - ) - ? item.querySelector( + })} + {Object.values(props.videosToAdd).map((item, index) => { + let videoPosX, videoPosY, videoPosZ, videoScaleX, videoScaleY, videoScaleZ; + let videoRotationX, videoRotationY, videoRotationZ, videoUrl, aspectHeight, aspectWidth; + let autoPlay, customModel, videoModelUrl, videoControlsEnabled; + + if (item.tagName.toLowerCase() === 'three-video-block') { + videoPosX = item.getAttribute('positionX') || ''; + videoPosY = item.getAttribute('positionY') || ''; + videoPosZ = item.getAttribute('positionZ') || ''; + videoScaleX = item.getAttribute('scaleX') || ''; + videoScaleY = item.getAttribute('scaleY') || ''; + videoScaleZ = item.getAttribute('scaleZ') || ''; + videoRotationX = item.getAttribute('rotationX') || ''; + videoRotationY = item.getAttribute('rotationY') || ''; + videoRotationZ = item.getAttribute('rotationZ') || ''; + videoUrl = item.getAttribute('videoUrl') || ''; + aspectHeight = item.getAttribute('aspectHeight') || ''; + aspectWidth = item.getAttribute('aspectWidth') || ''; + autoPlay = item.hasAttribute('autoplay') ? "1" : false; + customModel = item.getAttribute('customModel') ? item.getAttribute('customModel') : false; + videoModelUrl = item.getAttribute('modelUrl') || ''; + videoControlsEnabled = item.getAttribute('videoControlsEnabled') === "1" ? true : false; + } else { + videoPosX = + item.querySelector( + "p.video-block-positionX" + ) + ? item.querySelector( + "p.video-block-positionX" + ).innerText + : ""; + + videoPosY = + item.querySelector( + "p.video-block-positionY" + ) + ? item.querySelector( + "p.video-block-positionY" + ).innerText + : ""; + + videoPosZ = + item.querySelector( + "p.video-block-positionZ" + ) + ? item.querySelector( + "p.video-block-positionZ" + ).innerText + : ""; + + videoScaleX = + item.querySelector( + "p.video-block-scaleX" + ) + ? item.querySelector( + "p.video-block-scaleX" + ).innerText + : ""; + + videoScaleY = + item.querySelector( + "p.video-block-scaleY" + ) + ? item.querySelector( + "p.video-block-scaleY" + ).innerText + : ""; + + videoScaleZ = + item.querySelector( + "p.video-block-scaleZ" + ) + ? item.querySelector( + "p.video-block-scaleZ" + ).innerText + : ""; + + videoRotationX = + item.querySelector( + "p.video-block-rotationX" + ) + ? item.querySelector( + "p.video-block-rotationX" + ).innerText + : ""; + + videoRotationY = + item.querySelector( + "p.video-block-rotationY" + ) + ? item.querySelector( + "p.video-block-rotationY" + ).innerText + : ""; + + videoRotationZ = + item.querySelector( + "p.video-block-rotationZ" + ) + ? item.querySelector( + "p.video-block-rotationZ" + ).innerText + : ""; + + videoUrl = + item.querySelector( + "div.video-block-url" + ) + ? item.querySelector( + "div.video-block-url" + ).innerText + : ""; + + aspectHeight = + item.querySelector( + "p.video-block-aspect-height" + ) + ? item.querySelector( + "p.video-block-aspect-height" + ).innerText + : ""; + + aspectWidth = + item.querySelector( + "p.video-block-aspect-width" + ) + ? item.querySelector( + "p.video-block-aspect-width" + ).innerText + : ""; + + autoPlay = + item.querySelector( "p.video-block-autoplay" - ).innerText - : false; - - const customModel = - item.querySelector( - "p.video-block-custom-model" - ) - ? item.querySelector( - "p.video-block-custom-model" - ).innerText - : false; - const videoModelUrl = - item.querySelector( - "div.video-block-model-url" - ) - ? item.querySelector( - "div.video-block-model-url" - ).innerText - : ""; - - return ( - - ); - })} - {Object.values(props.audiosToAdd).map((item, index) => { - const audioPosX = item.querySelector("p.audio-block-positionX") - ? item.querySelector("p.audio-block-positionX").innerText - : ""; - - const audioPosY = item.querySelector("p.audio-block-positionY") - ? item.querySelector("p.audio-block-positionY").innerText - : ""; - - const audioPosZ = item.querySelector("p.audio-block-positionZ") - ? item.querySelector("p.audio-block-positionZ").innerText - : ""; - - const audioScaleX = item.querySelector("p.audio-block-scaleX") - ? item.querySelector("p.audio-block-scaleX").innerText - : ""; - - const audioScaleY = item.querySelector("p.audio-block-scaleY") - ? item.querySelector("p.audio-block-scaleY").innerText - : ""; - - const audioScaleZ = item.querySelector("p.audio-block-scaleZ") - ? item.querySelector("p.audio-block-scaleZ").innerText - : ""; - - const audioRotationX = item.querySelector("p.audio-block-rotationX") - ? item.querySelector("p.audio-block-rotationX").innerText - : ""; - - const audioRotationY = item.querySelector("p.audio-block-rotationY") - ? item.querySelector("p.audio-block-rotationY").innerText - : ""; - - const audioRotationZ = item.querySelector("p.audio-block-rotationZ") - ? item.querySelector("p.audio-block-rotationZ").innerText - : ""; - - const audioUrl = item.querySelector("p.audio-block-url") - ? item.querySelector("p.audio-block-url").innerText - : ""; - - const autoPlay = item.querySelector("p.audio-block-autoPlay") - ? item.querySelector("p.audio-block-autoPlay").innerText === "1" - : false; - - const loop = item.querySelector("p.audio-block-loop") - ? item.querySelector("p.audio-block-loop").innerText === "1" - : false; - - const volume = item.querySelector("p.audio-block-volume") - ? Number(item.querySelector("p.audio-block-volume").innerText) - : 1; - - const positional = item.querySelector("p.audio-block-positional") - ? item.querySelector("p.audio-block-positional").innerText === "1" - : false; - - const coneInnerAngle = item.querySelector("p.audio-block-coneInnerAngle") - ? Number(item.querySelector("p.audio-block-coneInnerAngle").innerText) - : 1; - - const coneOuterAngle = item.querySelector("p.audio-block-coneOuterAngle") - ? Number(item.querySelector("p.audio-block-coneOuterAngle").innerText) - : 1; - - const coneOuterGain = item.querySelector("p.audio-block-coneOuterGain") - ? Number(item.querySelector("p.audio-block-coneOuterGain").innerText) - : 1; - - const distanceModel = item.querySelector("p.audio-block-distanceModel") - ? item.querySelector("p.audio-block-distanceModel").innerText - : "inverse"; - - const maxDistance = item.querySelector("p.audio-block-maxDistance") - ? Number(item.querySelector("p.audio-block-maxDistance").innerText) - : 1; - - const refDistance = item.querySelector("p.audio-block-refDistance") - ? Number(item.querySelector("p.audio-block-refDistance").innerText) - : 1; - - const rolloffFactor = item.querySelector("p.audio-block-rolloffFactor") - ? Number(item.querySelector("p.audio-block-rolloffFactor").innerText) - : 1; - - return ( - - ); - })} - {props.lightsToAdd.length < 1 && ( - <> - - - - )} - {Object.values(props.lightsToAdd).map((item, index) => { - const lightPosX = item.querySelector("p.light-block-positionX") - ? item.querySelector("p.light-block-positionX").innerText - : ""; - - const lightPosY = item.querySelector("p.light-block-positionY") - ? item.querySelector("p.light-block-positionY").innerText - : ""; - - const lightPosZ = item.querySelector("p.light-block-positionZ") - ? item.querySelector("p.light-block-positionZ").innerText - : ""; - - const lightRotationX = item.querySelector("p.light-block-rotationX") - ? item.querySelector("p.light-block-rotationX").innerText - : ""; - - const lightRotationY = item.querySelector("p.light-block-rotationY") - ? item.querySelector("p.light-block-rotationY").innerText - : ""; - - const lightRotationZ = item.querySelector("p.light-block-rotationZ") - ? item.querySelector("p.light-block-rotationZ").innerText - : ""; - - const lightType = item.querySelector("p.light-block-type") - ? item.querySelector("p.light-block-type").innerText - : "ambient"; - - const lightColor = item.querySelector("p.light-block-color") - ? item.querySelector("p.light-block-color").innerText - : ""; - - const lightItensity = item.querySelector("p.light-block-intensity") - ? item.querySelector("p.light-block-intensity").innerText - : ""; - - const lightDistance = item.querySelector("p.light-block-distance") - ? item.querySelector("p.light-block-distance").innerText - : ""; - - const lightDecay = item.querySelector("p.light-block-decay") - ? item.querySelector("p.light-block-decay").innerText - : ""; - - const lightAngle = item.querySelector("p.light-block-angle") - ? item.querySelector("p.light-block-angle").innerText - : ""; - - const lightPenumbra = item.querySelector("p.light-block-penumbra") - ? item.querySelector("p.light-block-penumbra").innerText - : ""; - - return ( - - ); - })} - - {Object.values( - props.npcsToAdd - ).map((npc, index) => { - const modelPosX = - npc.querySelector( - "p.npc-block-position-x" - ) - ? npc.querySelector( - "p.npc-block-position-x" - ).innerText - : ""; - - const modelPosY = - npc.querySelector( - "p.npc-block-position-y" - ) - ? npc.querySelector( - "p.npc-block-position-y" - ).innerText - : ""; - - const modelPosZ = - npc.querySelector( - "p.npc-block-position-z" - ) - ? npc.querySelector( - "p.npc-block-position-z" - ).innerText - : ""; - - const modelRotationX = - npc.querySelector( - "p.npc-block-rotation-x" - ) - ? npc.querySelector( - "p.npc-block-rotation-x" - ).innerText - : ""; - - const modelRotationY = - npc.querySelector( - "p.npc-block-rotation-y" - ) - ? npc.querySelector( - "p.npc-block-rotation-y" - ).innerText - : ""; - - const modelRotationZ = - npc.querySelector( - "p.npc-block-rotation-z" - ) - ? npc.querySelector( - "p.npc-block-rotation-z" - ).innerText - : ""; - - const url = npc.querySelector( - "p.npc-block-url" - ) - ? npc.querySelector( - "p.npc-block-url" - ).innerText - : ""; - - const alt = npc.querySelector( - "p.npc-block-alt" - ) - ? npc.querySelector( - "p.npc-block-alt" - ).innerText - : ""; - - const personality = npc.querySelector( - "p.npc-block-personality" - ) - ? npc.querySelector( - "p.npc-block-personality" - ).innerText - : ""; - - const defaultMessage = npc.querySelector( - "p.npc-block-default-message" - ) - ? npc.querySelector( - "p.npc-block-default-message" - ).innerText - : ""; - - const name = npc.querySelector( - "p.npc-block-name" - ) - ? npc.querySelector( - "p.npc-block-name" - ).innerText - : ""; - - const objectAwareness = - npc.querySelector( - "p.npc-block-object-awareness" - ) - ? npc.querySelector( - "p.npc-block-object-awareness" - ).innerText - : false; - - return ( - - ); - })} - {Object.values( - props.modelsToAdd - ).map((model, index) => { - const modelPosX = - model.querySelector( - "p.model-block-position-x" - ) - ? model.querySelector( - "p.model-block-position-x" - ).innerText - : ""; - - const modelPosY = - model.querySelector( - "p.model-block-position-y" - ) - ? model.querySelector( - "p.model-block-position-y" - ).innerText - : ""; - - const modelPosZ = - model.querySelector( - "p.model-block-position-z" - ) - ? model.querySelector( - "p.model-block-position-z" - ).innerText - : ""; - - const modelScaleX = - model.querySelector( - "p.model-block-scale-x" - ) - ? model.querySelector( - "p.model-block-scale-x" - ).innerText - : ""; - - const modelScaleY = - model.querySelector( - "p.model-block-scale-y" - ) - ? model.querySelector( - "p.model-block-scale-y" - ).innerText - : ""; - - const modelScaleZ = - model.querySelector( - "p.model-block-scale-z" - ) - ? model.querySelector( - "p.model-block-scale-z" - ).innerText - : ""; - - const modelRotationX = - model.querySelector( - "p.model-block-rotation-x" - ) - ? model.querySelector( - "p.model-block-rotation-x" - ).innerText - : ""; - - const modelRotationY = - model.querySelector( - "p.model-block-rotation-y" - ) - ? model.querySelector( - "p.model-block-rotation-y" - ).innerText - : ""; - - const modelRotationZ = - model.querySelector( - "p.model-block-rotation-z" - ) - ? model.querySelector( - "p.model-block-rotation-z" - ).innerText - : ""; - - const url = model.querySelector( - "p.model-block-url" - ) - ? model.querySelector( - "p.model-block-url" - ).innerText - : ""; - - const animations = - model.querySelector( - "p.model-block-animations" - ) - ? model.querySelector( - "p.model-block-animations" - ).innerText - : ""; - - const alt = model.querySelector( - "p.model-block-alt" - ) - ? model.querySelector( - "p.model-block-alt" - ).innerText - : ""; - - if (!objectsInRoom.includes(alt)) { - setObjectsInRoom([...objectsInRoom, alt]); + ) + ? item.querySelector( + "p.video-block-autoplay" + ).innerText + : false; + + customModel = + item.querySelector( + "p.video-block-custom-model" + ) + ? item.querySelector( + "p.video-block-custom-model" + ).innerText + : false; + videoModelUrl = + item.querySelector( + "div.video-block-model-url" + ) + ? item.querySelector( + "div.video-block-model-url" + ).innerText + : ""; + videoControlsEnabled = true; } - - const collidable = - model.querySelector( - "p.model-block-collidable" - ) - ? model.querySelector( - "p.model-block-collidable" - ).innerText - : false; - return ( - - ); - })} - {Object.values(props.htmlToAdd).map( - (model, index) => { - const textContent = - model.querySelector( - "p.three-text-content" - ) - ? model.querySelector( - "p.three-text-content" - ).innerText + return ( + + ); + })} + {Object.values(props.audiosToAdd).map((item, index) => { + let audioPosX, audioPosY, audioPosZ, audioRotationX, audioRotationY, audioRotationZ; + let audioUrl, autoPlay, loop, volume, positional, coneInnerAngle, coneOuterAngle, coneOuterGain, distanceModel, maxDistance, refDistance, rolloffFactor; + + if (item.tagName.toLowerCase() === 'three-audio-block') { + audioPosX = item.getAttribute('positionX') || ''; + audioPosY = item.getAttribute('positionY') || ''; + audioPosZ = item.getAttribute('positionZ') || ''; + audioRotationX = item.getAttribute('rotationX') || ''; + audioRotationY = item.getAttribute('rotationY') || ''; + audioRotationZ = item.getAttribute('rotationZ') || ''; + audioUrl = item.getAttribute('audioUrl') || ''; + autoPlay = item.hasAttribute('autoplay') ? "1" : "0"; + loop = item.hasAttribute('loop') ? "1" : "0"; + volume = item.getAttribute('volume') || ''; + positional = item.hasAttribute('positional') ? "1" : "0"; + coneInnerAngle = item.getAttribute('coneInnerAngle') || ''; + coneOuterAngle = item.getAttribute('coneOuterAngle') || ''; + coneOuterGain = item.getAttribute('coneOuterGain') || ''; + distanceModel = item.getAttribute('distanceModel') || ''; + maxDistance = item.getAttribute('maxDistance') || ''; + refDistance = item.getAttribute('refDistance') || ''; + rolloffFactor = item.getAttribute('rolloffFactor') || ''; + } else { + audioPosX = item.querySelector("p.audio-block-positionX")?.innerText || ""; + audioPosY = item.querySelector("p.audio-block-positionY")?.innerText || ""; + audioPosZ = item.querySelector("p.audio-block-positionZ")?.innerText || ""; + audioRotationX = item.querySelector("p.audio-block-rotationX")?.innerText || ""; + audioRotationY = item.querySelector("p.audio-block-rotationY")?.innerText || ""; + audioRotationZ = item.querySelector("p.audio-block-rotationZ")?.innerText || ""; + audioUrl = item.querySelector("p.audio-block-url")?.innerText || ""; + autoPlay = item.querySelector("p.audio-block-autoPlay")?.innerText === "1" ? "1" : "0"; + loop = item.querySelector("p.audio-block-loop")?.innerText === "1" ? "1" : "0"; + volume = item.querySelector("p.audio-block-volume")?.innerText || ""; + positional = item.querySelector("p.audio-block-positional")?.innerText === "1" ? "1" : "0"; + coneInnerAngle = item.querySelector("p.audio-block-coneInnerAngle")?.innerText || ""; + coneOuterAngle = item.querySelector("p.audio-block-coneOuterAngle")?.innerText || ""; + coneOuterGain = item.querySelector("p.audio-block-coneOuterGain")?.innerText || ""; + distanceModel = item.querySelector("p.audio-block-distanceModel")?.innerText || ""; + maxDistance = item.querySelector("p.audio-block-maxDistance")?.innerText || ""; + refDistance = item.querySelector("p.audio-block-refDistance")?.innerText || ""; + rolloffFactor = item.querySelector("p.audio-block-rolloffFactor")?.innerText || ""; + } + + return ( + { + setLoadedAudios(prev => [...prev, loadedAudio]); + }} + /> + ); + })} + + + {props.lightsToAdd.length < 1 && ( + <> + + + + )} + {Object.values(props.lightsToAdd).map((item, index) => { + let lightPosX, lightPosY, lightPosZ, lightRotationX, lightRotationY, lightRotationZ; + let lightType, lightColor, lightItensity, lightDistance, lightDecay, lightAngle, lightPenumbra; + let targetX, targetY, targetZ = 0; + if (item.tagName.toLowerCase() === 'three-light-block') { + lightPosX = item.getAttribute('positionX') || ''; + lightPosY = item.getAttribute('positionY') || ''; + lightPosZ = item.getAttribute('positionZ') || ''; + lightRotationX = item.getAttribute('rotationX') || ''; + lightRotationY = item.getAttribute('rotationY') || ''; + lightRotationZ = item.getAttribute('rotationZ') || ''; + lightType = item.getAttribute('type') || 'ambient'; + lightColor = item.getAttribute('color') || ''; + lightItensity = item.getAttribute('intensity') || ''; + lightDistance = item.getAttribute('distance') || ''; + lightDecay = item.getAttribute('decay') || ''; + lightAngle = item.getAttribute('angle') || ''; + lightPenumbra = item.getAttribute('penumbra') || ''; + } else { + lightPosX = item.querySelector("p.light-block-positionX") + ? item.querySelector("p.light-block-positionX").innerText : ""; - const rotationX = - model.querySelector( - "p.three-text-rotationX" - ) - ? model.querySelector( - "p.three-text-rotationX" - ).innerText + + lightPosY = item.querySelector("p.light-block-positionY") + ? item.querySelector("p.light-block-positionY").innerText : ""; - const rotationY = - model.querySelector( - "p.three-text-rotationY" - ) - ? model.querySelector( - "p.three-text-rotationY" - ).innerText + + lightPosZ = item.querySelector("p.light-block-positionZ") + ? item.querySelector("p.light-block-positionZ").innerText : ""; - const rotationZ = - model.querySelector( - "p.three-text-rotationZ" - ) - ? model.querySelector( - "p.three-text-rotationZ" - ).innerText + + lightRotationX = item.querySelector("p.light-block-rotationX") + ? item.querySelector("p.light-block-rotationX").innerText : ""; - const positionX = - model.querySelector( - "p.three-text-positionX" + + lightRotationY = item.querySelector("p.light-block-rotationY") + ? item.querySelector("p.light-block-rotationY").innerText + : ""; + + lightRotationZ = item.querySelector("p.light-block-rotationZ") + ? item.querySelector("p.light-block-rotationZ").innerText + : ""; + + lightType = item.querySelector("p.light-block-type") + ? item.querySelector("p.light-block-type").innerText + : "ambient"; + + lightColor = item.querySelector("p.light-block-color") + ? item.querySelector("p.light-block-color").innerText + : ""; + + lightItensity = item.querySelector("p.light-block-intensity") + ? item.querySelector("p.light-block-intensity").innerText + : ""; + + lightDistance = item.querySelector("p.light-block-distance") + ? item.querySelector("p.light-block-distance").innerText + : ""; + + lightDecay = item.querySelector("p.light-block-decay") + ? item.querySelector("p.light-block-decay").innerText + : ""; + + lightAngle = item.querySelector("p.light-block-angle") + ? item.querySelector("p.light-block-angle").innerText + : ""; + + lightPenumbra = item.querySelector("p.light-block-penumbra") + ? item.querySelector("p.light-block-penumbra").innerText + : ""; + } + + return ( + + ); + })} + + {Object.values( + props.npcsToAdd + ).map((npc, index) => { + let url, modelPosX, modelPosY, modelPosZ, modelRotationX, modelRotationY, modelRotationZ, name, alt, defaultMessage, personality, objectAwareness; + if (npc.tagName.toLowerCase() === 'three-npc-block') { + url = npc.getAttribute('threeObjectUrl') || ''; + modelPosX = npc.getAttribute('positionX') || ''; + modelPosY = npc.getAttribute('positionY') || ''; + modelPosZ = npc.getAttribute('positionZ') || ''; + modelRotationX = npc.getAttribute('rotationX') || ''; + modelRotationY = npc.getAttribute('rotationY') || ''; + modelRotationZ = npc.getAttribute('rotationZ') || ''; + name = npc.getAttribute('name') || ''; + defaultMessage = npc.getAttribute('defaultMessage') || ''; + personality = npc.getAttribute('personality') || ''; + objectAwareness = npc.getAttribute('objectAwareness') || false; + } else { + modelPosX = + npc.querySelector( + "p.npc-block-position-x" + ) + ? npc.querySelector( + "p.npc-block-position-x" + ).innerText + : ""; + + modelPosY = + npc.querySelector( + "p.npc-block-position-y" + ) + ? npc.querySelector( + "p.npc-block-position-y" + ).innerText + : ""; + + modelPosZ = + npc.querySelector( + "p.npc-block-position-z" + ) + ? npc.querySelector( + "p.npc-block-position-z" + ).innerText + : ""; + + modelRotationX = + npc.querySelector( + "p.npc-block-rotation-x" + ) + ? npc.querySelector( + "p.npc-block-rotation-x" + ).innerText + : ""; + + modelRotationY = + npc.querySelector( + "p.npc-block-rotation-y" + ) + ? npc.querySelector( + "p.npc-block-rotation-y" + ).innerText + : ""; + + modelRotationZ = + npc.querySelector( + "p.npc-block-rotation-z" + ) + ? npc.querySelector( + "p.npc-block-rotation-z" + ).innerText + : ""; + + url = npc.querySelector( + "p.npc-block-url" ) - ? model.querySelector( - "p.three-text-positionX" + ? npc.querySelector( + "p.npc-block-url" ).innerText : ""; - const positionY = - model.querySelector( - "p.three-text-positionY" + + alt = npc.querySelector( + "p.npc-block-alt" ) - ? model.querySelector( - "p.three-text-positionY" + ? npc.querySelector( + "p.npc-block-alt" ).innerText : ""; - const positionZ = - model.querySelector( - "p.three-text-positionZ" + + personality = npc.querySelector( + "p.npc-block-personality" ) - ? model.querySelector( - "p.three-text-positionZ" + ? npc.querySelector( + "p.npc-block-personality" ).innerText : ""; - const scaleX = - model.querySelector( - "p.three-text-scaleX" + + defaultMessage = npc.querySelector( + "p.npc-block-default-message" ) - ? model.querySelector( - "p.three-text-scaleX" + ? npc.querySelector( + "p.npc-block-default-message" ).innerText : ""; - const scaleY = - model.querySelector( - "p.three-text-scaleY" + + name = npc.querySelector( + "p.npc-block-name" ) - ? model.querySelector( - "p.three-text-scaleY" + ? npc.querySelector( + "p.npc-block-name" ).innerText : ""; - const scaleZ = - model.querySelector( - "p.three-text-scaleZ" + + objectAwareness = + npc.querySelector( + "p.npc-block-object-awareness" + ) + ? npc.querySelector( + "p.npc-block-object-awareness" + ).innerText + : false; + } + + return ( + + ); + })} + {Object.values( + props.modelsToAdd + ).map((model, index) => { + let modelPosX, modelPosY, modelPosZ, modelScaleX, modelScaleY, modelScaleZ; + let modelRotationX, modelRotationY, modelRotationZ, url, animations, alt, collidable; + if (model.tagName.toLowerCase() === 'three-model-block') { + modelPosX = model.getAttribute('positionX') || ''; + modelPosY = model.getAttribute('positionY') || ''; + modelPosZ = model.getAttribute('positionZ') || ''; + modelScaleX = model.getAttribute('scaleX') || ''; + modelScaleY = model.getAttribute('scaleY') || ''; + modelScaleZ = model.getAttribute('scaleZ') || ''; + modelRotationX = model.getAttribute('rotationX') || ''; + modelRotationY = model.getAttribute('rotationY') || ''; + modelRotationZ = model.getAttribute('rotationZ') || ''; + url = model.getAttribute('threeObjectUrl') || ''; + animations = model.getAttribute('animations') || ''; + alt = model.getAttribute('alt') || ''; + if (!objectsInRoom.includes(alt)) { + setObjectsInRoom([...objectsInRoom, alt]); + } + collidable = model.getAttribute('collidable'); + } else { + modelPosX = + model.querySelector( + "p.model-block-position-x" + ) + ? model.querySelector( + "p.model-block-position-x" + ).innerText + : ""; + + modelPosY = + model.querySelector( + "p.model-block-position-y" + ) + ? model.querySelector( + "p.model-block-position-y" + ).innerText + : ""; + + modelPosZ = + model.querySelector( + "p.model-block-position-z" + ) + ? model.querySelector( + "p.model-block-position-z" + ).innerText + : ""; + + modelScaleX = + model.querySelector( + "p.model-block-scale-x" + ) + ? model.querySelector( + "p.model-block-scale-x" + ).innerText + : ""; + + modelScaleY = + model.querySelector( + "p.model-block-scale-y" + ) + ? model.querySelector( + "p.model-block-scale-y" + ).innerText + : ""; + + modelScaleZ = + model.querySelector( + "p.model-block-scale-z" + ) + ? model.querySelector( + "p.model-block-scale-z" + ).innerText + : ""; + + modelRotationX = + model.querySelector( + "p.model-block-rotation-x" + ) + ? model.querySelector( + "p.model-block-rotation-x" + ).innerText + : ""; + + modelRotationY = + model.querySelector( + "p.model-block-rotation-y" + ) + ? model.querySelector( + "p.model-block-rotation-y" + ).innerText + : ""; + + modelRotationZ = + model.querySelector( + "p.model-block-rotation-z" + ) + ? model.querySelector( + "p.model-block-rotation-z" + ).innerText + : ""; + + url = model.querySelector( + "p.model-block-url" ) ? model.querySelector( - "p.three-text-scaleZ" + "p.model-block-url" ).innerText : ""; - const textColor = - model.querySelector( - "p.three-text-color" + animations = + model.querySelector( + "p.model-block-animations" + ) + ? model.querySelector( + "p.model-block-animations" + ).innerText + : ""; + + alt = model.querySelector( + "p.model-block-alt" ) ? model.querySelector( - "p.three-text-color" + "p.model-block-alt" ).innerText : ""; + if (!objectsInRoom.includes(alt)) { + setObjectsInRoom([...objectsInRoom, alt]); + } + + collidable = + model.querySelector( + "p.model-block-collidable" + ) + ? model.querySelector( + "p.model-block-collidable" + ).innerText + : false; + } + // log all of the vars above return ( - + ); + })} + {Object.values(props.textToAdd).map( + (model, index) => { + let textContent, rotationX, rotationY, rotationZ, positionX, positionY, positionZ, scaleX, scaleY, scaleZ, textColor; + + if (model.tagName.toLowerCase() === 'three-text-block') { + textContent = model.getAttribute('textContent') || ''; + rotationX = model.getAttribute('rotationX') || ''; + rotationY = model.getAttribute('rotationY') || ''; + rotationZ = model.getAttribute('rotationZ') || ''; + positionX = model.getAttribute('positionX') || ''; + positionY = model.getAttribute('positionY') || ''; + positionZ = model.getAttribute('positionZ') || ''; + scaleX = model.getAttribute('scaleX') || ''; + scaleY = model.getAttribute('scaleY') || ''; + scaleZ = model.getAttribute('scaleZ') || ''; + textColor = model.getAttribute('textColor') || ''; + } else { + textContent = + model.querySelector( + "p.three-text-content" + ) + ? model.querySelector( + "p.three-text-content" + ).innerText + : ""; + rotationX = + model.querySelector( + "p.three-text-rotationX" + ) + ? model.querySelector( + "p.three-text-rotationX" + ).innerText + : ""; + rotationY = + model.querySelector( + "p.three-text-rotationY" + ) + ? model.querySelector( + "p.three-text-rotationY" + ).innerText + : ""; + rotationZ = + model.querySelector( + "p.three-text-rotationZ" + ) + ? model.querySelector( + "p.three-text-rotationZ" + ).innerText + : ""; + positionX = + model.querySelector( + "p.three-text-positionX" + ) + ? model.querySelector( + "p.three-text-positionX" + ).innerText + : ""; + positionY = + model.querySelector( + "p.three-text-positionY" + ) + ? model.querySelector( + "p.three-text-positionY" + ).innerText + : ""; + positionZ = + model.querySelector( + "p.three-text-positionZ" + ) + ? model.querySelector( + "p.three-text-positionZ" + ).innerText + : ""; + scaleX = + model.querySelector( + "p.three-text-scaleX" + ) + ? model.querySelector( + "p.three-text-scaleX" + ).innerText + : ""; + scaleY = + model.querySelector( + "p.three-text-scaleY" + ) + ? model.querySelector( + "p.three-text-scaleY" + ).innerText + : ""; + scaleZ = + model.querySelector( + "p.three-text-scaleZ" + ) + ? model.querySelector( + "p.three-text-scaleZ" + ).innerText + : ""; + + textColor = + model.querySelector( + "p.three-text-color" + ) + ? model.querySelector( + "p.three-text-color" + ).innerText + : ""; + } + + return ( + + ); + } + )} + {Object.values( + props.portalsToAdd + ).map((model, index) => { + let modelPosX, modelPosY, modelPosZ, modelScaleX, modelScaleY, modelScaleZ; + let modelRotationX, modelRotationY, modelRotationZ, url, destinationUrl, animations, label, labelOffsetX, labelOffsetY, labelOffsetZ, labelTextColor; + if (model.tagName.toLowerCase() === 'three-portal-block') { + modelPosX = model.getAttribute('positionX') || ''; + modelPosY = model.getAttribute('positionY') || ''; + modelPosZ = model.getAttribute('positionZ') || ''; + modelScaleX = model.getAttribute('scaleX') || ''; + modelScaleY = model.getAttribute('scaleY') || ''; + modelScaleZ = model.getAttribute('scaleZ') || ''; + modelRotationX = model.getAttribute('rotationX') || ''; + modelRotationY = model.getAttribute('rotationY') || ''; + modelRotationZ = model.getAttribute('rotationZ') || ''; + url = model.getAttribute('threeObjectUrl') || ''; + destinationUrl = model.getAttribute('destinationUrl') || ''; + animations = model.getAttribute('animations') || ''; + label = model.getAttribute('label') || ''; + labelOffsetX = model.getAttribute('labelOffsetX') || ''; + labelOffsetY = model.getAttribute('labelOffsetY') || ''; + labelOffsetZ = model.getAttribute('labelOffsetZ') || ''; + labelTextColor = model.getAttribute('labelTextColor') || ''; + } else { + modelPosX = + model.querySelector( + "p.three-portal-block-position-x" + ) + ? model.querySelector( + "p.three-portal-block-position-x" + ).innerText + : ""; + + modelPosY = + model.querySelector( + "p.three-portal-block-position-y" + ) + ? model.querySelector( + "p.three-portal-block-position-y" + ).innerText + : ""; + + modelPosZ = + model.querySelector( + "p.three-portal-block-position-z" + ) + ? model.querySelector( + "p.three-portal-block-position-z" + ).innerText + : ""; + + modelScaleX = + model.querySelector( + "p.three-portal-block-scale-x" + ) + ? model.querySelector( + "p.three-portal-block-scale-x" + ).innerText + : ""; + + modelScaleY = + model.querySelector( + "p.three-portal-block-scale-y" + ) + ? model.querySelector( + "p.three-portal-block-scale-y" + ).innerText + : ""; + + modelScaleZ = + model.querySelector( + "p.three-portal-block-scale-z" + ) + ? model.querySelector( + "p.three-portal-block-scale-z" + ).innerText + : ""; + + modelRotationX = + model.querySelector( + "p.three-portal-block-rotation-x" + ) + ? model.querySelector( + "p.three-portal-block-rotation-x" + ).innerText + : ""; + + modelRotationY = + model.querySelector( + "p.three-portal-block-rotation-y" + ) + ? model.querySelector( + "p.three-portal-block-rotation-y" + ).innerText + : ""; + + modelRotationZ = + model.querySelector( + "p.three-portal-block-rotation-z" + ) + ? model.querySelector( + "p.three-portal-block-rotation-z" + ).innerText + : ""; + + url = model.querySelector( + "p.three-portal-block-url" + ) + ? model.querySelector( + "p.three-portal-block-url" + ).innerText + : ""; + + destinationUrl = + model.querySelector( + "p.three-portal-block-destination-url" + ) + ? model.querySelector( + "p.three-portal-block-destination-url" + ).innerText + : ""; + + animations = + model.querySelector( + "p.three-portal-block-animations" + ) + ? model.querySelector( + "p.three-portal-block-animations" + ).innerText + : ""; + + label = + model.querySelector( + "p.three-portal-block-label" + ) + ? model.querySelector( + "p.three-portal-block-label" + ).innerText + : ""; + + labelOffsetX = + model.querySelector( + "p.three-portal-block-label-offset-x" + ) + ? model.querySelector( + "p.three-portal-block-label-offset-x" + ).innerText + : ""; + + labelOffsetY = + model.querySelector( + "p.three-portal-block-label-offset-y" + ) + ? model.querySelector( + "p.three-portal-block-label-offset-y" + ).innerText + : ""; + + labelOffsetZ = + model.querySelector( + "p.three-portal-block-label-offset-z" + ) + ? model.querySelector( + "p.three-portal-block-label-offset-z" + ).innerText + : ""; + labelTextColor = + model.querySelector( + "p.three-portal-block-label-text-color" + ) + ? model.querySelector( + "p.three-portal-block-label-text-color" + ).innerText + : ""; + } + + return ( + ); - } - )} - {Object.values( - props.portalsToAdd - ).map((model, index) => { - const modelPosX = - model.querySelector( - "p.three-portal-block-position-x" - ) - ? model.querySelector( - "p.three-portal-block-position-x" - ).innerText - : ""; - - const modelPosY = - model.querySelector( - "p.three-portal-block-position-y" - ) - ? model.querySelector( - "p.three-portal-block-position-y" - ).innerText - : ""; - - const modelPosZ = - model.querySelector( - "p.three-portal-block-position-z" - ) - ? model.querySelector( - "p.three-portal-block-position-z" - ).innerText - : ""; - - const modelScaleX = - model.querySelector( - "p.three-portal-block-scale-x" - ) - ? model.querySelector( - "p.three-portal-block-scale-x" - ).innerText - : ""; - - const modelScaleY = - model.querySelector( - "p.three-portal-block-scale-y" - ) - ? model.querySelector( - "p.three-portal-block-scale-y" - ).innerText - : ""; - - const modelScaleZ = - model.querySelector( - "p.three-portal-block-scale-z" - ) - ? model.querySelector( - "p.three-portal-block-scale-z" - ).innerText - : ""; - - const modelRotationX = - model.querySelector( - "p.three-portal-block-rotation-x" - ) - ? model.querySelector( - "p.three-portal-block-rotation-x" - ).innerText - : ""; - - const modelRotationY = - model.querySelector( - "p.three-portal-block-rotation-y" - ) - ? model.querySelector( - "p.three-portal-block-rotation-y" - ).innerText - : ""; - - const modelRotationZ = - model.querySelector( - "p.three-portal-block-rotation-z" - ) - ? model.querySelector( - "p.three-portal-block-rotation-z" - ).innerText - : ""; - - const url = model.querySelector( - "p.three-portal-block-url" - ) - ? model.querySelector( - "p.three-portal-block-url" - ).innerText - : ""; - - const destinationUrl = - model.querySelector( - "p.three-portal-block-destination-url" - ) - ? model.querySelector( - "p.three-portal-block-destination-url" - ).innerText - : ""; - - const animations = - model.querySelector( - "p.three-portal-block-animations" - ) - ? model.querySelector( - "p.three-portal-block-animations" - ).innerText - : ""; - - const label = - model.querySelector( - "p.three-portal-block-label" - ) - ? model.querySelector( - "p.three-portal-block-label" - ).innerText - : ""; - - const labelOffsetX = - model.querySelector( - "p.three-portal-block-label-offset-x" - ) - ? model.querySelector( - "p.three-portal-block-label-offset-x" - ).innerText - : ""; - - const labelOffsetY = - model.querySelector( - "p.three-portal-block-label-offset-y" - ) - ? model.querySelector( - "p.three-portal-block-label-offset-y" - ).innerText - : ""; - - const labelOffsetZ = - model.querySelector( - "p.three-portal-block-label-offset-z" - ) - ? model.querySelector( - "p.three-portal-block-label-offset-z" - ).innerText - : ""; - const labelTextColor = - model.querySelector( - "p.three-portal-block-label-text-color" - ) - ? model.querySelector( - "p.three-portal-block-label-text-color" - ).innerText - : ""; - - return ( - - ); - })} - - - )} - - - {/* + + )} + + + {/* */} - + + {Object.values( props.npcsToAdd ).map((npc, index) => { - - const personality = npc.querySelector( - "p.npc-block-personality" - ) - ? npc.querySelector( - "p.npc-block-personality" - ).innerText - : ""; - const defaultMessage = npc.querySelector( - "p.npc-block-default-message" - ) - ? npc.querySelector( - "p.npc-block-default-message" - ).innerText - : ""; - - const objectAwareness = npc.querySelector( - "p.npc-block-object-awareness" - ) - ? npc.querySelector( - "p.npc-block-object-awareness" - ).innerText - : ""; - - const name = npc.querySelector( - "p.npc-block-name" - ) - ? npc.querySelector( - "p.npc-block-name" - ).innerText - : ""; - - return ( - - ) + ) })} - <> - { isMobile() && ( - { - if(data.force > 1.5){ - movement.current.shift = true; - } else { - movement.current.shift = false; - } - if(data.direction && data.direction.angle){ - if(data.direction.angle === "up" && ! movement.current.forward){ - movement.current.forward = true; - movement.current.backward = false; - movement.current.left = false; - movement.current.right = false; - } else if(data.direction.angle === "down" && ! movement.current.backward){ - movement.current.forward = false; - movement.current.backward = true; - movement.current.left = false; - movement.current.right = false; - } else if(data.direction.angle === "left" && ! movement.current.left){ - movement.current.forward = false; - movement.current.backward = false; - movement.current.left = true; - movement.current.right = false; - } else if(data.direction.angle === "right" && ! movement.current.right){ - movement.current.forward = false; - movement.current.backward = false; - movement.current.left = false; - movement.current.right = true; - } - } - }} - onEnd={( evt, data ) => { - movement.current.forward = false; - movement.current.backward = false; - movement.current.left = false; - movement.current.right = false; - }} - /> - /* { - console.log(data.direction.angle); - }} - // onEnd={(evt, data) => setMobileRotControls(null)} - /> */ - ) } + <> + {isMobile() && ( + + )} ); @@ -2049,24 +2292,68 @@ export default function EnvironmentFront(props) { backgroundColor: props.backgroundColor, backgroundImage: `url(${props.previewImage})`, backgroundPosition: "center", + backgroundSize: "cover", margin: "0", - height: "900px", + height: "100vh", width: "100%", padding: "0", alignItems: "center", - justifyContent: "center" + justifyContent: "center", + display: "flex", }} >
+
+
+ {/* Display Name */} + {(props.networkingBlock.length > 0) ? ( + <> + setDisplayName(e.target.value)} /> + {(props.networkingBlock[0].attributes.customAvatars && props.networkingBlock[0].attributes.customAvatars.value === "1") && ( +
+ VRM or Sprite URL + { + e.preventDefault(); + setPlayerAvatar(e.dataTransfer.getData('text')); + }} + value={playerAvatar} + onChange={(e) => setPlayerAvatar(e.target.value)} + /> +
+ )} + + + ) : ( +
+ VRM or Sprite URL + setPlayerAvatar(e.target.value)} /> +
+ )} +
+ {(props.networkingBlock.length > 0) && ( +
+

After entering, use the "Join Voice" button to select your microphone.

+
+ )}
); diff --git a/blocks/environment/components/Networking.js b/blocks/environment/components/Networking.js index abde1d4..bfec508 100644 --- a/blocks/environment/components/Networking.js +++ b/blocks/environment/components/Networking.js @@ -1,127 +1,744 @@ import P2PCF from "./p2pcf/p2pcf.js"; +import React, { useEffect, useMemo, useState } from "react"; +import audioIcon from '../../../inc/assets/mic_icon.png'; +import audioIconMute from '../../../inc/assets/mic_icon_mute.png'; +import participants from '../../../inc/assets/participants.png'; +import worldIcon from '../../../inc/assets/world_icon.png'; +import cornerAccent from '../../../inc/assets/corner_accent.png'; +import settingsIcon from '../../../inc/assets/settings_icon.png'; +import { color } from "@wordpress/icons"; +import { XRButton } from "@react-three/xr"; -const Networking = (props) => { - if (!document.location.hash) { - document.location = - document.location.toString() + `#xpp-${props.postSlug}`; +const DEFAULT_TURN_ICE = [ + { + urls: "turn:openrelay.metered.ca:80", + username: "openrelayproject", + credential: "openrelayproject" + }, + { + urls: "turn:openrelay.metered.ca:443", + username: "openrelayproject", + credential: "openrelayproject" + }, + { + urls: "turn:openrelay.metered.ca:443?transport=tcp", + username: "openrelayproject", + credential: "openrelayproject" } +]; + +const generateRoomId = (sharedRoomID) => { + const domainName = window.location.hostname; + return `${domainName}-${sharedRoomID}`; + }; + + + async function fetchTURNcredentials() { + const endpoint = turnCredentials['apiUrl']; + const nonce = turnCredentials['nonce']; + + try { + // Fetch TURN credentials from the WordPress endpoint + const response = await fetch(endpoint, { + method: 'GET', + headers: { + 'X-WP-Nonce': nonce, + 'Content-Type': 'application/json' + } + }); + + if (!response.ok) { + throw new Error('Failed to fetch TURN credentials'); + } + + const data = await response.json(); + const turnUrls = data; + + return turnUrls; + } catch (error) { + console.error('Failed to fetch TURN credentials. Using defaults.', error); + return DEFAULT_TURN_ICE; + } +} + +const Networking = (props) => { + let isNetworkActivated = props.networkingBlock.length > 0; + let isOffline = false; + let isMuted = false; // Initial state of the microphone + let localStream = null; // To hold the local media stream + const [p2pcf, setP2pcf] = useState(null); + const returnXRButton = (props) => { + return ; + }; + + const RoomDropdownContent = () => { + let dropdown = document.getElementById("room-dropdown"); + // empty the contents of the dropdown + dropdown.innerHTML = ""; + dropdown.innerText = "Room: " + window.p2pcf.roomId; + + // create a paragraph element to be added after the dropdown + let roomParagraph = document.createElement("p"); + roomParagraph.innerText = "Description: "; + roomParagraph.style.marginTop = "10px"; + roomParagraph.style.marginBottom = "10px"; + roomParagraph.style.textAlign = "left"; + + dropdown.appendChild(roomParagraph); + + dropdown.style.display = dropdown.style.display === "none" ? "block" : "none"; + }; + + const AudioDropdownContent = async (button) => { + let dropdown = document.getElementById("room-dropdown"); + // empty the contents of the dropdown + dropdown.innerHTML = ""; + // add a h3 heading that says "Select Microphone" + let heading = document.createElement("h4"); + heading.innerText = "Select Microphone"; + heading.style.marginTop = "10px"; + heading.style.marginBottom = "10px"; + heading.style.textAlign = "left"; + heading.style.fontSize = "0.7em"; + heading.style.textAlign = "left"; + heading.style.fontWeight = "600"; + heading.style.color = "white"; + heading.style.paddingLeft = "5px"; + heading.style.fontWeight = "600"; + heading.style.fontFamily = "Arial"; + dropdown.appendChild(heading); + // make the inner text of the dropdown a list of the users in the room + // loop with index for each peer + // add a select toggle and a button to change the microphone device + let select = document.createElement("select"); + select.id = "audio-select"; + select.style.marginTop = "10px"; + select.style.marginBottom = "10px"; + select.style.textAlign = "left"; + select.style.fontSize = "0.6em"; + select.style.width = "100%"; + select.style.height = "30px"; + select.style.borderRadius = "5px"; + select.style.cursor = "pointer"; + select.style.backgroundColor = "white"; + select.style.color = "black"; + select.style.padding = "5px"; + select.style.marginBottom = "10px"; + select.style.marginTop = "10px"; + select.style.marginLeft = "0px"; + select.style.marginRight = "0px"; + select.style.border = "solid 1px #959595"; + select.style.boxSizing = "border-box"; + // populate the select with the available audio devices + navigator.mediaDevices.enumerateDevices().then(function(devices) { + devices.forEach(function(device) { + if (device.kind === 'audioinput') { + let option = document.createElement("option"); + option.value = device.deviceId; + option.text = device.label; + select.appendChild(option); + } + }); + }); + dropdown.appendChild(select); + // create a button for submitting the audio device change + let submit = document.createElement("button"); + submit.innerText = "Join"; + submit.style.marginTop = "10px"; + submit.style.marginBottom = "10px"; + submit.style.textAlign = "center"; + submit.style.fontSize = "0.6em"; + submit.style.fontWeight = "600"; + submit.style.height = "35px"; + submit.style.borderRadius = "15px"; + submit.style.backgroundColor = "white"; + submit.style.color = "black"; + submit.style.width = "55px"; + submit.style.padding = "10px"; + submit.style.marginBottom = "10px"; + submit.style.marginTop = "10px"; + submit.style.marginLeft = "0px"; + submit.style.marginRight = "0px"; + submit.style.border = "solid 1px #959595"; + submit.style.cursor = "pointer"; + submit.style.boxSizing = "border-box"; + submit.addEventListener("click", async (event) => { + // get the selected audio device + let audioSelect = document.getElementById("audio-select"); + let audioDevice = audioSelect.options[audioSelect.selectedIndex].value; + // set the audio device + navigator.mediaDevices.getUserMedia({ audio: { deviceId: audioDevice } }).then(function(stream) { + // set the local stream to the new stream + localStream = stream; + // set a window variable for the local stream + window.localStream = stream; + // loop through the peers and set their streams to the new stream + for (const peer of window.p2pcf.peers.values()) { + peer.addStream(stream); + } + }); + // getUserMedia(); // Initialize media stream + + stream = await navigator.mediaDevices.getUserMedia({ + audio: true + }); + + // for (const peer of p2pcf.peers.values()) { + // peer.addStream(stream); + // } + var audioJoin = button.target.parentNode; + audioJoin.style.display = "none"; + dropdown.style.display = dropdown.style.display === "none" ? "block" : "none"; + var muteIcon = document.createElement("button"); + muteIcon.style.backgroundImage = `url(${audioIcon})`; + muteIcon.style.backgroundSize = "cover"; + muteIcon.id = "mute-icon"; + muteIcon.style.width = "40px"; + muteIcon.style.height = "40px"; + muteIcon.style.padding = "10px"; + muteIcon.style.marginTop = "3px"; + muteIcon.style.marginRight = "5px"; + muteIcon.style.boxSizing = "border-box"; + muteIcon.style.borderRadius = "50%"; + muteIcon.style.backgroundPosition = "center"; + muteIcon.style.backgroundRepeat = "no-repeat"; + muteIcon.style.backgroundColor = "#FFFFFF"; + muteIcon.style.border = "solid 1px #959595"; + muteIcon.style.backgroundSize = "30px"; + muteIcon.addEventListener("click", (event) => { + // console.log("mute", event); + // stream.getAudioTracks()[0].enabled = !stream.getAudioTracks()[0] + // .enabled; + onMuteButtonPressed(stream); + }); + var settingsIconElement = document.createElement("button"); + settingsIconElement.style.backgroundImage = `url(${settingsIcon})`; + settingsIconElement.style.backgroundSize = "cover"; + settingsIconElement.id = "mute-icon"; + settingsIconElement.style.width = "40px"; + settingsIconElement.style.height = "40px"; + settingsIconElement.style.padding = "10px"; + settingsIconElement.style.marginTop = "3px"; + settingsIconElement.style.marginRight = "5px"; + settingsIconElement.style.boxSizing = "border-box"; + settingsIconElement.style.borderRadius = "50%"; + settingsIconElement.style.backgroundPosition = "center"; + settingsIconElement.style.backgroundRepeat = "no-repeat"; + settingsIconElement.style.backgroundColor = "#FFFFFF"; + settingsIconElement.style.border = "solid 1px #959595"; + settingsIconElement.style.backgroundSize = "30px"; + settingsIconElement.addEventListener("click", (event) => { + // console.log("mute", event); + // stream.getAudioTracks()[0].enabled = !stream.getAudioTracks()[0] + // .enabled; + SettingsDopdownContent() + }); + + //append to the audio button + if(audioJoin.parentNode){ + audioJoin.parentNode.appendChild(muteIcon); + audioJoin.parentNode.appendChild(settingsIconElement); + // remove the audioJoin button + //audioJoin.remove(); + toggleMute(stream); + } + + }); + dropdown.appendChild(submit); + + dropdown.style.display = dropdown.style.display === "none" ? "block" : "none"; + + }; - const userProfileName = + const SettingsDopdownContent = async (button) => { + + let dropdown = document.getElementById("room-dropdown"); + // empty the contents of the dropdown + dropdown.innerHTML = ""; + // add a h3 heading that says "Select Microphone" + let heading = document.createElement("h4"); + heading.innerText = "Select Microphone"; + heading.style.marginTop = "10px"; + heading.style.marginBottom = "10px"; + heading.style.textAlign = "left"; + heading.style.fontSize = "0.7em"; + heading.style.textAlign = "left"; + heading.style.fontWeight = "600"; + heading.style.color = "white"; + heading.style.paddingLeft = "5px"; + heading.style.fontWeight = "600"; + heading.style.fontFamily = "Arial"; + dropdown.appendChild(heading); + // make the inner text of the dropdown a list of the users in the room + // loop with index for each peer + // add a select toggle and a button to change the microphone device + let select = document.createElement("select"); + select.id = "audio-select"; + select.style.marginTop = "10px"; + select.style.marginBottom = "10px"; + select.style.textAlign = "left"; + select.style.fontSize = "0.6em"; + select.style.width = "100%"; + select.style.height = "30px"; + select.style.borderRadius = "5px"; + select.style.cursor = "pointer"; + select.style.backgroundColor = "white"; + select.style.color = "black"; + select.style.padding = "5px"; + select.style.marginBottom = "10px"; + select.style.marginTop = "10px"; + select.style.marginLeft = "0px"; + select.style.marginRight = "0px"; + select.style.border = "solid 1px #959595"; + select.style.boxSizing = "border-box"; + // populate the select with the available audio devices + navigator.mediaDevices.enumerateDevices().then(function(devices) { + devices.forEach(function(device) { + if (device.kind === 'audioinput') { + let option = document.createElement("option"); + option.value = device.deviceId; + option.text = device.label; + select.appendChild(option); + } + }); + }); + dropdown.appendChild(select); + // create a button for submitting the audio device change + let submit = document.createElement("button"); + submit.innerText = "Switch"; + submit.style.marginTop = "10px"; + submit.style.marginBottom = "10px"; + submit.style.textAlign = "center"; + submit.style.fontSize = "0.6em"; + submit.style.fontWeight = "600"; + submit.style.height = "35px"; + submit.style.borderRadius = "15px"; + submit.style.backgroundColor = "white"; + submit.style.color = "black"; + submit.style.width = "55px"; + submit.style.padding = "10px"; + submit.style.marginBottom = "10px"; + submit.style.marginTop = "10px"; + submit.style.marginLeft = "0px"; + submit.style.marginRight = "0px"; + submit.style.border = "solid 1px #959595"; + submit.style.cursor = "pointer"; + submit.style.boxSizing = "border-box"; + submit.addEventListener("click", async (event) => { + // get the selected audio device + let audioSelect = document.getElementById("audio-select"); + let audioDevice = audioSelect.options[audioSelect.selectedIndex].value; + // set the audio device + navigator.mediaDevices.getUserMedia({ audio: { deviceId: audioDevice } }).then(function(stream) { + // loop through the peers and set their streams to the new stream + for (const peer of p2pcf.peers.values()) { + peer.removeStream(localStream); + peer.addStream(stream); + } + // set the local stream to the new stream + localStream = stream; + }); + getUserMedia(); // Initialize media stream + + stream = await navigator.mediaDevices.getUserMedia({ + audio: true + }); + for (const peer of p2pcf.peers.values()) { + peer.addStream(stream); + } + }); + dropdown.appendChild(submit); + + dropdown.style.display = dropdown.style.display === "none" ? "block" : "none"; + + }; + + + const PeerDropdownContent = () => { + const userProfileName = userData.userId === "" ? Math.floor(Math.random() * 100000) : userData.userId; - const p2pcf = new P2PCF( - "user-" + userProfileName, - document.location.hash.substring(1), - { - workerUrl: "https://p2pcf.sxpdigital.workers.dev/", - slowPollingRateMs: 5000, - fastPollingRateMs: 1500 + + window.participants[window.p2pcf.sessionId] = window.userData.inWorldName ? window.userData.inWorldName : "User-" + userProfileName; + + let dropdown = document.getElementById("room-dropdown"); + // empty the contents of the dropdown + dropdown.innerHTML = ""; + // make the inner text of the dropdown a list of the users in the room + // loop with index for each peer + let index = 1; + if( index === 1 ){ + let playerParagraph = document.createElement("p"); + if(window.participants[window.p2pcf.sessionId]){ + playerParagraph.innerHTML = '' + index + ": " + window.participants[window.p2pcf.sessionId]; + } else { + playerParagraph.innerHTML = '' + index + ": " + peer.client_id; + } + playerParagraph.style.marginTop = "10px"; + playerParagraph.style.marginBottom = "10px"; + playerParagraph.style.textAlign = "left"; + playerParagraph.style.fontSize = "0.6em"; + dropdown.appendChild(playerParagraph); + index++; } - ); - window.p2pcf = p2pcf; - console.log("client id:", p2pcf.clientId); - const removePeerUi = (clientId) => { - document.getElementById(clientId)?.remove(); - document.getElementById(`${clientId}-video`)?.remove(); - }; + for (const peer of window.p2pcf.peers.values()) { + let peerParagraph = document.createElement("p"); - const addPeerUi = (sessionId) => { - if (document.getElementById(sessionId)) return; + if(window.participants[peer.id]){ + peerParagraph.innerHTML = '' + index + ": " + window.participants[peer.id]; + } else { + peerParagraph.innerHTML = '' + index + ": " + peer.client_id; + } + // peerParagraph.innerHTML = '' + index + ": " + window.participants[peer.id].inWorldName; + peerParagraph.style.marginTop = "10px"; + peerParagraph.style.marginBottom = "10px"; + peerParagraph.style.textAlign = "left"; + peerParagraph.style.fontSize = "0.6em"; + dropdown.appendChild(peerParagraph); + index++; + } - const peerEl = document.createElement("div"); - peerEl.style = "display: flex;"; + dropdown.style.display = dropdown.style.display === "none" ? "block" : "none"; + }; + // Function to toggle mute on the local audio stream + const toggleMute = async (stream) => { + if (stream) { + var muteIcon = document.getElementById("mute-icon"); - const name = document.createElement("div"); - name.innerText = sessionId.substring(0, 5); + isMuted = !isMuted; + // mute the local stream microphone + // localStream.getAudioTracks()[0].enabled = !isMuted; + for (let i = 0; i < window.localStream.getAudioTracks().length; i++) { + window.localStream.getAudioTracks()[i].enabled = !isMuted; + } - peerEl.id = sessionId; - peerEl.appendChild(name); + if(muteIcon){ + if (localStream.getAudioTracks()[0].enabled) { + muteIcon.style.backgroundImage = `url(${audioIcon})`; + } else { + muteIcon.style.backgroundImage = `url(${audioIconMute})`; + } + } - document.getElementById("peers").appendChild(peerEl); + } }; - const addMessage = (message) => { - const messageEl = document.createElement("div"); - messageEl.innerText = message; - document.getElementById("messages").appendChild(messageEl); + // Function to get the user's media + const getUserMedia = async () => { + try { + localStream = await navigator.mediaDevices.getUserMedia({ audio: true }); + // Do something with the stream like sending it to other peers + } catch (error) { + console.error('Error accessing the microphone', error); + } + }; + // Call this function when you want to toggle mute (e.g., when a button is pressed) + const onMuteButtonPressed = (stream) => { + toggleMute(stream); }; - let stream; - p2pcf.on("peerconnect", (peer) => { - console.log("Peer connect", peer.id, peer); - console.log(peer.client_id); - if (stream) { - peer.addStream(stream); - } - peer.on("track", (track, stream) => { - console.log("got track", track); - const video = document.createElement("audio"); - video.id = `${peer.id}-audio`; - video.srcObject = stream; - video.setAttribute("playsinline", true); - document.getElementById("videos").appendChild(video); - video.play(); - }); - addPeerUi(peer.id); - }); - - p2pcf.on("peerclose", (peer) => { - console.log("Peer close", peer.id, peer); - removePeerUi(peer.id); - }); - - p2pcf.on("msg", (peer, data) => { - addMessage( - peer.id.substring(0, 5) + - ": " + - new TextDecoder("utf-8").decode(data) - ); - }); + useEffect(() => { + const mainContainer = document.getElementById("networking"); + // set container background to accent image + mainContainer.style.backgroundImage = `url(${cornerAccent})`; + mainContainer.style.backgroundSize = "cover"; + }, []); const go = () => { - document.getElementById("session-id").innerText = - p2pcf.sessionId.substring(0, 5) + "@" + p2pcf.roomId + ":"; - + // document.getElementById("session-id").innerText = "Room: " + p2pcf.roomId; + // document.getElementById('send-button').addEventListener('click', () => { // const box = document.getElementById('send-box'); // addMessage(p2pcf.sessionId.substring(0, 5) + ': ' + box.value); // p2pcf.broadcast(new TextEncoder().encode(box.value)); // box.value = ''; // }) + const mainContainer = document.getElementById("networking"); + + // set container background to accent image + mainContainer.style.backgroundImage = `url(${cornerAccent})`; + mainContainer.style.backgroundSize = "cover"; + mainContainer.style.display = "block"; + + const audioButton = document.getElementById("audio-button"); + if (audioButton) { + audioButton.addEventListener("click", async (button) => { + // request permissions for microphone devices then do audioDropdownContent + navigator.mediaDevices.getUserMedia({ audio: true }).then(function (stream) { + AudioDropdownContent(button); + }); + }); + } + + if (isNetworkActivated && !window.p2pcf && !isOffline ) { + const userProfileName = Math.floor(Math.random() * 100000); + const domainName = window.location.hostname.replace(/\./g, '-'); + const roomIdentifier = `3ov-${props.postSlug}`; + const roomId = `${domainName}-${roomIdentifier}`; + + fetchTURNcredentials().then(iceServers => { + if (!iceServers) { + console.error('Could not fetch TURN credentials. P2P functionality may be limited.'); + } + + const p2pcf = new P2PCF( + "user-" + userProfileName, + roomId, + { + workerUrl: multiplayerWorker, + slowPollingRateMs: 5000, + fastPollingRateMs: 1500, + participantLimit: props.networkingBlock[0].attributes.participantLimit.value, + // Use fetched TURN credentials if available + turnIceServers: iceServers, + } + ); + + setupP2PCF(p2pcf); + setP2pcf(p2pcf); + window.p2pcf = p2pcf; + window.participants = []; + + }); + } + + // if the room id is different from the url/#hash then reinitialize the p2pcf + if (window.p2pcf && window.p2pcf.roomId !== window.location.hash.substring(1)) { + console.log("should not be hitting here"); + // remove the window.p2pcf object from the window + delete window.p2pcf; + // Reinitialize P2PCF with the new room ID + const userProfileName = Math.floor(Math.random() * 100000); + let p2pcf = new P2PCF( + "user-" + userProfileName, + window.location.hash.substring(1), + { + workerUrl: multiplayerWorker, + slowPollingRateMs: 5000, + fastPollingRateMs: 1500, + participantLimit: props.networkingBlock[0].attributes.participantLimit.value, + } + ); + setupP2PCF(p2pcf); + p2pcf.start({ playerVRM: userData.playerVRM ? userData.playerVRM : defaultAvatar }); + } else if (window.p2pcf) { + window.p2pcf.start({ playerVRM: userData.playerVRM ? userData.playerVRM : defaultAvatar }); + } + addPeerUi(); + addRoomUi(); + }; - document - .getElementById("audio-button") - .addEventListener("click", async () => { - stream = await navigator.mediaDevices.getUserMedia({ - audio: true - }); + useEffect(() => { + if (isNetworkActivated) { + const domainName = window.location.hostname.replace(/\./g, '-'); + const roomIdentifier = `3ov-${props.postSlug}`; + const roomId = `${domainName}-${roomIdentifier}`; + + if (!document.location.hash) { + document.location = document.location.toString() + `#${roomId}`; + } + } + + const handleLoaded = (event) => { + go(); + // Remove the event listener after handling the first 'loaded' event + window.removeEventListener("loaded", handleLoaded); + }; + + window.addEventListener("loaded", handleLoaded); + + return () => { + window.removeEventListener("loaded", handleLoaded); + }; + }, []); + - for (const peer of p2pcf.peers.values()) { + useEffect(() => { + if(isNetworkActivated && window.p2pcf ){ + window.p2pcf.on("roomfullrefresh", (peer) => { + console.log("So sorry, Room full refresh", peer); + const userProfileName = Math.floor(Math.random() * 100000); + + // Wait for the URL hash to update to ensure room ID is new + setTimeout(() => { + // remove the window.p2pcf object from the window + delete window.p2pcf; + // Reinitialize P2PCF with the new room ID + const p2pcf = new P2PCF( + "user-" + userProfileName, + window.location.hash.substring(1), + { + workerUrl: multiplayerWorker, + slowPollingRateMs: 5000, + fastPollingRateMs: 1500, + participantLimit: props.networkingBlock[0].attributes.participantLimit.value, + } + ); + setupP2PCF(p2pcf); + + }, 500); + }); + } + + if( isNetworkActivated && window.p2pcf ){ + window.p2pcf.on("peerconnect", (peer) => { + if (stream) { peer.addStream(stream); } + peer.on("track", (track, stream) => { + const video = document.createElement("audio"); + video.id = `${peer.id}-audio`; + video.srcObject = stream; + video.setAttribute("playsinline", true); + document.getElementById("videos").appendChild(video); + video.play(); + }); + }); + + window.p2pcf.on("peerclose", (peer) => { + removePeerUi(peer.id); }); + + window.p2pcf.on("msg", (peer, data) => { + addMessage( + peer.id.substring(0, 5) + + ": " + + new TextDecoder("utf-8").decode(data) + ); + }); + } + }, [p2pcf]); + + // if( isNetworkActivated ){ + // if ( ! document.location.hash ) { + // document.location = document.location.toString() + `#3ov-${props.postSlug}`; + // } + // const userProfileName = Math.floor( Math.random() * 100000 ); + // let p2pcf = new P2PCF( + // "user-" + userProfileName, + // document.location.hash.substring(1), + // { + // workerUrl: multiplayerWorker, + // slowPollingRateMs: 5000, + // fastPollingRateMs: 1500, + // participantLimit: props.networkingBlock[0].attributes.participantLimit.value, + // turnCredentials: turnCredentials, + // turnIceServers: turnIceServers, + // } + // ); + + // window.p2pcf = p2pcf; + // window.participants = []; + //} - p2pcf.start(); + const removePeerUi = (clientId) => { + document.getElementById(clientId)?.remove(); + document.getElementById(`${clientId}-video`)?.remove(); }; - if ( - document.readyState === "complete" || - document.readyState === "interactive" - ) { - document - .getElementById("join-button") - .addEventListener("click", async () => { - window.addEventListener("DOMContentLoaded", audio, { - once: true - }); - // window.addEventListener('DOMContentLoaded', go, { once: true }) - }); - } else { - window.addEventListener("DOMContentLoaded", go, { once: true }); - } + + const setupP2PCF = (p2pcfInstance) => { + // Start the P2PCF instance with any necessary configurations + p2pcfInstance.start({ playerVRM: userData.playerVRM ? userData.playerVRM : defaultAvatar }); + window.p2pcf = p2pcfInstance; + }; + + + const addPeerUi = (sessionId) => { + // if (document.getElementById(sessionId)) return; + var peerIcon = document.createElement("button"); + peerIcon.style.backgroundImage = `url(${participants})`; + peerIcon.style.backgroundSize = "cover"; + peerIcon.style.width = "40px"; + peerIcon.style.height = "40px"; + peerIcon.style.padding = "10px"; + peerIcon.style.boxSizing = "border-box"; + peerIcon.style.borderRadius = "50%"; + peerIcon.style.backgroundPosition = "center"; + peerIcon.style.backgroundRepeat = "no-repeat"; + peerIcon.style.backgroundColor = "#FFFFFF"; + peerIcon.style.border = "solid 1px #959595"; + peerIcon.style.backgroundSize = "30px"; + peerIcon.style.marginRight = "5px"; + peerIcon.style.marginTop = "3px"; + peerIcon.style.cursor = "pointer"; + + // const peerEl = document.createElement("div"); + // peerEl.style = "display: flex;"; + + // const name = document.createElement("div"); + // name.innerText = sessionId.substring(0, 5); + + // peerEl.id = sessionId; + // peerEl.appendChild(name); + + // add click listener + peerIcon.addEventListener("click", (event) => { + PeerDropdownContent(); + // Position the dropdown near the roomIcon + let dropdown = document.getElementById("room-dropdown"); + }); + + document.getElementById("network-ui-container").prepend(peerIcon); + }; + const addRoomUi = (sessionId) => { + // if (document.getElementById(sessionId)) return; + var roomIcon = document.createElement("button"); + roomIcon.style.backgroundImage = `url(${worldIcon})`; + roomIcon.style.backgroundSize = "cover"; + roomIcon.style.width = "40px"; + roomIcon.style.height = "40px"; + roomIcon.style.padding = "10px"; + roomIcon.style.marginTop = "3px"; + roomIcon.style.marginRight = "5px"; + roomIcon.style.marginLeft = "5px"; + roomIcon.style.boxSizing = "border-box"; + roomIcon.style.borderRadius = "50%"; + roomIcon.style.backgroundPosition = "center"; + roomIcon.style.backgroundRepeat = "no-repeat"; + roomIcon.style.backgroundColor = "#FFFFFF"; + roomIcon.style.border = "solid 1px #959595"; + roomIcon.style.cursor = "pointer"; + roomIcon.style.backgroundSize = "30px"; + roomIcon.addEventListener("click", (event) => { + RoomDropdownContent(); + // Position the dropdown near the roomIcon + let dropdown = document.getElementById("room-dropdown"); + dropdown.style.left = roomIcon.offsetLeft + "px"; + dropdown.style.top = roomIcon.offsetTop + roomIcon.offsetHeight + "px"; + }); + + let dropdown = document.getElementById("room-dropdown"); + dropdown.style.display = "none"; + dropdown.style.position = "absolute"; + dropdown.style.backgroundColor = "#000000cc"; + dropdown.style.color = "white"; + dropdown.style.padding = "10px"; + dropdown.style.width = "200px"; + dropdown.style.height = "150px"; + dropdown.style.borderRadius = "15px"; + // add corner accent + dropdown.style.backgroundImage = `url(${cornerAccent})`; + dropdown.style.backgroundSize = "auto"; + dropdown.style.backgroundPosition = "top left" + dropdown.style.backgroundRepeat = "no-repeat"; + + + document.getElementById("network-ui-container").prepend(roomIcon); + // prepend the returnXRButton to the network-ui-container + }; + + const addMessage = (message) => { + const messageEl = document.createElement("div"); + messageEl.innerText = message; + + document.getElementById("messages").appendChild(messageEl); + }; + let stream; + return <>; }; -export default Networking; \ No newline at end of file +export default Networking; diff --git a/blocks/environment/components/Player.js b/blocks/environment/components/Player.js index 79f2b65..22b618c 100644 --- a/blocks/environment/components/Player.js +++ b/blocks/environment/components/Player.js @@ -1,701 +1,1207 @@ -import { Mesh, Raycaster, DoubleSide, MeshBasicMaterial, RingGeometry, AudioListener, Group, Quaternion, Matrix4, VectorKeyframeTrack, QuaternionKeyframeTrack, LoopPingPong, AnimationClip, NumberKeyframeTrack, AnimationMixer, Vector3, Vector2, BufferGeometry, CircleGeometry, sRGBEncoding, MathUtils } from "three"; +import { Box3, + Mesh, + Raycaster, + PerspectiveCamera, + ArrowHelper, + Euler, MathUtils, NearestFilter, LoopOnce, DoubleSide, MeshBasicMaterial, RingGeometry, BoxGeometry, AudioListener, Color, Group, Quaternion, Matrix4, VectorKeyframeTrack, QuaternionKeyframeTrack, LoopPingPong, AnimationClip, NumberKeyframeTrack, AnimationMixer, Vector3, Vector2, BufferGeometry, CircleGeometry, sRGBEncoding } from "three"; import { TextureLoader } from "three/src/loaders/TextureLoader"; import { useFrame, useLoader, useThree, Interactive } from "@react-three/fiber"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; +import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader'; +import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader"; -import { OrbitControls } from '@react-three/drei'; -import { useKeyboardControls } from "./Controls" +import { OrbitControls, SpriteAnimator, KeyboardControls } from '@react-three/drei'; +import * as SkeletonUtils from "three/examples/jsm/utils/SkeletonUtils.js"; import { useRef, useState, useEffect } from "react"; +import { useXR, useController } from '@react-three/xr'; import { RigidBody, CapsuleCollider, useRapier, vec3, interactionGroups, CuboidCollider } from "@react-three/rapier"; import defaultVRM from "../../../inc/avatars/3ov_default_avatar.vrm"; -import { VRMUtils, VRMSchema, VRMLoaderPlugin, VRMExpressionPresetName, VRMHumanBoneName } from "@pixiv/three-vrm"; -import { useXR } from "@react-three/xr"; +import blankVRM from "../../../inc/avatars/blank_avatar.vrm"; +import { VRMUtils, VRMHumanBones, VRMSchema, VRMLoaderPlugin, VRMSpringBoneManager, VRMExpressionPresetName, VRMHumanBoneName, VRM } from "@pixiv/three-vrm"; + import idle from "../../../inc/avatars/friendly.fbx"; import walk from "../../../inc/avatars/walking.fbx"; import run from "../../../inc/avatars/running.fbx"; +import jump from "../../../inc/avatars/Jump.fbx"; +import fall from "../../../inc/avatars/falling.fbx"; +import { getMixamoRig } from "../utils/rigMap"; +import ShapePointsMesh from "../utils/ShapePointsMesh"; +import DynLineMesh from "../utils/DynLineMesh"; +import Ecctrl, { EcctrlAnimation, useGame, useFollowCam, useJoystickControls } from "ecctrl"; +// import avatar from ./avatar/index.js +import { ExokitAvatar } from "./avatar"; -function Reticle() { - const { camera } = useThree(); - var reticle = new Mesh( - new RingGeometry( 0.85 * 5, 5, 32), - new MeshBasicMaterial( {color: 0xffffff, side: DoubleSide }) - ); - reticle.scale.set(1.3, 1.3, 1.3); - reticle.position.z = -1000; - reticle.name = "reticle"; - reticle.frustumCulled = false; - reticle.renderOrder = 1000; - reticle.lookAt(camera.position) - reticle.material.depthTest = false; - reticle.material.depthWrite = false; - reticle.material.opacity = 0.025; - - return reticle; -} -/** - * A map from Mixamo rig name to VRM Humanoid bone name - */ -const mixamoVRMRigMap = { - mixamorigHips: 'hips', - mixamorigSpine: 'spine', - mixamorigSpine1: 'chest', - mixamorigSpine2: 'upperChest', - mixamorigNeck: 'neck', - mixamorigHead: 'head', - mixamorigLeftShoulder: 'leftShoulder', - mixamorigLeftArm: 'leftUpperArm', - mixamorigLeftForeArm: 'leftLowerArm', - mixamorigLeftHand: 'leftHand', - mixamorigLeftHandThumb1: 'leftThumbMetacarpal', - mixamorigLeftHandThumb2: 'leftThumbProximal', - mixamorigLeftHandThumb3: 'leftThumbDistal', - mixamorigLeftHandIndex1: 'leftIndexProximal', - mixamorigLeftHandIndex2: 'leftIndexIntermediate', - mixamorigLeftHandIndex3: 'leftIndexDistal', - mixamorigLeftHandMiddle1: 'leftMiddleProximal', - mixamorigLeftHandMiddle2: 'leftMiddleIntermediate', - mixamorigLeftHandMiddle3: 'leftMiddleDistal', - mixamorigLeftHandRing1: 'leftRingProximal', - mixamorigLeftHandRing2: 'leftRingIntermediate', - mixamorigLeftHandRing3: 'leftRingDistal', - mixamorigLeftHandPinky1: 'leftLittleProximal', - mixamorigLeftHandPinky2: 'leftLittleIntermediate', - mixamorigLeftHandPinky3: 'leftLittleDistal', - mixamorigRightShoulder: 'rightShoulder', - mixamorigRightArm: 'rightUpperArm', - mixamorigRightForeArm: 'rightLowerArm', - mixamorigRightHand: 'rightHand', - mixamorigRightHandPinky1: 'rightLittleProximal', - mixamorigRightHandPinky2: 'rightLittleIntermediate', - mixamorigRightHandPinky3: 'rightLittleDistal', - mixamorigRightHandRing1: 'rightRingProximal', - mixamorigRightHandRing2: 'rightRingIntermediate', - mixamorigRightHandRing3: 'rightRingDistal', - mixamorigRightHandMiddle1: 'rightMiddleProximal', - mixamorigRightHandMiddle2: 'rightMiddleIntermediate', - mixamorigRightHandMiddle3: 'rightMiddleDistal', - mixamorigRightHandIndex1: 'rightIndexProximal', - mixamorigRightHandIndex2: 'rightIndexIntermediate', - mixamorigRightHandIndex3: 'rightIndexDistal', - mixamorigRightHandThumb1: 'rightThumbMetacarpal', - mixamorigRightHandThumb2: 'rightThumbProximal', - mixamorigRightHandThumb3: 'rightThumbDistal', - mixamorigLeftUpLeg: 'leftUpperLeg', - mixamorigLeftLeg: 'leftLowerLeg', - mixamorigLeftFoot: 'leftFoot', - mixamorigLeftToeBase: 'leftToes', - mixamorigRightUpLeg: 'rightUpperLeg', - mixamorigRightLeg: 'rightLowerLeg', - mixamorigRightFoot: 'rightFoot', - mixamorigRightToeBase: 'rightToes', -}; +import { + Armature, + Pose, + BipedRig, + IKChain, + HipSolver, + SpineSolver, + LimbSolver, + FootSolver, + SwingTwistSolver, + SwingTwistEndsSolver, + ZSolver +} from 'ossos'; + +const DamperTimeS = 0.15; + +const __rot = new Quaternion(); +const __shoulderWPos = new Vector3(); +const __originWPos = new Vector3(); +const __originWDir = new Vector3(); +const __offset = new Vector3(); + +const mixamoVRMRigMap = getMixamoRig(); -/** - * Download Mixamo animation, convert it for usage with three-vrm, and return the converted animation. - * - * @param {string} url - The URL of Mixamo animation data - * @param {VRM} vrm - The target VRM - * @returns {Promise} - The adapted AnimationClip - */ +function addHandRotationControls() { + const container = document.createElement('div'); + container.style.position = 'fixed'; + container.style.top = '10px'; + container.style.right = '10px'; + container.style.backgroundColor = 'rgba(0, 0, 0, 0.7)'; + container.style.padding = '10px'; + container.style.borderRadius = '5px'; + container.style.color = 'white'; + container.style.fontFamily = 'Arial, sans-serif'; + container.style.zIndex = '10000'; + container.style.maxHeight = '80vh'; + container.style.overflowY = 'auto'; + + const createSlider = (name, min, max, step, defaultValue) => { + const label = document.createElement('label'); + label.textContent = `${name}: `; + label.style.display = 'block'; + label.style.marginBottom = '5px'; + + const slider = document.createElement('input'); + slider.type = 'range'; + slider.min = min; + slider.max = max; + slider.step = step; + slider.value = defaultValue; + slider.style.width = '100%'; + + const valueDisplay = document.createElement('span'); + valueDisplay.textContent = defaultValue; + valueDisplay.style.marginLeft = '5px'; + + slider.addEventListener('input', () => { + valueDisplay.textContent = slider.value; + window.handRotationControls[name] = parseFloat(slider.value); + }); + + label.appendChild(slider); + label.appendChild(valueDisplay); + return label; + }; + + const createCheckbox = (name) => { + const label = document.createElement('label'); + label.style.display = 'block'; + label.style.marginBottom = '5px'; + + const checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.style.marginRight = '5px'; + + checkbox.addEventListener('change', () => { + window.handRotationControls[name] = checkbox.checked; + }); + + label.appendChild(checkbox); + label.appendChild(document.createTextNode(name)); + return label; + }; + + window.handRotationControls = { + rightHandRotationOffsetX: 0, + rightHandRotationOffsetY: 0, + rightHandRotationOffsetZ: -90, + leftHandRotationOffsetX: 0, + leftHandRotationOffsetY: 0, + leftHandRotationOffsetZ: 90, + flipRightHandX: false, + flipRightHandY: false, + flipRightHandZ: false, + flipLeftHandX: false, + flipLeftHandY: false, + flipLeftHandZ: false, + rightArmPoleX: 0, + rightArmPoleY: 0, + rightArmPoleZ: -1, + leftArmPoleX: 0, + leftArmPoleY: 0, + leftArmPoleZ: -1 + }; + + container.appendChild(createSlider('rightHandRotationOffsetX', -180, 180, 1, 0)); + container.appendChild(createSlider('rightHandRotationOffsetY', -180, 180, 1, 0)); + container.appendChild(createSlider('rightHandRotationOffsetZ', -180, 180, 1, 0)); + container.appendChild(createSlider('leftHandRotationOffsetX', -180, 180, 1, 0)); + container.appendChild(createSlider('leftHandRotationOffsetY', -180, 180, 1, 0)); + container.appendChild(createSlider('leftHandRotationOffsetZ', -180, 180, 1, 0)); + container.appendChild(createCheckbox('flipRightHandX')); + container.appendChild(createCheckbox('flipRightHandY')); + container.appendChild(createCheckbox('flipRightHandZ')); + container.appendChild(createCheckbox('flipLeftHandX')); + container.appendChild(createCheckbox('flipLeftHandY')); + container.appendChild(createCheckbox('flipLeftHandZ')); + + // Add arm pole target sliders + container.appendChild(document.createElement('hr')); + container.appendChild(document.createTextNode('Arm Pole Targets:')); + container.appendChild(createSlider('rightArmPoleX', -1, 1, 0.1, 0)); + container.appendChild(createSlider('rightArmPoleY', -1, 1, 0.1, 0)); + container.appendChild(createSlider('rightArmPoleZ', -1, 1, 0.1, -1)); + container.appendChild(createSlider('leftArmPoleX', -1, 1, 0.1, 0)); + container.appendChild(createSlider('leftArmPoleY', -1, 1, 0.1, 0)); + container.appendChild(createSlider('leftArmPoleZ', -1, 1, 0.1, -1)); + + // document.body.appendChild(container); + } + function loadMixamoAnimation(url, vrm) { - let loader; - if (url.endsWith('.fbx')) { - loader = new FBXLoader(); // Use an FBX loader - } else { - loader = new GLTFLoader(); // Use a GLTF loader +let loader; +if (url.endsWith('.fbx')) { + loader = new FBXLoader(); +} else { + loader = new GLTFLoader(); +} +return loader.loadAsync(url).then((resource) => { + const clip = resource.animations[0]; + + if (url.endsWith('.glb')) { + resource = resource.scene; } - return loader.loadAsync(url).then((resource) => { - const clip = resource.animations[0]; // Extract the AnimationClip - // if resource is GLB, get the scene - if (url.endsWith('.glb')) { - resource = resource.scene; - } + let tracks = []; - let tracks = []; // KeyframeTracks compatible with VRM to be stored here - - let restRotationInverse = new Quaternion(); - let parentRestWorldRotation = new Quaternion(); - let _quatA = new Quaternion(); - let _vec3 = new Vector3(); - - // Adjust according to the height of the hips. - let mixamoHips = resource.getObjectByName('mixamorigHips'); - let regularHips = resource.getObjectByName('hips'); - let mainHip; - if (mixamoHips) { - mainHip = mixamoHips.position.y; - } else if (regularHips) { - mainHip = regularHips.position.y; - } - const vrmHipsY = vrm.humanoid?.getNormalizedBoneNode('hips').getWorldPosition(_vec3).y; - const vrmRootY = vrm.scene.getWorldPosition(_vec3).y; - const vrmHipsHeight = Math.abs(vrmHipsY - vrmRootY); - const hipsPositionScale = vrmHipsHeight / mainHip; + let restRotationInverse = new Quaternion(); + let parentRestWorldRotation = new Quaternion(); + let _quatA = new Quaternion(); + let _vec3 = new Vector3(); - clip.tracks.forEach((track) => { - // Convert each track for VRM usage, and push to `tracks` - let trackSplitted = track.name.split('.'); - let mixamoRigName = trackSplitted[0]; - let vrmBoneName = mixamoVRMRigMap[mixamoRigName]; - let vrmNodeName = vrm.humanoid?.getNormalizedBoneNode(vrmBoneName)?.name; - let mixamoRigNode = resource.getObjectByName(mixamoRigName); + let mixamoHips = resource.getObjectByName('mixamorigHips'); + let regularHips = resource.getObjectByName('hips'); + let mainHip; + if (mixamoHips) { + mainHip = mixamoHips.position.y; + } else if (regularHips) { + mainHip = regularHips.position.y; + } + VRMUtils.rotateVRM0(vrm); + VRMUtils.removeUnnecessaryVertices( vrm.scene ); + VRMUtils.removeUnnecessaryJoints( vrm.scene ); + const vrmHipsY = vrm.humanoid?.getNormalizedBoneNode('hips').getWorldPosition(_vec3).y; + const vrmRootY = vrm.scene.getWorldPosition(_vec3).y; + const vrmHipsHeight = Math.abs(vrmHipsY - vrmRootY); + const hipsPositionScale = vrmHipsHeight / mainHip; - if (vrmNodeName != null) { + clip.tracks.forEach((track) => { + let trackSplitted = track.name.split('.'); + let mixamoRigName = trackSplitted[0]; + let vrmBoneName = mixamoVRMRigMap[mixamoRigName]; + let vrmNodeName = vrm.humanoid?.getNormalizedBoneNode(vrmBoneName)?.name; + let mixamoRigNode = resource.getObjectByName(mixamoRigName); - let propertyName = trackSplitted[1]; + if (vrmNodeName != null) { + let propertyName = trackSplitted[1]; - // Store rotations of rest-pose. - mixamoRigNode.getWorldQuaternion(restRotationInverse).invert(); - mixamoRigNode.parent.getWorldQuaternion(parentRestWorldRotation); + mixamoRigNode.getWorldQuaternion(restRotationInverse).invert(); + mixamoRigNode.parent.getWorldQuaternion(parentRestWorldRotation); - if (track instanceof QuaternionKeyframeTrack) { + if (track instanceof QuaternionKeyframeTrack) { + for (let i = 0; i < track.values.length; i += 4) { + let flatQuaternion = track.values.slice(i, i + 4); - // Retarget rotation of mixamoRig to NormalizedBone. - for (let i = 0; i < track.values.length; i += 4) { + _quatA.fromArray(flatQuaternion); - let flatQuaternion = track.values.slice(i, i + 4); + _quatA + .premultiply(parentRestWorldRotation) + .multiply(restRotationInverse); - _quatA.fromArray(flatQuaternion); + _quatA.toArray(flatQuaternion); - _quatA - .premultiply(parentRestWorldRotation) - .multiply(restRotationInverse); + flatQuaternion.forEach((v, index) => { + track.values[index + i] = v; + }); + } - _quatA.toArray(flatQuaternion); + tracks.push( + new QuaternionKeyframeTrack( + `${vrmNodeName}.${propertyName}`, + track.times, + track.values.map((v, i) => (vrm.meta?.metaVersion === '0' && i % 2 === 0 ? -v : v)), + ), + ); + } else if (track instanceof VectorKeyframeTrack) { + let value = track.values.map((v, i) => (vrm.meta?.metaVersion === '0' && i % 3 !== 1 ? -v : v) * hipsPositionScale); + tracks.push(new VectorKeyframeTrack(`${vrmNodeName}.${propertyName}`, track.times, value)); + } + } + }); - flatQuaternion.forEach((v, index) => { + return new AnimationClip('vrmAnimation', clip.duration, tracks); +}); +} - track.values[index + i] = v; +function addResetButton(props) { +const button = document.createElement('button'); +button.innerHTML = 'Respawn'; +button.onclick = () => { + props.movement.current.respawn = true; + setTimeout(() => { + props.movement.current.respawn = false; + }, 100); +}; - }); +button.style.position = 'fixed'; +button.style.bottom = '190px'; +button.style.left = '10px'; +button.style.zIndex = '1000'; +button.style.padding = '10px'; +button.style.border = 'none'; +button.style.backgroundColor = 'rgba(0, 0, 0, 0.5)'; +button.style.color = 'white'; +button.style.cursor = 'pointer'; +button.style.borderRadius = '5px'; +button.style.fontFamily = 'Arial'; +button.style.fontSize = '16px'; +button.style.fontWeight = 'bold'; +document.body.appendChild(button); +} - } +class XrHead { +constructor(context) { + this.context = context; + this.position = new Vector3(); + this.quaternion = new Quaternion(); + this.worldUp = new Vector3(); + this.forward = new Vector3(); + this.up = new Vector3(); + this.right = new Vector3(); +} - tracks.push( - new QuaternionKeyframeTrack( - `${vrmNodeName}.${propertyName}`, - track.times, - track.values.map((v, i) => (vrm.meta?.metaVersion === '0' && i % 2 === 0 ? - v : v)), - ), - ); - - } else if (track instanceof VectorKeyframeTrack) { - let value = track.values.map((v, i) => (vrm.meta?.metaVersion === '0' && i % 3 !== 1 ? - v : v) * hipsPositionScale); - tracks.push(new VectorKeyframeTrack(`${vrmNodeName}.${propertyName}`, track.times, value)); - } +update() { + this.context.camera.getWorldPosition(this.position); + this.context.camera.getWorldQuaternion(this.quaternion); + this.worldUp.set(0, 1, 0); + this.up.set(0, 1, 0).applyQuaternion(this.quaternion); + this.forward.set(0, 0, -1).applyQuaternion(this.quaternion); + this.right.set(1, 0, 0).applyQuaternion(this.quaternion); +} +} - } +class Vector3Damper { +constructor(period) { + this.period = period || 0.15; + this._samples = []; + this._total = new Vector3(); + this._average = new Vector3(); +} - }); +add(time, sample) { + const removeSamplesBefore = time - this.period; + while (this._samples.length && this._samples[0].time < removeSamplesBefore) { + const s = this._samples.shift(); + this._total.x -= s.x; + this._total.y -= s.y; + this._total.z -= s.z; + } + this._total.x += sample.x; + this._total.y += sample.y; + this._total.z += sample.z; + this._samples.push({ time: time, x: sample.x, y: sample.y, z: sample.z }); + const count = this._samples.length; + this._average.set(this._total.x / count, this._total.y / count, this._total.z / count); + return this._average; +} - return new AnimationClip('vrmAnimation', clip.duration, tracks); +get average() { + return this._average; +} - }); +clear() { + this._samples = []; + this._total.setScalar(0); + this._average.setScalar(0); } +} export default function Player(props) { - const canMoveRef = useRef(true); - const falling = useRef(true); - const animationsRef = useRef(); - const orbitRef = useRef(); - const rigidRef = useRef(); - const castRef = useRef(); - - const idleFile = threeObjectPlugin + idle; - const walkingFile = threeObjectPlugin + walk; - const runningFile = threeObjectPlugin + run; - // const [walkFile, setWalkFile] = useState(model.threeObjectPlugin + walk); - const spawnPoint = props.spawnPoint? props.spawnPoint.map(Number) : [0,0,0]; // convert spawnPoint to numbers - const { controllers } = useXR(); - const { camera, scene, clock } = useThree(); - const { world, rapier } = useRapier(); - const participantObject = scene.getObjectByName("playerOne"); - const mouse = new Vector2(); - - // if (!scene.getObjectByName("reticle")){ - // camera.add(Reticle()); - // } +const [isModelLoaded, setIsModelLoaded] = useState(false); +const currentPlayerAvatarRef = useRef(null); +const playerControllerRef = useRef(null); +const playerMixerRef = useRef(null); +const { camera, gl } = useThree(); +const { isPresenting } = useXR(); +const [presentingState, setPresentingState] = useState(false); +const prevPositionRef = useRef(null); +const { controllers } = useXR(); +const rightController = useController('right'); +const leftController = useController('left'); +const head = useRef(new XrHead(useThree())); +const pointerOriginDamper = useRef(new Vector3Damper(DamperTimeS)); +const pointerDirectionDamper = useRef(new Vector3Damper(DamperTimeS)); - if ( controllers.length > 0 ) { - scene.remove(scene.getObjectByName("reticle")); - } +const characterRef = useRef(null); + +const [frameName, setFrameName] = useState(); + +const canMoveRef = useRef(true); +const spriteRef = useRef(); +const animationsRef = useRef(); +const playerModelRef = useRef(); + +const orbitRef = useRef(); +const rigidRef = useRef(); +const castRef = useRef(); +const [loaderIsGone, setLoaderIsGone] = useState(false); +const [avatarIsSprite, setAvatarIsSprite] = useState(false); + +const curAnimation = useGame((state) => state.curAnimation); +const initializeAnimationSet = useGame( + (state) => state.initializeAnimationSet +); +const idleAnimation = useGame((state) => state.idle); +const walkAnimation = useGame((state) => state.walk); +const runAnimation = useGame((state) => state.run); +const action1Animation = useGame((state) => state.action1); +const action2Animation = useGame((state) => state.action2); +const action3Animation = useGame((state) => state.action3); +const action4Animation = useGame((state) => state.action4); +const resetAnimation = useGame((state) => state.reset); +const [open, setOpen] = useState(false); +const HEAD_LAYER = 1; + + +const animationSet = { + idle: "idle", + walk: "walking", + run: "running", + jump: "jump", +}; +useEffect(() => { + initializeAnimationSet(animationSet); +}, []); - // useFrame(() => { - // if (participantObject) { - // const posY = participantObject.parent.position.y; - // camera.position.setY(posY + 0.23); - // } - // }); +useEffect(() => { + const handleReady = () => { + setLoaderIsGone(true); + removeEventListener('loaderIsGone', handleReady); + }; + window.addEventListener('loaderIsGone', handleReady); + addResetButton(props); +}, []); - // Participant VRM. - const fallbackURL = threeObjectPlugin + defaultVRM; - const defaultAvatarURL = props.defaultAvatar; - let playerURL = userData.vrm ? userData.vrm : fallbackURL; - if(defaultAvatarURL){ - playerURL = defaultAvatarURL; +const idleFile = idle; +const walkingFile = walk; +const runningFile = run; +const jumpFile = jump; +const fallingFile = fall; +const spawnPoint = props.spawnPoint ? props.spawnPoint.map(Number) : [0, 0, 0]; +const { scene, clock } = useThree(); +const { world, rapier } = useRapier(); +const participantObject = scene.getObjectByName("playerOne"); +let debug = {}; + +useEffect(() => { + if (userData.playerVRM.endsWith('.png')) { + setAvatarIsSprite(true); } - const someSceneState = useLoader(GLTFLoader, playerURL, (loader) => { - loader.register((parser) => { - return new VRMLoaderPlugin(parser); +}, []); + +let animationFiles = [idleFile, walkingFile, runningFile, jumpFile]; +// Participant VRM. +const fallbackURL = defaultVRM; +const defaultAvatarURL = props.defaultPlayerAvatar; +let playerURL; +if(defaultAvatarURL){ + playerURL = defaultAvatarURL; +} +playerURL = userData.playerVRM ? userData.playerVRM : fallbackURL; +if( playerURL.endsWith( '.png' ) ){ + playerURL = blankVRM; +} + +// if the playerURL ends in .png +useEffect(() => { + if( userData.playerVRM.endsWith( '.png' ) ){ + setAvatarIsSprite(true); + } +}, []); + +useEffect(() => { + if (!currentPlayerAvatarRef.current) { + const loader = new GLTFLoader(); + const ktx2Loader = new KTX2Loader(); + ktx2Loader.setTranscoderPath(threeObjectPluginRoot + "/inc/utils/basis/"); + ktx2Loader.detectSupport(gl); + loader.setKTX2Loader(ktx2Loader); + // const helperRoot = new Group(); + // helperRoot.renderOrder = 10000; + // scene.add(helperRoot); + // debug.pnt = new ShapePointsMesh(); + // debug.ln = new DynLineMesh(); + // scene.add(debug.pnt); + // scene.add(debug.ln); + + // loader.register( parser => new VRMLoaderPlugin( parser, { helperRoot } ) ); + loader.register( parser => new VRMLoaderPlugin( parser ) ); + + loader.load(playerURL, (gltf) => { + currentPlayerAvatarRef.current = gltf; + playerControllerRef.current = gltf.userData.vrm; + + // Calculate the avatar's height offset + const headBone = gltf.userData.vrm.humanoid.getNormalizedBoneNode(VRMHumanBoneName.Head); + headBone.layers.set(HEAD_LAYER); + headBone.visible = false; + // traverse the head bone to hide the mesh + headBone.traverse((child) => { + if(child.isMesh){ + child.visible = false; + } }); + const headWorldPosition = new Vector3(); + headBone.getWorldPosition(headWorldPosition); + + const avatarWorldPosition = new Vector3(); + gltf.scene.getWorldPosition(avatarWorldPosition); + + props.avatarHeightOffset.current = headWorldPosition.y - avatarWorldPosition.y; + + setIsModelLoaded(true); + }, undefined, error => { + console.error('An error happened during the loading of the model:', error); }); + } +}, [playerURL, gl]); - if (someSceneState?.userData?.gltfExtensions?.VRM) { - const playerController = someSceneState.userData.vrm; - // Check if the avatar is reachable with a 200 response code. - // Check if the avatar is reachable with a 200 response code. - const fetchProfile = async () => { - try { - const response = await fetch(userData.profileImage); - if (response.status === 200) { - const loadedProfile = useLoader(TextureLoader, userData.profileImage); - - playerController.scene.traverse((obj) => { - obj.frustumCulled = false; - - if (obj.name === "profile") { - const newMat = obj.material.clone(); - newMat.map = loadedProfile; - obj.material = newMat; - obj.material.map.needsUpdate = true; - } - }); +useEffect(() => { + if (isModelLoaded && playerControllerRef.current) { + + const avatarOptions = { + fingers: true, + hair: true, + decapitate: false, + visemes: true, + microphoneMediaStream: null, + muted: true, + debug: false, + }; + + const playerController = playerControllerRef.current; + playerControllerRef.current.avatar = new ExokitAvatar(playerControllerRef.current, avatarOptions); + const animationsMixer = new AnimationMixer(playerController.scene); + playerMixerRef.current = animationsMixer; + let animationsPromises = animationFiles.map(file => loadMixamoAnimation(file, playerController)); + playerController.scene.visible = false; + + Promise.all(animationsPromises) + .then(animations => { + const idleAction = animationsMixer.clipAction(animations[0]); + const walkingAction = animationsMixer.clipAction(animations[1]); + const runningAction = animationsMixer.clipAction(animations[2]); + const jumpingAction = animationsMixer.clipAction(animations[3]); + idleAction.timeScale = 1; + walkingAction.timeScale = 0; + runningAction.timeScale = 0; + jumpingAction.timeScale = 0; + animationsRef.current = { idle: idleAction, walking: walkingAction, running: runningAction, jump: jumpingAction }; + idleAction.play(); + playerController.scene.visible = true; + }); + } +}, [isModelLoaded]); + +useEffect(() => { + addHandRotationControls(); + }, []); + +useEffect(() => { + if( isPresenting ){ + console.log( 'Presenting' ); + // kill all animations + // if (playerMixerRef.current) { + // playerMixerRef.current.stopAllAction(); + // } + // stop idle + + if (animationsRef.current) { + const { idle, walking, running, jump, falling } = animationsRef.current; + if(idle){ + idle.stop(); } - return response; - } catch (err) { - // Handle the error properly or rethrow it to be caught elsewhere. - // console.error("Error fetching profile:", err); - // throw err; + if(walking){ + walking.stop(); } - }; + if(running){ + running.stop(); + } + if(jump){ + jump.stop(); + } + } + } +}, [isPresenting]); + +let lastUpdateTime = 0; +let blinkTimer = 0; +let blinkInterval = getRandomBlinkInterval(); + +function getRandomBlinkInterval() { + return 5 + Math.random() * 10; +} + +function handleBlinking(delta) { + blinkTimer += delta; + if (blinkTimer > blinkInterval && playerControllerRef.current) { + performBlink(playerControllerRef.current); + blinkTimer = 0; + blinkInterval = getRandomBlinkInterval(); + } +} + +function performBlink(vrm) { + const blinkDuration = 0.05 + Math.random() * 0.1; + const steps = Math.round(blinkDuration / 0.01); + + for (let i = 0; i <= steps; i++) { + const s = i / steps; + setTimeout(() => { + vrm.expressionManager.setValue('blinkLeft', s); + vrm.expressionManager.setValue('blinkRight', s); + }, s * blinkDuration * 1000); + } + + setTimeout(() => { + for (let i = 0; i <= steps; i++) { + const s = 1 - i / steps; setTimeout(() => { - fetchProfile() - .then((response) => { - // handle the response here if needed - }) - .catch((err) => { - // Handle the error here if needed - }); - }, 1000); - // VRMUtils.rotateVRM0(playerController); - const currentVrm = playerController; - const currentMixer = new AnimationMixer(currentVrm.scene); + vrm.expressionManager.setValue('blinkLeft', s); + vrm.expressionManager.setValue('blinkRight', s); + }, (1 - s) * blinkDuration * 1000); + } + }, blinkDuration * 1000 + 200); +} - - // need to dynamically do this on scroll - // playerController.firstPerson.humanoid.humanBones.head.node.scale.set([ - // 0, 0, 0 - // ]); - - // const movement = useKeyboardControls(); - const velocity = useRef(spawnPoint); // Use a ref instead of state for velocity - let lastUpdateTime = 0; - let blinkTimer = 0; // Initialize the blinkTimer outside the useFrame loop. - let blinkInterval = 5 + Math.random() * 10; // Blink roughly every 2 to 6 seconds - - // frame loop - useFrame((state, delta) => { - let isMoving = false; - const currentTime = state.clock.elapsedTime; - const timeSinceLastUpdate = currentTime - lastUpdateTime; - let rigidBodyPosition = [0, 0, 0] - if(rigidRef.current?.translation()){ - rigidBodyPosition = rigidRef.current.translation(); - } - const forward = new Vector3(); - camera.getWorldDirection(forward); - forward.negate(); // In Three.js camera looks towards negative Z, so we negate the vector - forward.normalize(); - const right = new Vector3(); - right.crossVectors(camera.up, forward); - right.normalize(); - // initialize the moving state to false - const raycaster = state.raycaster; - if (timeSinceLastUpdate >= 0.1) { - lastUpdateTime = currentTime; +const movementTimeoutRef = useRef(null); +const updateRate = 1000 / 5; +const lastNetworkUpdateTimeRef = useRef(0); +let countHangtime = 0; +let isMoving; +let lastKeyPressTime = 0; +let wasJumping = false; + +useEffect(() => { + isMoving = false; +}, []); +let isJumping = false; +const getJoystickValues = useJoystickControls( + (state) => state.getJoystickValues +); +const playerForward = new Vector3(0, 0, 1); + + useFrame((state, delta) => { + const joystickValues = getJoystickValues(); + let forward = props.movement.current.forward; + let backward = props.movement.current.backward; + let left = props.movement.current.left; + let right = props.movement.current.right; + let shift = props.movement.current.shift; + let space = props.movement.current.space; + + if (joystickValues) { + if (joystickValues.joystickAng > 0) { + if (joystickValues.joystickDis > 60) { + shift = true; } - if (currentVrm) { - currentVrm.update(delta); + forward = true; + } + if (joystickValues.button1Pressed === true) { + space = true; + } + } + + if (playerControllerRef.current) { + playerControllerRef.current.update(delta); + } + + if (playerMixerRef.current) { + playerMixerRef.current.update(delta); + } + + const now = state.clock.elapsedTime * 1000; + + if (backward || forward || left || right) { + if (characterRef.current.userData.canJump) { + isMoving = true; + + if (now - lastKeyPressTime > 100) { + if (window.p2pcf) { + const participantObject = scene.getObjectByName("playerOne"); + + var target = new Vector3(); + var worldPosition = participantObject.getWorldPosition(target); + const position = [ + worldPosition.x, + worldPosition.y, + worldPosition.z + ]; + + const rotation = [ + participantObject.parent.parent.rotation.x, + participantObject.parent.parent.rotation.y, + participantObject.parent.parent.rotation.z + ]; + + const currentAction = !characterRef.current.userData.canJump ? "jumping" : "walking"; + const messageObject = { + [window.p2pcf.clientId]: { + position: position, + rotation: rotation, + profileImage: userData.profileImage, + playerVRM: userData.playerVRM, + vrm: userData.vrm, + inWorldName: window.userData.inWorldName ? window.userData.inWorldName : userData.inWorldName, + isMoving: { + action: currentAction, + instance: 'update', + hangtime: countHangtime + } } - if (currentMixer) { - currentMixer.update(delta); + }; + + if (shift && characterRef.current.userData.canJump) { + messageObject[window.p2pcf.clientId].isMoving.action = "running"; } - blinkTimer += delta; // Increment timer - - //blink - if (blinkTimer > blinkInterval) { - if (currentVrm) { - // Randomize the duration of the blink between 0.05 and 0.15 seconds - const blinkDuration = 0.05 + Math.random() * 0.1; - const steps = Math.round(blinkDuration / 0.01); // We want each step to be roughly 0.01 seconds - - // Close both eyes over the course of the blink duration - for(let i = 0; i <= steps; i++) { - const s = i / steps; - setTimeout(() => { - currentVrm.expressionManager.setValue('blinkLeft', s); - currentVrm.expressionManager.setValue('blinkRight', s); - }, s * blinkDuration * 1000); - } - - // Open both eyes over the course of the blink duration, after a small delay - setTimeout(() => { - for(let i = 0; i <= steps; i++) { - const s = 1 - i / steps; - setTimeout(() => { - currentVrm.expressionManager.setValue('blinkLeft', s); - currentVrm.expressionManager.setValue('blinkRight', s); - }, (1 - s) * blinkDuration * 1000); - } - }, blinkDuration * 1000 + 200); // Add a small delay before opening the eyes - - blinkTimer = 0; // Reset the timer - blinkInterval = 5 + Math.random() * 10; // Blink roughly every 10 to 25 seconds + + const message = JSON.stringify(messageObject); + window.p2pcf.broadcast(new TextEncoder().encode(message)), window.p2pcf; + lastKeyPressTime = now; + lastNetworkUpdateTimeRef.current = now; + } + } + + if (now - lastNetworkUpdateTimeRef.current > updateRate) { + if (window.p2pcf) { + const participantObject = scene.getObjectByName("playerOne"); + + var target = new Vector3(); + var worldPosition = participantObject.getWorldPosition(target); + const position = [ + worldPosition.x, + worldPosition.y, + worldPosition.z + ]; + + const rotation = [ + participantObject.parent.parent.rotation.x, + participantObject.parent.parent.rotation.y, + participantObject.parent.parent.rotation.z + ]; + + const currentAction = !characterRef.current.userData.canJump ? "jumping" : "walking"; + + const messageObject = { + [window.p2pcf.clientId]: { + position: position, + rotation: rotation, + profileImage: userData.profileImage, + playerVRM: userData.playerVRM, + vrm: userData.vrm, + inWorldName: window.userData.inWorldName ? window.userData.inWorldName : userData.inWorldName, + isMoving: { + action: currentAction, + instance: 'update', + hangtime: countHangtime } } - let speedPerSecondFB = 3.6; // This is equivalent to 0.06 per frame at 60 FPS - let speedPerSecondLR = 1.8; // This is equivalent to 0.03 per frame at 60 FPS - - if (props.movement.current.shift) { - speedPerSecondFB = 7.2; // This is equivalent to 0.12 per frame at 60 FPS - speedPerSecondLR = 4.2; // This is equivalent to 0.07 per frame at 60 FPS + }; + + if (shift && characterRef.current.userData.canJump) { + messageObject[window.p2pcf.clientId].isMoving.action = "running"; } - let newVelocity = [...velocity.current]; - let newPosition = null; - - if (props.movement.current.backward && canMoveRef.current) { - let speed = speedPerSecondFB * delta; - newVelocity[0] += speed * forward.x; - newVelocity[2] += speed * forward.z; - isMoving = true; - } else if (props.movement.current.forward && canMoveRef.current) { - let speed = speedPerSecondFB * delta; - newVelocity[0] -= speed * forward.x; - newVelocity[2] -= speed * forward.z; - isMoving = true; - } else if (props.movement.current.left && canMoveRef.current) { - let speed = speedPerSecondLR * delta; - newVelocity[0] -= speed * right.x; - newVelocity[2] -= speed * right.z; - isMoving = true; - } else if (props.movement.current.right && canMoveRef.current) { - let speed = speedPerSecondLR * delta; - newVelocity[0] += speed * right.x; - newVelocity[2] += speed * right.z; - isMoving = true; + const message = JSON.stringify(messageObject); + window.p2pcf.broadcast(new TextEncoder().encode(message)), window.p2pcf; + lastNetworkUpdateTimeRef.current = now; + } + } + + clearTimeout(movementTimeoutRef.current); + movementTimeoutRef.current = setTimeout(() => { + isMoving = false; + }, 500); + } + } else { + if (isMoving) { + isMoving = false; + clearTimeout(movementTimeoutRef.current); + if (window.p2pcf?.clientId) { + const participantObject = scene.getObjectByName("playerOne"); + var target = new Vector3(); + var worldPosition = participantObject.getWorldPosition(target); + const position = [ + worldPosition.x, + worldPosition.y, + worldPosition.z + ]; + + const messageStopObject = { + [window.p2pcf.clientId]: { + isMoving: false, + position: position + } + }; + const messageStop = JSON.stringify(messageStopObject); + window.p2pcf.broadcast(new TextEncoder().encode(messageStop)); + lastNetworkUpdateTimeRef.current = now; + } + } + } + + if (isPresenting && !presentingState) { + const newCamera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); + const participantObject = scene.getObjectByName("playerOne"); + // set the rotation to 0 + participantObject.parent.parent.rotation.set(0, 0, 0); + participantObject.rotation.set(0, 0, 0); + const xrCamera = gl.xr.getCamera(newCamera); + gl.xr.enabled = true; + state.camera = xrCamera; + setPresentingState(true); + } else if (!isPresenting && presentingState) { + setPresentingState(false); + } + + handleBlinking(delta); + + if (animationsRef.current) { + if (playerControllerRef.current && participantObject) { + const cameraWorldQuaternion = new Quaternion(); + camera.getWorldQuaternion(cameraWorldQuaternion); + const cameraForward = new Vector3(0, 0, -1).applyQuaternion(cameraWorldQuaternion); + + const characterWorldQuaternion = new Quaternion(); + participantObject.parent.getWorldQuaternion(characterWorldQuaternion); + const characterForward = new Vector3(0, 0, 1).applyQuaternion(characterWorldQuaternion); + const neutralRotation = new Euler(0, 0, 0); + + const characterToCamera = new Vector3().subVectors(camera.position, participantObject.getWorldPosition(new Vector3())).normalize(); + + const dotProduct = characterForward.dot(cameraForward); + const azimuthalAngle = Math.acos(Math.min(Math.max(dotProduct, -1), 1)); + + const angleThreshold = Math.PI / 2; + if (azimuthalAngle < angleThreshold) { + if (avatarIsSprite) { + if (isMoving && frameName !== 'WalkForward') { + setFrameName('WalkForward'); } - if(props.movement.current.respawn === true){ - newPosition = spawnPoint; - newVelocity = spawnPoint; - velocity.current = spawnPoint; + if (isMoving === false) { + if (frameName !== 'ForwardIdle') { + setFrameName('ForwardIdle'); } - - // if shift is pressed, run by setting speed to 0.1 - if(canMoveRef.current){ - velocity.current = newVelocity; } + } + } else { + if (avatarIsSprite && isMoving && frameName !== 'WalkBackward') { + setFrameName('WalkBackward'); + } + if (avatarIsSprite && isMoving === false && frameName !== 'BackwardIdle') { + setFrameName('BackwardIdle'); + } + } + } - const rotationSpeed = 0.5; - if (props.movement.current.backward) { - orbitRef.current.minPolarAngle = Math.PI / 1.8; - orbitRef.current.maxPolarAngle = Math.PI / 1.25; - orbitRef.current.maxDistance = 2; - orbitRef.current.minDistance = 2; - } else { - // Reset properties to default values - if(isMoving){ - orbitRef.current.minPolarAngle = Math.PI / 1.5; - } else { - orbitRef.current.minPolarAngle = Math.PI / 1.8; + const { idle, walking, running, jump, falling } = animationsRef.current; + + if (props.movement.current.respawn) { + characterRef.current.setBodyType(rapier.RigidBodyType.Fixed, 1); + characterRef.current.setTranslation(new Vector3(Number(spawnPoint[0]), Number(spawnPoint[1]), Number(spawnPoint[2])), true); + } else if (!props.movement.current.respawn && characterRef.current.bodyType() === 1) { + characterRef.current.setBodyType(rapier.RigidBodyType.Dynamic, 0); + } + + if (isMoving && characterRef.current.userData.canJump) { + jump.clampWhenFinished = false; + jump.reset(); + jump.setEffectiveTimeScale(0); + jump.setEffectiveWeight(0); + } else if (!isMoving && characterRef.current.userData.canJump) { + jump.clampWhenFinished = false; + jump.reset(); + jump.setEffectiveTimeScale(0); + jump.setEffectiveWeight(0); + idle.setEffectiveTimeScale(1); + idle.setEffectiveWeight(1); + } + + if (!characterRef.current.userData.canJump) { + if (window.p2pcf) { + const participantObject = scene.getObjectByName("playerOne"); + + var target = new Vector3(); + var worldPosition = participantObject.getWorldPosition(target); + const position = [ + worldPosition.x, + worldPosition.y, + worldPosition.z + ]; + + const rotation = [ + participantObject.parent.parent.rotation.x, + participantObject.parent.parent.rotation.y, + participantObject.parent.parent.rotation.z + ]; + if (!prevPositionRef.current || Math.abs(position[1] - prevPositionRef.current[1]) > 0.01) { + const messageObject = { + [window.p2pcf.clientId]: { + position: position, + rotation: rotation, + profileImage: userData.profileImage, + playerVRM: userData.playerVRM, + vrm: userData.vrm, + inWorldName: window.userData.inWorldName ? window.userData.inWorldName : userData.inWorldName, + isMoving: { + action: "jumping", + instance: 'first', + hangtime: countHangtime + } } - orbitRef.current.maxPolarAngle = Math.PI / 1.2; - orbitRef.current.maxDistance = 2; - orbitRef.current.minDistance = 1.3; + }; + const message = JSON.stringify(messageObject); + if ((now - lastNetworkUpdateTimeRef.current > updateRate) && (lastNetworkUpdateTimeRef.current !== 0)) { + window.p2pcf.broadcast(new TextEncoder().encode(message)), window.p2pcf; + lastNetworkUpdateTimeRef.current = now; } - - // send a raycast from the orbit camera and check if there is an obstacle in the way - - // We compute the direction from the camera to the player - let direction = camera.getWorldDirection(new Vector3()); - // normalize the direction to not be looking up or downward - direction.normalize(); - direction.y = 0; - - // Adjust the direction based on the movement direction - if(props.movement.current.backward) { - direction.negate(); // for backward movement, we want to reverse the direction - } else if (props.movement.current.right && !props.movement.current.left && !props.movement.current.forward && !props.movement.current.backward) { - direction.applyAxisAngle(new Vector3(0, 1, 0), -Math.PI / 2); // for right movement, rotate the direction 90 degrees counterclockwise - } else if (props.movement.current.left && !props.movement.current.right && !props.movement.current.forward && !props.movement.current.backward) { - direction.applyAxisAngle(new Vector3(0, 1, 0), Math.PI / 2); // for left movement, rotate the direction 90 degrees clockwise } + prevPositionRef.current = position; + } - // Define the desired rotation matrix - let matrix = new Matrix4(); - matrix.lookAt(new Vector3(0,0,0), direction, new Vector3(0,1,0)); - - // Create a quaternion from the rotation matrix - let desiredQuaternion = new Quaternion(); - desiredQuaternion.setFromRotationMatrix(matrix); - - if ( props.movement.current.forward === true || - props.movement.current.backward === true || - props.movement.current.left === true || - props.movement.current.right === true - ) { - // Apply slerp to the player's current quaternion, gradually aligning it with the desired quaternion - playerController.scene.quaternion.slerp(desiredQuaternion, rotationSpeed); - } - if(castRef.current){ - castRef.current.setRotation(desiredQuaternion); + countHangtime++; + + if (jump.getEffectiveTimeScale() === 0) { + if (countHangtime > 3) { + jump.setEffectiveTimeScale(1); + jump.setEffectiveWeight(1); + jump.clampWhenFinished = true; + jump.time = jump._clip.duration; + jump.play(); + running.setEffectiveTimeScale(0); + running.setEffectiveWeight(0); + walking.setEffectiveTimeScale(0); + walking.setEffectiveWeight(0); } + } - if (isMoving && canMoveRef.current) { - newPosition = [ - velocity.current[0], - rigidBodyPosition.y, - velocity.current[2] + wasJumping = true; + } else { + if (wasJumping) { + if (window.p2pcf) { + const participantObject = scene.getObjectByName("playerOne"); + setTimeout(() => { + var target = new Vector3(); + var worldPosition = participantObject.getWorldPosition(target); + const position = [ + worldPosition.x, + worldPosition.y, + worldPosition.z ]; - participantObject.parent.position.set(...newPosition); - castRef.current.setTranslation({x: newPosition[0], y: newPosition[1], z: newPosition[2]}); - } - // animation logic - if (animationsRef.current) { - const { idle, walking, running } = animationsRef.current; - - if (isMoving) { - // If moving, but idle animation is playing, stop it and play walking animation - // if (idle.isRunning()) { - // blend from idle to walking - if(props.movement.current.shift) { - if (walking.isRunning()) { - walking.crossFadeTo(running, 1); - } else { - idle.crossFadeTo(running, 1); - } - running.enabled = true; - running.setEffectiveTimeScale(1); - running.setEffectiveWeight(1); - idle.enabled = true; - idle.setEffectiveTimeScale(1); - idle.setEffectiveWeight(0); - walking.enabled = true; - walking.setEffectiveTimeScale(1); - walking.setEffectiveWeight(0); - running.play(); - } else { - if (running.isRunning()) { - running.crossFadeTo(walking, 1); - } else { - idle.crossFadeTo(walking, 1); - } - walking.enabled = true; - walking.setEffectiveTimeScale(1); - walking.setEffectiveWeight(1); - running.enabled = true; - running.setEffectiveTimeScale(1); - running.setEffectiveWeight(0); - idle.enabled = true; - idle.setEffectiveTimeScale(1); - idle.setEffectiveWeight(0); - walking.play(); - } - // } - } else { - // If not moving, but walking animation is playing, stop it and play idle animation - if (walking.isRunning()) { - // blend from walking to idle - walking.crossFadeTo(idle, 1); - // set the walking animation to lower weight so it blends into the idle animation - walking.enabled = true; - walking.setEffectiveTimeScale(1); - walking.setEffectiveWeight(0); - running.setEffectiveTimeScale(1); - running.setEffectiveWeight(0); - idle.enabled = true; - idle.setEffectiveTimeScale(1); - idle.setEffectiveWeight(1); - idle.play(); - } else if (running.isRunning()) { - // blend from running to idle - running.crossFadeTo(idle, 1); - // set the running animation to lower weight so it blends into the idle animation - running.enabled = true; - running.setEffectiveTimeScale(1); - running.setEffectiveWeight(0); - walking.setEffectiveTimeScale(1); - walking.setEffectiveWeight(0); - idle.enabled = true; - idle.setEffectiveTimeScale(1); - idle.setEffectiveWeight(1); - idle.play(); + + const rotation = [ + participantObject.parent.parent.rotation.x, + participantObject.parent.parent.rotation.y, + participantObject.parent.parent.rotation.z + ]; + + if ((countHangtime > 0) && lastNetworkUpdateTimeRef.current !== 0) { + const messageStopObject = { + [window.p2pcf.clientId]: { + isMoving: { + action: "jumpStop", + hangtime: countHangtime + }, + position: position, + rotation: rotation } + }; + const messageStop = JSON.stringify(messageStopObject); + window.p2pcf.broadcast(new TextEncoder().encode(messageStop)); + countHangtime = 0; + lastNetworkUpdateTimeRef.current = now; } + }, 100); } - if (participantObject) { - camera.lookAt( - participantObject.parent.position.x, - playerController.firstPerson.humanoid.humanBones.head.node.getWorldPosition(new Vector3()).y, - participantObject.parent.position.z - ); - - if (orbitRef.current){ - let newTarget = new Vector3( - participantObject.parent.position.x, - playerController.firstPerson.humanoid.humanBones.head.node.getWorldPosition(new Vector3()).y + 1.5, - participantObject.parent.position.z - ); - // lerpVectors() orbitRef from current position target to newTarget - orbitRef.current.target.lerpVectors(orbitRef.current.target, newTarget, 0.5); - } - } - - // update rigidBody's position - if (rigidRef.current && participantObject?.parent?.position?.x) { - // // match the rigidBody's position to the participantObject's position. - // set the rigidbody type to one that can be moved by setTranslation - if(props.movement.current.backward || props.movement.current.forward || props.movement.current.left || props.movement.current.right || falling.current === true) { - rigidRef.current.setBodyType(rapier.RigidBodyType.Dynamic, 1); - // rigidRef.current.setFriction(1); // Set the friction to 1 so the player doesn't slide - } else { - rigidRef.current.setBodyType(rapier.RigidBodyType.Fixed, 1); - } + wasJumping = false; + } + } - // set a const of the rigidBody's current position - rigidRef.current.setTranslation({ x: participantObject.parent.position.x, y: rigidBodyPosition.y, z: participantObject.parent.position.z}); + if (isMoving && characterRef.current.userData.canJump) { + countHangtime = 0; + + if (shift) { + if (walking.isRunning()) { + walking.crossFadeTo(running, 1.1); + } else { + idle.crossFadeTo(running, 1.1); } - if(props.movement.current.respawn === true){ - - const x = Number(props.spawnPoint[0]); - const y = Number(props.spawnPoint[1]); - const z = Number(props.spawnPoint[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)]; - if([x, y, z] !== [camera.position.x, camera.position.y, camera.position.z]){ - // Check if the converted values are valid and finite - // Set the camera's position - // orbitRef.position.set(randomPoint[0], randomPoint[1], randomPoint[2]); - castRef.current.setTranslation({ - x: randomPoint[0], - y: randomPoint[1], - z: randomPoint[2] - }); - participantObject.parent.position.set(randomPoint[0], randomPoint[1], randomPoint[2]); - // move the rigidRef to the new position - rigidRef.current.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); - - castRef.current.setTranslation({ - x: x, - y: y, - z: z - }); - } + running.enabled = true; + running.setEffectiveTimeScale(1); + running.setEffectiveWeight(1); + idle.enabled = true; + idle.setEffectiveTimeScale(1); + idle.setEffectiveWeight(0); + walking.enabled = true; + walking.setEffectiveTimeScale(1); + walking.setEffectiveWeight(0); + running.play(); + } else { + if (running.isRunning()) { + running.crossFadeTo(walking, 1); + } else { + idle.crossFadeTo(walking, 1); } - }); + walking.enabled = true; + walking.setEffectiveTimeScale(1); + walking.setEffectiveWeight(1); + running.enabled = true; + running.setEffectiveTimeScale(1); + running.setEffectiveWeight(0); + idle.enabled = true; + idle.setEffectiveTimeScale(1); + idle.setEffectiveWeight(0); + walking.play(); + } + } else { + if (characterRef.current.userData.canJump) { + isJumping = false; + if (walking.isRunning()) { + walking.crossFadeTo(idle, 1); + walking.enabled = true; + walking.setEffectiveTimeScale(1); + walking.setEffectiveWeight(0); + running.setEffectiveTimeScale(1); + running.setEffectiveWeight(0); + idle.enabled = true; + idle.setEffectiveTimeScale(1); + idle.setEffectiveWeight(1); + idle.play(); + } else if (running.isRunning()) { + running.crossFadeTo(idle, 1); + running.enabled = true; + running.setEffectiveTimeScale(1); + running.setEffectiveWeight(0); + walking.setEffectiveTimeScale(1); + walking.setEffectiveWeight(0); + idle.enabled = true; + idle.setEffectiveTimeScale(1); + idle.setEffectiveWeight(1); + idle.play(); + } + } + } + if (space) { + if (characterRef.current.userData.canJump) { + isJumping = true; + countHangtime = 0; + jump.setEffectiveTimeScale(1); + jump.setEffectiveWeight(1); + idle.setEffectiveTimeScale(0); + walking.setEffectiveTimeScale(0); + running.setEffectiveTimeScale(0); + idle.setEffectiveWeight(0); + walking.setEffectiveWeight(0); + running.setEffectiveWeight(0); + jump.setLoop(LoopOnce, 1); + jump.reset(); + jump.clampWhenFinished = true; + jump.play(); + } + } + } + }); - let animationFiles = [idleFile, walkingFile, runningFile]; - let animationsPromises = animationFiles.map(file => loadMixamoAnimation(file, currentVrm)); - - Promise.all(animationsPromises) - .then(animations => { - const idleAction = currentMixer.clipAction(animations[0]); - const walkingAction = currentMixer.clipAction(animations[1]); - const runningAction = currentMixer.clipAction(animations[2]); - idleAction.timeScale = 1; - walkingAction.timeScale = 1; - runningAction.timeScale = 1; + // Add this function at the top of your component or in a utility file + function debugVector3(name, vector) { + console.log(`${name}: x: ${vector.x.toFixed(2)}, y: ${vector.y.toFixed(2)}, z: ${vector.z.toFixed(2)}`); + } + + + function applySimpleIK(spineBone, boneChain, targetPosition, iterations = 10, elbowWeight = 0.8, wristWeight = 0.2, shoulderWeight = 0.2) { + const endEffector = boneChain[boneChain.length - 1]; - animationsRef.current = { idle: idleAction, walking: walkingAction, running: runningAction }; - idleAction.play(); - }); + for (let i = 0; i < iterations; i++) { + for (let j = boneChain.length - 2; j >= 0; j--) { + const bone = boneChain[j]; + const nextBone = boneChain[j + 1]; + + const toTarget = targetPosition.clone().sub(bone.getWorldPosition(new Vector3())); + const toNextBone = nextBone.getWorldPosition(new Vector3()).sub(bone.getWorldPosition(new Vector3())); + + const quaternion = new Quaternion().setFromUnitVectors(toNextBone.normalize(), toTarget.normalize()); + + let weight; + if (j === 0) { + weight = shoulderWeight; + } else if (j === 1) { + const spineWorldPosition = new Vector3(); + spineBone.getWorldPosition(spineWorldPosition); + const distanceToBody = targetPosition.distanceTo(spineWorldPosition); + const elbowBendThreshold = 0.3; - return ( + if (distanceToBody < elbowBendThreshold) { + const elbowBendAngle = Math.PI / 16; + const elbowBendAxis = new Vector3(0, 0, 1); + const elbowBendQuaternion = new Quaternion().setFromAxisAngle(elbowBendAxis, elbowBendAngle); + quaternion.multiply(elbowBendQuaternion); + } + weight = elbowWeight; + } else { + weight = wristWeight; + } + // limit weight to reasonable values + weight = MathUtils.clamp(weight, 0, 1); + + bone.quaternion.slerp(quaternion, weight); + } + } + } + + let frameCounter = 0; + const logFrequency = 60; // Log every 60 frames, adjust this value as needed + function conditionalLog(message, ...optionalParams) { + frameCounter++; + if (frameCounter % logFrequency === 0) { + console.log(message, ...optionalParams); + } + } + const HAND_VERTICAL_OFFSET = -1.0; + const ARM_FORWARD = new Vector3(1, 0, 0); + const UP = new Vector3(0, 1, 0); + + const armRef = useRef(null); + const rigRef = useRef(null); +// Add these as component-level variables +const debugArrows = { + leftForward: null, + leftUp: null, + rightForward: null, + rightUp: null + }; + + useFrame((state, delta) => { + if (isPresenting && playerControllerRef.current && playerControllerRef.current.avatar) { + const avatar = playerControllerRef.current.avatar; + const avatarParent = avatar.model.parent.parent; + + // Update avatar inputs + avatar.inputs.hmd.position.copy(camera.position); + + // Update left and right hand positions and rotations + const leftHandPosition = leftController.controller.position.clone(); + const leftHandQuaternion = leftController.controller.quaternion.clone(); + avatarParent.worldToLocal(leftHandPosition); + avatar.inputs.leftGamepad.position.copy(leftHandPosition); + avatar.inputs.leftGamepad.quaternion.copy(leftHandQuaternion); + + const rightHandPosition = rightController.controller.position.clone(); + const rightHandQuaternion = rightController.controller.quaternion.clone(); + avatarParent.worldToLocal(rightHandPosition); + avatar.inputs.rightGamepad.position.copy(rightHandPosition); + avatar.inputs.rightGamepad.quaternion.copy(rightHandQuaternion); + + avatar.setFloorHeight(0); + avatar.update(delta); + } + }); + + const keyboardMap = [ + { name: "forward", keys: ["ArrowUp", "KeyW"] }, + { name: "backward", keys: ["ArrowDown", "KeyS"] }, + { name: "leftward", keys: ["ArrowLeft", "KeyA"] }, + { name: "rightward", keys: ["ArrowRight", "KeyD"] }, + { name: "jump", keys: ["Space"] }, + { name: "run", keys: ["Shift"] }, + // Optional animation key map + { name: "action1", keys: ["1"] }, + { name: "action2", keys: ["2"] }, + { name: "action3", keys: ["3"] }, + { name: "action4", keys: ["KeyF"] }, + ]; + + const canvas = document.querySelector('div.threeov-main-canvas'); + + return ( + <> + + + {isModelLoaded && playerControllerRef.current && ( <> - {playerController && ( - <> - - {/* */} - - - - - { - canMoveRef.current = false; - }} - onIntersectionExit={({ manifold, target }) => { - canMoveRef.current = true; - }} - angularVelocity={[0, 0, 0]} - linearVelocity={[0, 0, 0]} - > - console.log("enter")} - position={[0, 1.4, -0.5]} - args={[0.03, 0.03, 0.03]} - /> - - - )} + {avatarIsSprite && ( + + )} - ); - } + )} + + + + ); } diff --git a/blocks/environment/components/TeleportTravel.js b/blocks/environment/components/TeleportTravel.js index 6bc8ef9..9d67e92 100644 --- a/blocks/environment/components/TeleportTravel.js +++ b/blocks/environment/components/TeleportTravel.js @@ -1,14 +1,18 @@ -import { Raycaster, Vector3 } from "three"; -import { useXR, Interactive } from "@react-three/xr"; +import { Raycaster, Vector3, Mesh, MeshBasicMaterial, BoxGeometry } from "three"; +import { useXR, Interactive, useController, useTeleportation } from "@react-three/xr"; import { useFrame, useThree } from "@react-three/fiber"; import { useCallback, useRef, useState, useEffect } from "react"; import { useRapier, useRigidBody, RigidBody } from "@react-three/rapier"; +import { + Text, +} from "@react-three/drei"; export function TeleportIndicator(props) { + return ( <> - + @@ -24,6 +28,88 @@ export function ClickIndicatorObject(props) { ); } +function Button({ onClick, position, color, hoverColor }) { + const mesh = React.useRef(); + + const [hovered, setHovered] = React.useState(false); + const currentColor = hovered ? hoverColor : color; + + return ( + setHovered(true)} + onBlur={() => setHovered(false)} + > + + + + + + ); +} + +function Menu() { + const menuRef = useRef(); + const { camera } = useThree(); + const { player, controllers } = useXR(); + const [ muted, setMuted ] = useState(false); + const handleButtonClick = () => { + console.log("Button clicked"); + if(window.localStream){ + // loop through all audio tracks and mute them + for (let i = 0; i < window.localStream.getAudioTracks().length; i++) { + window.localStream.getAudioTracks()[i].enabled = !window.localStream.getAudioTracks()[i].enabled; + } + // if(muted){ + // window.localStream.getAudioTracks()[0].enabled = false; + // } else { + // window.localStream.getAudioTracks()[0].enabled = true; + // } + // console.log("window.localStream", window.localStream.getAudioTracks()[0]); + } + setMuted(!muted); + }; + + // when the player is available, add the menu to the player + // useEffect(() => { + // console.log("hit the player ref", player) + // if (player && menuRef.current) { + // //menuRef.current.visible = true; + // player.add(menuRef.current); + // menuRef.current.position.set(0, 2, -0.8); + // } + // console.log(window.localStream); + // }, [player]); + + + // useFrame(() => { + // if (menuRef.current) { + + // } + // }); + + return ( + + -
-
*/} - {/* */} + <> +
+ {/*
Room:
*/} + {/*

Peers

*/} + {/*
*/} + {/*

Messages

*/} +
+
+ + Enter VR + + { ( networkingBlock.length > 0 ) && ( + + )} +
+
+
+
+ + { positionY={positionY} rotationY={rotationY} animations={animations} + camCollisions={camCollisions} backgroundColor={backgroundColor} userData={userData} postSlug={postSlug} defaultAvatarAnimation={defaultAvatarAnimation} + networkingBlock={networkingBlock} modelsToAdd={modelsToAdd} portalsToAdd={portalsToAdd} imagesToAdd={imagesToAdd} @@ -141,14 +285,87 @@ threeApp.forEach((threeApp) => { audiosToAdd={audiosToAdd} lightsToAdd={lightsToAdd} spawnPoint={spawnPoint ? spawnPoint : null} - htmlToAdd={htmlToAdd} + textToAdd={textToAdd} npcsToAdd={npcsToAdd} sky={sky ? sky : ""} previewImage={threePreviewImage} hdr ={hdr ? hdr : ""} /> -\ , - threeApp + + ); + } +}); + +threeObjectViewerBlocks.forEach((threeApp) => { + const root = createRoot( threeApp ); + + let threeUrl, deviceTarget, backgroundColor, zoom, scale, hasZoom, hasTip, positionY, rotationY, animations; + if (threeApp) { + if(threeApp.tagName.toLowerCase() === 'three-object-viewer-block') { + deviceTarget = threeApp.getAttribute('device-target'); + threeUrl = threeApp.getAttribute('three-object-url'); + scale = threeApp.getAttribute('scale'); + backgroundColor = threeApp.getAttribute('bg-color'); + zoom = threeApp.getAttribute('zoom'); + hasZoom = threeApp.getAttribute('has-zoom'); + hasTip = threeApp.getAttribute('has-tip'); + positionY = threeApp.getAttribute('position-y'); + rotationY = threeApp.getAttribute('rotation-y'); + animations = threeApp.getAttribute('animations'); + } else { + threeUrl = threeApp.querySelector("p.three-object-block-url") + ? threeApp.querySelector("p.three-object-block-url").innerText + : ""; + deviceTarget = threeApp.querySelector( + "p.three-object-block-device-target" + ) + ? threeApp.querySelector("p.three-object-block-device-target") + .innerText + : "2D"; + backgroundColor = threeApp.querySelector( + "p.three-object-background-color" + ) + ? threeApp.querySelector("p.three-object-background-color") + .innerText + : "#ffffff"; + zoom = threeApp.querySelector("p.three-object-zoom") + ? threeApp.querySelector("p.three-object-zoom").innerText + : 90; + scale = threeApp.querySelector("p.three-object-scale") + ? threeApp.querySelector("p.three-object-scale").innerText + : 1; + hasZoom = threeApp.querySelector("p.three-object-has-zoom") + ? threeApp.querySelector("p.three-object-has-zoom").innerText + : false; + hasTip = threeApp.querySelector("p.three-object-has-tip") + ? threeApp.querySelector("p.three-object-has-tip").innerText + : true; + positionY = threeApp.querySelector("p.three-object-position-y") + ? threeApp.querySelector("p.three-object-position-y").innerText + : 0; + rotationY = threeApp.querySelector("p.three-object-rotation-y") + ? threeApp.querySelector("p.three-object-rotation-y").innerText + : 0; + animations = threeApp.querySelector("p.three-object-animations") + ? threeApp.querySelector("p.three-object-animations").innerText + : ""; + } + root.render( + ); } }); + diff --git a/blocks/environment/index.js b/blocks/environment/index.js index 1caacdd..6110389 100644 --- a/blocks/environment/index.js +++ b/blocks/environment/index.js @@ -3,8 +3,7 @@ import Edit from "./Edit"; import Save from "./Save"; import { useBlockProps } from "@wordpress/block-editor"; import React from "react"; - -import { useState } from '@wordpress/element'; +import Deprecated from "./Deprecated"; function Loading() { return ( @@ -29,7 +28,7 @@ const icon = ( ); - +const deprecated = Deprecated(); const blockConfig = require("./block.json"); registerBlockType(blockConfig.name, { ...blockConfig, @@ -37,261 +36,5 @@ registerBlockType(blockConfig.name, { apiVersion: 2, edit: Edit, save: Save, - deprecated: [ - { - attributes: { - bg_color: { - type: "string", - default: "#FFFFFF" - }, - zoom: { - type: "integer", - default: 90 - }, - scale: { - type: "integer", - default: 1 - }, - positionX: { - type: "integer", - default: 0 - }, - positionY: { - type: "integer", - default: 0 - }, - rotationY: { - type: "integer", - default: 0 - }, - threeObjectUrl: { - type: "string", - default: null - }, - hasZoom: { - type: "bool", - default: false - }, - hasTip: { - type: "bool", - default: true - }, - deviceTarget: { - type: "string", - default: "2d" - } - }, - save(props) { - return ( -
- <> -
-

- {props.attributes.deviceTarget} -

-

- {props.attributes.threeObjectUrl} -

-

- {props.attributes.scale} -

-

- {props.attributes.bg_color} -

-

- {props.attributes.zoom} -

-

- {props.attributes.hasZoom ? 1 : 0} -

-

- {props.attributes.hasTip ? 1 : 0} -

-

- {props.attributes.positionY} -

-

- {props.attributes.rotationY} -

-

- {props.attributes.scale} -

-
- -
- ); - } - }, - { - attributes: { - bg_color: { - type: "string", - default: "#FFFFFF" - }, - zoom: { - type: "integer", - default: 90 - }, - scale: { - type: "integer", - default: 1 - }, - positionX: { - type: "integer", - default: 0 - }, - positionY: { - type: "integer", - default: 0 - }, - rotationY: { - type: "integer", - default: 0 - }, - threeObjectUrl: { - type: "string", - default: null - }, - hasZoom: { - type: "bool", - default: false - }, - hasTip: { - type: "bool", - default: true - }, - deviceTarget: { - type: "string", - default: "2d" - }, - animations: { - type: "string", - default: "" - } - }, - save(props) { - return ( -
- <> -
-

- {props.attributes.deviceTarget} -

-

- {props.attributes.threeObjectUrl} -

-

- {props.attributes.scale} -

-

- {props.attributes.bg_color} -

-

- {props.attributes.zoom} -

-

- {props.attributes.hasZoom ? 1 : 0} -

-

- {props.attributes.hasTip ? 1 : 0} -

-

- {props.attributes.positionY} -

-

- {props.attributes.rotationY} -

-

- {props.attributes.scale} -

-

- {props.attributes.animations} -

-
- -
- ); - } - }, - { - attributes: { - align: { - type: "string", - default: "full" - }, - scale: { - type: "integer", - default: 1 - }, - positionX: { - type: "integer", - default: 0 - }, - positionY: { - type: "integer", - default: 0 - }, - rotationY: { - type: "integer", - default: 0 - }, - threeObjectUrl: { - type: "string", - default: null - }, - threePreviewImage: { - type: "string", - default: null - }, - deviceTarget: { - type: "string", - default: "vr" - }, - animations: { - type: "string", - default: "" - } - }, - save(props) { - return ( -
- <> -
-

- {props.attributes.deviceTarget} -

-

- {props.attributes.threeObjectUrl} -

-

{props.attributes.scale}

-

- {props.attributes.bg_color} -

-

{props.attributes.zoom}

-

- {props.attributes.hasZoom ? 1 : 0} -

-

- {props.attributes.hasTip ? 1 : 0} -

-

- {props.attributes.positionY} -

-

- {props.attributes.rotationY} -

-

{props.attributes.scale}

-

- {props.attributes.threePreviewImage} -

-

- {props.attributes.animations} -

-
- -
- ); - } - } - ] + deprecated: deprecated }); diff --git a/blocks/environment/style.scss b/blocks/environment/style.scss index 5e8f67d..ad74d13 100644 --- a/blocks/environment/style.scss +++ b/blocks/environment/style.scss @@ -1,3 +1,8 @@ +.asset-item.selected { + border: 2px solid #0073aa; + box-shadow: 0 0 5px rgba(0, 115, 170, 0.8); + } + .wp-block-three-object-block { background-color: #fff; color: #000; diff --git a/blocks/environment/utils/DynLineMesh.js b/blocks/environment/utils/DynLineMesh.js new file mode 100644 index 0000000..235931a --- /dev/null +++ b/blocks/environment/utils/DynLineMesh.js @@ -0,0 +1,180 @@ +import * as THREE from 'three'; + +class DynLineMesh extends THREE.LineSegments{ + _defaultColor = 0x00ff00; + _cnt = 0; + _verts = []; + _color = []; + _config = []; + _dirty = false; + + constructor( initSize = 20 ){ + super( + _newDynLineMeshGeometry( + new Float32Array( initSize * 2 * 3 ), // Two Points for Each Line + new Float32Array( initSize * 2 * 3 ), + new Float32Array( initSize * 2 * 1 ), + false + ), + newDynLineMeshMaterial() //new THREE.PointsMaterial( { color: 0xffffff, size:8, sizeAttenuation:false } ) + ); + + this.geometry.setDrawRange( 0, 0 ); + this.onBeforeRender = ()=>{ if( this._dirty ) this._updateGeometry(); } + } + + reset(){ + this._cnt = 0; + this._verts.length = 0; + this._color.length = 0; + this._config.length = 0; + this.geometry.setDrawRange( 0, 0 ); + return this; + } + + add( p0, p1, color0=this._defaultColor, color1=null, isDash=false ){ + this._verts.push( p0[0], p0[1], p0[2], p1[0], p1[1], p1[2] ); + this._color.push( ...glColor( color0 ), ...glColor( (color1 != null) ? color1:color0 ) ); + + if( isDash ){ + const len = Math.sqrt( + (p1[0] - p0[0]) ** 2 + + (p1[1] - p0[1]) ** 2 + + (p1[2] - p0[2]) ** 2 + ); + this._config.push( 0, len ); + }else{ + this._config.push( 0, 0 ); + } + + this._cnt++; + this._dirty = true; + return this; + } + + _updateGeometry(){ + const geo = this.geometry; + const bVerts = geo.attributes.position; + const bColor = geo.attributes.color; //this.geometry.index; + const bConfig = geo.attributes.config; + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + if( this._verts.length > bVerts.array.length || + this._color.length > bColor.array.length || + this._config.length > bConfig.array.length + ){ + if( this.geometry ) this.geometry.dispose(); + this.geometry = _newDynLineMeshGeometry( this._verts, this._color, this._config ); + this._dirty = false; + return; + } + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + bVerts.array.set( this._verts ); + bVerts.count = this._verts.length / 3; + bVerts.needsUpdate = true; + + bColor.array.set( this._color ); + bColor.count = this._color.length / 3; + bColor.needsUpdate = true; + + bConfig.array.set( this._config ); + bConfig.count = this._config.length / 1; + bConfig.needsUpdate = true; + + geo.setDrawRange( 0, bVerts.count ); + geo.computeBoundingBox(); + geo.computeBoundingSphere(); + + this._dirty = false; + } +} + +//#region SUPPORT +function _newDynLineMeshGeometry( aVerts, aColor, aConfig, doCompute=true ){ + //if( !( aVerts instanceof Float32Array) ) aVerts = new Float32Array( aVerts ); + //if( !( aColor instanceof Float32Array) ) aColor = new Float32Array( aColor ); + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const bVerts = new THREE.Float32BufferAttribute( aVerts, 3 ); + const bColor = new THREE.Float32BufferAttribute( aColor, 3 ); + const bConfig = new THREE.Float32BufferAttribute( aConfig, 1 ); + bVerts.setUsage( THREE.DynamicDrawUsage ); + bColor.setUsage( THREE.DynamicDrawUsage ); + bConfig.setUsage( THREE.DynamicDrawUsage ); + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const geo = new THREE.BufferGeometry(); + geo.setAttribute( 'position', bVerts ); + geo.setAttribute( 'color', bColor ); + geo.setAttribute( 'config', bConfig ); + + if( doCompute ){ + geo.computeBoundingSphere(); + geo.computeBoundingBox(); + } + return geo; +} + +function glColor( hex, out = null ){ + const NORMALIZE_RGB = 1 / 255; + out = out || [0,0,0]; + + out[0] = ( hex >> 16 & 255 ) * NORMALIZE_RGB; + out[1] = ( hex >> 8 & 255 ) * NORMALIZE_RGB; + out[2] = ( hex & 255 ) * NORMALIZE_RGB; + + return out; +} +//#endregion + +//#region SHADER + +function newDynLineMeshMaterial(){ + return new THREE.RawShaderMaterial({ + depthTest : false, + transparent : true, + uniforms : { + dashSeg : { value : 1 / 0.07 }, + dashDiv : { value : 0.4 }, + }, + vertexShader : `#version 300 es + in vec3 position; + in vec3 color; + in float config; + + uniform mat4 modelViewMatrix; + uniform mat4 projectionMatrix; + uniform float u_scale; + + out vec3 fragColor; + out float fragLen; + + void main(){ + vec4 wPos = modelViewMatrix * vec4( position, 1.0 ); + + fragColor = color; + fragLen = config; + + gl_Position = projectionMatrix * wPos; + }`, + fragmentShader : `#version 300 es + precision mediump float; + + uniform float dashSeg; + uniform float dashDiv; + + in vec3 fragColor; + in float fragLen; + out vec4 outColor; + + void main(){ + float alpha = 1.0; + if( fragLen > 0.0 ) alpha = step( dashDiv, fract( fragLen * dashSeg ) ); + outColor = vec4( fragColor, alpha ); + }`}); +} + +//#endregion + +export default DynLineMesh; \ No newline at end of file diff --git a/blocks/environment/utils/ShapePointsMesh.js b/blocks/environment/utils/ShapePointsMesh.js new file mode 100644 index 0000000..8842a53 --- /dev/null +++ b/blocks/environment/utils/ShapePointsMesh.js @@ -0,0 +1,278 @@ +import * as THREE from 'three'; + +class ShapePointsMesh extends THREE.Points{ + _defaultShape = 1; + _defaultSize = 6; + _defaultColor = 0x00ff00; + _cnt = 0; + _verts = []; + _color = []; + _config = []; + _dirty = false; + + constructor( initSize = 20 ){ + super( + _newShapePointsMeshGeometry( + new Float32Array( initSize * 3 ), + new Float32Array( initSize * 3 ), + new Float32Array( initSize * 2 ), + false + ), + newShapePointsMeshMaterial() //new THREE.PointsMaterial( { color: 0xffffff, size:8, sizeAttenuation:false } ) + ); + + this.geometry.setDrawRange( 0, 0 ); + this.onBeforeRender = ()=>{ if( this._dirty ) this._updateGeometry(); } + } + + reset(){ + this._cnt = 0; + this._verts.length = 0; + this._color.length = 0; + this._config.length = 0; + this.geometry.setDrawRange( 0, 0 ); + return this; + } + + add( pos, color = this._defaultColor, size = this._defaultSize, shape = this._defaultShape ){ + this._verts.push( pos[0], pos[1], pos[2] ); + this._color.push( ...glColor( color ) ); + this._config.push( size, shape ); + this._cnt++; + this._dirty = true; + return this; + } + + setColorAt( idx, color ){ + const c = glColor( color ); + idx *= 3; + + this._color[ idx ] = c[ 0 ]; + this._color[ idx + 1 ] = c[ 1 ]; + this._color[ idx + 2 ] = c[ 2 ]; + this._dirty = true; + return this; + } + + setPosAt( idx, pos ){ + idx *= 3; + this._verts[ idx ] = pos[ 0 ]; + this._verts[ idx + 1 ] = pos[ 1 ]; + this._verts[ idx + 2 ] = pos[ 2 ]; + this._dirty = true; + return this; + } + + getPosAt( idx ){ + idx *= 3; + return [ + this._verts[ idx + 0 ], + this._verts[ idx + 1 ], + this._verts[ idx + 2 ], + ]; + } + + _updateGeometry(){ + const geo = this.geometry; + const bVerts = geo.attributes.position; + const bColor = geo.attributes.color; //this.geometry.index; + const bConfig = geo.attributes.config; + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + if( this._verts.length > bVerts.array.length || + this._color.length > bColor.array.length || + this._config.length > bConfig.array.length + ){ + if( this.geometry ) this.geometry.dispose(); + this.geometry = _newShapePointsMeshGeometry( this._verts, this._color, this._config ); + this._dirty = false; + return; + } + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + bVerts.array.set( this._verts ); + bVerts.count = this._verts.length / 3; + bVerts.needsUpdate = true; + + bColor.array.set( this._color ); + bColor.count = this._color.length / 3; + bColor.needsUpdate = true; + + bConfig.array.set( this._config ); + bConfig.count = this._config.length / 2; + bConfig.needsUpdate = true; + + geo.setDrawRange( 0, bVerts.count ); + geo.computeBoundingBox(); + geo.computeBoundingSphere(); + + this._dirty = false; + } +} + +//#region SUPPORT +function _newShapePointsMeshGeometry( aVerts, aColor, aConfig, doCompute=true ){ + //if( !( aVerts instanceof Float32Array) ) aVerts = new Float32Array( aVerts ); + //if( !( aColor instanceof Float32Array) ) aColor = new Float32Array( aColor ); + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const bVerts = new THREE.Float32BufferAttribute( aVerts, 3 ); + const bColor = new THREE.Float32BufferAttribute( aColor, 3 ); + const bConfig = new THREE.Float32BufferAttribute( aConfig, 2 ); + bVerts.setUsage( THREE.DynamicDrawUsage ); + bColor.setUsage( THREE.DynamicDrawUsage ); + bConfig.setUsage( THREE.DynamicDrawUsage ); + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const geo = new THREE.BufferGeometry(); + geo.setAttribute( 'position', bVerts ); + geo.setAttribute( 'color', bColor ); + geo.setAttribute( 'config', bConfig ); + + if( doCompute ){ + geo.computeBoundingSphere(); + geo.computeBoundingBox(); + } + return geo; +} + +function glColor( hex, out = null ){ + const NORMALIZE_RGB = 1 / 255; + out = out || [0,0,0]; + + out[0] = ( hex >> 16 & 255 ) * NORMALIZE_RGB; + out[1] = ( hex >> 8 & 255 ) * NORMALIZE_RGB; + out[2] = ( hex & 255 ) * NORMALIZE_RGB; + + return out; +} +//#endregion + +//#region SHADER + +function newShapePointsMeshMaterial(){ + + return new THREE.RawShaderMaterial({ + depthTest : false, + transparent : true, + uniforms : { u_scale:{ value : 20.0 } }, + vertexShader : `#version 300 es + in vec3 position; + in vec3 color; + in vec2 config; + + uniform mat4 modelViewMatrix; + uniform mat4 projectionMatrix; + uniform float u_scale; + + out vec3 fragColor; + flat out int fragShape; + + void main(){ + vec4 wPos = modelViewMatrix * vec4( position.xyz, 1.0 ); + + fragColor = color; + fragShape = int( config.y ); + + gl_Position = projectionMatrix * wPos; + gl_PointSize = config.x * ( u_scale / -wPos.z ); + + // Get pnt to be World Space Size + //gl_PointSize = view_port_size.y * projectionMatrix[1][5] * 1.0 / gl_Position.w; + //gl_PointSize = view_port_size.y * projectionMatrix[1][1] * 1.0 / gl_Position.w; + }`, + fragmentShader : `#version 300 es + precision mediump float; + + #define PI 3.14159265359 + #define PI2 6.28318530718 + + in vec3 fragColor; + flat in int fragShape; + out vec4 outColor; + + float circle(){ + vec2 coord = gl_PointCoord * 2.0 - 1.0; // v_uv * 2.0 - 1.0; + float radius = dot( coord, coord ); + float dxdy = fwidth( radius ); + return smoothstep( 0.90 + dxdy, 0.90 - dxdy, radius ); + } + + float ring( float inner ){ + vec2 coord = gl_PointCoord * 2.0 - 1.0; + float radius = dot( coord, coord ); + float dxdy = fwidth( radius ); + return smoothstep( inner - dxdy, inner + dxdy, radius ) - + smoothstep( 1.0 - dxdy, 1.0 + dxdy, radius ); + } + + float diamond(){ + // http://www.numb3r23.net/2015/08/17/using-fwidth-for-distance-based-anti-aliasing/ + const float radius = 0.5; + + float dst = dot( abs(gl_PointCoord-vec2(0.5)), vec2(1.0) ); + float aaf = fwidth( dst ); + return 1.0 - smoothstep( radius - aaf, radius, dst ); + } + + float poly( int sides, float offset, float scale ){ + // https://thebookofshaders.com/07/ + vec2 coord = gl_PointCoord * 2.0 - 1.0; + + coord.y += offset; + coord *= scale; + + float a = atan( coord.x, coord.y ) + PI; // Angle of Pixel + float r = PI2 / float( sides ); // Radius of Pixel + float d = cos( floor( 0.5 + a / r ) * r-a ) * length( coord ); + float f = fwidth( d ); + return smoothstep( 0.5, 0.5 - f, d ); + } + + // signed distance to a n-star polygon with external angle en + float sdStar( float r, int n, float m ){ // m=[2,n] + vec2 p = vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * 2.0 - 1.0; + + // these 4 lines can be precomputed for a given shape + float an = 3.141593/float(n); + float en = 3.141593/m; + vec2 acs = vec2(cos(an),sin(an)); + vec2 ecs = vec2(cos(en),sin(en)); // ecs=vec2(0,1) and simplify, for regular polygon, + + // reduce to first sector + float bn = mod(atan(p.x,p.y),2.0*an) - an; + p = length(p)*vec2(cos(bn),abs(sin(bn))); + + // line sdf + p -= r*acs; + p += ecs*clamp( -dot(p,ecs), 0.0, r*acs.y/ecs.y); + + float dist = length(p)*sign(p.x); + float f = fwidth( dist ); + + return smoothstep( 0.0, 0.0 - f, dist ); + } + + + void main(){ + float alpha = 1.0; + + if( fragShape == 1 ) alpha = circle(); + if( fragShape == 2 ) alpha = diamond(); + if( fragShape == 3 ) alpha = poly( 3, 0.2, 1.0 ); // Triangle + if( fragShape == 4 ) alpha = poly( 5, 0.0, 0.65 ); // Pentagram + if( fragShape == 5 ) alpha = poly( 6, 0.0, 0.65 ); // Hexagon + if( fragShape == 6 ) alpha = ring( 0.2 ); + if( fragShape == 7 ) alpha = ring( 0.7 ); + if( fragShape == 8 ) alpha = sdStar( 1.0, 3, 2.3 ); + if( fragShape == 9 ) alpha = sdStar( 1.0, 6, 2.5 ); + if( fragShape == 10 ) alpha = sdStar( 1.0, 4, 2.4 ); + if( fragShape == 11 ) alpha = sdStar( 1.0, 5, 2.8 ); + + outColor = vec4( fragColor, alpha ); + }`}); +} + +//#endregion + +export default ShapePointsMesh; \ No newline at end of file diff --git a/blocks/environment/utils/rigMap.js b/blocks/environment/utils/rigMap.js new file mode 100644 index 0000000..9501d52 --- /dev/null +++ b/blocks/environment/utils/rigMap.js @@ -0,0 +1,63 @@ + + /** + * A map from Mixamo rig name to VRM Humanoid bone name + */ + const mixamoVRMRigMap = { + mixamorigHips: 'hips', + mixamorigSpine: 'spine', + mixamorigSpine1: 'chest', + mixamorigSpine2: 'upperChest', + mixamorigNeck: 'neck', + mixamorigHead: 'head', + mixamorigLeftShoulder: 'leftShoulder', + mixamorigLeftArm: 'leftUpperArm', + mixamorigLeftForeArm: 'leftLowerArm', + mixamorigLeftHand: 'leftHand', + mixamorigLeftHandThumb1: 'leftThumbMetacarpal', + mixamorigLeftHandThumb2: 'leftThumbProximal', + mixamorigLeftHandThumb3: 'leftThumbDistal', + mixamorigLeftHandIndex1: 'leftIndexProximal', + mixamorigLeftHandIndex2: 'leftIndexIntermediate', + mixamorigLeftHandIndex3: 'leftIndexDistal', + mixamorigLeftHandMiddle1: 'leftMiddleProximal', + mixamorigLeftHandMiddle2: 'leftMiddleIntermediate', + mixamorigLeftHandMiddle3: 'leftMiddleDistal', + mixamorigLeftHandRing1: 'leftRingProximal', + mixamorigLeftHandRing2: 'leftRingIntermediate', + mixamorigLeftHandRing3: 'leftRingDistal', + mixamorigLeftHandPinky1: 'leftLittleProximal', + mixamorigLeftHandPinky2: 'leftLittleIntermediate', + mixamorigLeftHandPinky3: 'leftLittleDistal', + mixamorigRightShoulder: 'rightShoulder', + mixamorigRightArm: 'rightUpperArm', + mixamorigRightForeArm: 'rightLowerArm', + mixamorigRightHand: 'rightHand', + mixamorigRightHandPinky1: 'rightLittleProximal', + mixamorigRightHandPinky2: 'rightLittleIntermediate', + mixamorigRightHandPinky3: 'rightLittleDistal', + mixamorigRightHandRing1: 'rightRingProximal', + mixamorigRightHandRing2: 'rightRingIntermediate', + mixamorigRightHandRing3: 'rightRingDistal', + mixamorigRightHandMiddle1: 'rightMiddleProximal', + mixamorigRightHandMiddle2: 'rightMiddleIntermediate', + mixamorigRightHandMiddle3: 'rightMiddleDistal', + mixamorigRightHandIndex1: 'rightIndexProximal', + mixamorigRightHandIndex2: 'rightIndexIntermediate', + mixamorigRightHandIndex3: 'rightIndexDistal', + mixamorigRightHandThumb1: 'rightThumbMetacarpal', + mixamorigRightHandThumb2: 'rightThumbProximal', + mixamorigRightHandThumb3: 'rightThumbDistal', + mixamorigLeftUpLeg: 'leftUpperLeg', + mixamorigLeftLeg: 'leftLowerLeg', + mixamorigLeftFoot: 'leftFoot', + mixamorigLeftToeBase: 'leftToes', + mixamorigRightUpLeg: 'rightUpperLeg', + mixamorigRightLeg: 'rightLowerLeg', + mixamorigRightFoot: 'rightFoot', + mixamorigRightToeBase: 'rightToes', +}; + +// make function to return the mixamo rig +export function getMixamoRig() { + return mixamoVRMRigMap; +} diff --git a/blocks/model-block/Deprecated.js b/blocks/model-block/Deprecated.js new file mode 100644 index 0000000..132ab6a --- /dev/null +++ b/blocks/model-block/Deprecated.js @@ -0,0 +1,106 @@ +import { useBlockProps } from "@wordpress/block-editor"; + +export default function Deprecated(){ + return [ + { + attributes: { + scaleX: { + type: "int", + default:1 + }, + name: { + type: "string" + }, + scaleY: { + type: "int", + default:1 + }, + scaleZ: { + type: "int", + default:1 + }, + positionX: { + type: "int", + default:0 + }, + positionY: { + type: "int", + default:0 + }, + positionZ: { + type: "int", + default:0 + }, + rotationX: { + type: "int", + default:0 + }, + rotationY: { + type: "int", + default:0 + }, + rotationZ: { + type: "int", + default:0 + }, + threeObjectUrl: { + type: "string", + default: null + }, + animations: { + type: "string", + default: "" + }, + alt: { + type: "string", + default: "" + }, + collidable: { + type: "boolean", + default: false + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.threeObjectUrl} +

+

{props.attributes.scaleX}

+

{props.attributes.scaleY}

+

{props.attributes.scaleZ}

+

+ {props.attributes.positionX} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.positionZ} +

+

+ {props.attributes.rotationX} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.rotationZ} +

+

+ {props.attributes.animations} +

+

+ {props.attributes.collidable ? 1 : 0} +

+

{props.attributes.alt}

+
+ +
+ ); + } + } + ]; +} \ No newline at end of file diff --git a/blocks/model-block/Edit.js b/blocks/model-block/Edit.js index 423d888..50d2462 100644 --- a/blocks/model-block/Edit.js +++ b/blocks/model-block/Edit.js @@ -149,6 +149,7 @@ export default function Edit({ attributes, setAttributes, isSelected, clientId } label={ __( "GLB File", 'three-object-viewer' ) } allowedTypes={ALLOWED_MEDIA_TYPES} value={attributes.threeObjectUrl} + threeov={true} render={({ open }) => (
+ ); } diff --git a/blocks/model-block/block.json b/blocks/model-block/block.json index 8d1cb20..11b3304 100644 --- a/blocks/model-block/block.json +++ b/blocks/model-block/block.json @@ -54,10 +54,10 @@ }, "collidable": { "type": "boolean", - "default": true + "default": false } }, - "category": "design", + "category": "spatial", "parent": [ "three-object-viewer/environment" ], "apiVersion": 2, "supports": { diff --git a/blocks/model-block/index.js b/blocks/model-block/index.js index fc9e326..7bf6e78 100644 --- a/blocks/model-block/index.js +++ b/blocks/model-block/index.js @@ -2,6 +2,7 @@ import { registerBlockType } from "@wordpress/blocks"; import Edit from "./Edit"; import Save from "./Save"; import { useBlockProps } from "@wordpress/block-editor"; +import Deprecated from "./Deprecated"; const icon = ( ); - +const deprecated = Deprecated(); const blockConfig = require("./block.json"); registerBlockType(blockConfig.name, { ...blockConfig, @@ -23,106 +24,5 @@ registerBlockType(blockConfig.name, { apiVersion: 2, edit: Edit, save: Save, - deprecated: [ - { - attributes: { - scaleX: { - type: "int", - default:1 - }, - name: { - type: "string" - }, - scaleY: { - type: "int", - default:1 - }, - scaleZ: { - type: "int", - default:1 - }, - positionX: { - type: "int", - default:0 - }, - positionY: { - type: "int", - default:0 - }, - positionZ: { - type: "int", - default:0 - }, - rotationX: { - type: "int", - default:0 - }, - rotationY: { - type: "int", - default:0 - }, - rotationZ: { - type: "int", - default:0 - }, - threeObjectUrl: { - type: "string", - default: null - }, - animations: { - type: "string", - default: "" - }, - alt: { - type: "string", - default: "" - }, - collidable: { - type: "boolean", - default: false - } - }, - save(props) { - return ( -
- <> -
-

- {props.attributes.threeObjectUrl} -

-

{props.attributes.scaleX}

-

{props.attributes.scaleY}

-

{props.attributes.scaleZ}

-

- {props.attributes.positionX} -

-

- {props.attributes.positionY} -

-

- {props.attributes.positionZ} -

-

- {props.attributes.rotationX} -

-

- {props.attributes.rotationY} -

-

- {props.attributes.rotationZ} -

-

- {props.attributes.animations} -

-

- {props.attributes.collidable ? 1 : 0} -

-

{props.attributes.alt}

-
- -
- ); - } - } - ] + deprecated: deprecated }); diff --git a/blocks/npc-block/Deprecated.js b/blocks/npc-block/Deprecated.js new file mode 100644 index 0000000..f07cd55 --- /dev/null +++ b/blocks/npc-block/Deprecated.js @@ -0,0 +1,269 @@ +import { useBlockProps } from "@wordpress/block-editor"; + +export default function Deprecated(){ +return [ + { + attributes: { + bg_color: { + type: "string", + default: "#FFFFFF" + }, + zoom: { + type: "integer", + default: 90 + }, + scale: { + type: "integer", + default: 1 + }, + positionX: { + type: "integer", + default: 0 + }, + positionY: { + type: "integer", + default: 0 + }, + rotationY: { + type: "integer", + default: 0 + }, + threeObjectUrl: { + type: "string", + default: null + }, + hasZoom: { + type: "bool", + default: false + }, + hasTip: { + type: "bool", + default: true + }, + deviceTarget: { + type: "string", + default: "2d" + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.deviceTarget} +

+

+ {props.attributes.threeObjectUrl} +

+

+ {props.attributes.scale} +

+

+ {props.attributes.bg_color} +

+

+ {props.attributes.zoom} +

+

+ {props.attributes.hasZoom ? 1 : 0} +

+

+ {props.attributes.hasTip ? 1 : 0} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.scale} +

+
+ +
+ ); + } + }, + { + attributes: { + bg_color: { + type: "string", + default: "#FFFFFF" + }, + zoom: { + type: "integer", + default: 90 + }, + scale: { + type: "integer", + default: 1 + }, + positionX: { + type: "integer", + default: 0 + }, + positionY: { + type: "integer", + default: 0 + }, + rotationY: { + type: "integer", + default: 0 + }, + threeObjectUrl: { + type: "string", + default: null + }, + hasZoom: { + type: "bool", + default: false + }, + hasTip: { + type: "bool", + default: true + }, + deviceTarget: { + type: "string", + default: "2d" + }, + animations: { + type: "string", + default: "" + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.deviceTarget} +

+

+ {props.attributes.threeObjectUrl} +

+

+ {props.attributes.scale} +

+

+ {props.attributes.bg_color} +

+

+ {props.attributes.zoom} +

+

+ {props.attributes.hasZoom ? 1 : 0} +

+

+ {props.attributes.hasTip ? 1 : 0} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.scale} +

+

+ {props.attributes.animations} +

+
+ +
+ ); + } + }, + { + attributes: { + name: { + type: "string" + }, + personality: { + type: "string" + }, + defaultMessage: { + type: "string" + }, + positionX: { + type: "int", + default:0 + }, + positionY: { + type: "int", + default:0 + }, + positionZ: { + type: "int", + default:0 + }, + rotationX: { + type: "int", + default:0 + }, + rotationY: { + type: "int", + default:0 + }, + rotationZ: { + type: "int", + default:0 + }, + threeObjectUrl: { + type: "string", + default: null + }, + objectAwareness: { + type: "boolean", + default: false + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.threeObjectUrl} +

+

+ {props.attributes.positionX} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.positionZ} +

+

+ {props.attributes.rotationX} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.rotationZ} +

+

+ {props.attributes.name} +

+

+ {props.attributes.defaultMessage} +

+

+ {props.attributes.personality} +

+

+ {props.attributes.objectAwareness ? 1 : 0} +

+
+ +
+ ); + } + } +]; +} \ No newline at end of file diff --git a/blocks/npc-block/Save.js b/blocks/npc-block/Save.js index 91a342e..bba140e 100644 --- a/blocks/npc-block/Save.js +++ b/blocks/npc-block/Save.js @@ -2,45 +2,22 @@ import { __ } from "@wordpress/i18n"; import { useBlockProps } from "@wordpress/block-editor"; export default function save({ attributes }) { + const blockProps = useBlockProps.save(); + return ( -
- <> -
-

- {attributes.threeObjectUrl} -

-

- {attributes.positionX} -

-

- {attributes.positionY} -

-

- {attributes.positionZ} -

-

- {attributes.rotationX} -

-

- {attributes.rotationY} -

-

- {attributes.rotationZ} -

-

- {attributes.name} -

-

- {attributes.defaultMessage} -

-

- {attributes.personality} -

-

- {attributes.objectAwareness ? 1 : 0} -

-
- -
+ ); } diff --git a/blocks/npc-block/block.json b/blocks/npc-block/block.json index 6391b3d..3bee901 100644 --- a/blocks/npc-block/block.json +++ b/blocks/npc-block/block.json @@ -43,7 +43,7 @@ "default": false } }, - "category": "design", + "category": "spatial", "parent": [ "three-object-viewer/environment" ], "apiVersion": 2, "supports": { diff --git a/blocks/npc-block/index.js b/blocks/npc-block/index.js index 0b83de5..7bf6e78 100644 --- a/blocks/npc-block/index.js +++ b/blocks/npc-block/index.js @@ -2,6 +2,7 @@ import { registerBlockType } from "@wordpress/blocks"; import Edit from "./Edit"; import Save from "./Save"; import { useBlockProps } from "@wordpress/block-editor"; +import Deprecated from "./Deprecated"; const icon = ( ); - +const deprecated = Deprecated(); const blockConfig = require("./block.json"); registerBlockType(blockConfig.name, { ...blockConfig, @@ -23,181 +24,5 @@ registerBlockType(blockConfig.name, { apiVersion: 2, edit: Edit, save: Save, - deprecated: [ - { - attributes: { - bg_color: { - type: "string", - default: "#FFFFFF" - }, - zoom: { - type: "integer", - default: 90 - }, - scale: { - type: "integer", - default: 1 - }, - positionX: { - type: "integer", - default: 0 - }, - positionY: { - type: "integer", - default: 0 - }, - rotationY: { - type: "integer", - default: 0 - }, - threeObjectUrl: { - type: "string", - default: null - }, - hasZoom: { - type: "bool", - default: false - }, - hasTip: { - type: "bool", - default: true - }, - deviceTarget: { - type: "string", - default: "2d" - } - }, - save(props) { - return ( -
- <> -
-

- {props.attributes.deviceTarget} -

-

- {props.attributes.threeObjectUrl} -

-

- {props.attributes.scale} -

-

- {props.attributes.bg_color} -

-

- {props.attributes.zoom} -

-

- {props.attributes.hasZoom ? 1 : 0} -

-

- {props.attributes.hasTip ? 1 : 0} -

-

- {props.attributes.positionY} -

-

- {props.attributes.rotationY} -

-

- {props.attributes.scale} -

-
- -
- ); - } - }, - { - attributes: { - bg_color: { - type: "string", - default: "#FFFFFF" - }, - zoom: { - type: "integer", - default: 90 - }, - scale: { - type: "integer", - default: 1 - }, - positionX: { - type: "integer", - default: 0 - }, - positionY: { - type: "integer", - default: 0 - }, - rotationY: { - type: "integer", - default: 0 - }, - threeObjectUrl: { - type: "string", - default: null - }, - hasZoom: { - type: "bool", - default: false - }, - hasTip: { - type: "bool", - default: true - }, - deviceTarget: { - type: "string", - default: "2d" - }, - animations: { - type: "string", - default: "" - } - }, - save(props) { - return ( -
- <> -
-

- {props.attributes.deviceTarget} -

-

- {props.attributes.threeObjectUrl} -

-

- {props.attributes.scale} -

-

- {props.attributes.bg_color} -

-

- {props.attributes.zoom} -

-

- {props.attributes.hasZoom ? 1 : 0} -

-

- {props.attributes.hasTip ? 1 : 0} -

-

- {props.attributes.positionY} -

-

- {props.attributes.rotationY} -

-

- {props.attributes.scale} -

-

- {props.attributes.animations} -

-
- -
- ); - } - } - ] + deprecated: deprecated }); diff --git a/blocks/sky-block/Deprecated.js b/blocks/sky-block/Deprecated.js new file mode 100644 index 0000000..509da87 --- /dev/null +++ b/blocks/sky-block/Deprecated.js @@ -0,0 +1,69 @@ +import { useBlockProps } from "@wordpress/block-editor"; + +export default function Deprecated(){ + return [ + { + attributes: { + skyUrl: { + type: "string", + default: null + }, + }, + save(props) { + return ( +
+ <> +
+

{props.attributes.skyUrl}

+
+ +
+ ); + } + }, + { + attributes: { + skyUrl: { + type: "string", + default: null + }, + distance: { + type: "int", + default: 170000 + }, + rayleigh: { + type: "int", + default: 1 + }, + sunPositionX: { + type: "int", + default: 0 + }, + sunPositionY: { + type: "int", + default: 10000 + }, + sunPositionZ: { + type: "int", + default: -10000 + } + }, + save(props) { + return ( +
+ <> +
+

{props.attributes.skyUrl}

+

{props.attributes.distance}

+

{props.attributes.rayleigh}

+

{props.attributes.sunPositionX}

+

{props.attributes.sunPositionY}

+

{props.attributes.sunPositionZ}

+
+ +
+ ); + } + } + ]; +} diff --git a/blocks/sky-block/Edit.js b/blocks/sky-block/Edit.js index 4fdfbee..70125e4 100644 --- a/blocks/sky-block/Edit.js +++ b/blocks/sky-block/Edit.js @@ -160,7 +160,7 @@ export default function Edit({ attributes, setAttributes, isSelected }) {

- { __( 'Sky block', 'three-object-viewer' ) } + { __( 'Sky Block', 'three-object-viewer' ) }

{/*

URL: {attributes.skyUrl}

*/}
@@ -181,7 +181,7 @@ export default function Edit({ attributes, setAttributes, isSelected }) {

- { __( 'Sky block', 'three-object-viewer' ) } + { __( 'Sky Block', 'three-object-viewer' ) }

{/*

URL: {attributes.skyUrl}

*/}
diff --git a/blocks/sky-block/Save.js b/blocks/sky-block/Save.js index c9fa5e7..008453d 100644 --- a/blocks/sky-block/Save.js +++ b/blocks/sky-block/Save.js @@ -2,18 +2,17 @@ import { __ } from "@wordpress/i18n"; import { useBlockProps } from "@wordpress/block-editor"; export default function save({ attributes }) { + const blockProps = useBlockProps.save(); + return ( -
- <> -
-

{attributes.skyUrl}

-

{attributes.distance}

-

{attributes.rayleigh}

-

{attributes.sunPositionX}

-

{attributes.sunPositionY}

-

{attributes.sunPositionZ}

-
- -
+ ); } diff --git a/blocks/sky-block/block.json b/blocks/sky-block/block.json index d6645c5..81c5f24 100644 --- a/blocks/sky-block/block.json +++ b/blocks/sky-block/block.json @@ -26,7 +26,7 @@ "default": -10000 } }, - "category": "design", + "category": "spatial", "parent": [ "three-object-viewer/environment" ], "apiVersion": 2, "supports": { diff --git a/blocks/sky-block/index.js b/blocks/sky-block/index.js index f4d0b08..7bf6e78 100644 --- a/blocks/sky-block/index.js +++ b/blocks/sky-block/index.js @@ -2,6 +2,7 @@ import { registerBlockType } from "@wordpress/blocks"; import Edit from "./Edit"; import Save from "./Save"; import { useBlockProps } from "@wordpress/block-editor"; +import Deprecated from "./Deprecated"; const icon = ( ); - +const deprecated = Deprecated(); const blockConfig = require("./block.json"); registerBlockType(blockConfig.name, { ...blockConfig, @@ -23,25 +24,5 @@ registerBlockType(blockConfig.name, { apiVersion: 2, edit: Edit, save: Save, - deprecated: [ - { - attributes: { - skyUrl: { - type: "string", - default: null - }, - }, - save(props) { - return ( -
- <> -
-

{props.attributes.skyUrl}

-
- -
- ); - } - } - ] + deprecated: deprecated }); diff --git a/blocks/spawn-point-block/Deprecated.js b/blocks/spawn-point-block/Deprecated.js new file mode 100644 index 0000000..e127363 --- /dev/null +++ b/blocks/spawn-point-block/Deprecated.js @@ -0,0 +1,62 @@ +import { useBlockProps } from "@wordpress/block-editor"; + +export default function Deprecated(){ + return [ + { + attributes: { + positionX: { + type: "int", + default:0 + }, + positionY: { + type: "int", + default:0 + }, + positionZ: { + type: "int", + default:0 + }, + rotationX: { + type: "int", + default:0 + }, + rotationY: { + type: "int", + default:0 + }, + rotationZ: { + type: "int", + default:0 + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.positionX} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.positionZ} +

+

+ {props.attributes.rotationX} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.rotationZ} +

+
+ +
+ ); + } + } + ]; +} diff --git a/blocks/spawn-point-block/Save.js b/blocks/spawn-point-block/Save.js index 9ef1449..74bda1e 100644 --- a/blocks/spawn-point-block/Save.js +++ b/blocks/spawn-point-block/Save.js @@ -2,30 +2,17 @@ import { __ } from "@wordpress/i18n"; import { useBlockProps } from "@wordpress/block-editor"; export default function save({ attributes }) { + const blockProps = useBlockProps.save(); + return ( -
- <> -
-

- {attributes.positionX} -

-

- {attributes.positionY} -

-

- {attributes.positionZ} -

-

- {attributes.rotationX} -

-

- {attributes.rotationY} -

-

- {attributes.rotationZ} -

-
- -
+ ); } diff --git a/blocks/spawn-point-block/block.json b/blocks/spawn-point-block/block.json index 5071cc8..b82e5f2 100644 --- a/blocks/spawn-point-block/block.json +++ b/blocks/spawn-point-block/block.json @@ -26,7 +26,7 @@ "default":0 } }, - "category": "design", + "category": "spatial", "parent": [ "three-object-viewer/environment" ], "apiVersion": 2, "supports": { diff --git a/blocks/spawn-point-block/index.js b/blocks/spawn-point-block/index.js index 0b83de5..7bf6e78 100644 --- a/blocks/spawn-point-block/index.js +++ b/blocks/spawn-point-block/index.js @@ -2,6 +2,7 @@ import { registerBlockType } from "@wordpress/blocks"; import Edit from "./Edit"; import Save from "./Save"; import { useBlockProps } from "@wordpress/block-editor"; +import Deprecated from "./Deprecated"; const icon = ( ); - +const deprecated = Deprecated(); const blockConfig = require("./block.json"); registerBlockType(blockConfig.name, { ...blockConfig, @@ -23,181 +24,5 @@ registerBlockType(blockConfig.name, { apiVersion: 2, edit: Edit, save: Save, - deprecated: [ - { - attributes: { - bg_color: { - type: "string", - default: "#FFFFFF" - }, - zoom: { - type: "integer", - default: 90 - }, - scale: { - type: "integer", - default: 1 - }, - positionX: { - type: "integer", - default: 0 - }, - positionY: { - type: "integer", - default: 0 - }, - rotationY: { - type: "integer", - default: 0 - }, - threeObjectUrl: { - type: "string", - default: null - }, - hasZoom: { - type: "bool", - default: false - }, - hasTip: { - type: "bool", - default: true - }, - deviceTarget: { - type: "string", - default: "2d" - } - }, - save(props) { - return ( -
- <> -
-

- {props.attributes.deviceTarget} -

-

- {props.attributes.threeObjectUrl} -

-

- {props.attributes.scale} -

-

- {props.attributes.bg_color} -

-

- {props.attributes.zoom} -

-

- {props.attributes.hasZoom ? 1 : 0} -

-

- {props.attributes.hasTip ? 1 : 0} -

-

- {props.attributes.positionY} -

-

- {props.attributes.rotationY} -

-

- {props.attributes.scale} -

-
- -
- ); - } - }, - { - attributes: { - bg_color: { - type: "string", - default: "#FFFFFF" - }, - zoom: { - type: "integer", - default: 90 - }, - scale: { - type: "integer", - default: 1 - }, - positionX: { - type: "integer", - default: 0 - }, - positionY: { - type: "integer", - default: 0 - }, - rotationY: { - type: "integer", - default: 0 - }, - threeObjectUrl: { - type: "string", - default: null - }, - hasZoom: { - type: "bool", - default: false - }, - hasTip: { - type: "bool", - default: true - }, - deviceTarget: { - type: "string", - default: "2d" - }, - animations: { - type: "string", - default: "" - } - }, - save(props) { - return ( -
- <> -
-

- {props.attributes.deviceTarget} -

-

- {props.attributes.threeObjectUrl} -

-

- {props.attributes.scale} -

-

- {props.attributes.bg_color} -

-

- {props.attributes.zoom} -

-

- {props.attributes.hasZoom ? 1 : 0} -

-

- {props.attributes.hasTip ? 1 : 0} -

-

- {props.attributes.positionY} -

-

- {props.attributes.rotationY} -

-

- {props.attributes.scale} -

-

- {props.attributes.animations} -

-
- -
- ); - } - } - ] + deprecated: deprecated }); diff --git a/blocks/three-audio-block/Deprecated.js b/blocks/three-audio-block/Deprecated.js new file mode 100644 index 0000000..3dbdf5d --- /dev/null +++ b/blocks/three-audio-block/Deprecated.js @@ -0,0 +1,326 @@ +import { useBlockProps } from "@wordpress/block-editor"; + +export default function Deprecated(){ + return [ + { + attributes: { + bg_color: { + type: "string", + default: "#FFFFFF" + }, + zoom: { + type: "integer", + default: 90 + }, + scale: { + type: "integer", + default: 1 + }, + positionX: { + type: "integer", + default: 0 + }, + positionY: { + type: "integer", + default: 0 + }, + rotationY: { + type: "integer", + default: 0 + }, + threeObjectUrl: { + type: "string", + default: null + }, + hasZoom: { + type: "bool", + default: false + }, + hasTip: { + type: "bool", + default: true + }, + deviceTarget: { + type: "string", + default: "2d" + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.deviceTarget} +

+

+ {props.attributes.threeObjectUrl} +

+

+ {props.attributes.scale} +

+

+ {props.attributes.bg_color} +

+

+ {props.attributes.zoom} +

+

+ {props.attributes.hasZoom ? 1 : 0} +

+

+ {props.attributes.hasTip ? 1 : 0} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.scale} +

+
+ +
+ ); + } + }, + { + attributes: { + bg_color: { + type: "string", + default: "#FFFFFF" + }, + zoom: { + type: "integer", + default: 90 + }, + scale: { + type: "integer", + default: 1 + }, + positionX: { + type: "integer", + default: 0 + }, + positionY: { + type: "integer", + default: 0 + }, + rotationY: { + type: "integer", + default: 0 + }, + threeObjectUrl: { + type: "string", + default: null + }, + hasZoom: { + type: "bool", + default: false + }, + hasTip: { + type: "bool", + default: true + }, + deviceTarget: { + type: "string", + default: "2d" + }, + animations: { + type: "string", + default: "" + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.deviceTarget} +

+

+ {props.attributes.threeObjectUrl} +

+

+ {props.attributes.scale} +

+

+ {props.attributes.bg_color} +

+

+ {props.attributes.zoom} +

+

+ {props.attributes.hasZoom ? 1 : 0} +

+

+ {props.attributes.hasTip ? 1 : 0} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.scale} +

+

+ {props.attributes.animations} +

+
+ +
+ ); + } + }, + { + attributes: { + name: { + type: "string", + default: null + }, + audioUrl: { + type: "string", + default: null + }, + autoPlay: { + type: "bool", + default: true + }, + loop: { + type: "bool", + default: true + }, + volume: { + type: "int", + default: 1 + }, + positional: { + type: "bool", + default: true + }, + coneInnerAngle: { + type: "int", + default:360 + }, + coneOuterAngle: { + type: "int", + default:0 + }, + coneOuterGain: { + type: "int", + default:0.8 + }, + distanceModel: { + type: "string", + default: "inverse" + }, + maxDistance: { + type: "int", + default:10000 + }, + refDistance: { + type: "int", + default:5 + }, + rolloffFactor: { + type: "int", + default:5 + }, + positionX: { + type: "int", + default:0 + }, + positionY: { + type: "int", + default:0 + }, + positionZ: { + type: "int", + default:0 + }, + rotationX: { + type: "int", + default:0 + }, + rotationY: { + type: "int", + default:0 + }, + rotationZ: { + type: "int", + default:0 + } + }, + save(props) { + return ( +
+ <> +
+

{props.attributes.audioUrl}

+

{props.attributes.scaleX}

+

{props.attributes.scaleY}

+

{props.attributes.scaleZ}

+

+ {props.attributes.positionX} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.positionZ} +

+

+ {props.attributes.rotationX} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.rotationZ} +

+

+ {props.attributes.autoPlay ? '1' : '0'} +

+

+ {props.attributes.loop ? '1' : '0'} +

+

+ {props.attributes.volume} +

+

+ {props.attributes.positional ? '1' : '0'} +

+

+ {props.attributes.coneInnerAngle} +

+

+ {props.attributes.coneOuterAngle} +

+

+ {props.attributes.coneOuterGain} +

+

+ {props.attributes.distanceModel} +

+

+ {props.attributes.maxDistance} +

+

+ {props.attributes.refDistance} +

+

+ {props.attributes.rolloffFactor} +

+
+ +
+ ); + } + } + ]; +} diff --git a/blocks/three-audio-block/Save.js b/blocks/three-audio-block/Save.js index 41500bb..7116dfe 100644 --- a/blocks/three-audio-block/Save.js +++ b/blocks/three-audio-block/Save.js @@ -2,67 +2,32 @@ import { __ } from "@wordpress/i18n"; import { useBlockProps } from "@wordpress/block-editor"; export default function save({ attributes }) { + const blockProps = useBlockProps.save(); + return ( -
- <> -
-

{attributes.audioUrl}

-

{attributes.scaleX}

-

{attributes.scaleY}

-

{attributes.scaleZ}

-

- {attributes.positionX} -

-

- {attributes.positionY} -

-

- {attributes.positionZ} -

-

- {attributes.rotationX} -

-

- {attributes.rotationY} -

-

- {attributes.rotationZ} -

-

- {attributes.autoPlay ? '1' : '0'} -

-

- {attributes.loop ? '1' : '0'} -

-

- {attributes.volume} -

-

- {attributes.positional ? '1' : '0'} -

-

- {attributes.coneInnerAngle} -

-

- {attributes.coneOuterAngle} -

-

- {attributes.coneOuterGain} -

-

- {attributes.distanceModel} -

-

- {attributes.maxDistance} -

-

- {attributes.refDistance} -

-

- {attributes.rolloffFactor} -

-
- -
+ ); } diff --git a/blocks/three-audio-block/block.json b/blocks/three-audio-block/block.json index 1e9469f..60fe144 100644 --- a/blocks/three-audio-block/block.json +++ b/blocks/three-audio-block/block.json @@ -78,7 +78,7 @@ "default":0 } }, - "category": "design", + "category": "spatial", "parent": [ "three-object-viewer/environment" ], "apiVersion": 2, "supports": { diff --git a/blocks/three-audio-block/index.js b/blocks/three-audio-block/index.js index 0b83de5..88edd15 100644 --- a/blocks/three-audio-block/index.js +++ b/blocks/three-audio-block/index.js @@ -2,7 +2,7 @@ import { registerBlockType } from "@wordpress/blocks"; import Edit from "./Edit"; import Save from "./Save"; import { useBlockProps } from "@wordpress/block-editor"; - +import Deprecated from "./Deprecated"; const icon = ( ); - +const deprecated = Deprecated(); const blockConfig = require("./block.json"); registerBlockType(blockConfig.name, { ...blockConfig, @@ -23,181 +23,5 @@ registerBlockType(blockConfig.name, { apiVersion: 2, edit: Edit, save: Save, - deprecated: [ - { - attributes: { - bg_color: { - type: "string", - default: "#FFFFFF" - }, - zoom: { - type: "integer", - default: 90 - }, - scale: { - type: "integer", - default: 1 - }, - positionX: { - type: "integer", - default: 0 - }, - positionY: { - type: "integer", - default: 0 - }, - rotationY: { - type: "integer", - default: 0 - }, - threeObjectUrl: { - type: "string", - default: null - }, - hasZoom: { - type: "bool", - default: false - }, - hasTip: { - type: "bool", - default: true - }, - deviceTarget: { - type: "string", - default: "2d" - } - }, - save(props) { - return ( -
- <> -
-

- {props.attributes.deviceTarget} -

-

- {props.attributes.threeObjectUrl} -

-

- {props.attributes.scale} -

-

- {props.attributes.bg_color} -

-

- {props.attributes.zoom} -

-

- {props.attributes.hasZoom ? 1 : 0} -

-

- {props.attributes.hasTip ? 1 : 0} -

-

- {props.attributes.positionY} -

-

- {props.attributes.rotationY} -

-

- {props.attributes.scale} -

-
- -
- ); - } - }, - { - attributes: { - bg_color: { - type: "string", - default: "#FFFFFF" - }, - zoom: { - type: "integer", - default: 90 - }, - scale: { - type: "integer", - default: 1 - }, - positionX: { - type: "integer", - default: 0 - }, - positionY: { - type: "integer", - default: 0 - }, - rotationY: { - type: "integer", - default: 0 - }, - threeObjectUrl: { - type: "string", - default: null - }, - hasZoom: { - type: "bool", - default: false - }, - hasTip: { - type: "bool", - default: true - }, - deviceTarget: { - type: "string", - default: "2d" - }, - animations: { - type: "string", - default: "" - } - }, - save(props) { - return ( -
- <> -
-

- {props.attributes.deviceTarget} -

-

- {props.attributes.threeObjectUrl} -

-

- {props.attributes.scale} -

-

- {props.attributes.bg_color} -

-

- {props.attributes.zoom} -

-

- {props.attributes.hasZoom ? 1 : 0} -

-

- {props.attributes.hasTip ? 1 : 0} -

-

- {props.attributes.positionY} -

-

- {props.attributes.rotationY} -

-

- {props.attributes.scale} -

-

- {props.attributes.animations} -

-
- -
- ); - } - } - ] + deprecated: deprecated }); diff --git a/blocks/three-image-block/Deprecated.js b/blocks/three-image-block/Deprecated.js new file mode 100644 index 0000000..52b2d1f --- /dev/null +++ b/blocks/three-image-block/Deprecated.js @@ -0,0 +1,103 @@ +import { useBlockProps } from "@wordpress/block-editor"; + +export default function Deprecated(){ + return [ + { + attributes: { + imageUrl: { + type: "string", + default: null + }, + transparent: { + type: "boolean", + default: false + }, + scaleX: { + type: "int", + default: 1 + }, + scaleY: { + type: "int", + default: 1 + }, + scaleZ: { + type: "int", + default: 1 + }, + positionX: { + type: "int", + default: 0 + }, + positionY: { + type: "int", + default: 0 + }, + positionZ: { + type: "int", + default: 0 + }, + rotationX: { + type: "int", + default: 0 + }, + rotationY: { + type: "int", + default: 0 + }, + rotationZ: { + type: "int", + default: 0 + }, + aspectHeight: { + type: "int", + default: 0 + }, + aspectWidth: { + type: "int", + default: 0 + } + }, + save(props) { + return ( +
+ <> +
+

{props.attributes.imageUrl}

+

{props.attributes.scaleX}

+

{props.attributes.scaleY}

+

{props.attributes.scaleZ}

+

+ {props.attributes.positionX} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.positionZ} +

+

+ {props.attributes.rotationX} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.rotationZ} +

+

+ {props.attributes.aspectHeight} +

+

+ {props.attributes.aspectWidth} +

+

+ {props.attributes.transparent ? 1 : 0} +

+
+ +
+ ); + } + } + ]; +} diff --git a/blocks/three-image-block/Save.js b/blocks/three-image-block/Save.js index 776c9f9..8d834b1 100644 --- a/blocks/three-image-block/Save.js +++ b/blocks/three-image-block/Save.js @@ -2,43 +2,24 @@ import { __ } from "@wordpress/i18n"; import { useBlockProps } from "@wordpress/block-editor"; export default function save({ attributes }) { + const blockProps = useBlockProps.save(); + return ( -
- <> -
-

{attributes.imageUrl}

-

{attributes.scaleX}

-

{attributes.scaleY}

-

{attributes.scaleZ}

-

- {attributes.positionX} -

-

- {attributes.positionY} -

-

- {attributes.positionZ} -

-

- {attributes.rotationX} -

-

- {attributes.rotationY} -

-

- {attributes.rotationZ} -

-

- {attributes.aspectHeight} -

-

- {attributes.aspectWidth} -

-

- {attributes.transparent ? 1 : 0} -

-
- -
+ ); } diff --git a/blocks/three-image-block/block.json b/blocks/three-image-block/block.json index af8cb81..eeb8d34 100644 --- a/blocks/three-image-block/block.json +++ b/blocks/three-image-block/block.json @@ -54,7 +54,7 @@ "default":0 } }, - "category": "design", + "category": "spatial", "parent": [ "three-object-viewer/environment" ], "apiVersion": 2, "supports": { diff --git a/blocks/three-image-block/index.js b/blocks/three-image-block/index.js index 0b83de5..88edd15 100644 --- a/blocks/three-image-block/index.js +++ b/blocks/three-image-block/index.js @@ -2,7 +2,7 @@ import { registerBlockType } from "@wordpress/blocks"; import Edit from "./Edit"; import Save from "./Save"; import { useBlockProps } from "@wordpress/block-editor"; - +import Deprecated from "./Deprecated"; const icon = ( ); - +const deprecated = Deprecated(); const blockConfig = require("./block.json"); registerBlockType(blockConfig.name, { ...blockConfig, @@ -23,181 +23,5 @@ registerBlockType(blockConfig.name, { apiVersion: 2, edit: Edit, save: Save, - deprecated: [ - { - attributes: { - bg_color: { - type: "string", - default: "#FFFFFF" - }, - zoom: { - type: "integer", - default: 90 - }, - scale: { - type: "integer", - default: 1 - }, - positionX: { - type: "integer", - default: 0 - }, - positionY: { - type: "integer", - default: 0 - }, - rotationY: { - type: "integer", - default: 0 - }, - threeObjectUrl: { - type: "string", - default: null - }, - hasZoom: { - type: "bool", - default: false - }, - hasTip: { - type: "bool", - default: true - }, - deviceTarget: { - type: "string", - default: "2d" - } - }, - save(props) { - return ( -
- <> -
-

- {props.attributes.deviceTarget} -

-

- {props.attributes.threeObjectUrl} -

-

- {props.attributes.scale} -

-

- {props.attributes.bg_color} -

-

- {props.attributes.zoom} -

-

- {props.attributes.hasZoom ? 1 : 0} -

-

- {props.attributes.hasTip ? 1 : 0} -

-

- {props.attributes.positionY} -

-

- {props.attributes.rotationY} -

-

- {props.attributes.scale} -

-
- -
- ); - } - }, - { - attributes: { - bg_color: { - type: "string", - default: "#FFFFFF" - }, - zoom: { - type: "integer", - default: 90 - }, - scale: { - type: "integer", - default: 1 - }, - positionX: { - type: "integer", - default: 0 - }, - positionY: { - type: "integer", - default: 0 - }, - rotationY: { - type: "integer", - default: 0 - }, - threeObjectUrl: { - type: "string", - default: null - }, - hasZoom: { - type: "bool", - default: false - }, - hasTip: { - type: "bool", - default: true - }, - deviceTarget: { - type: "string", - default: "2d" - }, - animations: { - type: "string", - default: "" - } - }, - save(props) { - return ( -
- <> -
-

- {props.attributes.deviceTarget} -

-

- {props.attributes.threeObjectUrl} -

-

- {props.attributes.scale} -

-

- {props.attributes.bg_color} -

-

- {props.attributes.zoom} -

-

- {props.attributes.hasZoom ? 1 : 0} -

-

- {props.attributes.hasTip ? 1 : 0} -

-

- {props.attributes.positionY} -

-

- {props.attributes.rotationY} -

-

- {props.attributes.scale} -

-

- {props.attributes.animations} -

-
- -
- ); - } - } - ] + deprecated: deprecated }); diff --git a/blocks/three-light-block/Deprecated.js b/blocks/three-light-block/Deprecated.js new file mode 100644 index 0000000..34a741e --- /dev/null +++ b/blocks/three-light-block/Deprecated.js @@ -0,0 +1,120 @@ +import { useBlockProps } from "@wordpress/block-editor"; + +export default function Deprecated(){ + return [ + { + attributes: { + type: { + type: "string", + default: "ambient" + }, + color: { + type: "string", + default: "0xffffff" + }, + intensity: { + type: "float", + default: 0.7 + }, + distance: { + type: "int", + default: 100 + }, + decay: { + type: "int", + default: 1 + }, + positionX: { + type: "float", + default: 0 + }, + positionY: { + type: "float", + default: 0 + }, + positionZ: { + type: "float", + default: 0 + }, + rotationX: { + type: "float", + default: 0 + }, + rotationY: { + type: "float", + default: 0 + }, + rotationZ: { + type: "float", + default: 0 + }, + angle: { + type: "float", + default: 0.78539816339 + }, + penumbra: { + type: "float", + default: 0.1 + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.positionX} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.positionZ} +

+

+ {props.attributes.rotationX} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.rotationZ} +

+

+ {props.attributes.type} +

+

+ {props.attributes.color} +

+

+ {props.attributes.intensity} +

+

+ {props.attributes.distance} +

+

+ {props.attributes.decay} +

+

+ {props.attributes.targetX} +

+

+ {props.attributes.targetY} +

+

+ {props.attributes.targetZ} +

+

+ {props.attributes.angle} +

+

+ {props.attributes.penumbra} +

+
+ +
+ ); + } + } + ]; +} \ No newline at end of file diff --git a/blocks/three-light-block/Save.js b/blocks/three-light-block/Save.js index 7e0eee5..3fff041 100644 --- a/blocks/three-light-block/Save.js +++ b/blocks/three-light-block/Save.js @@ -2,60 +2,27 @@ import { __ } from "@wordpress/i18n"; import { useBlockProps } from "@wordpress/block-editor"; export default function save({ attributes }) { + const blockProps = useBlockProps.save(); + return ( -
- <> -
-

- {attributes.positionX} -

-

- {attributes.positionY} -

-

- {attributes.positionZ} -

-

- {attributes.rotationX} -

-

- {attributes.rotationY} -

-

- {attributes.rotationZ} -

-

- {attributes.type} -

-

- {attributes.color} -

-

- {attributes.intensity} -

-

- {attributes.distance} -

-

- {attributes.decay} -

-

- {attributes.targetX} -

-

- {attributes.targetY} -

-

- {attributes.targetZ} -

-

- {attributes.angle} -

-

- {attributes.penumbra} -

-
- -
+ ); } diff --git a/blocks/three-light-block/block.json b/blocks/three-light-block/block.json index b0eac7f..26a9a85 100644 --- a/blocks/three-light-block/block.json +++ b/blocks/three-light-block/block.json @@ -7,7 +7,7 @@ }, "color": { "type": "string", - "default": "0xffffff" + "default": "#ffffff" }, "intensity": { "type": "float", @@ -54,7 +54,7 @@ "default": 0.1 } }, - "category": "design", + "category": "spatial", "parent": [ "three-object-viewer/environment" ], "apiVersion": 2, "supports": { diff --git a/blocks/three-light-block/index.js b/blocks/three-light-block/index.js index 661ed94..7bf6e78 100644 --- a/blocks/three-light-block/index.js +++ b/blocks/three-light-block/index.js @@ -2,6 +2,7 @@ import { registerBlockType } from "@wordpress/blocks"; import Edit from "./Edit"; import Save from "./Save"; import { useBlockProps } from "@wordpress/block-editor"; +import Deprecated from "./Deprecated"; const icon = ( ); - +const deprecated = Deprecated(); const blockConfig = require("./block.json"); registerBlockType(blockConfig.name, { ...blockConfig, icon, apiVersion: 2, edit: Edit, - save: Save + save: Save, + deprecated: deprecated }); diff --git a/blocks/three-networking-block/Edit.js b/blocks/three-networking-block/Edit.js new file mode 100644 index 0000000..62f8303 --- /dev/null +++ b/blocks/three-networking-block/Edit.js @@ -0,0 +1,198 @@ +import { __ } from "@wordpress/i18n"; +import React, { useState, useEffect } from "react"; +import "./editor.scss"; +import { + useBlockProps, + ColorPalette, + InspectorControls, + MediaUpload +} from "@wordpress/block-editor"; +import { + Panel, + PanelBody, + PanelRow, + RangeControl, + ToggleControl, + SelectControl, + TextControl, + DropZone, +} from "@wordpress/components"; +import { more } from "@wordpress/icons"; + +export default function Edit({ attributes, setAttributes, isSelected, clientId }) { + const { select, dispatch } = wp.data; + const { onSelectionChange, getSelectedBlock } = wp.blocks; + useEffect(() => { + if( isSelected ){ + dispatch( 'three-object-environment-events' ).setFocusEvent( clientId ); + } + }, [isSelected]); + + const onImageSelect = (imageObject) => { + setAttributes({ videoUrl: null }); + setAttributes({ + videoUrl: imageObject.url, + aspectHeight: imageObject.height, + aspectWidth: imageObject.width + }); + }; + + const onChangeParticipantLimit = (participantLimit) => { + setAttributes({ participantLimit }); + }; + const onChangeMultiplayerAccess = (multiplayerAccess) => { + setAttributes({ multiplayerAccess: multiplayerAccess }); + }; + const setCustomAvatars = (customAvatars) => { + setAttributes({ customAvatars: customAvatars }); + }; + + const { mediaUpload } = wp.editor; + + const ALLOWED_MEDIA_TYPES = ["video"]; + const THREE_ALLOWED_MEDIA_TYPES = [ + "model/gltf-binary", + "application/octet-stream" + ]; + + return ( +
+ + + + {/* + { + onChangeAutoPlay(e); + }} + /> + */} + + + {__("Participant Limit", "three-object-viewer")} + + + + onChangeParticipantLimit(value)} + /> + + + { __( "Custom Avatars:", "three-object-viewer" ) } + + + { + setCustomAvatars(e); + }} + /> + + + + + {isSelected ? ( + <> + {attributes.videoUrl ? ( +
+
+ + + + + +

+ {__( 'Networking Block', 'three-object-viewer' ) } +

+
+
+ ) : ( +
+
+ + + + + +

+ {__( 'Networking Block', 'three-object-viewer' ) } +

+
+
+ )} + + ) : ( + <> + {attributes.videoUrl ? ( +
+
+ + + + + +

+ { __( 'Networking Block', 'three-object-viewer' ) } +

+ {/*

URL: {attributes.threeObjectUrl}

*/} +
+
+ ) : ( +
+
+ + + + + +

+ {__( 'Networking Block', 'three-object-viewer' ) } +

+
+
+ )} + + )} +
+ ); +} diff --git a/blocks/three-networking-block/Edit.test.js b/blocks/three-networking-block/Edit.test.js new file mode 100644 index 0000000..ab22d96 --- /dev/null +++ b/blocks/three-networking-block/Edit.test.js @@ -0,0 +1,47 @@ +//Import React +import React from "react"; +//Import test renderer +import { render, fireEvent, cleanup } from "@testing-library/react"; +//Import component to test +import { Editor } from "./Edit"; + +describe("Editor componet", () => { + afterEach(cleanup); + it("matches snapshot when selected", () => { + const onChange = jest.fn(); + const { container } = render( + + ); + expect(container).toMatchSnapshot(); + }); + + it("matches snapshot when not selected", () => { + const onChange = jest.fn(); + const { container } = render( + + ); + expect(container).toMatchSnapshot(); + }); + + it("Calls the onchange function", () => { + const onChange = jest.fn(); + const { getByDisplayValue } = render( + + ); + fireEvent.change(getByDisplayValue("Salad"), { + target: { value: "New Value" } + }); + expect(onChange).toHaveBeenCalledTimes(1); + }); + + it("Passes updated value, not event to onChange callback", () => { + const onChange = jest.fn(); + const { getByDisplayValue } = render( + + ); + fireEvent.change(getByDisplayValue("Seltzer"), { + target: { value: "Boring Water" } + }); + expect(onChange).toHaveBeenCalledWith("Boring Water"); + }); +}); diff --git a/blocks/three-networking-block/Save.js b/blocks/three-networking-block/Save.js new file mode 100644 index 0000000..9b6d059 --- /dev/null +++ b/blocks/three-networking-block/Save.js @@ -0,0 +1,15 @@ +import { __ } from "@wordpress/i18n"; +import { useBlockProps } from "@wordpress/block-editor"; + +export default function save({ attributes }) { + const blockProps = useBlockProps.save(); + + return ( + + ); +} diff --git a/blocks/three-networking-block/block.json b/blocks/three-networking-block/block.json new file mode 100644 index 0000000..554b753 --- /dev/null +++ b/blocks/three-networking-block/block.json @@ -0,0 +1,24 @@ +{ + "name": "three-object-viewer/three-networking-block", + "attributes": { + "participantLimit": { + "type": "int", + "default": 5 + }, + "customAvatars": { + "type": "bool", + "default": false + } + }, + "category": "spatial", + "parent": [ "three-object-viewer/environment" ], + "apiVersion": 2, + "supports": { + "html": false, + "multiple": false + }, + "textdomain": "three-object-viewer", + "editorScript": "file:../../build/block-three-networking-block.js", + "editorStyle": "file:../../build/block-three-networking-block.css", + "style": "file:../../build/block-three-networking-block.css" +} diff --git a/blocks/three-networking-block/editor.scss b/blocks/three-networking-block/editor.scss new file mode 100644 index 0000000..4a6a4c4 --- /dev/null +++ b/blocks/three-networking-block/editor.scss @@ -0,0 +1,42 @@ + + .wp-block-three-object-block { + border: 1px dotted #f00; +} + .glb-preview-container { + padding: 100px; + text-align: center; + align-items: center; + align-content: center; + background-color:#f2f2f2; + } + + .glb-preview-container button{ + padding: 15px; + border-radius: 30px; +} + +.glb-preview-container button:hover{ + border-radius: 30px; + background-color:rgb(156, 199, 0); + cursor: pointer; +} +.three-object-block-tip { + overflow-wrap: break-word; + background-color: black; + color: white; + padding: 10px; + font-weight: 500; + max-width: 160px; + font-size: 12px; + margin-top: 0px; + margin: 0 auto; + text-align: center; +} + +.three-object-block-url-input { + padding-bottom: 20px; +} + +.three-object-block-url-input input{ + height: 40px; +} diff --git a/blocks/three-networking-block/index.js b/blocks/three-networking-block/index.js new file mode 100644 index 0000000..f564734 --- /dev/null +++ b/blocks/three-networking-block/index.js @@ -0,0 +1,47 @@ +import { registerBlockType } from "@wordpress/blocks"; +import Edit from "./Edit"; +import Save from "./Save"; +import { useBlockProps } from "@wordpress/block-editor"; + +const icon = ( + + + + + +); + +const blockConfig = require("./block.json"); +registerBlockType(blockConfig.name, { + ...blockConfig, + icon, + apiVersion: 2, + edit: Edit, + save: Save, + deprecated: [ + { + attributes: { + participantCount: { + type: "int", + default: 1 + } + }, + save(props) { + return ( +
+ <> +
+

{props.attributes.scaleX}

+
+ +
+ ); + } + } + ] +}); diff --git a/blocks/three-networking-block/init.php b/blocks/three-networking-block/init.php new file mode 100644 index 0000000..ab2a4c5 --- /dev/null +++ b/blocks/three-networking-block/init.php @@ -0,0 +1,16 @@ + _x( 'Multiplayer Environment Block', 'block title', 'three-object-viewer' ), + 'description' => _x( 'A 3D multiplayer environment component', 'block description', 'three-object-viewer' ), + ] ); + } + } +}); diff --git a/blocks/three-networking-block/style.scss b/blocks/three-networking-block/style.scss new file mode 100644 index 0000000..18f1fcf --- /dev/null +++ b/blocks/three-networking-block/style.scss @@ -0,0 +1,44 @@ +.wp-block-three-object-block { + background-color: #fff; + color: #000; + padding: 2px; +} + +.wp-block-three-object-block { + border: 1px dotted #f00; +} + .glb-preview-container { + padding: 100px; + text-align: center; + align-items: center; + align-content: center; + background-color:#f2f2f2; + } + + .glb-preview-container button{ + padding: 10px; + border-radius: 30px; + background-color:#333; + color: white; +} + +.three-object-block-tip { + overflow-wrap: break-word; + background-color: black; + color: white; + padding: 10px; + font-weight: 500; + max-width: 160px; + font-size: 14px; + margin-top: 0px; + margin: 0 auto; + text-align: center; +} + +.glb-preview-container button:hover{ + padding: 10px; + border-radius: 30px; + background-color:rgb(69, 69, 69); + color: white; + cursor: pointer; +} \ No newline at end of file diff --git a/blocks/three-object-block/Deprecated.js b/blocks/three-object-block/Deprecated.js new file mode 100644 index 0000000..9657a17 --- /dev/null +++ b/blocks/three-object-block/Deprecated.js @@ -0,0 +1,259 @@ +import { useBlockProps } from "@wordpress/block-editor"; + +export default function Deprecated(){ + return [ + { + attributes: { + bg_color: { + type: 'string', + default: '#FFFFFF', + }, + zoom: { + type: 'integer', + default: 90, + }, + scale: { + type: 'integer', + default: 1, + }, + positionX: { + type: 'integer', + default: 0, + }, + positionY: { + type: 'integer', + default: 0, + }, + rotationY: { + type: 'integer', + default: 0, + }, + threeObjectUrl: { + type: 'string', + default: null, + }, + hasZoom: { + type: 'bool', + default: false, + }, + hasTip: { + type: 'bool', + default: true, + }, + deviceTarget: { + type: 'string', + default: '2d', + }, + }, + save( props ) { + return ( +
+ <> +
+

+ { props.attributes.deviceTarget } +

+

+ { props.attributes.threeObjectUrl } +

+

+ { props.attributes.scale } +

+

+ { props.attributes.bg_color } +

+

+ { props.attributes.zoom } +

+

+ { props.attributes.hasZoom ? 1 : 0 } +

+

+ { props.attributes.hasTip ? 1 : 0 } +

+

+ { props.attributes.positionY } +

+

+ { props.attributes.rotationY } +

+

+ { props.attributes.scale } +

+
+ +
+ ); + }, + }, + { + attributes: { + bg_color: { + type: 'string', + default: '#FFFFFF', + }, + zoom: { + type: 'integer', + default: 90, + }, + scale: { + type: 'integer', + default: 1, + }, + positionX: { + type: 'integer', + default: 0, + }, + positionY: { + type: 'integer', + default: 0, + }, + rotationY: { + type: 'integer', + default: 0, + }, + threeObjectUrl: { + type: 'string', + default: null, + }, + hasZoom: { + type: 'bool', + default: false, + }, + hasTip: { + type: 'bool', + default: true, + }, + deviceTarget: { + type: 'string', + default: '2d', + }, + animations: { + type: 'string', + default: '', + } + }, + save( props ) { + return ( +
+ <> +
+

+ { props.attributes.deviceTarget } +

+

+ { props.attributes.threeObjectUrl } +

+

{ props.attributes.scale }

+

+ { props.attributes.bg_color } +

+

{ props.attributes.zoom }

+

+ { props.attributes.hasZoom ? 1 : 0 } +

+

+ { props.attributes.hasTip ? 1 : 0 } +

+

+ { props.attributes.positionY } +

+

+ { props.attributes.rotationY } +

+

{ props.attributes.scale }

+

+ { props.attributes.animations } +

+
+ +
+ ); + }, + }, + { + attributes: { + bg_color: { + type: 'string', + default: '#FFFFFF', + }, + zoom: { + type: 'integer', + default: 1, + }, + scale: { + type: 'integer', + default: 1, + }, + positionX: { + type: 'integer', + default: 0, + }, + positionY: { + type: 'integer', + default: 0, + }, + rotationY: { + type: 'integer', + default: 0, + }, + threeObjectUrl: { + type: 'string', + default: null, + }, + hasZoom: { + type: 'bool', + default: false, + }, + hasTip: { + type: 'bool', + default: true, + }, + deviceTarget: { + type: 'string', + default: '2d', + }, + animations: { + type: 'string', + default: '', + } + }, save( props ) { + return ( +
+ <> +
+

+ { props.attributes.deviceTarget } +

+

+ { props.attributes.threeObjectUrl } +

+

{ props.attributes.scale }

+

+ { props.attributes.bg_color } +

+

{ props.attributes.zoom }

+

+ { props.attributes.hasZoom ? 1 : 0 } +

+

+ { props.attributes.hasTip ? 1 : 0 } +

+

+ { props.attributes.positionY } +

+

+ { props.attributes.rotationY } +

+

{ props.attributes.scale }

+

+ { props.attributes.animations } +

+
+ +
+ ); + }, + } + ]; +} \ No newline at end of file diff --git a/blocks/three-object-block/Edit.js b/blocks/three-object-block/Edit.js index 4d67033..6ea87a3 100644 --- a/blocks/three-object-block/Edit.js +++ b/blocks/three-object-block/Edit.js @@ -207,8 +207,9 @@ export default function Edit( { attributes, setAttributes, isSelected } ) { diff --git a/blocks/three-object-block/Save.js b/blocks/three-object-block/Save.js index 20717ae..40e4a7e 100644 --- a/blocks/three-object-block/Save.js +++ b/blocks/three-object-block/Save.js @@ -2,39 +2,20 @@ import { __ } from '@wordpress/i18n'; import { useBlockProps } from '@wordpress/block-editor'; export default function save( { attributes } ) { + const blockProps = useBlockProps.save(); return ( -
- <> -
-

- { attributes.deviceTarget } -

-

- { attributes.threeObjectUrl } -

-

{ attributes.scale }

-

- { attributes.bg_color } -

-

{ attributes.zoom }

-

- { attributes.hasZoom ? 1 : 0 } -

-

- { attributes.hasTip ? 1 : 0 } -

-

- { attributes.positionY } -

-

- { attributes.rotationY } -

-

{ attributes.scale }

-

- { attributes.animations } -

-
- -
+ ); } diff --git a/blocks/three-object-block/components/ThreeObjectFront.js b/blocks/three-object-block/components/ThreeObjectFront.js index 9f30c19..75ff430 100644 --- a/blocks/three-object-block/components/ThreeObjectFront.js +++ b/blocks/three-object-block/components/ThreeObjectFront.js @@ -93,10 +93,10 @@ const mixamoVRMRigMap = { * @returns {Promise} The converted AnimationClip */ function loadMixamoAnimation(url, vrm, positionY, positionX, positionZ, scaleX, scaleY, scaleZ, rotationX, rotationY, rotationZ, rotationW) { - const loader = new FBXLoader(); // A loader which loads FBX + const loader = new FBXLoader(); return loader.loadAsync(url).then((asset) => { - const clip = AnimationClip.findByName(asset.animations, 'mixamo.com'); // extract the AnimationClip - const tracks = []; // KeyframeTracks compatible with VRM will be added here + const clip = AnimationClip.findByName(asset.animations, 'mixamo.com'); + const tracks = []; const restRotationInverse = new Quaternion(); const parentRestWorldRotation = new Quaternion(); @@ -173,7 +173,7 @@ function loadMixamoAnimation(url, vrm, positionY, positionX, positionZ, scaleX, } function SavedObject( props ) { - const [idleFile, setIdleFile] = useState(props.threeObjectPlugin + idle); + const [idleFile, setIdleFile] = useState(idle); const [ url, set ] = useState( props.url ); useEffect( () => { setTimeout( () => set( props.url ), 2000 ); diff --git a/blocks/three-object-block/index.js b/blocks/three-object-block/index.js index 22b14b6..c28e79e 100644 --- a/blocks/three-object-block/index.js +++ b/blocks/three-object-block/index.js @@ -2,6 +2,7 @@ import { registerBlockType } from '@wordpress/blocks'; import Edit from './Edit'; import Save from './Save'; import { useBlockProps } from '@wordpress/block-editor'; +import Deprecated from './Deprecated'; const icon = ( ); +const deprecated = Deprecated(); const blockConfig = require( './block.json' ); registerBlockType( blockConfig.name, { @@ -23,175 +25,5 @@ registerBlockType( blockConfig.name, { apiVersion: 2, edit: Edit, save: Save, - deprecated: [ - { - attributes: { - bg_color: { - type: 'string', - default: '#FFFFFF', - }, - zoom: { - type: 'integer', - default: 90, - }, - scale: { - type: 'integer', - default: 1, - }, - positionX: { - type: 'integer', - default: 0, - }, - positionY: { - type: 'integer', - default: 0, - }, - rotationY: { - type: 'integer', - default: 0, - }, - threeObjectUrl: { - type: 'string', - default: null, - }, - hasZoom: { - type: 'bool', - default: false, - }, - hasTip: { - type: 'bool', - default: true, - }, - deviceTarget: { - type: 'string', - default: '2d', - }, - }, - save( props ) { - return ( -
- <> -
-

- { props.attributes.deviceTarget } -

-

- { props.attributes.threeObjectUrl } -

-

- { props.attributes.scale } -

-

- { props.attributes.bg_color } -

-

- { props.attributes.zoom } -

-

- { props.attributes.hasZoom ? 1 : 0 } -

-

- { props.attributes.hasTip ? 1 : 0 } -

-

- { props.attributes.positionY } -

-

- { props.attributes.rotationY } -

-

- { props.attributes.scale } -

-
- -
- ); - }, - }, - { - attributes: { - bg_color: { - type: 'string', - default: '#FFFFFF', - }, - zoom: { - type: 'integer', - default: 90, - }, - scale: { - type: 'integer', - default: 1, - }, - positionX: { - type: 'integer', - default: 0, - }, - positionY: { - type: 'integer', - default: 0, - }, - rotationY: { - type: 'integer', - default: 0, - }, - threeObjectUrl: { - type: 'string', - default: null, - }, - hasZoom: { - type: 'bool', - default: false, - }, - hasTip: { - type: 'bool', - default: true, - }, - deviceTarget: { - type: 'string', - default: '2d', - }, - animations: { - type: 'string', - default: '', - } - }, - save( props ) { - return ( -
- <> -
-

- { props.attributes.deviceTarget } -

-

- { props.attributes.threeObjectUrl } -

-

{ props.attributes.scale }

-

- { props.attributes.bg_color } -

-

{ props.attributes.zoom }

-

- { props.attributes.hasZoom ? 1 : 0 } -

-

- { props.attributes.hasTip ? 1 : 0 } -

-

- { props.attributes.positionY } -

-

- { props.attributes.rotationY } -

-

{ props.attributes.scale }

-

- { props.attributes.animations } -

-
- -
- ); - }, - }, - ], + deprecated: deprecated, } ); diff --git a/blocks/three-portal-block/Deprecated.js b/blocks/three-portal-block/Deprecated.js new file mode 100644 index 0000000..3949c4e --- /dev/null +++ b/blocks/three-portal-block/Deprecated.js @@ -0,0 +1,318 @@ +import { useBlockProps } from "@wordpress/block-editor"; + +export default function Deprecated(){ +return [ + { + attributes: { + bg_color: { + type: "string", + default: "#FFFFFF" + }, + zoom: { + type: "integer", + default: 90 + }, + scale: { + type: "integer", + default: 1 + }, + positionX: { + type: "integer", + default: 0 + }, + positionY: { + type: "integer", + default: 0 + }, + rotationY: { + type: "integer", + default: 0 + }, + threeObjectUrl: { + type: "string", + default: null + }, + hasZoom: { + type: "bool", + default: false + }, + hasTip: { + type: "bool", + default: true + }, + deviceTarget: { + type: "string", + default: "2d" + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.deviceTarget} +

+

+ {props.attributes.threeObjectUrl} +

+

+ {props.attributes.scale} +

+

+ {props.attributes.bg_color} +

+

+ {props.attributes.zoom} +

+

+ {props.attributes.hasZoom ? 1 : 0} +

+

+ {props.attributes.hasTip ? 1 : 0} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.scale} +

+
+ +
+ ); + } + }, + { + attributes: { + bg_color: { + type: "string", + default: "#FFFFFF" + }, + zoom: { + type: "integer", + default: 90 + }, + scale: { + type: "integer", + default: 1 + }, + positionX: { + type: "integer", + default: 0 + }, + positionY: { + type: "integer", + default: 0 + }, + rotationY: { + type: "integer", + default: 0 + }, + threeObjectUrl: { + type: "string", + default: null + }, + hasZoom: { + type: "bool", + default: false + }, + hasTip: { + type: "bool", + default: true + }, + deviceTarget: { + type: "string", + default: "2d" + }, + animations: { + type: "string", + default: "" + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.deviceTarget} +

+

+ {props.attributes.threeObjectUrl} +

+

+ {props.attributes.scale} +

+

+ {props.attributes.bg_color} +

+

+ {props.attributes.zoom} +

+

+ {props.attributes.hasZoom ? 1 : 0} +

+

+ {props.attributes.hasTip ? 1 : 0} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.scale} +

+

+ {props.attributes.animations} +

+
+ +
+ ); + } + }, + { + attributes: { + scaleX: { + type: "int", + default:1 + }, + scaleY: { + type: "int", + default:1 + }, + scaleZ: { + type: "int", + default:1 + }, + positionX: { + type: "int", + default:0 + }, + positionY: { + type: "int", + default:0 + }, + positionZ: { + type: "int", + default:0 + }, + rotationX: { + type: "int", + default:0 + }, + rotationY: { + type: "int", + default:0 + }, + rotationZ: { + type: "int", + default:0 + }, + threeObjectUrl: { + type: "string", + default: null + }, + destinationUrl: { + type: "string", + default: null + }, + label: { + type: "string", + default: null + }, + labelTextColor: { + type: "string", + default: "0x000000" + }, + labelOffsetX: { + type: "int", + default:0 + }, + labelOffsetY: { + type: "int", + default:0 + }, + labelOffsetZ: { + type: "int", + default:0 + }, + animations: { + type: "string", + default: "" + }, + collidable: { + type: "boolean", + default: false + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.threeObjectUrl} +

+

+ {props.attributes.destinationUrl} +

+

+ {props.attributes.scaleX} +

+

+ {props.attributes.scaleY} +

+

+ {props.attributes.scaleZ} +

+

+ {props.attributes.positionX} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.positionZ} +

+

+ {props.attributes.rotationX} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.rotationZ} +

+

+ {props.attributes.animations} +

+

+ {props.attributes.label} +

+

+ {props.attributes.labelOffsetX} +

+

+ {props.attributes.labelOffsetY} +

+

+ {props.attributes.labelOffsetZ} +

+

+ {props.attributes.labelTextColor} +

+
+ +
+ ); + } + } +]; +} \ No newline at end of file diff --git a/blocks/three-portal-block/Save.js b/blocks/three-portal-block/Save.js index 61d4ff1..3638074 100644 --- a/blocks/three-portal-block/Save.js +++ b/blocks/three-portal-block/Save.js @@ -2,63 +2,28 @@ import { __ } from "@wordpress/i18n"; import { useBlockProps } from "@wordpress/block-editor"; export default function save({ attributes }) { + const blockProps = useBlockProps.save(); + return ( -
- <> -
-

- {attributes.threeObjectUrl} -

-

- {attributes.destinationUrl} -

-

- {attributes.scaleX} -

-

- {attributes.scaleY} -

-

- {attributes.scaleZ} -

-

- {attributes.positionX} -

-

- {attributes.positionY} -

-

- {attributes.positionZ} -

-

- {attributes.rotationX} -

-

- {attributes.rotationY} -

-

- {attributes.rotationZ} -

-

- {attributes.animations} -

-

- {attributes.label} -

-

- {attributes.labelOffsetX} -

-

- {attributes.labelOffsetY} -

-

- {attributes.labelOffsetZ} -

-

- {attributes.labelTextColor} -

-
- -
+ ); } diff --git a/blocks/three-portal-block/block.json b/blocks/three-portal-block/block.json index dab7f97..7e933d3 100644 --- a/blocks/three-portal-block/block.json +++ b/blocks/three-portal-block/block.json @@ -1,88 +1,88 @@ { - "name": "three-object-viewer/three-portal-block", - "attributes": { - "scaleX": { - "type": "int", - "default":1 - }, - "scaleY": { - "type": "int", - "default":1 - }, - "scaleZ": { - "type": "int", - "default":1 - }, - "positionX": { - "type": "int", - "default":0 - }, - "positionY": { - "type": "int", - "default":0 - }, - "positionZ": { - "type": "int", - "default":0 - }, - "rotationX": { - "type": "int", - "default":0 - }, - "rotationY": { - "type": "int", - "default":0 - }, - "rotationZ": { - "type": "int", - "default":0 - }, - "threeObjectUrl": { - "type": "string", - "default": null - }, - "destinationUrl": { - "type": "string", - "default": null - }, - "label": { - "type": "string", - "default": null - }, - "labelTextColor": { - "type": "string", - "default": "0x000000" - }, - "labelOffsetX": { - "type": "int", - "default":0 - }, - "labelOffsetY": { - "type": "int", - "default":0 - }, - "labelOffsetZ": { - "type": "int", - "default":0 - }, - "animations": { - "type": "string", - "default": "" - }, - "collidable": { - "type": "boolean", - "default": false - } - }, - "category": "design", - "parent": [ "three-object-viewer/environment" ], - "apiVersion": 2, - "supports": { - "html": false, - "multiple": true - }, - "textdomain": "three-object-viewer", - "editorScript": "file:../../build/block-three-portal-block.js", + "name": "three-object-viewer/three-portal-block", + "attributes": { + "scaleX": { + "type": "int", + "default":1 + }, + "scaleY": { + "type": "int", + "default":1 + }, + "scaleZ": { + "type": "int", + "default":1 + }, + "positionX": { + "type": "int", + "default":0 + }, + "positionY": { + "type": "int", + "default":0 + }, + "positionZ": { + "type": "int", + "default":0 + }, + "rotationX": { + "type": "int", + "default":0 + }, + "rotationY": { + "type": "int", + "default":0 + }, + "rotationZ": { + "type": "int", + "default":0 + }, + "threeObjectUrl": { + "type": "string", + "default": null + }, + "destinationUrl": { + "type": "string", + "default": null + }, + "label": { + "type": "string", + "default": null + }, + "labelTextColor": { + "type": "string", + "default": "#000000" + }, + "labelOffsetX": { + "type": "int", + "default":0 + }, + "labelOffsetY": { + "type": "int", + "default":0 + }, + "labelOffsetZ": { + "type": "int", + "default":0 + }, + "animations": { + "type": "string", + "default": "" + }, + "collidable": { + "type": "boolean", + "default": false + } + }, + "category": "spatial", + "parent": [ "three-object-viewer/environment" ], + "apiVersion": 2, + "supports": { + "html": false, + "multiple": true + }, + "textdomain": "three-object-viewer", + "editorScript": "file:../../build/block-three-portal-block.js", "editorStyle": "file:../../build/block-three-portal-block.css", "style": "file:../../build/block-three-portal-block.css" } diff --git a/blocks/three-portal-block/index.js b/blocks/three-portal-block/index.js index 0b83de5..88edd15 100644 --- a/blocks/three-portal-block/index.js +++ b/blocks/three-portal-block/index.js @@ -2,7 +2,7 @@ import { registerBlockType } from "@wordpress/blocks"; import Edit from "./Edit"; import Save from "./Save"; import { useBlockProps } from "@wordpress/block-editor"; - +import Deprecated from "./Deprecated"; const icon = ( ); - +const deprecated = Deprecated(); const blockConfig = require("./block.json"); registerBlockType(blockConfig.name, { ...blockConfig, @@ -23,181 +23,5 @@ registerBlockType(blockConfig.name, { apiVersion: 2, edit: Edit, save: Save, - deprecated: [ - { - attributes: { - bg_color: { - type: "string", - default: "#FFFFFF" - }, - zoom: { - type: "integer", - default: 90 - }, - scale: { - type: "integer", - default: 1 - }, - positionX: { - type: "integer", - default: 0 - }, - positionY: { - type: "integer", - default: 0 - }, - rotationY: { - type: "integer", - default: 0 - }, - threeObjectUrl: { - type: "string", - default: null - }, - hasZoom: { - type: "bool", - default: false - }, - hasTip: { - type: "bool", - default: true - }, - deviceTarget: { - type: "string", - default: "2d" - } - }, - save(props) { - return ( -
- <> -
-

- {props.attributes.deviceTarget} -

-

- {props.attributes.threeObjectUrl} -

-

- {props.attributes.scale} -

-

- {props.attributes.bg_color} -

-

- {props.attributes.zoom} -

-

- {props.attributes.hasZoom ? 1 : 0} -

-

- {props.attributes.hasTip ? 1 : 0} -

-

- {props.attributes.positionY} -

-

- {props.attributes.rotationY} -

-

- {props.attributes.scale} -

-
- -
- ); - } - }, - { - attributes: { - bg_color: { - type: "string", - default: "#FFFFFF" - }, - zoom: { - type: "integer", - default: 90 - }, - scale: { - type: "integer", - default: 1 - }, - positionX: { - type: "integer", - default: 0 - }, - positionY: { - type: "integer", - default: 0 - }, - rotationY: { - type: "integer", - default: 0 - }, - threeObjectUrl: { - type: "string", - default: null - }, - hasZoom: { - type: "bool", - default: false - }, - hasTip: { - type: "bool", - default: true - }, - deviceTarget: { - type: "string", - default: "2d" - }, - animations: { - type: "string", - default: "" - } - }, - save(props) { - return ( -
- <> -
-

- {props.attributes.deviceTarget} -

-

- {props.attributes.threeObjectUrl} -

-

- {props.attributes.scale} -

-

- {props.attributes.bg_color} -

-

- {props.attributes.zoom} -

-

- {props.attributes.hasZoom ? 1 : 0} -

-

- {props.attributes.hasTip ? 1 : 0} -

-

- {props.attributes.positionY} -

-

- {props.attributes.rotationY} -

-

- {props.attributes.scale} -

-

- {props.attributes.animations} -

-
- -
- ); - } - } - ] + deprecated: deprecated }); diff --git a/blocks/three-text-block/Deprecated.js b/blocks/three-text-block/Deprecated.js new file mode 100644 index 0000000..4be143d --- /dev/null +++ b/blocks/three-text-block/Deprecated.js @@ -0,0 +1,280 @@ +import { useBlockProps } from "@wordpress/block-editor"; + +export default function Deprecated(){ + return [ + { + attributes: { + bg_color: { + type: "string", + default: "#FFFFFF" + }, + zoom: { + type: "integer", + default: 90 + }, + scale: { + type: "integer", + default: 1 + }, + positionX: { + type: "integer", + default: 0 + }, + positionY: { + type: "integer", + default: 0 + }, + rotationY: { + type: "integer", + default: 0 + }, + threeObjectUrl: { + type: "string", + default: null + }, + hasZoom: { + type: "bool", + default: false + }, + hasTip: { + type: "bool", + default: true + }, + deviceTarget: { + type: "string", + default: "2d" + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.deviceTarget} +

+

+ {props.attributes.threeObjectUrl} +

+

+ {props.attributes.scale} +

+

+ {props.attributes.bg_color} +

+

+ {props.attributes.zoom} +

+

+ {props.attributes.hasZoom ? 1 : 0} +

+

+ {props.attributes.hasTip ? 1 : 0} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.scale} +

+
+ +
+ ); + } + }, + { + attributes: { + bg_color: { + type: "string", + default: "#FFFFFF" + }, + zoom: { + type: "integer", + default: 90 + }, + scale: { + type: "integer", + default: 1 + }, + positionX: { + type: "integer", + default: 0 + }, + positionY: { + type: "integer", + default: 0 + }, + rotationY: { + type: "integer", + default: 0 + }, + threeObjectUrl: { + type: "string", + default: null + }, + hasZoom: { + type: "bool", + default: false + }, + hasTip: { + type: "bool", + default: true + }, + deviceTarget: { + type: "string", + default: "2d" + }, + animations: { + type: "string", + default: "" + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.deviceTarget} +

+

+ {props.attributes.threeObjectUrl} +

+

+ {props.attributes.scale} +

+

+ {props.attributes.bg_color} +

+

+ {props.attributes.zoom} +

+

+ {props.attributes.hasZoom ? 1 : 0} +

+

+ {props.attributes.hasTip ? 1 : 0} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.scale} +

+

+ {props.attributes.animations} +

+
+ +
+ ); + } + }, + { + attributes: { + scaleX: { + type: "int", + default:1 + }, + scaleY: { + type: "int", + default:1 + }, + scaleZ: { + type: "int", + default:1 + }, + positionX: { + type: "int", + default:0 + }, + positionY: { + type: "int", + default:0 + }, + positionZ: { + type: "int", + default:0 + }, + rotationX: { + type: "int", + default:0 + }, + rotationY: { + type: "int", + default:0 + }, + rotationZ: { + type: "int", + default:0 + }, + threeObjectUrl: { + type: "string", + default: null + }, + destinationUrl: { + type: "string", + default: null + }, + textContent: { + type: "string", + default: null + }, + textColor: { + type: "string", + default: "0x000000" + }, + animations: { + type: "string", + default: "" + }, + collidable: { + type: "boolean", + default: false + } + }, + save(props) { + return ( +
+ <> +
+

+ {props.attributes.textContent} +

+

+ {props.attributes.positionX} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.positionZ} +

+

+ {props.attributes.rotationX} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.rotationZ} +

+

{props.attributes.scaleX}

+

{props.attributes.scaleY}

+

{props.attributes.scaleZ}

+

{props.attributes.textColor}

+
+ +
+ ); + } + } + ]; +} \ No newline at end of file diff --git a/blocks/three-text-block/Save.js b/blocks/three-text-block/Save.js index f03482e..ea3f6b4 100644 --- a/blocks/three-text-block/Save.js +++ b/blocks/three-text-block/Save.js @@ -2,37 +2,22 @@ import { __ } from "@wordpress/i18n"; import { useBlockProps } from "@wordpress/block-editor"; export default function save({ attributes }) { + const blockProps = useBlockProps.save(); + return ( -
- <> -
-

- {attributes.textContent} -

-

- {attributes.positionX} -

-

- {attributes.positionY} -

-

- {attributes.positionZ} -

-

- {attributes.rotationX} -

-

- {attributes.rotationY} -

-

- {attributes.rotationZ} -

-

{attributes.scaleX}

-

{attributes.scaleY}

-

{attributes.scaleZ}

-

{attributes.textColor}

-
- -
+ ); } diff --git a/blocks/three-text-block/block.json b/blocks/three-text-block/block.json index 3bac44e..f1cd7a7 100644 --- a/blocks/three-text-block/block.json +++ b/blocks/three-text-block/block.json @@ -1,76 +1,76 @@ { - "name": "three-object-viewer/three-text-block", - "attributes": { - "scaleX": { - "type": "int", - "default":1 - }, - "scaleY": { - "type": "int", - "default":1 - }, - "scaleZ": { - "type": "int", - "default":1 - }, - "positionX": { - "type": "int", - "default":0 - }, - "positionY": { - "type": "int", - "default":0 - }, - "positionZ": { - "type": "int", - "default":0 - }, - "rotationX": { - "type": "int", - "default":0 - }, - "rotationY": { - "type": "int", - "default":0 - }, - "rotationZ": { - "type": "int", - "default":0 - }, - "threeObjectUrl": { - "type": "string", - "default": null - }, - "destinationUrl": { - "type": "string", - "default": null - }, - "textContent": { - "type": "string", - "default": null - }, + "name": "three-object-viewer/three-text-block", + "attributes": { + "scaleX": { + "type": "int", + "default":1 + }, + "scaleY": { + "type": "int", + "default":1 + }, + "scaleZ": { + "type": "int", + "default":1 + }, + "positionX": { + "type": "int", + "default":0 + }, + "positionY": { + "type": "int", + "default":0 + }, + "positionZ": { + "type": "int", + "default":0 + }, + "rotationX": { + "type": "int", + "default":0 + }, + "rotationY": { + "type": "int", + "default":0 + }, + "rotationZ": { + "type": "int", + "default":0 + }, + "threeObjectUrl": { + "type": "string", + "default": null + }, + "destinationUrl": { + "type": "string", + "default": null + }, + "textContent": { + "type": "string", + "default": null + }, "textColor": { - "type": "string", - "default": "0x000000" - }, - "animations": { - "type": "string", - "default": "" - }, - "collidable": { - "type": "boolean", - "default": false - } - }, - "category": "design", - "parent": [ "three-object-viewer/environment" ], - "apiVersion": 2, - "supports": { - "html": false, - "multiple": true - }, - "textdomain": "three-object-viewer", - "editorScript": "file:../../build/block-three-text-block.js", + "type": "string", + "default": "#ffffff" + }, + "animations": { + "type": "string", + "default": "" + }, + "collidable": { + "type": "boolean", + "default": false + } + }, + "category": "spatial", + "parent": [ "three-object-viewer/environment" ], + "apiVersion": 2, + "supports": { + "html": false, + "multiple": true + }, + "textdomain": "three-object-viewer", + "editorScript": "file:../../build/block-three-text-block.js", "editorStyle": "file:../../build/block-three-text-block.css", "style": "file:../../build/block-three-text-block.css" } diff --git a/blocks/three-text-block/index.js b/blocks/three-text-block/index.js index 0b83de5..88edd15 100644 --- a/blocks/three-text-block/index.js +++ b/blocks/three-text-block/index.js @@ -2,7 +2,7 @@ import { registerBlockType } from "@wordpress/blocks"; import Edit from "./Edit"; import Save from "./Save"; import { useBlockProps } from "@wordpress/block-editor"; - +import Deprecated from "./Deprecated"; const icon = ( ); - +const deprecated = Deprecated(); const blockConfig = require("./block.json"); registerBlockType(blockConfig.name, { ...blockConfig, @@ -23,181 +23,5 @@ registerBlockType(blockConfig.name, { apiVersion: 2, edit: Edit, save: Save, - deprecated: [ - { - attributes: { - bg_color: { - type: "string", - default: "#FFFFFF" - }, - zoom: { - type: "integer", - default: 90 - }, - scale: { - type: "integer", - default: 1 - }, - positionX: { - type: "integer", - default: 0 - }, - positionY: { - type: "integer", - default: 0 - }, - rotationY: { - type: "integer", - default: 0 - }, - threeObjectUrl: { - type: "string", - default: null - }, - hasZoom: { - type: "bool", - default: false - }, - hasTip: { - type: "bool", - default: true - }, - deviceTarget: { - type: "string", - default: "2d" - } - }, - save(props) { - return ( -
- <> -
-

- {props.attributes.deviceTarget} -

-

- {props.attributes.threeObjectUrl} -

-

- {props.attributes.scale} -

-

- {props.attributes.bg_color} -

-

- {props.attributes.zoom} -

-

- {props.attributes.hasZoom ? 1 : 0} -

-

- {props.attributes.hasTip ? 1 : 0} -

-

- {props.attributes.positionY} -

-

- {props.attributes.rotationY} -

-

- {props.attributes.scale} -

-
- -
- ); - } - }, - { - attributes: { - bg_color: { - type: "string", - default: "#FFFFFF" - }, - zoom: { - type: "integer", - default: 90 - }, - scale: { - type: "integer", - default: 1 - }, - positionX: { - type: "integer", - default: 0 - }, - positionY: { - type: "integer", - default: 0 - }, - rotationY: { - type: "integer", - default: 0 - }, - threeObjectUrl: { - type: "string", - default: null - }, - hasZoom: { - type: "bool", - default: false - }, - hasTip: { - type: "bool", - default: true - }, - deviceTarget: { - type: "string", - default: "2d" - }, - animations: { - type: "string", - default: "" - } - }, - save(props) { - return ( -
- <> -
-

- {props.attributes.deviceTarget} -

-

- {props.attributes.threeObjectUrl} -

-

- {props.attributes.scale} -

-

- {props.attributes.bg_color} -

-

- {props.attributes.zoom} -

-

- {props.attributes.hasZoom ? 1 : 0} -

-

- {props.attributes.hasTip ? 1 : 0} -

-

- {props.attributes.positionY} -

-

- {props.attributes.rotationY} -

-

- {props.attributes.scale} -

-

- {props.attributes.animations} -

-
- -
- ); - } - } - ] + deprecated: deprecated }); diff --git a/blocks/three-video-block/Deprecated.js b/blocks/three-video-block/Deprecated.js new file mode 100644 index 0000000..90b9229 --- /dev/null +++ b/blocks/three-video-block/Deprecated.js @@ -0,0 +1,216 @@ +import { useBlockProps } from "@wordpress/block-editor"; + +export default function Deprecated(){ + return [ + { + attributes: { + videoUrl: { + type: "string", + default: null + }, + modelUrl: { + type: "string", + default: null + }, + autoPlay: { + type: "bool", + default: true + }, + scaleX: { + type: "int", + default: 1 + }, + scaleY: { + type: "int", + default: 1 + }, + scaleZ: { + type: "int", + default: 1 + }, + positionX: { + type: "int", + default: 0 + }, + positionY: { + type: "int", + default: 0 + }, + positionZ: { + type: "int", + default: 0 + }, + rotationX: { + type: "int", + default: 0 + }, + rotationY: { + type: "int", + default: 0 + }, + rotationZ: { + type: "int", + default: 0 + }, + aspectHeight: { + type: "int", + default: 0 + }, + aspectWidth: { + type: "int", + default: 0 + } + }, + save(props) { + return ( +
+ <> +
+
{props.attributes.videoUrl}
+

{props.attributes.scaleX}

+

{props.attributes.scaleY}

+

{props.attributes.scaleZ}

+

+ {props.attributes.positionX} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.positionZ} +

+

+ {props.attributes.rotationX} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.rotationZ} +

+

+ {props.attributes.aspectHeight} +

+

+ {props.attributes.aspectWidth} +

+

+ {props.attributes.autoPlay ? 1 : 0} +

+
+ +
+ ); + } + }, + { + attributes: { + videoUrl: { + type: "string", + default: null + }, + modelUrl: { + type: "string", + default: null + }, + autoPlay: { + type: "bool", + default: true + }, + scaleX: { + type: "int", + default:1 + }, + scaleY: { + type: "int", + default:1 + }, + scaleZ: { + type: "int", + default:1 + }, + positionX: { + type: "int", + default:0 + }, + positionY: { + type: "int", + default:0 + }, + positionZ: { + type: "int", + default:0 + }, + rotationX: { + type: "int", + default:0 + }, + rotationY: { + type: "int", + default:0 + }, + rotationZ: { + type: "int", + default:0 + }, + aspectHeight: { + type: "int", + default:0 + }, + aspectWidth: { + type: "int", + default:0 + }, + customModel: { + type: "bool", + default: false + } + }, + save(props) { + return ( +
+ <> +
+
{props.attributes.videoUrl}
+

{props.attributes.scaleX}

+

{props.attributes.scaleY}

+

{props.attributes.scaleZ}

+

+ {props.attributes.positionX} +

+

+ {props.attributes.positionY} +

+

+ {props.attributes.positionZ} +

+

+ {props.attributes.rotationX} +

+

+ {props.attributes.rotationY} +

+

+ {props.attributes.rotationZ} +

+

+ {props.attributes.autoPlay ? 1 : 0} +

+

+ {props.attributes.customModel ? 1 : 0} +

+

+ {props.attributes.aspectHeight} +

+

+ {props.attributes.aspectWidth} +

+
{props.attributes.modelUrl}
+
+ +
+ ); + } + } + ]; +} diff --git a/blocks/three-video-block/Edit.js b/blocks/three-video-block/Edit.js index f8baab7..f27715e 100644 --- a/blocks/three-video-block/Edit.js +++ b/blocks/three-video-block/Edit.js @@ -79,6 +79,9 @@ export default function Edit({ attributes, setAttributes, isSelected, clientId } const onChangeCustomModel = (customModelSetting) => { setAttributes({ customModel: customModelSetting }); }; + const onChangeControlsEnabled = (videoControlsEnabled) => { + setAttributes({ videoControlsEnabled: videoControlsEnabled }); + }; const { mediaUpload } = wp.editor; @@ -161,6 +164,17 @@ export default function Edit({ attributes, setAttributes, isSelected, clientId } }} /> + + {attributes.videoUrl && ( + { + onChangeControlsEnabled(e); + }} + />)} + {attributes.videoUrl && ( - <> -
-
{attributes.videoUrl}
-

{attributes.scaleX}

-

{attributes.scaleY}

-

{attributes.scaleZ}

-

- {attributes.positionX} -

-

- {attributes.positionY} -

-

- {attributes.positionZ} -

-

- {attributes.rotationX} -

-

- {attributes.rotationY} -

-

- {attributes.rotationZ} -

-

- {attributes.autoPlay ? 1 : 0} -

-

- {attributes.customModel ? 1 : 0} -

-

- {attributes.aspectHeight} -

-

- {attributes.aspectWidth} -

-
{attributes.modelUrl}
-
- -
+ ); } diff --git a/blocks/three-video-block/block.json b/blocks/three-video-block/block.json index fe87b36..3da1a61 100644 --- a/blocks/three-video-block/block.json +++ b/blocks/three-video-block/block.json @@ -1,76 +1,80 @@ { - "name": "three-object-viewer/three-video-block", - "attributes": { - "videoUrl": { - "type": "string", - "default": null - }, - "modelUrl": { - "type": "string", - "default": null - }, - "autoPlay": { - "type": "bool", - "default": true - }, - "scaleX": { - "type": "int", - "default":1 - }, - "scaleY": { - "type": "int", - "default":1 - }, - "scaleZ": { - "type": "int", - "default":1 - }, - "positionX": { - "type": "int", - "default":0 - }, - "positionY": { - "type": "int", - "default":0 - }, - "positionZ": { - "type": "int", - "default":0 - }, - "rotationX": { - "type": "int", - "default":0 - }, - "rotationY": { - "type": "int", - "default":0 - }, - "rotationZ": { - "type": "int", - "default":0 - }, - "aspectHeight": { - "type": "int", - "default":0 - }, - "aspectWidth": { - "type": "int", - "default":0 - }, - "customModel": { - "type": "bool", - "default": false - } - }, - "category": "design", - "parent": [ "three-object-viewer/environment" ], - "apiVersion": 2, - "supports": { - "html": false, - "multiple": true - }, - "textdomain": "three-object-viewer", - "editorScript": "file:../../build/block-three-video-block.js", + "name": "three-object-viewer/three-video-block", + "attributes": { + "videoUrl": { + "type": "string", + "default": null + }, + "modelUrl": { + "type": "string", + "default": null + }, + "autoPlay": { + "type": "bool", + "default": true + }, + "scaleX": { + "type": "int", + "default":1 + }, + "scaleY": { + "type": "int", + "default":1 + }, + "scaleZ": { + "type": "int", + "default":1 + }, + "positionX": { + "type": "int", + "default":0 + }, + "positionY": { + "type": "int", + "default":0 + }, + "positionZ": { + "type": "int", + "default":0 + }, + "rotationX": { + "type": "int", + "default":0 + }, + "rotationY": { + "type": "int", + "default":0 + }, + "rotationZ": { + "type": "int", + "default":0 + }, + "aspectHeight": { + "type": "int", + "default":0 + }, + "aspectWidth": { + "type": "int", + "default":0 + }, + "customModel": { + "type": "bool", + "default": false + }, + "videoControlsEnabled": { + "type": "bool", + "default": true + } + }, + "category": "spatial", + "parent": [ "three-object-viewer/environment" ], + "apiVersion": 2, + "supports": { + "html": false, + "multiple": true + }, + "textdomain": "three-object-viewer", + "editorScript": "file:../../build/block-three-video-block.js", "editorStyle": "file:../../build/block-three-video-block.css", "style": "file:../../build/block-three-video-block.css" } diff --git a/blocks/three-video-block/index.js b/blocks/three-video-block/index.js index b4bd853..09b58ee 100644 --- a/blocks/three-video-block/index.js +++ b/blocks/three-video-block/index.js @@ -1,7 +1,7 @@ import { registerBlockType } from "@wordpress/blocks"; import Edit from "./Edit"; import Save from "./Save"; -import { useBlockProps } from "@wordpress/block-editor"; +import Deprecated from "./Deprecated"; const icon = ( ); - +const deprecated = Deprecated(); const blockConfig = require("./block.json"); registerBlockType(blockConfig.name, { ...blockConfig, @@ -23,107 +23,5 @@ registerBlockType(blockConfig.name, { apiVersion: 2, edit: Edit, save: Save, - deprecated: [ - { - attributes: { - videoUrl: { - type: "string", - default: null - }, - modelUrl: { - type: "string", - default: null - }, - autoPlay: { - type: "bool", - default: true - }, - scaleX: { - type: "int", - default: 1 - }, - scaleY: { - type: "int", - default: 1 - }, - scaleZ: { - type: "int", - default: 1 - }, - positionX: { - type: "int", - default: 0 - }, - positionY: { - type: "int", - default: 0 - }, - positionZ: { - type: "int", - default: 0 - }, - rotationX: { - type: "int", - default: 0 - }, - rotationY: { - type: "int", - default: 0 - }, - rotationZ: { - type: "int", - default: 0 - }, - aspectHeight: { - type: "int", - default: 0 - }, - aspectWidth: { - type: "int", - default: 0 - } - }, - save(props) { - return ( -
- <> -
-
{props.attributes.videoUrl}
-

{props.attributes.scaleX}

-

{props.attributes.scaleY}

-

{props.attributes.scaleZ}

-

- {props.attributes.positionX} -

-

- {props.attributes.positionY} -

-

- {props.attributes.positionZ} -

-

- {props.attributes.rotationX} -

-

- {props.attributes.rotationY} -

-

- {props.attributes.rotationZ} -

-

- {props.attributes.aspectHeight} -

-

- {props.attributes.aspectWidth} -

-

- {props.attributes.autoPlay ? 1 : 0} -

-
- -
- ); - } - } - ] + deprecated: deprecated }); diff --git a/inc/assets/corner_accent.png b/inc/assets/corner_accent.png new file mode 100644 index 0000000..4a4cde5 Binary files /dev/null and b/inc/assets/corner_accent.png differ diff --git a/inc/assets/hmdicon.png b/inc/assets/hmdicon.png new file mode 100644 index 0000000..de17e99 Binary files /dev/null and b/inc/assets/hmdicon.png differ diff --git a/inc/assets/mic_icon.png b/inc/assets/mic_icon.png new file mode 100644 index 0000000..b2d5406 Binary files /dev/null and b/inc/assets/mic_icon.png differ diff --git a/inc/assets/mic_icon_mute.png b/inc/assets/mic_icon_mute.png new file mode 100644 index 0000000..05b87f9 Binary files /dev/null and b/inc/assets/mic_icon_mute.png differ diff --git a/inc/assets/participants.png b/inc/assets/participants.png new file mode 100644 index 0000000..03ef893 Binary files /dev/null and b/inc/assets/participants.png differ diff --git a/inc/assets/room_entry_background.svg b/inc/assets/room_entry_background.svg new file mode 100644 index 0000000..c36652b --- /dev/null +++ b/inc/assets/room_entry_background.svg @@ -0,0 +1,254 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inc/assets/settings_icon.png b/inc/assets/settings_icon.png new file mode 100644 index 0000000..f343202 Binary files /dev/null and b/inc/assets/settings_icon.png differ diff --git a/inc/assets/world_icon.png b/inc/assets/world_icon.png new file mode 100644 index 0000000..ffad075 Binary files /dev/null and b/inc/assets/world_icon.png differ diff --git a/inc/avatars/Jump.fbx b/inc/avatars/Jump.fbx new file mode 100644 index 0000000..207599b Binary files /dev/null and b/inc/avatars/Jump.fbx differ diff --git a/inc/avatars/Running.fbx b/inc/avatars/Running.fbx index ab4beb5..87433b4 100644 Binary files a/inc/avatars/Running.fbx and b/inc/avatars/Running.fbx differ diff --git a/inc/avatars/blank_avatar.vrm b/inc/avatars/blank_avatar.vrm new file mode 100644 index 0000000..ef25982 Binary files /dev/null and b/inc/avatars/blank_avatar.vrm differ diff --git a/inc/avatars/falling.fbx b/inc/avatars/falling.fbx new file mode 100644 index 0000000..ffa0479 Binary files /dev/null and b/inc/avatars/falling.fbx differ diff --git a/inc/utils/basis/basis_transcoder.js b/inc/utils/basis/basis_transcoder.js new file mode 100644 index 0000000..9e285dd --- /dev/null +++ b/inc/utils/basis/basis_transcoder.js @@ -0,0 +1,21 @@ + +var BASIS = (function() { + var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; + if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename; + return ( +function(BASIS) { + BASIS = BASIS || {}; + +var Module=typeof BASIS!=="undefined"?BASIS:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var tempRet0=0;var setTempRet0=function(value){tempRet0=value};var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function UTF16ToString(ptr,maxBytesToRead){var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder){return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr))}else{var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit)}return str}}function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}function lengthBytesUTF16(str){return str.length*2}function UTF32ToString(ptr,maxBytesToRead){var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str}function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}function lengthBytesUTF32(str){var len=0;for(var i=0;i=55296&&codeUnit<=57343)++i;len+=4}return len}function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="basis_transcoder.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return Promise.resolve().then(getBinary)}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["K"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["L"];removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var structRegistrations={};function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return new Function("body","return function "+name+"() {\n"+' "use strict";'+" return body.apply(this, arguments);\n"+"};\n")(body)}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function whenDependentTypesAreResolved(myTypes,dependentTypes,getTypeConverters){myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i>shift])},destructorFunction:null})}function ClassHandle_isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right}function shallowCopyInternalPointer(o){return{count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType}}function throwInstanceAlreadyDeleted(obj){function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")}var finalizationGroup=false;function detachFinalizer(handle){}function runDestructor($$){if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}}function releaseClassHandle($$){$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}}function attachFinalizer(handle){if("undefined"===typeof FinalizationGroup){attachFinalizer=function(handle){return handle};return handle}finalizationGroup=new FinalizationGroup(function(iter){for(var result=iter.next();!result.done;result=iter.next()){var $$=result.value;if(!$$.ptr){console.warn("object already deleted: "+$$.ptr)}else{releaseClassHandle($$)}}});attachFinalizer=function(handle){finalizationGroup.register(handle,handle.$$,handle.$$);return handle};detachFinalizer=function(handle){finalizationGroup.unregister(handle.$$)};return attachFinalizer(handle)}function ClassHandle_clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}}function ClassHandle_delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}}function ClassHandle_isDeleted(){return!this.$$.ptr}var delayFunction=undefined;var deletionQueue=[];function flushPendingDeletes(){while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}}function ClassHandle_deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}function init_ClassHandle(){ClassHandle.prototype["isAliasOf"]=ClassHandle_isAliasOf;ClassHandle.prototype["clone"]=ClassHandle_clone;ClassHandle.prototype["delete"]=ClassHandle_delete;ClassHandle.prototype["isDeleted"]=ClassHandle_isDeleted;ClassHandle.prototype["deleteLater"]=ClassHandle_deleteLater}function ClassHandle(){}var registeredPointers={};function ensureOverloadTable(proto,methodName,humanName){if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError("Function '"+humanName+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+proto[methodName].overloadTable+")!")}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}}function exposePublicSymbol(name,value,numArguments){if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError("Cannot register public name '"+name+"' twice")}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError("Cannot register multiple overloads of a function with the same number of arguments ("+numArguments+")!")}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}}function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}function upcastPointer(ptr,ptrClass,desiredClass){while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError("Expected null or instance of "+desiredClass.name+", got an instance of "+ptrClass.name)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr}function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,__emval_register(function(){clonedHandle["delete"]()}));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupporting sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+handle.$$.ptrType.name+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function RegisteredPointer_getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr}function RegisteredPointer_destructor(ptr){if(this.rawDestructor){this.rawDestructor(ptr)}}function RegisteredPointer_deleteObject(handle){if(handle!==null){handle["delete"]()}}function downcastPointer(ptr,ptrClass,desiredClass){if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)}function getInheritedInstanceCount(){return Object.keys(registeredInstances).length}function getLiveInheritedInstances(){var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv}function setDelayFunction(fn){delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}}function init_embind(){Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction}var registeredInstances={};function getBasestPointer(class_,ptr){if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr}function getInheritedInstance(class_,ptr){ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]}function makeClassHandle(prototype,record){if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record}}))}function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr:ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}function init_RegisteredPointer(){RegisteredPointer.prototype.getPointee=RegisteredPointer_getPointee;RegisteredPointer.prototype.destructor=RegisteredPointer_destructor;RegisteredPointer.prototype["argPackAdvance"]=8;RegisteredPointer.prototype["readValueFromPointer"]=simpleReadValueFromPointer;RegisteredPointer.prototype["deleteObject"]=RegisteredPointer_deleteObject;RegisteredPointer.prototype["fromWireType"]=RegisteredPointer_fromWireType}function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===undefined){if(isConst){this["toWireType"]=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this["toWireType"]=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this["toWireType"]=genericPointerToWireType}}function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}}function dynCallLegacy(sig,ptr,args){if(args&&args.length){return Module["dynCall_"+sig].apply(null,[ptr].concat(args))}return Module["dynCall_"+sig].call(null,ptr)}function dynCall(sig,ptr,args){if(sig.indexOf("j")!=-1){return dynCallLegacy(sig,ptr,args)}return wasmTable.get(ptr).apply(null,args)}function getDynCaller(sig,ptr){assert(sig.indexOf("j")>=0,"getDynCaller should only be called with i64 sigs");var argCache=[];return function(){argCache.length=arguments.length;for(var i=0;i>2)+i])}return array}function __embind_register_class_constructor(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor){assert(argCount>0);var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);var args=[rawConstructor];var destructors=[];whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName="constructor "+classType.name;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError("Cannot register multiple constructors with identical number of parameters ("+(argCount-1)+") for class '"+classType.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!")}classType.registeredClass.constructor_body[argCount-1]=function unboundTypeHandler(){throwUnboundTypeError("Cannot construct "+classType.name+" due to unbound types",rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){classType.registeredClass.constructor_body[argCount-1]=function constructor_body(){if(arguments.length!==argCount-1){throwBindingError(humanName+" called with "+arguments.length+" arguments, expected "+(argCount-1))}destructors.length=0;args.length=argCount;for(var i=1;i0?", ":"")+argsListWired}invokerFnBody+=(returns?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i>1])};case 2:return function(pointer){var heap=signed?HEAP32:HEAPU32;return this["fromWireType"](heap[pointer>>2])};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_enum(rawType,name,size,isSigned){var shift=getShiftFromSize(size);name=readLatin1String(name);function ctor(){}ctor.values={};registerType(rawType,{name:name,constructor:ctor,"fromWireType":function(c){return this.constructor.values[c]},"toWireType":function(destructors,c){return c.value},"argPackAdvance":8,"readValueFromPointer":enumReadValueFromPointer(name,shift,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor)}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __embind_register_enum_value(rawEnumType,name,enumValue){var enumType=requireRegisteredType(rawEnumType,"enum");name=readLatin1String(name);var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(enumType.name+"_"+name,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value}function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function __embind_register_function(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn){var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError("Cannot call "+name+" due to unbound types",argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn),argCount-1);return[]})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<>>bitshift}}var isUnsignedType=name.indexOf("unsigned")!=-1;registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(valuemaxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(i==length||HEAPU8[currentBytePtr]==0){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i>2];var HEAP=getHeap();var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||HEAP[currentBytePtr>>shift]==0){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},"toWireType":function(destructors,value){if(!(typeof value==="string")){throwBindingError("Cannot pass non-string to C++ string type "+name)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length>>shift;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_value_object(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor){structRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}}function __embind_register_value_object_field(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){structRegistrations[structType].fields.push({fieldName:readLatin1String(fieldName),getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function requireHandle(handle){if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value}function __emval_as(handle,returnType,destructorsRef){handle=requireHandle(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=__emval_register(destructors);HEAP32[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}else{return symbol}}var emval_methodCallers=[];function __emval_call_void_method(caller,handle,methodName,args){caller=emval_methodCallers[caller];handle=requireHandle(handle);methodName=getStringOrSymbol(methodName);caller(handle,methodName,null,args)}function emval_get_global(){if(typeof globalThis==="object"){return globalThis}return function(){return Function}()("return this")()}function __emval_get_global(name){if(name===0){return __emval_register(emval_get_global())}else{name=getStringOrSymbol(name);return __emval_register(emval_get_global()[name])}}function __emval_addMethodCaller(caller){var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id}function __emval_lookupTypes(argCount,argTypes){var a=new Array(argCount);for(var i=0;i>2)+i],"parameter "+i)}return a}function __emval_get_method_caller(argCount,argTypes){var types=__emval_lookupTypes(argCount,argTypes);var retType=types[0];var signatureName=retType.name+"_$"+types.slice(1).map(function(t){return t.name}).join("_")+"$";var params=["retType"];var args=[retType];var argsList="";for(var i=0;i4){emval_handle_array[handle].refcount+=1}}function craftEmvalAllocator(argCount){var argsList="";for(var i=0;i>> 2) + "+i+'], "parameter '+i+'");\n'+"var arg"+i+" = argType"+i+".readValueFromPointer(args);\n"+"args += argType"+i+"['argPackAdvance'];\n"}functionBody+="var obj = new constructor("+argsList+");\n"+"return __emval_register(obj);\n"+"}\n";return new Function("requireRegisteredType","Module","__emval_register",functionBody)(requireRegisteredType,Module,__emval_register)}var emval_newers={};function __emval_new(handle,argCount,argTypes,args){handle=requireHandle(handle);var newer=emval_newers[argCount];if(!newer){newer=craftEmvalAllocator(argCount);emval_newers[argCount]=newer}return newer(handle,argTypes,args)}function __emval_new_cstring(v){return __emval_register(getStringOrSymbol(v))}function __emval_run_destructors(handle){var destructors=emval_handle_array[handle].value;runDestructors(destructors);__emval_decref(handle)}function _abort(){abort()}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var SYSCALLS={mappings:{},buffers:[null,[],[]],printChar:function(stream,curr){var buffer=SYSCALLS.buffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},get64:function(low,high){return low}};function _fd_close(fd){return 0}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){}function _fd_write(fd,iov,iovcnt,pnum){var num=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];for(var j=0;j>2]=num;return 0}function _setTempRet0($i){setTempRet0($i|0)}InternalError=Module["InternalError"]=extendError(Error,"InternalError");embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");init_ClassHandle();init_RegisteredPointer();init_embind();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();__ATINIT__.push({func:function(){___wasm_call_ctors()}});var asmLibraryArg={"t":__embind_finalize_value_object,"I":__embind_register_bool,"x":__embind_register_class,"w":__embind_register_class_constructor,"d":__embind_register_class_function,"k":__embind_register_constant,"H":__embind_register_emval,"n":__embind_register_enum,"a":__embind_register_enum_value,"A":__embind_register_float,"i":__embind_register_function,"j":__embind_register_integer,"h":__embind_register_memory_view,"B":__embind_register_std_string,"v":__embind_register_std_wstring,"u":__embind_register_value_object,"c":__embind_register_value_object_field,"J":__embind_register_void,"m":__emval_as,"s":__emval_call_void_method,"b":__emval_decref,"y":__emval_get_global,"p":__emval_get_method_caller,"r":__emval_get_module_property,"e":__emval_get_property,"g":__emval_incref,"q":__emval_new,"f":__emval_new_cstring,"l":__emval_run_destructors,"o":_abort,"E":_emscripten_memcpy_big,"F":_emscripten_resize_heap,"G":_fd_close,"C":_fd_seek,"z":_fd_write,"D":_setTempRet0};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["M"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["N"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["O"]).apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return(___getTypeName=Module["___getTypeName"]=Module["asm"]["P"]).apply(null,arguments)};var ___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=function(){return(___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=Module["asm"]["Q"]).apply(null,arguments)};var dynCall_jiji=Module["dynCall_jiji"]=function(){return(dynCall_jiji=Module["dynCall_jiji"]=Module["asm"]["R"]).apply(null,arguments)};var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run(); + + + return BASIS.ready +} +); +})(); +if (typeof exports === 'object' && typeof module === 'object') + module.exports = BASIS; +else if (typeof define === 'function' && define['amd']) + define([], function() { return BASIS; }); +else if (typeof exports === 'object') + exports["BASIS"] = BASIS; diff --git a/inc/utils/basis/basis_transcoder.wasm b/inc/utils/basis/basis_transcoder.wasm new file mode 100644 index 0000000..4b9c343 Binary files /dev/null and b/inc/utils/basis/basis_transcoder.wasm differ diff --git a/inc/utils/media-modal/collections/asset-images.js b/inc/utils/media-modal/collections/asset-images.js new file mode 100644 index 0000000..10128f3 --- /dev/null +++ b/inc/utils/media-modal/collections/asset-images.js @@ -0,0 +1,7 @@ +import AssetImage from '../models/asset-image'; + +const AssetImages = Backbone.Collection.extend({ + model: AssetImage, +}); + +export default AssetImages; \ No newline at end of file diff --git a/inc/utils/media-modal/customtab.php b/inc/utils/media-modal/customtab.php new file mode 100644 index 0000000..04ce1b8 --- /dev/null +++ b/inc/utils/media-modal/customtab.php @@ -0,0 +1,5 @@ + diff --git a/inc/utils/media-modal/media-modal.css b/inc/utils/media-modal/media-modal.css new file mode 100644 index 0000000..ec801f7 --- /dev/null +++ b/inc/utils/media-modal/media-modal.css @@ -0,0 +1,36 @@ +.asset-item.selected { + outline: 4px solid #0073aa; + /* box-shadow: 0 0 5px rgba(0, 115, 170, 0.8); */ + overflow: hidden; + height: 150px; + } + +.asset-item img { + width: auto; + height: 150px; + } + + .asset-item div { + height: 150px; + } + + .search-container { + margin-bottom: 10px; +} + +.search-input { + width: 200px; + margin-right: 10px; +} + +.category-filters { + margin-bottom: 10px; +} + +.category-filter { + margin-right: 5px; +} + +.category-filter.selected { + font-weight: bold; +} diff --git a/inc/utils/media-modal/media-modal.js b/inc/utils/media-modal/media-modal.js new file mode 100644 index 0000000..d5b3dcf --- /dev/null +++ b/inc/utils/media-modal/media-modal.js @@ -0,0 +1,264 @@ +(function (wp) { + const AssetView = wp.Backbone.View.extend({ + tagName: 'li', + className: 'asset-item', + style: 'margin: 10px;', + template: wp.template('asset-template'), + events: { + 'click .asset-details': 'selectAsset', + 'click .assets-load-more': 'loadMoreAssets', + }, + initialize: function () { + this.listenTo(this.model, 'change:selected', this.toggleSelected); + }, + render: function () { + this.$el.html(this.template(model= this.model)); + return this; + }, + toggleSelected: function () { + this.$el.toggleClass('selected', this.model.get('selected')); + }, + selectAsset: function () { + const parent = this.$el[0].parentElement; + + // Check if the current item is already selected + const isSelected = this.$el.hasClass('selected'); + + // Remove the 'selected' class from all children + for (let i = 0; i < parent.children.length; i++) { + parent.children[i].classList.remove('selected'); + } + + // If the current item was not selected before, add the 'selected' class + if (!isSelected) { + this.$el.addClass('selected'); + this.model.set('selected', true); + this.trigger('select', this.model); + } else { + // If the current item was already selected, deselect it + this.model.set('selected', false); + this.trigger('select', null); + } + }, + }); + let currentPage = 1; + let totalPages = 1; + let hasMore = false; + let offset = 0; + let selectedCategories = []; + let searchQuery = ''; + let nonce = threeovAssetsTab.toybox_nonce; + const CustomMediaTab = wp.media.View.extend({ + tagName: 'div', + className: 'custom-media-tab', + template: wp.template('custom-media-tab'), + events: { + 'click .category-filter': 'handleCategoryFilter', + 'click .search-submit': 'handleSearch', // Change the event to 'click' on the search submit button + 'click .clear-search': 'clearSearch', + 'click .assets-load-more': 'loadMoreAssets', + 'click .media-modal-close': 'close', + }, + initialize: function (options) { + this.apiKey = options.apiKey; + this.assets = new wp.media.model.Attachments(); + this.listenTo(this.assets, 'reset', this.render); + this.loadAssets(); + this.isThreeObjectViewerModal = options.controller.options.three_object_viewer_modal; + console.log('isThreeObjectViewerModal', options); + }, + render: function () { + this.$el.html(this.template()); + this.$('.asset-list').empty(); + this.assets.each(this.renderAsset, this); + this.renderCategories(); + return this; + }, + renderAsset: function (asset) { + if (!asset.attributes.fileurl || !asset.attributes.filename) { + console.error('Incomplete asset data:', asset); + return; + } + const assetData = { + id: asset.attributes.id || 0, + fileurl: asset.attributes.fileurl, + filename: asset.attributes.filename, + thumburl: asset.attributes.thumburl, + }; + const assetModel = new wp.media.model.Attachment(assetData); + if (!assetModel) { + console.error('Failed to create asset model:', assetData); + return; + } + const assetView = new AssetView({ model: assetModel }); + this.listenTo(assetView, 'select', this.selectAttachment); + this.$('.asset-list').append(assetView.render().el); + }, + loadAssets: function () { + const self = this; + const apiUrl = '/wp-json/toybox/v1/assets'; + const params = { + limit: 10, + offset: offset, + search: encodeURIComponent(searchQuery), + categories: encodeURIComponent(selectedCategories.join(',')), + }; + + jQuery.ajax({ + url: apiUrl, + method: 'GET', + data: params, + beforeSend: function ( xhr ) { + xhr.setRequestHeader( 'X-WP-Nonce', nonce ); + }, + success: function (response) { + self.assets.add(response.assets); + totalPages = response.pagination.total; + hasMore = response.pagination.hasMore; + if (hasMore) { + currentPage++; + } + self.render(); + }, + error: function (xhr, status, error) { + console.error('Error:', error); + }, + }); + }, + loadMoreAssets: function () { + offset = currentPage * 10; + this.loadAssets(); + }, + close: function () { + // reset assets and pagination + currentPage = 1; + offset = 0; + totalPages = 1; + hasMore = false; + selectedCategories = []; + searchQuery = ''; + this.assets.reset(); + }, + selectAttachment: function (selectedModel) { + // Deselect all assets + this.assets.each(function (asset) { + asset.set('selected', false); + }); + + // Set the selected asset + selectedModel.set('selected', true); + + const attachment = { + id: selectedModel.get('id') || 0, + url: selectedModel.get('fileurl'), + alt: selectedModel.get('filename'), + thumb: selectedModel.get('thumburl'), + // Add other relevant attachment details + }; + + const selection = this.controller.state().get('selection'); + const attachmentModel = new wp.media.model.Attachment(attachment); + selection.reset([attachmentModel]); + // clear pagination and load more assets + currentPage = 1; + offset = 0; + this.assets.reset(); + this.controller.trigger('selection:toggle'); + }, + handleCategoryFilter: function (event) { + const category = jQuery(event.currentTarget).data('category'); + const index = selectedCategories.indexOf(category); + if (index > -1) { + selectedCategories.splice(index, 1); + } else { + selectedCategories.push(category); + } + currentPage = 1; + offset = 0; + this.assets.reset(); + this.loadAssets(); + }, + handleSearch: function (event) { + event.preventDefault(); // Prevent form submission + searchQuery = this.$('.search-input').val(); // Get the search query from the input field + currentPage = 1; + offset = 0; + // clear the current assets and load new assets based on the search query + this.assets.reset(); + this.loadAssets(); + }, + clearSearch: function () { + searchQuery = ''; + this.$('.search-input').val(''); + currentPage = 1; + offset = 0; + selectedCategories = []; + this.assets.reset(); + this.loadAssets(); + }, + renderCategories: function () { + const self = this; + const url = '/wp-json/toybox/v1/categories'; // Update the URL to the new endpoint + + jQuery.ajax({ + url: url, + method: 'GET', + beforeSend: function (xhr) { + xhr.setRequestHeader('X-WP-Nonce', threeovAssetsTab.toybox_nonce); + }, + success: function (categories) { + const categoriesContainer = self.$('.category-filters'); + categoriesContainer.empty(); + categories.forEach(function (category) { + const isSelected = selectedCategories.includes(category.category); + const categoryButton = jQuery('