diff --git a/.gitignore b/.gitignore
index 478e4e5..b473bf1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@
**_site**
**.jekyll**
galaxy-react/public/models
+holo/public/models
diff --git a/holo/.env b/holo/.env
new file mode 100644
index 0000000..ba7cc18
--- /dev/null
+++ b/holo/.env
@@ -0,0 +1 @@
+GENERATE_SOURCEMAP=false
diff --git a/holo/.gitignore b/holo/.gitignore
new file mode 100644
index 0000000..8a4bd62
--- /dev/null
+++ b/holo/.gitignore
@@ -0,0 +1,24 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+*-lock.json
diff --git a/holo/package.json b/holo/package.json
new file mode 100644
index 0000000..fff64fb
--- /dev/null
+++ b/holo/package.json
@@ -0,0 +1,52 @@
+{
+ "name": "holo",
+ "version": "0.1.0",
+ "private": true,
+ "dependencies": {
+ "@pixiv/three-vrm": "^3.1.1",
+ "@pixiv/three-vrm-animation": "^3.1.1",
+ "@react-three/drei": "^9.114.0",
+ "@react-three/fiber": "^8.17.9",
+ "@react-three/postprocessing": "^2.16.3",
+ "@testing-library/jest-dom": "^5.17.0",
+ "@testing-library/react": "^13.4.0",
+ "@testing-library/user-event": "^13.5.0",
+ "@types/jest": "^27.5.2",
+ "@types/node": "^16.18.112",
+ "@types/react": "^18.3.10",
+ "@types/react-dom": "^18.3.0",
+ "@types/three": "^0.167.2",
+ "axios": "^1.7.7",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-scripts": "5.0.1",
+ "three": "^0.167.1",
+ "three-stdlib": "^2.30.5",
+ "typescript": "^4.9.5",
+ "web-vitals": "^2.1.4"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test",
+ "eject": "react-scripts eject"
+ },
+ "eslintConfig": {
+ "extends": [
+ "react-app",
+ "react-app/jest"
+ ]
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ }
+}
diff --git a/holo/public/favicon.ico b/holo/public/favicon.ico
new file mode 100644
index 0000000..a11777c
Binary files /dev/null and b/holo/public/favicon.ico differ
diff --git a/holo/public/index.html b/holo/public/index.html
new file mode 100644
index 0000000..5633250
--- /dev/null
+++ b/holo/public/index.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ React App
+
+
+
+
+
+
+
diff --git a/holo/public/logo192.png b/holo/public/logo192.png
new file mode 100644
index 0000000..fc44b0a
Binary files /dev/null and b/holo/public/logo192.png differ
diff --git a/holo/public/logo512.png b/holo/public/logo512.png
new file mode 100644
index 0000000..a4e47a6
Binary files /dev/null and b/holo/public/logo512.png differ
diff --git a/holo/public/manifest.json b/holo/public/manifest.json
new file mode 100644
index 0000000..080d6c7
--- /dev/null
+++ b/holo/public/manifest.json
@@ -0,0 +1,25 @@
+{
+ "short_name": "React App",
+ "name": "Create React App Sample",
+ "icons": [
+ {
+ "src": "favicon.ico",
+ "sizes": "64x64 32x32 24x24 16x16",
+ "type": "image/x-icon"
+ },
+ {
+ "src": "logo192.png",
+ "type": "image/png",
+ "sizes": "192x192"
+ },
+ {
+ "src": "logo512.png",
+ "type": "image/png",
+ "sizes": "512x512"
+ }
+ ],
+ "start_url": ".",
+ "display": "standalone",
+ "theme_color": "#000000",
+ "background_color": "#ffffff"
+}
diff --git a/holo/public/robots.txt b/holo/public/robots.txt
new file mode 100644
index 0000000..e9e57dc
--- /dev/null
+++ b/holo/public/robots.txt
@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:
diff --git a/holo/readme.md b/holo/readme.md
new file mode 100644
index 0000000..2f3dc51
--- /dev/null
+++ b/holo/readme.md
@@ -0,0 +1,20 @@
+react-three-fiber + three-vrm
+
+```sh
+$ npm i
+$ npm run start
+```
+
+## install
+
+```sh
+# npx create-react-app vrm1 --template typescript
+# npm install three three-stdlib @types/three @react-three/fiber @react-three/fiber @react-three/drei @react-three/postprocessing
+```
+
+## three-vrm
+
+`vrm 1.0`
+
+unity export
+
diff --git a/holo/src/App.css b/holo/src/App.css
new file mode 100644
index 0000000..e247a5f
--- /dev/null
+++ b/holo/src/App.css
@@ -0,0 +1,39 @@
+.App {
+ text-align: center;
+}
+
+.App-logo {
+ height: 40vmin;
+ pointer-events: none;
+}
+
+@media (prefers-reduced-motion: no-preference) {
+ .App-logo {
+ animation: App-logo-spin infinite 20s linear;
+ }
+}
+
+.App-header {
+ background-color: #282c34;
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ font-size: calc(10px + 2vmin);
+ color: white;
+}
+
+.App-link {
+ color: #61dafb;
+}
+
+@keyframes App-logo-spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
diff --git a/holo/src/App.test.tsx b/holo/src/App.test.tsx
new file mode 100644
index 0000000..2a68616
--- /dev/null
+++ b/holo/src/App.test.tsx
@@ -0,0 +1,9 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import App from './App';
+
+test('renders learn react link', () => {
+ render();
+ const linkElement = screen.getByText(/learn react/i);
+ expect(linkElement).toBeInTheDocument();
+});
diff --git a/holo/src/App.tsx b/holo/src/App.tsx
new file mode 100644
index 0000000..8fc3840
--- /dev/null
+++ b/holo/src/App.tsx
@@ -0,0 +1,16 @@
+import React from 'react'
+import VRMModelCanvas from './pages/vrm'
+import ScreenTimeCanvas from './pages/time'
+import ScreenApiCanvas from './pages/api'
+
+const App = () => {
+ return (
+ <>
+
+
+
+ >
+ )
+}
+
+export default App;
diff --git a/holo/src/index.css b/holo/src/index.css
new file mode 100644
index 0000000..eda55a9
--- /dev/null
+++ b/holo/src/index.css
@@ -0,0 +1,37 @@
+body {
+ height: 100%;
+ margin: 0;
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+ monospace;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+ monospace;
+}
+
+html {
+ height: 100%;
+}
+
+.time {
+ position: absolute;
+ top: 10px;
+ padding: 10px;
+ z-index: 100;
+ color: #e9ff00;
+ font-size: 30px;
+ text-align: center;
+}
+
+.api {
+ position: absolute;
+ top: 50px;
+ padding: 10px;
+ z-index: 100;
+ color: #e9ff00;
+ font-size: 25px;
+ text-align: center;
+}
diff --git a/holo/src/index.tsx b/holo/src/index.tsx
new file mode 100644
index 0000000..032464f
--- /dev/null
+++ b/holo/src/index.tsx
@@ -0,0 +1,19 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import './index.css';
+import App from './App';
+import reportWebVitals from './reportWebVitals';
+
+const root = ReactDOM.createRoot(
+ document.getElementById('root') as HTMLElement
+);
+root.render(
+
+
+
+);
+
+// If you want to start measuring performance in your app, pass a function
+// to log results (for example: reportWebVitals(console.log))
+// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
+reportWebVitals();
diff --git a/holo/src/logo.svg b/holo/src/logo.svg
new file mode 100644
index 0000000..9dfc1c0
--- /dev/null
+++ b/holo/src/logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/holo/src/pages/api.tsx b/holo/src/pages/api.tsx
new file mode 100644
index 0000000..ad4c5cd
--- /dev/null
+++ b/holo/src/pages/api.tsx
@@ -0,0 +1,58 @@
+//import React, { useState, useEffect } from 'react';
+//import axios from 'axios';
+
+//interface Post {
+// id: number;
+// username: string;
+// planet: number;
+//}
+
+//const ScreenApiCanvas: React.FC = () => {
+// const [posts, setPosts] = useState([]);
+// const [loading, setLoading] = useState(true);
+// const [error, setError] = useState(null);
+// const searchParams = new URLSearchParams(window.location.search);
+// var id = searchParams.get('id') ?? '2';
+// var url = 'https://api.syui.ai/users/' + id;
+// useEffect(() => {
+// const fetchPosts = async () => {
+// try {
+// const response = await axios.get(url);
+// setPosts(response.data);
+// setLoading(false);
+// } catch (err) {
+// setError('error network api');
+// setLoading(false);
+// }
+// };
+//
+// fetchPosts();
+// }, [url]);
+//
+// if (loading) return 読み込み中...
;
+// if (error) return {error}
;
+//
+// return (
+//
+//
+// {posts.map(post => (
+// - {post.username} / M {post.planet}
+// ))}
+//
+//
+// );
+//};
+
+import React from 'react';
+const ScreenApiCanvas: React.FC = () => {
+ const searchParams = new URLSearchParams(window.location.search);
+ var m = searchParams.get('m') ?? '0';
+ var u = searchParams.get('u') ?? 'ai';
+ return (
+
+ );
+};
+
+export default ScreenApiCanvas;
diff --git a/holo/src/pages/time.tsx b/holo/src/pages/time.tsx
new file mode 100644
index 0000000..aeb9852
--- /dev/null
+++ b/holo/src/pages/time.tsx
@@ -0,0 +1,33 @@
+import React, { useState, useEffect } from 'react';
+
+const ScreenTimeCanvas: React.FC = () => {
+ const [currentDateTime, setCurrentDateTime] = useState('');
+
+ useEffect(() => {
+ const updateDateTime = () => {
+ const now = new Date();
+ const formatted = now.toLocaleString("ja-JP", {
+ year: 'numeric',
+ month: '2-digit',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit'
+ });
+ setCurrentDateTime(formatted);
+ };
+
+ updateDateTime();
+ const timer = setInterval(updateDateTime, 1000);
+
+ return () => clearInterval(timer);
+ }, []);
+
+ return (
+
+ );
+};
+
+export default ScreenTimeCanvas;
diff --git a/holo/src/pages/vrm.tsx b/holo/src/pages/vrm.tsx
new file mode 100644
index 0000000..e4f40eb
--- /dev/null
+++ b/holo/src/pages/vrm.tsx
@@ -0,0 +1,76 @@
+import * as THREE from 'three'
+import React, { useState, useEffect, useRef } from 'react';
+import { OrbitControls } from '@react-three/drei'
+import { useFrame, Canvas } from '@react-three/fiber';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
+import { VRM, VRMUtils, VRMLoaderPlugin } from '@pixiv/three-vrm';
+import { VRMAnimationLoaderPlugin, VRMAnimation, createVRMAnimationClip } from "@pixiv/three-vrm-animation";
+
+interface ModelProps {
+ url: string
+ url_anim: string
+ scale: [number, number, number]
+ position: [number, number, number]
+ rotation: [number, number, number]
+}
+
+const VRMModel: React.FC = ({ url, url_anim, position, rotation, scale }) => {
+
+ const [vrm, setVrm] = useState(null);
+ const mixerRef = useRef(null);
+
+ useEffect(() => {
+ const loader = new GLTFLoader();
+ loader.register((parser) => new VRMLoaderPlugin(parser));
+ loader.register((parser) => new VRMAnimationLoaderPlugin(parser));
+ loader.load(url, (gltf) => {
+ const vrmModel = gltf.userData.vrm as VRM;
+ VRMUtils.removeUnnecessaryJoints(vrmModel.scene);
+ setVrm(vrmModel);
+ const mixer = new THREE.AnimationMixer(vrmModel.scene);
+ mixerRef.current = mixer;
+ loader.load(url_anim, (animGltf) => {
+ const vrmAnimations = animGltf.userData.vrmAnimations as VRMAnimation[];
+ if (vrmAnimations && vrmAnimations.length > 0) {
+ const clip = createVRMAnimationClip(vrmAnimations[0], vrmModel);
+ mixer.clipAction(clip).play();
+ }
+ });
+ });
+ }, [url, url_anim]);
+
+ useFrame((state, delta) => {
+ if (mixerRef.current) mixerRef.current.update(delta);
+ if (vrm) vrm.update(delta);
+ });
+
+ return vrm ? : null;
+};
+
+export const VRMModelCanvas = () => {
+ return (
+
+
+
+ )
+}
+export default VRMModelCanvas;
diff --git a/holo/src/react-app-env.d.ts b/holo/src/react-app-env.d.ts
new file mode 100644
index 0000000..6431bc5
--- /dev/null
+++ b/holo/src/react-app-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/holo/src/reportWebVitals.ts b/holo/src/reportWebVitals.ts
new file mode 100644
index 0000000..49a2a16
--- /dev/null
+++ b/holo/src/reportWebVitals.ts
@@ -0,0 +1,15 @@
+import { ReportHandler } from 'web-vitals';
+
+const reportWebVitals = (onPerfEntry?: ReportHandler) => {
+ if (onPerfEntry && onPerfEntry instanceof Function) {
+ import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
+ getCLS(onPerfEntry);
+ getFID(onPerfEntry);
+ getFCP(onPerfEntry);
+ getLCP(onPerfEntry);
+ getTTFB(onPerfEntry);
+ });
+ }
+};
+
+export default reportWebVitals;
diff --git a/holo/src/setupTests.ts b/holo/src/setupTests.ts
new file mode 100644
index 0000000..8f2609b
--- /dev/null
+++ b/holo/src/setupTests.ts
@@ -0,0 +1,5 @@
+// jest-dom adds custom jest matchers for asserting on DOM nodes.
+// allows you to do things like:
+// expect(element).toHaveTextContent(/react/i)
+// learn more: https://github.com/testing-library/jest-dom
+import '@testing-library/jest-dom';
diff --git a/holo/tsconfig.json b/holo/tsconfig.json
new file mode 100644
index 0000000..a273b0c
--- /dev/null
+++ b/holo/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noFallthroughCasesInSwitch": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx"
+ },
+ "include": [
+ "src"
+ ]
+}