Imrao commited on
Commit
ae6e5d7
Β·
verified Β·
1 Parent(s): 7df3451

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -34
app.py CHANGED
@@ -2,6 +2,7 @@ import os
2
  import logging
3
  import json
4
  import tempfile
 
5
  import shapely.geometry
6
  import pyprt
7
  import glob
@@ -102,55 +103,61 @@ def _clean_attributes(raw: Dict[str, Any]) -> Dict[str, Any]:
102
  return cleaned
103
 
104
 
105
- def _download_textures(attrs: Dict[str, Any]) -> tuple:
106
  """
107
- Download any texture attributes (values starting with '/texture/') from
108
- the Material API, save them to a temporary directory, and replace the
109
- attribute values with absolute ``file://`` URIs so PRT can load them
110
- from the local filesystem.
111
-
112
- Returns ``(modified_attrs, temp_dir)`` where ``temp_dir`` is the path
113
- of the created directory (or ``None`` if nothing was downloaded).
114
- The caller **must** remove ``temp_dir`` after generation completes.
115
-
116
- Strategy D: browser-side picks /texture/… path β†’ backend downloads &
117
- stores locally β†’ passes absolute path as CGA attribute β†’ PRT resolves
118
- from disk β†’ cleanup after generate_model().
 
 
 
 
 
119
  """
120
- if not MATERIAL_API_BASE:
121
- logger.warning("MATERIAL_API_URL not set β€” texture attributes will not be resolved")
122
- return attrs, None
123
-
124
  texture_attrs = {
125
  k: v for k, v in attrs.items()
126
  if isinstance(v, str) and v.startswith("/texture/")
127
  }
128
  if not texture_attrs:
129
- return attrs, None
 
 
 
 
 
 
 
130
 
131
- temp_dir = tempfile.mkdtemp(prefix="pyprt_tex_")
132
  modified = dict(attrs)
133
 
134
  import requests as _requests
135
  for key, rel_path in texture_attrs.items():
136
  url = f"{MATERIAL_API_BASE}{rel_path}"
137
  filename = os.path.basename(rel_path)
138
- local_path = os.path.join(temp_dir, filename)
 
139
  try:
140
  logger.info(f"Downloading texture '{key}': {url}")
141
  resp = _requests.get(url, timeout=20)
142
  resp.raise_for_status()
143
- with open(local_path, "wb") as fh:
144
- fh.write(resp.content)
145
- # PRT accepts file:// URIs for asset resolution
146
- file_uri = "file:///" + local_path.replace("\\", "/").lstrip("/")
147
- modified[key] = file_uri
148
- logger.info(f"Texture saved β†’ {local_path}")
149
  except Exception as exc:
150
- logger.warning(f"Texture download failed for '{key}' ({url}): {exc}. Using CGA default.")
151
- modified.pop(key, None) # drop so CGA falls back to its default
152
 
153
- return modified, temp_dir
154
 
155
 
156
  # RPKs that control report emission via boolean attributes β€” always force them ON.
@@ -349,8 +356,8 @@ async def generate_model(request: GenerateRequest):
349
 
350
  # Sanitise attributes
351
  clean_attributes = _clean_attributes(request.attributes)
352
- # Strategy D: download any Material-API textures to disk, replace attr values
353
- clean_attributes, tex_temp_dir = _download_textures(clean_attributes)
354
  logger.info(f"Generating with RPK: {rpk_path}")
355
  logger.info(f"Clean attributes: {clean_attributes}")
356
 
@@ -536,8 +543,8 @@ async def generate_i3s(request: GenerateRequest):
536
 
537
  # Sanitise attributes
538
  clean_attributes = _clean_attributes(request.attributes)
539
- # Strategy D: download any Material-API textures to disk, replace attr values
540
- clean_attributes, tex_temp_dir = _download_textures(clean_attributes)
541
 
542
  initial_shape = pyprt.InitialShape(flattened_coords, indices, face_counts)
543
 
@@ -792,7 +799,8 @@ async def get_model_report(request: GenerateRequest):
792
 
793
  # --- Sanitise attributes ---
794
  clean_attributes = _clean_attributes(request.attributes)
795
- clean_attributes, tex_temp_dir = _download_textures(clean_attributes)
 
796
  initial_shape = pyprt.InitialShape(flattened_coords, indices, face_counts)
797
 
798
  # --- Run PyEncoder report pass ---
 
2
  import logging
3
  import json
4
  import tempfile
5
+ import zipfile
6
  import shapely.geometry
7
  import pyprt
8
  import glob
 
103
  return cleaned
104
 
105
 
