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 ( +
+

{u} / M{m}

+
+ ); +}; + +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 ( +
+

{currentDateTime}

+
+ ); +}; + +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 ( +
+ + {/* Light gray background */} + + + + + +
+ ) +} +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" + ] +}