ArturoNereu commited on
Commit
90d9dd9
·
1 Parent(s): 5b29015

Added HF emoji

Browse files
Files changed (3) hide show
  1. app.py +4 -0
  2. backend/storage.py +7 -39
  3. frontend/game_viewer.html +64 -6
app.py CHANGED
@@ -111,6 +111,10 @@ def get_viewer_html(scene_id="welcome"):
111
  if not scene_data:
112
  return '<div style="color: red;">Scene not found</div>'
113
 
 
 
 
 
114
  # Read the viewer HTML template
115
  viewer_path = os.path.join(os.path.dirname(__file__), "frontend", "game_viewer.html")
116
  with open(viewer_path, 'r') as f:
 
111
  if not scene_data:
112
  return '<div style="color: red;">Scene not found</div>'
113
 
114
+ # Add the static assets base URL for model loading
115
+ # On local dev: http://localhost:8000, on HF Spaces: empty (relative)
116
+ scene_data["static_base_url"] = FASTAPI_INTERNAL if not IS_HF_SPACES else ""
117
+
118
  # Read the viewer HTML template
119
  viewer_path = os.path.join(os.path.dirname(__file__), "frontend", "game_viewer.html")
120
  with open(viewer_path, 'r') as f:
backend/storage.py CHANGED
@@ -80,47 +80,15 @@ def initialize_default_scene():
80
  background_color="#1a0a20", # Dark purple (will be overridden by skybox)
81
  )
82
 
83
- # Create some visually interesting starter objects
84
  objects = [
85
- # Red cube - classic demo object
86
  create_game_object(
87
- object_type="cube",
88
- name="RedCube",
89
- position=create_vector3(3, 1, -3),
90
- scale=create_vector3(2, 2, 2),
91
- material=create_material(color="#ff4444", metalness=0.3, roughness=0.4),
92
- ),
93
- # Blue sphere - metallic
94
- create_game_object(
95
- object_type="sphere",
96
- name="BlueSphere",
97
- position=create_vector3(-4, 1.5, -2),
98
  scale=create_vector3(3, 3, 3),
99
- material=create_material(color="#4488ff", metalness=0.8, roughness=0.2),
100
- ),
101
- # Green cylinder
102
- create_game_object(
103
- object_type="cylinder",
104
- name="GreenCylinder",
105
- position=create_vector3(0, 1.5, -6),
106
- scale=create_vector3(1.5, 3, 1.5),
107
- material=create_material(color="#44ff44", metalness=0.2, roughness=0.6),
108
- ),
109
- # Yellow torus
110
- create_game_object(
111
- object_type="torus",
112
- name="YellowTorus",
113
- position=create_vector3(-3, 2, -7),
114
- scale=create_vector3(2, 2, 2),
115
- material=create_material(color="#ffcc00", metalness=0.6, roughness=0.3),
116
- ),
117
- # Purple cone
118
- create_game_object(
119
- object_type="cone",
120
- name="PurpleCone",
121
- position=create_vector3(5, 1, -5),
122
- scale=create_vector3(1.5, 3, 1.5),
123
- material=create_material(color="#aa44ff", metalness=0.4, roughness=0.5),
124
  ),
125
  ]
126
 
@@ -152,7 +120,7 @@ def initialize_default_scene():
152
  storage.save(scene)
153
  print(f"✓ Initialized Welcome Scene (ID: welcome)")
154
  print(f" - 25x25 world with sunset skybox")
155
- print(f" - 5 starter objects (cube, sphere, cylinder, torus, cone)")
156
  print(f" - FPS physics controller ready")
157
 
158
 
 
80
  background_color="#1a0a20", # Dark purple (will be overridden by skybox)
81
  )
82
 
83
+ # Hugging Face emoji model at center (animated)
84
  objects = [
 
85
  create_game_object(
86
+ object_type="model",
87
+ name="HuggingFace",
88
+ position=create_vector3(0, 1.5, 0),
 
 
 
 
 
 
 
 
89
  scale=create_vector3(3, 3, 3),
90
+ model_path="/static/models/Norod78/huggingface_emoji.glb",
91
+ metadata={"animate": True, "baseY": 1.5},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  ),
93
  ]
94
 
 
120
  storage.save(scene)
121
  print(f"✓ Initialized Welcome Scene (ID: welcome)")
122
  print(f" - 25x25 world with sunset skybox")
123
+ print(f" - Hugging Face emoji model at center")
124
  print(f" - FPS physics controller ready")
125
 
126
 
frontend/game_viewer.html CHANGED
@@ -75,6 +75,7 @@
75
 
76
  // Particle systems
77
  let particleSystems = new Map();
 
78
 
79
  // UI overlay container
80
  let uiContainer = null;
@@ -555,13 +556,13 @@
555
  canvas.height = size;
556
  const ctx = canvas.getContext('2d');
557
 
558
- // Background - blueprint blue
559
- ctx.fillStyle = '#1a4a6e';
560
  ctx.fillRect(0, 0, size, size);
561
 
562
- // Major grid lines (1 unit = 64px at 512 texture for 8 divisions)
563
  const majorSpacing = size / 8;
564
- ctx.strokeStyle = '#2a6a9e';
565
  ctx.lineWidth = 2;
566
  ctx.beginPath();
567
  for (let i = 0; i <= 8; i++) {
@@ -573,9 +574,9 @@
573
  }
