aiqtech commited on
Commit
10e0fb7
ยท
verified ยท
1 Parent(s): 9b0615b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +284 -0
app.py CHANGED
@@ -0,0 +1,284 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import json
3
+ import os
4
+ from pathlib import Path
5
+ import shutil
6
+
7
+ def create_game_html(model_path):
8
+ html_template = f"""
9
+ <!DOCTYPE html>
10
+ <html>
11
+ <head>
12
+ <title>3D Open World Game</title>
13
+ <style>
14
+ body {{ margin: 0; }}
15
+ canvas {{ display: block; }}
16
+ #instructions {{
17
+ position: absolute;
18
+ top: 10px;
19
+ left: 10px;
20
+ background: rgba(0,0,0,0.7);
21
+ color: white;
22
+ padding: 10px;
23
+ border-radius: 5px;
24
+ font-family: Arial, sans-serif;
25
+ }}
26
+ </style>
27
+ <script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
28
+ <script type="importmap">
29
+ {{
30
+ "imports": {{
31
+ "three": "https://unpkg.com/[email protected]/build/three.module.js",
32
+ "three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
33
+ }}
34
+ }}
35
+ </script>
36
+ </head>
37
+ <body>
38
+ <div id="instructions">
39
+ Controls:<br>
40
+ W - Move Forward<br>
41
+ S - Move Backward<br>
42
+ A - Move Left<br>
43
+ D - Move Right<br>
44
+ Mouse - Look Around
45
+ </div>
46
+ <script type="module">
47
+ import * as THREE from 'three';
48
+ import {{ GLTFLoader }} from 'three/addons/loaders/GLTFLoader.js';
49
+ import {{ PointerLockControls }} from 'three/addons/controls/PointerLockControls.js';
50
+
51
+ // ๊ธฐ๋ณธ ์„ค์ •
52
+ const scene = new THREE.Scene();
53
+ const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
54
+ const renderer = new THREE.WebGLRenderer();
55
+ renderer.setSize(window.innerWidth, window.innerHeight);
56
+ renderer.shadowMap.enabled = true;
57
+ document.body.appendChild(renderer.domElement);
58
+
59
+ // ํฌ์ธํ„ฐ ๋ฝ ์ปจํŠธ๋กค
60
+ const controls = new PointerLockControls(camera, document.body);
61
+
62
+ // ํ‚ค๋ณด๋“œ ์ž…๋ ฅ ์ฒ˜๋ฆฌ
63
+ const moveState = {{
64
+ forward: false,
65
+ backward: false,
66
+ left: false,
67
+ right: false
68
+ }};
69
+
70
+ document.addEventListener('keydown', (event) => {{
71
+ switch (event.code) {{
72
+ case 'KeyW':
73
+ moveState.forward = true;
74
+ break;
75
+ case 'KeyS':
76
+ moveState.backward = true;
77
+ break;
78
+ case 'KeyA':
79
+ moveState.left = true;
80
+ break;
81
+ case 'KeyD':
82
+ moveState.right = true;
83
+ break;
84
+ }}
85
+ }});
86
+
87
+ document.addEventListener('keyup', (event) => {{
88
+ switch (event.code) {{
89
+ case 'KeyW':
90
+ moveState.forward = false;
91
+ break;
92
+ case 'KeyS':
93
+ moveState.backward = false;
94
+ break;
95
+ case 'KeyA':
96
+ moveState.left = false;
97
+ break;
98
+ case 'KeyD':
99
+ moveState.right = false;
100
+ break;
101
+ }}
102
+ }});
103
+
104
+ // ํด๋ฆญ์œผ๋กœ ๊ฒŒ์ž„ ์‹œ์ž‘
105
+ document.addEventListener('click', () => {{
106
+ controls.lock();
107
+ }});
108
+
109
+ // ์ง€ํ˜• ์ƒ์„ฑ
110
+ const terrainGeometry = new THREE.PlaneGeometry(1000, 1000, 100, 100);
111
+ const terrainMaterial = new THREE.MeshStandardMaterial({{
112
+ color: 0x3a8c3a,
113
+ wireframe: false
114
+ }});
115
+ const terrain = new THREE.Mesh(terrainGeometry, terrainMaterial);
116
+ terrain.rotation.x = -Math.PI / 2;
117
+ terrain.receiveShadow = true;
118
+ scene.add(terrain);
119
+
120
+ // ์ง€ํ˜• ๋†’๋‚ฎ์ด ์„ค์ •
121
+ const vertices = terrainGeometry.attributes.position.array;
122
+ for (let i = 0; i < vertices.length; i += 3) {{
123
+ vertices[i + 2] = Math.random() * 10;
124
+ }}
125
+ terrainGeometry.attributes.position.needsUpdate = true;
126
+ terrainGeometry.computeVertexNormals();
127
+
128
+ // ์กฐ๋ช… ์„ค์ •
129
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
130
+ scene.add(ambientLight);
131
+
132
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
133
+ directionalLight.position.set(100, 100, 100);
134
+ directionalLight.castShadow = true;
135
+ scene.add(directionalLight);
136
+
137
+ // ์บ๋ฆญํ„ฐ ๋ชจ๋ธ ๋กœ๋“œ
138
+ let character;
139
+ const loader = new GLTFLoader();
140
+ loader.load('{model_path}', (gltf) => {{
141
+ character = gltf.scene;
142
+ character.scale.set(0.5, 0.5, 0.5);
143
+ character.position.y = 2;
144
+ character.castShadow = true;
145
+ character.receiveShadow = true;
146
+ scene.add(character);
147
+
148
+ // ์บ๋ฆญํ„ฐ ๊ทธ๋ฆผ์ž ์„ค์ •
149
+ character.traverse((node) => {{
150
+ if (node.isMesh) {{
151
+ node.castShadow = true;
152
+ node.receiveShadow = true;
153
+ }}
154
+ }});
155
+ }});
156
+
157
+ // ์นด๋ฉ”๋ผ ์ดˆ๊ธฐ ์œ„์น˜ ์„ค์ •
158
+ camera.position.set(0, 5, 10);
159
+
160
+ // ๋ฐฐ๊ฒฝ ํ•˜๋Š˜ ์„ค์ •
161
+ const skyGeometry = new THREE.SphereGeometry(500, 32, 32);
162
+ const skyMaterial = new THREE.MeshBasicMaterial({{
163
+ color: 0x87ceeb,
164
+ side: THREE.BackSide
165
+ }});
166
+ const sky = new THREE.Mesh(skyGeometry, skyMaterial);
167
+ scene.add(sky);
168
+
169
+ // ๋‚˜๋ฌด์™€ ๋ฐ”์œ„ ์ถ”๊ฐ€
170
+ function addEnvironmentObject(geometry, material, count, yOffset) {{
171
+ for (let i = 0; i < count; i++) {{
172
+ const mesh = new THREE.Mesh(geometry, material);
173
+ const x = (Math.random() - 0.5) * 900;
174
+ const z = (Math.random() - 0.5) * 900;
175
+ const y = yOffset;
176
+ mesh.position.set(x, y, z);
177
+ mesh.castShadow = true;
178
+ mesh.receiveShadow = true;
179
+ scene.add(mesh);
180
+ }}
181
+ }}
182
+
183
+ // ๋‚˜๋ฌด ์ƒ์„ฑ
184
+ const treeGeometry = new THREE.ConeGeometry(2, 8, 8);
185
+ const treeMaterial = new THREE.MeshStandardMaterial({{ color: 0x0d5c0d }});
186
+ addEnvironmentObject(treeGeometry, treeMaterial, 100, 4);
187
+
188
+ // ๋ฐ”์œ„ ์ƒ์„ฑ
189
+ const rockGeometry = new THREE.DodecahedronGeometry(2);
190
+ const rockMaterial = new THREE.MeshStandardMaterial({{ color: 0x666666 }});
191
+ addEnvironmentObject(rockGeometry, rockMaterial, 50, 1);
192
+
193
+ // ์ด๋™ ์†๋„
194
+ const MOVE_SPEED = 0.5;
195
+
196
+ // ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ฃจํ”„
197
+ function animate() {{
198
+ requestAnimationFrame(animate);
199
+
200
+ // ํ‚ค๋ณด๋“œ ์ž…๋ ฅ์— ๋”ฐ๋ฅธ ์ด๋™
201
+ const direction = new THREE.Vector3();
202
+ const rotation = camera.getWorldDirection(new THREE.Vector3());
203
+
204
+ if (moveState.forward) {{
205
+ direction.add(rotation);
206
+ }}
207
+ if (moveState.backward) {{
208
+ direction.sub(rotation);
209
+ }}
210
+ if (moveState.left) {{
211
+ direction.cross(camera.up).negate();
212
+ }}
213
+ if (moveState.right) {{
214
+ direction.cross(camera.up);
215
+ }}
216
+
217
+ direction.y = 0;
218
+ direction.normalize();
219
+
220
+ if (controls.isLocked) {{
221
+ controls.moveRight(-direction.z * MOVE_SPEED);
222
+ controls.moveForward(direction.x * MOVE_SPEED);
223
+
224
+ // ์บ๋ฆญํ„ฐ ์œ„์น˜ ์—…๋ฐ์ดํŠธ
225
+ if (character) {{
226
+ character.position.x = camera.position.x;
227
+ character.position.z = camera.position.z;
228
+
229
+ // ์ด๋™ ๋ฐฉํ–ฅ์œผ๋กœ ์บ๋ฆญํ„ฐ ํšŒ์ „
230
+ if (direction.length() > 0) {{
231
+ const angle = Math.atan2(direction.x, direction.z);
232
+ character.rotation.y = angle;
233
+ }}
234
+ }}
235
+ }}
236
+
237
+ renderer.render(scene, camera);
238
+ }}
239
+ animate();
240
+
241
+ // ์œˆ๋„์šฐ ๋ฆฌ์‚ฌ์ด์ฆˆ ์ฒ˜๋ฆฌ
242
+ window.addEventListener('resize', () => {{
243
+ camera.aspect = window.innerWidth / window.innerHeight;
244
+ camera.updateProjectionMatrix();
245
+ renderer.setSize(window.innerWidth, window.innerHeight);
246
+ }});
247
+ </script>
248
+ </body>
249
+ </html>
250
+ """
251
+ return html_template
252
+
253
+ def process_upload(glb_file):
254
+ # ์—…๋กœ๋“œ๋œ ํŒŒ์ผ ์ฒ˜๋ฆฌ
255
+ upload_dir = "uploads"
256
+ os.makedirs(upload_dir, exist_ok=True)
257
+
258
+ file_path = Path(glb_file.name)
259
+ dest_path = Path(upload_dir) / file_path.name
260
+ shutil.copy(file_path, dest_path)
261
+
262
+ # ๊ฒŒ์ž„ HTML ์ƒ์„ฑ
263
+ game_html = create_game_html(str(dest_path))
264
+
265
+ # HTML ํŒŒ์ผ ์ €์žฅ
266
+ game_path = Path("game.html")
267
+ with open(game_path, "w", encoding="utf-8") as f:
268
+ f.write(game_html)
269
+
270
+ return str(game_path)
271
+
272
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์„ค์ •
273
+ iface = gr.Interface(
274
+ fn=process_upload,
275
+ inputs=[
276
+ gr.File(label="Upload Character Model (GLB)", type="file")
277
+ ],
278
+ outputs=gr.HTML(label="3D Open World Game"),
279
+ title="3D Open World Game Generator",
280
+ description="Upload your character model (GLB) to create a 3D open world game with WASD controls!"
281
+ )
282
+
283
+ if __name__ == "__main__":
284
+ iface.launch()