106
+ def _inject_textures_into_rpk(rpk_path: str, attrs: Dict[str, Any]) -> tuple:
107
  """
108
+ For any texture attribute (value starts with '/texture/'), download the
109
+ texture from the Material API and inject it into a temporary copy of the
110
+ RPK (which is a ZIP file). The attribute value is replaced with the
111
+ RPK-relative path so CGA's asset resolution finds it normally.
112
+
113
+ Returns ``(rpk_to_use, modified_attrs, temp_dir)``.
114
+ - ``rpk_to_use`` : path to use for generation (temp copy if textures injected)
115
+ - ``modified_attrs``: attrs with texture values replaced by RPK-relative paths
116
+ - ``temp_dir`` : directory to clean up after generation (or ``None``)
117
+
118
+ Why RPK injection instead of file:// URIs
119
+ -----------------------------------------
120
+ CGA's ``set(material.colormap, WallTexture)`` resolves asset paths relative
121
+ to the RPK, not the filesystem. Passing a ``file://`` URI as an attribute
122
+ value results in "Unknown Texture" because PRT's asset pipeline never sees
123
+ it. Injecting the texture directly into the RPK ZIP at a known path lets
124
+ PRT find it through the normal resolution mechanism.
125
  """
 
 
 
 
126
  texture_attrs = {
127
  k: v for k, v in attrs.items()
128
  if isinstance(v, str) and v.startswith("/texture/")
129
  }
130
  if not texture_attrs:
131
+ return rpk_path, attrs, None
132
+ if not MATERIAL_API_BASE:
133
+ logger.warning("MATERIAL_API_URL not set β€” texture attributes will use CGA defaults")
134
+ return rpk_path, attrs, None
135
+
136
+ temp_dir = tempfile.mkdtemp(prefix="pyprt_rpk_")
137
+ temp_rpk = os.path.join(temp_dir, os.path.basename(rpk_path))
138
+ shutil.copy(rpk_path, temp_rpk)
139
 
 
140
  modified = dict(attrs)
141
 
142
  import requests as _requests
143
  for key, rel_path in texture_attrs.items():
144
  url = f"{MATERIAL_API_BASE}{rel_path}"
145
  filename = os.path.basename(rel_path)
146
+ # Store under assets/custom/ inside the RPK to avoid collision with existing assets
147
+ rpk_asset_path = f"assets/custom/{filename}"
148
  try:
149
  logger.info(f"Downloading texture '{key}': {url}")
150
  resp = _requests.get(url, timeout=20)
151
  resp.raise_for_status()
152
+ with zipfile.ZipFile(temp_rpk, "a", compression=zipfile.ZIP_STORED) as zf:
153
+ zf.writestr(rpk_asset_path, resp.content)
154
+ modified[key] = rpk_asset_path
155
+ logger.info(f"Texture '{key}' injected into RPK as: {rpk_asset_path}")
 
 
156
  except Exception as exc:
157
+ logger.warning(f"Texture injection failed for '{key}' ({url}): {exc}. Using CGA default.")
158
+ modified.pop(key, None)
159
 
160
+ return temp_rpk, modified, temp_dir
161
 
162
 
163
  # RPKs that control report emission via boolean attributes β€” always force them ON.
 
356
 
357
  # Sanitise attributes
358
  clean_attributes = _clean_attributes(request.attributes)
359
+ # Inject any Material-API textures into a temp RPK copy so CGA resolves them
360
+ rpk_path, clean_attributes, tex_temp_dir = _inject_textures_into_rpk(rpk_path, clean_attributes)
361
  logger.info(f"Generating with RPK: {rpk_path}")
362
  logger.info(f"Clean attributes: {clean_attributes}")
363
 
 
543
 
544
  # Sanitise attributes
545
  clean_attributes = _clean_attributes(request.attributes)
546
+ # Inject any Material-API textures into a temp RPK copy so CGA resolves them
547
+ rpk_path, clean_attributes, tex_temp_dir = _inject_textures_into_rpk(rpk_path, clean_attributes)
548
 
549
  initial_shape = pyprt.InitialShape(flattened_coords, indices, face_counts)
550
 
 
799
 
800
  # --- Sanitise attributes ---
801
  clean_attributes = _clean_attributes(request.attributes)
802
+ # Inject any Material-API textures into a temp RPK copy so CGA resolves them
803
+ rpk_path, clean_attributes, tex_temp_dir = _inject_textures_into_rpk(rpk_path, clean_attributes)
804
  initial_shape = pyprt.InitialShape(flattened_coords, indices, face_counts)
805
 
806
  # --- Run PyEncoder report pass ---