├── plugin-build ├── .gitkeep └── .gitignore ├── inc ├── threeobjectloaderinit │ ├── index.css │ └── index.js ├── hooks.php ├── functions.php ├── avatars │ ├── Idle.fbx │ ├── Running.fbx │ ├── talking.fbx │ ├── walking.fbx │ ├── friendly.fbx │ └── 3ov_default_avatar.vrm ├── fonts │ └── roboto.woff └── assets │ ├── audio_icon.png │ ├── light_icon.png │ └── default_grid.glb ├── .eslintrc ├── .prettierrc ├── assets └── wporg │ ├── banner-772x250.jpg │ ├── icon-128x128.png │ ├── icon-256x256.png │ └── banner-1544x500.jpg ├── languages ├── three-object-viewer.mo ├── three-object-viewer-ja.mo ├── three-object-viewer-es_MX.mo ├── three-object-viewer-es_MX-three-object-viewer-spawn-point-block-editor-script.json ├── three-object-viewer-ja-three-object-viewer-spawn-point-block-editor-script.json ├── three-object-viewer-es_MX-three-object-viewer-three-text-block-editor-script.json ├── three-object-viewer-es_MX-three-object-viewer-three-light-block-editor-script.json ├── three-object-viewer-ja-three-object-viewer-three-text-block-editor-script.json ├── three-object-viewer-es_MX-three-object-viewer-three-image-block-editor-script.json ├── three-object-viewer-es_MX-three-object-viewer-sky-block-editor-script.json ├── three-object-viewer-ja-three-object-viewer-three-light-block-editor-script.json ├── three-object-viewer-ja-three-object-viewer-three-image-block-editor-script.json ├── three-object-viewer-ja-three-object-viewer-sky-block-editor-script.json ├── three-object-viewer-es_MX-three-object-viewer-three-video-block-editor-script.json ├── three-object-viewer-es_MX-three-object-viewer-npc-block-editor-script.json ├── three-object-viewer-es_MX-three-object-viewer-environment-editor-script.json ├── three-object-viewer-es_MX-three-object-viewer-model-block-editor-script.json ├── three-object-viewer-es_MX-three-object-viewer-settings.json ├── three-object-viewer-es_MX-three-object-viewer-three-portal-block-editor-script.json ├── three-object-viewer-ja-three-object-viewer-three-video-block-editor-script.json ├── three-object-viewer-ja-three-object-viewer-npc-block-editor-script.json ├── three-object-viewer-ja-three-object-viewer-environment-editor-script.json ├── three-object-viewer-ja-three-object-viewer-settings.json ├── three-object-viewer-ja-three-object-viewer-model-block-editor-script.json ├── three-object-viewer-ja-three-object-viewer-three-portal-block-editor-script.json ├── three-object-viewer-es_MX-three-object-viewer-three-audio-block-editor-script.json └── three-object-viewer.po ├── babel.config.js ├── tests ├── Unit │ ├── TestCase.php │ └── EnvironmentTest.php ├── Integration │ └── EnvironmentTest.php └── bootstrap.php ├── .vscode └── settings.json ├── blocks ├── three-object-block │ ├── init.php │ ├── style.scss │ ├── editor.scss │ ├── Save.js │ ├── block.json │ ├── frontend.js │ └── components │ │ ├── TeleportTravel.js │ │ └── ThreeObjectEdit.js ├── sky-block │ ├── init.php │ ├── Save.js │ ├── editor.scss │ ├── style.scss │ ├── block.json │ ├── index.js │ └── Edit.test.js ├── model-block │ ├── init.php │ ├── style.scss │ ├── editor.scss │ ├── Save.js │ ├── Edit.test.js │ ├── block.json │ ├── components │ │ └── ModelEdit.js │ └── index.js ├── three-text-block │ ├── init.php │ ├── style.scss │ ├── editor.scss │ ├── Save.js │ ├── Edit.test.js │ └── block.json ├── npc-block │ ├── init.php │ ├── style.scss │ ├── editor.scss │ ├── Save.js │ ├── block.json │ ├── Edit.test.js │ └── components │ │ └── ModelEdit.js ├── three-portal-block │ ├── init.php │ ├── style.scss │ ├── editor.scss │ ├── Edit.test.js │ ├── Save.js │ └── block.json ├── spawn-point-block │ ├── init.php │ ├── editor.scss │ ├── Save.js │ ├── style.scss │ ├── block.json │ └── Edit.test.js ├── three-audio-block │ ├── init.php │ ├── editor.scss │ ├── style.scss │ ├── Edit.test.js │ ├── Save.js │ └── block.json ├── three-light-block │ ├── init.php │ ├── index.js │ ├── editor.scss │ ├── style.scss │ ├── Edit.test.js │ ├── Save.js │ └── block.json ├── three-image-block │ ├── init.php │ ├── editor.scss │ ├── style.scss │ ├── Save.js │ ├── Edit.test.js │ └── block.json ├── three-video-block │ ├── init.php │ ├── editor.scss │ ├── style.scss │ ├── Edit.test.js │ ├── Save.js │ ├── block.json │ └── index.js └── environment │ ├── init.php │ ├── style.scss │ ├── components │ ├── core │ │ └── front │ │ │ ├── TextObject.js │ │ │ ├── ThreeImage.js │ │ │ ├── ThreeSky.js │ │ │ ├── ThreeLight.js │ │ │ └── ThreeAudio.js │ ├── EditorPluginProvider.js │ ├── FrontPluginProvider.js │ ├── ContextBridgeComponent.js │ ├── Controls.js │ ├── EditControls.js │ └── Networking.js │ ├── Save.js │ ├── Edit.test.js │ └── block.json ├── admin └── three-object-viewer-settings │ ├── App.test.js.test │ └── index.js ├── phpunit-unit.xml ├── phpunit-integration.xml ├── .github └── workflows │ ├── test-js.yml │ ├── php-unit.yml │ └── wordpress.yml ├── .gitignore ├── .svnignore ├── phpcs.xml.dist ├── composer.json ├── package.js ├── pluginMachine.json ├── docker-compose.yml ├── rename.js ├── README.md ├── package.json └── webpack.config.js /plugin-build/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /inc/threeobjectloaderinit/index.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plugin-build/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | !.gitkeep 3 | -------------------------------------------------------------------------------- /inc/hooks.php: -------------------------------------------------------------------------------- 1 | { 5 | const div = document.createElement("div"); 6 | ReactDOM.render(, div); 7 | ReactDOM.unmountComponentAtNode(div); 8 | }); 9 | -------------------------------------------------------------------------------- /phpunit-unit.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | ./tests/Unit 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /phpunit-integration.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | ./tests/Integration 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /languages/three-object-viewer-es_MX-three-object-viewer-spawn-point-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:28-0500","generator":"WP-CLI\/2.8.1","source":"blocks\/spawn-point-block\/Edit.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"es_MX","plural-forms":"nplurals=2; plural=(n != 1);"},"Settings":["Ajustes"],"Rotation":["Rotaci\u00f3n"],"Spawn Point":["Punto de Aparici\u00f3n"]}}} -------------------------------------------------------------------------------- /languages/three-object-viewer-ja-three-object-viewer-spawn-point-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:23-0500","generator":"WP-CLI\/2.8.1","source":"blocks\/spawn-point-block\/Edit.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"ja","plural-forms":"nplurals=2; plural=(n != 1);"},"Settings":["\u8a2d\u5b9a"],"Rotation":["\u56de\u8ee2"],"Spawn Point":["\u30b9\u30dd\u30fc\u30f3\u30dd\u30a4\u30f3\u30c8"]}}} -------------------------------------------------------------------------------- /.github/workflows/test-js.yml: -------------------------------------------------------------------------------- 1 | name: JavaScripts 2 | 3 | on: [push] 4 | 5 | jobs: 6 | buildAndTest: 7 | name: Test 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v1 11 | - uses: actions/setup-node@v1 12 | with: 13 | node-version: '12' 14 | - name: Install dependencies 15 | run: yarn 16 | - name: Test 17 | run: yarn test --ci 18 | -------------------------------------------------------------------------------- /blocks/sky-block/init.php: -------------------------------------------------------------------------------- 1 | _x( 'Sky Block', 'block title', 'three-object-viewer' ), 10 | 'description' => _x( 'A sky your environment', 'block description', 'three-object-viewer' ), 11 | ] ); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /blocks/model-block/init.php: -------------------------------------------------------------------------------- 1 | _x( 'Model Block', 'block title', 'three-object-viewer' ), 10 | 'description' => _x( 'A 3D model for your environment', 'block description', 'three-object-viewer' ), 11 | ] ); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /blocks/three-text-block/init.php: -------------------------------------------------------------------------------- 1 | _x( 'Text Block', 'block title', 'three-object-viewer' ), 10 | 'description' => _x( 'A 3D Text Block', 'block description', 'three-object-viewer' ), 11 | ] ); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /blocks/npc-block/init.php: -------------------------------------------------------------------------------- 1 | _x( 'NPC Block', 'block title', 'three-object-viewer' ), 10 | 'description' => _x( 'A NPC to live in your environment', 'block description', 'three-object-viewer' ), 11 | ] ); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /blocks/three-portal-block/init.php: -------------------------------------------------------------------------------- 1 | _x( 'Portal Block', 'block title', 'three-object-viewer' ), 10 | 'description' => _x( 'A portal for traversal', 'block description', 'three-object-viewer' ), 11 | ] ); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /blocks/spawn-point-block/init.php: -------------------------------------------------------------------------------- 1 | _x( 'Spawn Point Block', 'block title', 'three-object-viewer' ), 10 | 'description' => _x( 'A spawn point for your users', 'block description', 'three-object-viewer' ), 11 | ] ); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /blocks/three-audio-block/init.php: -------------------------------------------------------------------------------- 1 | _x( 'Audio Block', 'block title', 'three-object-viewer' ), 10 | 'description' => _x( 'An audio block for your environment', 'block description', 'three-object-viewer' ), 11 | ] ); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /blocks/three-light-block/init.php: -------------------------------------------------------------------------------- 1 | _x( 'Light Block', 'block title', 'three-object-viewer' ), 10 | 'description' => _x( 'A light block for your environment', 'block description', 'three-object-viewer' ), 11 | ] ); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /blocks/three-image-block/init.php: -------------------------------------------------------------------------------- 1 | _x( '3D Image Block', 'block title', 'three-object-viewer' ), 10 | 'description' => _x( 'An image block for your environment', 'block description', 'three-object-viewer' ), 11 | ] ); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /blocks/three-video-block/init.php: -------------------------------------------------------------------------------- 1 | _x( '3D Video Block', 'block title', 'three-object-viewer' ), 10 | 'description' => _x( 'A video block for your environment', 'block description', 'three-object-viewer' ), 11 | ] ); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /blocks/environment/init.php: -------------------------------------------------------------------------------- 1 | _x( 'Environment Block', 'block title', 'three-object-viewer' ), 10 | 'description' => _x( 'A 3D environment component', 'block description', 'three-object-viewer' ), 11 | ] ); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /languages/three-object-viewer-es_MX-three-object-viewer-three-text-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:28-0500","generator":"WP-CLI\/2.8.1","source":"blocks\/three-text-block\/Edit.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"es_MX","plural-forms":"nplurals=2; plural=(n != 1);"},"Scale":["Escala"],"Position":["Posici\u00f3n"],"Rotation":["Rotaci\u00f3n"],"Text Color":["Color de Texto"],"Text Attributes":["Atributos de Texto"],"Text":["Texto"],"Write here.":["Escribe aqu\u00ed."],"Text Block":["Bloque de Texto"]}}} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Logs 3 | logs 4 | *.log 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .DS_Store 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 13 | 14 | # Dependency directories 15 | node_modules/ 16 | vendor/ 17 | 18 | # dotenv environment variables file 19 | .env 20 | .env.test 21 | 22 | vendor 23 | wordpress 24 | build 25 | pro/ 26 | .phpunit.result.cache 27 | plugin-build/pro/three-object-viewer/* 28 | !plugin-build/pro/three-object-viewer/.gitkeep 29 | plugin-build/free/three-object-viewer/* 30 | !plugin-build/free/three-object-viewer/.gitkeep 31 | -------------------------------------------------------------------------------- /languages/three-object-viewer-es_MX-three-object-viewer-three-light-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:28-0500","generator":"WP-CLI\/2.8.1","source":"build\/block-three-light-block.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"es_MX","plural-forms":"nplurals=2; plural=(n != 1);"},"Settings":["Ajustes"],"Position":["Posici\u00f3n"],"Rotation":["Rotaci\u00f3n"],"Light Object":["Objeto de Luz"],"Light Type":["Tipo de Luz"],"Point":["Punto"],"Ambient":["Ambiental"],"Directional":["Directional"],"Intensity":["Intensidad"],"Distance":["Distancia"],"Decay":["Decaimiento"],"Angle":["\u00c1ngulo"],"Penumbra":["Penumbra"],"Light Block":["Bloque de Luz"]}}} -------------------------------------------------------------------------------- /languages/three-object-viewer-ja-three-object-viewer-three-text-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:23-0500","generator":"WP-CLI\/2.8.1","source":"blocks\/three-text-block\/Edit.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"ja","plural-forms":"nplurals=2; plural=(n != 1);"},"Scale":["\u30b9\u30b1\u30fc\u30eb"],"Position":["\u4f4d\u7f6e"],"Rotation":["\u56de\u8ee2"],"Text Color":["\u30c6\u30ad\u30b9\u30c8\u306e\u8272"],"Text Attributes":["\u30c6\u30ad\u30b9\u30c8\u5c5e\u6027"],"Text":["\u30c6\u30ad\u30b9\u30c8"],"Write here.":["\u3053\u3053\u306b\u66f8\u3044\u3066\u304f\u3060\u3055\u3044\u3002"],"Text Block":["\u30c6\u30ad\u30b9\u30c8\u30d6\u30ed\u30c3\u30af"]}}} -------------------------------------------------------------------------------- /blocks/sky-block/Save.js: -------------------------------------------------------------------------------- 1 | import { __ } from "@wordpress/i18n"; 2 | import { useBlockProps } from "@wordpress/block-editor"; 3 | 4 | export default function save({ attributes }) { 5 | return ( 6 |
7 | <> 8 |
9 |

{attributes.skyUrl}

10 |

{attributes.distance}

11 |

{attributes.rayleigh}

12 |

{attributes.sunPositionX}

13 |

{attributes.sunPositionY}

14 |

{attributes.sunPositionZ}

