-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcli.js
214 lines (182 loc) · 6.68 KB
/
cli.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const ejs = require('ejs');
const { program } = require('commander');
// Template for a GLB model with animation
const animatedTemplate = `
import React, { useEffect, useRef } from "react";
import { useGLTF, useAnimations } from "@react-three/drei";
import { useFrame } from "@react-three/fiber";
const <%= componentName %> = ({ position = [0, 0, 0], rotation = [0, 0, 0], scale = 1, animationName = "<%= animationName %>" }) => {
// Load the GLB model
const { scene, animations } = useGLTF(process.env.PUBLIC_URL + "<%= glbPath %>");
// Create an AnimationMixer to control animations
const mixer = useRef(null);
// Get and control the loaded animations
const { actions } = useAnimations(animations, scene);
// Play the specified animation if it exists
useEffect(() => {
if (actions && animationName && actions[animationName]) {
actions[animationName].play(); // Play the specified animation
}
}, [actions, animationName]);
// Update animation on each frame without needing to manually call update
useFrame(() => {
if (mixer.current) {
mixer.current.update(); // No need to call .update(delta) here
}
});
return (
<primitive
object={scene}
position={position}
rotation={rotation}
scale={scale}
castShadow
receiveShadow
/>
);
};
useGLTF.preload(process.env.PUBLIC_URL + "<%= glbPath %>");
export default <%= componentName %>;
`;
// Template for a GLB model without animation
const staticTemplate = `
import React from "react";
import { useGLTF } from "@react-three/drei";
const <%= componentName %> = ({ position = [0, 0, 0], rotation = [0, 0, 0], scale = 1 }) => {
// Load the GLB model
const { scene } = useGLTF(process.env.PUBLIC_URL + "<%= glbPath %>");
return (
<primitive
object={scene}
position={position}
rotation={rotation}
scale={scale}
castShadow
receiveShadow
/>
);
};
useGLTF.preload(process.env.PUBLIC_URL + "<%= glbPath %>");
export default <%= componentName %>;
`;
// Template for a glowing text model
const glowingTextTemplate = `
import React, { useRef, useEffect } from 'react';
import { useGLTF } from '@react-three/drei';
import { RigidBody } from '@react-three/rapier';
import { useFrame } from '@react-three/fiber';
import * as THREE from 'three';
import { KernelSize } from 'postprocessing'
import { EffectComposer, Bloom } from '@react-three/postprocessing'
const <%= componentName %> = ({ position = [0, 0, 0], rotation = [0, 0, 0], scale = 1, color = '<%= defaultColor %>', onClickUrl = '<%= defaultUrl %>' }) => {
const { scene } = useGLTF(process.env.PUBLIC_URL + "<%= glbPath %>");
const rigidBodyRef = useRef();
const modelRef = useRef();
const emissiveMaterialRef = useRef();
useEffect(() => {
modelRef.current.traverse((child) => {
if (child.isMesh) {
// Create a glowing material
const emissiveMaterial = new THREE.MeshStandardMaterial({
color: 0xffffff, // Base color
emissive: new THREE.Color(color), // Emissive color
emissiveIntensity: 1, // Emissive intensity
transparent: true,
opacity: 0.4,
});
child.material = emissiveMaterial;
emissiveMaterialRef.current = emissiveMaterial;
}
});
}, [color]);
// Pulsing effect
useFrame((state) => {
if (emissiveMaterialRef.current) {
const time = state.clock.getElapsedTime();
emissiveMaterialRef.current.emissiveIntensity = 1.5 + Math.sin(time * 2) * 0.5;
}
});
const handleClick = () => {
if (onClickUrl) {
window.open(onClickUrl, '_blank', 'noopener,noreferrer,width=800,height=600,top=100,left=100');
}
};
const handleCollisionEnter = (e) => {
if (rigidBodyRef.current) {
const impulse = [0, 1, 0]; // Upward impulse
rigidBodyRef.current.applyImpulse(impulse, true);
if (onClickUrl) {
window.open(onClickUrl, '_blank', 'noopener,noreferrer,width=800,height=600,top=100,left=100');
}
}
};
return (
<>
<RigidBody type="fixed" colliders="trimesh" ref={rigidBodyRef} onCollisionEnter={handleCollisionEnter}>
<primitive
ref={modelRef}
object={scene}
position={position}
rotation={rotation}
scale={Array.isArray(scale) ? scale : [scale, scale, scale]}
onClick={handleClick}
castShadow
receiveShadow
/>
</RigidBody>
{/* <EffectComposer multisampling={1}>
<Bloom kernelSize={3} luminanceThreshold={0} luminanceSmoothing={0.4} intensity={0.6} />
<Bloom kernelSize={KernelSize.HUGE} luminanceThreshold={0} luminanceSmoothing={0} intensity={0.5} />
</EffectComposer>*/}
</>
);
};
useGLTF.preload(process.env.PUBLIC_URL + "<%= glbPath %>");
export default <%= componentName %>;
`;
program
.command('glbgx <glbPath>')
.alias('some-3d-models')
.description('Generate a React component for a GLB model with or without animation')
.option('-n, --name <name>', 'Component name', 'GLBModel')
.option('-a, --animation <animation>', 'Animation name') // Optional animation name
.option('-t, --text', 'Generate a glowing text component') // New option to generate glowing text component
.option('-c, --color <color>', 'Glow color (in hexadecimal)', '0xffa500') // Default color
.option('-u, --url <url>', 'URL to open on click', 'https://github.com/Reene444') // Default URL
.action(async (glbPath, options) => {
const { name, animation, text, color, url } = options;
const componentName = name.charAt(0).toUpperCase() + name.slice(1);
let rendered;
// If --text option is used, generate glowing text component
if (text) {
if (!color || !url) {
console.error('Both --color and --url are required when using the --text option.');
process.exit(1);
}
rendered = ejs.render(glowingTextTemplate, {
componentName: componentName,
glbPath: glbPath,
defaultColor: color,
defaultUrl: url,
});
} else {
// Generate GLB model component
const template = animation ? animatedTemplate : staticTemplate;
rendered = ejs.render(template, {
componentName: componentName,
glbPath: glbPath,
animationName: animation || "Take 01",
});
}
const componentDir = path.join(process.cwd(), 'src/components');
if (!fs.existsSync(componentDir)) {
fs.mkdirSync(componentDir, { recursive: true });
}
const componentPath = path.join(componentDir, `${componentName}.js`);
fs.writeFileSync(componentPath, rendered);
console.log(`Component ${componentName} created at ${componentPath}`);
});
program.parse(process.argv);