574
  ctx.stroke();
575
 
576
- // Minor grid lines (subdivisions)
577
  const minorSpacing = majorSpacing / 4;
578
- ctx.strokeStyle = '#1f5a8e';
579
  ctx.lineWidth = 1;
580
  ctx.beginPath();
581
  for (let i = 0; i <= 32; i++) {
@@ -831,6 +832,47 @@
831
  case 'torus':
832
  geometry = new THREE.TorusGeometry(obj.scale.x, obj.scale.x * 0.4, 16, 100);
833
  break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
834
  default:
835
  console.warn('Unknown object type:', obj.type);
836
  return;
@@ -1055,6 +1097,9 @@
1055
  // Update particle systems
1056
  updateParticleSystems(delta);
1057
 
 
 
 
1058
  // Render using composer (for outlines) instead of direct renderer
1059
  if (composer) {
1060
  composer.render();
@@ -1942,6 +1987,19 @@
1942
  }
1943
  }
1944
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1945
  function updateParticleSystems(delta) {
1946
  particleSystems.forEach((system, id) => {
1947
  const positions = system.geometry.attributes.position.array;
 
75
 
76
  // Particle systems
77
  let particleSystems = new Map();
78
+ let animatedModels = []; // Models with animation metadata
79
 
80
  // UI overlay container
81
  let uiContainer = null;
 
556
  canvas.height = size;
557
  const ctx = canvas.getContext('2d');
558
 
559
+ // Background - bright blue
560
+ ctx.fillStyle = '#2080dd';
561
  ctx.fillRect(0, 0, size, size);
562
 
563
+ // Major grid lines - white
564
  const majorSpacing = size / 8;
565
+ ctx.strokeStyle = '#ffffff';
566
  ctx.lineWidth = 2;
567
  ctx.beginPath();
568
  for (let i = 0; i <= 8; i++) {
 
574
  }
575
  ctx.stroke();
576
 
577
+ // Minor grid lines - white (semi-transparent)
578
  const minorSpacing = majorSpacing / 4;
579
+ ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
580
  ctx.lineWidth = 1;
581
  ctx.beginPath();
582
  for (let i = 0; i <= 32; i++) {
 
832
  case 'torus':
833
  geometry = new THREE.TorusGeometry(obj.scale.x, obj.scale.x * 0.4, 16, 100);
834
  break;
835
+ case 'model':
836
+ // Load GLB/GLTF model asynchronously
837
+ if (obj.model_path) {
838
+ const loader = new GLTFLoader();
839
+ // Use static_base_url from scene data for correct server
840
+ const staticBase = sceneData.static_base_url || '';
841
+ const modelUrl = staticBase + obj.model_path;
842
+ loader.load(
843
+ modelUrl,
844
+ (gltf) => {
845
+ const model = gltf.scene;
846
+ model.position.set(obj.position.x, obj.position.y, obj.position.z);
847
+ model.scale.set(obj.scale.x, obj.scale.y, obj.scale.z);
848
+ model.rotation.set(
849
+ THREE.MathUtils.degToRad(obj.rotation.x),
850
+ THREE.MathUtils.degToRad(obj.rotation.y),
851
+ THREE.MathUtils.degToRad(obj.rotation.z)
852
+ );
853
+ model.userData = {
854
+ id: obj.id,
855
+ name: obj.name,
856
+ type: 'model',
857
+ isSceneObject: true,
858
+ animate: obj.metadata?.animate || false,
859
+ baseY: obj.metadata?.baseY || obj.position.y,
860
+ };
861
+ scene.add(model);
862
+
863
+ // Track animated models
864
+ if (obj.metadata?.animate) {
865
+ animatedModels.push(model);
866
+ console.log(`Loaded animated model: ${obj.name} from ${modelUrl}`);
867
+ } else {
868
+ console.log(`Loaded model: ${obj.name} from ${modelUrl}`);
869
+ }
870
+ },
871
+ undefined,
872
+ (error) => console.error(`Failed to load model ${modelUrl}:`, error)
873
+ );
874
+ }
875
+ return; // Skip the rest of the geometry/material creation
876
  default:
877
  console.warn('Unknown object type:', obj.type);
878
  return;
 
1097
  // Update particle systems
1098
  updateParticleSystems(delta);
1099
 
1100
+ // Update animated models (rotate + bob up/down)
1101
+ updateAnimatedModels(time);
1102
+
1103
  // Render using composer (for outlines) instead of direct renderer
1104
  if (composer) {
1105
  composer.render();
 
1987
  }
1988
  }
1989
 
1990
+ function updateAnimatedModels(time) {
1991
+ // Animate models with rotate + sine wave bobbing
1992
+ const timeInSeconds = time / 1000;
1993
+ animatedModels.forEach(model => {
1994
+ // Rotate slowly around Y axis
1995
+ model.rotation.y = timeInSeconds * 0.5;
1996
+
1997
+ // Bob up and down with sine wave
1998
+ const baseY = model.userData.baseY || 1.5;
1999
+ model.position.y = baseY + Math.sin(timeInSeconds * 2) * 0.3;
2000
+ });
2001
+ }
2002
+
2003
  function updateParticleSystems(delta) {
2004
  particleSystems.forEach((system, id) => {
2005
  const positions = system.geometry.attributes.position.array;