15 |
16 | 17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /languages/three-object-viewer-es_MX-three-object-viewer-three-image-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:28-0500","generator":"WP-CLI\/2.8.1","source":"build\/block-three-image-block.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"es_MX","plural-forms":"nplurals=2; plural=(n != 1);"},"Replace Image":["Reemplazar Imagen"],"Select Image":["Seleccionar Imagen"],"Scale":["Escala"],"Rotation":["Rotaci\u00f3n"],"Image Object":["Objeto de Imagen"],"Select an image to render in your environment:":["Selecciona una imagen para renderizar en tu entorno:"],"Image File":["Archivo de Imagen"],"Item is transparent.":["El elemento es transparente."],"Item is not transparent.":["El elemento no es transparente."],"Image block":["Bloque de Imagen"]}}} -------------------------------------------------------------------------------- /tests/Integration/EnvironmentTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(is_object($wpdb)); 25 | $id = wp_insert_post([ 26 | 'post_type' => 'post', 27 | 'post_title' => 'roy', 28 | 'post_content' => 'sivan' 29 | ]); 30 | $this->assertTrue(is_numeric($id)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /languages/three-object-viewer-es_MX-three-object-viewer-sky-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:28-0500","generator":"WP-CLI\/2.8.1","source":"build\/block-sky-block.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"es_MX","plural-forms":"nplurals=2; plural=(n != 1);"},"Settings":["Ajustes"],"Sky Object":["Objeto de Cielo"],"Select an image to be used as your skybox. 360 panoramics recommended:":["Selecciona una imagen para usar como tu skybox. Se recomiendan panor\u00e1micas de 360 grados:"],"Sky File":["Archivo de Cielo"],"Replace Sky":["Reemplazar Cielo"],"Select Sky":["Seleccionar Cielo"],"Remove Image":["Eliminar Imagen"],"distance":["distancia"],"rayleigh":["rayleigh"],"Sun Position":["Posici\u00f3n del Sol"],"Sky block":["Bloque de Cielo"]}}} -------------------------------------------------------------------------------- /.svnignore: -------------------------------------------------------------------------------- 1 | .git 2 | .github 3 | .gitignore 4 | .gitattributes 5 | .DS_Store 6 | .babelrc 7 | .cache 8 | .codeclimate* 9 | .idea 10 | .parcel-cache 11 | .eslintignore 12 | .eslintrc.json 13 | .eslintrc.js 14 | .circleci 15 | .sass-cache 16 | .editorconfig 17 | .husky 18 | .env.testing* 19 | .prettier* 20 | .husky 21 | .phpcs.xml.dist 22 | .svnignore 23 | .zipignore 24 | .docker 25 | docs 26 | node_modules 27 | scripts 28 | tests 29 | *phpunit* 30 | *bin* 31 | *config* 32 | *tests* 33 | *composer.json* 34 | *composer.lock* 35 | webpack.config.js 36 | pluginMachine.json 37 | babel.config.json 38 | phpunit-unit.xml 39 | phpunit-integration.xml 40 | phpcs.xml.dist 41 | yarn.lock 42 | yarn-error.log 43 | jest.config.js 44 | package.json 45 | package-lock.json 46 | docker-compose.yml 47 | docker-composer-phpunit.yml 48 | Makefile 49 | *vendor* -------------------------------------------------------------------------------- /blocks/three-light-block/index.js: -------------------------------------------------------------------------------- 1 | import { registerBlockType } from "@wordpress/blocks"; 2 | import Edit from "./Edit"; 3 | import Save from "./Save"; 4 | import { useBlockProps } from "@wordpress/block-editor"; 5 | 6 | const icon = ( 7 | 13 | 14 | 15 | 16 | 17 | ); 18 | 19 | const blockConfig = require("./block.json"); 20 | registerBlockType(blockConfig.name, { 21 | ...blockConfig, 22 | icon, 23 | apiVersion: 2, 24 | edit: Edit, 25 | save: Save 26 | }); 27 | -------------------------------------------------------------------------------- /languages/three-object-viewer-ja-three-object-viewer-three-light-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:23-0500","generator":"WP-CLI\/2.8.1","source":"build\/block-three-light-block.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"ja","plural-forms":"nplurals=2; plural=(n != 1);"},"Settings":["\u8a2d\u5b9a"],"Position":["\u4f4d\u7f6e"],"Rotation":["\u56de\u8ee2"],"Light Object":["\u30e9\u30a4\u30c8\u30aa\u30d6\u30b8\u30a7\u30af\u30c8"],"Light Type":["\u30e9\u30a4\u30c8\u306e\u7a2e\u985e"],"Point":["\u30dd\u30a4\u30f3\u30c8"],"Ambient":["\u30a2\u30f3\u30d3\u30a8\u30f3\u30c8"],"Directional":["\u30c7\u30a3\u30ec\u30af\u30b7\u30e7\u30ca\u30eb"],"Intensity":["\u5f37\u5ea6"],"Distance":["\u8ddd\u96e2"],"Decay":["\u6e1b\u8870"],"Angle":["\u89d2\u5ea6"],"Penumbra":["\u30da\u30cc\u30f3\u30d6\u30e9"],"Light Block":["\u30e9\u30a4\u30c8\u30d6\u30ed\u30c3\u30af"]}}} -------------------------------------------------------------------------------- /blocks/spawn-point-block/editor.scss: -------------------------------------------------------------------------------- 1 | 2 | .wp-block-three-object-block { 3 | border: 1px dotted #f00; 4 | } 5 | .glb-preview-container { 6 | padding: 100px; 7 | text-align: center; 8 | align-items: center; 9 | align-content: center; 10 | background-color:#f2f2f2; 11 | } 12 | 13 | .glb-preview-container button{ 14 | padding: 15px; 15 | border-radius: 30px; 16 | } 17 | 18 | .three-object-viewer-button { 19 | color: black; 20 | border: solid 1.5px black; 21 | } 22 | 23 | .three-object-block-tip { 24 | overflow-wrap: break-word; 25 | background-color: black; 26 | color: white; 27 | padding: 10px; 28 | font-weight: 500; 29 | max-width: 160px; 30 | font-size: 12px; 31 | margin-top: 0px; 32 | margin: 0 auto; 33 | text-align: center; 34 | } 35 | 36 | .three-object-block-url-input { 37 | padding-bottom: 20px; 38 | } 39 | 40 | .three-object-block-url-input input{ 41 | height: 40px; 42 | } 43 | -------------------------------------------------------------------------------- /blocks/three-image-block/editor.scss: -------------------------------------------------------------------------------- 1 | 2 | .wp-block-three-object-block { 3 | border: 1px dotted #f00; 4 | } 5 | .glb-preview-container { 6 | padding: 100px; 7 | text-align: center; 8 | align-items: center; 9 | align-content: center; 10 | background-color:#f2f2f2; 11 | } 12 | 13 | .glb-preview-container button{ 14 | padding: 15px; 15 | border-radius: 30px; 16 | } 17 | 18 | .three-object-viewer-button { 19 | color: black; 20 | border: solid 1.5px black; 21 | } 22 | 23 | .three-object-block-tip { 24 | overflow-wrap: break-word; 25 | background-color: black; 26 | color: white; 27 | padding: 10px; 28 | font-weight: 500; 29 | max-width: 160px; 30 | font-size: 12px; 31 | margin-top: 0px; 32 | margin: 0 auto; 33 | text-align: center; 34 | } 35 | 36 | .three-object-block-url-input { 37 | padding-bottom: 20px; 38 | } 39 | 40 | .three-object-block-url-input input{ 41 | height: 40px; 42 | } 43 | -------------------------------------------------------------------------------- /blocks/sky-block/editor.scss: -------------------------------------------------------------------------------- 1 | 2 | .wp-block-three-object-block { 3 | border: 1px dotted #f00; 4 | } 5 | .glb-preview-container { 6 | padding: 100px; 7 | text-align: center; 8 | align-items: center; 9 | align-content: center; 10 | background-color:#f2f2f2; 11 | } 12 | 13 | .glb-preview-container button{ 14 | padding: 15px; 15 | border-radius: 30px; 16 | } 17 | 18 | .glb-preview-container button:hover{ 19 | border-radius: 30px; 20 | background-color:rgb(156, 199, 0); 21 | cursor: pointer; 22 | } 23 | .three-object-block-tip { 24 | overflow-wrap: break-word; 25 | background-color: black; 26 | color: white; 27 | padding: 10px; 28 | font-weight: 500; 29 | max-width: 160px; 30 | font-size: 12px; 31 | margin-top: 0px; 32 | margin: 0 auto; 33 | text-align: center; 34 | } 35 | 36 | .three-object-block-url-input { 37 | padding-bottom: 20px; 38 | } 39 | 40 | .three-object-block-url-input input{ 41 | height: 40px; 42 | } 43 | -------------------------------------------------------------------------------- /blocks/three-audio-block/editor.scss: -------------------------------------------------------------------------------- 1 | 2 | .wp-block-three-object-block { 3 | border: 1px dotted #f00; 4 | } 5 | .glb-preview-container { 6 | padding: 100px; 7 | text-align: center; 8 | align-items: center; 9 | align-content: center; 10 | background-color:#f2f2f2; 11 | } 12 | 13 | .glb-preview-container button{ 14 | padding: 15px; 15 | border-radius: 30px; 16 | } 17 | 18 | .glb-preview-container button:hover{ 19 | border-radius: 30px; 20 | background-color:rgb(156, 199, 0); 21 | cursor: pointer; 22 | } 23 | .three-object-block-tip { 24 | overflow-wrap: break-word; 25 | background-color: black; 26 | color: white; 27 | padding: 10px; 28 | font-weight: 500; 29 | max-width: 160px; 30 | font-size: 12px; 31 | margin-top: 0px; 32 | margin: 0 auto; 33 | text-align: center; 34 | } 35 | 36 | .three-object-block-url-input { 37 | padding-bottom: 20px; 38 | } 39 | 40 | .three-object-block-url-input input{ 41 | height: 40px; 42 | } 43 | -------------------------------------------------------------------------------- /blocks/three-light-block/editor.scss: -------------------------------------------------------------------------------- 1 | 2 | .wp-block-three-object-block { 3 | border: 1px dotted #f00; 4 | } 5 | .glb-preview-container { 6 | padding: 100px; 7 | text-align: center; 8 | align-items: center; 9 | align-content: center; 10 | background-color:#f2f2f2; 11 | } 12 | 13 | .glb-preview-container button{ 14 | padding: 15px; 15 | border-radius: 30px; 16 | } 17 | 18 | .glb-preview-container button:hover{ 19 | border-radius: 30px; 20 | background-color:rgb(156, 199, 0); 21 | cursor: pointer; 22 | } 23 | .three-object-block-tip { 24 | overflow-wrap: break-word; 25 | background-color: black; 26 | color: white; 27 | padding: 10px; 28 | font-weight: 500; 29 | max-width: 160px; 30 | font-size: 12px; 31 | margin-top: 0px; 32 | margin: 0 auto; 33 | text-align: center; 34 | } 35 | 36 | .three-object-block-url-input { 37 | padding-bottom: 20px; 38 | } 39 | 40 | .three-object-block-url-input input{ 41 | height: 40px; 42 | } 43 | -------------------------------------------------------------------------------- /blocks/three-video-block/editor.scss: -------------------------------------------------------------------------------- 1 | 2 | .wp-block-three-object-block { 3 | border: 1px dotted #f00; 4 | } 5 | .glb-preview-container { 6 | padding: 100px; 7 | text-align: center; 8 | align-items: center; 9 | align-content: center; 10 | background-color:#f2f2f2; 11 | } 12 | 13 | .glb-preview-container button{ 14 | padding: 15px; 15 | border-radius: 30px; 16 | } 17 | 18 | .glb-preview-container button:hover{ 19 | border-radius: 30px; 20 | background-color:rgb(156, 199, 0); 21 | cursor: pointer; 22 | } 23 | .three-object-block-tip { 24 | overflow-wrap: break-word; 25 | background-color: black; 26 | color: white; 27 | padding: 10px; 28 | font-weight: 500; 29 | max-width: 160px; 30 | font-size: 12px; 31 | margin-top: 0px; 32 | margin: 0 auto; 33 | text-align: center; 34 | } 35 | 36 | .three-object-block-url-input { 37 | padding-bottom: 20px; 38 | } 39 | 40 | .three-object-block-url-input input{ 41 | height: 40px; 42 | } 43 | -------------------------------------------------------------------------------- /admin/three-object-viewer-settings/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@wordpress/element'; 3 | import App from './App'; 4 | import apiFetch from '@wordpress/api-fetch'; 5 | 6 | window.addEventListener( 'load', async function () { 7 | //Endpoint URL 8 | const path = '/three-object-viewer/v1/three-object-viewer-settings/'; 9 | 10 | //Get settings from the REST API endpoint 11 | const getSettings = async () => { 12 | let data = await apiFetch( { 13 | path, 14 | method: 'GET', 15 | } ); 16 | return data; 17 | }; 18 | 19 | //Update settings via the REST API endpoint 20 | const updateSettings = async ( data ) => { 21 | let updatedData = apiFetch( { 22 | path, 23 | data, 24 | method: 'POST', 25 | } ); 26 | return updatedData; 27 | }; 28 | 29 | render( 30 | , 31 | document.getElementById( 'three-object-viewer-settings' ) 32 | ); 33 | } ); 34 | -------------------------------------------------------------------------------- /blocks/spawn-point-block/Save.js: -------------------------------------------------------------------------------- 1 | import { __ } from "@wordpress/i18n"; 2 | import { useBlockProps } from "@wordpress/block-editor"; 3 | 4 | export default function save({ attributes }) { 5 | return ( 6 |
7 | <> 8 |
9 |

10 | {attributes.positionX} 11 |

12 |

13 | {attributes.positionY} 14 |

15 |

16 | {attributes.positionZ} 17 |

18 |

19 | {attributes.rotationX} 20 |

21 |

22 | {attributes.rotationY} 23 |

24 |

25 | {attributes.rotationZ} 26 |

27 |
28 | 29 |
30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PSR2 with tabs instead of spaces. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /blocks/environment/style.scss: -------------------------------------------------------------------------------- 1 | .wp-block-three-object-block { 2 | background-color: #fff; 3 | color: #000; 4 | padding: 2px; 5 | } 6 | 7 | #networking { 8 | display: none !important; 9 | } 10 | 11 | .wp-block-three-object-block { 12 | border: 1px dotted #f00; 13 | } 14 | .glb-preview-container { 15 | padding: 100px; 16 | text-align: center; 17 | align-items: center; 18 | align-content: center; 19 | background-color:#f2f2f2; 20 | } 21 | 22 | .glb-preview-container button{ 23 | padding: 10px; 24 | border-radius: 30px; 25 | background-color:#333; 26 | color: white; 27 | } 28 | 29 | .three-object-block-tip { 30 | overflow-wrap: break-word; 31 | background-color: black; 32 | color: white; 33 | padding: 10px; 34 | font-weight: 500; 35 | max-width: 160px; 36 | font-size: 14px; 37 | margin-top: 0px; 38 | margin: 0 auto; 39 | text-align: center; 40 | } 41 | 42 | .glb-preview-container button:hover{ 43 | padding: 10px; 44 | border-radius: 30px; 45 | background-color:rgb(69, 69, 69); 46 | color: white; 47 | cursor: pointer; 48 | } -------------------------------------------------------------------------------- /blocks/environment/components/core/front/TextObject.js: -------------------------------------------------------------------------------- 1 | import React, { useRef } from "react"; 2 | import { 3 | Text, 4 | } from "@react-three/drei"; 5 | 6 | /** 7 | * Represents a text object in a virtual reality scene. 8 | * 9 | * @param {Object} model - The props for the text object. 10 | * 11 | * @return {JSX.Element} The text object. 12 | */ 13 | export function TextObject(model) { 14 | const htmlObj = useRef(); 15 | return ( 16 | <> 17 | 23 | 33 | {model.textContent} 34 | 35 | 36 | 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /blocks/sky-block/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-object-viewer/sky-block", 3 | "attributes": { 4 | "skyUrl": { 5 | "type": "string", 6 | "default": null 7 | }, 8 | "distance": { 9 | "type": "int", 10 | "default": 170000 11 | }, 12 | "rayleigh": { 13 | "type": "int", 14 | "default": 1 15 | }, 16 | "sunPositionX": { 17 | "type": "int", 18 | "default": 0 19 | }, 20 | "sunPositionY": { 21 | "type": "int", 22 | "default": 10000 23 | }, 24 | "sunPositionZ": { 25 | "type": "int", 26 | "default": -10000 27 | } 28 | }, 29 | "category": "design", 30 | "parent": [ "three-object-viewer/environment" ], 31 | "apiVersion": 2, 32 | "supports": { 33 | "html": false, 34 | "multiple": false 35 | }, 36 | "textdomain": "three-object-viewer", 37 | "editorScript": "file:../../build/block-sky-block.js", 38 | "editorStyle": "file:../../build/block-sky-block.css", 39 | "style": "file:../../build/block-sky-block.css" 40 | } 41 | -------------------------------------------------------------------------------- /blocks/environment/components/EditorPluginProvider.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext, useEffect, useCallback } from "react"; 2 | 3 | export const EditorPluginContext = React.createContext(); 4 | 5 | export function EditorPluginProvider({ children }) { 6 | 7 | const [plugins, setPlugins] = useState([]); 8 | 9 | const registerEditorPlugin = useCallback((plugin) => { 10 | setPlugins(prevPlugins => [...prevPlugins, plugin]); 11 | }, []); 12 | 13 | useEffect(() => { 14 | // Expose the registerPlugin method globally 15 | window.registerEditorPlugin = registerEditorPlugin; 16 | window.dispatchEvent(new Event('registerEditorPluginReady')); 17 | 18 | return () => { 19 | // Cleanup 20 | window.registerEditorPlugin = null; 21 | }; 22 | }, [registerEditorPlugin]); 23 | 24 | return ( 25 | 26 | {children} 27 | 28 | ); 29 | } 30 | 31 | export const useEditorPlugins = () => useContext(EditorPluginContext); 32 | -------------------------------------------------------------------------------- /blocks/three-object-block/editor.scss: -------------------------------------------------------------------------------- 1 | 2 | .wp-block-three-object-block { 3 | border: 1px dotted #f00; 4 | } 5 | .glb-preview-container { 6 | padding: 100px; 7 | text-align: center; 8 | align-items: center; 9 | align-content: center; 10 | background-color:#f2f2f2; 11 | } 12 | 13 | .glb-preview-container button{ 14 | padding: 15px; 15 | border-radius: 30px; 16 | } 17 | 18 | .three-object-viewer-button { 19 | background-color:rgb(199, 254, 0); 20 | color: black; 21 | border: solid 1.5px black; 22 | } 23 | 24 | .glb-preview-container button:hover{ 25 | border-radius: 30px; 26 | background-color:rgb(156, 199, 0); 27 | cursor: pointer; 28 | } 29 | .three-object-block-tip { 30 | overflow-wrap: break-word; 31 | background-color: black; 32 | color: white; 33 | padding: 10px; 34 | font-weight: 500; 35 | max-width: 160px; 36 | font-size: 12px; 37 | margin-top: 0px; 38 | margin: 0 auto; 39 | text-align: center; 40 | } 41 | 42 | .three-object-block-url-input { 43 | padding-bottom: 20px; 44 | } 45 | 46 | .three-object-block-url-input input{ 47 | height: 40px; 48 | } 49 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "antpb/three-object-viewer", 3 | "description": "threeObjectViewer", 4 | "require": { 5 | "php": "^7.2|^8.0", 6 | "wp-cli/i18n-command": "^2.4" 7 | }, 8 | "type": "wordpress-plugin", 9 | "autoload": { 10 | "psr-4": { 11 | "threeObjectViewer\\": "./php" 12 | } 13 | }, 14 | "autoload-dev": { 15 | "psr-4": { 16 | "threeObjectViewer\\Tests\\": "./tests" 17 | } 18 | }, 19 | "require-dev": { 20 | "phpunit/phpunit": "^7.0", 21 | "yoast/phpunit-polyfills": "^1.0.1", 22 | "mockery/mockery": "1.2", 23 | "brain/monkey": "2.*", 24 | "squizlabs/php_codesniffer": "^3.6" 25 | }, 26 | "scripts": { 27 | "test": "composer test:unit && composer test:wordpress", 28 | "test:unit": "phpunit --config=phpunit-unit.xml", 29 | "test:wordpress": "phpunit --config=phpunit-integration.xml", 30 | "sniffs": "phpcs php/ && phpcs tests/", 31 | "fixes": "phpcbf php/ && phpcbf tests/" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /languages/three-object-viewer-ja-three-object-viewer-three-image-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:23-0500","generator":"WP-CLI\/2.8.1","source":"build\/block-three-image-block.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"ja","plural-forms":"nplurals=2; plural=(n != 1);"},"Replace Image":["\u753b\u50cf\u3092\u7f6e\u304d\u63db\u3048\u308b"],"Select Image":["\u753b\u50cf\u3092\u9078\u629e\u3059\u308b"],"Scale":["\u30b9\u30b1\u30fc\u30eb"],"Rotation":["\u56de\u8ee2"],"Image Object":["\u30a4\u30e1\u30fc\u30b8\u30aa\u30d6\u30b8\u30a7\u30af\u30c8"],"Select an image to render in your environment:":["\u74b0\u5883\u5185\u306b\u30ec\u30f3\u30c0\u30ea\u30f3\u30b0\u3059\u308b\u753b\u50cf\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002"],"Image File":["\u753b\u50cf\u30d5\u30a1\u30a4\u30eb"],"Item is transparent.":["\u30a2\u30a4\u30c6\u30e0\u306f\u900f\u660e\u3067\u3059\u3002"],"Item is not transparent.":["\u30a2\u30a4\u30c6\u30e0\u306f\u900f\u660e\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002"],"Image block":["\u30a4\u30e1\u30fc\u30b8\u30d6\u30ed\u30c3\u30af"]}}} -------------------------------------------------------------------------------- /languages/three-object-viewer-ja-three-object-viewer-sky-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:23-0500","generator":"WP-CLI\/2.8.1","source":"build\/block-sky-block.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"ja","plural-forms":"nplurals=2; plural=(n != 1);"},"Settings":["\u8a2d\u5b9a"],"Sky Object":["\u30b9\u30ab\u30a4\u30aa\u30d6\u30b8\u30a7\u30af\u30c8"],"Select an image to be used as your skybox. 360 panoramics recommended:":["\u30b9\u30ab\u30a4\u30dc\u30c3\u30af\u30b9\u306b\u4f7f\u7528\u3059\u308b\u753b\u50cf\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002360\u5ea6\u306e\u30d1\u30ce\u30e9\u30df\u30c3\u30af\u753b\u50cf\u304c\u63a8\u5968\u3055\u308c\u307e\u3059\u3002"],"Sky File":["\u30b9\u30ab\u30a4\u30d5\u30a1\u30a4\u30eb"],"Replace Sky":["\u30b9\u30ab\u30a4\u3092\u7f6e\u304d\u63db\u3048\u308b"],"Select Sky":["\u30b9\u30ab\u30a4\u3092\u9078\u629e\u3059\u308b"],"Remove Image":["\u753b\u50cf\u3092\u524a\u9664\u3059\u308b"],"distance":["\u8ddd\u96e2"],"rayleigh":["\u30ec\u30a4\u30ea\u30fc"],"Sun Position":["\u592a\u967d\u306e\u4f4d\u7f6e"],"Sky block":["\u30b9\u30ab\u30a4\u30d6\u30ed\u30c3\u30af"]}}} -------------------------------------------------------------------------------- /blocks/environment/components/FrontPluginProvider.js: -------------------------------------------------------------------------------- 1 | import React, { createContext, useContext, useState, useEffect, useCallback } from "react"; 2 | import { useThree } from '@react-three/fiber'; 3 | 4 | export const FrontPluginContext = React.createContext(); 5 | 6 | export function FrontPluginProvider({ children }) { 7 | 8 | const [plugins, setPlugins] = useState([]); 9 | const { scene, camera } = useThree(); 10 | 11 | const registerFrontPlugin = useCallback((plugin) => { 12 | setPlugins(prevPlugins => [...prevPlugins, plugin]); 13 | }, []); 14 | 15 | useEffect(() => { 16 | // Expose the registerPlugin method globally 17 | window.registerFrontPlugin = registerFrontPlugin; 18 | window.dispatchEvent(new Event('registerFrontPluginReady')); 19 | 20 | return () => { 21 | // Cleanup 22 | window.registerFrontPlugin = null; 23 | }; 24 | }, [registerFrontPlugin]); 25 | 26 | return ( 27 | 28 | {children} 29 | 30 | ); 31 | } 32 | 33 | export const useFrontPlugins = () => useContext(FrontPluginContext); 34 | -------------------------------------------------------------------------------- /blocks/spawn-point-block/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-object-viewer/spawn-point-block", 3 | "attributes": { 4 | "positionX": { 5 | "type": "int", 6 | "default":0 7 | }, 8 | "positionY": { 9 | "type": "int", 10 | "default":0 11 | }, 12 | "positionZ": { 13 | "type": "int", 14 | "default":0 15 | }, 16 | "rotationX": { 17 | "type": "int", 18 | "default":0 19 | }, 20 | "rotationY": { 21 | "type": "int", 22 | "default":0 23 | }, 24 | "rotationZ": { 25 | "type": "int", 26 | "default":0 27 | } 28 | }, 29 | "category": "design", 30 | "parent": [ "three-object-viewer/environment" ], 31 | "apiVersion": 2, 32 | "supports": { 33 | "html": false, 34 | "multiple": false 35 | }, 36 | "textdomain": "three-object-viewer", 37 | "editorScript": "file:../../build/block-spawn-point-block.js", 38 | "editorStyle": "file:../../build/block-spawn-point-block.css", 39 | "style": "file:../../build/block-spawn-point-block.css" 40 | } 41 | -------------------------------------------------------------------------------- /blocks/three-text-block/editor.scss: -------------------------------------------------------------------------------- 1 | 2 | .wp-block-three-object-block { 3 | border: 1px dotted #f00; 4 | } 5 | .glb-preview-container { 6 | padding: 100px; 7 | text-align: center; 8 | align-items: center; 9 | align-content: center; 10 | background-color:#f2f2f2; 11 | } 12 | 13 | .glb-preview-container button{ 14 | padding: 15px; 15 | border-radius: 30px; 16 | } 17 | 18 | .glb-preview-container button:hover{ 19 | border-radius: 30px; 20 | background-color:rgb(156, 199, 0); 21 | cursor: pointer; 22 | } 23 | .three-object-block-tip { 24 | overflow-wrap: break-word; 25 | background-color: black; 26 | color: white; 27 | padding: 10px; 28 | font-weight: 500; 29 | max-width: 160px; 30 | font-size: 12px; 31 | margin-top: 0px; 32 | margin: 0 auto; 33 | text-align: center; 34 | } 35 | 36 | .three-object-block-url-input { 37 | padding-bottom: 20px; 38 | } 39 | 40 | .three-object-block-url-input input{ 41 | height: 40px; 42 | } 43 | 44 | .block-editor-block-inspector .components-base-control.position-inputs:last-child { 45 | margin-bottom: 24px !important; 46 | } 47 | .block-editor-block-inspector .components-base-control.position-inputs { 48 | padding-right: 5px; 49 | } 50 | -------------------------------------------------------------------------------- /blocks/model-block/editor.scss: -------------------------------------------------------------------------------- 1 | 2 | .wp-block-three-object-block { 3 | border: 1px dotted #f00; 4 | } 5 | .glb-preview-container { 6 | padding: 100px; 7 | text-align: center; 8 | align-items: center; 9 | align-content: center; 10 | background-color:#f2f2f2; 11 | } 12 | 13 | .glb-preview-container button{ 14 | padding: 15px; 15 | border-radius: 30px; 16 | } 17 | 18 | .glb-preview-container button:hover{ 19 | border-radius: 30px; 20 | // background-color:rgb(156, 199, 0); 21 | cursor: pointer; 22 | } 23 | 24 | .three-object-block-tip { 25 | overflow-wrap: break-word; 26 | background-color: black; 27 | color: white; 28 | padding: 10px; 29 | font-weight: 500; 30 | max-width: 160px; 31 | font-size: 12px; 32 | margin-top: 0px; 33 | margin: 0 auto; 34 | text-align: center; 35 | } 36 | 37 | .three-object-block-url-input { 38 | padding-bottom: 20px; 39 | } 40 | 41 | .three-object-block-url-input input{ 42 | height: 40px; 43 | } 44 | 45 | .block-editor-block-inspector .components-base-control.position-inputs:last-child { 46 | margin-bottom: 24px !important; 47 | } 48 | .block-editor-block-inspector .components-base-control.position-inputs { 49 | padding-right: 5px; 50 | } 51 | -------------------------------------------------------------------------------- /blocks/npc-block/editor.scss: -------------------------------------------------------------------------------- 1 | 2 | .wp-block-three-object-block { 3 | border: 1px dotted #f00; 4 | } 5 | .glb-preview-container { 6 | padding: 100px; 7 | text-align: center; 8 | align-items: center; 9 | align-content: center; 10 | background-color:#f2f2f2; 11 | } 12 | 13 | .glb-preview-container button{ 14 | padding: 15px; 15 | border-radius: 30px; 16 | } 17 | 18 | .glb-preview-container button:hover{ 19 | border-radius: 30px; 20 | // background-color:rgb(156, 199, 0); 21 | cursor: pointer; 22 | } 23 | 24 | .three-object-block-tip { 25 | overflow-wrap: break-word; 26 | background-color: black; 27 | color: white; 28 | padding: 10px; 29 | font-weight: 500; 30 | max-width: 160px; 31 | font-size: 12px; 32 | margin-top: 0px; 33 | margin: 0 auto; 34 | text-align: center; 35 | } 36 | 37 | .three-object-block-url-input { 38 | padding-bottom: 20px; 39 | } 40 | 41 | .three-object-block-url-input input{ 42 | height: 40px; 43 | } 44 | 45 | .block-editor-block-inspector .components-base-control.position-inputs:last-child { 46 | margin-bottom: 24px !important; 47 | } 48 | .block-editor-block-inspector .components-base-control.position-inputs { 49 | padding-right: 5px; 50 | } 51 | -------------------------------------------------------------------------------- /blocks/three-portal-block/editor.scss: -------------------------------------------------------------------------------- 1 | 2 | .wp-block-three-object-block { 3 | border: 1px dotted #f00; 4 | } 5 | .glb-preview-container { 6 | padding: 100px; 7 | text-align: center; 8 | align-items: center; 9 | align-content: center; 10 | background-color:#f2f2f2; 11 | } 12 | 13 | .glb-preview-container button{ 14 | padding: 15px; 15 | border-radius: 30px; 16 | } 17 | 18 | .glb-preview-container button:hover{ 19 | border-radius: 30px; 20 | background-color:rgb(156, 199, 0); 21 | cursor: pointer; 22 | } 23 | .three-object-block-tip { 24 | overflow-wrap: break-word; 25 | background-color: black; 26 | color: white; 27 | padding: 10px; 28 | font-weight: 500; 29 | max-width: 160px; 30 | font-size: 12px; 31 | margin-top: 0px; 32 | margin: 0 auto; 33 | text-align: center; 34 | } 35 | 36 | .three-object-block-url-input { 37 | padding-bottom: 20px; 38 | } 39 | 40 | .three-object-block-url-input input{ 41 | height: 40px; 42 | } 43 | 44 | .block-editor-block-inspector .components-base-control.position-inputs:last-child { 45 | margin-bottom: 24px !important; 46 | } 47 | .block-editor-block-inspector .components-base-control.position-inputs { 48 | padding-right: 5px; 49 | } 50 | -------------------------------------------------------------------------------- /languages/three-object-viewer-es_MX-three-object-viewer-three-video-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:28-0500","generator":"WP-CLI\/2.8.1","source":"blocks\/three-video-block\/Edit.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"es_MX","plural-forms":"nplurals=2; plural=(n != 1);"},"Scale":["Escala"],"Settings":["Ajustes"],"Replace Object":["Reemplazar Objeto"],"Rotation":["Rotaci\u00f3n"],"Item will autoplay.":["El elemento se reproducir\u00e1 autom\u00e1ticamente."],"Item will not autoplay.":["El elemento no se reproducir\u00e1 autom\u00e1ticamente."],"Select an image to render in your environment:":["Selecciona una imagen para renderizar en tu entorno:"],"Video File":["Archivo de Video"],"Replace Video":["Reemplazar Video"],"Select Video":["Seleccionar Video"],"Custom Model":["Modelo Personalizado"],"Item will use a custom model. Name your faces 'screen'.":["El elemento usar\u00e1 un modelo personalizado. Nombra tus caras como \u2018screen\u2019."],"Item will not use a custom model.":["El elemento no usar\u00e1 un modelo personalizado."],"Replace model":["Reemplazar modelo"],"Select model":["Seleccionar modelo"],"Video block":["Bloque de Video"]}}} -------------------------------------------------------------------------------- /blocks/environment/components/core/front/ThreeImage.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useLoader, useThree } from "@react-three/fiber"; 3 | import { TextureLoader, DoubleSide } from "three"; 4 | 5 | /** 6 | * Renders an image in a three.js scene. 7 | * 8 | * @param {Object} threeImage - The props for the image. 9 | * 10 | * @return {JSX.Element} The image. 11 | */ 12 | export function ThreeImage(threeImage) { 13 | const texture2 = useLoader(TextureLoader, threeImage.url); 14 | return ( 15 | 29 | 35 | {threeImage.transparent == "1" ? ( 36 | 41 | ) : ( 42 | 43 | )} 44 | 45 | ); 46 | } -------------------------------------------------------------------------------- /blocks/three-text-block/Save.js: -------------------------------------------------------------------------------- 1 | import { __ } from "@wordpress/i18n"; 2 | import { useBlockProps } from "@wordpress/block-editor"; 3 | 4 | export default function save({ attributes }) { 5 | return ( 6 |
7 | <> 8 |
9 |

10 | {attributes.textContent} 11 |

12 |

13 | {attributes.positionX} 14 |

15 |

16 | {attributes.positionY} 17 |

18 |

19 | {attributes.positionZ} 20 |

21 |

22 | {attributes.rotationX} 23 |

24 |

25 | {attributes.rotationY} 26 |

27 |

28 | {attributes.rotationZ} 29 |

30 |

{attributes.scaleX}

31 |

{attributes.scaleY}

32 |

{attributes.scaleZ}

33 |

{attributes.textColor}

34 |
35 | 36 |
37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /blocks/sky-block/index.js: -------------------------------------------------------------------------------- 1 | import { registerBlockType } from "@wordpress/blocks"; 2 | import Edit from "./Edit"; 3 | import Save from "./Save"; 4 | import { useBlockProps } from "@wordpress/block-editor"; 5 | 6 | const icon = ( 7 | 13 | 14 | 15 | 16 | 17 | ); 18 | 19 | const blockConfig = require("./block.json"); 20 | registerBlockType(blockConfig.name, { 21 | ...blockConfig, 22 | icon, 23 | apiVersion: 2, 24 | edit: Edit, 25 | save: Save, 26 | deprecated: [ 27 | { 28 | attributes: { 29 | skyUrl: { 30 | type: "string", 31 | default: null 32 | }, 33 | }, 34 | save(props) { 35 | return ( 36 |
37 | <> 38 |
39 |

{props.attributes.skyUrl}

40 |
41 | 42 |
43 | ); 44 | } 45 | } 46 | ] 47 | }); 48 | -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra'); 2 | const path = require('path'); 3 | 4 | const isPro = process.argv.includes('pro'); 5 | 6 | const sourceDirectory = path.join(__dirname); 7 | const targetDirectory = path.join(__dirname, 'plugin-build', isPro ? 'pro/three-object-viewer' : 'free/three-object-viewer'); 8 | 9 | // Explicitly specify directories or files you want to copy for the free version 10 | const itemsToCopy = [ 11 | 'LICENSE', 12 | 'admin', 13 | 'blocks', 14 | 'build', 15 | 'inc', 16 | 'languages', 17 | 'php', 18 | 'readme.txt', 19 | 'three-object-viewer.php', 20 | ]; 21 | 22 | // Ensure the target directory is clean before copying 23 | fs.removeSync(targetDirectory); 24 | fs.ensureDirSync(targetDirectory); 25 | 26 | itemsToCopy.forEach(item => { 27 | const sourcePath = path.join(sourceDirectory, item); 28 | const targetPath = path.join(targetDirectory, item); 29 | fs.copySync(sourcePath, targetPath); 30 | }); 31 | 32 | // If it's the pro build, include the 'pro' directory plus all free version files/directories 33 | if (isPro) { 34 | const sourcePro = path.join(sourceDirectory, 'pro'); 35 | const targetPro = path.join(targetDirectory, 'pro'); 36 | fs.copySync(sourcePro, targetPro); 37 | } 38 | 39 | console.log(`Packaged the ${isPro ? 'pro' : 'free'} version to ${targetDirectory}`); 40 | -------------------------------------------------------------------------------- /blocks/three-object-block/Save.js: -------------------------------------------------------------------------------- 1 | import { __ } from '@wordpress/i18n'; 2 | import { useBlockProps } from '@wordpress/block-editor'; 3 | 4 | export default function save( { attributes } ) { 5 | return ( 6 |
7 | <> 8 |
9 |

10 | { attributes.deviceTarget } 11 |

12 |

13 | { attributes.threeObjectUrl } 14 |

15 |

{ attributes.scale }

16 |

17 | { attributes.bg_color } 18 |

19 |

{ attributes.zoom }

20 |

21 | { attributes.hasZoom ? 1 : 0 } 22 |

23 |

24 | { attributes.hasTip ? 1 : 0 } 25 |

26 |

27 | { attributes.positionY } 28 |

29 |

30 | { attributes.rotationY } 31 |

32 |

{ attributes.scale }

33 |

34 | { attributes.animations } 35 |

36 |
37 | 38 |
39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /languages/three-object-viewer-es_MX-three-object-viewer-npc-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:28-0500","generator":"WP-CLI\/2.8.1","source":"blocks\/npc-block\/Edit.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"es_MX","plural-forms":"nplurals=2; plural=(n != 1);"},"Settings":["Ajustes"],"Name":["Nombre"],"Position":["Posici\u00f3n"],"Rotation":["Rotaci\u00f3n"],"NPC Options":["Opciones de PNJ"],"Give your avatar a name.":["Asigna un nombre a tu avatar."],"Personality":["Personalidad"],"Give your avatar a personality in 600 characters.":["Asigna una personalidad a tu avatar en 600 caracteres."],"Default Message":["Mensaje Predeterminado"],"Give your avatar a default message to initialize with.":["Asigna un mensaje predeterminado a tu avatar para la inicializaci\u00f3n."],"Select a VRM file to be used as your NPC in world:":["Selecciona un archivo VRM para usar como tu PNJ en el mundo:"],"Advanced Settings":["Ajustes Avanzados"],"Include World Objects in Prompt":["Incluir Objetos del Mundo en la Sugerencia"],"If enabled, will result in higher token costs with your AI service provider. Use with caution.":["Si est\u00e1 activado, resultar\u00e1 en costos de tokens m\u00e1s altos con tu proveedor de servicios de IA. \u00dasalo con precauci\u00f3n."],"NPC block":["Bloque de PNJ"],"Replace NPC":["Reemplazar PNJ"],"Select NPC":["Seleccionar PNJ"]}}} -------------------------------------------------------------------------------- /blocks/npc-block/Save.js: -------------------------------------------------------------------------------- 1 | import { __ } from "@wordpress/i18n"; 2 | import { useBlockProps } from "@wordpress/block-editor"; 3 | 4 | export default function save({ attributes }) { 5 | return ( 6 |
7 | <> 8 |
9 |

10 | {attributes.threeObjectUrl} 11 |

12 |

13 | {attributes.positionX} 14 |

15 |

16 | {attributes.positionY} 17 |

18 |

19 | {attributes.positionZ} 20 |

21 |

22 | {attributes.rotationX} 23 |

24 |

25 | {attributes.rotationY} 26 |

27 |

28 | {attributes.rotationZ} 29 |

30 |

31 | {attributes.name} 32 |

33 |

34 | {attributes.defaultMessage} 35 |

36 |

37 | {attributes.personality} 38 |

39 |

40 | {attributes.objectAwareness ? 1 : 0} 41 |

42 |
43 | 44 |
45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /.github/workflows/php-unit.yml: -------------------------------------------------------------------------------- 1 | name: PHP Unit Tests 2 | 3 | on: push 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | php-versions: [7.2, 7.3, 7.4] 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Setup PHP 15 | uses: shivammathur/setup-php@v2 16 | with: 17 | php-version: ${{ matrix.php-versions }} 18 | # Install composer with cache 19 | - name: Get Composer Cache Directory 20 | id: get-composer-cache-dir # Instead of composer-cache 21 | run: | 22 | echo "::set-output name=dir::$(composer config cache-files-dir)" 23 | 24 | - name: Cache Composer packages 25 | id: composer-cache 26 | uses: actions/cache@v2 27 | with: 28 | path: vendor 29 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 30 | restore-keys: | 31 | ${{ runner.os }}-php- 32 | 33 | - name: Install dependencies 34 | if: steps.composer-cache.outputs.cache-hit != 'true' 35 | run: composer install --prefer-dist --no-progress 36 | 37 | # Run unit tests 38 | - name: Unit Tests 39 | run: composer test:unit 40 | -------------------------------------------------------------------------------- /blocks/environment/components/ContextBridgeComponent.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { useFrontPlugins, FrontPluginContext } from './FrontPluginProvider'; 3 | import { useContextBridge } from "@react-three/drei"; 4 | 5 | //import contextBridgef 6 | // add function for context 7 | export function ContextBridgeComponent(props) { 8 | const { plugins } = useFrontPlugins(); // From your own context 9 | const [registeredThreeovBlocks, setRegisteredThreeovBlocks] = useState([]); 10 | const ContextBridge = useContextBridge(FrontPluginContext); 11 | 12 | useEffect(() => { 13 | if (plugins.length > 0) { 14 | plugins.forEach((plugin) => { 15 | // add the plugin to the registered blocks 16 | setRegisteredThreeovBlocks((registeredThreeovBlocks) => [ 17 | ...registeredThreeovBlocks, 18 | plugin, 19 | ]); 20 | }); 21 | } 22 | }, [plugins]); 23 | 24 | return ( 25 | 26 | { 27 | registeredThreeovBlocks.length > 0 && registeredThreeovBlocks.map((blockElement, index) => { 28 | const BlockComponent = blockElement.type; 29 | return ( 30 | 36 | 37 | 38 | ) 39 | }) 40 | } 41 | 42 | ) 43 | 44 | } 45 | -------------------------------------------------------------------------------- /blocks/model-block/Save.js: -------------------------------------------------------------------------------- 1 | import { __ } from "@wordpress/i18n"; 2 | import { useBlockProps } from "@wordpress/block-editor"; 3 | 4 | export default function save({ attributes }) { 5 | return ( 6 |
7 | <> 8 |
9 |

10 | {attributes.threeObjectUrl} 11 |

12 |

{attributes.scaleX}

13 |

{attributes.scaleY}

14 |

{attributes.scaleZ}

15 |

16 | {attributes.positionX} 17 |

18 |

19 | {attributes.positionY} 20 |

21 |

22 | {attributes.positionZ} 23 |

24 |

25 | {attributes.rotationX} 26 |

27 |

28 | {attributes.rotationY} 29 |

30 |

31 | {attributes.rotationZ} 32 |

33 |

34 | {attributes.animations} 35 |

36 |

37 | {attributes.collidable ? 1 : 0} 38 |

39 |

{attributes.alt}

40 |
41 | 42 |
43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /tests/Unit/EnvironmentTest.php: -------------------------------------------------------------------------------- 1 | assertIsBool(true); 24 | self::assertIsNotIterable(new \stdClass()); 25 | } 26 | 27 | /** 28 | * A test ensuring that the composer autoloader works 29 | */ 30 | public function testAutoloaderWorks() 31 | { 32 | $this->assertSame('Hey, Listen!', (new Plugin())->listen()); 33 | } 34 | 35 | /** 36 | * Test that we can mock WordPress functions 37 | * 38 | * @see https://giuseppe-mazzapica.gitbook.io/brain-monkey/functions-testing-tools/functions-when#justreturn 39 | */ 40 | public function testMockWordPressFunction() 41 | { 42 | //A fake wp_insert_post() that always returns 1 43 | Functions\when('wp_insert_post')->justReturn(1); 44 | $this->assertIsNumeric( 45 | wp_insert_post([ 46 | 'post_title' => 'If I learn it again, I would recommend:', 47 | 'post_content' => 'grow aloe. then grow cactus. then grow sempervivum. then grow lithops and echeveria' 48 | ]) 49 | ); 50 | $this->assertSame(1, wp_insert_post()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /blocks/three-image-block/Save.js: -------------------------------------------------------------------------------- 1 | import { __ } from "@wordpress/i18n"; 2 | import { useBlockProps } from "@wordpress/block-editor"; 3 | 4 | export default function save({ attributes }) { 5 | return ( 6 |
7 | <> 8 |
9 |

{attributes.imageUrl}

10 |

{attributes.scaleX}

11 |

{attributes.scaleY}

12 |

{attributes.scaleZ}

13 |

14 | {attributes.positionX} 15 |

16 |

17 | {attributes.positionY} 18 |

19 |

20 | {attributes.positionZ} 21 |

22 |

23 | {attributes.rotationX} 24 |

25 |

26 | {attributes.rotationY} 27 |

28 |

29 | {attributes.rotationZ} 30 |

31 |

32 | {attributes.aspectHeight} 33 |

34 |

35 | {attributes.aspectWidth} 36 |

37 |

38 | {attributes.transparent ? 1 : 0} 39 |

40 |
41 | 42 |
43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /languages/three-object-viewer-es_MX-three-object-viewer-environment-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:28-0500","generator":"WP-CLI\/2.8.1","source":"build\/block-environment.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"es_MX","plural-forms":"nplurals=2; plural=(n != 1);"},"Environment Settings":["Configuraci\u00f3n de Entorno"],"Environment Object (Changing this value changes your scene ground planes)":["Objeto de Entorno (Cambiar este valor cambia los planos de suelo de la escena)"],"Select a glb file from your media library. This will be treated as a collidable mesh that visitors can walk on:":["Selecciona un archivo .glb de tu biblioteca de medios. Esto ser\u00e1 tratado como una malla colisionable sobre la cual los visitantes pueden caminar:"],"Replace Environment":["Reemplazar Entorno"],"Select Environment":["Seleccionar Entorno"],"Select an image to be used as the preview image:":["Selecciona una imagen para usar como imagen de vista previa:"],"Replace Image":["Reemplazar Imagen"],"Select Image":["Seleccionar Imagen"],"Replace HDR":["Reemplazar HDR"],"Select HDR":["Seleccionar HDR"],"Remove HDR":["Eliminar HDR"],"Scene Settings":["Configuraci\u00f3n de Escena"],"Object Display Type:":["Tipo de Visualizaci\u00f3n del Objeto:"],"Loop Animations":["Repetir Animaciones"],"Separate each animation name you wish to loop with a comma":["Separa cada nombre de animaci\u00f3n que deseas repetir con una coma"],"Scale":["Escala"],"Position Y":["Posici\u00f3n Y"],"Rotation Y":["Rotaci\u00f3n Y"]}}} -------------------------------------------------------------------------------- /languages/three-object-viewer-es_MX-three-object-viewer-model-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:28-0500","generator":"WP-CLI\/2.8.1","source":"build\/block-model-block.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"es_MX","plural-forms":"nplurals=2; plural=(n != 1);"},"Loop Animations":["Repetir Animaciones"],"Separate each animation name you wish to loop with a comma":["Separa cada nombre de animaci\u00f3n que deseas repetir con una coma"],"Scale":["Escala"],"Settings":["Ajustes"],"GLB Object":["Objeto GLB"],"Name":["Nombre"],"Give your object a name.":["Asigna un nombre a tu objeto."],"select a glb file from your media library to render an object in the canvas:":["selecciona un archivo glb de tu biblioteca de medios para renderizar un objeto en el lienzo:"],"GLB File":["Archivo GLB"],"Replace Object":["Reemplazar Objeto"],"Select Object":["Seleccionar Objeto"],"Model Attributes":["Atributos del Modelo"],"Collidable":["Colisionable"],"Item is currently collidable.":["El elemento es actualmente colisionable."],"Item is not collidable. Users will walk through it.":["El elemento no es colisionable. Los usuarios podr\u00e1n atravesarlo."],"Model Alt Text":["Texto Alternativo del Modelo"],"Describe your model to provide context for screen readers.":["Describe tu modelo para proporcionar contexto a los lectores de pantalla."],"Position":["Posici\u00f3n"],"Rotation":["Rotaci\u00f3n"],"Model block":["Bloque de Modelo"],"Replace Model":["Reemplazar Modelo"],"Select Model":["Seleccionar Modelo"]}}} -------------------------------------------------------------------------------- /languages/three-object-viewer-es_MX-three-object-viewer-settings.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:28-0500","generator":"WP-CLI\/2.8.1","source":"admin\/three-object-viewer-settings\/App.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"es_MX","plural-forms":"nplurals=2; plural=(n != 1);"},"3OV Settings":["Ajustes 3OV"],"Select or Upload Media":["Seleccionar o Subir Medios"],"Use this media":["Usar este medio"],"Here you can manage the settings for 3OV to tweak global configuration options and save your API keys for connected serivces.":["Aqu\u00ed puedes gestionar la configuraci\u00f3n de 3OV para ajustar las opciones de configuraci\u00f3n global y guardar tus claves de API para los servicios conectados."],"Avatar Settings":["Ajustes de Avatar"],"Default animation":["Animaci\u00f3n predeterminada"],"No custom default animation set":["Sin conjunto de animaci\u00f3n predeterminada personalizada"],"Set Default Animation":["Establecer Animaci\u00f3n Predeterminada"],"Clear Default Animation":["Borrar Animaci\u00f3n Predeterminada"],"No custom default avatar set":["Sin conjunto de avatar predeterminado personalizado"],"Set Default Avatar":["Establecer Avatar Predeterminado"],"Clear Default Avatar":["Borrar Avatar Predeterminado"],"AI Settings":["Ajustes de IA"],"NPC Settings":["Ajustes de PNJ"],"Enable":["Habilitar"],"AI Endpoint URL":["URL del Punto de Extremo de la IA"],"OpenAI API Token":["Token de API de OpenAI"],"AI Access Level":["Nivel de Acceso de la IA"],"Public":["P\u00fablico"],"Logged In":["Sesi\u00f3n Iniciada"],"Saving...":["Guardando\u2026"]}}} -------------------------------------------------------------------------------- /blocks/environment/Save.js: -------------------------------------------------------------------------------- 1 | import { __ } from "@wordpress/i18n"; 2 | import { useBlockProps, InnerBlocks } from "@wordpress/block-editor"; 3 | 4 | export default function save({ attributes }) { 5 | return ( 6 |
7 | <> 8 |
9 |

10 | {attributes.deviceTarget} 11 |

12 |

13 | {attributes.threeObjectUrl} 14 |

15 |

16 | {attributes.hdr} 17 |

18 |

{attributes.scale}

19 |

20 | {attributes.bg_color} 21 |

22 |

{attributes.zoom}

23 |

24 | {attributes.hasZoom ? 1 : 0} 25 |

26 |

27 | {attributes.hasTip ? 1 : 0} 28 |

29 |

30 | {attributes.positionY} 31 |

32 |

33 | {attributes.rotationY} 34 |

35 |

{attributes.scale}

36 |

37 | {attributes.threePreviewImage} 38 |

39 |

40 | {attributes.animations} 41 |

42 | 43 |
44 | 45 |
46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /blocks/npc-block/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-object-viewer/npc-block", 3 | "attributes": { 4 | "name": { 5 | "type": "string" 6 | }, 7 | "personality": { 8 | "type": "string" 9 | }, 10 | "defaultMessage": { 11 | "type": "string" 12 | }, 13 | "positionX": { 14 | "type": "int", 15 | "default":0 16 | }, 17 | "positionY": { 18 | "type": "int", 19 | "default":0 20 | }, 21 | "positionZ": { 22 | "type": "int", 23 | "default":0 24 | }, 25 | "rotationX": { 26 | "type": "int", 27 | "default":0 28 | }, 29 | "rotationY": { 30 | "type": "int", 31 | "default":0 32 | }, 33 | "rotationZ": { 34 | "type": "int", 35 | "default":0 36 | }, 37 | "threeObjectUrl": { 38 | "type": "string", 39 | "default": null 40 | }, 41 | "objectAwareness": { 42 | "type": "boolean", 43 | "default": false 44 | } 45 | }, 46 | "category": "design", 47 | "parent": [ "three-object-viewer/environment" ], 48 | "apiVersion": 2, 49 | "supports": { 50 | "html": false, 51 | "multiple": false 52 | }, 53 | "textdomain": "three-object-viewer", 54 | "editorScript": "file:../../build/block-npc-block.js", 55 | "editorStyle": "file:../../build/block-npc-block.css", 56 | "style": "file:../../build/block-npc-block.css" 57 | } 58 | -------------------------------------------------------------------------------- /blocks/environment/Edit.test.js: -------------------------------------------------------------------------------- 1 | //Import React 2 | import React from "react"; 3 | //Import test renderer 4 | import { render, fireEvent, cleanup } from "@testing-library/react"; 5 | //Import component to test 6 | import { Editor } from "./Edit"; 7 | 8 | describe("Editor componet", () => { 9 | afterEach(cleanup); 10 | it("matches snapshot when selected", () => { 11 | const onChange = jest.fn(); 12 | const { container } = render( 13 | 14 | ); 15 | expect(container).toMatchSnapshot(); 16 | }); 17 | 18 | it("matches snapshot when not selected", () => { 19 | const onChange = jest.fn(); 20 | const { container } = render( 21 | 22 | ); 23 | expect(container).toMatchSnapshot(); 24 | }); 25 | 26 | it("Calls the onchange function", () => { 27 | const onChange = jest.fn(); 28 | const { getByDisplayValue } = render( 29 | 30 | ); 31 | fireEvent.change(getByDisplayValue("Salad"), { 32 | target: { value: "New Value" } 33 | }); 34 | expect(onChange).toHaveBeenCalledTimes(1); 35 | }); 36 | 37 | it("Passes updated value, not event to onChange callback", () => { 38 | const onChange = jest.fn(); 39 | const { getByDisplayValue } = render( 40 | 41 | ); 42 | fireEvent.change(getByDisplayValue("Seltzer"), { 43 | target: { value: "Boring Water" } 44 | }); 45 | expect(onChange).toHaveBeenCalledWith("Boring Water"); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /blocks/model-block/Edit.test.js: -------------------------------------------------------------------------------- 1 | //Import React 2 | import React from "react"; 3 | //Import test renderer 4 | import { render, fireEvent, cleanup } from "@testing-library/react"; 5 | //Import component to test 6 | import { Editor } from "./Edit"; 7 | 8 | describe("Editor componet", () => { 9 | afterEach(cleanup); 10 | it("matches snapshot when selected", () => { 11 | const onChange = jest.fn(); 12 | const { container } = render( 13 | 14 | ); 15 | expect(container).toMatchSnapshot(); 16 | }); 17 | 18 | it("matches snapshot when not selected", () => { 19 | const onChange = jest.fn(); 20 | const { container } = render( 21 | 22 | ); 23 | expect(container).toMatchSnapshot(); 24 | }); 25 | 26 | it("Calls the onchange function", () => { 27 | const onChange = jest.fn(); 28 | const { getByDisplayValue } = render( 29 | 30 | ); 31 | fireEvent.change(getByDisplayValue("Salad"), { 32 | target: { value: "New Value" } 33 | }); 34 | expect(onChange).toHaveBeenCalledTimes(1); 35 | }); 36 | 37 | it("Passes updated value, not event to onChange callback", () => { 38 | const onChange = jest.fn(); 39 | const { getByDisplayValue } = render( 40 | 41 | ); 42 | fireEvent.change(getByDisplayValue("Seltzer"), { 43 | target: { value: "Boring Water" } 44 | }); 45 | expect(onChange).toHaveBeenCalledWith("Boring Water"); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /blocks/npc-block/Edit.test.js: -------------------------------------------------------------------------------- 1 | //Import React 2 | import React from "react"; 3 | //Import test renderer 4 | import { render, fireEvent, cleanup } from "@testing-library/react"; 5 | //Import component to test 6 | import { Editor } from "./Edit"; 7 | 8 | describe("Editor componet", () => { 9 | afterEach(cleanup); 10 | it("matches snapshot when selected", () => { 11 | const onChange = jest.fn(); 12 | const { container } = render( 13 | 14 | ); 15 | expect(container).toMatchSnapshot(); 16 | }); 17 | 18 | it("matches snapshot when not selected", () => { 19 | const onChange = jest.fn(); 20 | const { container } = render( 21 | 22 | ); 23 | expect(container).toMatchSnapshot(); 24 | }); 25 | 26 | it("Calls the onchange function", () => { 27 | const onChange = jest.fn(); 28 | const { getByDisplayValue } = render( 29 | 30 | ); 31 | fireEvent.change(getByDisplayValue("Salad"), { 32 | target: { value: "New Value" } 33 | }); 34 | expect(onChange).toHaveBeenCalledTimes(1); 35 | }); 36 | 37 | it("Passes updated value, not event to onChange callback", () => { 38 | const onChange = jest.fn(); 39 | const { getByDisplayValue } = render( 40 | 41 | ); 42 | fireEvent.change(getByDisplayValue("Seltzer"), { 43 | target: { value: "Boring Water" } 44 | }); 45 | expect(onChange).toHaveBeenCalledWith("Boring Water"); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /blocks/sky-block/Edit.test.js: -------------------------------------------------------------------------------- 1 | //Import React 2 | import React from "react"; 3 | //Import test renderer 4 | import { render, fireEvent, cleanup } from "@testing-library/react"; 5 | //Import component to test 6 | import { Editor } from "./Edit"; 7 | 8 | describe("Editor componet", () => { 9 | afterEach(cleanup); 10 | it("matches snapshot when selected", () => { 11 | const onChange = jest.fn(); 12 | const { container } = render( 13 | 14 | ); 15 | expect(container).toMatchSnapshot(); 16 | }); 17 | 18 | it("matches snapshot when not selected", () => { 19 | const onChange = jest.fn(); 20 | const { container } = render( 21 | 22 | ); 23 | expect(container).toMatchSnapshot(); 24 | }); 25 | 26 | it("Calls the onchange function", () => { 27 | const onChange = jest.fn(); 28 | const { getByDisplayValue } = render( 29 | 30 | ); 31 | fireEvent.change(getByDisplayValue("Salad"), { 32 | target: { value: "New Value" } 33 | }); 34 | expect(onChange).toHaveBeenCalledTimes(1); 35 | }); 36 | 37 | it("Passes updated value, not event to onChange callback", () => { 38 | const onChange = jest.fn(); 39 | const { getByDisplayValue } = render( 40 | 41 | ); 42 | fireEvent.change(getByDisplayValue("Seltzer"), { 43 | target: { value: "Boring Water" } 44 | }); 45 | expect(onChange).toHaveBeenCalledWith("Boring Water"); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /blocks/spawn-point-block/Edit.test.js: -------------------------------------------------------------------------------- 1 | //Import React 2 | import React from "react"; 3 | //Import test renderer 4 | import { render, fireEvent, cleanup } from "@testing-library/react"; 5 | //Import component to test 6 | import { Editor } from "./Edit"; 7 | 8 | describe("Editor componet", () => { 9 | afterEach(cleanup); 10 | it("matches snapshot when selected", () => { 11 | const onChange = jest.fn(); 12 | const { container } = render( 13 | 14 | ); 15 | expect(container).toMatchSnapshot(); 16 | }); 17 | 18 | it("matches snapshot when not selected", () => { 19 | const onChange = jest.fn(); 20 | const { container } = render( 21 | 22 | ); 23 | expect(container).toMatchSnapshot(); 24 | }); 25 | 26 | it("Calls the onchange function", () => { 27 | const onChange = jest.fn(); 28 | const { getByDisplayValue } = render( 29 | 30 | ); 31 | fireEvent.change(getByDisplayValue("Salad"), { 32 | target: { value: "New Value" } 33 | }); 34 | expect(onChange).toHaveBeenCalledTimes(1); 35 | }); 36 | 37 | it("Passes updated value, not event to onChange callback", () => { 38 | const onChange = jest.fn(); 39 | const { getByDisplayValue } = render( 40 | 41 | ); 42 | fireEvent.change(getByDisplayValue("Seltzer"), { 43 | target: { value: "Boring Water" } 44 | }); 45 | expect(onChange).toHaveBeenCalledWith("Boring Water"); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /blocks/three-audio-block/Edit.test.js: -------------------------------------------------------------------------------- 1 | //Import React 2 | import React from "react"; 3 | //Import test renderer 4 | import { render, fireEvent, cleanup } from "@testing-library/react"; 5 | //Import component to test 6 | import { Editor } from "./Edit"; 7 | 8 | describe("Editor componet", () => { 9 | afterEach(cleanup); 10 | it("matches snapshot when selected", () => { 11 | const onChange = jest.fn(); 12 | const { container } = render( 13 | 14 | ); 15 | expect(container).toMatchSnapshot(); 16 | }); 17 | 18 | it("matches snapshot when not selected", () => { 19 | const onChange = jest.fn(); 20 | const { container } = render( 21 | 22 | ); 23 | expect(container).toMatchSnapshot(); 24 | }); 25 | 26 | it("Calls the onchange function", () => { 27 | const onChange = jest.fn(); 28 | const { getByDisplayValue } = render( 29 | 30 | ); 31 | fireEvent.change(getByDisplayValue("Salad"), { 32 | target: { value: "New Value" } 33 | }); 34 | expect(onChange).toHaveBeenCalledTimes(1); 35 | }); 36 | 37 | it("Passes updated value, not event to onChange callback", () => { 38 | const onChange = jest.fn(); 39 | const { getByDisplayValue } = render( 40 | 41 | ); 42 | fireEvent.change(getByDisplayValue("Seltzer"), { 43 | target: { value: "Boring Water" } 44 | }); 45 | expect(onChange).toHaveBeenCalledWith("Boring Water"); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /blocks/three-image-block/Edit.test.js: -------------------------------------------------------------------------------- 1 | //Import React 2 | import React from "react"; 3 | //Import test renderer 4 | import { render, fireEvent, cleanup } from "@testing-library/react"; 5 | //Import component to test 6 | import { Editor } from "./Edit"; 7 | 8 | describe("Editor componet", () => { 9 | afterEach(cleanup); 10 | it("matches snapshot when selected", () => { 11 | const onChange = jest.fn(); 12 | const { container } = render( 13 | 14 | ); 15 | expect(container).toMatchSnapshot(); 16 | }); 17 | 18 | it("matches snapshot when not selected", () => { 19 | const onChange = jest.fn(); 20 | const { container } = render( 21 | 22 | ); 23 | expect(container).toMatchSnapshot(); 24 | }); 25 | 26 | it("Calls the onchange function", () => { 27 | const onChange = jest.fn(); 28 | const { getByDisplayValue } = render( 29 | 30 | ); 31 | fireEvent.change(getByDisplayValue("Salad"), { 32 | target: { value: "New Value" } 33 | }); 34 | expect(onChange).toHaveBeenCalledTimes(1); 35 | }); 36 | 37 | it("Passes updated value, not event to onChange callback", () => { 38 | const onChange = jest.fn(); 39 | const { getByDisplayValue } = render( 40 | 41 | ); 42 | fireEvent.change(getByDisplayValue("Seltzer"), { 43 | target: { value: "Boring Water" } 44 | }); 45 | expect(onChange).toHaveBeenCalledWith("Boring Water"); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /blocks/three-light-block/Edit.test.js: -------------------------------------------------------------------------------- 1 | //Import React 2 | import React from "react"; 3 | //Import test renderer 4 | import { render, fireEvent, cleanup } from "@testing-library/react"; 5 | //Import component to test 6 | import { Editor } from "./Edit"; 7 | 8 | describe("Editor componet", () => { 9 | afterEach(cleanup); 10 | it("matches snapshot when selected", () => { 11 | const onChange = jest.fn(); 12 | const { container } = render( 13 | 14 | ); 15 | expect(container).toMatchSnapshot(); 16 | }); 17 | 18 | it("matches snapshot when not selected", () => { 19 | const onChange = jest.fn(); 20 | const { container } = render( 21 | 22 | ); 23 | expect(container).toMatchSnapshot(); 24 | }); 25 | 26 | it("Calls the onchange function", () => { 27 | const onChange = jest.fn(); 28 | const { getByDisplayValue } = render( 29 | 30 | ); 31 | fireEvent.change(getByDisplayValue("Salad"), { 32 | target: { value: "New Value" } 33 | }); 34 | expect(onChange).toHaveBeenCalledTimes(1); 35 | }); 36 | 37 | it("Passes updated value, not event to onChange callback", () => { 38 | const onChange = jest.fn(); 39 | const { getByDisplayValue } = render( 40 | 41 | ); 42 | fireEvent.change(getByDisplayValue("Seltzer"), { 43 | target: { value: "Boring Water" } 44 | }); 45 | expect(onChange).toHaveBeenCalledWith("Boring Water"); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /blocks/three-text-block/Edit.test.js: -------------------------------------------------------------------------------- 1 | //Import React 2 | import React from "react"; 3 | //Import test renderer 4 | import { render, fireEvent, cleanup } from "@testing-library/react"; 5 | //Import component to test 6 | import { Editor } from "./Edit"; 7 | 8 | describe("Editor componet", () => { 9 | afterEach(cleanup); 10 | it("matches snapshot when selected", () => { 11 | const onChange = jest.fn(); 12 | const { container } = render( 13 | 14 | ); 15 | expect(container).toMatchSnapshot(); 16 | }); 17 | 18 | it("matches snapshot when not selected", () => { 19 | const onChange = jest.fn(); 20 | const { container } = render( 21 | 22 | ); 23 | expect(container).toMatchSnapshot(); 24 | }); 25 | 26 | it("Calls the onchange function", () => { 27 | const onChange = jest.fn(); 28 | const { getByDisplayValue } = render( 29 | 30 | ); 31 | fireEvent.change(getByDisplayValue("Salad"), { 32 | target: { value: "New Value" } 33 | }); 34 | expect(onChange).toHaveBeenCalledTimes(1); 35 | }); 36 | 37 | it("Passes updated value, not event to onChange callback", () => { 38 | const onChange = jest.fn(); 39 | const { getByDisplayValue } = render( 40 | 41 | ); 42 | fireEvent.change(getByDisplayValue("Seltzer"), { 43 | target: { value: "Boring Water" } 44 | }); 45 | expect(onChange).toHaveBeenCalledWith("Boring Water"); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /blocks/three-video-block/Edit.test.js: -------------------------------------------------------------------------------- 1 | //Import React 2 | import React from "react"; 3 | //Import test renderer 4 | import { render, fireEvent, cleanup } from "@testing-library/react"; 5 | //Import component to test 6 | import { Editor } from "./Edit"; 7 | 8 | describe("Editor componet", () => { 9 | afterEach(cleanup); 10 | it("matches snapshot when selected", () => { 11 | const onChange = jest.fn(); 12 | const { container } = render( 13 | 14 | ); 15 | expect(container).toMatchSnapshot(); 16 | }); 17 | 18 | it("matches snapshot when not selected", () => { 19 | const onChange = jest.fn(); 20 | const { container } = render( 21 | 22 | ); 23 | expect(container).toMatchSnapshot(); 24 | }); 25 | 26 | it("Calls the onchange function", () => { 27 | const onChange = jest.fn(); 28 | const { getByDisplayValue } = render( 29 | 30 | ); 31 | fireEvent.change(getByDisplayValue("Salad"), { 32 | target: { value: "New Value" } 33 | }); 34 | expect(onChange).toHaveBeenCalledTimes(1); 35 | }); 36 | 37 | it("Passes updated value, not event to onChange callback", () => { 38 | const onChange = jest.fn(); 39 | const { getByDisplayValue } = render( 40 | 41 | ); 42 | fireEvent.change(getByDisplayValue("Seltzer"), { 43 | target: { value: "Boring Water" } 44 | }); 45 | expect(onChange).toHaveBeenCalledWith("Boring Water"); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /blocks/three-portal-block/Edit.test.js: -------------------------------------------------------------------------------- 1 | //Import React 2 | import React from "react"; 3 | //Import test renderer 4 | import { render, fireEvent, cleanup } from "@testing-library/react"; 5 | //Import component to test 6 | import { Editor } from "./Edit"; 7 | 8 | describe("Editor componet", () => { 9 | afterEach(cleanup); 10 | it("matches snapshot when selected", () => { 11 | const onChange = jest.fn(); 12 | const { container } = render( 13 | 14 | ); 15 | expect(container).toMatchSnapshot(); 16 | }); 17 | 18 | it("matches snapshot when not selected", () => { 19 | const onChange = jest.fn(); 20 | const { container } = render( 21 | 22 | ); 23 | expect(container).toMatchSnapshot(); 24 | }); 25 | 26 | it("Calls the onchange function", () => { 27 | const onChange = jest.fn(); 28 | const { getByDisplayValue } = render( 29 | 30 | ); 31 | fireEvent.change(getByDisplayValue("Salad"), { 32 | target: { value: "New Value" } 33 | }); 34 | expect(onChange).toHaveBeenCalledTimes(1); 35 | }); 36 | 37 | it("Passes updated value, not event to onChange callback", () => { 38 | const onChange = jest.fn(); 39 | const { getByDisplayValue } = render( 40 | 41 | ); 42 | fireEvent.change(getByDisplayValue("Seltzer"), { 43 | target: { value: "Boring Water" } 44 | }); 45 | expect(onChange).toHaveBeenCalledWith("Boring Water"); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /blocks/environment/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-object-viewer/environment", 3 | "attributes": { 4 | "align": { 5 | "type": "string", 6 | "default": "full" 7 | }, 8 | "scale": { 9 | "type": "integer", 10 | "default": 1 11 | }, 12 | "positionX": { 13 | "type": "integer", 14 | "default": 0 15 | }, 16 | "positionY": { 17 | "type": "integer", 18 | "default": 0 19 | }, 20 | "rotationY": { 21 | "type": "integer", 22 | "default": 0 23 | }, 24 | "threeObjectUrl": { 25 | "type": "string", 26 | "default": null 27 | }, 28 | "threePreviewImage": { 29 | "type": "string", 30 | "default": null 31 | }, 32 | "hdr": { 33 | "type": "string", 34 | "default": null 35 | }, 36 | "deviceTarget": { 37 | "type": "string", 38 | "default": "vr" 39 | }, 40 | "animations": { 41 | "type": "string", 42 | "default": "" 43 | } 44 | }, 45 | "category": "design", 46 | "apiVersion": 2, 47 | "supports": { 48 | "html": false, 49 | "multiple": false, 50 | "hasOverlay": false, 51 | "align": ["full"] 52 | }, 53 | "textdomain": "three-object-viewer", 54 | "editorScript": "file:../../build/block-environment.js", 55 | "editorStyle": "file:../../build/block-environment.css", 56 | "style": "file:../../build/block-environment.css" 57 | } 58 | -------------------------------------------------------------------------------- /pluginMachine.json: -------------------------------------------------------------------------------- 1 | { 2 | "slug": "three-object-viewer", 3 | "pluginId": 166, 4 | "buildId": 172, 5 | "entryPoints": { 6 | "adminPages": [ 7 | "three-object-viewer-settings" 8 | ], 9 | "proAdminPages": [ 10 | "three-object-viewer-pro-settings" 11 | ], 12 | "blocks": [ 13 | "three-object-block", 14 | "environment", 15 | "model-block", 16 | "npc-block", 17 | "sky-block", 18 | "three-image-block", 19 | "three-video-block", 20 | "three-audio-block", 21 | "three-light-block", 22 | "three-portal-block", 23 | "three-text-block", 24 | "spawn-point-block" 25 | ], 26 | "proBlocks": [ 27 | "three-mirror-block" 28 | ] 29 | }, 30 | "buildIncludes": [ 31 | "three-object-viewer.php", 32 | "readme.txt", 33 | "php", 34 | "vendor", 35 | "build", 36 | "blocks/three-object-block/init.php", 37 | "blocks/environment/init.php", 38 | "blocks/model-block/init.php", 39 | "blocks/npc-block/init.php", 40 | "blocks/sky-block/init.php", 41 | "blocks/three-text-block/init.php", 42 | "blocks/three-image-block/init.php", 43 | "blocks/three-video-block/init.php", 44 | "blocks/three-portal-block/init.php", 45 | "blocks/spawn-point-block/init.php", 46 | "blocks/three-mirror-block/init.php", 47 | "admin/three-object-viewer-settings/init.php", 48 | "pro/admin/three-object-viewer-pro-settings/init.php" 49 | ] 50 | } -------------------------------------------------------------------------------- /blocks/three-video-block/Save.js: -------------------------------------------------------------------------------- 1 | import { __ } from "@wordpress/i18n"; 2 | import { useBlockProps } from "@wordpress/block-editor"; 3 | 4 | export default function save({ attributes }) { 5 | return ( 6 |
7 | <> 8 |
9 |
{attributes.videoUrl}
10 |

{attributes.scaleX}

11 |

{attributes.scaleY}

12 |

{attributes.scaleZ}

13 |

14 | {attributes.positionX} 15 |

16 |

17 | {attributes.positionY} 18 |

19 |

20 | {attributes.positionZ} 21 |

22 |

23 | {attributes.rotationX} 24 |

25 |

26 | {attributes.rotationY} 27 |

28 |

29 | {attributes.rotationZ} 30 |

31 |

32 | {attributes.autoPlay ? 1 : 0} 33 |

34 |

35 | {attributes.customModel ? 1 : 0} 36 |

37 |

38 | {attributes.aspectHeight} 39 |

40 |

41 | {attributes.aspectWidth} 42 |

43 |
{attributes.modelUrl}
44 |
45 | 46 |
47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /languages/three-object-viewer-es_MX-three-object-viewer-three-portal-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:28-0500","generator":"WP-CLI\/2.8.1","source":"blocks\/three-portal-block\/Edit.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"es_MX","plural-forms":"nplurals=2; plural=(n != 1);"},"Loop Animations":["Repetir Animaciones"],"Separate each animation name you wish to loop with a comma":["Separa cada nombre de animaci\u00f3n que deseas repetir con una coma"],"Scale":["Escala"],"Settings":["Ajustes"],"GLB Object":["Objeto GLB"],"Replace Object":["Reemplazar Objeto"],"Select Object":["Seleccionar Objeto"],"Model Attributes":["Atributos del Modelo"],"Collidable":["Colisionable"],"Item is currently collidable.":["El elemento es actualmente colisionable."],"Item is not collidable. Users will walk through it.":["El elemento no es colisionable. Los usuarios podr\u00e1n atravesarlo."],"Rotation":["Rotaci\u00f3n"],"Select a glb file from your media library to render an object in the canvas:":["Selecciona un archivo glb de tu biblioteca de medios para renderizar un objeto en el lienzo:"],"Destination URL":["URL de Destino"],"Define a url.":["Define una URL."],"Link Label":["Etiqueta del Enlace"],"This text will describe where the link goes. If blank, will use the url as the label.":["Este texto describir\u00e1 a d\u00f3nde lleva el enlace. Si est\u00e1 en blanco, se usar\u00e1 la URL como etiqueta."],"Text Color":["Color de Texto"],"Label Offset":["Desplazamiento de Etiqueta"],"X Offset":["Desplazamiento de X"],"Y Offset":["Desplazamiento de Y"],"Z Offset":["Desplazamiento de Z"],"Portal block":["Bloque de Portal"],"Select Portal":["Seleccionar Portal"]}}} -------------------------------------------------------------------------------- /blocks/three-object-block/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-object-viewer/three-object-block", 3 | "title": "Three Object Block", 4 | "description": "A 3D object viewer focused on glTF", 5 | "attributes": { 6 | "bg_color": { 7 | "type": "string", 8 | "default": "#FFFFFF" 9 | }, 10 | "zoom": { 11 | "type": "integer", 12 | "default": 1 13 | }, 14 | "scale": { 15 | "type": "integer", 16 | "default": 1 17 | }, 18 | "positionX": { 19 | "type": "integer", 20 | "default": 0 21 | }, 22 | "positionY": { 23 | "type": "integer", 24 | "default": 0 25 | }, 26 | "rotationY": { 27 | "type": "integer", 28 | "default": 0 29 | }, 30 | "threeObjectUrl": { 31 | "type": "string", 32 | "default": null 33 | }, 34 | "hasZoom": { 35 | "type": "bool", 36 | "default": false 37 | }, 38 | "hasTip": { 39 | "type": "bool", 40 | "default": true 41 | }, 42 | "deviceTarget": { 43 | "type": "string", 44 | "default": "2d" 45 | }, 46 | "animations": { 47 | "type": "string", 48 | "default": "" 49 | } 50 | }, 51 | "category": "media", 52 | "apiVersion": 2, 53 | "supports": { 54 | "html": false, 55 | "multiple": true 56 | }, 57 | "editorScript": ["file:../../build/block-three-object-block.js"], 58 | "editorStyle": "file:../../build/block-three-object-block.css", 59 | "style": "file:../../build/block-three-object-block.css" 60 | } 61 | -------------------------------------------------------------------------------- /blocks/three-light-block/Save.js: -------------------------------------------------------------------------------- 1 | import { __ } from "@wordpress/i18n"; 2 | import { useBlockProps } from "@wordpress/block-editor"; 3 | 4 | export default function save({ attributes }) { 5 | return ( 6 |
7 | <> 8 |
9 |

