aiqtech commited on
Commit
644fbbd
ยท
verified ยท
1 Parent(s): 0d2ef55

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +227 -93
app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import gradio as gr
2
  import json
3
  import os
@@ -9,11 +10,11 @@ def create_game_html(model_path):
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;
@@ -23,6 +24,15 @@ def create_game_html(model_path):
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">
@@ -35,13 +45,20 @@ def create_game_html(model_path):
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';
@@ -51,11 +68,22 @@ def create_game_html(model_path):
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
 
@@ -64,40 +92,34 @@ def create_game_html(model_path):
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
 
@@ -106,6 +128,103 @@ def create_game_html(model_path):
106
  controls.lock();
107
  }});
108
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  // ์ง€ํ˜• ์ƒ์„ฑ
110
  const terrainGeometry = new THREE.PlaneGeometry(1000, 1000, 100, 100);
111
  const terrainMaterial = new THREE.MeshStandardMaterial({{
@@ -134,29 +253,6 @@ def create_game_html(model_path):
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({{
@@ -190,48 +286,87 @@ def create_game_html(model_path):
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);
@@ -241,7 +376,9 @@ def create_game_html(model_path):
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>
@@ -250,7 +387,6 @@ def create_game_html(model_path):
250
  """
251
  return html_template
252
 
253
-
254
  def process_upload(glb_file):
255
  if glb_file is None:
256
  return None
@@ -259,7 +395,6 @@ def process_upload(glb_file):
259
  upload_dir = "uploads"
260
  os.makedirs(upload_dir, exist_ok=True)
261
 
262
- # filepath ํƒ€์ž…์œผ๋กœ ๋ฐ›์€ ํŒŒ์ผ ์ฒ˜๋ฆฌ
263
  file_path = Path(glb_file)
264
  dest_path = Path(upload_dir) / file_path.name
265
  shutil.copy(file_path, dest_path)
@@ -274,22 +409,21 @@ def process_upload(glb_file):
274
 
275
  return str(game_path)
276
 
277
- # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์„ค์ • - type์„ 'filepath'๋กœ ๋ณ€๊ฒฝ
278
  iface = gr.Interface(
279
  fn=process_upload,
280
  inputs=[
281
  gr.File(
282
  label="Upload Character Model (GLB)",
283
- type="filepath", # 'file' ๋Œ€์‹  'filepath' ์‚ฌ์šฉ
284
- file_types=[".glb"] # GLB ํŒŒ์ผ๋งŒ ํ—ˆ์šฉ
285
  )
286
  ],
287
  outputs=gr.HTML(label="3D Open World Game"),
288
- title="3D Open World Game Generator",
289
- description="Upload your character model (GLB) to create a 3D open world game with WASD controls!",
290
- examples=[], # ์˜ˆ์ œ ํŒŒ์ผ์ด ์žˆ๋‹ค๋ฉด ์—ฌ๊ธฐ์— ์ถ”๊ฐ€
291
  cache_examples=False
292
  )
293
 
294
  if __name__ == "__main__":
295
- iface.launch()
 
1
+ # app.py
2
  import gradio as gr
3
  import json
4
  import os
 
10
  <!DOCTYPE html>
11
  <html>
12
  <head>
13
+ <title>Advanced 3D Open World Game</title>
14
  <style>
15
  body {{ margin: 0; }}
16
  canvas {{ display: block; }}
17
+ #info {{
18
  position: absolute;
19
  top: 10px;
20
  left: 10px;
 
24
  border-radius: 5px;
25
  font-family: Arial, sans-serif;
26
  }}
27
+ #weather-controls {{
28
+ position: absolute;
29
+ top: 10px;
30
+ right: 10px;
31
+ background: rgba(0,0,0,0.7);
32
+ color: white;
33
+ padding: 10px;
34
+ border-radius: 5px;
35
+ }}
36
  </style>
37
  <script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
38
  <script type="importmap">
 
45
  </script>
46
  </head>
47
  <body>
48
+ <div id="info">
49
  Controls:<br>
50
+ W/S/A/D - Move<br>
51
+ SPACE - Jump<br>
52
+ Mouse - Look Around<br>
53
+ HP: <span id="hp">100</span>
54
+ </div>
55
+ <div id="weather-controls">
56
+ Weather:
57
+ <select id="weather-select" onchange="changeWeather(this.value)">
58
+ <option value="clear">Clear</option>
59
+ <option value="rain">Rain</option>
60
+ <option value="fog">Fog</option>
61
+ </select>
62
  </div>
63
  <script type="module">
64
  import * as THREE from 'three';
 
68
  // ๊ธฐ๋ณธ ์„ค์ •
69
  const scene = new THREE.Scene();
70
  const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
71
+ const renderer = new THREE.WebGLRenderer({{ antialias: true }});
72
  renderer.setSize(window.innerWidth, window.innerHeight);
73
  renderer.shadowMap.enabled = true;
74
  document.body.appendChild(renderer.domElement);
75
 
76
+ // ๋ฌผ๋ฆฌ ์‹œ์Šคํ…œ ๋ณ€์ˆ˜
77
+ const gravity = -0.5;
78
+ let velocity = new THREE.Vector3();
79
+ let isJumping = false;
80
+ let canJump = true;
81
+ let playerHeight = 2;
82
+
83
+ // ์บ๋ฆญํ„ฐ ์ƒํƒœ
84
+ let hp = 100;
85
+ const hpElement = document.getElementById('hp');
86
+
87
  // ํฌ์ธํ„ฐ ๋ฝ ์ปจํŠธ๋กค
88
  const controls = new PointerLockControls(camera, document.body);
89
 
 
92
  forward: false,
93
  backward: false,
94
  left: false,
95
+ right: false,
96
+ jump: false
97
  }};
98
 
99
  document.addEventListener('keydown', (event) => {{
100
  switch (event.code) {{
101
+ case 'KeyW': moveState.forward = true; break;
102
+ case 'KeyS': moveState.backward = true; break;
103
+ case 'KeyA': moveState.left = true; break;
104
+ case 'KeyD': moveState.right = true; break;
105
+ case 'Space':
106
+ if (canJump) {{
107
+ moveState.jump = true;
108
+ velocity.y = 10;
109
+ isJumping = true;
110
+ canJump = false;
111
+ }}
112
  break;
113
  }}
114
  }});
115
 
116
  document.addEventListener('keyup', (event) => {{
117
  switch (event.code) {{
118
+ case 'KeyW': moveState.forward = false; break;
119
+ case 'KeyS': moveState.backward = false; break;
120
+ case 'KeyA': moveState.left = false; break;
121
+ case 'KeyD': moveState.right = false; break;
122
+ case 'Space': moveState.jump = false; break;
 
 
 
 
 
 
 
123
  }}
124
  }});
125
 
 
128
  controls.lock();
129
  }});
130
 
131
+ // ๋‚ ์”จ ์‹œ์Šคํ…œ
132
+ let raindrops = [];
133
+ const rainCount = 1000;
134
+ const rainGeometry = new THREE.BufferGeometry();
135
+ const rainMaterial = new THREE.PointsMaterial({{
136
+ color: 0xaaaaaa,
137
+ size: 0.1,
138
+ transparent: true
139
+ }});
140
+
141
+ function createRain() {{
142
+ const positions = new Float32Array(rainCount * 3);
143
+ for (let i = 0; i < rainCount * 3; i += 3) {{
144
+ positions[i] = (Math.random() - 0.5) * 1000;
145
+ positions[i + 1] = Math.random() * 500;
146
+ positions[i + 2] = (Math.random() - 0.5) * 1000;
147
+ }}
148
+ rainGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
149
+ const rain = new THREE.Points(rainGeometry, rainMaterial);
150
+ scene.add(rain);
151
+ return rain;
152
+ }}
153
+
154
+ let rain = createRain();
155
+ rain.visible = false;
156
+
157
+ // ์•ˆ๊ฐœ ์„ค์ •
158
+ scene.fog = new THREE.Fog(0xcce0ff, 1, 1000);
159
+ scene.fog.near = 1;
160
+ scene.fog.far = 1000;
161
+
162
+ // ๋‚ ์”จ ๋ณ€๊ฒฝ ํ•จ์ˆ˜
163
+ window.changeWeather = function(weather) {{
164
+ switch(weather) {{
165
+ case 'clear':
166
+ rain.visible = false;
167
+ scene.fog.near = 1;
168
+ scene.fog.far = 1000;
169
+ break;
170
+ case 'rain':
171
+ rain.visible = true;
172
+ scene.fog.near = 1;
173
+ scene.fog.far = 100;
174
+ break;
175
+ case 'fog':
176
+ rain.visible = false;
177
+ scene.fog.near = 1;
178
+ scene.fog.far = 50;
179
+ break;
180
+ }}
181
+ }};
182
+
183
+ // NPC ์‹œ์Šคํ…œ
184
+ class NPC {{
185
+ constructor(position) {{
186
+ const geometry = new THREE.CapsuleGeometry(1, 2, 4, 8);
187
+ const material = new THREE.MeshPhongMaterial({{ color: 0xff0000 }});
188
+ this.mesh = new THREE.Mesh(geometry, material);
189
+ this.mesh.position.copy(position);
190
+ this.mesh.castShadow = true;
191
+ this.velocity = new THREE.Vector3();
192
+ this.direction = new THREE.Vector3();
193
+ scene.add(this.mesh);
194
+ }}
195
+
196
+ update(playerPosition) {{
197
+ // ํ”Œ๋ ˆ์ด์–ด ๋ฐฉํ–ฅ์œผ๋กœ ์ด๋™
198
+ this.direction.subVectors(playerPosition, this.mesh.position);
199
+ this.direction.normalize();
200
+ this.velocity.add(this.direction.multiplyScalar(0.1));
201
+ this.velocity.multiplyScalar(0.95); // ๊ฐ์†
202
+ this.mesh.position.add(this.velocity);
203
+
204
+ // ํ”Œ๋ ˆ์ด์–ด์™€์˜ ์ถฉ๋Œ ๊ฒ€์‚ฌ
205
+ const distance = this.mesh.position.distanceTo(playerPosition);
206
+ if (distance < 2) {{
207
+ hp -= 1;
208
+ hpElement.textContent = hp;
209
+ if (hp <= 0) {{
210
+ alert('Game Over!');
211
+ location.reload();
212
+ }}
213
+ }}
214
+ }}
215
+ }}
216
+
217
+ // NPC ์ƒ์„ฑ
218
+ const npcs = [];
219
+ for (let i = 0; i < 5; i++) {{
220
+ const position = new THREE.Vector3(
221
+ (Math.random() - 0.5) * 100,
222
+ 2,
223
+ (Math.random() - 0.5) * 100
224
+ );
225
+ npcs.push(new NPC(position));
226
+ }}
227
+
228
  // ์ง€ํ˜• ์ƒ์„ฑ
229
  const terrainGeometry = new THREE.PlaneGeometry(1000, 1000, 100, 100);
230
  const terrainMaterial = new THREE.MeshStandardMaterial({{
 
253
  directionalLight.castShadow = true;
254
  scene.add(directionalLight);
255
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  // ๋ฐฐ๊ฒฝ ํ•˜๋Š˜ ์„ค์ •
257
  const skyGeometry = new THREE.SphereGeometry(500, 32, 32);
258
  const skyMaterial = new THREE.MeshBasicMaterial({{
 
286
  const rockMaterial = new THREE.MeshStandardMaterial({{ color: 0x666666 }});
287
  addEnvironmentObject(rockGeometry, rockMaterial, 50, 1);
288
 
289
+ // ์บ๋ฆญํ„ฐ ๋ชจ๋ธ ๋กœ๋“œ
290
+ let character;
291
+ const loader = new GLTFLoader();
292
+ loader.load('{model_path}', (gltf) => {{
293
+ character = gltf.scene;
294
+ character.scale.set(0.5, 0.5, 0.5);
295
+ character.position.y = 2;
296
+ character.castShadow = true;
297
+ character.receiveShadow = true;
298
+ scene.add(character);
299
+
300
+ // ์บ๋ฆญํ„ฐ ๊ทธ๋ฆผ์ž ์„ค์ •
301
+ character.traverse((node) => {{
302
+ if (node.isMesh) {{
303
+ node.castShadow = true;
304
+ node.receiveShadow = true;
305
+ }}
306
+ }});
307
+ }});
308
+
309
+ // ์นด๋ฉ”๋ผ ์ดˆ๊ธฐ ์œ„์น˜ ์„ค์ •
310
+ camera.position.set(0, playerHeight, 0);
311
 
312
  // ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ฃจํ”„
313
  function animate() {{
314
  requestAnimationFrame(animate);
315
 
316
+ if (controls.isLocked) {{
317
+ // ์ด๋™ ์ฒ˜๋ฆฌ
318
+ const direction = new THREE.Vector3();
319
+ const rotation = camera.getWorldDirection(new THREE.Vector3());
320
 
321
+ if (moveState.forward) direction.add(rotation);
322
+ if (moveState.backward) direction.sub(rotation);
323
+ if (moveState.left) direction.cross(camera.up).negate();
324
+ if (moveState.right) direction.cross(camera.up);
 
 
 
 
 
 
 
 
325
 
326
+ direction.y = 0;
327
+ direction.normalize();
328
 
329
+ // ๋ฌผ๋ฆฌ ์‹œ์Šคํ…œ ์ ์šฉ
330
+ velocity.y += gravity;
331
+ camera.position.y += velocity.y * 0.1;
332
+
333
+ // ๋ฐ”๋‹ฅ ์ถฉ๋Œ ๊ฒ€์‚ฌ
334
+ if (camera.position.y <= playerHeight) {{
335
+ camera.position.y = playerHeight;
336
+ velocity.y = 0;
337
+ isJumping = false;
338
+ canJump = true;
339
+ }}
340
+
341
+ // ์ด๋™ ์†๋„ ์ ์šฉ
342
+ const moveSpeed = 0.5;
343
+ controls.moveRight(-direction.z * moveSpeed);
344
+ controls.moveForward(direction.x * moveSpeed);
345
 
346
+ // ์บ๋ฆญํ„ฐ ๋ชจ๋ธ ์—…๋ฐ์ดํŠธ
347
  if (character) {{
348
+ character.position.copy(camera.position);
349
+ character.position.y -= playerHeight;
 
 
350
  if (direction.length() > 0) {{
351
  const angle = Math.atan2(direction.x, direction.z);
352
  character.rotation.y = angle;
353
  }}
354
  }}
355
+
356
+ // ๋น„ ์—…๋ฐ์ดํŠธ
357
+ if (rain.visible) {{
358
+ const positions = rain.geometry.attributes.position.array;
359
+ for (let i = 1; i < positions.length; i += 3) {{
360
+ positions[i] -= 2;
361
+ if (positions[i] < 0) {{
362
+ positions[i] = 500;
363
+ }}
364
+ }}
365
+ rain.geometry.attributes.position.needsUpdate = true;
366
+ }}
367
+
368
+ // NPC ์—…๋ฐ์ดํŠธ
369
+ npcs.forEach(npc => npc.update(camera.position));
370
  }}
371
 
372
  renderer.render(scene, camera);
 
376
  // ์œˆ๋„์šฐ ๋ฆฌ์‚ฌ์ด์ฆˆ ์ฒ˜๋ฆฌ
377
  window.addEventListener('resize', () => {{
378
  camera.aspect = window.innerWidth / window.innerHeight;
379
+
380
+
381
+ camera.updateProjectionMatrix();
382
  renderer.setSize(window.innerWidth, window.innerHeight);
383
  }});
384
  </script>
 
387
  """
388
  return html_template
389
 
 
390
  def process_upload(glb_file):
391
  if glb_file is None:
392
  return None
 
395
  upload_dir = "uploads"
396
  os.makedirs(upload_dir, exist_ok=True)
397
 
 
398
  file_path = Path(glb_file)
399
  dest_path = Path(upload_dir) / file_path.name
400
  shutil.copy(file_path, dest_path)
 
409
 
410
  return str(game_path)
411
 
412
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์„ค์ •
413
  iface = gr.Interface(
414
  fn=process_upload,
415
  inputs=[
416
  gr.File(
417
  label="Upload Character Model (GLB)",
418
+ type="filepath",
419
+ file_types=[".glb"]
420
  )
421
  ],
422
  outputs=gr.HTML(label="3D Open World Game"),
423
+ title="Advanced 3D Open World Game Generator",
424
+ description="Upload your character model (GLB) to create a 3D open world game with advanced features!",
 
425
  cache_examples=False
426
  )
427
 
428
  if __name__ == "__main__":
429
+ iface.launch()