diff --git a/admin/three-model-viewer-settings/App.js b/admin/three-model-viewer-settings/App.js
deleted file mode 100644
index 071af07..0000000
--- a/admin/three-model-viewer-settings/App.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import { useState, useEffect } from '@wordpress/element';
-
-//Main component for admin page app
-export default function App( { getSettings, updateSettings } ) {
- //Track settings state
- const [ settings, setSettings ] = useState( {} );
- //Use to show loading spinner
-
- const [ isLoading, setIsLoading ] = useState( true );
- //When app loads, get settings
- useEffect( () => {
- getSettings().then( ( r ) => {
- setSettings( r );
- setIsLoading( false );
- } );
- }, [ getSettings, setSettings ] );
-
- //Function to update settings via API
- const onSave = () => {
- updateSettings( settings ).then( ( r ) => {
- setSettings( r );
- } );
- };
-
- //Show a spinner if loading
- if ( isLoading ) {
- return
;
- }
-
- //Show settings if not loading
- return (
-
-
{ settings.enabled ? 'Enabled' : 'Not enabled' }
-
-
- {
- setSettings( {
- ...settings,
- enabled: ! settings.enabled,
- } );
- } }
- />
-
-
-
-
-
-
- );
-}
diff --git a/admin/three-object-viewer-settings/App.js b/admin/three-object-viewer-settings/App.js
new file mode 100644
index 0000000..0a4653c
--- /dev/null
+++ b/admin/three-object-viewer-settings/App.js
@@ -0,0 +1,203 @@
+import { Suspense, useState, useEffect } from "@wordpress/element";
+import { Canvas, useLoader, useFrame, useThree } from '@react-three/fiber';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
+import { VRM, VRMUtils, VRMSchema, VRMLoaderPlugin } from '@pixiv/three-vrm'
+import {
+ OrthographicCamera,
+ OrbitControls,
+ useAnimations,
+} from '@react-three/drei';
+import * as THREE from 'three';
+import defaultAikonaut from '../../inc/avatars/mummy.vrm';
+
+function SavedObject( props ) {
+ const [ url, set ] = useState( props.url );
+ useEffect( () => {
+ setTimeout( () => set( props.url ), 2000 );
+ }, [] );
+ const [ listener ] = useState( () => new THREE.AudioListener() );
+
+ useThree( ( { camera } ) => {
+ camera.add( listener );
+ } );
+ const fallbackURL = threeObjectPlugin + defaultAikonaut;
+ const playerURL = props.url ? props.url : 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( 3, 3, 3 );
+ playerController.scene.position.set( 0, -2.5, 0 );
+ return <>>;
+ }
+}
+
+//Main component for admin page app
+export default function App({ getSettings, updateSettings }) {
+
+ let frame
+
+ //Track settings state
+ const [settings, setSettings] = useState({});
+ //Use to show loading spinner
+ const [defaultVRM, setDefaultVRM] = useState();
+ //Use to show loading spinner
+
+ const [isLoading, setIsLoading] = useState(true);
+ //When app loads, get settings
+ useEffect(() => {
+ getSettings().then((r) => {
+ setSettings(r);
+ setIsLoading(false);
+ });
+ }, [getSettings, setSettings]);
+
+ //Function to update settings via API
+ const onSave = () => {
+ updateSettings(settings).then((r) => {
+ setSettings(r);
+ });
+ };
+ const runUploader = (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',
+ button: {
+ text: 'Use this media',
+ },
+ multiple: false, // Set to true to allow multiple files to be selected
+ })
+ frame.on( 'select', function() {
+
+ // Get media attachment details from the frame state
+ var attachment = frame.state().get('selection').first().toJSON();
+ // console.log(attachment);
+ setDefaultVRM(attachment.url);
+ // Send the attachment URL to our custom image input field.
+ });
+
+
+ // Finally, open the modal on click
+ frame.open()
+ }
+
+ //Show a spinner if loading
+ if (isLoading) {
+ return ;
+ }
+
+ //Show settings if not loading
+ return (
+
+
+
Three Object Viewer Settings
+
+
+
Avatar and World Defaults
+
This avatar will be used for guest visitors or logged in users that have not set their main avatar in the user profile page.
+
+
+
+
+
+ { defaultVRM && defaultVRM }
+
+
+
+
+
Network Settings
+
+
+
Network Settings
+
+
+ {
+ setSettings({ ...settings, enabled: !settings.enabled });
+ }}
+ />
+
+
+
{
+ setSettings({ ...settings, networkWorker: !settings.networkWorker });
+ }}
+ />
+
+
+
+
+
+
+ );
+}
diff --git a/admin/three-model-viewer-settings/App.test.js.test b/admin/three-object-viewer-settings/App.test.js.test
similarity index 100%
rename from admin/three-model-viewer-settings/App.test.js.test
rename to admin/three-object-viewer-settings/App.test.js.test
diff --git a/admin/three-model-viewer-settings/index.js b/admin/three-object-viewer-settings/index.js
similarity index 83%
rename from admin/three-model-viewer-settings/index.js
rename to admin/three-object-viewer-settings/index.js
index b359266..25c724f 100644
--- a/admin/three-model-viewer-settings/index.js
+++ b/admin/three-object-viewer-settings/index.js
@@ -5,7 +5,7 @@ import apiFetch from '@wordpress/api-fetch';
window.addEventListener( 'load', async function () {
//Endpoint URL
- const path = '/three-object-viewer/v1/three-model-viewer-settings/';
+ const path = '/three-object-viewer/v1/three-object-viewer-settings/';
//Get settings from the REST API endpoint
const getSettings = async () => {
@@ -28,6 +28,6 @@ window.addEventListener( 'load', async function () {
render(
,
- document.getElementById( 'three-model-viewer-settings' )
+ document.getElementById( 'three-object-viewer-settings' )
);
} );
diff --git a/admin/three-model-viewer-settings/init.php b/admin/three-object-viewer-settings/init.php
similarity index 66%
rename from admin/three-model-viewer-settings/init.php
rename to admin/three-object-viewer-settings/init.php
index 519948b..370df69 100644
--- a/admin/three-model-viewer-settings/init.php
+++ b/admin/three-object-viewer-settings/init.php
@@ -1,7 +1,8 @@
['GET'],
@@ -47,22 +51,22 @@
//Enqueue assets for Model Viewer Settings on admin page only
add_action('admin_enqueue_scripts', function ($hook) {
- if ('toplevel_page_three-model-viewer-settings' != $hook) {
+ if ('toplevel_page_three-object-viewer-settings' != $hook) {
return;
}
- wp_enqueue_script('three-model-viewer-settings');
+ wp_enqueue_script('three-object-viewer-settings');
});
//Register Model Viewer Settings menu page
-// add_action('admin_menu', function () {
-// add_menu_page(
-// __('Model Viewer Settings', 'three-object-viewer'),
-// __('Model Viewer Settings', 'three-object-viewer'),
-// 'manage_options',
-// 'three-model-viewer-settings',
-// function () {
-// //React root
-// echo '';
-// }
-// );
-// });
+add_action('admin_menu', function () {
+ add_menu_page(
+ __('Model Viewer Settings', 'three-object-viewer'),
+ __('Model Viewer Settings', 'three-object-viewer'),
+ 'manage_options',
+ 'three-object-viewer-settings',
+ function () {
+ //React root
+ echo '';
+ }
+ );
+});
diff --git a/blocks/environment/Edit.js b/blocks/environment/Edit.js
new file mode 100644
index 0000000..4df0dde
--- /dev/null
+++ b/blocks/environment/Edit.js
@@ -0,0 +1,284 @@
+import { __ } from '@wordpress/i18n';
+import React, { useState } from 'react';
+import { DropZone } from '@wordpress/components';
+import './editor.scss';
+import {
+ useBlockProps,
+ ColorPalette,
+ InspectorControls,
+ MediaUpload,
+ InnerBlocks
+} from '@wordpress/block-editor';
+import {
+ Panel,
+ PanelBody,
+ PanelRow,
+ RangeControl,
+ ToggleControl,
+ SelectControl,
+ TextControl,
+} from '@wordpress/components';
+import { more } from '@wordpress/icons';
+
+import ThreeObjectEdit from './components/ThreeObjectEdit';
+
+export default function Edit( { attributes, setAttributes, isSelected } ) {
+ const ALLOWED_BLOCKS = ['three-object-viewer/model-block', 'three-object-viewer/sky-block', 'three-object-viewer/npc-block', 'three-object-viewer/three-image-block', 'three-object-viewer/three-video-block', 'three-object-viewer/three-audio-block' ];
+
+ const onChangeAnimations = ( animations ) => {
+ setAttributes( { animations: animations } );
+ };
+
+ const onImageSelect = ( imageObject ) => {
+ setAttributes( { threeObjectUrl: null } );
+ setAttributes( { threeObjectUrl: imageObject.url } );
+ };
+ const onChangePositionY = ( posy ) => {
+ setAttributes( { positionY: posy } );
+ };
+
+ const onChangeScale = ( scale ) => {
+ setAttributes( { scale: scale } );
+ };
+
+ const onChangerotationY = ( rotz ) => {
+ setAttributes( { rotationY: rotz } );
+ };
+
+ const setDeviceTarget = ( target ) => {
+ setAttributes( { deviceTarget: target } );
+ };
+
+ const [ enteredURL, setEnteredURL ] = useState( "" );
+
+ const { mediaUpload } = wp.editor;
+
+ const ALLOWED_MEDIA_TYPES = [
+ 'model/gltf-binary',
+ 'application/octet-stream',
+ ];
+
+ const MyDropZone = () => {
+ const [ hasDropped, setHasDropped ] = useState( false );
+ return (
+
+ { hasDropped ? 'Dropped!' : 'Drop a glb here or' }
+
+ mediaUpload( {
+ allowedTypes: ALLOWED_MEDIA_TYPES,
+ filesList: files,
+ onFileChange: ( [ images ] ) => {
+ onImageSelect( images );
+ },
+ } )
+ }
+ />
+
+ );
+ };
+
+ return (
+
+
+
+
+
+
+ Select a glb file from your media library. This will be treated as a collidable mesh that visitors can walk on:
+
+
+
+
+ onImageSelect( imageObject )
+ }
+ type="image"
+ label="GLB File"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.threeObjectUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+
+
+
+ Object Display Type:
+
+
+
+ setDeviceTarget( target )
+ }
+ />
+
+
+
+ onChangeAnimations( value )
+ }
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { isSelected ? (
+ <>
+ { attributes.threeObjectUrl ? (
+
+ ) : (
+
+
+
+
+
+ Select a glb file to render in the canvas:
+
+ {/*
+ setEnteredURL(e.target.value)}>
+
+
*/}
+
+ onImageSelect( imageObject )
+ }
+ type="image"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.threeObjectUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+
+ ) }
+
+ >
+ ) : (
+ <>
+ { attributes.threeObjectUrl ? (
+
+ ) : (
+
+
+
+
+ Select a glb file to render in the canvas:
+
+ {/*
+ console.log(e.target.value) && setEnteredURL(e.target.value)}>
+
+
*/}
+
+
+ onImageSelect( imageObject )
+ }
+ type="image"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.threeObjectUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+ ) }
+
+ >
+ ) }
+
+ );
+}
diff --git a/blocks/environment/Edit.test.js b/blocks/environment/Edit.test.js
new file mode 100644
index 0000000..ab10afe
--- /dev/null
+++ b/blocks/environment/Edit.test.js
@@ -0,0 +1,57 @@
+
+//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");
+ });
+});
\ No newline at end of file
diff --git a/blocks/environment/Save.js b/blocks/environment/Save.js
new file mode 100644
index 0000000..79a0576
--- /dev/null
+++ b/blocks/environment/Save.js
@@ -0,0 +1,41 @@
+import { __ } from '@wordpress/i18n';
+import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
+
+export default function save( { attributes } ) {
+ 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/environment/block.json b/blocks/environment/block.json
new file mode 100644
index 0000000..722fa06
--- /dev/null
+++ b/blocks/environment/block.json
@@ -0,0 +1,44 @@
+{
+ "name": "three-object-viewer/environment",
+ "title": "Environment Block",
+ "description": "A 3D environment component for VersePress",
+ "attributes": {
+ "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
+ },
+ "deviceTarget": {
+ "type": "string",
+ "default": "2d"
+ },
+ "animations": {
+ "type": "string",
+ "default": ""
+ }
+ },
+ "category": "3D",
+ "apiVersion": 2,
+ "supports": {
+ "html": false,
+ "multiple": true
+ },
+ "editorScript": "file:../../build/block-environment.js",
+ "editorStyle": "file:../../build/block-environment.css",
+ "style": "file:../../build/block-environment.css"
+}
diff --git a/blocks/environment/components/Controls.js b/blocks/environment/components/Controls.js
new file mode 100644
index 0000000..fbca728
--- /dev/null
+++ b/blocks/environment/components/Controls.js
@@ -0,0 +1,109 @@
+import React, { useRef, useState } from 'react';
+import { useFrame } from '@react-three/fiber';
+import { PointerLockControls } from '@react-three/drei';
+import previewOptions from '@wordpress/block-editor/build/components/preview-options';
+
+const Controls = () => {
+ const controlsRef = useRef();
+ const isLocked = useRef( false );
+ const [ moveForward, setMoveForward ] = useState( false );
+ const [ moveBackward, setMoveBackward ] = useState( false );
+ const [ moveLeft, setMoveLeft ] = useState( false );
+ const [ moveRight, setMoveRight ] = useState( false );
+
+ useFrame( () => {
+ const velocity = 0.5;
+ if ( moveForward ) {
+ controlsRef.current.moveForward( velocity );
+ } else if ( moveLeft ) {
+ controlsRef.current.moveRight( -velocity );
+ } else if ( moveBackward ) {
+ controlsRef.current.moveForward( -velocity );
+ } else if ( moveRight ) {
+ controlsRef.current.moveRight( velocity );
+ }
+ } );
+
+ const onKeyDown = function ( event ) {
+ switch ( event.code ) {
+ case 'ArrowUp':
+ case 'KeyW':
+ setMoveForward( true );
+ break;
+
+ case 'ArrowLeft':
+ case 'KeyA':
+ setMoveLeft( true );
+ break;
+
+ case 'ArrowDown':
+ case 'KeyS':
+ setMoveBackward( true );
+ break;
+
+ case 'ArrowRight':
+ case 'KeyD':
+ setMoveRight( true );
+ break;
+ case "Space":
+ window.addEventListener('keydown', (e) => {
+ if (e.keyCode === 32 && e.target === document.body) {
+ e.preventDefault();
+ }
+ });
+ console.log("boing");
+ break;
+ default:
+ return;
+ }
+ };
+
+ const onKeyUp = function ( event ) {
+ switch ( event.code ) {
+ case 'ArrowUp':
+ case 'KeyW':
+ setMoveForward( false );
+ break;
+
+ case 'ArrowLeft':
+ case 'KeyA':
+ setMoveLeft( false );
+ break;
+
+ case 'ArrowDown':
+ case 'KeyS':
+ setMoveBackward( false );
+ break;
+
+ case 'ArrowRight':
+ case 'KeyD':
+ setMoveRight( false );
+ break;
+
+ default:
+ return;
+ }
+ };
+
+ document.addEventListener( 'keydown', onKeyDown );
+ document.addEventListener( 'keyup', onKeyUp );
+ return (
+ {
+ if ( controlsRef.current ) {
+ controlsRef.current.addEventListener( 'lock', () => {
+ console.log( 'lock' );
+ isLocked.current = true;
+ } );
+ controlsRef.current.addEventListener( 'unlock', () => {
+ console.log( 'unlock' );
+ isLocked.current = false;
+ } );
+ }
+ } }
+ ref={ controlsRef }
+ />
+ );
+};
+
+export default Controls;
diff --git a/blocks/environment/components/EnvironmentFront.js b/blocks/environment/components/EnvironmentFront.js
new file mode 100644
index 0000000..d0d009c
--- /dev/null
+++ b/blocks/environment/components/EnvironmentFront.js
@@ -0,0 +1,513 @@
+import * as THREE from 'three';
+import React, { Suspense, useRef, useState, useEffect } from 'react';
+import { Canvas, useLoader, useFrame, useThree } from '@react-three/fiber';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
+import { Physics, RigidBody } from "@react-three/rapier";
+
+import {
+ OrthographicCamera,
+ OrbitControls,
+ useAnimations,
+} from '@react-three/drei';
+import { GLTFAudioEmitterExtension } from 'three-omi';
+import {
+ VRCanvas,
+ ARCanvas,
+ DefaultXRControllers,
+ Hands,
+} from '@react-three/xr';
+import { VRM, VRMUtils, VRMSchema, VRMLoaderPlugin } from '@pixiv/three-vrm'
+import TeleportTravel from './TeleportTravel';
+import defaultVRM from '../../../inc/avatars/mummy.vrm';
+import Controls from './Controls';
+import { useAspect } from '@react-three/drei'
+
+function SavedObject( props ) {
+ const [ url, set ] = useState( props.url );
+ const [cameraPosition, setCameraPosition] = useState();
+ useEffect( () => {
+ setTimeout( () => set( props.url ), 2000 );
+ }, [] );
+ const [ listener ] = useState( () => new THREE.AudioListener() );
+
+ useThree( ( { camera } ) => {
+ camera.add( listener );
+ } );
+
+ const gltf = useLoader( GLTFLoader, url, ( loader ) => {
+ loader.register(
+ ( parser ) => new GLTFAudioEmitterExtension( parser, listener )
+ );
+ loader.register( ( parser ) => {
+ return new VRMLoaderPlugin( parser );
+ } );
+ } );
+
+ const { actions } = useAnimations( gltf.animations, gltf.scene );
+
+ const animationList = props.animations ? props.animations.split( ',' ) : '';
+ useEffect( () => {
+ if ( animationList ) {
+ animationList.forEach( ( name ) => {
+ if ( Object.keys( actions ).includes( name ) ) {
+ actions[ name ].play();
+ }
+ } );
+ }
+ }, [] );
+
+ // Player controller.
+ // const fallbackURL = threeObjectPlugin + defaultVRM;
+ // const playerURL = props.playerData.vrm ? props.playerData.vrm : fallbackURL
+
+ // const someSceneState = useLoader( GLTFLoader, playerURL, ( loader ) => {
+ // loader.register(
+ // ( parser ) => new GLTFAudioEmitterExtension( parser, listener )
+ // );
+ // loader.register( ( parser ) => {
+ // return new VRMLoaderPlugin( parser );
+ // } );
+ // } );
+
+ // if(someSceneState?.userData?.gltfExtensions?.VRM){
+ // const playerController = someSceneState.userData.vrm;
+ // const { camera } = useThree();
+ // useFrame(() => {
+ // const offsetZ = camera.position.z - 0.4;
+ // const offsetY = camera.position.y - 10;
+ // playerController.scene.position.set( camera.position.x, offsetY, offsetZ );
+ // playerController.scene.rotation.set( camera.rotation.x, camera.rotation.y, camera.rotation.z );
+ // });
+ // VRMUtils.rotateVRM0( playerController );
+ // const rotationVRM = playerController.scene.rotation.y;
+ // playerController.scene.rotation.set( 0, rotationVRM, 0 );
+ // playerController.scene.scale.set( 1, 1, 1 );
+ // gltf.scene.position.set( 0, props.positionY, 0 );
+ // gltf.scene.rotation.set( 0, props.rotationY, 0 );
+ // gltf.scene.scale.set( props.scale, props.scale, props.scale );
+ // return <>>;
+ // }
+ // End controller.
+
+ if(gltf?.userData?.gltfExtensions?.VRM){
+ const vrm = gltf.userData.vrm;
+ vrm.scene.position.set( 0, props.positionY, 0 );
+ VRMUtils.rotateVRM0( vrm );
+ const rotationVRM = vrm.scene.rotation.y + parseFloat(props.rotationY);
+ vrm.scene.rotation.set( 0, rotationVRM, 0 );
+ vrm.scene.scale.set( props.scale, props.scale, props.scale );
+ return ;
+ }
+ gltf.scene.position.set( 0, props.positionY, 0 );
+ gltf.scene.rotation.set( 0, props.rotationY, 0 );
+ gltf.scene.scale.set( props.scale, props.scale, props.scale );
+ return <>>;
+}
+
+function ModelObject( model ) {
+ const [ url, set ] = useState( model.url );
+ useEffect( () => {
+ setTimeout( () => set( model.url ), 2000 );
+ }, [] );
+ const [ listener ] = useState( () => new THREE.AudioListener() );
+
+ useThree( ( { camera } ) => {
+ camera.add( listener );
+ } );
+
+ const gltf = useLoader( GLTFLoader, url, ( loader ) => {
+ loader.register(
+ ( parser ) => new GLTFAudioEmitterExtension( parser, listener )
+ );
+ loader.register( ( parser ) => {
+ return new VRMLoaderPlugin( parser );
+ } );
+ } );
+
+ const { actions } = useAnimations( gltf.animations, gltf.scene );
+
+ const animationList = model.animations ? model.animations.split( ',' ) : '';
+ useEffect( () => {
+ if ( animationList ) {
+ animationList.forEach( ( name ) => {
+ if ( Object.keys( actions ).includes( name ) ) {
+ actions[ name ].play();
+ }
+ } );
+ }
+ }, [] );
+ if(gltf?.userData?.gltfExtensions?.VRM){
+ const vrm = gltf.userData.vrm;
+ vrm.scene.position.set( model.positionX, model.positionY, model.positionZ );
+ VRMUtils.rotateVRM0( vrm );
+ const rotationVRM = vrm.scene.rotation.y + parseFloat(0);
+ vrm.scene.rotation.set( 0, rotationVRM, 0 );
+ vrm.scene.scale.set( 1, 1, 1 );
+ // vrm.scene.scale.set( props.scale, props.scale, props.scale );
+ return ;
+ }
+ gltf.scene.position.set( model.positionX, model.positionY, model.positionZ );
+ gltf.scene.rotation.set( 0, 0, 0 );
+ gltf.scene.scale.set(model.scaleX , model.scaleY, model.scaleZ );
+ // console.log(model.rotationX, model.rotationY, model.rotationZ);
+ gltf.scene.rotation.set(model.rotationX , model.rotationY, model.rotationZ );
+ // gltf.scene.scale.set( props.scale, props.scale, props.scale );
+ return <>>;
+}
+
+
+function Sky( sky ) {
+ // console.log(sky.src);
+ const skyUrl = sky.src[0].querySelector( 'p.sky-block-url' )
+ ? sky.src[0].querySelector( 'p.sky-block-url' ).innerText
+ : '';
+
+ const texture_1 = useLoader(THREE.TextureLoader, skyUrl);
+
+ return (
+
+
+
+
+ );
+}
+
+function ThreeImage( threeImage ) {
+ // console.log(threeImage.aspectWidth, threeImage.aspectHeight);
+ const texture_2 = useLoader(THREE.TextureLoader, threeImage.url);
+
+ return (
+
+
+
+
+ );
+}
+
+function ThreeVideo(threeVideo) {
+ console.log(threeVideo);
+ const clicked = true;
+ const [video] = useState(() => Object.assign(document.createElement('video'), { src: threeVideo.url, crossOrigin: 'Anonymous', loop: true, muted: true }));
+
+ useEffect(() => void (clicked && video.play()), [video, clicked]);
+
+ return (
+
+
+
+
+
+
+ );
+}
+
+
+function Floor( props ) {
+ return (
+
+
+
+
+ );
+}
+
+export default function EnvironmentFront( props ) {
+ if ( props.deviceTarget === 'vr' ) {
+ return (
+ <>
+
+
+
+
+
+
+
+
+ { props.threeUrl && (
+ <>
+
+
+
+
+ { props.threeUrl && (
+ <>
+
+ >
+ )}
+ { 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
+ : '';
+
+ 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( 'p.video-block-url' )
+ ? item.querySelector( 'p.video-block-url' ).innerText
+ : '';
+ console.log("no url?", videoUrl);
+ console.log(item);
+
+ 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
+ : '';
+
+ 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
+ : '';
+
+ return();
+ })}
+
+
+
+
+ >
+ ) }
+
+
+ {/* */}
+
+ >
+ );
+ }
+ if ( props.deviceTarget === '2d' ) {
+ return (
+ <>
+
+ { props.hasTip === '1' ? (
+ Click and drag ^
+ ) : (
+
+ ) }
+ >
+ );
+ }
+}
diff --git a/blocks/environment/components/TeleportTravel.js b/blocks/environment/components/TeleportTravel.js
new file mode 100644
index 0000000..3c0b646
--- /dev/null
+++ b/blocks/environment/components/TeleportTravel.js
@@ -0,0 +1,103 @@
+import { Raycaster, Vector3 } from 'three';
+import { useXR, Interactive } from '@react-three/xr';
+import { useFrame } from '@react-three/fiber';
+import { useCallback, useRef, useState } from 'react';
+
+export function TeleportIndicator( props ) {
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+}
+
+export default function TeleportTravel( props ) {
+ const {
+ centerOnTeleport,
+ Indicator = TeleportIndicator,
+ useNormal = true,
+ } = props;
+ const [ isHovered, setIsHovered ] = useState( false );
+ const target = useRef();
+ const targetLoc = useRef();
+ const ray = useRef( new Raycaster() );
+
+ const rayDir = useRef( {
+ pos: new Vector3(),
+ dir: new Vector3(),
+ } );
+
+ const { controllers, player } = useXR();
+
+ useFrame( () => {
+ if (
+ isHovered &&
+ controllers.length > 0 &&
+ ray.current &&
+ target.current &&
+ targetLoc.current
+ ) {
+ controllers[ 0 ].controller.getWorldDirection( rayDir.current.dir );
+ controllers[ 0 ].controller.getWorldPosition( rayDir.current.pos );
+ rayDir.current.dir.multiplyScalar( -1 );
+ ray.current.set( rayDir.current.pos, rayDir.current.dir );
+
+ const [ intersection ] = ray.current.intersectObject(
+ target.current
+ );
+
+ if ( intersection ) {
+ if ( useNormal ) {
+ const p = intersection.point;
+
+ targetLoc.current.position.set( 0, 0, 0 );
+
+ const n = intersection.face.normal.clone();
+ n.transformDirection( intersection.object.matrixWorld );
+
+ targetLoc.current.lookAt( n );
+ targetLoc.current.rotateOnAxis(
+ new Vector3( 1, 0, 0 ),
+ Math.PI / 2
+ );
+ targetLoc.current.position.copy( p );
+ } else {
+ targetLoc.current.position.copy( intersection.point );
+ }
+ }
+ }
+ } );
+
+ const click = useCallback( () => {
+ if ( isHovered ) {
+ player.position.copy( targetLoc.current.position );
+ }
+ }, [ centerOnTeleport, isHovered, useNormal ] );
+
+ return (
+ <>
+ { isHovered && (
+
+
+
+ ) }
+ setIsHovered( true ) }
+ onBlur={ () => setIsHovered( false ) }
+ >
+ { props.children }
+
+ >
+ );
+}
diff --git a/blocks/environment/components/ThreeObjectEdit.js b/blocks/environment/components/ThreeObjectEdit.js
new file mode 100644
index 0000000..2ae77f2
--- /dev/null
+++ b/blocks/environment/components/ThreeObjectEdit.js
@@ -0,0 +1,101 @@
+import * as THREE from 'three';
+import React, { Suspense, useRef, useState, useEffect } from 'react';
+import { Canvas, useLoader, useFrame, useThree } from '@react-three/fiber';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
+import {
+ PerspectiveCamera,
+ OrbitControls,
+ useAnimations,
+} from '@react-three/drei';
+import { VRMUtils, VRMLoaderPlugin } from '@pixiv/three-vrm'
+import { GLTFAudioEmitterExtension } from 'three-omi';
+
+function ThreeObject( props ) {
+ const [ url, set ] = useState( props.url );
+ useEffect( () => {
+ setTimeout( () => set( props.url ), 2000 );
+ }, [] );
+ const [ listener ] = useState( () => new THREE.AudioListener() );
+
+ useThree( ( { camera } ) => {
+ camera.add( listener );
+ } );
+
+ const gltf = useLoader( GLTFLoader, url, ( loader ) => {
+ loader.register(
+ ( parser ) => new GLTFAudioEmitterExtension( parser, listener )
+ );
+ loader.register( ( parser ) => {
+
+ return new VRMLoaderPlugin( parser );
+
+ } );
+
+ } );
+
+ const { actions } = useAnimations( gltf.animations, gltf.scene );
+
+ const animationList = props.animations ? props.animations.split( ',' ) : '';
+
+ useEffect( () => {
+ if ( animationList ) {
+ animationList.forEach( ( name ) => {
+ if ( Object.keys( actions ).includes( name ) ) {
+ actions[ name ].play();
+ }
+ } );
+ }
+ }, [] );
+
+ if(gltf?.userData?.gltfExtensions?.VRM){
+ const vrm = gltf.userData.vrm;
+ vrm.scene.position.set( 0, props.positionY, 0 );
+ VRMUtils.rotateVRM0( vrm );
+ const rotationVRM = vrm.scene.rotation.y + parseFloat(props.rotationY);
+ vrm.scene.rotation.set( 0, rotationVRM, 0 );
+ vrm.scene.scale.set( props.scale, props.scale, props.scale );
+ return ;
+ }
+ gltf.scene.position.set( 0, props.positionY, 0 );
+ gltf.scene.rotation.set( 0, props.rotationY, 0 );
+ gltf.scene.scale.set( props.scale, props.scale, props.scale );
+ return ;
+}
+
+export default function ThreeObjectEdit( props ) {
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/blocks/environment/editor.scss b/blocks/environment/editor.scss
new file mode 100644
index 0000000..a41c19e
--- /dev/null
+++ b/blocks/environment/editor.scss
@@ -0,0 +1,48 @@
+
+ .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;
+}
+
+.three-object-viewer-button {
+ background-color:rgb(199, 254, 0);
+ color: black;
+ border: solid 1.5px black;
+}
+
+.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/environment/frontend.js b/blocks/environment/frontend.js
new file mode 100644
index 0000000..0a4c388
--- /dev/null
+++ b/blocks/environment/frontend.js
@@ -0,0 +1,72 @@
+const { Component, render } = wp.element;
+
+import EnvironmentFront from './components/EnvironmentFront';
+
+const threeApp = document.querySelectorAll( '.three-object-three-app-environment' );
+const modelsToAdd = document.querySelectorAll( '.three-object-three-app-model-block' );
+const sky = document.querySelectorAll( '.three-object-three-app-sky-block' );
+const imagesToAdd = document.querySelectorAll( '.three-object-three-app-image-block' );
+const videosToAdd = document.querySelectorAll( '.three-object-three-app-video-block' );
+
+threeApp.forEach( ( threeApp ) => {
+ if ( threeApp ) {
+ const threeUrl = threeApp.querySelector( 'p.three-object-block-url' )
+ ? threeApp.querySelector( 'p.three-object-block-url' ).innerText
+ : '';
+ const deviceTarget = threeApp.querySelector(
+ 'p.three-object-block-device-target'
+ )
+ ? threeApp.querySelector( 'p.three-object-block-device-target' )
+ .innerText
+ : '2D';
+ const backgroundColor = threeApp.querySelector(
+ 'p.three-object-background-color'
+ )
+ ? threeApp.querySelector( 'p.three-object-background-color' ).innerText
+ : '#ffffff';
+ const zoom = threeApp.querySelector( 'p.three-object-zoom' )
+ ? threeApp.querySelector( 'p.three-object-zoom' ).innerText
+ : 90;
+ const scale = threeApp.querySelector( 'p.three-object-scale' )
+ ? threeApp.querySelector( 'p.three-object-scale' ).innerText
+ : 1;
+ const hasZoom = threeApp.querySelector( 'p.three-object-has-zoom' )
+ ? threeApp.querySelector( 'p.three-object-has-zoom' ).innerText
+ : false;
+ const hasTip = threeApp.querySelector( 'p.three-object-has-tip' )
+ ? threeApp.querySelector( 'p.three-object-has-tip' ).innerText
+ : true;
+ const positionY = threeApp.querySelector( 'p.three-object-position-y' )
+ ? threeApp.querySelector( 'p.three-object-position-y' ).innerText
+ : 0;
+ const rotationY = threeApp.querySelector( 'p.three-object-rotation-y' )
+ ? threeApp.querySelector( 'p.three-object-rotation-y' ).innerText
+ : 0;
+ const animations = threeApp.querySelector( 'p.three-object-animations' )
+ ? threeApp.querySelector( 'p.three-object-animations' ).innerText
+ : '';
+
+ render(
+ ,
+ threeApp
+ );
+ }
+} );
+
+
diff --git a/blocks/environment/index.js b/blocks/environment/index.js
new file mode 100644
index 0000000..22b14b6
--- /dev/null
+++ b/blocks/environment/index.js
@@ -0,0 +1,197 @@
+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: icon,
+ 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 }
+
+
+ >
+
+ );
+ },
+ },
+ ],
+} );
diff --git a/blocks/environment/init.php b/blocks/environment/init.php
new file mode 100644
index 0000000..4d16124
--- /dev/null
+++ b/blocks/environment/init.php
@@ -0,0 +1,10 @@
+ {
+ setAttributes( { positionX: positionX } );
+ };
+ const onChangePositionY = ( positionY ) => {
+ setAttributes( { positionY: positionY } );
+ };
+ const onChangePositionZ = ( positionZ ) => {
+ setAttributes( { positionZ: positionZ } );
+ };
+
+ const onChangeRotationX = ( rotationX ) => {
+ setAttributes( { rotationX: rotationX } );
+ };
+ const onChangeRotationY = ( rotationY ) => {
+ setAttributes( { rotationY: rotationY } );
+ };
+ const onChangeRotationZ = ( rotationZ ) => {
+ setAttributes( { rotationZ: rotationZ } );
+ };
+
+ const onChangeScaleX = ( scaleX ) => {
+ setAttributes( { scaleX: scaleX } );
+ };
+ const onChangeScaleY = ( scaleY ) => {
+ setAttributes( { scaleY: scaleY } );
+ };
+ const onChangeScaleZ = ( scaleZ ) => {
+ setAttributes( { scaleZ: scaleZ } );
+ };
+
+ const onChangeAnimations = ( animations ) => {
+ setAttributes( { animations: animations } );
+ };
+
+ const onImageSelect = ( imageObject ) => {
+ setAttributes( { threeObjectUrl: null } );
+ setAttributes( { threeObjectUrl: imageObject.url } );
+ };
+
+ const onChangeCollidable = ( collidableSetting ) => {
+ setAttributes( { collidable: collidableSetting } );
+ };
+
+ const [ enteredURL, setEnteredURL ] = useState( "" );
+
+ const { mediaUpload } = wp.editor;
+
+ const ALLOWED_MEDIA_TYPES = [
+ 'model/gltf-binary',
+ 'application/octet-stream',
+ ];
+
+ const MyDropZone = () => {
+ const [ hasDropped, setHasDropped ] = useState( false );
+ return (
+
+ { hasDropped ? 'Dropped!' : 'Drop a glb here or' }
+
+ mediaUpload( {
+ allowedTypes: ALLOWED_MEDIA_TYPES,
+ filesList: files,
+ onFileChange: ( [ images ] ) => {
+ onImageSelect( images );
+ },
+ } )
+ }
+ />
+
+ );
+ };
+
+ function handleClick(objectURL){
+ if(objectURL){
+ console.log("success good job", objectURL);
+ onImageSelect(objectURL);
+ }
+ console.log("fail", objectURL);
+ }
+
+
+ return (
+
+
+
+
+
+
+ select a glb file from your media library to
+ render an object in the canvas:
+
+
+
+
+ onImageSelect( imageObject )
+ }
+ type="image"
+ label="GLB File"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.threeObjectUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+
+
+
+ {
+ onChangeCollidable( e );
+ } }
+ />
+
+
+
+ onChangeAnimations( value )
+ }
+ />
+
+
+
+
+
+
+ onChangePositionX( value )
+ }
+ />
+
+ onChangePositionY( value )
+ }
+ />
+
+ onChangePositionZ( value )
+ }
+ />
+
+
+
+
+
+
+ onChangeRotationX( value )
+ }
+ />
+
+ onChangeRotationY( value )
+ }
+ />
+
+ onChangeRotationZ( value )
+ }
+ />
+
+
+
+
+
+
+ onChangeScaleX( value )
+ }
+ />
+
+ onChangeScaleY( value )
+ }
+ />
+
+ onChangeScaleZ( value )
+ }
+ />
+
+
+
+
+ { isSelected ? (
+ <>
+ { attributes.threeObjectUrl ? (
+
+ ) : (
+
+
+
+
+
+ Select a glb file to render in the canvas:
+
+ {/*
+ setEnteredURL(e.target.value)}>
+
+
*/}
+
+ onImageSelect( imageObject )
+ }
+ type="image"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.threeObjectUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+
+ ) }
+ >
+ ) : (
+ <>
+ { attributes.threeObjectUrl ? (
+
+ ) : (
+
+
+
+
+ Select a glb file to render in the canvas:
+
+ {/*
+ console.log(e.target.value) && setEnteredURL(e.target.value)}>
+
+
*/}
+
+
+ onImageSelect( imageObject )
+ }
+ type="image"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.threeObjectUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+ ) }
+ >
+ ) }
+
+ );
+}
diff --git a/blocks/model-block/Edit.test.js b/blocks/model-block/Edit.test.js
new file mode 100644
index 0000000..ab10afe
--- /dev/null
+++ b/blocks/model-block/Edit.test.js
@@ -0,0 +1,57 @@
+
+//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");
+ });
+});
\ No newline at end of file
diff --git a/blocks/model-block/Save.js b/blocks/model-block/Save.js
new file mode 100644
index 0000000..518ee4a
--- /dev/null
+++ b/blocks/model-block/Save.js
@@ -0,0 +1,28 @@
+import { __ } from '@wordpress/i18n';
+import { useBlockProps } from '@wordpress/block-editor';
+
+export default function save( { attributes } ) {
+ return (
+
+ <>
+
+
+ { attributes.threeObjectUrl }
+
+
{ attributes.scaleX }
+
{ attributes.scaleY }
+
{ attributes.scaleZ }
+
{ attributes.positionX }
+
{ attributes.positionY }
+
{ attributes.positionZ }
+
{ attributes.rotationX }
+
{ attributes.rotationY }
+
{ attributes.rotationZ }
+
+ { attributes.animations }
+
+
+ >
+
+ );
+}
diff --git a/blocks/model-block/block.json b/blocks/model-block/block.json
new file mode 100644
index 0000000..3fe62a1
--- /dev/null
+++ b/blocks/model-block/block.json
@@ -0,0 +1,64 @@
+{
+ "name": "three-object-viewer/model-block",
+ "title": "Model Block",
+ "description": "A 3D model for your VersePress environment",
+ "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
+ },
+ "animations": {
+ "type": "string",
+ "default": ""
+ },
+ "collidable": {
+ "type": "boolean",
+ "default": false
+ }
+ },
+ "category": "3D",
+ "apiVersion": 2,
+ "supports": {
+ "html": false,
+ "multiple": true
+ },
+ "editorScript": "file:../../build/block-model-block.js",
+ "editorStyle": "file:../../build/block-model-block.css",
+ "style": "file:../../build/block-model-block.css"
+}
diff --git a/blocks/model-block/components/ModelEdit.js b/blocks/model-block/components/ModelEdit.js
new file mode 100644
index 0000000..c71bd1b
--- /dev/null
+++ b/blocks/model-block/components/ModelEdit.js
@@ -0,0 +1,105 @@
+import * as THREE from 'three';
+import React, { Suspense, useRef, useState, useEffect } from 'react';
+import { Canvas, useLoader, useFrame, useThree } from '@react-three/fiber';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
+import {
+ OrthographicCamera,
+ PerspectiveCamera,
+ OrbitControls,
+ useAnimations,
+} from '@react-three/drei';
+import { VRM, VRMUtils, VRMSchema, VRMLoaderPlugin } from '@pixiv/three-vrm'
+import { GLTFAudioEmitterExtension } from 'three-omi';
+
+
+function ThreeObject( props ) {
+ const [ url, set ] = useState( props.url );
+ useEffect( () => {
+ setTimeout( () => set( props.url ), 2000 );
+ }, [] );
+ const [ listener ] = useState( () => new THREE.AudioListener() );
+
+ useThree( ( { camera } ) => {
+ camera.add( listener );
+ } );
+
+ const gltf = useLoader( GLTFLoader, url, ( loader ) => {
+ loader.register(
+ ( parser ) => new GLTFAudioEmitterExtension( parser, listener )
+ );
+ loader.register( ( parser ) => {
+
+ return new VRMLoaderPlugin( parser );
+
+ } );
+
+ } );
+
+ const { actions } = useAnimations( gltf.animations, gltf.scene );
+
+ const animationList = props.animations ? props.animations.split( ',' ) : '';
+
+ useEffect( () => {
+ if ( animationList ) {
+ animationList.forEach( ( name ) => {
+ if ( Object.keys( actions ).includes( name ) ) {
+ actions[ name ].play();
+ }
+ } );
+ }
+ }, [] );
+
+ if(gltf?.userData?.gltfExtensions?.VRM){
+ const vrm = gltf.userData.vrm;
+ vrm.scene.position.set( 0, props.positionY, 0 );
+ VRMUtils.rotateVRM0( vrm );
+ const rotationVRM = vrm.scene.rotation.y + parseFloat(props.rotationY);
+ vrm.scene.rotation.set( 0, rotationVRM, 0 );
+ vrm.scene.scale.set( props.scale, props.scale, props.scale );
+ return ;
+ }
+ gltf.scene.position.set( 0, 0, 0 );
+ gltf.scene.rotation.set( 0, 0, 0 );
+ gltf.scene.scale.set( 1, 1, 1 );
+ return ;
+}
+
+export default function ModelEdit( props ) {
+ return (
+ <>
+
+ { props.hasTip && (
+ Click and drag ^
+ ) }
+ >
+ );
+}
diff --git a/blocks/model-block/editor.scss b/blocks/model-block/editor.scss
new file mode 100644
index 0000000..9ed5c19
--- /dev/null
+++ b/blocks/model-block/editor.scss
@@ -0,0 +1,55 @@
+
+ .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;
+}
+
+.three-object-viewer-button {
+ background-color:rgb(199, 254, 0);
+ color: black;
+ border: solid 1.5px black;
+}
+
+.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;
+}
+
+.block-editor-block-inspector .components-base-control.position-inputs:last-child {
+ margin-bottom: 24px !important;
+}
+.block-editor-block-inspector .components-base-control.position-inputs {
+ padding-right: 5px;
+}
diff --git a/blocks/model-block/index.js b/blocks/model-block/index.js
new file mode 100644
index 0000000..22b14b6
--- /dev/null
+++ b/blocks/model-block/index.js
@@ -0,0 +1,197 @@
+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: icon,
+ 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 }
+
+
+ >
+
+ );
+ },
+ },
+ ],
+} );
diff --git a/blocks/model-block/init.php b/blocks/model-block/init.php
new file mode 100644
index 0000000..4d16124
--- /dev/null
+++ b/blocks/model-block/init.php
@@ -0,0 +1,10 @@
+ (
+ <>
+ {isSelected ? (
+
+ ) : (
+ {value}
+ )}
+ >
+);
+
+export default function Edit({ attributes, setAttributes, isSelected }) {
+ return (
+
+ setAttributes({ content })}
+ />
+
+ );
+}
diff --git a/blocks/npc-block/Edit.test.js b/blocks/npc-block/Edit.test.js
new file mode 100644
index 0000000..ab10afe
--- /dev/null
+++ b/blocks/npc-block/Edit.test.js
@@ -0,0 +1,57 @@
+
+//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");
+ });
+});
\ No newline at end of file
diff --git a/blocks/npc-block/Save.js b/blocks/npc-block/Save.js
new file mode 100644
index 0000000..3a499f1
--- /dev/null
+++ b/blocks/npc-block/Save.js
@@ -0,0 +1,6 @@
+import { __ } from '@wordpress/i18n';
+import { useBlockProps } from '@wordpress/block-editor';
+
+export default function save({ attributes }) {
+ return {attributes.content}
;
+}
\ No newline at end of file
diff --git a/blocks/npc-block/block.json b/blocks/npc-block/block.json
new file mode 100644
index 0000000..e197d03
--- /dev/null
+++ b/blocks/npc-block/block.json
@@ -0,0 +1,20 @@
+{
+ "name": "three-object-viewer/npc-block",
+ "title": "NPC Block",
+ "description": "A NPC Block to make your experience a bit more personal.",
+ "attributes": {
+ "content": {
+ "type": "string",
+ "default": "Hi Roy"
+ }
+ },
+ "category": "theme",
+ "apiVersion": 2,
+ "icon": "smiley",
+ "supports": {
+ "html": false
+ },
+ "editorScript": "file:../../build/block-npc-block.js",
+ "editorStyle": "file:../../build/block-npc-block.css",
+ "style": "file:../../build/block-npc-block.css"
+}
diff --git a/blocks/npc-block/editor.scss b/blocks/npc-block/editor.scss
new file mode 100644
index 0000000..318f617
--- /dev/null
+++ b/blocks/npc-block/editor.scss
@@ -0,0 +1,4 @@
+
+ .wp-block-npc-block {
+ border: 1px dotted #f00;
+}
diff --git a/blocks/npc-block/index.js b/blocks/npc-block/index.js
new file mode 100644
index 0000000..3539797
--- /dev/null
+++ b/blocks/npc-block/index.js
@@ -0,0 +1,12 @@
+
+import { registerBlockType } from "@wordpress/blocks";
+import Edit from './Edit';
+import Save from './Save';
+
+const blockConfig = require('./block.json');
+registerBlockType(blockConfig.name, {
+ ...blockConfig,
+ apiVersion: 2,
+ edit: Edit,
+ save: Save
+});
diff --git a/blocks/npc-block/init.php b/blocks/npc-block/init.php
new file mode 100644
index 0000000..93dd084
--- /dev/null
+++ b/blocks/npc-block/init.php
@@ -0,0 +1,10 @@
+ {
+ setAttributes( { skyUrl: null } );
+ setAttributes( { skyUrl: imageObject.url } );
+ };
+
+ const onChangeCollidable = ( zoomSetting ) => {
+ setAttributes( { hasZoom: zoomSetting } );
+ };
+
+
+ const { mediaUpload } = wp.editor;
+
+ const ALLOWED_MEDIA_TYPES = [
+ 'image'
+ ];
+
+ const MyDropZone = () => {
+ const [ hasDropped, setHasDropped ] = useState( false );
+ return (
+
+ { hasDropped ? 'Dropped!' : 'Drop an image here or' }
+
+ mediaUpload( {
+ allowedTypes: ALLOWED_MEDIA_TYPES,
+ filesList: files,
+ onFileChange: ( [ images ] ) => {
+ onImageSelect( images );
+ },
+ } )
+ }
+ />
+
+ );
+ };
+
+ function handleClick(objectURL){
+ if(objectURL){
+ console.log("success good job", objectURL);
+ onImageSelect(objectURL);
+ }
+ console.log("fail", objectURL);
+ }
+
+
+ return (
+
+
+
+
+
+
+ Select an image to be used as your skybox. 360 spherical panoramics recommended:
+
+
+
+
+ onImageSelect( imageObject )
+ }
+ type="image"
+ label="Sky File"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.skyUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+
+
+
+ { isSelected ? (
+ <>
+ { attributes.skyUrl ? (
+ <>
+
+ >) : (
+
+
+
+
+
+ Select an image to be used as your skybox. 360 spherical panoramics recommended:
+
+
+ onImageSelect( imageObject )
+ }
+ type="image"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.skyUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+
+ ) }
+ >
+ ) : (
+ <>
+ { attributes.skyUrl ? (
+ <>
+
+ >) : (
+
+
+
+
+ Select an image to be used as your skybox. 360 spherical panoramics recommended:
+
+ {/*
+ console.log(e.target.value) && setEnteredURL(e.target.value)}>
+
+
*/}
+
+
+ onImageSelect( imageObject )
+ }
+ type="image"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.skyUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+ ) }
+ >
+ ) }
+
+ );
+}
diff --git a/blocks/sky-block/Edit.test.js b/blocks/sky-block/Edit.test.js
new file mode 100644
index 0000000..ab10afe
--- /dev/null
+++ b/blocks/sky-block/Edit.test.js
@@ -0,0 +1,57 @@
+
+//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");
+ });
+});
\ No newline at end of file
diff --git a/blocks/sky-block/Save.js b/blocks/sky-block/Save.js
new file mode 100644
index 0000000..ee57703
--- /dev/null
+++ b/blocks/sky-block/Save.js
@@ -0,0 +1,16 @@
+import { __ } from '@wordpress/i18n';
+import { useBlockProps } from '@wordpress/block-editor';
+
+export default function save( { attributes } ) {
+ return (
+
+ <>
+
+
+ { attributes.skyUrl }
+
+
+ >
+
+ );
+}
diff --git a/blocks/sky-block/block.json b/blocks/sky-block/block.json
new file mode 100644
index 0000000..fa63499
--- /dev/null
+++ b/blocks/sky-block/block.json
@@ -0,0 +1,20 @@
+{
+ "name": "three-object-viewer/sky-block",
+ "title": "Sky Block",
+ "description": "A sky your environment",
+ "attributes": {
+ "skyUrl": {
+ "type": "string",
+ "default": null
+ }
+ },
+ "category": "3D",
+ "apiVersion": 2,
+ "supports": {
+ "html": false,
+ "multiple": false
+ },
+ "editorScript": "file:../../build/block-sky-block.js",
+ "editorStyle": "file:../../build/block-sky-block.css",
+ "style": "file:../../build/block-sky-block.css"
+}
diff --git a/blocks/sky-block/components/SkyEdit.js b/blocks/sky-block/components/SkyEdit.js
new file mode 100644
index 0000000..eb2cde8
--- /dev/null
+++ b/blocks/sky-block/components/SkyEdit.js
@@ -0,0 +1,66 @@
+import * as THREE from 'three';
+import React, { Suspense, useRef, useState, useEffect } from 'react';
+import { Canvas, useLoader, useFrame, useThree } from '@react-three/fiber';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
+import {
+ OrthographicCamera,
+ PerspectiveCamera,
+ OrbitControls,
+ useAnimations,
+} from '@react-three/drei';
+import { VRM, VRMUtils, VRMSchema, VRMLoaderPlugin } from '@pixiv/three-vrm'
+import { GLTFAudioEmitterExtension } from 'three-omi';
+
+ // Geometry
+function Sphere(props) {
+ const texture_1 = useLoader(THREE.TextureLoader, props.url);
+
+ return (
+
+
+
+
+ );
+}
+
+function ThreeObject( props ) {
+ const [ url, set ] = useState( props.src );
+ useEffect( () => {
+ setTimeout( () => set( props.src ), 2000 );
+ }, [] );
+
+ return Sphere(props);
+}
+
+export default function SkyEdit( props ) {
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/blocks/sky-block/editor.scss b/blocks/sky-block/editor.scss
new file mode 100644
index 0000000..a41c19e
--- /dev/null
+++ b/blocks/sky-block/editor.scss
@@ -0,0 +1,48 @@
+
+ .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;
+}
+
+.three-object-viewer-button {
+ background-color:rgb(199, 254, 0);
+ color: black;
+ border: solid 1.5px black;
+}
+
+.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/sky-block/index.js b/blocks/sky-block/index.js
new file mode 100644
index 0000000..22b14b6
--- /dev/null
+++ b/blocks/sky-block/index.js
@@ -0,0 +1,197 @@
+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: icon,
+ 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 }
+
+
+ >
+
+ );
+ },
+ },
+ ],
+} );
diff --git a/blocks/sky-block/init.php b/blocks/sky-block/init.php
new file mode 100644
index 0000000..4d16124
--- /dev/null
+++ b/blocks/sky-block/init.php
@@ -0,0 +1,10 @@
+ {
+ console.log(imageObject);
+ setAttributes( { videoUrl: null } );
+ setAttributes( { videoUrl: imageObject.url, aspectHeight: imageObject.height, aspectWidth: imageObject.width } );
+ };
+ const onChangePositionX = ( positionX ) => {
+ setAttributes( { positionX: positionX } );
+ };
+ const onChangePositionY = ( positionY ) => {
+ setAttributes( { positionY: positionY } );
+ };
+ const onChangePositionZ = ( positionZ ) => {
+ setAttributes( { positionZ: positionZ } );
+ };
+
+ const onChangeRotationX = ( rotationX ) => {
+ setAttributes( { rotationX: rotationX } );
+ };
+ const onChangeRotationY = ( rotationY ) => {
+ setAttributes( { rotationY: rotationY } );
+ };
+ const onChangeRotationZ = ( rotationZ ) => {
+ setAttributes( { rotationZ: rotationZ } );
+ };
+
+ const onChangeScaleX = ( scaleX ) => {
+ setAttributes( { scaleX: scaleX } );
+ };
+ const onChangeScaleY = ( scaleY ) => {
+ setAttributes( { scaleY: scaleY } );
+ };
+ const onChangeScaleZ = ( scaleZ ) => {
+ setAttributes( { scaleZ: scaleZ } );
+ };
+
+ const onChangeAutoPlay = ( autoPlaySetting ) => {
+ setAttributes( { autoPlay: autoPlaySetting } );
+ };
+ const onChangeLoop = ( loopSetting ) => {
+ setAttributes( { loop: loopSetting } );
+ };
+ const onChangePositional = ( positionalSetting ) => {
+ setAttributes( { positional: positionalSetting } );
+ };
+ const onChangeVolume = ( volumeSetting ) => {
+ setAttributes( { volume: volumeSetting } );
+ };
+ const onChangeConeInnerAngle = ( coneInnerAngleSetting ) => {
+ setAttributes( { coneInnerAngle: coneInnerAngleSetting } );
+ };
+ const onChangeConeOuterAngle = ( coneOuterAngleSetting ) => {
+ setAttributes( { coneOuterAngle: coneOuterAngleSetting } );
+ };
+ const onChangeConeOuterGain = ( coneOuterGainSetting ) => {
+ setAttributes( { coneOuterGain: coneOuterGainSetting } );
+ };
+ const onChangeDistanceModel = ( distanceModelSetting ) => {
+ setAttributes( { distanceModel: distanceModelSetting } );
+ };
+ const onChangeMaxDistance = ( maxDistanceSetting ) => {
+ setAttributes( { maxDistance: maxDistanceSetting } );
+ };
+ const onChangeRefDistance = ( refDistanceSetting ) => {
+ setAttributes( { refDistance: refDistanceSetting } );
+ };
+
+ const onChangeRolloffFactor = ( rolloffFactorSetting ) => {
+ setAttributes( { rolloffFactor: rolloffFactorSetting } );
+ };
+
+
+
+ const { mediaUpload } = wp.editor;
+
+ const ALLOWED_MEDIA_TYPES = [
+ 'audio'
+ ];
+
+ const MyDropZone = () => {
+ const [ hasDropped, setHasDropped ] = useState( false );
+ return (
+
+ { hasDropped ? 'Dropped!' : 'Drop an image here or' }
+
+ mediaUpload( {
+ allowedTypes: ALLOWED_MEDIA_TYPES,
+ filesList: files,
+ onFileChange: ( [ images ] ) => {
+ onImageSelect( images );
+ },
+ } )
+ }
+ />
+
+ );
+ };
+
+ function handleClick(objectURL){
+ if(objectURL){
+ console.log("success good job", objectURL);
+ onImageSelect(objectURL);
+ }
+ console.log("fail", objectURL);
+ }
+
+
+ return (
+
+
+
+
+
+
+ Select an image or object to attach to your audio. Leave blank for no mesh.
+
+
+
+
+ onImageSelect( imageObject )
+ }
+ type="audio"
+ label="Audio File"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.audioUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+
+
+
+
+
+ onChangePositionX( value )
+ }
+ />
+
+ onChangePositionY( value )
+ }
+ />
+
+ onChangePositionZ( value )
+ }
+ />
+
+
+
+
+
+
+ onChangeRotationX( value )
+ }
+ />
+
+ onChangeRotationY( value )
+ }
+ />
+
+ onChangeRotationZ( value )
+ }
+ />
+
+
+ {
+ onChangeAutoPlay( e );
+ } }
+ />
+
+
+ {
+ onChangeLoop( e );
+ } }
+ />
+
+
+ {
+ onChangePositional( e );
+ } }
+ />
+
+
+
+ onChangeVolume( value )
+ }
+ />
+
+
+ { attributes.positional && (
+
+
+
+ onChangeConeInnerAngle( value )
+ }
+ />
+
+
+
+ onChangeConeOuterAngle( value )
+ }
+ />
+
+
+
+ onChangeConeOuterGain( value )
+ }
+ />
+
+
+
+ onChangeDistanceModel( target )
+ }
+ />
+
+
+
+ onChangeMaxDistance( value )
+ }
+ />
+
+
+
+ onChangeRefDistance( value )
+ }
+ />
+
+
+
+ onChangeRolloffFactor( value )
+ }
+ />
+
+
+ )}
+
+
+ { isSelected ? (
+ <>
+ { attributes.videoUrl ? (
+ <>
+
+ >) : (
+
+
+
+
+
+ Select an image:
+
+
+ onImageSelect( imageObject )
+ }
+ type="video"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.videoUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+
+ ) }
+ >
+ ) : (
+ <>
+ { attributes.videoUrl ? (
+ <>
+
+ >) : (
+
+
+
+
+ Select an image to render in your environment:
+
+ {/*
+ console.log(e.target.value) && setEnteredURL(e.target.value)}>
+
+
*/}
+
+
+ onImageSelect( imageObject )
+ }
+ type="image"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.videoUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+ ) }
+ >
+ ) }
+
+ );
+}
diff --git a/blocks/three-audio-block/Edit.test.js b/blocks/three-audio-block/Edit.test.js
new file mode 100644
index 0000000..ab10afe
--- /dev/null
+++ b/blocks/three-audio-block/Edit.test.js
@@ -0,0 +1,57 @@
+
+//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");
+ });
+});
\ No newline at end of file
diff --git a/blocks/three-audio-block/Save.js b/blocks/three-audio-block/Save.js
new file mode 100644
index 0000000..2cfa1de
--- /dev/null
+++ b/blocks/three-audio-block/Save.js
@@ -0,0 +1,50 @@
+import { __ } from '@wordpress/i18n';
+import { useBlockProps } from '@wordpress/block-editor';
+
+export default function save( { attributes } ) {
+ return (
+
+ <>
+
+
+ { attributes.audioUrl }
+
+
+ { attributes.scaleX }
+
+
+ { attributes.scaleY }
+
+
+ { attributes.scaleZ }
+
+
+ { attributes.positionX }
+
+
+ { attributes.positionY }
+
+
+ { attributes.positionZ }
+
+
+ { attributes.rotationX }
+
+
+ { attributes.rotationY }
+
+
+ { attributes.rotationZ }
+
+
+ { attributes.aspectHeight }
+
+
+ { attributes.aspectWidth }
+
+
+
+ >
+
+ );
+}
diff --git a/blocks/three-audio-block/block.json b/blocks/three-audio-block/block.json
new file mode 100644
index 0000000..16cbfea
--- /dev/null
+++ b/blocks/three-audio-block/block.json
@@ -0,0 +1,108 @@
+{
+ "name": "three-object-viewer/three-audio-block",
+ "title": "Three Audio Block",
+ "description": "An audio block for your environment",
+ "attributes": {
+ "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":1
+ },
+ "coneOuterAngle": {
+ "type": "int",
+ "default":1
+ },
+ "coneOuterGain": {
+ "type": "int",
+ "default":1
+ },
+ "distanceModel": {
+ "type": "string",
+ "default": "inverse"
+ },
+ "maxDistance": {
+ "type": "int",
+ "default":1
+ },
+ "refDistance": {
+ "type": "int",
+ "default":1
+ },
+ "rolloffFactor": {
+ "type": "int",
+ "default":1
+ },
+ "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
+ }
+ },
+ "category": "3D",
+ "apiVersion": 2,
+ "supports": {
+ "html": false,
+ "multiple": true
+ },
+ "editorScript": "file:../../build/block-three-audio-block.js",
+ "editorStyle": "file:../../build/block-three-audio-block.css",
+ "style": "file:../../build/block-three-audio-block.css"
+}
diff --git a/blocks/three-audio-block/components/ImageEdit.js b/blocks/three-audio-block/components/ImageEdit.js
new file mode 100644
index 0000000..16e566c
--- /dev/null
+++ b/blocks/three-audio-block/components/ImageEdit.js
@@ -0,0 +1,75 @@
+import * as THREE from 'three';
+import React, { Suspense, useRef, useState, useEffect, useMemo } from 'react';
+import { Canvas, useLoader, useFrame, useThree } from '@react-three/fiber';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
+import {
+ OrthographicCamera,
+ PerspectiveCamera,
+ OrbitControls,
+ useAnimations,
+} from '@react-three/drei';
+import { VRM, VRMUtils, VRMSchema, VRMLoaderPlugin } from '@pixiv/three-vrm'
+import { GLTFAudioEmitterExtension } from 'three-omi';
+import { useAspect } from '@react-three/drei';
+
+// Geometry
+function Plane(props) {
+ console.log(props);
+ const clicked = true;
+ const [video] = useState(() => Object.assign(document.createElement('video'), { src: props.url, crossOrigin: 'Anonymous', loop: true, muted: true }));
+
+ useEffect(() => void (clicked && video.play()), [video, clicked]);
+
+ return (
+
+
+
+
+
+
+ );
+}
+
+function ThreeObject( props ) {
+ const [ url, set ] = useState( props.src );
+ useEffect( () => {
+ setTimeout( () => set( props.src ), 2000 );
+ }, [] );
+
+ return Plane(props);
+}
+
+export default function ImageEdit( props ) {
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/blocks/three-audio-block/editor.scss b/blocks/three-audio-block/editor.scss
new file mode 100644
index 0000000..a41c19e
--- /dev/null
+++ b/blocks/three-audio-block/editor.scss
@@ -0,0 +1,48 @@
+
+ .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;
+}
+
+.three-object-viewer-button {
+ background-color:rgb(199, 254, 0);
+ color: black;
+ border: solid 1.5px black;
+}
+
+.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-audio-block/index.js b/blocks/three-audio-block/index.js
new file mode 100644
index 0000000..22b14b6
--- /dev/null
+++ b/blocks/three-audio-block/index.js
@@ -0,0 +1,197 @@
+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: icon,
+ 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 }
+
+
+ >
+
+ );
+ },
+ },
+ ],
+} );
diff --git a/blocks/three-audio-block/init.php b/blocks/three-audio-block/init.php
new file mode 100644
index 0000000..4d16124
--- /dev/null
+++ b/blocks/three-audio-block/init.php
@@ -0,0 +1,10 @@
+ {
+ console.log(imageObject);
+ setAttributes( { imageUrl: null } );
+ setAttributes( { imageUrl: imageObject.url, aspectHeight: imageObject.height, aspectWidth: imageObject.width } );
+ };
+ const onChangePositionX = ( positionX ) => {
+ setAttributes( { positionX: positionX } );
+ };
+ const onChangePositionY = ( positionY ) => {
+ setAttributes( { positionY: positionY } );
+ };
+ const onChangePositionZ = ( positionZ ) => {
+ setAttributes( { positionZ: positionZ } );
+ };
+
+ const onChangeRotationX = ( rotationX ) => {
+ setAttributes( { rotationX: rotationX } );
+ };
+ const onChangeRotationY = ( rotationY ) => {
+ setAttributes( { rotationY: rotationY } );
+ };
+ const onChangeRotationZ = ( rotationZ ) => {
+ setAttributes( { rotationZ: rotationZ } );
+ };
+
+ const onChangeScaleX = ( scaleX ) => {
+ setAttributes( { scaleX: scaleX } );
+ };
+ const onChangeScaleY = ( scaleY ) => {
+ setAttributes( { scaleY: scaleY } );
+ };
+ const onChangeScaleZ = ( scaleZ ) => {
+ setAttributes( { scaleZ: scaleZ } );
+ };
+
+ const onChangeCollidable = ( zoomSetting ) => {
+ setAttributes( { hasZoom: zoomSetting } );
+ };
+
+
+ const { mediaUpload } = wp.editor;
+
+ const ALLOWED_MEDIA_TYPES = [
+ 'image'
+ ];
+
+ const MyDropZone = () => {
+ const [ hasDropped, setHasDropped ] = useState( false );
+ return (
+
+ { hasDropped ? 'Dropped!' : 'Drop an image here or' }
+
+ mediaUpload( {
+ allowedTypes: ALLOWED_MEDIA_TYPES,
+ filesList: files,
+ onFileChange: ( [ images ] ) => {
+ onImageSelect( images );
+ },
+ } )
+ }
+ />
+
+ );
+ };
+
+ function handleClick(objectURL){
+ if(objectURL){
+ console.log("success good job", objectURL);
+ onImageSelect(objectURL);
+ }
+ console.log("fail", objectURL);
+ }
+
+
+ return (
+
+
+
+
+
+
+ Select an image to render in your environment:
+
+
+
+
+ onImageSelect( imageObject )
+ }
+ type="image"
+ label="Image File"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.imageUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+
+
+ onChangePositionX( value )
+ }
+ />
+
+ onChangePositionY( value )
+ }
+ />
+
+ onChangePositionZ( value )
+ }
+ />
+
+
+
+
+
+
+ onChangeRotationX( value )
+ }
+ />
+
+ onChangeRotationY( value )
+ }
+ />
+
+ onChangeRotationZ( value )
+ }
+ />
+
+
+
+
+
+
+ onChangeScaleX( value )
+ }
+ />
+
+ onChangeScaleY( value )
+ }
+ />
+
+ onChangeScaleZ( value )
+ }
+ />
+
+
+
+
+ { isSelected ? (
+ <>
+ { attributes.imageUrl ? (
+ <>
+
+ >) : (
+
+
+
+
+
+ Select an image:
+
+
+ onImageSelect( imageObject )
+ }
+ type="image"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.imageUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+
+ ) }
+ >
+ ) : (
+ <>
+ { attributes.imageUrl ? (
+ <>
+
+ >) : (
+
+
+
+
+ Select an image to render in your environment:
+
+ {/*
+ console.log(e.target.value) && setEnteredURL(e.target.value)}>
+
+
*/}
+
+
+ onImageSelect( imageObject )
+ }
+ type="image"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.imageUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+ ) }
+ >
+ ) }
+
+ );
+}
diff --git a/blocks/three-image-block/Edit.test.js b/blocks/three-image-block/Edit.test.js
new file mode 100644
index 0000000..ab10afe
--- /dev/null
+++ b/blocks/three-image-block/Edit.test.js
@@ -0,0 +1,57 @@
+
+//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");
+ });
+});
\ No newline at end of file
diff --git a/blocks/three-image-block/Save.js b/blocks/three-image-block/Save.js
new file mode 100644
index 0000000..6c7a9ad
--- /dev/null
+++ b/blocks/three-image-block/Save.js
@@ -0,0 +1,50 @@
+import { __ } from '@wordpress/i18n';
+import { useBlockProps } from '@wordpress/block-editor';
+
+export default function save( { attributes } ) {
+ 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 }
+
+
+
+ >
+
+ );
+}
diff --git a/blocks/three-image-block/block.json b/blocks/three-image-block/block.json
new file mode 100644
index 0000000..b3e904f
--- /dev/null
+++ b/blocks/three-image-block/block.json
@@ -0,0 +1,64 @@
+{
+ "name": "three-object-viewer/three-image-block",
+ "title": "Three Image Block",
+ "description": "An image block for your environment",
+ "attributes": {
+ "imageUrl": {
+ "type": "string",
+ "default": null
+ },
+ "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
+ }
+ },
+ "category": "3D",
+ "apiVersion": 2,
+ "supports": {
+ "html": false,
+ "multiple": true
+ },
+ "editorScript": "file:../../build/block-three-image-block.js",
+ "editorStyle": "file:../../build/block-three-image-block.css",
+ "style": "file:../../build/block-three-image-block.css"
+}
diff --git a/blocks/three-image-block/components/ImageEdit.js b/blocks/three-image-block/components/ImageEdit.js
new file mode 100644
index 0000000..f1d475a
--- /dev/null
+++ b/blocks/three-image-block/components/ImageEdit.js
@@ -0,0 +1,69 @@
+import * as THREE from 'three';
+import React, { Suspense, useRef, useState, useEffect } from 'react';
+import { Canvas, useLoader, useFrame, useThree } from '@react-three/fiber';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
+import {
+ OrthographicCamera,
+ PerspectiveCamera,
+ OrbitControls,
+ useAnimations,
+} from '@react-three/drei';
+import { VRM, VRMUtils, VRMSchema, VRMLoaderPlugin } from '@pixiv/three-vrm'
+import { GLTFAudioEmitterExtension } from 'three-omi';
+import { useAspect } from '@react-three/drei'
+
+ // Geometry
+function Plane(props) {
+ const texture_1 = useLoader(THREE.TextureLoader, props.url);
+
+ return (
+
+
+
+
+ );
+}
+
+function ThreeObject( props ) {
+ const [ url, set ] = useState( props.src );
+ useEffect( () => {
+ setTimeout( () => set( props.src ), 2000 );
+ }, [] );
+
+ return Plane(props);
+}
+
+export default function ImageEdit( props ) {
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/blocks/three-image-block/editor.scss b/blocks/three-image-block/editor.scss
new file mode 100644
index 0000000..a41c19e
--- /dev/null
+++ b/blocks/three-image-block/editor.scss
@@ -0,0 +1,48 @@
+
+ .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;
+}
+
+.three-object-viewer-button {
+ background-color:rgb(199, 254, 0);
+ color: black;
+ border: solid 1.5px black;
+}
+
+.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-image-block/index.js b/blocks/three-image-block/index.js
new file mode 100644
index 0000000..22b14b6
--- /dev/null
+++ b/blocks/three-image-block/index.js
@@ -0,0 +1,197 @@
+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: icon,
+ 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 }
+
+
+ >
+
+ );
+ },
+ },
+ ],
+} );
diff --git a/blocks/three-image-block/init.php b/blocks/three-image-block/init.php
new file mode 100644
index 0000000..4d16124
--- /dev/null
+++ b/blocks/three-image-block/init.php
@@ -0,0 +1,10 @@
+ new GLTFAudioEmitterExtension( parser, listener )
);
loader.register( ( parser ) => {
+ return new VRMLoaderPlugin( parser );
+ } );
+ } );
+ const fallbackURL = threeObjectPlugin + defaultVRM;
+ const playerURL = props.playerData.vrm ? props.playerData.vrm : fallbackURL
+ const someSceneState = useLoader( GLTFLoader, playerURL, ( loader ) => {
+ loader.register(
+ ( parser ) => new GLTFAudioEmitterExtension( parser, listener )
+ );
+ loader.register( ( parser ) => {
return new VRMLoaderPlugin( parser );
} );
} );
@@ -52,6 +63,23 @@ function SavedObject( props ) {
} );
}
}, [] );
+ if(someSceneState?.userData?.gltfExtensions?.VRM){
+ const playerController = someSceneState.userData.vrm;
+ const { camera } = useThree();
+ useFrame(() => {
+ const offset = camera.position.z - 3;
+ playerController.scene.position.set( camera.position.x, camera.position.y, offset );
+ playerController.scene.rotation.set( camera.rotation.x, camera.rotation.y, camera.rotation.z );
+ });
+ VRMUtils.rotateVRM0( playerController );
+ const rotationVRM = playerController.scene.rotation.y;
+ playerController.scene.rotation.set( 0, rotationVRM, 0 );
+ playerController.scene.scale.set( 1, 1, 1 );
+ gltf.scene.position.set( 0, props.positionY, 0 );
+ gltf.scene.rotation.set( 0, props.rotationY, 0 );
+ gltf.scene.scale.set( props.scale, props.scale, props.scale );
+ return <>>;
+ }
if(gltf?.userData?.gltfExtensions?.VRM){
const vrm = gltf.userData.vrm;
vrm.scene.position.set( 0, props.positionY, 0 );
@@ -64,7 +92,7 @@ function SavedObject( props ) {
gltf.scene.position.set( 0, props.positionY, 0 );
gltf.scene.rotation.set( 0, props.rotationY, 0 );
gltf.scene.scale.set( props.scale, props.scale, props.scale );
- return ;
+ return <>>;
}
function Floor( props ) {
@@ -119,6 +147,7 @@ export default function ThreeObjectFront( props ) {
scale={ props.scale }
hasTip={ props.hasTip }
animations={ props.animations }
+ playerData={ props.userData }
/>
diff --git a/blocks/three-object-block/frontend.js b/blocks/three-object-block/frontend.js
index 96bec21..02a7a97 100644
--- a/blocks/three-object-block/frontend.js
+++ b/blocks/three-object-block/frontend.js
@@ -4,6 +4,7 @@ import ThreeObjectFront from './components/ThreeObjectFront';
const threeApp = document.querySelectorAll( '.three-object-three-app' );
+
threeApp.forEach( ( threeApp ) => {
if ( threeApp ) {
const threeUrl = threeApp.querySelector( 'p.three-object-block-url' )
@@ -54,6 +55,7 @@ threeApp.forEach( ( threeApp ) => {
rotationY={ rotationY }
animations={ animations }
backgroundColor={ backgroundColor }
+ userData={userData}
/>,
threeApp
);
diff --git a/blocks/three-video-block/Edit.js b/blocks/three-video-block/Edit.js
new file mode 100644
index 0000000..f2de848
--- /dev/null
+++ b/blocks/three-video-block/Edit.js
@@ -0,0 +1,328 @@
+import { __ } from '@wordpress/i18n';
+import React, { useState } from 'react';
+import { DropZone } from '@wordpress/components';
+import './editor.scss';
+import {
+ useBlockProps,
+ ColorPalette,
+ InspectorControls,
+ MediaUpload,
+} from '@wordpress/block-editor';
+import {
+ Panel,
+ PanelBody,
+ PanelRow,
+ RangeControl,
+ ToggleControl,
+ SelectControl,
+ TextControl,
+} from '@wordpress/components';
+import { more } from '@wordpress/icons';
+
+import VideoEdit from './components/VideoEdit';
+
+export default function Edit( { attributes, setAttributes, isSelected } ) {
+
+ const onImageSelect = ( imageObject ) => {
+ console.log(imageObject);
+ setAttributes( { audioUrl: null } );
+ setAttributes( { audioUrl: imageObject.url, aspectHeight: imageObject.height, aspectWidth: imageObject.width } );
+ };
+ const onChangePositionX = ( positionX ) => {
+ setAttributes( { positionX: positionX } );
+ };
+ const onChangePositionY = ( positionY ) => {
+ setAttributes( { positionY: positionY } );
+ };
+ const onChangePositionZ = ( positionZ ) => {
+ setAttributes( { positionZ: positionZ } );
+ };
+
+ const onChangeRotationX = ( rotationX ) => {
+ setAttributes( { rotationX: rotationX } );
+ };
+ const onChangeRotationY = ( rotationY ) => {
+ setAttributes( { rotationY: rotationY } );
+ };
+ const onChangeRotationZ = ( rotationZ ) => {
+ setAttributes( { rotationZ: rotationZ } );
+ };
+
+ const onChangeScaleX = ( scaleX ) => {
+ setAttributes( { scaleX: scaleX } );
+ };
+ const onChangeScaleY = ( scaleY ) => {
+ setAttributes( { scaleY: scaleY } );
+ };
+ const onChangeScaleZ = ( scaleZ ) => {
+ setAttributes( { scaleZ: scaleZ } );
+ };
+
+ const onChangeAutoPlay = ( autoPlaySetting ) => {
+ setAttributes( { autoPlay: autoPlaySetting } );
+ };
+
+
+ const { mediaUpload } = wp.editor;
+
+ const ALLOWED_MEDIA_TYPES = [
+ 'video'
+ ];
+
+ const MyDropZone = () => {
+ const [ hasDropped, setHasDropped ] = useState( false );
+ return (
+
+ { hasDropped ? 'Dropped!' : 'Drop an image here or' }
+
+ mediaUpload( {
+ allowedTypes: ALLOWED_MEDIA_TYPES,
+ filesList: files,
+ onFileChange: ( [ images ] ) => {
+ onImageSelect( images );
+ },
+ } )
+ }
+ />
+
+ );
+ };
+
+ function handleClick(objectURL){
+ if(objectURL){
+ console.log("success good job", objectURL);
+ onImageSelect(objectURL);
+ }
+ console.log("fail", objectURL);
+ }
+
+
+ return (
+
+
+
+
+
+
+ Select an image to render in your environment:
+
+
+
+
+ onImageSelect( imageObject )
+ }
+ type="image"
+ label="Image File"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.audioUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+
+ {
+ onChangeAutoPlay( e );
+ } }
+ />
+
+
+
+ onChangePositionX( value )
+ }
+ />
+
+ onChangePositionY( value )
+ }
+ />
+
+ onChangePositionZ( value )
+ }
+ />
+
+
+
+
+
+
+ onChangeRotationX( value )
+ }
+ />
+
+ onChangeRotationY( value )
+ }
+ />
+
+ onChangeRotationZ( value )
+ }
+ />
+
+
+
+
+
+
+ onChangeScaleX( value )
+ }
+ />
+
+ onChangeScaleY( value )
+ }
+ />
+
+ onChangeScaleZ( value )
+ }
+ />
+
+
+
+
+ { isSelected ? (
+ <>
+ { attributes.audioUrl ? (
+ <>
+
+ >) : (
+
+
+
+
+
+ Select an image:
+
+
+ onImageSelect( imageObject )
+ }
+ type="video"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.audioUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+
+ ) }
+ >
+ ) : (
+ <>
+ { attributes.audioUrl ? (
+ <>
+
+ >) : (
+
+
+
+
+ Select an image to render in your environment:
+
+ {/*
+ console.log(e.target.value) && setEnteredURL(e.target.value)}>
+
+
*/}
+
+
+ onImageSelect( imageObject )
+ }
+ type="image"
+ allowedTypes={ ALLOWED_MEDIA_TYPES }
+ value={ attributes.audioUrl }
+ render={ ( { open } ) => (
+
+ ) }
+ />
+
+ ) }
+ >
+ ) }
+
+ );
+}
diff --git a/blocks/three-video-block/Edit.test.js b/blocks/three-video-block/Edit.test.js
new file mode 100644
index 0000000..ab10afe
--- /dev/null
+++ b/blocks/three-video-block/Edit.test.js
@@ -0,0 +1,57 @@
+
+//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");
+ });
+});
\ No newline at end of file
diff --git a/blocks/three-video-block/Save.js b/blocks/three-video-block/Save.js
new file mode 100644
index 0000000..adbc7b7
--- /dev/null
+++ b/blocks/three-video-block/Save.js
@@ -0,0 +1,50 @@
+import { __ } from '@wordpress/i18n';
+import { useBlockProps } from '@wordpress/block-editor';
+
+export default function save( { attributes } ) {
+ return (
+
+ <>
+
+
+ { attributes.videoUrl }
+
+
+ { attributes.scaleX }
+
+
+ { attributes.scaleY }
+
+
+ { attributes.scaleZ }
+
+
+ { attributes.positionX }
+
+
+ { attributes.positionY }
+
+
+ { attributes.positionZ }
+
+
+ { attributes.rotationX }
+
+
+ { attributes.rotationY }
+
+
+ { attributes.rotationZ }
+
+
+ { attributes.aspectHeight }
+
+
+ { attributes.aspectWidth }
+
+
+
+ >
+
+ );
+}
diff --git a/blocks/three-video-block/block.json b/blocks/three-video-block/block.json
new file mode 100644
index 0000000..cde1c93
--- /dev/null
+++ b/blocks/three-video-block/block.json
@@ -0,0 +1,68 @@
+{
+ "name": "three-object-viewer/three-video-block",
+ "title": "Three Video Block",
+ "description": "A video block for your environment",
+ "attributes": {
+ "videoUrl": {
+ "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
+ }
+ },
+ "category": "3D",
+ "apiVersion": 2,
+ "supports": {
+ "html": false,
+ "multiple": true
+ },
+ "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/components/VideoEdit.js b/blocks/three-video-block/components/VideoEdit.js
new file mode 100644
index 0000000..7e3cfad
--- /dev/null
+++ b/blocks/three-video-block/components/VideoEdit.js
@@ -0,0 +1,75 @@
+import * as THREE from 'three';
+import React, { Suspense, useRef, useState, useEffect, useMemo } from 'react';
+import { Canvas, useLoader, useFrame, useThree } from '@react-three/fiber';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
+import {
+ OrthographicCamera,
+ PerspectiveCamera,
+ OrbitControls,
+ useAnimations,
+} from '@react-three/drei';
+import { VRM, VRMUtils, VRMSchema, VRMLoaderPlugin } from '@pixiv/three-vrm'
+import { GLTFAudioEmitterExtension } from 'three-omi';
+import { useAspect } from '@react-three/drei';
+
+// Geometry
+function Plane(props) {
+ console.log(props);
+ const clicked = true;
+ const [video] = useState(() => Object.assign(document.createElement('video'), { src: props.url, crossOrigin: 'Anonymous', loop: true, muted: true }));
+
+ useEffect(() => void (clicked && video.play()), [video, clicked]);
+
+ return (
+
+
+
+
+
+
+ );
+}
+
+function ThreeObject( props ) {
+ const [ url, set ] = useState( props.src );
+ useEffect( () => {
+ setTimeout( () => set( props.src ), 2000 );
+ }, [] );
+
+ return Plane(props);
+}
+
+export default function VideoEdit( props ) {
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/blocks/three-video-block/editor.scss b/blocks/three-video-block/editor.scss
new file mode 100644
index 0000000..a41c19e
--- /dev/null
+++ b/blocks/three-video-block/editor.scss
@@ -0,0 +1,48 @@
+
+ .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;
+}
+
+.three-object-viewer-button {
+ background-color:rgb(199, 254, 0);
+ color: black;
+ border: solid 1.5px black;
+}
+
+.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-video-block/index.js b/blocks/three-video-block/index.js
new file mode 100644
index 0000000..22b14b6
--- /dev/null
+++ b/blocks/three-video-block/index.js
@@ -0,0 +1,197 @@
+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: icon,
+ 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 }
+
+
+ >
+
+ );
+ },
+ },
+ ],
+} );
diff --git a/blocks/three-video-block/init.php b/blocks/three-video-block/init.php
new file mode 100644
index 0000000..4d16124
--- /dev/null
+++ b/blocks/three-video-block/init.php
@@ -0,0 +1,10 @@
+avatar);
+ $user_data_passed = array(
+ 'userId' => $current_user->user_login,
+ 'inWorldName' => $current_user->in_world_name,
+ 'banner' => $current_user->custom_banner,
+ 'vrm' => $vrm,
+ );
+
+ $three_object_plugin = plugins_url() . '/three-object-viewer/build/';
+
+ // $user_data_passed = array(
+ // 'userId' => 'something',
+ // 'userName' => 'someone',
+ // 'vrm' => 'somefile.vrm',
+ // );
+
+ wp_register_script( 'threeobjectloader-frontend', plugin_dir_url( __FILE__ ) . '../build/assets/js/blocks.frontend.js', ['wp-element', 'wp-data', 'wp-hooks'], '', true );
+ wp_localize_script( 'threeobjectloader-frontend', 'userData', $user_data_passed );
+ wp_localize_script( 'threeobjectloader-frontend', 'threeObjectPlugin', $three_object_plugin );
+
wp_enqueue_script(
- "threeobjectloader-frontend",
- plugin_dir_url( __FILE__ ) . '../build/assets/js/blocks.frontend.js',
- ['wp-element'],
- '',
- true
+ "threeobjectloader-frontend"
);
+
+ wp_register_script( 'versepress-frontend', plugin_dir_url( __FILE__ ) . '../build/assets/js/blocks.frontend-versepress.js', ['wp-element', 'wp-data', 'wp-hooks'], '', true );
+ wp_localize_script( 'versepress-frontend', 'userData', $user_data_passed );
+ wp_localize_script( 'versepress-frontend', 'threeObjectPlugin', $three_object_plugin );
+
+ wp_enqueue_script(
+ "versepress-frontend"
+ );
+
}
diff --git a/pluginMachine.json b/pluginMachine.json
index 8cc14cb..dfcca36 100644
--- a/pluginMachine.json
+++ b/pluginMachine.json
@@ -4,10 +4,16 @@
"buildId": 172,
"entryPoints": {
"adminPages": [
- "three-model-viewer-settings"
+ "three-object-viewer-settings"
],
"blocks": [
- "three-object-block"
+ "three-object-block",
+ "environment",
+ "model-block",
+ "sky-block",
+ "three-image-block",
+ "three-video-block",
+ "three-audio-block"
]
},
"buildIncludes": [
@@ -17,6 +23,11 @@
"vendor",
"build",
"blocks/three-object-block/init.php",
- "admin/three-model-viewer-settings/init.php"
+ "blocks/environment/init.php",
+ "blocks/model-block/init.php",
+ "blocks/sky-block/init.php",
+ "blocks/three-image-block/init.php",
+ "blocks/three-video-block/init.php",
+ "admin/three-object-viewer-settings/init.php"
]
}
\ No newline at end of file
diff --git a/three-object-viewer.php b/three-object-viewer.php
index e9f7d4e..e296379 100644
--- a/three-object-viewer.php
+++ b/three-object-viewer.php
@@ -17,6 +17,24 @@
// Include three-object-block
include_once dirname( __FILE__ ) . '/blocks/three-object-block/init.php';
+// Include environment
+include_once dirname( __FILE__ ) . '/blocks/environment/init.php';
+
+// Include model
+include_once dirname( __FILE__ ) . '/blocks/model-block/init.php';
+
+// Include sky
+include_once dirname( __FILE__ ) . '/blocks/sky-block/init.php';
+
+// Include image
+include_once dirname( __FILE__ ) . '/blocks/three-image-block/init.php';
+
+// Include image
+include_once dirname( __FILE__ ) . '/blocks/three-video-block/init.php';
+
+// Include audio
+include_once dirname( __FILE__ ) . '/blocks/three-audio-block/init.php';
+
/**
* Include the autoloader
*/
@@ -28,4 +46,4 @@
include_once dirname( __FILE__ ). '/inc/functions.php';
include_once dirname( __FILE__ ). '/inc/hooks.php';
-include_once dirname( __FILE__ ) . '/admin/three-model-viewer-settings/init.php';
+include_once dirname( __FILE__ ) . '/admin/three-object-viewer-settings/init.php';
diff --git a/webpack.config.js b/webpack.config.js
index 18e259d..b830e14 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -25,6 +25,9 @@ if ( entryPoints.hasOwnProperty( 'adminPages' ) ) {
entry[ `./assets/js/blocks.frontend` ] =
'./blocks/three-object-block/frontend.js';
+entry[ `./assets/js/blocks.frontend-versepress` ] =
+'./blocks/environment/frontend.js';
+
module.exports = {
mode: isProduction ? 'production' : 'development',
...defaultConfig,
@@ -36,7 +39,15 @@ module.exports = {
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ],
},
- ],
+ {
+ test: /\.vrm$/,
+ use: [
+ {
+ loader: 'file-loader',
+ },
+ ],
+ },
+ ],
},
entry,
output: {