10 | {attributes.positionX} 11 |

12 |

13 | {attributes.positionY} 14 |

15 |

16 | {attributes.positionZ} 17 |

18 |

19 | {attributes.rotationX} 20 |

21 |

22 | {attributes.rotationY} 23 |

24 |

25 | {attributes.rotationZ} 26 |

27 |

28 | {attributes.type} 29 |

30 |

31 | {attributes.color} 32 |

33 |

34 | {attributes.intensity} 35 |

36 |

37 | {attributes.distance} 38 |

39 |

40 | {attributes.decay} 41 |

42 |

43 | {attributes.targetX} 44 |

45 |

46 | {attributes.targetY} 47 |

48 |

49 | {attributes.targetZ} 50 |

51 |

52 | {attributes.angle} 53 |

54 |

55 | {attributes.penumbra} 56 |

57 |
58 | 59 |
60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /languages/three-object-viewer-ja-three-object-viewer-three-video-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:23-0500","generator":"WP-CLI\/2.8.1","source":"blocks\/three-video-block\/Edit.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"ja","plural-forms":"nplurals=2; plural=(n != 1);"},"Scale":["\u30b9\u30b1\u30fc\u30eb"],"Settings":["\u8a2d\u5b9a"],"Replace Object":["\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u7f6e\u304d\u63db\u3048\u308b"],"Rotation":["\u56de\u8ee2"],"Item will autoplay.":["\u30a2\u30a4\u30c6\u30e0\u306f\u81ea\u52d5\u518d\u751f\u3055\u308c\u307e\u3059\u3002"],"Item will not autoplay.":["\u30a2\u30a4\u30c6\u30e0\u306f\u81ea\u52d5\u518d\u751f\u3055\u308c\u307e\u305b\u3093\u3002"],"Select an image to render in your environment:":["\u74b0\u5883\u5185\u306b\u30ec\u30f3\u30c0\u30ea\u30f3\u30b0\u3059\u308b\u753b\u50cf\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002"],"Video File":["\u30d3\u30c7\u30aa\u30d5\u30a1\u30a4\u30eb"],"Replace Video":["\u30d3\u30c7\u30aa\u3092\u7f6e\u304d\u63db\u3048\u308b"],"Select Video":["\u30d3\u30c7\u30aa\u3092\u9078\u629e\u3059\u308b"],"Custom Model":["\u30ab\u30b9\u30bf\u30e0\u30e2\u30c7\u30eb"],"Item will use a custom model. Name your faces 'screen'.":["\u30a2\u30a4\u30c6\u30e0\u306f\u30ab\u30b9\u30bf\u30e0\u30e2\u30c7\u30eb\u3092\u4f7f\u7528\u3057\u307e\u3059\u3002\u30d5\u30a7\u30fc\u30b9\u3092 \u2018screen\u2019 \u3068\u540d\u524d\u3092\u4ed8\u3051\u3066\u304f\u3060\u3055\u3044\u3002"],"Item will not use a custom model.":["\u30a2\u30a4\u30c6\u30e0\u306f\u30ab\u30b9\u30bf\u30e0\u30e2\u30c7\u30eb\u3092\u4f7f\u7528\u3057\u307e\u305b\u3093\u3002"],"Replace model":["\u30e2\u30c7\u30eb\u3092\u7f6e\u304d\u63db\u3048\u308b"],"Select model":["\u30e2\u30c7\u30eb\u3092\u9078\u629e\u3059\u308b"],"Video block":["\u30d3\u30c7\u30aa\u30d6\u30ed\u30c3\u30af"]}}} -------------------------------------------------------------------------------- /blocks/three-image-block/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-object-viewer/three-image-block", 3 | "attributes": { 4 | "imageUrl": { 5 | "type": "string", 6 | "default": null 7 | }, 8 | "transparent": { 9 | "type": "boolean", 10 | "default": false 11 | }, 12 | "scaleX": { 13 | "type": "int", 14 | "default":1 15 | }, 16 | "scaleY": { 17 | "type": "int", 18 | "default":1 19 | }, 20 | "scaleZ": { 21 | "type": "int", 22 | "default":1 23 | }, 24 | "positionX": { 25 | "type": "int", 26 | "default":0 27 | }, 28 | "positionY": { 29 | "type": "int", 30 | "default":0 31 | }, 32 | "positionZ": { 33 | "type": "int", 34 | "default":0 35 | }, 36 | "rotationX": { 37 | "type": "int", 38 | "default":0 39 | }, 40 | "rotationY": { 41 | "type": "int", 42 | "default":0 43 | }, 44 | "rotationZ": { 45 | "type": "int", 46 | "default":0 47 | }, 48 | "aspectHeight": { 49 | "type": "int", 50 | "default":0 51 | }, 52 | "aspectWidth": { 53 | "type": "int", 54 | "default":0 55 | } 56 | }, 57 | "category": "design", 58 | "parent": [ "three-object-viewer/environment" ], 59 | "apiVersion": 2, 60 | "supports": { 61 | "html": false, 62 | "multiple": true 63 | }, 64 | "textdomain": "three-object-viewer", 65 | "editorScript": "file:../../build/block-three-image-block.js", 66 | "editorStyle": "file:../../build/block-three-image-block.css", 67 | "style": "file:../../build/block-three-image-block.css" 68 | } 69 | -------------------------------------------------------------------------------- /blocks/model-block/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-object-viewer/model-block", 3 | "attributes": { 4 | "scaleX": { 5 | "type": "int", 6 | "default":1 7 | }, 8 | "name": { 9 | "type": "string" 10 | }, 11 | "scaleY": { 12 | "type": "int", 13 | "default":1 14 | }, 15 | "scaleZ": { 16 | "type": "int", 17 | "default":1 18 | }, 19 | "positionX": { 20 | "type": "int", 21 | "default":0 22 | }, 23 | "positionY": { 24 | "type": "int", 25 | "default":0 26 | }, 27 | "positionZ": { 28 | "type": "int", 29 | "default":0 30 | }, 31 | "rotationX": { 32 | "type": "int", 33 | "default":0 34 | }, 35 | "rotationY": { 36 | "type": "int", 37 | "default":0 38 | }, 39 | "rotationZ": { 40 | "type": "int", 41 | "default":0 42 | }, 43 | "threeObjectUrl": { 44 | "type": "string", 45 | "default": null 46 | }, 47 | "animations": { 48 | "type": "string", 49 | "default": "" 50 | }, 51 | "alt": { 52 | "type": "string", 53 | "default": "" 54 | }, 55 | "collidable": { 56 | "type": "boolean", 57 | "default": true 58 | } 59 | }, 60 | "category": "design", 61 | "parent": [ "three-object-viewer/environment" ], 62 | "apiVersion": 2, 63 | "supports": { 64 | "html": false, 65 | "multiple": true 66 | }, 67 | "textdomain": "three-object-viewer", 68 | "editorScript": "file:../../build/block-model-block.js", 69 | "editorStyle": "file:../../build/block-model-block.css", 70 | "style": "file:../../build/block-model-block.css" 71 | } 72 | -------------------------------------------------------------------------------- /blocks/three-light-block/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-object-viewer/three-light-block", 3 | "attributes": { 4 | "type": { 5 | "type": "string", 6 | "default": "ambient" 7 | }, 8 | "color": { 9 | "type": "string", 10 | "default": "0xffffff" 11 | }, 12 | "intensity": { 13 | "type": "float", 14 | "default": 0.7 15 | }, 16 | "distance": { 17 | "type": "int", 18 | "default": 100 19 | }, 20 | "decay": { 21 | "type": "int", 22 | "default": 1 23 | }, 24 | "positionX": { 25 | "type": "float", 26 | "default": 0 27 | }, 28 | "positionY": { 29 | "type": "float", 30 | "default": 0 31 | }, 32 | "positionZ": { 33 | "type": "float", 34 | "default": 0 35 | }, 36 | "rotationX": { 37 | "type": "float", 38 | "default": 0 39 | }, 40 | "rotationY": { 41 | "type": "float", 42 | "default": 0 43 | }, 44 | "rotationZ": { 45 | "type": "float", 46 | "default": 0 47 | }, 48 | "angle": { 49 | "type": "float", 50 | "default": 0.78539816339 51 | }, 52 | "penumbra": { 53 | "type": "float", 54 | "default": 0.1 55 | } 56 | }, 57 | "category": "design", 58 | "parent": [ "three-object-viewer/environment" ], 59 | "apiVersion": 2, 60 | "supports": { 61 | "html": false, 62 | "multiple": true 63 | }, 64 | "textdomain": "three-object-viewer", 65 | "editorScript": "file:../../build/block-three-light-block.js", 66 | "editorStyle": "file:../../build/block-three-light-block.css", 67 | "style": "file:../../build/block-three-light-block.css" 68 | } 69 | -------------------------------------------------------------------------------- /blocks/environment/components/core/front/ThreeSky.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useLoader } from "@react-three/fiber"; 3 | import { TextureLoader, DoubleSide } from "three"; 4 | import { 5 | Sky 6 | } from "@react-three/drei"; 7 | 8 | /** 9 | * Represents a sky in a virtual reality scene. 10 | * 11 | * @param {Object} sky - The props for the sky. 12 | * 13 | * @return {JSX.Element} The sky. 14 | */ 15 | export function ThreeSky(sky) { 16 | const skyUrl = sky.src[0].querySelector("p.sky-block-url") 17 | ? sky.src[0].querySelector("p.sky-block-url").innerText 18 | : ""; 19 | 20 | const distance = sky.src[0].querySelector("p.sky-block-distance") 21 | ? sky.src[0].querySelector("p.sky-block-distance").innerText 22 | : ""; 23 | 24 | const rayleigh = sky.src[0].querySelector("p.sky-block-rayleigh") 25 | ? sky.src[0].querySelector("p.sky-block-rayleigh").innerText 26 | : ""; 27 | 28 | const sunPositionX = sky.src[0].querySelector("p.sky-block-sunPositionX") 29 | ? sky.src[0].querySelector("p.sky-block-sunPositionX").innerText 30 | : ""; 31 | 32 | const sunPositionY = sky.src[0].querySelector("p.sky-block-sunPositionY") 33 | ? sky.src[0].querySelector("p.sky-block-sunPositionY").innerText 34 | : ""; 35 | 36 | const sunPositionZ = sky.src[0].querySelector("p.sky-block-sunPositionZ") 37 | ? sky.src[0].querySelector("p.sky-block-sunPositionZ").innerText 38 | : ""; 39 | 40 | if(skyUrl === "" || skyUrl === undefined || skyUrl === null) { 41 | return ( 42 | 47 | ); 48 | } else { 49 | const texture1 = useLoader(TextureLoader, skyUrl); 50 | return ( 51 | <> 52 | 58 | 59 | 60 | 61 | 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /.github/workflows/wordpress.yml: -------------------------------------------------------------------------------- 1 | name: WordPress Tests 2 | 3 | on: [push] 4 | 5 | env: 6 | WP_TESTS_DIR: /github/home/wp-tests/wordpress-tests-lib 7 | WP_CORE_DIR: /github/home/wp-tests/wordpress 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | php-version: [7.2, 7.3, 7.4] 15 | wordpress-version: [latest] 16 | container: 17 | image: junaidbhura/wp-tests:php-${{ matrix.php-version }} 18 | services: 19 | mysql: 20 | image: mysql:5.7.27 21 | options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 22 | env: 23 | MYSQL_ROOT_PASSWORD: root 24 | 25 | steps: 26 | # Setup 27 | - name: Checkout repository 28 | uses: actions/checkout@v1 29 | 30 | ## Install 31 | - name: Get Composer Cache Directory 32 | id: get-composer-cache-dir # Instead of composer-cache 33 | run: | 34 | echo "::set-output name=dir::$(composer config cache-files-dir)" 35 | 36 | - name: Cache Composer packages 37 | id: composer-cache 38 | uses: actions/cache@v2 39 | with: 40 | path: vendor 41 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 42 | restore-keys: | 43 | ${{ runner.os }}-php- 44 | 45 | - name: Install dependencies 46 | if: steps.composer-cache.outputs.cache-hit != 'true' 47 | run: composer install --prefer-dist --no-progress 48 | 49 | ## Install test suite 50 | - name: Install WordPress test suite 51 | run: bash bin/install-wp-tests.sh wordpress_test root root mysql ${{ matrix.wordpress-version }} 52 | 53 | ## Run integration tests 54 | - name: Tests 55 | run: composer test:wordpress 56 | -------------------------------------------------------------------------------- /languages/three-object-viewer-ja-three-object-viewer-npc-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:23-0500","generator":"WP-CLI\/2.8.1","source":"blocks\/npc-block\/Edit.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"ja","plural-forms":"nplurals=2; plural=(n != 1);"},"Settings":["\u8a2d\u5b9a"],"Name":["\u540d\u524d"],"Position":["\u4f4d\u7f6e"],"Rotation":["\u56de\u8ee2"],"NPC Options":["NPC\u30aa\u30d7\u30b7\u30e7\u30f3"],"Give your avatar a name.":["\u30a2\u30d0\u30bf\u30fc\u306b\u540d\u524d\u3092\u4ed8\u3051\u3066\u304f\u3060\u3055\u3044\u3002"],"Personality":["\u6027\u683c"],"Give your avatar a personality in 600 characters.":["600\u6587\u5b57\u3067\u30a2\u30d0\u30bf\u30fc\u306b\u6027\u683c\u3092\u4e0e\u3048\u3066\u304f\u3060\u3055\u3044\u3002"],"Default Message":["\u30c7\u30d5\u30a9\u30eb\u30c8\u30e1\u30c3\u30bb\u30fc\u30b8"],"Give your avatar a default message to initialize with.":["\u30a2\u30d0\u30bf\u30fc\u306b\u521d\u671f\u5316\u6642\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u8a2d\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002"],"Select a VRM file to be used as your NPC in world:":["\u30ef\u30fc\u30eb\u30c9\u5185\u3067NPC\u3068\u3057\u3066\u4f7f\u7528\u3059\u308b\u305f\u3081\u306eVRM\u30d5\u30a1\u30a4\u30eb\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002"],"Advanced Settings":["\u9ad8\u5ea6\u306a\u8a2d\u5b9a"],"Include World Objects in Prompt":["\u30d7\u30ed\u30f3\u30d7\u30c8\u306b\u30ef\u30fc\u30eb\u30c9\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u542b\u3081\u308b"],"If enabled, will result in higher token costs with your AI service provider. Use with caution.":["\u6709\u52b9\u306b\u3059\u308b\u3068\u3001AI\u30b5\u30fc\u30d3\u30b9\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u306e\u30c8\u30fc\u30af\u30f3\u30b3\u30b9\u30c8\u304c\u5897\u52a0\u3059\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u6ce8\u610f\u3057\u3066\u4f7f\u7528\u3057\u3066\u304f\u3060\u3055\u3044\u3002"],"NPC block":["NPC\u30d6\u30ed\u30c3\u30af"],"Replace NPC":["NPC\u3092\u7f6e\u304d\u63db\u3048\u308b"],"Select NPC":["NPC\u3092\u9078\u629e\u3059\u308b"]}}} -------------------------------------------------------------------------------- /blocks/three-portal-block/Save.js: -------------------------------------------------------------------------------- 1 | import { __ } from "@wordpress/i18n"; 2 | import { useBlockProps } from "@wordpress/block-editor"; 3 | 4 | export default function save({ attributes }) { 5 | return ( 6 |
7 | <> 8 |
9 |

10 | {attributes.threeObjectUrl} 11 |

12 |

13 | {attributes.destinationUrl} 14 |

15 |

16 | {attributes.scaleX} 17 |

18 |

19 | {attributes.scaleY} 20 |

21 |

22 | {attributes.scaleZ} 23 |

24 |

25 | {attributes.positionX} 26 |

27 |

28 | {attributes.positionY} 29 |

30 |

31 | {attributes.positionZ} 32 |

33 |

34 | {attributes.rotationX} 35 |

36 |

37 | {attributes.rotationY} 38 |

39 |

40 | {attributes.rotationZ} 41 |

42 |

43 | {attributes.animations} 44 |

45 |

46 | {attributes.label} 47 |

48 |

49 | {attributes.labelOffsetX} 50 |

51 |

52 | {attributes.labelOffsetY} 53 |

54 |

55 | {attributes.labelOffsetZ} 56 |

57 |

58 | {attributes.labelTextColor} 59 |

60 |
61 | 62 |
63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /blocks/three-video-block/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-object-viewer/three-video-block", 3 | "attributes": { 4 | "videoUrl": { 5 | "type": "string", 6 | "default": null 7 | }, 8 | "modelUrl": { 9 | "type": "string", 10 | "default": null 11 | }, 12 | "autoPlay": { 13 | "type": "bool", 14 | "default": true 15 | }, 16 | "scaleX": { 17 | "type": "int", 18 | "default":1 19 | }, 20 | "scaleY": { 21 | "type": "int", 22 | "default":1 23 | }, 24 | "scaleZ": { 25 | "type": "int", 26 | "default":1 27 | }, 28 | "positionX": { 29 | "type": "int", 30 | "default":0 31 | }, 32 | "positionY": { 33 | "type": "int", 34 | "default":0 35 | }, 36 | "positionZ": { 37 | "type": "int", 38 | "default":0 39 | }, 40 | "rotationX": { 41 | "type": "int", 42 | "default":0 43 | }, 44 | "rotationY": { 45 | "type": "int", 46 | "default":0 47 | }, 48 | "rotationZ": { 49 | "type": "int", 50 | "default":0 51 | }, 52 | "aspectHeight": { 53 | "type": "int", 54 | "default":0 55 | }, 56 | "aspectWidth": { 57 | "type": "int", 58 | "default":0 59 | }, 60 | "customModel": { 61 | "type": "bool", 62 | "default": false 63 | } 64 | }, 65 | "category": "design", 66 | "parent": [ "three-object-viewer/environment" ], 67 | "apiVersion": 2, 68 | "supports": { 69 | "html": false, 70 | "multiple": true 71 | }, 72 | "textdomain": "three-object-viewer", 73 | "editorScript": "file:../../build/block-three-video-block.js", 74 | "editorStyle": "file:../../build/block-three-video-block.css", 75 | "style": "file:../../build/block-three-video-block.css" 76 | } 77 | -------------------------------------------------------------------------------- /blocks/three-text-block/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-object-viewer/three-text-block", 3 | "attributes": { 4 | "scaleX": { 5 | "type": "int", 6 | "default":1 7 | }, 8 | "scaleY": { 9 | "type": "int", 10 | "default":1 11 | }, 12 | "scaleZ": { 13 | "type": "int", 14 | "default":1 15 | }, 16 | "positionX": { 17 | "type": "int", 18 | "default":0 19 | }, 20 | "positionY": { 21 | "type": "int", 22 | "default":0 23 | }, 24 | "positionZ": { 25 | "type": "int", 26 | "default":0 27 | }, 28 | "rotationX": { 29 | "type": "int", 30 | "default":0 31 | }, 32 | "rotationY": { 33 | "type": "int", 34 | "default":0 35 | }, 36 | "rotationZ": { 37 | "type": "int", 38 | "default":0 39 | }, 40 | "threeObjectUrl": { 41 | "type": "string", 42 | "default": null 43 | }, 44 | "destinationUrl": { 45 | "type": "string", 46 | "default": null 47 | }, 48 | "textContent": { 49 | "type": "string", 50 | "default": null 51 | }, 52 | "textColor": { 53 | "type": "string", 54 | "default": "0x000000" 55 | }, 56 | "animations": { 57 | "type": "string", 58 | "default": "" 59 | }, 60 | "collidable": { 61 | "type": "boolean", 62 | "default": false 63 | } 64 | }, 65 | "category": "design", 66 | "parent": [ "three-object-viewer/environment" ], 67 | "apiVersion": 2, 68 | "supports": { 69 | "html": false, 70 | "multiple": true 71 | }, 72 | "textdomain": "three-object-viewer", 73 | "editorScript": "file:../../build/block-three-text-block.js", 74 | "editorStyle": "file:../../build/block-three-text-block.css", 75 | "style": "file:../../build/block-three-text-block.css" 76 | } 77 | -------------------------------------------------------------------------------- /languages/three-object-viewer-ja-three-object-viewer-environment-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:23-0500","generator":"WP-CLI\/2.8.1","source":"build\/block-environment.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"ja","plural-forms":"nplurals=2; plural=(n != 1);"},"Environment Settings":["\u74b0\u5883\u8a2d\u5b9a"],"Environment Object (Changing this value changes your scene ground planes)":["\u74b0\u5883\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\uff08\u3053\u306e\u5024\u3092\u5909\u66f4\u3059\u308b\u3068\u3001\u30b7\u30fc\u30f3\u306e\u5730\u9762\u304c\u5909\u308f\u308a\u307e\u3059\uff09"],"Select a glb file from your media library. This will be treated as a collidable mesh that visitors can walk on:":["\u30e1\u30c7\u30a3\u30a2\u30e9\u30a4\u30d6\u30e9\u30ea\u304b\u3089glb\u30d5\u30a1\u30a4\u30eb\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u3053\u308c\u306f\u8a2a\u554f\u8005\u304c\u6b69\u884c\u3067\u304d\u308b\u885d\u7a81\u30e1\u30c3\u30b7\u30e5\u3068\u3057\u3066\u6271\u308f\u308c\u307e\u3059\u3002"],"Replace Environment":["\u74b0\u5883\u3092\u7f6e\u304d\u63db\u3048\u308b"],"Select Environment":["\u74b0\u5883\u3092\u9078\u629e\u3059\u308b"],"Select an image to be used as the preview image:":["\u30d7\u30ec\u30d3\u30e5\u30fc\u753b\u50cf\u3068\u3057\u3066\u4f7f\u7528\u3059\u308b\u753b\u50cf\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002"],"Replace Image":["\u753b\u50cf\u3092\u7f6e\u304d\u63db\u3048\u308b"],"Select Image":["\u753b\u50cf\u3092\u9078\u629e\u3059\u308b"],"Replace HDR":["HDR\u3092\u7f6e\u304d\u63db\u3048\u308b"],"Select HDR":["HDR\u3092\u9078\u629e\u3059\u308b"],"Remove HDR":["HDR\u3092\u524a\u9664\u3059\u308b"],"Scene Settings":["\u30b7\u30fc\u30f3\u8a2d\u5b9a"],"Object Display Type:":["\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u8868\u793a\u30bf\u30a4\u30d7\uff1a"],"Loop Animations":["\u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3\u3092\u30eb\u30fc\u30d7\u3059\u308b"],"Separate each animation name you wish to loop with a comma":["\u5404\u30eb\u30fc\u30d7\u3055\u305b\u305f\u3044\u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3\u540d\u3092\u30b3\u30f3\u30de\u3067\u533a\u5207\u3063\u3066\u304f\u3060\u3055\u3044\u3002"],"Scale":["\u30b9\u30b1\u30fc\u30eb"],"Position Y":["Y\u5ea7\u6a19"],"Rotation Y":["Y\u8ef8\u56de\u8ee2"]}}} -------------------------------------------------------------------------------- /blocks/three-audio-block/Save.js: -------------------------------------------------------------------------------- 1 | import { __ } from "@wordpress/i18n"; 2 | import { useBlockProps } from "@wordpress/block-editor"; 3 | 4 | export default function save({ attributes }) { 5 | return ( 6 |
7 | <> 8 |
9 |

{attributes.audioUrl}

10 |

{attributes.scaleX}

11 |

{attributes.scaleY}

12 |

{attributes.scaleZ}

13 |

14 | {attributes.positionX} 15 |

16 |

17 | {attributes.positionY} 18 |

19 |

20 | {attributes.positionZ} 21 |

22 |

23 | {attributes.rotationX} 24 |

25 |

26 | {attributes.rotationY} 27 |

28 |

29 | {attributes.rotationZ} 30 |

31 |

32 | {attributes.autoPlay ? '1' : '0'} 33 |

34 |

35 | {attributes.loop ? '1' : '0'} 36 |

37 |

38 | {attributes.volume} 39 |

40 |

41 | {attributes.positional ? '1' : '0'} 42 |

43 |

44 | {attributes.coneInnerAngle} 45 |

46 |

47 | {attributes.coneOuterAngle} 48 |

49 |

50 | {attributes.coneOuterGain} 51 |

52 |

53 | {attributes.distanceModel} 54 |

55 |

56 | {attributes.maxDistance} 57 |

58 |

59 | {attributes.refDistance} 60 |

61 |

62 | {attributes.rolloffFactor} 63 |

64 |
65 | 66 |
67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | 3 | services: 4 | 5 | wordpress: 6 | depends_on: 7 | wpdb: 8 | condition: service_healthy 9 | image: wordpress:latest 10 | volumes: 11 | - wordpress_data:/var/www/html 12 | - ./:/var/www/html/wp-content/plugins/three-object-viewer 13 | ports: 14 | - "6039:80" 15 | restart: always 16 | environment: 17 | WORDPRESS_DB_HOST: wpdb:3306 18 | WORDPRESS_DB_USER: wordpress 19 | WORDPRESS_DB_PASSWORD: wordpress 20 | WORDPRESS_DB_NAME: wordpress 21 | 22 | wpdb: 23 | image: mariadb:10.5.8 24 | volumes: 25 | - db_data:/var/lib/mysql 26 | restart: always 27 | environment: 28 | MYSQL_ROOT_PASSWORD: wordpress 29 | MYSQL_DATABASE: wordpress 30 | MYSQL_USER: wordpress 31 | MYSQL_PASSWORD: wordpress 32 | healthcheck: 33 | test: "/usr/bin/mysql --user=wordpress --password=wordpress --execute \"SHOW DATABASES;\"" 34 | interval: 3s 35 | timeout: 1s 36 | retries: 5 37 | 38 | wpcli: 39 | image: wordpress:cli 40 | depends_on: 41 | wpdb: 42 | condition: service_healthy 43 | volumes: 44 | - wordpress_data:/var/www/html 45 | - ./:/var/www/html/wp-content/plugins/three-object-viewer 46 | - ./db:/var/www/html/db 47 | environment: 48 | WORDPRESS_DB_HOST: wpdb:3306 49 | WORDPRESS_DB_USER: wordpress 50 | WORDPRESS_DB_PASSWORD: wordpress 51 | WORDPRESS_DB_NAME: wordpress 52 | ABSPATH: /usr/src/wordpress/ 53 | 54 | phpunit: 55 | image: futureys/phpunit-wordpress-plugin 56 | depends_on: 57 | testwpdb: 58 | condition: service_healthy 59 | command: 60 | - bash 61 | depends_on: 62 | - testwpdb 63 | environment: 64 | DATABASE_PASSWORD: examplepass 65 | DATABASE_HOST: testwpdb 66 | stdin_open: true 67 | tty: true 68 | volumes: 69 | - ./:/plugin 70 | 71 | testwpdb: 72 | environment: 73 | MYSQL_ROOT_PASSWORD: examplepass 74 | image: mariadb:10.5.8 75 | healthcheck: 76 | test: "/usr/bin/mysql --user=wordpress --password=wordpress --execute \"SHOW DATABASES;\"" 77 | interval: 3s 78 | timeout: 1s 79 | retries: 5 80 | volumes: 81 | db_data: {} 82 | wordpress_data: {} 83 | -------------------------------------------------------------------------------- /rename.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const HASH_to_file = [ 5 | { '51af751c2048f283b39a777e1a3fae1b': 'three-object-viewer-three-portal-block-editor-script' }, 6 | { 'bd2a3e7576d2e549fc22bfab35d48ca0': 'three-object-viewer-three-text-block-editor-script' }, 7 | { 'a302549a555228fc26253251cff757bd': 'three-object-viewer-model-block-editor-script' }, 8 | { 'a6625e41527a4ebb684bf747e8040213': 'three-object-viewer-three-audio-block-editor-script' }, 9 | { 'f38ba41c49ce087276f6fb65a04d09f5': 'three-object-viewer-three-light-block-editor-script' }, 10 | { '99e744b4fac1824cd9bd14f27bcce02b': 'three-object-viewer-npc-block-editor-script' }, 11 | { '1c9eb460996b98bed51790c80e90f705': 'three-object-viewer-sky-block-editor-script' }, 12 | { '760f26759af0cf80372c18dbbff3b8af': 'three-object-viewer-three-image-block-editor-script' }, 13 | { '2f1be97f0700f196acecdfec131ed2a4': 'three-object-viewer-three-video-block-editor-script' }, 14 | { 'db711d0e65eaf2e9518213c2fa3f74ff': 'three-object-viewer-spawn-point-block-editor-script' }, 15 | { '05c0f6b04d52130d2210e3b0b4859a63': 'three-object-viewer-environment-editor-script' }, 16 | { 'dd10ea5e2a0076c36967f4c3fdb50a0b': 'three-object-viewer-settings'} 17 | ]; 18 | 19 | const folderPath = 'languages'; 20 | 21 | fs.readdirSync(folderPath).forEach((file) => { 22 | const match = file.match(/three-object-viewer-es_MX-(.*).json/); 23 | 24 | if (match && match[1]) { 25 | const hash = match[1]; 26 | const mapping = HASH_to_file.find((obj) => Object.keys(obj)[0] === hash); 27 | 28 | if (mapping) { 29 | const newName = 'three-object-viewer-es_MX-' + Object.values(mapping)[0] + '.json'; 30 | fs.renameSync(path.join(folderPath, file), path.join(folderPath, newName)); 31 | } 32 | } 33 | }); 34 | 35 | fs.readdirSync(folderPath).forEach((file) => { 36 | const match = file.match(/three-object-viewer-ja-(.*).json/); 37 | 38 | if (match && match[1]) { 39 | const hash = match[1]; 40 | const mapping = HASH_to_file.find((obj) => Object.keys(obj)[0] === hash); 41 | 42 | if (mapping) { 43 | const newName = 'three-object-viewer-ja-' + Object.values(mapping)[0] + '.json'; 44 | fs.renameSync(path.join(folderPath, file), path.join(folderPath, newName)); 45 | } 46 | } 47 | }); 48 | 49 | -------------------------------------------------------------------------------- /blocks/environment/components/Controls.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | 3 | export function useKeyboardControls() { 4 | const movement = useRef({ 5 | forward: false, 6 | backward: false, 7 | left: false, 8 | right: false, 9 | shift: false, 10 | space: false 11 | }); 12 | 13 | useEffect(() => { 14 | const handleKeyDown = (e) => { 15 | let element = e.target; 16 | // if the element is an input, dont move 17 | if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') return; 18 | if (e.key === 'w' || e.key === 'W' && ! movement.current.forward) movement.current.forward = true; 19 | else if (e.key === 's' || e.key === 'S' && ! movement.current.backward) movement.current.backward = true; 20 | else if (e.key === 'a' || e.key === 'A' && ! movement.current.left) movement.current.left = true; 21 | else if (e.key === 'd' || e.key === 'D' && ! movement.current.right) movement.current.right = true; 22 | else if (e.key === 'space') movement.current.space = true; 23 | else if (e.key === 'Shift') movement.current.shift = true; 24 | else if (e.key === 'r' || e.key === 'R'){ 25 | if (e.metaKey || e.ctrlKey){ 26 | movement.current.respawn = false; 27 | } else { 28 | movement.current.respawn = true; 29 | } 30 | 31 | } 32 | 33 | } 34 | 35 | const handleKeyUp = (e) => { 36 | if (e.key === 'w' || e.key === 'W') movement.current.forward = false; 37 | else if (e.key === 's' || e.key === 'S') movement.current.backward = false; 38 | else if (e.key === 'a' || e.key === 'A') movement.current.left = false; 39 | else if (e.key === 'd' || e.key === 'D') movement.current.right = false; 40 | else if (e.key === 'space') movement.current.space = false; 41 | else if (e.key === 'Shift') movement.current.shift = false; 42 | else if (e.key === 'r' || e.key === 'R') movement.current.respawn = false; 43 | } 44 | 45 | window.addEventListener('keydown', handleKeyDown); 46 | window.addEventListener('keyup', handleKeyUp); 47 | 48 | return () => { 49 | window.removeEventListener('keydown', handleKeyDown); 50 | window.removeEventListener('keyup', handleKeyUp); 51 | } 52 | }, []); 53 | 54 | return movement; 55 | } 56 | -------------------------------------------------------------------------------- /blocks/three-object-block/frontend.js: -------------------------------------------------------------------------------- 1 | const { Component, render } = wp.element; 2 | 3 | import ThreeObjectFront from "./components/ThreeObjectFront"; 4 | 5 | const threeApp = document.querySelectorAll(".three-object-three-app"); 6 | 7 | threeApp.forEach((threeApp) => { 8 | if (threeApp) { 9 | const threeUrl = threeApp.querySelector("p.three-object-block-url") 10 | ? threeApp.querySelector("p.three-object-block-url").innerText 11 | : ""; 12 | const deviceTarget = threeApp.querySelector( 13 | "p.three-object-block-device-target" 14 | ) 15 | ? threeApp.querySelector("p.three-object-block-device-target") 16 | .innerText 17 | : "2D"; 18 | const backgroundColor = threeApp.querySelector( 19 | "p.three-object-background-color" 20 | ) 21 | ? threeApp.querySelector("p.three-object-background-color") 22 | .innerText 23 | : "#ffffff"; 24 | const zoom = threeApp.querySelector("p.three-object-zoom") 25 | ? threeApp.querySelector("p.three-object-zoom").innerText 26 | : 90; 27 | const scale = threeApp.querySelector("p.three-object-scale") 28 | ? threeApp.querySelector("p.three-object-scale").innerText 29 | : 1; 30 | const hasZoom = threeApp.querySelector("p.three-object-has-zoom") 31 | ? threeApp.querySelector("p.three-object-has-zoom").innerText 32 | : false; 33 | const hasTip = threeApp.querySelector("p.three-object-has-tip") 34 | ? threeApp.querySelector("p.three-object-has-tip").innerText 35 | : true; 36 | const positionY = threeApp.querySelector("p.three-object-position-y") 37 | ? threeApp.querySelector("p.three-object-position-y").innerText 38 | : 0; 39 | const rotationY = threeApp.querySelector("p.three-object-rotation-y") 40 | ? threeApp.querySelector("p.three-object-rotation-y").innerText 41 | : 0; 42 | const animations = threeApp.querySelector("p.three-object-animations") 43 | ? threeApp.querySelector("p.three-object-animations").innerText 44 | : ""; 45 | 46 | render( 47 | , 61 | threeApp 62 | ); 63 | } 64 | }); 65 | -------------------------------------------------------------------------------- /blocks/three-portal-block/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-object-viewer/three-portal-block", 3 | "attributes": { 4 | "scaleX": { 5 | "type": "int", 6 | "default":1 7 | }, 8 | "scaleY": { 9 | "type": "int", 10 | "default":1 11 | }, 12 | "scaleZ": { 13 | "type": "int", 14 | "default":1 15 | }, 16 | "positionX": { 17 | "type": "int", 18 | "default":0 19 | }, 20 | "positionY": { 21 | "type": "int", 22 | "default":0 23 | }, 24 | "positionZ": { 25 | "type": "int", 26 | "default":0 27 | }, 28 | "rotationX": { 29 | "type": "int", 30 | "default":0 31 | }, 32 | "rotationY": { 33 | "type": "int", 34 | "default":0 35 | }, 36 | "rotationZ": { 37 | "type": "int", 38 | "default":0 39 | }, 40 | "threeObjectUrl": { 41 | "type": "string", 42 | "default": null 43 | }, 44 | "destinationUrl": { 45 | "type": "string", 46 | "default": null 47 | }, 48 | "label": { 49 | "type": "string", 50 | "default": null 51 | }, 52 | "labelTextColor": { 53 | "type": "string", 54 | "default": "0x000000" 55 | }, 56 | "labelOffsetX": { 57 | "type": "int", 58 | "default":0 59 | }, 60 | "labelOffsetY": { 61 | "type": "int", 62 | "default":0 63 | }, 64 | "labelOffsetZ": { 65 | "type": "int", 66 | "default":0 67 | }, 68 | "animations": { 69 | "type": "string", 70 | "default": "" 71 | }, 72 | "collidable": { 73 | "type": "boolean", 74 | "default": false 75 | } 76 | }, 77 | "category": "design", 78 | "parent": [ "three-object-viewer/environment" ], 79 | "apiVersion": 2, 80 | "supports": { 81 | "html": false, 82 | "multiple": true 83 | }, 84 | "textdomain": "three-object-viewer", 85 | "editorScript": "file:../../build/block-three-portal-block.js", 86 | "editorStyle": "file:../../build/block-three-portal-block.css", 87 | "style": "file:../../build/block-three-portal-block.css" 88 | } 89 | -------------------------------------------------------------------------------- /languages/three-object-viewer-ja-three-object-viewer-settings.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:23-0500","generator":"WP-CLI\/2.8.1","source":"admin\/three-object-viewer-settings\/App.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"ja","plural-forms":"nplurals=2; plural=(n != 1);"},"3OV Settings":["3OV\u8a2d\u5b9a"],"Select or Upload Media":["\u30e1\u30c7\u30a3\u30a2\u306e\u9078\u629e\u307e\u305f\u306f\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9"],"Use this media":["\u3053\u306e\u30e1\u30c7\u30a3\u30a2\u3092\u4f7f\u7528\u3059\u308b"],"Here you can manage the settings for 3OV to tweak global configuration options and save your API keys for connected serivces.":["\u3053\u3053\u3067\u306f\u30013OV\u306e\u8a2d\u5b9a\u3092\u7ba1\u7406\u3057\u3066\u3001\u30b0\u30ed\u30fc\u30d0\u30eb\u306a\u69cb\u6210\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u8abf\u6574\u3057\u3001\u63a5\u7d9a\u3055\u308c\u305f\u30b5\u30fc\u30d3\u30b9\u306eAPI\u30ad\u30fc\u3092\u4fdd\u5b58\u3067\u304d\u307e\u3059\u3002"],"Avatar Settings":["\u30a2\u30d0\u30bf\u30fc\u8a2d\u5b9a"],"Default animation":["\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3"],"No custom default animation set":["\u30ab\u30b9\u30bf\u30e0\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3\u306f\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002"],"Set Default Animation":["\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3\u3092\u8a2d\u5b9a\u3059\u308b"],"Clear Default Animation":["\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3\u3092\u30af\u30ea\u30a2\u3059\u308b"],"No custom default avatar set":["\u30ab\u30b9\u30bf\u30e0\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u30a2\u30d0\u30bf\u30fc\u306f\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002"],"Set Default Avatar":["\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30a2\u30d0\u30bf\u30fc\u3092\u8a2d\u5b9a\u3059\u308b"],"Clear Default Avatar":["\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30a2\u30d0\u30bf\u30fc\u3092\u30af\u30ea\u30a2\u3059\u308b"],"AI Settings":["AI\u8a2d\u5b9a"],"NPC Settings":["NPC\u8a2d\u5b9a"],"Enable":["\u6709\u52b9\u306b\u3059\u308b"],"AI Endpoint URL":["AI\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u306eURL"],"OpenAI API Token":["OpenAI API\u30c8\u30fc\u30af\u30f3"],"AI Access Level":["AI\u30a2\u30af\u30bb\u30b9\u30ec\u30d9\u30eb"],"Public":["\u30d1\u30d6\u30ea\u30c3\u30af"],"Logged In":["\u30ed\u30b0\u30a4\u30f3\u6e08\u307f"],"Saving...":["\u4fdd\u5b58\u4e2d\u2026"]}}} -------------------------------------------------------------------------------- /blocks/three-audio-block/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-object-viewer/three-audio-block", 3 | "attributes": { 4 | "name": { 5 | "type": "string", 6 | "default": null 7 | }, 8 | "audioUrl": { 9 | "type": "string", 10 | "default": null 11 | }, 12 | "autoPlay": { 13 | "type": "bool", 14 | "default": true 15 | }, 16 | "loop": { 17 | "type": "bool", 18 | "default": true 19 | }, 20 | "volume": { 21 | "type": "int", 22 | "default": 1 23 | }, 24 | "positional": { 25 | "type": "bool", 26 | "default": true 27 | }, 28 | "coneInnerAngle": { 29 | "type": "int", 30 | "default":360 31 | }, 32 | "coneOuterAngle": { 33 | "type": "int", 34 | "default":0 35 | }, 36 | "coneOuterGain": { 37 | "type": "int", 38 | "default":0.8 39 | }, 40 | "distanceModel": { 41 | "type": "string", 42 | "default": "inverse" 43 | }, 44 | "maxDistance": { 45 | "type": "int", 46 | "default":10000 47 | }, 48 | "refDistance": { 49 | "type": "int", 50 | "default":5 51 | }, 52 | "rolloffFactor": { 53 | "type": "int", 54 | "default":5 55 | }, 56 | "positionX": { 57 | "type": "int", 58 | "default":0 59 | }, 60 | "positionY": { 61 | "type": "int", 62 | "default":0 63 | }, 64 | "positionZ": { 65 | "type": "int", 66 | "default":0 67 | }, 68 | "rotationX": { 69 | "type": "int", 70 | "default":0 71 | }, 72 | "rotationY": { 73 | "type": "int", 74 | "default":0 75 | }, 76 | "rotationZ": { 77 | "type": "int", 78 | "default":0 79 | } 80 | }, 81 | "category": "design", 82 | "parent": [ "three-object-viewer/environment" ], 83 | "apiVersion": 2, 84 | "supports": { 85 | "html": false, 86 | "multiple": true 87 | }, 88 | "textdomain": "three-object-viewer", 89 | "editorScript": "file:../../build/block-three-audio-block.js", 90 | "editorStyle": "file:../../build/block-three-audio-block.css", 91 | "style": "file:../../build/block-three-audio-block.css" 92 | } 93 | -------------------------------------------------------------------------------- /languages/three-object-viewer-ja-three-object-viewer-model-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:23-0500","generator":"WP-CLI\/2.8.1","source":"build\/block-model-block.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"ja","plural-forms":"nplurals=2; plural=(n != 1);"},"Loop Animations":["\u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3\u3092\u30eb\u30fc\u30d7\u3059\u308b"],"Separate each animation name you wish to loop with a comma":["\u5404\u30eb\u30fc\u30d7\u3055\u305b\u305f\u3044\u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3\u540d\u3092\u30b3\u30f3\u30de\u3067\u533a\u5207\u3063\u3066\u304f\u3060\u3055\u3044\u3002"],"Scale":["\u30b9\u30b1\u30fc\u30eb"],"Settings":["\u8a2d\u5b9a"],"GLB Object":["GLB\u30aa\u30d6\u30b8\u30a7\u30af\u30c8"],"Name":["\u540d\u524d"],"Give your object a name.":["\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306b\u540d\u524d\u3092\u4ed8\u3051\u3066\u304f\u3060\u3055\u3044\u3002"],"select a glb file from your media library to render an object in the canvas:":["\u30ad\u30e3\u30f3\u30d0\u30b9\u4e0a\u306b\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u30ec\u30f3\u30c0\u30ea\u30f3\u30b0\u3059\u308b\u305f\u3081\u306b\u3001\u30e1\u30c7\u30a3\u30a2\u30e9\u30a4\u30d6\u30e9\u30ea\u304b\u3089glb\u30d5\u30a1\u30a4\u30eb\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002"],"GLB File":["GLB\u30d5\u30a1\u30a4\u30eb"],"Replace Object":["\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u7f6e\u304d\u63db\u3048\u308b"],"Select Object":["\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u9078\u629e\u3059\u308b"],"Model Attributes":["\u30e2\u30c7\u30eb\u306e\u5c5e\u6027"],"Collidable":["\u885d\u7a81\u53ef\u80fd"],"Item is currently collidable.":["\u30a2\u30a4\u30c6\u30e0\u306f\u73fe\u5728\u885d\u7a81\u53ef\u80fd\u3067\u3059\u3002"],"Item is not collidable. Users will walk through it.":["\u30a2\u30a4\u30c6\u30e0\u306f\u885d\u7a81\u3057\u306a\u3044\u8a2d\u5b9a\u3067\u3059\u3002\u30e6\u30fc\u30b6\u30fc\u306f\u305d\u308c\u3092\u901a\u308a\u629c\u3051\u307e\u3059\u3002"],"Model Alt Text":["\u30e2\u30c7\u30eb\u306e\u4ee3\u66ff\u30c6\u30ad\u30b9\u30c8"],"Describe your model to provide context for screen readers.":["\u753b\u9762\u8aad\u307f\u4e0a\u3052\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u306b\u5bfe\u3057\u3066\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3092\u63d0\u4f9b\u3059\u308b\u305f\u3081\u306b\u3001\u30e2\u30c7\u30eb\u3092\u8aac\u660e\u3057\u3066\u304f\u3060\u3055\u3044\u3002"],"Position":["\u4f4d\u7f6e"],"Rotation":["\u56de\u8ee2"],"Model block":["\u30e2\u30c7\u30eb\u30d6\u30ed\u30c3\u30af"],"Replace Model":["\u30e2\u30c7\u30eb\u3092\u7f6e\u304d\u63db\u3048\u308b"],"Select Model":["\u30e2\u30c7\u30eb\u3092\u9078\u629e\u3059\u308b"]}}} -------------------------------------------------------------------------------- /languages/three-object-viewer-ja-three-object-viewer-three-portal-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:23-0500","generator":"WP-CLI\/2.8.1","source":"blocks\/three-portal-block\/Edit.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"ja","plural-forms":"nplurals=2; plural=(n != 1);"},"Loop Animations":["\u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3\u3092\u30eb\u30fc\u30d7\u3059\u308b"],"Separate each animation name you wish to loop with a comma":["\u5404\u30eb\u30fc\u30d7\u3055\u305b\u305f\u3044\u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3\u540d\u3092\u30b3\u30f3\u30de\u3067\u533a\u5207\u3063\u3066\u304f\u3060\u3055\u3044\u3002"],"Scale":["\u30b9\u30b1\u30fc\u30eb"],"Settings":["\u8a2d\u5b9a"],"GLB Object":["GLB\u30aa\u30d6\u30b8\u30a7\u30af\u30c8"],"Replace Object":["\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u7f6e\u304d\u63db\u3048\u308b"],"Select Object":["\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u9078\u629e\u3059\u308b"],"Model Attributes":["\u30e2\u30c7\u30eb\u306e\u5c5e\u6027"],"Collidable":["\u885d\u7a81\u53ef\u80fd"],"Item is currently collidable.":["\u30a2\u30a4\u30c6\u30e0\u306f\u73fe\u5728\u885d\u7a81\u53ef\u80fd\u3067\u3059\u3002"],"Item is not collidable. Users will walk through it.":["\u30a2\u30a4\u30c6\u30e0\u306f\u885d\u7a81\u3057\u306a\u3044\u8a2d\u5b9a\u3067\u3059\u3002\u30e6\u30fc\u30b6\u30fc\u306f\u305d\u308c\u3092\u901a\u308a\u629c\u3051\u307e\u3059\u3002"],"Rotation":["\u56de\u8ee2"],"Select a glb file from your media library to render an object in the canvas:":["\u30e1\u30c7\u30a3\u30a2\u30e9\u30a4\u30d6\u30e9\u30ea\u304b\u3089GLB\u30d5\u30a1\u30a4\u30eb\u3092\u9078\u629e\u3057\u3066\u3001\u30ad\u30e3\u30f3\u30d0\u30b9\u306b\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u30ec\u30f3\u30c0\u30ea\u30f3\u30b0\u3057\u3066\u304f\u3060\u3055\u3044\u3002"],"Destination URL":["\u5b9b\u5148\u306eURL"],"Define a url.":["URL\u3092\u5b9a\u7fa9\u3057\u3066\u304f\u3060\u3055\u3044\u3002"],"Link Label":["\u30ea\u30f3\u30af\u306e\u30e9\u30d9\u30eb"],"This text will describe where the link goes. If blank, will use the url as the label.":["\u3053\u306e\u30c6\u30ad\u30b9\u30c8\u306f\u30ea\u30f3\u30af\u5148\u3092\u8aac\u660e\u3057\u307e\u3059\u3002\u7a7a\u767d\u306e\u5834\u5408\u3001URL\u304c\u30e9\u30d9\u30eb\u3068\u3057\u3066\u4f7f\u7528\u3055\u308c\u307e\u3059\u3002"],"Text Color":["\u30c6\u30ad\u30b9\u30c8\u306e\u8272"],"Label Offset":["\u30e9\u30d9\u30eb\u306e\u30aa\u30d5\u30bb\u30c3\u30c8"],"X Offset":["X\u30aa\u30d5\u30bb\u30c3\u30c8"],"Y Offset":["Y\u30aa\u30d5\u30bb\u30c3\u30c8"],"Z Offset":["Z\u30aa\u30d5\u30bb\u30c3\u30c8"],"Portal block":["\u30dd\u30fc\u30bf\u30eb\u30d6\u30ed\u30c3\u30af"],"Select Portal":["\u30dd\u30fc\u30bf\u30eb\u3092\u9078\u629e\u3059\u308b"]}}} -------------------------------------------------------------------------------- /blocks/three-object-block/components/TeleportTravel.js: -------------------------------------------------------------------------------- 1 | import { Raycaster, Vector3 } from "three"; 2 | import { useXR, Interactive } from "@react-three/xr"; 3 | import { useFrame } from "@react-three/fiber"; 4 | import { useCallback, useRef, useState } from "react"; 5 | 6 | export function TeleportIndicator(props) { 7 | return ( 8 | <> 9 | 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | } 17 | 18 | export default function TeleportTravel(props) { 19 | const { 20 | centerOnTeleport, 21 | Indicator = TeleportIndicator, 22 | useNormal = true 23 | } = props; 24 | const [isHovered, setIsHovered] = useState(false); 25 | const target = useRef(); 26 | const targetLoc = useRef(); 27 | const ray = useRef(new Raycaster()); 28 | 29 | const rayDir = useRef({ 30 | pos: new Vector3(), 31 | dir: new Vector3() 32 | }); 33 | 34 | const { controllers, player } = useXR(); 35 | 36 | useFrame(() => { 37 | if ( 38 | isHovered && 39 | controllers.length > 0 && 40 | ray.current && 41 | target.current && 42 | targetLoc.current 43 | ) { 44 | controllers[0].controller.getWorldDirection(rayDir.current.dir); 45 | controllers[0].controller.getWorldPosition(rayDir.current.pos); 46 | rayDir.current.dir.multiplyScalar(-1); 47 | ray.current.set(rayDir.current.pos, rayDir.current.dir); 48 | 49 | const [intersection] = ray.current.intersectObject(target.current); 50 | 51 | if (intersection) { 52 | if (useNormal) { 53 | const p = intersection.point; 54 | 55 | targetLoc.current.position.set(0, 0, 0); 56 | 57 | const n = intersection.face.normal.clone(); 58 | n.transformDirection(intersection.object.matrixWorld); 59 | 60 | targetLoc.current.lookAt(n); 61 | targetLoc.current.rotateOnAxis( 62 | new Vector3(1, 0, 0), 63 | Math.PI / 2 64 | ); 65 | targetLoc.current.position.copy(p); 66 | } else { 67 | targetLoc.current.position.copy(intersection.point); 68 | } 69 | } 70 | } 71 | }); 72 | 73 | const click = useCallback(() => { 74 | if (isHovered) { 75 | player.position.copy(targetLoc.current.position); 76 | } 77 | }, [centerOnTeleport, isHovered, useNormal]); 78 | 79 | return ( 80 | <> 81 | {isHovered && ( 82 | 83 | 84 | 85 | )} 86 | setIsHovered(true)} 89 | onBlur={() => setIsHovered(false)} 90 | > 91 | {props.children} 92 | 93 | 94 | ); 95 | } 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Three Object Viewer 2 | 3 | [![Built With Plugin Machine](https://img.shields.io/badge/Built%20With-Plugin%20Machine-lightgrey)](https://pluginmachine.com) 4 | 5 | ## Installation 6 | 7 | Download the from WordPress or install by searching for "Three Object Viewer" in the plugin repository in wp-admin. 8 | https://wordpress.org/plugins/three-object-viewer/ 9 | 10 | 11 | ## Local Development 12 | 13 | - Git clone: 14 | - `git clone git@github.com:antpb/three-object-viewer.git` 15 | - Install javascript dependencies 16 | - `yarn` 17 | - Install php dependencies 18 | - `composer install` 19 | 20 | ## Working With JavaScript 21 | 22 | - Build JS/CSS 23 | - `yarn build` 24 | - Start JS/CSS for development - currently broken 25 | - `yarn start` 26 | - Test changed files - pending tests 27 | - `yarn test --watch` 28 | - Test all files once 29 | - `yarn test` 30 | - `yarn test --ci` 31 | 32 | 33 | ## Working With PHP 34 | 35 | ### Autoloader 36 | 37 | PHP classes should be located in the "php" directory and follow the [PSR-4 standard](https://www.php-fig.org/psr/psr-4/). 38 | 39 | The root namespace is `threeObjectViewer`. 40 | 41 | 42 | 43 | ### Tests 44 | - Run unit tests 45 | - `composer test:unit` 46 | - Run WordPress tests 47 | - `composer test:wordpress` 48 | - See local development instructions for how to run with Docker. 49 | - Run unit tests and WordPress tests 50 | - `composer test` 51 | 52 | ### Linter 53 | 54 | [PHPCS](https://github.com/squizlabs/PHP_CodeSniffer) is installed for linting and [automatic code fixing](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Fixing-Errors-Automatically). 55 | 56 | - Run linter and autofix 57 | - `composer fixes` 58 | - Run linter to identify issues. 59 | - `compose sniffs` 60 | 61 | ## Local Development Environment 62 | 63 | A [docker-compose](https://docs.docker.com/samples/wordpress/)-based local development environment is provided. 64 | 65 | - Start server 66 | - `docker-compose up -d` 67 | - Acess Site 68 | - [http://localhost:6039](http://localhost:6039) 69 | - WP CLI 70 | - Run any WP CLI command in container: 71 | - `docker-compose run wpcli wp ...` 72 | - Setup site with WP CLI 73 | - `docker-compose run wpcli wp core install --url=http://localhost:6039 --title="Three Object Viewer" --admin_user=admin0 --admin_email=something@example.com` 74 | - `docker-compose run wpcli wp user create admin admin@example.com --role=administrator --user_pass=pass` 75 | 76 | 77 | There is a special phpunit container for running WordPress tests, with WordPress and MySQL configured. 78 | 79 | - Enter container 80 | - `docker-compose run phpunit` 81 | - Composer install 82 | - `composer install` 83 | - Test 84 | - `composer test:wordpress` 85 | 86 | -------------------------------------------------------------------------------- /blocks/environment/components/core/front/ThreeLight.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { useThree } from "@react-three/fiber"; 3 | import { 4 | DirectionalLight, 5 | AmbientLight, 6 | PointLight, 7 | SpotLight, 8 | Color 9 | } from "three"; 10 | 11 | /** 12 | * Light component that creates various Three.js light objects based on the provided configuration. 13 | * 14 | * @param {Object} threeLight - An object containing light configuration options. 15 | * @param {string} threeLight.type - The type of light: "directional", "ambient", "point", "spot". 16 | * @param {number} threeLight.color - The color of the light. 17 | * @param {number} threeLight.intensity - The intensity of the light. 18 | * @param {number} threeLight.distance - Maximum range of the point light (for PointLight). 19 | * @param {number} threeLight.decay - The amount the light dims along the distance of the point light (for PointLight). 20 | * @param {number} threeLight.positionX - The X-coordinate of the light's position. 21 | * @param {number} threeLight.positionY - The Y-coordinate of the light's position. 22 | * @param {number} threeLight.positionZ - The Z-coordinate of the light's position. 23 | * @param {number} threeLight.angle - Maximum extent of the spotlight, in radians (for SpotLight). 24 | * @param {number} threeLight.penumbra - Percentage of the spotlight cone that is attenuated due to penumbra (for SpotLight). 25 | * 26 | * @returns {JSX.Element} - Returns a JSX element containing a Three.js light object. 27 | */ 28 | export function ThreeLight(threeLight) { 29 | const { scene } = useThree(); 30 | 31 | useEffect(() => { 32 | let lightInstance; 33 | const color = new Color( threeLight.color ); 34 | 35 | switch (threeLight.type) { 36 | case "directional": 37 | lightInstance = new DirectionalLight(color, threeLight.intensity); 38 | lightInstance.position.set(threeLight.positionX, threeLight.positionY, threeLight.positionZ); 39 | break; 40 | 41 | case "ambient": 42 | lightInstance = new AmbientLight(color, Number(threeLight.intensity)); 43 | break; 44 | 45 | case "point": 46 | lightInstance = new PointLight(color, threeLight.intensity, threeLight.distance, threeLight.decay); 47 | lightInstance.position.set(threeLight.positionX, threeLight.positionY, threeLight.positionZ); 48 | break; 49 | 50 | case "spot": 51 | lightInstance = new SpotLight(color, threeLight.intensity, threeLight.distance, threeLight.angle, threeLight.penumbra); 52 | lightInstance.position.set(threeLight.positionX, threeLight.positionY, threeLight.positionZ); 53 | break; 54 | 55 | default: 56 | console.warn("Invalid light type provided"); 57 | } 58 | 59 | // add the light to the scene 60 | scene.add(lightInstance); 61 | }, []); 62 | 63 | return; 64 | } 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@antpb/three-object-viewer", 3 | "private": true, 4 | "version": "0.6.3", 5 | "license": "GPL-2.0-or-later", 6 | "main": "build/index.js", 7 | "scripts": { 8 | "test": "yarn test:unit", 9 | "test:unit": "wp-scripts test-unit-js", 10 | "build": "wp-scripts build", 11 | "build:pro": "ISPRO=true wp-scripts build && node package.js pro", 12 | "build:free": "wp-scripts build && node package.js free", 13 | "test:ci": "wp-scripts test-unit-js --passWithNoTests", 14 | "format:js": "wp-scripts format-js", 15 | "lint:css": "wp-scripts lint-style", 16 | "lint:js": "wp-scripts lint-js", 17 | "start": "wp-scripts start", 18 | "rename": "node rename.js", 19 | "make-pot": "vendor/bin/wp i18n make-pot . ./languages/three-object-viewer.pot --slug=three-object-viewer --domain=three-object-viewer", 20 | "make-json": "vendor/bin/wp i18n make-json ./languages/ --no-purge" 21 | }, 22 | "devDependencies": { 23 | "@babel/core": "^7", 24 | "@testing-library/react": "^12", 25 | "@wordpress/eslint-plugin": "^13.5.0", 26 | "@wordpress/scripts": "^16", 27 | "airtap": "^4.0.4", 28 | "browserify": "^17.0.0", 29 | "esbuild": "^0.15.5", 30 | "eslint": "^8.7.0", 31 | "fs-extra": "^11.1.1", 32 | "lint-staged": "^13.0.3", 33 | "prettier": "^2.7.1", 34 | "prettier-standard": "^16.4.1", 35 | "standard": "^17.0.0", 36 | "tape": "^5.6.0", 37 | "tinyify": "^3.1.0" 38 | }, 39 | "dependencies": { 40 | "@babel/plugin-proposal-optional-chaining": "^7.18.9", 41 | "@babel/preset-env": "^7.20.2", 42 | "@babel/preset-react": "^7.18.6", 43 | "@pixiv/three-vrm": "2.0.1", 44 | "@react-three/a11y": "2.2.4", 45 | "@react-three/drei": "8.20.2", 46 | "@react-three/fiber": "8.12.0", 47 | "@react-three/rapier": "1.0.1", 48 | "@react-three/xr": "^3.5.0", 49 | "@wordpress/block-editor": "^6", 50 | "@wordpress/blocks": "^9", 51 | "@wordpress/components": "^14", 52 | "@wordpress/element": "^3", 53 | "@wordpress/i18n": "^4.40.0", 54 | "@wordpress/icons": "^9.17.0", 55 | "array-buffer-to-hex": "^1.0.0", 56 | "axios": "^1.2.1", 57 | "babel-loader": "8", 58 | "base64-arraybuffer": "^1.0.2", 59 | "camera-controls": "^2.7.0", 60 | "convert-hex": "^0.1.0", 61 | "events": "^3.3.0", 62 | "get-browser-rtc": "^1.1.0", 63 | "image-webpack-loader": "^8.1.0", 64 | "json-loader": "^0.5.7", 65 | "r3f-perf": "4.9.1", 66 | "re-resizable": "^6.9.9", 67 | "react-nipple": "^1.0.2", 68 | "react-scrollable-feed": "^1.3.1", 69 | "sass-loader": "^13.2.0", 70 | "style-loader": "2.0.0", 71 | "three": "0.151.1", 72 | "three-icosa": "^0.4.0", 73 | "three-omi": "^0.1.5", 74 | "tiny-simple-peer": "^10.1.1", 75 | "webpack-glsl-loader": "^1.0.1" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /blocks/environment/components/EditControls.js: -------------------------------------------------------------------------------- 1 | import React, { useRef, useState } from "react"; 2 | 3 | import { useFrame, useThree } from "@react-three/fiber"; 4 | import { PointerLockControls, OrbitControls } from "@react-three/drei"; 5 | 6 | const EditControls = (props) => { 7 | const controlsRef = useRef(); 8 | const isLocked = useRef(false); 9 | const [moveForward, setMoveForward] = useState(false); 10 | const [moveBackward, setMoveBackward] = useState(false); 11 | const [moveLeft, setMoveLeft] = useState(false); 12 | const [moveRight, setMoveRight] = useState(false); 13 | const [jump, setJump] = useState(false); 14 | 15 | useFrame(() => { 16 | const velocity = 0.5; 17 | 18 | if (moveForward) { 19 | // playerThing.applyImpulse({x:0, y:0, z:0.1}, true); 20 | controlsRef.current.moveForward(velocity); 21 | } else if (moveLeft) { 22 | controlsRef.current.moveRight(-velocity); 23 | } else if (moveBackward) { 24 | controlsRef.current.moveForward(-velocity); 25 | } else if (moveRight) { 26 | controlsRef.current.moveRight(velocity); 27 | } else if (jump) { 28 | } 29 | }); 30 | 31 | const onKeyDown = function (event, props) { 32 | switch (event.code) { 33 | case "ArrowUp": 34 | case "KeyW": 35 | setMoveForward(true); 36 | break; 37 | 38 | case "ArrowLeft": 39 | case "KeyA": 40 | setMoveLeft(true); 41 | break; 42 | 43 | case "ArrowDown": 44 | case "KeyS": 45 | setMoveBackward(true); 46 | break; 47 | 48 | case "ArrowRight": 49 | case "KeyD": 50 | setMoveRight(true); 51 | break; 52 | case "Space": 53 | window.addEventListener("keydown", (e) => { 54 | if (e.keyCode === 32 && e.target === document.body) { 55 | e.preventDefault(); 56 | } 57 | }); 58 | setJump(true); 59 | break; 60 | default: 61 | } 62 | }; 63 | 64 | const onKeyUp = function (event) { 65 | switch (event.code) { 66 | case "ArrowUp": 67 | case "KeyW": 68 | setMoveForward(false); 69 | break; 70 | 71 | case "ArrowLeft": 72 | case "KeyA": 73 | setMoveLeft(false); 74 | break; 75 | 76 | case "ArrowDown": 77 | case "KeyS": 78 | setMoveBackward(false); 79 | break; 80 | 81 | case "Space": 82 | setJump(false); 83 | break; 84 | 85 | case "ArrowRight": 86 | case "KeyD": 87 | setMoveRight(false); 88 | break; 89 | 90 | default: 91 | } 92 | }; 93 | 94 | document.addEventListener("keydown", onKeyDown); 95 | document.addEventListener("keyup", onKeyUp); 96 | const { gl } = useThree(); 97 | if (gl) { 98 | return ( 99 | { 102 | if (controlsRef.current) { 103 | renderer.addEventListener("lock", () => { 104 | console.log("lock"); 105 | isLocked.current = true; 106 | }); 107 | controlsRef.current.addEventListener("unlock", () => { 108 | console.log("unlock"); 109 | isLocked.current = false; 110 | }); 111 | } 112 | }} 113 | ref={controlsRef} 114 | /> 115 | ); 116 | } 117 | }; 118 | 119 | export default EditControls; 120 | -------------------------------------------------------------------------------- /blocks/model-block/components/ModelEdit.js: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import React, { Suspense, useRef, useState, useEffect } from "react"; 3 | import { Canvas, useLoader, useFrame, useThree } from "@react-three/fiber"; 4 | import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; 5 | import { 6 | OrthographicCamera, 7 | PerspectiveCamera, 8 | OrbitControls, 9 | useAnimations 10 | } from "@react-three/drei"; 11 | import { VRM, VRMUtils, VRMSchema, VRMLoaderPlugin } from "@pixiv/three-vrm"; 12 | import { GLTFAudioEmitterExtension } from "three-omi"; 13 | 14 | function ThreeObject(props) { 15 | const [url, set] = useState(props.url); 16 | useEffect(() => { 17 | setTimeout(() => set(props.url), 2000); 18 | }, []); 19 | const [listener] = useState(() => new THREE.AudioListener()); 20 | 21 | useThree(({ camera }) => { 22 | camera.add(listener); 23 | }); 24 | 25 | const gltf = useLoader(GLTFLoader, url, (loader) => { 26 | loader.register( 27 | (parser) => new GLTFAudioEmitterExtension(parser, listener) 28 | ); 29 | loader.register((parser) => { 30 | return new VRMLoaderPlugin(parser); 31 | }); 32 | }); 33 | 34 | const { actions } = useAnimations(gltf.animations, gltf.scene); 35 | 36 | const animationList = props.animations ? props.animations.split(",") : ""; 37 | 38 | useEffect(() => { 39 | if (animationList) { 40 | animationList.forEach((name) => { 41 | if (Object.keys(actions).includes(name)) { 42 | actions[name].play(); 43 | } 44 | }); 45 | } 46 | }, []); 47 | 48 | if (gltf?.userData?.gltfExtensions?.VRM) { 49 | const vrm = gltf.userData.vrm; 50 | vrm.scene.position.set(0, props.positionY, 0); 51 | VRMUtils.rotateVRM0(vrm); 52 | const rotationVRM = vrm.scene.rotation.y + parseFloat(props.rotationY); 53 | vrm.scene.rotation.set(0, rotationVRM, 0); 54 | // vrm.scene.scale.set( props.scaleX, props.scaleY, props.scaleZ ); 55 | return ; 56 | } 57 | gltf.scene.position.set(0, 0, 0); 58 | gltf.scene.rotation.set(0, 0, 0); 59 | gltf.scene.scale.set(1, 1, 1); 60 | return ; 61 | } 62 | 63 | export default function ModelEdit(props) { 64 | return ( 65 | <> 66 | 76 | 82 | 83 | 90 | {props.url && ( 91 | 92 | 97 | 98 | )} 99 | 100 | 101 | {props.hasTip && ( 102 |

Click and drag ^

103 | )} 104 | 105 | ); 106 | } 107 | -------------------------------------------------------------------------------- /blocks/npc-block/components/ModelEdit.js: -------------------------------------------------------------------------------- 1 | import * as THREE from "three"; 2 | import React, { Suspense, useRef, useState, useEffect } from "react"; 3 | import { Canvas, useLoader, useFrame, useThree } from "@react-three/fiber"; 4 | import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; 5 | import { 6 | OrthographicCamera, 7 | PerspectiveCamera, 8 | OrbitControls, 9 | useAnimations 10 | } from "@react-three/drei"; 11 | import { VRM, VRMUtils, VRMSchema, VRMLoaderPlugin } from "@pixiv/three-vrm"; 12 | import { GLTFAudioEmitterExtension } from "three-omi"; 13 | 14 | function ThreeObject(props) { 15 | const [url, set] = useState(props.url); 16 | useEffect(() => { 17 | setTimeout(() => set(props.url), 2000); 18 | }, []); 19 | const [listener] = useState(() => new THREE.AudioListener()); 20 | 21 | useThree(({ camera }) => { 22 | camera.add(listener); 23 | }); 24 | 25 | const gltf = useLoader(GLTFLoader, url, (loader) => { 26 | loader.register( 27 | (parser) => new GLTFAudioEmitterExtension(parser, listener) 28 | ); 29 | loader.register((parser) => { 30 | return new VRMLoaderPlugin(parser); 31 | }); 32 | }); 33 | 34 | const { actions } = useAnimations(gltf.animations, gltf.scene); 35 | 36 | const animationList = props.animations ? props.animations.split(",") : ""; 37 | 38 | useEffect(() => { 39 | if (animationList) { 40 | animationList.forEach((name) => { 41 | if (Object.keys(actions).includes(name)) { 42 | actions[name].play(); 43 | } 44 | }); 45 | } 46 | }, []); 47 | 48 | if (gltf?.userData?.gltfExtensions?.VRM) { 49 | const vrm = gltf.userData.vrm; 50 | vrm.scene.position.set(0, props.positionY, 0); 51 | VRMUtils.rotateVRM0(vrm); 52 | const rotationVRM = vrm.scene.rotation.y + parseFloat(props.rotationY); 53 | vrm.scene.rotation.set(0, rotationVRM, 0); 54 | // vrm.scene.scale.set( props.scaleX, props.scaleY, props.scaleZ ); 55 | return ; 56 | } 57 | gltf.scene.position.set(0, 0, 0); 58 | gltf.scene.rotation.set(0, 0, 0); 59 | gltf.scene.scale.set(1, 1, 1); 60 | return ; 61 | } 62 | 63 | export default function ModelEdit(props) { 64 | return ( 65 | <> 66 | 76 | 82 | 83 | 90 | {props.url && ( 91 | 92 | 97 | 98 | )} 99 | 100 | 101 | {props.hasTip && ( 102 |

Click and drag ^

103 | )} 104 | 105 | ); 106 | } 107 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const defaultConfig = require("@wordpress/scripts/config/webpack.config"); 2 | const path = require("path"); 3 | const isProduction = "production" === process.env.NODE_ENV; 4 | const { entryPoints } = require("./pluginMachine.json"); 5 | const isPro = process.env.ISPRO === 'true'; 6 | 7 | const entry = {}; 8 | if (entryPoints.hasOwnProperty("blocks")) { 9 | entryPoints.blocks.forEach((entryPoint) => { 10 | entry[`block-${entryPoint}`] = path.resolve( 11 | process.cwd(), 12 | `blocks/${entryPoint}/index.js` 13 | ); 14 | }); 15 | } 16 | 17 | if (isPro) { 18 | if (entryPoints.hasOwnProperty("proBlocks")) { 19 | entryPoints.proBlocks.forEach((entryPoint) => { 20 | entry[`block-${entryPoint}`] = path.resolve( 21 | process.cwd(), 22 | `pro/blocks/${entryPoint}/index.js` 23 | ); 24 | }); 25 | } 26 | } 27 | 28 | if (entryPoints.hasOwnProperty("adminPages")) { 29 | entryPoints.adminPages.forEach((entryPoint) => { 30 | entry[`admin-page-${entryPoint}`] = path.resolve( 31 | process.cwd(), 32 | `admin/${entryPoint}/index.js` 33 | ); 34 | }); 35 | } 36 | 37 | if (isPro) { 38 | if (entryPoints.hasOwnProperty("proAdminPages")) { 39 | entryPoints.proAdminPages.forEach((entryPoint) => { 40 | entry[`admin-page-${entryPoint}`] = path.resolve( 41 | process.cwd(), 42 | `pro/admin/${entryPoint}/index.js` 43 | ); 44 | }); 45 | } 46 | } 47 | 48 | entry[`./assets/js/blocks.frontend`] = "./blocks/three-object-block/frontend.js"; 49 | 50 | entry[`./assets/js/blocks.frontend-versepress`] = "./blocks/environment/frontend.js"; 51 | 52 | if (isPro) { 53 | entry[`./assets/js/blocks.three-mirror-block`] = "./pro/blocks/three-mirror-block/three-mirror-block-front.js"; 54 | } 55 | 56 | module.exports = { 57 | mode: isProduction ? "production" : "development", 58 | ...defaultConfig, 59 | module: { 60 | ...defaultConfig.module, 61 | 62 | rules: [ 63 | ...defaultConfig.module.rules, 64 | { 65 | test: /\.js$/, 66 | exclude: /node_modules/, 67 | use: 'babel-loader' 68 | }, 69 | { 70 | test: /\.css$/, 71 | use: ["style-loader", "sass-loader", "css-loader"] 72 | }, 73 | { 74 | test: /\.glsl$/, 75 | include: [path.resolve(__dirname, "node_modules/three-icosa")], 76 | use: "webpack-glsl" 77 | }, 78 | { 79 | test: /\.js$/, 80 | include: [path.resolve(__dirname, "node_modules/three-icosa")], 81 | use: "babel-loader" 82 | }, 83 | { 84 | test: /\.vrm$/, 85 | use: [ 86 | { 87 | loader: "file-loader" 88 | } 89 | ] 90 | }, 91 | { 92 | test: /\.glb$/, 93 | use: [ 94 | { 95 | loader: "file-loader" 96 | } 97 | ] 98 | }, 99 | { 100 | test: /\.fbx$/, 101 | use: [ 102 | { 103 | loader: "file-loader" 104 | } 105 | ] 106 | } 107 | ] 108 | }, 109 | entry, 110 | output: { 111 | filename: "[name].js", 112 | path: path.join(__dirname, "./build") 113 | }, 114 | externals: { 115 | react: "React", 116 | "react-dom": "ReactDOM", 117 | }, 118 | resolve: { 119 | modules: ['node_modules'], 120 | extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'], 121 | alias: { 122 | Brushes: path.resolve(__dirname, "brushes"), 123 | '@magickml/editor': path.resolve(__dirname, 'node_modules/@magickml/editor'), 124 | 'draco-decoder': path.resolve(__dirname, 'node_modules/draco3d/') 125 | } 126 | } 127 | }; 128 | -------------------------------------------------------------------------------- /languages/three-object-viewer-es_MX-three-object-viewer-three-audio-block-editor-script.json: -------------------------------------------------------------------------------- 1 | {"translation-revision-date":"2023-08-20 22:28-0500","generator":"WP-CLI\/2.8.1","source":"build\/block-three-audio-block.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"es_MX","plural-forms":"nplurals=2; plural=(n != 1);"},"Settings":["Ajustes"],"Name":["Nombre"],"Position":["Posici\u00f3n"],"Rotation":["Rotaci\u00f3n"],"Audio Object":["Objeto de Audio"],"Give your audio a name.":["Asigna un nombre a tu audio."],"Select an audio file to add to your scene.":["Selecciona un archivo de audio para a\u00f1adir a tu escena."],"Audio File":["Archivo de Audio"],"Replace Audio":["Reemplazar Audio"],"Select Audio":["Seleccionar Audio"],"AutoPlay":["Reproducci\u00f3n Autom\u00e1tica"],"Item will autoplay.":["El elemento se reproducir\u00e1 autom\u00e1ticamente."],"Item will not autoplay.":["El elemento no se reproducir\u00e1 autom\u00e1ticamente."],"Item will loop.":["El elemento se repetir\u00e1 en bucle."],"Item will not loop.":["El elemento no se repetir\u00e1 en bucle."],"Item will be spatial audio.":["El elemento ser\u00e1 audio espacial."],"Item will be global audio.":["El elemento ser\u00e1 audio global."],"Volume":["Volumen"],"Unitless multiplier against original source volume for determining emitter loudness.":["Multiplicador sin unidades contra el volumen original de la fuente para determinar la intensidad del emisor."],"Positional Volume Settings":["Ajustes de Volumen Posicional"],"Cone Inner angle":["\u00c1ngulo Interior del Cono"],"The angle, in radians, of a cone outside of which the volume will be reduced to a constant value ofconeOuterGain.":["El \u00e1ngulo, en radianes, de un cono fuera del cual el volumen se reducir\u00e1 a un valor constante de coneOuterGain."],"Cone Outer Angle":["\u00c1ngulo Exterior del Cono"],"Cone Outer Gain":["Ganancia Exterior del Cono"],"The gain of the audio emitter set when outside the cone defined by the coneOuterAngle property.":["La ganancia del emisor de audio establecida cuando est\u00e1 fuera del cono definido por la propiedad coneOuterAngle."],"Distance Model":["Modelo de Distancia"],"Specifies the distance model for the audio emitter.":["Especifica el modelo de distancia para el emisor de audio."],"Inverse":["Inverso"],"Linear":["Lineal"],"Exponential":["Exponencial"],"Max Distance":["Distancia M\u00e1xima"],"The maximum distance between the emitter and listener, after which the volume will not be reduced any further. maximumDistance may only be applied when the distanceModel is set to linear. Otherwise, it should be ignored.":["La distancia m\u00e1xima entre el emisor y el oyente, despu\u00e9s de la cual el volumen no se reducir\u00e1 m\u00e1s. maximumDistance solo se puede aplicar cuando el modelo de distancia est\u00e1 configurado en lineal. De lo contrario, debe ignorarse."],"Ref Distance":["Distancia de Referencia"],"A reference distance for reducing volume as the emitter moves further from the listener. For distances less than this, the volume is not reduced.":["Una distancia de referencia para reducir el volumen a medida que el emisor se aleja del oyente. Para distancias menores que esta, el volumen no se reduce."],"Rolloff Factor":["Factor de Atenuaci\u00f3n"],"Describes how quickly the volume is reduced as the emitter moves away from listener. When distanceModel is set to linear, the maximum value is 1 otherwise there is no upper limit.":["Describe qu\u00e9 tan r\u00e1pido se reduce el volumen a medida que el emisor se aleja del oyente. Cuando el modelo de distancia est\u00e1 configurado en lineal, el valor m\u00e1ximo es 1; de lo contrario, no hay l\u00edmite superior."],"Audio block":["Bloque de Audio"]}}} -------------------------------------------------------------------------------- /blocks/model-block/index.js: -------------------------------------------------------------------------------- 1 | import { registerBlockType } from "@wordpress/blocks"; 2 | import Edit from "./Edit"; 3 | import Save from "./Save"; 4 | import { useBlockProps } from "@wordpress/block-editor"; 5 | 6 | const icon = ( 7 | 13 | 14 | 15 | 16 | 17 | ); 18 | 19 | const blockConfig = require("./block.json"); 20 | registerBlockType(blockConfig.name, { 21 | ...blockConfig, 22 | icon, 23 | apiVersion: 2, 24 | edit: Edit, 25 | save: Save, 26 | deprecated: [ 27 | { 28 | attributes: { 29 | scaleX: { 30 | type: "int", 31 | default:1 32 | }, 33 | name: { 34 | type: "string" 35 | }, 36 | scaleY: { 37 | type: "int", 38 | default:1 39 | }, 40 | scaleZ: { 41 | type: "int", 42 | default:1 43 | }, 44 | positionX: { 45 | type: "int", 46 | default:0 47 | }, 48 | positionY: { 49 | type: "int", 50 | default:0 51 | }, 52 | positionZ: { 53 | type: "int", 54 | default:0 55 | }, 56 | rotationX: { 57 | type: "int", 58 | default:0 59 | }, 60 | rotationY: { 61 | type: "int", 62 | default:0 63 | }, 64 | rotationZ: { 65 | type: "int", 66 | default:0 67 | }, 68 | threeObjectUrl: { 69 | type: "string", 70 | default: null 71 | }, 72 | animations: { 73 | type: "string", 74 | default: "" 75 | }, 76 | alt: { 77 | type: "string", 78 | default: "" 79 | }, 80 | collidable: { 81 | type: "boolean", 82 | default: false 83 | } 84 | }, 85 | save(props) { 86 | return ( 87 |
88 | <> 89 |
90 |

91 | {props.attributes.threeObjectUrl} 92 |

93 |

{props.attributes.scaleX}

94 |

{props.attributes.scaleY}

95 |

{props.attributes.scaleZ}

96 |

97 | {props.attributes.positionX} 98 |

99 |

100 | {props.attributes.positionY} 101 |

102 |

103 | {props.attributes.positionZ} 104 |

105 |

106 | {props.attributes.rotationX} 107 |

108 |

109 | {props.attributes.rotationY} 110 |

111 |

112 | {props.attributes.rotationZ} 113 |

114 |

115 | {props.attributes.animations} 116 |

117 |

118 | {props.attributes.collidable ? 1 : 0} 119 |

120 |

{props.attributes.alt}

121 |
122 | 123 |
124 | ); 125 | } 126 | } 127 | ] 128 | }); 129 | -------------------------------------------------------------------------------- /blocks/three-video-block/index.js: -------------------------------------------------------------------------------- 1 | import { registerBlockType } from "@wordpress/blocks"; 2 | import Edit from "./Edit"; 3 | import Save from "./Save"; 4 | import { useBlockProps } from "@wordpress/block-editor"; 5 | 6 | const icon = ( 7 | 13 | 14 | 15 | 16 | 17 | ); 18 | 19 | const blockConfig = require("./block.json"); 20 | registerBlockType(blockConfig.name, { 21 | ...blockConfig, 22 | icon, 23 | apiVersion: 2, 24 | edit: Edit, 25 | save: Save, 26 | deprecated: [ 27 | { 28 | attributes: { 29 | videoUrl: { 30 | type: "string", 31 | default: null 32 | }, 33 | modelUrl: { 34 | type: "string", 35 | default: null 36 | }, 37 | autoPlay: { 38 | type: "bool", 39 | default: true 40 | }, 41 | scaleX: { 42 | type: "int", 43 | default: 1 44 | }, 45 | scaleY: { 46 | type: "int", 47 | default: 1 48 | }, 49 | scaleZ: { 50 | type: "int", 51 | default: 1 52 | }, 53 | positionX: { 54 | type: "int", 55 | default: 0 56 | }, 57 | positionY: { 58 | type: "int", 59 | default: 0 60 | }, 61 | positionZ: { 62 | type: "int", 63 | default: 0 64 | }, 65 | rotationX: { 66 | type: "int", 67 | default: 0 68 | }, 69 | rotationY: { 70 | type: "int", 71 | default: 0 72 | }, 73 | rotationZ: { 74 | type: "int", 75 | default: 0 76 | }, 77 | aspectHeight: { 78 | type: "int", 79 | default: 0 80 | }, 81 | aspectWidth: { 82 | type: "int", 83 | default: 0 84 | } 85 | }, 86 | save(props) { 87 | return ( 88 |
89 | <> 90 |
91 |
{props.attributes.videoUrl}
92 |

{props.attributes.scaleX}

93 |

{props.attributes.scaleY}

94 |

{props.attributes.scaleZ}

95 |

96 | {props.attributes.positionX} 97 |

98 |

99 | {props.attributes.positionY} 100 |

101 |

102 | {props.attributes.positionZ} 103 |

104 |

105 | {props.attributes.rotationX} 106 |

107 |

108 | {props.attributes.rotationY} 109 |

110 |

111 | {props.attributes.rotationZ} 112 |

113 |

114 | {props.attributes.aspectHeight} 115 |

116 |

117 | {props.attributes.aspectWidth} 118 |

119 |

120 | {props.attributes.autoPlay ? 1 : 0} 121 |

122 |
123 | 124 |
125 | ); 126 | } 127 | } 128 | ] 129 | }); 130 | -------------------------------------------------------------------------------- /blocks/environment/components/Networking.js: -------------------------------------------------------------------------------- 1 | import P2PCF from "./p2pcf/p2pcf.js"; 2 | 3 | const Networking = (props) => { 4 | if (!document.location.hash) { 5 | document.location = 6 | document.location.toString() + `#xpp-${props.postSlug}`; 7 | } 8 | 9 | const userProfileName = 10 | userData.userId === "" 11 | ? Math.floor(Math.random() * 100000) 12 | : userData.userId; 13 | const p2pcf = new P2PCF( 14 | "user-" + userProfileName, 15 | document.location.hash.substring(1), 16 | { 17 | workerUrl: "https://p2pcf.sxpdigital.workers.dev/", 18 | slowPollingRateMs: 5000, 19 | fastPollingRateMs: 1500 20 | } 21 | ); 22 | window.p2pcf = p2pcf; 23 | console.log("client id:", p2pcf.clientId); 24 | 25 | const removePeerUi = (clientId) => { 26 | document.getElementById(clientId)?.remove(); 27 | document.getElementById(`${clientId}-video`)?.remove(); 28 | }; 29 | 30 | const addPeerUi = (sessionId) => { 31 | if (document.getElementById(sessionId)) return; 32 | 33 | const peerEl = document.createElement("div"); 34 | peerEl.style = "display: flex;"; 35 | 36 | const name = document.createElement("div"); 37 | name.innerText = sessionId.substring(0, 5); 38 | 39 | peerEl.id = sessionId; 40 | peerEl.appendChild(name); 41 | 42 | document.getElementById("peers").appendChild(peerEl); 43 | }; 44 | const addMessage = (message) => { 45 | const messageEl = document.createElement("div"); 46 | messageEl.innerText = message; 47 | 48 | document.getElementById("messages").appendChild(messageEl); 49 | }; 50 | let stream; 51 | p2pcf.on("peerconnect", (peer) => { 52 | console.log("Peer connect", peer.id, peer); 53 | console.log(peer.client_id); 54 | 55 | if (stream) { 56 | peer.addStream(stream); 57 | } 58 | peer.on("track", (track, stream) => { 59 | console.log("got track", track); 60 | const video = document.createElement("audio"); 61 | video.id = `${peer.id}-audio`; 62 | video.srcObject = stream; 63 | video.setAttribute("playsinline", true); 64 | document.getElementById("videos").appendChild(video); 65 | video.play(); 66 | }); 67 | addPeerUi(peer.id); 68 | }); 69 | 70 | p2pcf.on("peerclose", (peer) => { 71 | console.log("Peer close", peer.id, peer); 72 | removePeerUi(peer.id); 73 | }); 74 | 75 | p2pcf.on("msg", (peer, data) => { 76 | addMessage( 77 | peer.id.substring(0, 5) + 78 | ": " + 79 | new TextDecoder("utf-8").decode(data) 80 | ); 81 | }); 82 | 83 | const go = () => { 84 | document.getElementById("session-id").innerText = 85 | p2pcf.sessionId.substring(0, 5) + "@" + p2pcf.roomId + ":"; 86 | 87 | // document.getElementById('send-button').addEventListener('click', () => { 88 | // const box = document.getElementById('send-box'); 89 | // addMessage(p2pcf.sessionId.substring(0, 5) + ': ' + box.value); 90 | // p2pcf.broadcast(new TextEncoder().encode(box.value)); 91 | // box.value = ''; 92 | // }) 93 | 94 | document 95 | .getElementById("audio-button") 96 | .addEventListener("click", async () => { 97 | stream = await navigator.mediaDevices.getUserMedia({ 98 | audio: true 99 | }); 100 | 101 | for (const peer of p2pcf.peers.values()) { 102 | peer.addStream(stream); 103 | } 104 | }); 105 | 106 | p2pcf.start(); 107 | }; 108 | if ( 109 | document.readyState === "complete" || 110 | document.readyState === "interactive" 111 | ) { 112 | document 113 | .getElementById("join-button") 114 | .addEventListener("click", async () => { 115 | window.addEventListener("DOMContentLoaded", audio, { 116 | once: true 117 | }); 118 | // window.addEventListener('DOMContentLoaded', go, { once: true }) 119 | }); 120 | } else { 121 | window.addEventListener("DOMContentLoaded", go, { once: true }); 122 | } 123 | 124 | return <>; 125 | }; 126 | 127 | export default Networking; -------------------------------------------------------------------------------- /blocks/environment/components/core/front/ThreeAudio.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { useThree } from "@react-three/fiber"; 3 | import { 4 | AudioListener, 5 | PositionalAudio, 6 | AudioLoader, 7 | Audio 8 | } from "three"; 9 | 10 | /** 11 | * Audio component that creates an Audio or PositionalAudio object and attaches it to the Three.js camera. 12 | * 13 | * @param {Object} threeAudio - An object containing audio configuration options. 14 | * @param {string} threeAudio.positional - Indicates if the audio should be positional ("1") or not ("0"). 15 | * @param {string} threeAudio.audioUrl - The URL of the audio file to be loaded and played. 16 | * @param {string} threeAudio.loop - Indicates if the audio should loop ("1") or not ("0"). 17 | * @param {number} threeAudio.volume - The volume level of the audio (0 to 1). 18 | * @param {string} threeAudio.autoPlay - Indicates if the audio should play automatically ("1") or not ("0"). 19 | * @param {number} threeAudio.refDistance - The reference distance for positional audio. 20 | * @param {number} threeAudio.maxDistance - The maximum distance for positional audio. 21 | * @param {number} threeAudio.rolloffFactor - The rolloff factor for positional audio. 22 | * @param {number} threeAudio.coneInnerAngle - The inner cone angle for positional audio (in degrees). 23 | * @param {number} threeAudio.coneOuterAngle - The outer cone angle for positional audio (in degrees). 24 | * @param {number} threeAudio.coneOuterGain - The outer cone gain for positional audio. 25 | * @param {string} threeAudio.distanceModel - The distance model for positional audio. 26 | * @param {number} threeAudio.positionX - The X-coordinate of the audio's position for positional audio. 27 | * @param {number} threeAudio.positionY - The Y-coordinate of the audio's position for positional audio. 28 | * @param {number} threeAudio.positionZ - The Z-coordinate of the audio's position for positional audio. 29 | * @param {number} threeAudio.rotationX - The X-coordinate of the audio's rotation for positional audio (in radians). 30 | * @param {number} threeAudio.rotationY - The Y-coordinate of the audio's rotation for positional audio (in radians). 31 | * @param {number} threeAudio.rotationZ - The Z-coordinate of the audio's rotation for positional audio (in radians). 32 | * 33 | * @returns {JSX.Element} - Returns a JSX element containing a Three.js primitive object (Audio/PositionalAudio). 34 | */ 35 | export function ThreeAudio(threeAudio) { 36 | const { camera } = useThree(); 37 | const [audio, setAudio] = useState(null); 38 | 39 | useEffect(() => { 40 | const listener = new AudioListener(); 41 | camera.add(listener); 42 | 43 | // Create either a PositionalAudio object or a normal Audio object based on the positional attribute 44 | const audio = threeAudio.positional === "1" ? new PositionalAudio(listener) : new Audio(listener); 45 | 46 | if (threeAudio.audioUrl) { 47 | const audioLoader = new AudioLoader(); 48 | audioLoader.load(threeAudio.audioUrl, (buffer) => { 49 | audio.setBuffer(buffer); 50 | audio.setLoop(threeAudio.loop === "1" ? true : false); 51 | audio.setVolume(threeAudio.volume); 52 | if (threeAudio.autoPlay === "1") audio.play(); 53 | }); 54 | } 55 | 56 | if (threeAudio.positional === "1") { 57 | audio.refDistance = threeAudio.refDistance; 58 | audio.maxDistance = threeAudio.maxDistance; 59 | audio.rolloffFactor = threeAudio.rolloffFactor; 60 | audio.coneInnerAngle = threeAudio.coneInnerAngle; 61 | audio.coneOuterAngle = threeAudio.coneOuterAngle; 62 | audio.coneOuterGain = threeAudio.coneOuterGain; 63 | audio.distanceModel = threeAudio.distanceModel; 64 | audio.position.set(threeAudio.positionX, threeAudio.positionY, threeAudio.positionZ); 65 | audio.rotation.set(threeAudio.rotationX, threeAudio.rotationY, threeAudio.rotationZ); 66 | } 67 | 68 | setAudio(audio); 69 | 70 | return () => { 71 | audio.stop(); 72 | camera.remove(listener); 73 | } 74 | }, []); 75 | 76 | return ( 77 | <> 78 | {audio && } 79 | 80 | ); 81 | } 82 | -------------------------------------------------------------------------------- /blocks/three-object-block/components/ThreeObjectEdit.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three'; 2 | import { USDZLoader } from 'three/examples/jsm/loaders/USDZLoader'; 3 | import React, { Suspense, useRef, useState, useEffect } from 'react'; 4 | import { Canvas, useLoader, useFrame, useThree } from '@react-three/fiber'; 5 | import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; 6 | import { 7 | OrthographicCamera, 8 | PerspectiveCamera, 9 | OrbitControls, 10 | useAnimations, 11 | } from '@react-three/drei'; 12 | import { VRM, VRMUtils, VRMSchema, VRMLoaderPlugin } from '@pixiv/three-vrm' 13 | import { GLTFAudioEmitterExtension } from 'three-omi'; 14 | 15 | 16 | function ThreeObject( props ) { 17 | const [ url, set ] = useState( props.url ); 18 | const {scene} = useThree(); 19 | useEffect( () => { 20 | setTimeout( () => set( props.url ), 2000 ); 21 | }, [] ); 22 | const [ listener ] = useState( () => new THREE.AudioListener() ); 23 | 24 | useThree( ( { camera } ) => { 25 | camera.add( listener ); 26 | } ); 27 | 28 | // USDZ loader. 29 | if(props.url.split(/[#?]/)[0].split('.').pop().trim() === "usdz") { 30 | 31 | const usdz = useLoader( USDZLoader, url); 32 | 33 | return ; 34 | } 35 | 36 | const gltf = useLoader( GLTFLoader, url, ( loader ) => { 37 | loader.register( 38 | ( parser ) => new GLTFAudioEmitterExtension( parser, listener ) 39 | ); 40 | loader.register( ( parser ) => { 41 | 42 | return new VRMLoaderPlugin( parser ); 43 | 44 | } ); 45 | } ); 46 | const { actions } = useAnimations( gltf.animations, gltf.scene ); 47 | 48 | const animationList = props.animations ? props.animations.split( ',' ) : ''; 49 | 50 | useEffect( () => { 51 | if ( animationList ) { 52 | animationList.forEach( ( name ) => { 53 | if ( Object.keys( actions ).includes( name ) ) { 54 | actions[ name ].play(); 55 | } 56 | } ); 57 | } 58 | }, [] ); 59 | 60 | if(gltf?.userData?.gltfExtensions?.VRM){ 61 | const vrm = gltf.userData.vrm; 62 | vrm.scene.position.set( 0, props.positionY, 0 ); 63 | VRMUtils.rotateVRM0( vrm ); 64 | const rotationVRM = vrm.scene.rotation.y + parseFloat(props.rotationY); 65 | vrm.scene.rotation.set( 0, rotationVRM, 0 ); 66 | vrm.scene.scale.set( props.scale, props.scale, props.scale ); 67 | return ; 68 | } 69 | 70 | gltf.scene.position.set( 0, props.positionY, 0 ); 71 | gltf.scene.rotation.set( 0, props.rotationY, 0 ); 72 | gltf.scene.scale.set( props.scale, props.scale, props.scale ); 73 | return ; 74 | } 75 | 76 | export default function ThreeObjectEdit( props ) { 77 | return ( 78 | <> 79 | 89 | 90 | 91 | 98 | { props.url && ( 99 | 100 | 109 | 110 | ) } 111 | 112 | 113 | { props.hasTip && ( 114 |

Click and drag ^

115 | ) } 116 | 117 | ); 118 | } 119 | -------------------------------------------------------------------------------- /languages/three-object-viewer.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: Three Object Viewer 1.5.2\n" 4 | "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/three-object-viewer\n" 5 | "Last-Translator: FULL NAME \n" 6 | "Language-Team: LANGUAGE \n" 7 | "MIME-Version: 1.0\n" 8 | "Content-Type: text/plain; charset=UTF-8\n" 9 | "Content-Transfer-Encoding: 8bit\n" 10 | "POT-Creation-Date: 2023-08-19T05:20:49+00:00\n" 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 12 | "Language: \n" 13 | "X-Generator: WP-CLI 2.8.1\n" 14 | "X-Domain: three-object-viewer\n" 15 | 16 | #. Plugin Name of the plugin 17 | msgid "Three Object Viewer" 18 | msgstr "" 19 | 20 | #. Plugin URI of the plugin 21 | msgid "https://3ov.xyz/" 22 | msgstr "" 23 | 24 | #. Description of the plugin 25 | msgid "A plugin for viewing 3D files with support for WebXR and Open Metaverse Interoperability GLTF Extensions." 26 | msgstr "" 27 | 28 | #. Author of the plugin 29 | msgid "antpb" 30 | msgstr "" 31 | 32 | #. Author URI of the plugin 33 | msgid "https://antpb.com" 34 | msgstr "" 35 | 36 | #: admin/three-object-viewer-settings/init.php:75 37 | #: admin/three-object-viewer-settings/init.php:76 38 | msgid "3OV Settings" 39 | msgstr "" 40 | 41 | #: blocks/environment/block.json 42 | msgctxt "block title" 43 | msgid "3D Environment" 44 | msgstr "" 45 | 46 | #: blocks/environment/block.json 47 | msgctxt "block description" 48 | msgid "A 3D environment component" 49 | msgstr "" 50 | 51 | #: blocks/model-block/block.json 52 | msgctxt "block title" 53 | msgid "3D Model" 54 | msgstr "" 55 | 56 | #: blocks/model-block/block.json 57 | msgctxt "block description" 58 | msgid "A 3D model for your environment" 59 | msgstr "" 60 | 61 | #: blocks/npc-block/block.json 62 | msgctxt "block title" 63 | msgid "NPC Block" 64 | msgstr "" 65 | 66 | #: blocks/npc-block/block.json 67 | msgctxt "block description" 68 | msgid "A NPC to live in your environment" 69 | msgstr "" 70 | 71 | #: blocks/sky-block/block.json 72 | msgctxt "block title" 73 | msgid "3D Sky Block" 74 | msgstr "" 75 | 76 | #: blocks/sky-block/block.json 77 | msgctxt "block description" 78 | msgid "A sky your environment" 79 | msgstr "" 80 | 81 | #: blocks/spawn-point-block/block.json 82 | msgctxt "block title" 83 | msgid "Spawn Point" 84 | msgstr "" 85 | 86 | #: blocks/spawn-point-block/block.json 87 | msgctxt "block description" 88 | msgid "A spawn point for your users" 89 | msgstr "" 90 | 91 | #: blocks/three-audio-block/block.json 92 | msgctxt "block title" 93 | msgid "3D Audio" 94 | msgstr "" 95 | 96 | #: blocks/three-audio-block/block.json 97 | msgctxt "block description" 98 | msgid "An audio block for your environment" 99 | msgstr "" 100 | 101 | #: blocks/three-image-block/block.json 102 | msgctxt "block title" 103 | msgid "3D Image" 104 | msgstr "" 105 | 106 | #: blocks/three-image-block/block.json 107 | msgctxt "block description" 108 | msgid "An image block for your environment" 109 | msgstr "" 110 | 111 | #: blocks/three-light-block/block.json 112 | msgctxt "block title" 113 | msgid "3D Light" 114 | msgstr "" 115 | 116 | #: blocks/three-light-block/block.json 117 | msgctxt "block description" 118 | msgid "A light block for your environment" 119 | msgstr "" 120 | 121 | #: blocks/three-object-block/block.json 122 | msgctxt "block title" 123 | msgid "Three Object Block" 124 | msgstr "" 125 | 126 | #: blocks/three-object-block/block.json 127 | msgctxt "block description" 128 | msgid "A 3D object viewer focused on glTF" 129 | msgstr "" 130 | 131 | #: blocks/three-portal-block/block.json 132 | msgctxt "block title" 133 | msgid "3D Portal" 134 | msgstr "" 135 | 136 | #: blocks/three-portal-block/block.json 137 | msgctxt "block description" 138 | msgid "A 3D portal" 139 | msgstr "" 140 | 141 | #: blocks/three-text-block/block.json 142 | msgctxt "block title" 143 | msgid "Three Text Block" 144 | msgstr "" 145 | 146 | #: blocks/three-text-block/block.json 147 | msgctxt "block description" 148 | msgid "A 3D Text Block" 149 | msgstr "" 150 | 151 | #: blocks/three-video-block/block.json 152 | msgctxt "block title" 153 | msgid "3D Video" 154 | msgstr "" 155 | 156 | #: blocks/three-video-block/block.json 157 | msgctxt "block description" 158 | msgid "A video block for your environment" 159 | msgstr "" 160 | --------------------------------------------------------------------------------