From c3c56a130bc58f06ffbf0fb094cc9ba3efafc212 Mon Sep 17 00:00:00 2001 From: Alex Fernandez Date: Wed, 20 May 2026 17:20:14 +0200 Subject: [PATCH 01/18] feat: Add web view component (WIP) --- .gitignore | 9 + examples/00-view-usd.py | 2 +- examples/01-vtk-asset-resolver.py | 2 +- examples/02-convert-types.py | 4 +- examples/03-web-view.py | 16 + examples/04-export-viewer-html.py | 43 +++ examples/{ => assets}/display_color.usda | 0 examples/assets/display_color_vertex.usda | 23 ++ examples/{ => assets}/display_color_vtk.usda | 2 +- .../assets/display_color_vtk_webprep.usda | 38 ++ examples/assets/page_template.html | 123 ++++++ examples/{ => assets}/robot_walk_idle.usdz | Bin examples/{ => assets}/sphere.vtk | Bin examples/assets/stress.vtk | Bin 0 -> 71411 bytes examples/{ => assets}/warp_demo_mesh.usda | 0 pyproject.toml | 2 + src/ansys/tools/usdviewer/__init__.py | 4 + src/ansys/tools/usdviewer/vtk_converter.py | 81 +++- src/ansys/tools/usdviewer/web/__init__.py | 8 + src/ansys/tools/usdviewer/web/glb.py | 357 ++++++++++++++++++ .../tools/usdviewer/web/glb_template.html | 140 +++++++ src/ansys/tools/usdviewer/web/mesh.py | 113 ++++++ .../tools/usdviewer/web/mesh_template.html | 150 ++++++++ src/ansys/tools/usdviewer/web/templates.py | 36 ++ src/ansys/tools/usdviewer/web/viewer.py | 143 +++++++ tests/test_conversion.py | 74 ++++ tests/test_web_viewer.py | 118 ++++++ 27 files changed, 1468 insertions(+), 20 deletions(-) create mode 100644 examples/03-web-view.py create mode 100644 examples/04-export-viewer-html.py rename examples/{ => assets}/display_color.usda (100%) create mode 100644 examples/assets/display_color_vertex.usda rename examples/{ => assets}/display_color_vtk.usda (92%) create mode 100644 examples/assets/display_color_vtk_webprep.usda create mode 100644 examples/assets/page_template.html rename examples/{ => assets}/robot_walk_idle.usdz (100%) rename examples/{ => assets}/sphere.vtk (100%) create mode 100644 examples/assets/stress.vtk rename examples/{ => assets}/warp_demo_mesh.usda (100%) create mode 100644 src/ansys/tools/usdviewer/web/__init__.py create mode 100644 src/ansys/tools/usdviewer/web/glb.py create mode 100644 src/ansys/tools/usdviewer/web/glb_template.html create mode 100644 src/ansys/tools/usdviewer/web/mesh.py create mode 100644 src/ansys/tools/usdviewer/web/mesh_template.html create mode 100644 src/ansys/tools/usdviewer/web/templates.py create mode 100644 src/ansys/tools/usdviewer/web/viewer.py create mode 100644 tests/test_web_viewer.py diff --git a/.gitignore b/.gitignore index 3f0a9c2..608f662 100644 --- a/.gitignore +++ b/.gitignore @@ -185,7 +185,16 @@ not_a_dir *usd_build/ *OpenUSD/ *install_dir/ +*oneTBB/ +*tbb_wasm/ +*tbb_wasm_build/ +*usd_wasm/ +*openusd_wasm_build/ +*emsdk/ examples/sphere.usda +examples/*_viewer.html +examples/*_page.html +examples/*_webprep.usda /ALL_BUILD* /CMakeCache.txt diff --git a/examples/00-view-usd.py b/examples/00-view-usd.py index 8ede622..4d67d7e 100644 --- a/examples/00-view-usd.py +++ b/examples/00-view-usd.py @@ -31,7 +31,7 @@ from ansys.tools.usdviewer.viewer import USDViewer # Load USD file -path = r"display_color.usda" +path = r"assets/display_color_vertex.usda" viewer = USDViewer(title="USD Viewer", size=(800, 800)) viewer.load_usd(path) viewer.show() diff --git a/examples/01-vtk-asset-resolver.py b/examples/01-vtk-asset-resolver.py index 26efc8c..64ce750 100644 --- a/examples/01-vtk-asset-resolver.py +++ b/examples/01-vtk-asset-resolver.py @@ -31,7 +31,7 @@ from ansys.tools.usdviewer.viewer import USDViewer -vtk_asset_path = r"display_color_vtk.usda" +vtk_asset_path = r"assets/display_color_vtk.usda" # Initialize the USD Viewer. viewer = USDViewer(title="USD Viewer", size=(800, 800)) # Load USD file with VTK asset. diff --git a/examples/02-convert-types.py b/examples/02-convert-types.py index e18b7e4..71bf94d 100644 --- a/examples/02-convert-types.py +++ b/examples/02-convert-types.py @@ -36,13 +36,13 @@ import pyvista # Read a simple VTK file -sphere = pyvista.read("sphere.vtk") +sphere = pyvista.read("assets/sphere.vtk") # Create a new USD stage stage = Usd.Stage.CreateNew("sphere.usda") # Convert VTK to USD and add to stage -stage = VTKConverter.convert_vtk_file_to_usd("sphere.vtk", stage) +stage = VTKConverter.convert_vtk_file_to_usd("assets/sphere.vtk", stage) # View the USD file in the USD Viewer viewer = USDViewer(title="USD Viewer", size=(800, 800)) diff --git a/examples/03-web-view.py b/examples/03-web-view.py new file mode 100644 index 0000000..c0ac2e9 --- /dev/null +++ b/examples/03-web-view.py @@ -0,0 +1,16 @@ +# Copyright (C) 2025 - 2026 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +"""Generate and open a browser-based USD viewer page.""" + +import webbrowser + +from ansys.tools.usdviewer.web.viewer import export_viewer_html + + +if __name__ == "__main__": + html_path = export_viewer_html("assets/display_color_vtk.usda") + print(f"Viewer page generated at: {html_path}") + print("Controls: left-drag orbit, right-drag pan, mouse wheel zoom.") + webbrowser.open(html_path.resolve().as_uri()) + diff --git a/examples/04-export-viewer-html.py b/examples/04-export-viewer-html.py new file mode 100644 index 0000000..82b4bf0 --- /dev/null +++ b/examples/04-export-viewer-html.py @@ -0,0 +1,43 @@ +# Copyright (C) 2025 - 2026 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +"""Export a USD viewer and embed it inside a standalone webpage via iframe. + +Running this script produces two files in the same directory as the source +asset: + +* ``display_color_vtk_viewer.html`` – the self-contained Three.js viewer +* ``display_color_vtk_page.html`` – a demo webpage that hosts the viewer + inside an `` + + +

+ The viewer above is a self-contained {viewer_filename} + file generated by + export_viewer_html() and embedded here with a single + <iframe> tag. Replace the + src attribute with any + *_viewer.html file produced by the same function to + display a different model. +

+ + + diff --git a/examples/robot_walk_idle.usdz b/examples/assets/robot_walk_idle.usdz similarity index 100% rename from examples/robot_walk_idle.usdz rename to examples/assets/robot_walk_idle.usdz diff --git a/examples/sphere.vtk b/examples/assets/sphere.vtk similarity index 100% rename from examples/sphere.vtk rename to examples/assets/sphere.vtk diff --git a/examples/assets/stress.vtk b/examples/assets/stress.vtk new file mode 100644 index 0000000000000000000000000000000000000000..871531f0fcc723abf9f6f713d6ad9e075080e87d GIT binary patch literal 71411 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 NW_)M*_!ciO_#bjb5pw_l literal 0 HcmV?d00001 diff --git a/examples/warp_demo_mesh.usda b/examples/assets/warp_demo_mesh.usda similarity index 100% rename from examples/warp_demo_mesh.usda rename to examples/assets/warp_demo_mesh.usda diff --git a/pyproject.toml b/pyproject.toml index 873fe58..ca4ed74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,7 @@ dependencies = [ "pyopengl", # OpenUSD build dep "pyside6", # OpenUSD build dep "vtk", + "pygltflib", ] scripts.usd-setup = "ansys.tools.usdviewer.autosetup:main" @@ -68,6 +69,7 @@ tests = [ [tool.hatch.build.targets.wheel] packages = [ "src/ansys" ] +include = [ "src/ansys/tools/usdviewer/web/*.html" ] [tool.ruff] line-length = 120 diff --git a/src/ansys/tools/usdviewer/__init__.py b/src/ansys/tools/usdviewer/__init__.py index 35e53b7..afea124 100644 --- a/src/ansys/tools/usdviewer/__init__.py +++ b/src/ansys/tools/usdviewer/__init__.py @@ -23,4 +23,8 @@ import importlib.metadata as importlib_metadata +from ansys.tools.usdviewer.web.viewer import export_viewer_html + __version__ = importlib_metadata.version(__name__.replace(".", "-")) + +__all__ = ["__version__", "export_viewer_html"] diff --git a/src/ansys/tools/usdviewer/vtk_converter.py b/src/ansys/tools/usdviewer/vtk_converter.py index 238be46..e03e99d 100644 --- a/src/ansys/tools/usdviewer/vtk_converter.py +++ b/src/ansys/tools/usdviewer/vtk_converter.py @@ -23,6 +23,56 @@ class VTKConverter: """Convert VTK files to USD format for visualization.""" + @staticmethod + def _normalize_rgb_color(color: tuple[float, float, float]) -> "Gf.Vec3f": + """Normalize an RGB tuple to USD's 0-1 range.""" + if any(c > 1.0 for c in color): + return Gf.Vec3f(color[0] / 255.0, color[1] / 255.0, color[2] / 255.0) + return Gf.Vec3f(color[0], color[1], color[2]) + + @staticmethod + def _extract_display_colors_from_scalars( + scalars: "vtk.vtkDataArray", + ) -> list["Gf.Vec3f"]: + """Extract RGB display colors from VTK scalar data. + + For RGB/RGBA arrays, values are used directly (alpha ignored). + For single-component scalar arrays, a lookup table is used to map + scalar values to colors. + """ + num_components = scalars.GetNumberOfComponents() + num_tuples = scalars.GetNumberOfTuples() + + if num_tuples == 0: + return [] + + if num_components >= 3: + colors = [] + for i in range(num_tuples): + color = scalars.GetTuple(i) + colors.append(VTKConverter._normalize_rgb_color((color[0], color[1], color[2]))) + return colors + + if num_components == 1: + lut = scalars.GetLookupTable() + if lut is None: + lut = vtk.vtkLookupTable() + scalar_min, scalar_max = scalars.GetRange() + if scalar_min == scalar_max: + scalar_max = scalar_min + 1.0 + lut.SetRange(scalar_min, scalar_max) + lut.Build() + + colors = [] + for i in range(num_tuples): + scalar_value = scalars.GetTuple1(i) + rgb = [0.0, 0.0, 0.0] + lut.GetColor(scalar_value, rgb) + colors.append(Gf.Vec3f(rgb[0], rgb[1], rgb[2])) + return colors + + return [] + @staticmethod def convert_vtk_file_to_usd(vtk_file_path: Union[str, Path], stage: "Usd.Stage" = None) -> None: """Convert a VTK file to a USD file. @@ -175,22 +225,23 @@ def convert_polydata_to_usd_mesh( # Handle colors if available point_data = polydata.GetPointData() - if point_data and point_data.GetScalars(): + cell_data = polydata.GetCellData() + + scalars = None + interpolation = None + + if point_data and point_data.GetScalars() and point_data.GetScalars().GetNumberOfTuples() > 0: scalars = point_data.GetScalars() - if scalars.GetNumberOfComponents() >= 3: - colors = [] - for i in range(scalars.GetNumberOfTuples()): - color = scalars.GetTuple(i) - if scalars.GetNumberOfComponents() == 3: - colors.append(Gf.Vec3f(color[0], color[1], color[2])) - else: # RGBA - colors.append(Gf.Vec3f(color[0], color[1], color[2])) - - # Normalize colors if they're in 0-255 range - if any(c > 1.0 for color in colors for c in color): - colors = [Gf.Vec3f(c[0] / 255.0, c[1] / 255.0, c[2] / 255.0) for c in colors] - - mesh_prim.CreateDisplayColorAttr().Set(colors) + interpolation = UsdGeom.Tokens.vertex + elif cell_data and cell_data.GetScalars() and cell_data.GetScalars().GetNumberOfTuples() > 0: + scalars = cell_data.GetScalars() + interpolation = UsdGeom.Tokens.uniform + + if scalars is not None: + colors = VTKConverter._extract_display_colors_from_scalars(scalars) + if colors: + display_color_primvar = mesh_prim.CreateDisplayColorPrimvar(interpolation) + display_color_primvar.Set(colors) @staticmethod def convert_usd_to_vtk(stage: "Usd.Stage", mesh_path: Optional[str] = None) -> Optional[vtk.vtkPolyData]: diff --git a/src/ansys/tools/usdviewer/web/__init__.py b/src/ansys/tools/usdviewer/web/__init__.py new file mode 100644 index 0000000..7aa4816 --- /dev/null +++ b/src/ansys/tools/usdviewer/web/__init__.py @@ -0,0 +1,8 @@ +# Copyright (C) 2025 - 2026 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +"""Web viewer package exports.""" + +from ansys.tools.usdviewer.web.viewer import export_viewer_html + +__all__ = ["export_viewer_html"] diff --git a/src/ansys/tools/usdviewer/web/glb.py b/src/ansys/tools/usdviewer/web/glb.py new file mode 100644 index 0000000..05bd860 --- /dev/null +++ b/src/ansys/tools/usdviewer/web/glb.py @@ -0,0 +1,357 @@ +# Copyright (C) 2025 - 2026 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +"""GLB conversion helpers for USD web viewer export.""" + +from __future__ import annotations + +from pathlib import Path +import struct +from typing import Any + +from .mesh import triangulate_faces + + +class GLBBuilder: + """Utility for writing GLTF buffers and accessors.""" + + def __init__(self, gltf: Any, pygltflib: Any): + self.gltf = gltf + self.pygltflib = pygltflib + self.binary_blob = bytearray() + + def _pad4(self) -> None: + while len(self.binary_blob) % 4: + self.binary_blob.append(0) + + def add_buffer_view(self, data: bytes, target: int) -> int: + """Append binary data to the shared buffer and return its buffer-view index.""" + self._pad4() + offset = len(self.binary_blob) + self.binary_blob.extend(data) + buffer_view = self.pygltflib.BufferView(buffer=0, byteOffset=offset, byteLength=len(data), target=target) + self.gltf.bufferViews.append(buffer_view) + return len(self.gltf.bufferViews) - 1 + + def add_accessor( + self, + buffer_view_idx: int, + component_type: int, + count: int, + accessor_type: str, + min_values: list[float] | None = None, + max_values: list[float] | None = None, + ) -> int: + """Create an accessor for an existing buffer view and return its index.""" + accessor = self.pygltflib.Accessor( + bufferView=buffer_view_idx, + byteOffset=0, + componentType=component_type, + count=count, + type=accessor_type, + ) + if min_values is not None: + accessor.min = min_values + if max_values is not None: + accessor.max = max_values + self.gltf.accessors.append(accessor) + return len(self.gltf.accessors) - 1 + + def finalize(self) -> bytes: + """Finalize the GLTF document and return the serialized GLB bytes.""" + self.gltf.buffers.append(self.pygltflib.Buffer(byteLength=len(self.binary_blob))) + self.gltf.set_binary_blob(bytes(self.binary_blob)) + return bytes(self.gltf.save_to_bytes()) + + +def _import_glb_dependencies() -> tuple[Any, Any, Any, Any]: + """Import optional dependencies required for GLB export.""" + try: + import pygltflib + except ImportError as exc: + raise ImportError("pygltflib is required for GLB export. Install it with: pip install pygltflib") from exc + + try: + from pxr import Usd, UsdGeom, UsdShade + except ImportError as exc: + raise RuntimeError( + "OpenUSD Python bindings are required to convert USD to GLB. Run usd-setup and ensure pxr is importable." + ) from exc + + return pygltflib, Usd, UsdGeom, UsdShade + + +def _open_stage_for_glb(path: Path, usd: Any) -> Any: + """Open a USD stage for GLB conversion.""" + stage = usd.Stage.Open(str(path)) + if not stage: + raise RuntimeError(f"Failed to open USD stage: {path}") + return stage + + +def _create_gltf_scene(pygltflib: Any) -> tuple[Any, Any]: + """Create an initialized glTF document and default scene.""" + gltf = pygltflib.GLTF2() + gltf.asset = pygltflib.Asset(version="2.0") + scene = pygltflib.Scene(nodes=[]) + gltf.scenes.append(scene) + gltf.scene = 0 + return gltf, scene + + +def _get_triangle_indices(usd_mesh: Any) -> list[int]: + """Get triangulated face indices for a USD mesh.""" + indices_raw = usd_mesh.GetFaceVertexIndicesAttr().Get() + counts_raw = usd_mesh.GetFaceVertexCountsAttr().Get() + if indices_raw and counts_raw: + return triangulate_faces(list(indices_raw), list(counts_raw)) + points = usd_mesh.GetPointsAttr().Get() or [] + return list(range(len(points))) + + +def _add_position_attribute(points: list[Any], builder: GLBBuilder, pygltflib: Any) -> int: + """Write position data and return the POSITION accessor index.""" + pos_data = struct.pack(f"{len(points) * 3}f", *[c for p in points for c in (float(p[0]), float(p[1]), float(p[2]))]) + xs = [float(p[0]) for p in points] + ys = [float(p[1]) for p in points] + zs = [float(p[2]) for p in points] + pos_buffer_view = builder.add_buffer_view(pos_data, pygltflib.ARRAY_BUFFER) + return builder.add_accessor( + pos_buffer_view, + pygltflib.FLOAT, + len(points), + pygltflib.VEC3, + min_values=[min(xs), min(ys), min(zs)], + max_values=[max(xs), max(ys), max(zs)], + ) + + +def _add_index_accessor(triangle_indices: list[int], builder: GLBBuilder, pygltflib: Any) -> int: + """Write triangle index data and return the index accessor.""" + idx_data = struct.pack(f"{len(triangle_indices)}I", *triangle_indices) + idx_buffer_view = builder.add_buffer_view(idx_data, pygltflib.ELEMENT_ARRAY_BUFFER) + return builder.add_accessor(idx_buffer_view, pygltflib.UNSIGNED_INT, len(triangle_indices), pygltflib.SCALAR) + + +def _add_normals_attribute( + usd_mesh: Any, points: list[Any], attributes: Any, builder: GLBBuilder, pygltflib: Any +) -> None: + """Attach NORMAL accessor when mesh normals are compatible.""" + normals = usd_mesh.GetNormalsAttr().Get() + if not normals or len(normals) != len(points): + return + + norm_data = struct.pack( + f"{len(normals) * 3}f", *[c for n in normals for c in (float(n[0]), float(n[1]), float(n[2]))] + ) + norm_buffer_view = builder.add_buffer_view(norm_data, pygltflib.ARRAY_BUFFER) + attributes.NORMAL = builder.add_accessor(norm_buffer_view, pygltflib.FLOAT, len(normals), pygltflib.VEC3) + + +def _add_uv_attribute( + prim: Any, + points: list[Any], + attributes: Any, + builder: GLBBuilder, + pygltflib: Any, + usd_geom: Any, +) -> None: + """Attach TEXCOORD_0 accessor when matching UV data exists.""" + st_primvar = usd_geom.PrimvarsAPI(prim).GetPrimvar("st") + if not st_primvar or not st_primvar.IsDefined(): + return + + st_values = st_primvar.Get() + if not st_values or len(st_values) != len(points): + return + + uv_data = struct.pack(f"{len(st_values) * 2}f", *[c for uv in st_values for c in (float(uv[0]), float(uv[1]))]) + uv_buffer_view = builder.add_buffer_view(uv_data, pygltflib.ARRAY_BUFFER) + attributes.TEXCOORD_0 = builder.add_accessor(uv_buffer_view, pygltflib.FLOAT, len(st_values), pygltflib.VEC2) + + +def _get_display_colors(usd_mesh: Any) -> tuple[Any, Any]: + """Return display colors and interpolation token from USD mesh.""" + display_color_primvar = usd_mesh.GetDisplayColorPrimvar() + colors = display_color_primvar.Get() if display_color_primvar else usd_mesh.GetDisplayColorAttr().Get() + interpolation = display_color_primvar.GetInterpolation() if display_color_primvar else None + return colors, interpolation + + +def _add_display_color_attribute( + points: list[Any], + display_colors: Any, + color_interpolation: Any, + attributes: Any, + builder: GLBBuilder, + pygltflib: Any, + usd_geom: Any, +) -> None: + """Attach COLOR_0 when displayColor is vertex-compatible.""" + if not ( + display_colors + and color_interpolation in {usd_geom.Tokens.vertex, usd_geom.Tokens.varying} + and len(display_colors) == len(points) + ): + return + + color_data = struct.pack( + f"{len(display_colors) * 3}f", + *[c for color in display_colors for c in (float(color[0]), float(color[1]), float(color[2]))], + ) + color_buffer_view = builder.add_buffer_view(color_data, pygltflib.ARRAY_BUFFER) + attributes.COLOR_0 = builder.add_accessor(color_buffer_view, pygltflib.FLOAT, len(display_colors), pygltflib.VEC3) + + +def _append_material_and_get_index(gltf: Any, material: Any) -> int: + """Append a GLTF material and return its index.""" + gltf.materials.append(material) + return len(gltf.materials) - 1 + + +def _build_material_from_shader(shader: Any, prim_name: str, pygltflib: Any) -> Any: + """Build a GLTF material from a UsdPreviewSurface shader.""" + pbr = pygltflib.PbrMetallicRoughness() + + diffuse = shader.GetInput("diffuseColor") + if diffuse and diffuse.Get() is not None: + dc = diffuse.Get() + pbr.baseColorFactor = [float(dc[0]), float(dc[1]), float(dc[2]), 1.0] + + metallic = shader.GetInput("metallic") + if metallic and metallic.Get() is not None: + pbr.metallicFactor = float(metallic.Get()) + + roughness = shader.GetInput("roughness") + if roughness and roughness.Get() is not None: + pbr.roughnessFactor = float(roughness.Get()) + + mat = pygltflib.Material(pbrMetallicRoughness=pbr, name=prim_name) + + emissive = shader.GetInput("emissiveColor") + if emissive and emissive.Get() is not None: + ec = emissive.Get() + mat.emissiveFactor = [float(ec[0]), float(ec[1]), float(ec[2])] + + return mat + + +def _build_display_color_material(display_colors: Any, prim_name: str, pygltflib: Any) -> Any: + """Build a fallback GLTF material from the first displayColor value.""" + c = display_colors[0] + pbr = pygltflib.PbrMetallicRoughness(baseColorFactor=[float(c[0]), float(c[1]), float(c[2]), 1.0]) + return pygltflib.Material(pbrMetallicRoughness=pbr, name=f"{prim_name}_displayColor") + + +def _get_material_index(prim: Any, display_colors: Any, gltf: Any, pygltflib: Any, usd_shade: Any) -> int | None: + """Resolve GLTF material index from USD bindings or displayColor fallback.""" + mat_index = None + binding = usd_shade.MaterialBindingAPI(prim).GetDirectBinding() + if not binding: + return mat_index + + material = binding.GetMaterial() + if material: + surface_source = material.ComputeSurfaceSource() + shader = surface_source[0] if surface_source else None + if shader and shader.GetPrim().IsValid(): + mat = _build_material_from_shader(shader, prim.GetName(), pygltflib) + mat_index = _append_material_and_get_index(gltf, mat) + + if mat_index is None and display_colors and len(display_colors) > 0: + mat = _build_display_color_material(display_colors, prim.GetName(), pygltflib) + mat_index = _append_material_and_get_index(gltf, mat) + + return mat_index + + +def _append_mesh_to_scene( + prim: Any, attributes: Any, idx_acc: int, mat_index: int | None, gltf: Any, scene: Any, pygltflib: Any +) -> None: + """Append one mesh primitive and its node to the active scene.""" + primitive = pygltflib.Primitive(attributes=attributes, indices=idx_acc, material=mat_index) + gltf_mesh = pygltflib.Mesh(primitives=[primitive], name=prim.GetName()) + gltf.meshes.append(gltf_mesh) + + node = pygltflib.Node(mesh=len(gltf.meshes) - 1, name=prim.GetName()) + gltf.nodes.append(node) + scene.nodes.append(len(gltf.nodes) - 1) + + +def _iter_usd_mesh_prims(stage: Any, usd_geom: Any) -> list[Any]: + """Collect mesh prims from a USD stage.""" + mesh_prims = [] + for prim in stage.Traverse(): + if prim.IsA(usd_geom.Mesh): + mesh_prims.append(prim) + return mesh_prims + + +def _build_attributes_for_mesh( + prim: Any, + usd_mesh: Any, + points: list[Any], + builder: GLBBuilder, + pygltflib: Any, + usd_geom: Any, +) -> tuple[Any, int, Any]: + """Build geometric attributes and index accessor for one mesh.""" + tri_indices = _get_triangle_indices(usd_mesh) + pos_acc = _add_position_attribute(points, builder, pygltflib) + idx_acc = _add_index_accessor(tri_indices, builder, pygltflib) + + attributes = pygltflib.Attributes(POSITION=pos_acc) + _add_normals_attribute(usd_mesh, points, attributes, builder, pygltflib) + _add_uv_attribute(prim, points, attributes, builder, pygltflib, usd_geom) + + display_colors, color_interpolation = _get_display_colors(usd_mesh) + _add_display_color_attribute( + points, + display_colors, + color_interpolation, + attributes, + builder, + pygltflib, + usd_geom, + ) + return attributes, idx_acc, display_colors + + +def _convert_mesh_prim_to_gltf( + prim: Any, + gltf: Any, + scene: Any, + builder: GLBBuilder, + pygltflib: Any, + usd_geom: Any, + usd_shade: Any, +) -> None: + """Convert one USD mesh prim into GLTF mesh/node entries.""" + usd_mesh = usd_geom.Mesh(prim) + points = usd_mesh.GetPointsAttr().Get() + if not points: + return + + attributes, idx_acc, display_colors = _build_attributes_for_mesh( + prim, + usd_mesh, + points, + builder, + pygltflib, + usd_geom, + ) + mat_index = _get_material_index(prim, display_colors, gltf, pygltflib, usd_shade) + _append_mesh_to_scene(prim, attributes, idx_acc, mat_index, gltf, scene, pygltflib) + + +def convert_usd_to_glb(path: Path) -> bytes: + """Convert a USD stage to a self-contained GLB binary.""" + pygltflib, usd, usd_geom, usd_shade = _import_glb_dependencies() + stage = _open_stage_for_glb(path, usd) + gltf, scene = _create_gltf_scene(pygltflib) + builder = GLBBuilder(gltf, pygltflib) + + for prim in _iter_usd_mesh_prims(stage, usd_geom): + _convert_mesh_prim_to_gltf(prim, gltf, scene, builder, pygltflib, usd_geom, usd_shade) + + return builder.finalize() diff --git a/src/ansys/tools/usdviewer/web/glb_template.html b/src/ansys/tools/usdviewer/web/glb_template.html new file mode 100644 index 0000000..e9c01ac --- /dev/null +++ b/src/ansys/tools/usdviewer/web/glb_template.html @@ -0,0 +1,140 @@ + + + + + + USD Web Viewer + + + + +
Loading:
+ + + + + diff --git a/src/ansys/tools/usdviewer/web/mesh.py b/src/ansys/tools/usdviewer/web/mesh.py new file mode 100644 index 0000000..34780f0 --- /dev/null +++ b/src/ansys/tools/usdviewer/web/mesh.py @@ -0,0 +1,113 @@ +# Copyright (C) 2025 - 2026 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +"""Mesh extraction helpers for web viewer fallback rendering.""" + +from __future__ import annotations + +from pathlib import Path +from typing import Any + + +def triangulate_faces(indices: list[int], counts: list[int]) -> list[int]: + """Convert polygon faces to triangles using fan triangulation.""" + result = [] + idx = 0 + for count in counts: + face = indices[idx : idx + count] + for i in range(1, count - 1): + result.extend([face[0], face[i], face[i + 1]]) + idx += count + return result + + +def _import_usd_for_mesh_extraction() -> tuple[Any, Any]: + """Import USD modules required for mesh extraction.""" + try: + from pxr import Usd, UsdGeom + except ImportError as exc: + raise RuntimeError( + "OpenUSD Python bindings are required to extract mesh data. Run usd-setup and ensure pxr is importable." + ) from exc + + return Usd, UsdGeom + + +def _open_usd_stage(path: Path, usd: Any) -> Any: + """Open a USD stage and raise a descriptive error on failure.""" + stage = usd.Stage.Open(str(path)) + if not stage: + raise RuntimeError(f"Failed to open USD stage: {path}") + return stage + + +def _build_tri_indices(mesh: Any, point_count: int) -> list[int]: + """Build triangulated indices from face arrays or fallback to point order.""" + indices_attr = mesh.GetFaceVertexIndicesAttr().Get() + counts_attr = mesh.GetFaceVertexCountsAttr().Get() + if indices_attr and counts_attr: + return triangulate_faces(list(indices_attr), list(counts_attr)) + return list(range(point_count)) + + +def _extract_vertex_normals(mesh: Any, point_count: int) -> list[float] | None: + """Extract flat vertex normals when they align with points.""" + normals_attr = mesh.GetNormalsAttr().Get() + if not normals_attr or len(normals_attr) != point_count: + return None + return [coord for n in normals_attr for coord in (float(n[0]), float(n[1]), float(n[2]))] + + +def _extract_mesh_colors(mesh: Any, point_count: int, usd_geom: Any) -> tuple[list[float] | None, list[float] | None]: + """Extract per-vertex colors or fallback constant color from displayColor.""" + display_color_primvar = mesh.GetDisplayColorPrimvar() + color_attr = display_color_primvar.Get() if display_color_primvar else mesh.GetDisplayColorAttr().Get() + color_interpolation = display_color_primvar.GetInterpolation() if display_color_primvar else None + + colors = None + color = None + if color_attr and len(color_attr) > 0: + if color_interpolation in {usd_geom.Tokens.vertex, usd_geom.Tokens.varying} and len(color_attr) == point_count: + colors = [coord for c in color_attr for coord in (float(c[0]), float(c[1]), float(c[2]))] + else: + c = color_attr[0] + color = [float(c[0]), float(c[1]), float(c[2])] + + return colors, color + + +def _extract_mesh_payload(mesh: Any, usd_geom: Any) -> dict | None: + """Extract web-renderable mesh payload from a USD mesh prim.""" + points_attr = mesh.GetPointsAttr().Get() + if not points_attr: + return None + + point_count = len(points_attr) + flat_points = [coord for pt in points_attr for coord in (float(pt[0]), float(pt[1]), float(pt[2]))] + tri_indices = _build_tri_indices(mesh, point_count) + flat_normals = _extract_vertex_normals(mesh, point_count) + colors, color = _extract_mesh_colors(mesh, point_count, usd_geom) + + return { + "positions": flat_points, + "indices": tri_indices, + "normals": flat_normals, + "colors": colors, + "color": color, + } + + +def extract_meshes_from_stage_file(path: Path) -> list[dict]: + """Extract all mesh geometry from a USD stage file for web rendering.""" + usd, usd_geom = _import_usd_for_mesh_extraction() + stage = _open_usd_stage(path, usd) + + meshes = [] + for prim in stage.Traverse(): + if not prim.IsA(usd_geom.Mesh): + continue + mesh_payload = _extract_mesh_payload(usd_geom.Mesh(prim), usd_geom) + if mesh_payload is not None: + meshes.append(mesh_payload) + + return meshes diff --git a/src/ansys/tools/usdviewer/web/mesh_template.html b/src/ansys/tools/usdviewer/web/mesh_template.html new file mode 100644 index 0000000..27ddeb8 --- /dev/null +++ b/src/ansys/tools/usdviewer/web/mesh_template.html @@ -0,0 +1,150 @@ + + + + + + USD Web Viewer + + + + +
Loading:
+ + + + + diff --git a/src/ansys/tools/usdviewer/web/templates.py b/src/ansys/tools/usdviewer/web/templates.py new file mode 100644 index 0000000..eccc407 --- /dev/null +++ b/src/ansys/tools/usdviewer/web/templates.py @@ -0,0 +1,36 @@ +# Copyright (C) 2025 - 2026 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +"""HTML template rendering helpers for web viewer export.""" + +from __future__ import annotations + +import json +from pathlib import Path + +_GLB_TEMPLATE_FILE = "glb_template.html" +_MESH_TEMPLATE_FILE = "mesh_template.html" + + +def build_viewer_html_glb(glb_b64: str, model_name: str) -> str: + """Build a self-contained HTML viewer that renders a base64-encoded GLB.""" + template = _load_template(_GLB_TEMPLATE_FILE) + return template.replace("__MODEL_NAME_JSON__", json.dumps(model_name)).replace( + "__GLB_B64_JSON__", json.dumps(glb_b64) + ) + + +def build_viewer_html(meshes: list[dict], model_name: str) -> str: + """Build a self-contained HTML viewer from extracted mesh JSON.""" + template = _load_template(_MESH_TEMPLATE_FILE) + return template.replace("__MODEL_NAME_JSON__", json.dumps(model_name)).replace( + "__MESH_DATA_JSON__", json.dumps(meshes) + ) + + +def _load_template(template_name: str) -> str: + """Load a viewer HTML template from the package directory.""" + template_path = Path(__file__).with_name(template_name) + if not template_path.exists(): + raise RuntimeError(f"Viewer template not found: {template_path}") + return template_path.read_text(encoding="utf-8") diff --git a/src/ansys/tools/usdviewer/web/viewer.py b/src/ansys/tools/usdviewer/web/viewer.py new file mode 100644 index 0000000..06957ed --- /dev/null +++ b/src/ansys/tools/usdviewer/web/viewer.py @@ -0,0 +1,143 @@ +# Copyright (C) 2025 - 2026 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +"""Utilities to export USD assets and launch a simple browser viewer.""" + +from __future__ import annotations + +import base64 +from pathlib import Path + +from ansys.tools.usdviewer.vtk_converter import VTKConverter + +from .glb import convert_usd_to_glb +from .mesh import extract_meshes_from_stage_file +from .templates import build_viewer_html, build_viewer_html_glb + +_SUPPORTED_USD_EXTENSIONS = {".usd", ".usda", ".usdc", ".usdz"} + + +def export_viewer_html( + input_path: str | Path, + output_path: str | Path | None = None, +) -> Path: + """Generate a self-contained HTML viewer file for embedding in other pages. + + The generated HTML embeds all mesh geometry as inline JSON and requires + only a CDN connection for Three.js. It is designed to be embedded in + other web pages via an `` + + Parameters + ---------- + input_path : str | Path + Source asset path. Supported formats are ``.usd``, ``.usda``, + ``.usdc``, and ``.usdz``. + output_path : str | Path | None, default: ``None`` + Full path for the generated HTML file. When ``None``, the file is + written alongside the source asset as ``{stem}_viewer.html``. + + Returns + ------- + Path + Absolute path to the generated HTML file. + """ + source_path = Path(input_path).expanduser().resolve() + if not source_path.exists(): + raise FileNotFoundError(f"Input file not found: {source_path}") + + extension = source_path.suffix.lower() + if extension not in _SUPPORTED_USD_EXTENSIONS: + supported = ", ".join(sorted(_SUPPORTED_USD_EXTENSIONS)) + raise ValueError(f"Unsupported input format '{extension}'. Supported formats: {supported}.") + + if output_path is None: + html_path = source_path.parent / f"{source_path.stem}_viewer.html" + else: + html_path = Path(output_path).expanduser().resolve() + + html_path.parent.mkdir(parents=True, exist_ok=True) + _generate_viewer_html(source_path, html_path) + return html_path + + +def _prepare_source_for_web(source_path: Path, export_root: Path) -> Path: + """Prepare a source USD file for web export. + + For files that reference VTK assets via custom ``Asset`` attributes, + convert those assets into USD mesh data before packaging. + """ + if source_path.suffix.lower() == ".usdz": + return source_path + + try: + from pxr import Usd + except ImportError as exc: + raise RuntimeError( + "OpenUSD Python bindings are required to prepare web export assets. " + "Run usd-setup and ensure pxr is importable." + ) from exc + + stage = Usd.Stage.Open(str(source_path)) + if not stage: + raise RuntimeError(f"Failed to open USD stage: {source_path}") + + vtk_asset_paths: list[Path] = [] + vtk_asset_attrs = [] + for prim in stage.Traverse(): + attr = prim.GetAttribute("Asset") + if not attr: + continue + value = attr.Get() + asset_path = getattr(value, "path", None) + if not asset_path: + continue + if Path(asset_path).suffix.lower() not in {".vtk", ".vtp", ".vtu", ".vts", ".obj", ".ply", ".stl"}: + continue + + resolved_asset = Path(asset_path) + if not resolved_asset.is_absolute(): + resolved_asset = (source_path.parent / resolved_asset).resolve() + vtk_asset_paths.append(resolved_asset) + vtk_asset_attrs.append(attr) + + if not vtk_asset_paths: + return source_path + + converter = VTKConverter() + for vtk_path in vtk_asset_paths: + loaded = converter.load_asset(str(vtk_path), stage) + if loaded is None: + raise RuntimeError(f"Failed to load VTK asset referenced by stage: {vtk_path}") + + # Clear custom Asset links after conversion so USDZ packaging doesn't retain + # unresolved non-USD references that web loaders cannot consume. + for attr in vtk_asset_attrs: + attr.Clear() + + prepared_source = export_root / f"{source_path.stem}_webprep.usda" + stage.Export(str(prepared_source)) + return prepared_source + + +def _generate_viewer_html(source_path: Path, html_path: Path) -> None: + """Prepare mesh data and write a viewer HTML file to *html_path*. + + Tries to export a GLB via ``pygltflib`` for full PBR material support. + Falls back to the raw mesh extraction path when ``pygltflib`` is not + installed. Intermediate files (e.g. ``_webprep.usda``) are written to + ``html_path.parent``. + """ + prepared_path = _prepare_source_for_web(source_path, html_path.parent) + try: + glb_bytes = convert_usd_to_glb(prepared_path) + glb_b64 = base64.b64encode(glb_bytes).decode("ascii") + html = build_viewer_html_glb(glb_b64, source_path.name) + except ImportError: + meshes = extract_meshes_from_stage_file(prepared_path) + html = build_viewer_html(meshes, source_path.name) + html_path.write_text(html, encoding="utf-8") diff --git a/tests/test_conversion.py b/tests/test_conversion.py index b7c4a1a..dd735ce 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -275,6 +275,80 @@ def test_convert_polydata_with_rgba_colors(): assert abs(usd_colors[1][1] - 1.0) < 0.01, "Second color green component should be ~1.0" +@requires_openusd +def test_convert_polydata_with_single_component_point_scalars(): + """Test conversion of scalar point data to mapped USD display colors.""" + stage = Usd.Stage.CreateInMemory() + points = vtk.vtkPoints() + points.InsertNextPoint(0, 0, 0) + points.InsertNextPoint(1, 0, 0) + points.InsertNextPoint(0, 1, 0) + + polydata = vtk.vtkPolyData() + polydata.SetPoints(points) + + scalar_values = vtk.vtkFloatArray() + scalar_values.SetNumberOfComponents(1) + scalar_values.SetName("stress") + scalar_values.InsertNextValue(10.0) + scalar_values.InsertNextValue(20.0) + scalar_values.InsertNextValue(30.0) + polydata.GetPointData().SetScalars(scalar_values) + + VTKConverter.convert_polydata_to_usd_mesh(polydata, stage, "ScalarPoints") + + mesh = UsdGeom.Mesh(stage.GetPrimAtPath("/ScalarPoints")) + display_color_primvar = mesh.GetDisplayColorPrimvar() + usd_colors = display_color_primvar.Get() + + assert usd_colors is not None, "Mapped colors should be authored" + assert len(usd_colors) == 3, "Should have one mapped color per point" + assert display_color_primvar.GetInterpolation() == UsdGeom.Tokens.vertex + + +@requires_openusd +def test_convert_polydata_with_single_component_cell_scalars(): + """Test conversion of scalar cell data to mapped USD display colors.""" + stage = Usd.Stage.CreateInMemory() + + points = vtk.vtkPoints() + points.InsertNextPoint(0, 0, 0) + points.InsertNextPoint(1, 0, 0) + points.InsertNextPoint(1, 1, 0) + points.InsertNextPoint(0, 1, 0) + + polys = vtk.vtkCellArray() + polys.InsertNextCell(3) + polys.InsertCellPoint(0) + polys.InsertCellPoint(1) + polys.InsertCellPoint(2) + polys.InsertNextCell(3) + polys.InsertCellPoint(0) + polys.InsertCellPoint(2) + polys.InsertCellPoint(3) + + polydata = vtk.vtkPolyData() + polydata.SetPoints(points) + polydata.SetPolys(polys) + + cell_scalars = vtk.vtkFloatArray() + cell_scalars.SetNumberOfComponents(1) + cell_scalars.SetName("cell_stress") + cell_scalars.InsertNextValue(100.0) + cell_scalars.InsertNextValue(200.0) + polydata.GetCellData().SetScalars(cell_scalars) + + VTKConverter.convert_polydata_to_usd_mesh(polydata, stage, "ScalarCells") + + mesh = UsdGeom.Mesh(stage.GetPrimAtPath("/ScalarCells")) + display_color_primvar = mesh.GetDisplayColorPrimvar() + usd_colors = display_color_primvar.Get() + + assert usd_colors is not None, "Mapped colors should be authored" + assert len(usd_colors) == 2, "Should have one mapped color per cell" + assert display_color_primvar.GetInterpolation() == UsdGeom.Tokens.uniform + + @requires_openusd def test_convert_usd_to_vtk_with_normalized_colors(): """Test conversion from USD to VTK with normalized colors (0-1 range).""" diff --git a/tests/test_web_viewer.py b/tests/test_web_viewer.py new file mode 100644 index 0000000..4553f2a --- /dev/null +++ b/tests/test_web_viewer.py @@ -0,0 +1,118 @@ +# Copyright (C) 2025 - 2026 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +"""Unit tests for web viewer utilities.""" + +from pathlib import Path +from unittest.mock import patch + +from conftest import requires_openusd +import pytest + +from ansys.tools.usdviewer.web.viewer import export_viewer_html + + +@patch("ansys.tools.usdviewer.web.viewer.extract_meshes_from_stage_file") +@patch("ansys.tools.usdviewer.web.viewer._prepare_source_for_web") +def test_export_viewer_html_default_output_path(mock_prepare, mock_extract, tmp_path): + """Test that export_viewer_html writes the HTML alongside the input file by default.""" + usda_path = tmp_path / "scene.usda" + usda_path.write_text("#usda 1.0", encoding="utf-8") + mock_prepare.return_value = usda_path + mock_extract.return_value = [] + + html_path = export_viewer_html(usda_path) + + assert html_path == tmp_path / "scene_viewer.html" + assert html_path.exists() + assert "meshData" in html_path.read_text(encoding="utf-8") + + +@patch("ansys.tools.usdviewer.web.viewer.extract_meshes_from_stage_file") +@patch("ansys.tools.usdviewer.web.viewer._prepare_source_for_web") +def test_export_viewer_html_explicit_output_path(mock_prepare, mock_extract, tmp_path): + """Test that export_viewer_html respects an explicit output path.""" + usda_path = tmp_path / "scene.usda" + usda_path.write_text("#usda 1.0", encoding="utf-8") + out_path = tmp_path / "output" / "custom.html" + mock_prepare.return_value = usda_path + mock_extract.return_value = [] + + html_path = export_viewer_html(usda_path, output_path=out_path) + + assert html_path == out_path + assert html_path.exists() + + +def test_export_viewer_html_missing_file_raises(tmp_path): + """Test that a missing input raises FileNotFoundError.""" + with pytest.raises(FileNotFoundError): + export_viewer_html(tmp_path / "missing.usda") + + +def test_export_viewer_html_unsupported_extension_raises(tmp_path): + """Test that unsupported file formats are rejected.""" + invalid_file = tmp_path / "model.obj" + invalid_file.write_text("data", encoding="utf-8") + + with pytest.raises(ValueError, match="Unsupported input format"): + export_viewer_html(invalid_file) + + +@patch("pxr.Usd.Stage.Open") +def test_prepare_source_for_web_converts_vtk_assets(mock_stage_open, tmp_path): + """Test that VTK assets referenced by custom Asset attributes are converted.""" + from ansys.tools.usdviewer.web.viewer import _prepare_source_for_web + + input_file = tmp_path / "scene_vtk.usda" + input_file.write_text("#usda 1.0", encoding="utf-8") + vtk_file = tmp_path / "mesh.vtk" + vtk_file.write_text("vtk", encoding="utf-8") + + mock_attr = type( + "MockAttr", (), {"Get": lambda self: type("V", (), {"path": "mesh.vtk"})(), "Clear": lambda self: None} + )() + mock_prim = type("MockPrim", (), {"GetAttribute": lambda self, name: mock_attr if name == "Asset" else None})() + + class MockStage: + def Traverse(self): # noqa: N802 + return [mock_prim] + + def Export(self, path): # noqa: N802 + Path(path).write_text("#usda 1.0", encoding="utf-8") + + mock_stage_open.return_value = MockStage() + + with patch( + "ansys.tools.usdviewer.web.viewer.VTKConverter.load_asset", return_value=mock_stage_open.return_value + ) as mock_load_asset: + prepared = _prepare_source_for_web(input_file, tmp_path) + + assert prepared.name.endswith("_webprep.usda") + assert prepared.exists() + mock_load_asset.assert_called_once_with(str(vtk_file.resolve()), mock_stage_open.return_value) + + +@requires_openusd +def test_extract_meshes_from_stage_file_keeps_vertex_display_color(tmp_path): + """Test that per-vertex USD displayColor is preserved for web rendering.""" + from pxr import Gf, Usd, UsdGeom + + from ansys.tools.usdviewer.web.mesh import extract_meshes_from_stage_file + + usd_path = tmp_path / "vertex_colors.usda" + stage = Usd.Stage.CreateNew(str(usd_path)) + mesh = UsdGeom.Mesh.Define(stage, "/Mesh") + mesh.CreatePointsAttr().Set([Gf.Vec3f(0, 0, 0), Gf.Vec3f(1, 0, 0), Gf.Vec3f(0, 1, 0)]) + mesh.CreateFaceVertexCountsAttr().Set([3]) + mesh.CreateFaceVertexIndicesAttr().Set([0, 1, 2]) + + display_color = mesh.CreateDisplayColorPrimvar(UsdGeom.Tokens.vertex) + display_color.Set([Gf.Vec3f(1, 0, 0), Gf.Vec3f(0, 1, 0), Gf.Vec3f(0, 0, 1)]) + stage.Save() + + meshes = extract_meshes_from_stage_file(usd_path) + + assert len(meshes) == 1 + assert meshes[0]["colors"] == [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0] + assert meshes[0]["color"] is None From 7f34c888d8a00484187d87cc483a2b3ff7eba276 Mon Sep 17 00:00:00 2001 From: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com> Date: Wed, 20 May 2026 15:22:26 +0000 Subject: [PATCH 02/18] chore: adding changelog file 75.added.md [dependabot-skip] --- doc/changelog.d/75.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/changelog.d/75.added.md diff --git a/doc/changelog.d/75.added.md b/doc/changelog.d/75.added.md new file mode 100644 index 0000000..a21cf2b --- /dev/null +++ b/doc/changelog.d/75.added.md @@ -0,0 +1 @@ +Add web view component (WIP) From 626a6618014337d6f297a8195bd57df8146ac97f Mon Sep 17 00:00:00 2001 From: Alex Fernandez Date: Mon, 25 May 2026 10:40:24 +0200 Subject: [PATCH 03/18] feat: Add web view component (WIP) --- .gitignore | 4 +- examples/assets/display_color_vtk.usda | 2 +- examples/assets/display_color_vtk_viewer.html | 197 ++++++ .../assets/display_color_vtk_webprep.usda | 4 +- src/ansys/tools/usdviewer/web/glb.py | 623 +++++++++++++++--- .../tools/usdviewer/web/glb_template.html | 65 +- src/ansys/tools/usdviewer/web/mesh.py | 113 ---- .../tools/usdviewer/web/mesh_template.html | 150 ----- src/ansys/tools/usdviewer/web/templates.py | 9 - src/ansys/tools/usdviewer/web/viewer.py | 18 +- tests/test_web_viewer.py | 37 +- 11 files changed, 797 insertions(+), 425 deletions(-) create mode 100644 examples/assets/display_color_vtk_viewer.html delete mode 100644 src/ansys/tools/usdviewer/web/mesh.py delete mode 100644 src/ansys/tools/usdviewer/web/mesh_template.html diff --git a/.gitignore b/.gitignore index 608f662..97c883b 100644 --- a/.gitignore +++ b/.gitignore @@ -210,4 +210,6 @@ examples/*_webprep.usda /resources/ /source/ /cmake.* -temp.usda \ No newline at end of file +temp.usda + +examples/assets/complex_scene/* \ No newline at end of file diff --git a/examples/assets/display_color_vtk.usda b/examples/assets/display_color_vtk.usda index c3e25b2..cdd80cb 100644 --- a/examples/assets/display_color_vtk.usda +++ b/examples/assets/display_color_vtk.usda @@ -14,7 +14,7 @@ def Xform "root" int[] faceVertexIndices = [0, 2, 3, 1] normal3f[] normals = [(0, 1, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0)] point3f[] points = [(-5, 0, 5), (-5, 0, -5), (5, 0, 5), (5, 0, -5)] - color3f[] primvars:displayColor = [(1, 0, 0)] + color3f[] primvars:displayColor = [(0, 1, 0)] uniform token subdivisionScheme = "none" } diff --git a/examples/assets/display_color_vtk_viewer.html b/examples/assets/display_color_vtk_viewer.html new file mode 100644 index 0000000..e039bfe --- /dev/null +++ b/examples/assets/display_color_vtk_viewer.html @@ -0,0 +1,197 @@ + + + + + + USD Web Viewer + + + + +
Loading:
+ + + + + diff --git a/examples/assets/display_color_vtk_webprep.usda b/examples/assets/display_color_vtk_webprep.usda index c7e3271..3bb2747 100644 --- a/examples/assets/display_color_vtk_webprep.usda +++ b/examples/assets/display_color_vtk_webprep.usda @@ -1,7 +1,7 @@ #usda 1.0 ( defaultPrim = "root" - doc = """Generated from Composed Stage of root layer D:\\repositories\\python-usd-viewer\\examples\\display_color_vtk.usda + doc = """Generated from Composed Stage of root layer D:\\repositories\\python-usd-viewer\\examples\\assets\\display_color_vtk.usda """ endTimeCode = 300 framesPerSecond = 30 @@ -16,7 +16,7 @@ def Xform "root" int[] faceVertexIndices = [0, 2, 3, 1] normal3f[] normals = [(0, 1, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0)] point3f[] points = [(-5, 0, 5), (-5, 0, -5), (5, 0, 5), (5, 0, -5)] - color3f[] primvars:displayColor = [(1, 0, 0)] + color3f[] primvars:displayColor = [(0, 1, 0)] uniform token subdivisionScheme = "none" } diff --git a/src/ansys/tools/usdviewer/web/glb.py b/src/ansys/tools/usdviewer/web/glb.py index 05bd860..26eeb97 100644 --- a/src/ansys/tools/usdviewer/web/glb.py +++ b/src/ansys/tools/usdviewer/web/glb.py @@ -5,31 +5,56 @@ from __future__ import annotations +import mimetypes from pathlib import Path import struct from typing import Any -from .mesh import triangulate_faces +from pxr import Usd, UsdGeom, UsdLux, UsdShade +import pygltflib + + +def triangulate_faces(indices: list[int], counts: list[int]) -> list[int]: + """Convert polygon faces to triangles using fan triangulation.""" + result = [] + idx = 0 + for count in counts: + face = indices[idx : idx + count] + for i in range(1, count - 1): + result.extend([face[0], face[i], face[i + 1]]) + idx += count + return result class GLBBuilder: - """Utility for writing GLTF buffers and accessors.""" + """Utility for writing GLTF buffers and accessors. + + Abstracts the low-level glTF buffer/accessor bookkeeping when + converting USD geometry into a .glb file for web viewing. + + Parameters + ---------- + gltf : Any + An instance of pygltflib.GLTF2 to populate with buffer views and accessors + """ - def __init__(self, gltf: Any, pygltflib: Any): + def __init__(self, gltf: Any): + """Initialize the GLBBuilder with a GLTF document.""" self.gltf = gltf - self.pygltflib = pygltflib self.binary_blob = bytearray() def _pad4(self) -> None: while len(self.binary_blob) % 4: self.binary_blob.append(0) - def add_buffer_view(self, data: bytes, target: int) -> int: + def add_buffer_view(self, data: bytes, target: int | None) -> int: """Append binary data to the shared buffer and return its buffer-view index.""" self._pad4() offset = len(self.binary_blob) self.binary_blob.extend(data) - buffer_view = self.pygltflib.BufferView(buffer=0, byteOffset=offset, byteLength=len(data), target=target) + buffer_view = pygltflib.BufferView(buffer=0, byteOffset=offset, byteLength=len(data)) + if target is not None: + buffer_view.target = target self.gltf.bufferViews.append(buffer_view) return len(self.gltf.bufferViews) - 1 @@ -43,7 +68,7 @@ def add_accessor( max_values: list[float] | None = None, ) -> int: """Create an accessor for an existing buffer view and return its index.""" - accessor = self.pygltflib.Accessor( + accessor = pygltflib.Accessor( bufferView=buffer_view_idx, byteOffset=0, componentType=component_type, @@ -59,37 +84,21 @@ def add_accessor( def finalize(self) -> bytes: """Finalize the GLTF document and return the serialized GLB bytes.""" - self.gltf.buffers.append(self.pygltflib.Buffer(byteLength=len(self.binary_blob))) + self.gltf.buffers.append(pygltflib.Buffer(byteLength=len(self.binary_blob))) self.gltf.set_binary_blob(bytes(self.binary_blob)) - return bytes(self.gltf.save_to_bytes()) - - -def _import_glb_dependencies() -> tuple[Any, Any, Any, Any]: - """Import optional dependencies required for GLB export.""" - try: - import pygltflib - except ImportError as exc: - raise ImportError("pygltflib is required for GLB export. Install it with: pip install pygltflib") from exc - - try: - from pxr import Usd, UsdGeom, UsdShade - except ImportError as exc: - raise RuntimeError( - "OpenUSD Python bindings are required to convert USD to GLB. Run usd-setup and ensure pxr is importable." - ) from exc + data = self.gltf.save_to_bytes() + return data if isinstance(data, bytes) else b"".join(data) - return pygltflib, Usd, UsdGeom, UsdShade - -def _open_stage_for_glb(path: Path, usd: Any) -> Any: +def _open_stage_for_glb(path: Path) -> Any: """Open a USD stage for GLB conversion.""" - stage = usd.Stage.Open(str(path)) + stage = Usd.Stage.Open(str(path)) if not stage: raise RuntimeError(f"Failed to open USD stage: {path}") return stage -def _create_gltf_scene(pygltflib: Any) -> tuple[Any, Any]: +def _create_gltf_scene() -> tuple[Any, Any]: """Create an initialized glTF document and default scene.""" gltf = pygltflib.GLTF2() gltf.asset = pygltflib.Asset(version="2.0") @@ -99,6 +108,109 @@ def _create_gltf_scene(pygltflib: Any) -> tuple[Any, Any]: return gltf, scene +def _ensure_lights_extension(gltf: Any) -> list[dict[str, Any]]: + """Ensure KHR_lights_punctual is declared and return its light list.""" + if gltf.extensionsUsed is None: + gltf.extensionsUsed = [] + if "KHR_lights_punctual" not in gltf.extensionsUsed: + gltf.extensionsUsed.append("KHR_lights_punctual") + + if gltf.extensions is None: + gltf.extensions = {} + + lights_ext = gltf.extensions.get("KHR_lights_punctual") + if lights_ext is None: + lights_ext = {"lights": []} + gltf.extensions["KHR_lights_punctual"] = lights_ext + + lights = lights_ext.get("lights") + if lights is None: + lights = [] + lights_ext["lights"] = lights + + return lights + + +def _get_light_schema_and_type(prim: Any) -> tuple[Any, str] | None: + """Map a USD light prim to a KHR_lights_punctual-compatible type.""" + if prim.IsA(UsdLux.DistantLight): + return UsdLux.DistantLight(prim), "directional" + + if prim.IsA(UsdLux.SphereLight): + return UsdLux.SphereLight(prim), "point" + + if prim.IsA(UsdLux.DiskLight): + return UsdLux.DiskLight(prim), "point" + + return None + + +def _make_gltf_light(prim: Any) -> dict[str, Any] | None: + """Build one glTF punctual light description from a USD light prim.""" + schema_and_type = _get_light_schema_and_type(prim) + if schema_and_type is None: + return None + + light_schema, light_type = schema_and_type + color_value = light_schema.GetColorAttr().Get() + intensity_value = light_schema.GetIntensityAttr().Get() + exposure_value = light_schema.GetExposureAttr().Get() + + if intensity_value is None: + intensity_value = 1.0 + intensity = float(intensity_value) + if exposure_value is not None: + intensity *= 2.0 ** float(exposure_value) + + # USD light intensity conventions vary across renderers and can be orders + # of magnitude larger than typical WebGL values. + if light_type == "directional": + intensity /= 50000.0 + else: + intensity /= 500.0 + intensity = max(0.0, min(intensity, 20.0)) + + if color_value is None: + color = [1.0, 1.0, 1.0] + else: + color = [float(color_value[0]), float(color_value[1]), float(color_value[2])] + + light: dict[str, Any] = { + "type": light_type, + "color": color, + "intensity": intensity, + "name": prim.GetName(), + } + return light + + +def _append_usd_lights_to_scene(stage: Any, gltf: Any, scene: Any) -> None: + """Convert supported USD lights into glTF KHR_lights_punctual lights.""" + lights = _ensure_lights_extension(gltf) + + for prim in stage.Traverse(): + gltf_light = _make_gltf_light(prim) + if gltf_light is None: + continue + + light_index = len(lights) + lights.append(gltf_light) + + node = pygltflib.Node( + name=prim.GetName(), + extensions={"KHR_lights_punctual": {"light": light_index}}, + ) + + xformable = UsdGeom.Xformable(prim) + if xformable: + world = xformable.ComputeLocalToWorldTransform(Usd.TimeCode.Default()) + translation = world.ExtractTranslation() + node.translation = [float(translation[0]), float(translation[1]), float(translation[2])] + + gltf.nodes.append(node) + scene.nodes.append(len(gltf.nodes) - 1) + + def _get_triangle_indices(usd_mesh: Any) -> list[int]: """Get triangulated face indices for a USD mesh.""" indices_raw = usd_mesh.GetFaceVertexIndicesAttr().Get() @@ -109,7 +221,7 @@ def _get_triangle_indices(usd_mesh: Any) -> list[int]: return list(range(len(points))) -def _add_position_attribute(points: list[Any], builder: GLBBuilder, pygltflib: Any) -> int: +def _add_position_attribute(points: list[Any], builder: GLBBuilder) -> int: """Write position data and return the POSITION accessor index.""" pos_data = struct.pack(f"{len(points) * 3}f", *[c for p in points for c in (float(p[0]), float(p[1]), float(p[2]))]) xs = [float(p[0]) for p in points] @@ -126,16 +238,14 @@ def _add_position_attribute(points: list[Any], builder: GLBBuilder, pygltflib: A ) -def _add_index_accessor(triangle_indices: list[int], builder: GLBBuilder, pygltflib: Any) -> int: +def _add_index_accessor(triangle_indices: list[int], builder: GLBBuilder) -> int: """Write triangle index data and return the index accessor.""" idx_data = struct.pack(f"{len(triangle_indices)}I", *triangle_indices) idx_buffer_view = builder.add_buffer_view(idx_data, pygltflib.ELEMENT_ARRAY_BUFFER) return builder.add_accessor(idx_buffer_view, pygltflib.UNSIGNED_INT, len(triangle_indices), pygltflib.SCALAR) -def _add_normals_attribute( - usd_mesh: Any, points: list[Any], attributes: Any, builder: GLBBuilder, pygltflib: Any -) -> None: +def _add_normals_attribute(usd_mesh: Any, points: list[Any], attributes: Any, builder: GLBBuilder) -> None: """Attach NORMAL accessor when mesh normals are compatible.""" normals = usd_mesh.GetNormalsAttr().Get() if not normals or len(normals) != len(points): @@ -150,24 +260,17 @@ def _add_normals_attribute( def _add_uv_attribute( prim: Any, - points: list[Any], attributes: Any, + uv_values: list[tuple[float, float]] | None, builder: GLBBuilder, - pygltflib: Any, - usd_geom: Any, ) -> None: """Attach TEXCOORD_0 accessor when matching UV data exists.""" - st_primvar = usd_geom.PrimvarsAPI(prim).GetPrimvar("st") - if not st_primvar or not st_primvar.IsDefined(): - return - - st_values = st_primvar.Get() - if not st_values or len(st_values) != len(points): + if not uv_values: return - uv_data = struct.pack(f"{len(st_values) * 2}f", *[c for uv in st_values for c in (float(uv[0]), float(uv[1]))]) + uv_data = struct.pack(f"{len(uv_values) * 2}f", *[c for uv in uv_values for c in (float(uv[0]), float(uv[1]))]) uv_buffer_view = builder.add_buffer_view(uv_data, pygltflib.ARRAY_BUFFER) - attributes.TEXCOORD_0 = builder.add_accessor(uv_buffer_view, pygltflib.FLOAT, len(st_values), pygltflib.VEC2) + attributes.TEXCOORD_0 = builder.add_accessor(uv_buffer_view, pygltflib.FLOAT, len(uv_values), pygltflib.VEC2) def _get_display_colors(usd_mesh: Any) -> tuple[Any, Any]: @@ -184,13 +287,11 @@ def _add_display_color_attribute( color_interpolation: Any, attributes: Any, builder: GLBBuilder, - pygltflib: Any, - usd_geom: Any, ) -> None: """Attach COLOR_0 when displayColor is vertex-compatible.""" if not ( display_colors - and color_interpolation in {usd_geom.Tokens.vertex, usd_geom.Tokens.varying} + and color_interpolation in {UsdGeom.Tokens.vertex, UsdGeom.Tokens.varying} and len(display_colors) == len(points) ): return @@ -209,9 +310,15 @@ def _append_material_and_get_index(gltf: Any, material: Any) -> int: return len(gltf.materials) - 1 -def _build_material_from_shader(shader: Any, prim_name: str, pygltflib: Any) -> Any: +def _build_material_from_shader(shader: Any, prim_name: str) -> Any: """Build a GLTF material from a UsdPreviewSurface shader.""" - pbr = pygltflib.PbrMetallicRoughness() + # Match UsdPreviewSurface defaults to avoid glTF defaults (metallic=1) + # when USD scalar inputs are texture-connected and return None. + pbr = pygltflib.PbrMetallicRoughness( + baseColorFactor=[1.0, 1.0, 1.0, 1.0], + metallicFactor=0.0, + roughnessFactor=0.5, + ) diffuse = shader.GetInput("diffuseColor") if diffuse and diffuse.Get() is not None: @@ -226,7 +333,7 @@ def _build_material_from_shader(shader: Any, prim_name: str, pygltflib: Any) -> if roughness and roughness.Get() is not None: pbr.roughnessFactor = float(roughness.Get()) - mat = pygltflib.Material(pbrMetallicRoughness=pbr, name=prim_name) + mat = pygltflib.Material(pbrMetallicRoughness=pbr, name=prim_name, doubleSided=True) emissive = shader.GetInput("emissiveColor") if emissive and emissive.Get() is not None: @@ -236,37 +343,239 @@ def _build_material_from_shader(shader: Any, prim_name: str, pygltflib: Any) -> return mat -def _build_display_color_material(display_colors: Any, prim_name: str, pygltflib: Any) -> Any: +def _get_connected_shader_input(shader: Any, input_name: str) -> tuple[Any, str] | None: + """Return connected shader and output name for a shader input.""" + shader_input = shader.GetInput(input_name) + if not shader_input: + return None + + connected = shader_input.GetConnectedSource() + if not connected: + return None + + source_api, output_name, _ = connected + source_prim = source_api.GetPrim() + if not source_prim or not source_prim.IsValid(): + return None + + source_shader = UsdShade.Shader(source_prim) + if not source_shader or not source_shader.GetPrim().IsValid(): + return None + + return source_shader, output_name + + +def _map_wrap_mode(token: Any) -> int: + """Map USD wrap token to glTF sampler wrap mode.""" + if token == "repeat": + return pygltflib.REPEAT + if token == "mirror": + return pygltflib.MIRRORED_REPEAT + return pygltflib.CLAMP_TO_EDGE + + +def _resolve_asset_file_path(asset_input: Any, shader_prim: Any) -> Path | None: + """Resolve a USD asset path from an input to a file path.""" + if not asset_input: + return None + + asset_value = asset_input.Get() + if asset_value is None: + return None + + resolved = getattr(asset_value, "resolvedPath", None) + if resolved: + resolved_path = Path(str(resolved)) + if resolved_path.exists(): + return resolved_path + + authored = getattr(asset_value, "path", None) + if not authored: + return None + + authored_path = Path(str(authored)) + if authored_path.is_absolute() and authored_path.exists(): + return authored_path + + layer = shader_prim.GetStage().GetRootLayer() + if not layer: + return None + layer_path = Path(layer.realPath) + candidate = (layer_path.parent / authored_path).resolve() + return candidate if candidate.exists() else None + + +def _get_texture_index_from_usd_uv_texture( + source_shader: Any, + gltf: Any, + builder: GLBBuilder, + texture_cache: dict[tuple[str, str, str], int], + image_cache: dict[str, int], + sampler_cache: dict[tuple[int, int], int], +) -> int | None: + """Create or reuse a glTF texture index from a UsdUVTexture shader.""" + shader_id = source_shader.GetIdAttr().Get() + if shader_id != "UsdUVTexture": + return None + + file_path = _resolve_asset_file_path(source_shader.GetInput("file"), source_shader.GetPrim()) + if file_path is None: + return None + + wrap_s = _map_wrap_mode(source_shader.GetInput("wrapS").Get() if source_shader.GetInput("wrapS") else None) + wrap_t = _map_wrap_mode(source_shader.GetInput("wrapT").Get() if source_shader.GetInput("wrapT") else None) + + texture_key = (str(file_path), str(wrap_s), str(wrap_t)) + cached_texture = texture_cache.get(texture_key) + if cached_texture is not None: + return cached_texture + + image_key = str(file_path) + image_index = image_cache.get(image_key) + if image_index is None: + image_bytes = file_path.read_bytes() + image_buffer_view = builder.add_buffer_view(image_bytes, None) + mime_type, _ = mimetypes.guess_type(str(file_path)) + if mime_type is None: + mime_type = "image/png" + gltf.images.append(pygltflib.Image(bufferView=image_buffer_view, mimeType=mime_type, name=file_path.name)) + image_index = len(gltf.images) - 1 + image_cache[image_key] = image_index + + sampler_key = (wrap_s, wrap_t) + sampler_index = sampler_cache.get(sampler_key) + if sampler_index is None: + gltf.samplers.append(pygltflib.Sampler(wrapS=wrap_s, wrapT=wrap_t)) + sampler_index = len(gltf.samplers) - 1 + sampler_cache[sampler_key] = sampler_index + + gltf.textures.append(pygltflib.Texture(source=image_index, sampler=sampler_index)) + texture_index = len(gltf.textures) - 1 + texture_cache[texture_key] = texture_index + return texture_index + + +def _apply_preview_surface_textures( + shader: Any, + material: Any, + gltf: Any, + builder: GLBBuilder, + texture_cache: dict[tuple[str, str, str], int], + image_cache: dict[str, int], + sampler_cache: dict[tuple[int, int], int], +) -> None: + """Apply supported UsdPreviewSurface texture connections to a GLTF material.""" + pbr = material.pbrMetallicRoughness + + diffuse_connection = _get_connected_shader_input(shader, "diffuseColor") + if diffuse_connection is not None: + diffuse_shader, _ = diffuse_connection + tex_index = _get_texture_index_from_usd_uv_texture( + diffuse_shader, + gltf, + builder, + texture_cache, + image_cache, + sampler_cache, + ) + if tex_index is not None: + pbr.baseColorTexture = pygltflib.TextureInfo(index=tex_index) + + normal_connection = _get_connected_shader_input(shader, "normal") + if normal_connection is not None: + normal_shader, _ = normal_connection + tex_index = _get_texture_index_from_usd_uv_texture( + normal_shader, + gltf, + builder, + texture_cache, + image_cache, + sampler_cache, + ) + if tex_index is not None: + material.normalTexture = pygltflib.NormalMaterialTexture(index=tex_index) + + emissive_connection = _get_connected_shader_input(shader, "emissiveColor") + if emissive_connection is not None: + emissive_shader, _ = emissive_connection + tex_index = _get_texture_index_from_usd_uv_texture( + emissive_shader, + gltf, + builder, + texture_cache, + image_cache, + sampler_cache, + ) + if tex_index is not None: + material.emissiveTexture = pygltflib.TextureInfo(index=tex_index) + + opacity_input = shader.GetInput("opacity") + opacity_threshold_input = shader.GetInput("opacityThreshold") + opacity_threshold = float(opacity_threshold_input.Get()) if opacity_threshold_input else 0.0 + has_connected_opacity = _get_connected_shader_input(shader, "opacity") is not None + opacity_value = float(opacity_input.Get()) if opacity_input and opacity_input.Get() is not None else 1.0 + + if has_connected_opacity or opacity_value < 1.0: + material.alphaMode = "MASK" if opacity_threshold > 0.0 else "BLEND" + if opacity_threshold > 0.0: + material.alphaCutoff = opacity_threshold + + +def _build_display_color_material(display_colors: Any, prim_name: str) -> Any: """Build a fallback GLTF material from the first displayColor value.""" c = display_colors[0] pbr = pygltflib.PbrMetallicRoughness(baseColorFactor=[float(c[0]), float(c[1]), float(c[2]), 1.0]) - return pygltflib.Material(pbrMetallicRoughness=pbr, name=f"{prim_name}_displayColor") + return pygltflib.Material(pbrMetallicRoughness=pbr, name=f"{prim_name}_displayColor", doubleSided=True) -def _get_material_index(prim: Any, display_colors: Any, gltf: Any, pygltflib: Any, usd_shade: Any) -> int | None: +def _get_material_index( + prim: Any, + display_colors: Any, + color_interpolation: Any, + gltf: Any, + builder: GLBBuilder, + texture_cache: dict[tuple[str, str, str], int], + image_cache: dict[str, int], + sampler_cache: dict[tuple[int, int], int], +) -> int | None: """Resolve GLTF material index from USD bindings or displayColor fallback.""" mat_index = None - binding = usd_shade.MaterialBindingAPI(prim).GetDirectBinding() - if not binding: - return mat_index - - material = binding.GetMaterial() - if material: - surface_source = material.ComputeSurfaceSource() - shader = surface_source[0] if surface_source else None - if shader and shader.GetPrim().IsValid(): - mat = _build_material_from_shader(shader, prim.GetName(), pygltflib) + binding = UsdShade.MaterialBindingAPI(prim).GetDirectBinding() + if binding: + material = binding.GetMaterial() + if material: + surface_source = material.ComputeSurfaceSource() + shader = surface_source[0] if surface_source else None + if shader and shader.GetPrim().IsValid(): + mat = _build_material_from_shader(shader, prim.GetName()) + _apply_preview_surface_textures( + shader, + mat, + gltf, + builder, + texture_cache, + image_cache, + sampler_cache, + ) + mat_index = _append_material_and_get_index(gltf, mat) + + vertex_interp = {UsdGeom.Tokens.vertex, UsdGeom.Tokens.varying} + if mat_index is None and display_colors: + if color_interpolation in vertex_interp: + # Create a white material to properly use vertex colors (COLOR_0) + pbr = pygltflib.PbrMetallicRoughness(baseColorFactor=[1.0, 1.0, 1.0, 1.0]) + mat = pygltflib.Material(pbrMetallicRoughness=pbr, name=f"{prim.GetName()}_vertexColor", doubleSided=True) mat_index = _append_material_and_get_index(gltf, mat) - - if mat_index is None and display_colors and len(display_colors) > 0: - mat = _build_display_color_material(display_colors, prim.GetName(), pygltflib) + else: + # Use the first color value as a uniform material + mat = _build_display_color_material(display_colors, prim.GetName()) mat_index = _append_material_and_get_index(gltf, mat) return mat_index def _append_mesh_to_scene( - prim: Any, attributes: Any, idx_acc: int, mat_index: int | None, gltf: Any, scene: Any, pygltflib: Any + prim: Any, attributes: Any, idx_acc: int, mat_index: int | None, gltf: Any, scene: Any ) -> None: """Append one mesh primitive and its node to the active scene.""" primitive = pygltflib.Primitive(attributes=attributes, indices=idx_acc, material=mat_index) @@ -278,11 +587,11 @@ def _append_mesh_to_scene( scene.nodes.append(len(gltf.nodes) - 1) -def _iter_usd_mesh_prims(stage: Any, usd_geom: Any) -> list[Any]: +def _iter_usd_mesh_prims(stage: Any) -> list[Any]: """Collect mesh prims from a USD stage.""" mesh_prims = [] for prim in stage.Traverse(): - if prim.IsA(usd_geom.Mesh): + if prim.IsA(UsdGeom.Mesh): mesh_prims.append(prim) return mesh_prims @@ -290,31 +599,127 @@ def _iter_usd_mesh_prims(stage: Any, usd_geom: Any) -> list[Any]: def _build_attributes_for_mesh( prim: Any, usd_mesh: Any, - points: list[Any], builder: GLBBuilder, - pygltflib: Any, - usd_geom: Any, -) -> tuple[Any, int, Any]: +) -> tuple[Any, int, Any, Any]: """Build geometric attributes and index accessor for one mesh.""" - tri_indices = _get_triangle_indices(usd_mesh) - pos_acc = _add_position_attribute(points, builder, pygltflib) - idx_acc = _add_index_accessor(tri_indices, builder, pygltflib) + triangle_positions, triangle_indices, triangle_uvs = _build_triangle_mesh_data(prim, usd_mesh) + pos_acc = _add_position_attribute(triangle_positions, builder) + idx_acc = _add_index_accessor(triangle_indices, builder) attributes = pygltflib.Attributes(POSITION=pos_acc) - _add_normals_attribute(usd_mesh, points, attributes, builder, pygltflib) - _add_uv_attribute(prim, points, attributes, builder, pygltflib, usd_geom) + _add_uv_attribute(prim, attributes, triangle_uvs, builder) display_colors, color_interpolation = _get_display_colors(usd_mesh) _add_display_color_attribute( - points, + triangle_positions, display_colors, color_interpolation, attributes, builder, - pygltflib, - usd_geom, ) - return attributes, idx_acc, display_colors + return attributes, idx_acc, display_colors, color_interpolation + + +def _resolve_primvar_element_index( + interpolation: Any, + face_index: int, + corner_index: int, + point_index: int, +) -> int | None: + """Resolve primvar element index by interpolation for one face corner.""" + if interpolation in {UsdGeom.Tokens.vertex, UsdGeom.Tokens.varying}: + return point_index + if interpolation == UsdGeom.Tokens.faceVarying: + return corner_index + if interpolation == UsdGeom.Tokens.uniform: + return face_index + if interpolation == UsdGeom.Tokens.constant: + return 0 + return None + + +def _build_corner_uvs( + prim: Any, face_vertex_indices: list[int], face_vertex_counts: list[int] +) -> list[tuple[float, float]] | None: + """Build one UV coordinate per face corner, honoring indexed primvars.""" + st_primvar = UsdGeom.PrimvarsAPI(prim).GetPrimvar("st") + if not st_primvar or not st_primvar.IsDefined(): + return None + + st_values = st_primvar.Get() + if not st_values: + return None + + st_indices = st_primvar.GetIndices() or [] + interpolation = st_primvar.GetInterpolation() or UsdGeom.Tokens.faceVarying + + corner_uvs: list[tuple[float, float]] = [] + corner_offset = 0 + for face_index, count in enumerate(face_vertex_counts): + for local_corner in range(count): + corner_index = corner_offset + local_corner + point_index = face_vertex_indices[corner_index] + element_index = _resolve_primvar_element_index(interpolation, face_index, corner_index, point_index) + if element_index is None: + corner_uvs.append((0.0, 0.0)) + continue + + if st_indices and element_index < len(st_indices): + element_index = st_indices[element_index] + + if element_index is None or element_index < 0 or element_index >= len(st_values): + corner_uvs.append((0.0, 0.0)) + continue + + uv = st_values[element_index] + corner_uvs.append((float(uv[0]), float(uv[1]))) + + corner_offset += count + + return corner_uvs + + +def _build_triangle_mesh_data( + prim: Any, usd_mesh: Any +) -> tuple[list[Any], list[int], list[tuple[float, float]] | None]: + """Build triangle-ready vertex data, including expanded UVs for face-varying primvars.""" + points = usd_mesh.GetPointsAttr().Get() or [] + face_vertex_indices = list(usd_mesh.GetFaceVertexIndicesAttr().Get() or []) + face_vertex_counts = list(usd_mesh.GetFaceVertexCountsAttr().Get() or []) + + if not points or not face_vertex_indices or not face_vertex_counts: + return points, list(range(len(points))), None + + corner_uvs = _build_corner_uvs(prim, face_vertex_indices, face_vertex_counts) + + triangle_positions: list[Any] = [] + triangle_indices: list[int] = [] + triangle_uvs: list[tuple[float, float]] | None = [] if corner_uvs is not None else None + + corner_offset = 0 + for count in face_vertex_counts: + if count < 3: + corner_offset += count + continue + + for tri_step in range(1, count - 1): + tri_corners = (0, tri_step, tri_step + 1) + for local_corner in tri_corners: + corner_index = corner_offset + local_corner + point_index = face_vertex_indices[corner_index] + if point_index < 0 or point_index >= len(points): + continue + + point = points[point_index] + triangle_positions.append(point) + triangle_indices.append(len(triangle_positions) - 1) + + if triangle_uvs is not None and corner_uvs is not None: + triangle_uvs.append(corner_uvs[corner_index]) + + corner_offset += count + + return triangle_positions, triangle_indices, triangle_uvs def _convert_mesh_prim_to_gltf( @@ -322,36 +727,54 @@ def _convert_mesh_prim_to_gltf( gltf: Any, scene: Any, builder: GLBBuilder, - pygltflib: Any, - usd_geom: Any, - usd_shade: Any, + texture_cache: dict[tuple[str, str, str], int], + image_cache: dict[str, int], + sampler_cache: dict[tuple[int, int], int], ) -> None: """Convert one USD mesh prim into GLTF mesh/node entries.""" - usd_mesh = usd_geom.Mesh(prim) + usd_mesh = UsdGeom.Mesh(prim) points = usd_mesh.GetPointsAttr().Get() if not points: return - attributes, idx_acc, display_colors = _build_attributes_for_mesh( + attributes, idx_acc, display_colors, color_interpolation = _build_attributes_for_mesh( prim, usd_mesh, - points, builder, - pygltflib, - usd_geom, ) - mat_index = _get_material_index(prim, display_colors, gltf, pygltflib, usd_shade) - _append_mesh_to_scene(prim, attributes, idx_acc, mat_index, gltf, scene, pygltflib) + mat_index = _get_material_index( + prim, + display_colors, + color_interpolation, + gltf, + builder, + texture_cache, + image_cache, + sampler_cache, + ) + _append_mesh_to_scene(prim, attributes, idx_acc, mat_index, gltf, scene) def convert_usd_to_glb(path: Path) -> bytes: """Convert a USD stage to a self-contained GLB binary.""" - pygltflib, usd, usd_geom, usd_shade = _import_glb_dependencies() - stage = _open_stage_for_glb(path, usd) - gltf, scene = _create_gltf_scene(pygltflib) - builder = GLBBuilder(gltf, pygltflib) - - for prim in _iter_usd_mesh_prims(stage, usd_geom): - _convert_mesh_prim_to_gltf(prim, gltf, scene, builder, pygltflib, usd_geom, usd_shade) + stage = _open_stage_for_glb(path) + gltf, scene = _create_gltf_scene() + builder = GLBBuilder(gltf) + texture_cache: dict[tuple[str, str, str], int] = {} + image_cache: dict[str, int] = {} + sampler_cache: dict[tuple[int, int], int] = {} + + _append_usd_lights_to_scene(stage, gltf, scene) + + for prim in _iter_usd_mesh_prims(stage): + _convert_mesh_prim_to_gltf( + prim, + gltf, + scene, + builder, + texture_cache, + image_cache, + sampler_cache, + ) return builder.finalize() diff --git a/src/ansys/tools/usdviewer/web/glb_template.html b/src/ansys/tools/usdviewer/web/glb_template.html index e9c01ac..940f4b4 100644 --- a/src/ansys/tools/usdviewer/web/glb_template.html +++ b/src/ansys/tools/usdviewer/web/glb_template.html @@ -75,6 +75,7 @@ renderer.setSize(window.innerWidth, window.innerHeight); renderer.outputColorSpace = THREE.SRGBColorSpace; renderer.toneMapping = THREE.ACESFilmicToneMapping; + renderer.toneMappingExposure = 1.1; const scene = new THREE.Scene(); scene.background = new THREE.Color(0x1a1d23); @@ -84,19 +85,75 @@ const controls = new OrbitControls(camera, renderer.domElement); controls.enableDamping = true; - scene.add(new THREE.HemisphereLight(0xffffff, 0x2a2a2a, 0.85)); - const keyLight = new THREE.DirectionalLight(0xffffff, 1.15); + const homogeneousLightRig = new THREE.Group(); + homogeneousLightRig.name = "HomogeneousLightRig"; + + const ambient = new THREE.AmbientLight(0xffffff, 1.25); + homogeneousLightRig.add(ambient); + + const hemi = new THREE.HemisphereLight(0xffffff, 0x7a7a7a, 0.55); + homogeneousLightRig.add(hemi); + + scene.add(homogeneousLightRig); + + const defaultDirectionalRig = new THREE.Group(); + defaultDirectionalRig.name = "DefaultDirectionalRig"; + + const keyLight = new THREE.DirectionalLight(0xffffff, 0.6); keyLight.position.set(3, 5, 4); - scene.add(keyLight); + defaultDirectionalRig.add(keyLight); + + const fillLight = new THREE.DirectionalLight(0xffffff, 0.45); + fillLight.position.set(-3, 2, -4); + defaultDirectionalRig.add(fillLight); + + const topLight = new THREE.DirectionalLight(0xffffff, 0.25); + topLight.position.set(0, 8, 0); + defaultDirectionalRig.add(topLight); + + const bottomLight = new THREE.DirectionalLight(0xffffff, 0.3); + bottomLight.position.set(0, -6, 0); + defaultDirectionalRig.add(bottomLight); + + scene.add(defaultDirectionalRig); const binary = atob(glbBase64); const buffer = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) buffer[i] = binary.charCodeAt(i); new GLTFLoader().parse(buffer.buffer, "", (gltf) => { + let hasSceneLights = false; + gltf.scene.traverse((obj) => { + if (obj.isLight) { + hasSceneLights = true; + } + + if (obj.isMesh && obj.material) { + const materials = Array.isArray(obj.material) ? obj.material : [obj.material]; + for (const mat of materials) { + // Ensure texture color-space matches glTF PBR expectations. + if (mat.map) { + mat.map.colorSpace = THREE.SRGBColorSpace; + mat.map.needsUpdate = true; + } + if (mat.emissiveMap) { + mat.emissiveMap.colorSpace = THREE.SRGBColorSpace; + mat.emissiveMap.needsUpdate = true; + } + } + } + }); + + if (hasSceneLights) { + // Keep homogeneous fill light and remove only directional defaults. + scene.remove(defaultDirectionalRig); + } + scene.add(gltf.scene); fitCameraToObject(camera, controls, gltf.scene); - status.textContent = "Loaded: " + modelName; + status.textContent = hasSceneLights + ? "Loaded: " + modelName + " (USD lights + ambient fill)" + : "Loaded: " + modelName + " (default lights)"; }, (error) => { status.textContent = "Error loading model"; console.error(error); diff --git a/src/ansys/tools/usdviewer/web/mesh.py b/src/ansys/tools/usdviewer/web/mesh.py deleted file mode 100644 index 34780f0..0000000 --- a/src/ansys/tools/usdviewer/web/mesh.py +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright (C) 2025 - 2026 ANSYS, Inc. and/or its affiliates. -# SPDX-License-Identifier: MIT -# -"""Mesh extraction helpers for web viewer fallback rendering.""" - -from __future__ import annotations - -from pathlib import Path -from typing import Any - - -def triangulate_faces(indices: list[int], counts: list[int]) -> list[int]: - """Convert polygon faces to triangles using fan triangulation.""" - result = [] - idx = 0 - for count in counts: - face = indices[idx : idx + count] - for i in range(1, count - 1): - result.extend([face[0], face[i], face[i + 1]]) - idx += count - return result - - -def _import_usd_for_mesh_extraction() -> tuple[Any, Any]: - """Import USD modules required for mesh extraction.""" - try: - from pxr import Usd, UsdGeom - except ImportError as exc: - raise RuntimeError( - "OpenUSD Python bindings are required to extract mesh data. Run usd-setup and ensure pxr is importable." - ) from exc - - return Usd, UsdGeom - - -def _open_usd_stage(path: Path, usd: Any) -> Any: - """Open a USD stage and raise a descriptive error on failure.""" - stage = usd.Stage.Open(str(path)) - if not stage: - raise RuntimeError(f"Failed to open USD stage: {path}") - return stage - - -def _build_tri_indices(mesh: Any, point_count: int) -> list[int]: - """Build triangulated indices from face arrays or fallback to point order.""" - indices_attr = mesh.GetFaceVertexIndicesAttr().Get() - counts_attr = mesh.GetFaceVertexCountsAttr().Get() - if indices_attr and counts_attr: - return triangulate_faces(list(indices_attr), list(counts_attr)) - return list(range(point_count)) - - -def _extract_vertex_normals(mesh: Any, point_count: int) -> list[float] | None: - """Extract flat vertex normals when they align with points.""" - normals_attr = mesh.GetNormalsAttr().Get() - if not normals_attr or len(normals_attr) != point_count: - return None - return [coord for n in normals_attr for coord in (float(n[0]), float(n[1]), float(n[2]))] - - -def _extract_mesh_colors(mesh: Any, point_count: int, usd_geom: Any) -> tuple[list[float] | None, list[float] | None]: - """Extract per-vertex colors or fallback constant color from displayColor.""" - display_color_primvar = mesh.GetDisplayColorPrimvar() - color_attr = display_color_primvar.Get() if display_color_primvar else mesh.GetDisplayColorAttr().Get() - color_interpolation = display_color_primvar.GetInterpolation() if display_color_primvar else None - - colors = None - color = None - if color_attr and len(color_attr) > 0: - if color_interpolation in {usd_geom.Tokens.vertex, usd_geom.Tokens.varying} and len(color_attr) == point_count: - colors = [coord for c in color_attr for coord in (float(c[0]), float(c[1]), float(c[2]))] - else: - c = color_attr[0] - color = [float(c[0]), float(c[1]), float(c[2])] - - return colors, color - - -def _extract_mesh_payload(mesh: Any, usd_geom: Any) -> dict | None: - """Extract web-renderable mesh payload from a USD mesh prim.""" - points_attr = mesh.GetPointsAttr().Get() - if not points_attr: - return None - - point_count = len(points_attr) - flat_points = [coord for pt in points_attr for coord in (float(pt[0]), float(pt[1]), float(pt[2]))] - tri_indices = _build_tri_indices(mesh, point_count) - flat_normals = _extract_vertex_normals(mesh, point_count) - colors, color = _extract_mesh_colors(mesh, point_count, usd_geom) - - return { - "positions": flat_points, - "indices": tri_indices, - "normals": flat_normals, - "colors": colors, - "color": color, - } - - -def extract_meshes_from_stage_file(path: Path) -> list[dict]: - """Extract all mesh geometry from a USD stage file for web rendering.""" - usd, usd_geom = _import_usd_for_mesh_extraction() - stage = _open_usd_stage(path, usd) - - meshes = [] - for prim in stage.Traverse(): - if not prim.IsA(usd_geom.Mesh): - continue - mesh_payload = _extract_mesh_payload(usd_geom.Mesh(prim), usd_geom) - if mesh_payload is not None: - meshes.append(mesh_payload) - - return meshes diff --git a/src/ansys/tools/usdviewer/web/mesh_template.html b/src/ansys/tools/usdviewer/web/mesh_template.html deleted file mode 100644 index 27ddeb8..0000000 --- a/src/ansys/tools/usdviewer/web/mesh_template.html +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - USD Web Viewer - - - - -
Loading:
- - - - - diff --git a/src/ansys/tools/usdviewer/web/templates.py b/src/ansys/tools/usdviewer/web/templates.py index eccc407..fb59767 100644 --- a/src/ansys/tools/usdviewer/web/templates.py +++ b/src/ansys/tools/usdviewer/web/templates.py @@ -9,7 +9,6 @@ from pathlib import Path _GLB_TEMPLATE_FILE = "glb_template.html" -_MESH_TEMPLATE_FILE = "mesh_template.html" def build_viewer_html_glb(glb_b64: str, model_name: str) -> str: @@ -20,14 +19,6 @@ def build_viewer_html_glb(glb_b64: str, model_name: str) -> str: ) -def build_viewer_html(meshes: list[dict], model_name: str) -> str: - """Build a self-contained HTML viewer from extracted mesh JSON.""" - template = _load_template(_MESH_TEMPLATE_FILE) - return template.replace("__MODEL_NAME_JSON__", json.dumps(model_name)).replace( - "__MESH_DATA_JSON__", json.dumps(meshes) - ) - - def _load_template(template_name: str) -> str: """Load a viewer HTML template from the package directory.""" template_path = Path(__file__).with_name(template_name) diff --git a/src/ansys/tools/usdviewer/web/viewer.py b/src/ansys/tools/usdviewer/web/viewer.py index 06957ed..7c9771e 100644 --- a/src/ansys/tools/usdviewer/web/viewer.py +++ b/src/ansys/tools/usdviewer/web/viewer.py @@ -11,8 +11,7 @@ from ansys.tools.usdviewer.vtk_converter import VTKConverter from .glb import convert_usd_to_glb -from .mesh import extract_meshes_from_stage_file -from .templates import build_viewer_html, build_viewer_html_glb +from .templates import build_viewer_html_glb _SUPPORTED_USD_EXTENSIONS = {".usd", ".usda", ".usdc", ".usdz"} @@ -127,17 +126,12 @@ def _prepare_source_for_web(source_path: Path, export_root: Path) -> Path: def _generate_viewer_html(source_path: Path, html_path: Path) -> None: """Prepare mesh data and write a viewer HTML file to *html_path*. - Tries to export a GLB via ``pygltflib`` for full PBR material support. - Falls back to the raw mesh extraction path when ``pygltflib`` is not - installed. Intermediate files (e.g. ``_webprep.usda``) are written to + Converts the USD asset to GLB via ``pygltflib`` for full PBR material + support. Intermediate files (e.g. ``_webprep.usda``) are written to ``html_path.parent``. """ prepared_path = _prepare_source_for_web(source_path, html_path.parent) - try: - glb_bytes = convert_usd_to_glb(prepared_path) - glb_b64 = base64.b64encode(glb_bytes).decode("ascii") - html = build_viewer_html_glb(glb_b64, source_path.name) - except ImportError: - meshes = extract_meshes_from_stage_file(prepared_path) - html = build_viewer_html(meshes, source_path.name) + glb_bytes = convert_usd_to_glb(prepared_path) + glb_b64 = base64.b64encode(glb_bytes).decode("ascii") + html = build_viewer_html_glb(glb_b64, source_path.name) html_path.write_text(html, encoding="utf-8") diff --git a/tests/test_web_viewer.py b/tests/test_web_viewer.py index 4553f2a..f488d0c 100644 --- a/tests/test_web_viewer.py +++ b/tests/test_web_viewer.py @@ -6,37 +6,33 @@ from pathlib import Path from unittest.mock import patch -from conftest import requires_openusd import pytest from ansys.tools.usdviewer.web.viewer import export_viewer_html -@patch("ansys.tools.usdviewer.web.viewer.extract_meshes_from_stage_file") @patch("ansys.tools.usdviewer.web.viewer._prepare_source_for_web") -def test_export_viewer_html_default_output_path(mock_prepare, mock_extract, tmp_path): +def test_export_viewer_html_default_output_path(mock_prepare, tmp_path): """Test that export_viewer_html writes the HTML alongside the input file by default.""" usda_path = tmp_path / "scene.usda" usda_path.write_text("#usda 1.0", encoding="utf-8") mock_prepare.return_value = usda_path - mock_extract.return_value = [] html_path = export_viewer_html(usda_path) assert html_path == tmp_path / "scene_viewer.html" assert html_path.exists() - assert "meshData" in html_path.read_text(encoding="utf-8") + html = html_path.read_text(encoding="utf-8") + assert "glbBase64" in html -@patch("ansys.tools.usdviewer.web.viewer.extract_meshes_from_stage_file") @patch("ansys.tools.usdviewer.web.viewer._prepare_source_for_web") -def test_export_viewer_html_explicit_output_path(mock_prepare, mock_extract, tmp_path): +def test_export_viewer_html_explicit_output_path(mock_prepare, tmp_path): """Test that export_viewer_html respects an explicit output path.""" usda_path = tmp_path / "scene.usda" usda_path.write_text("#usda 1.0", encoding="utf-8") out_path = tmp_path / "output" / "custom.html" mock_prepare.return_value = usda_path - mock_extract.return_value = [] html_path = export_viewer_html(usda_path, output_path=out_path) @@ -91,28 +87,3 @@ def Export(self, path): # noqa: N802 assert prepared.name.endswith("_webprep.usda") assert prepared.exists() mock_load_asset.assert_called_once_with(str(vtk_file.resolve()), mock_stage_open.return_value) - - -@requires_openusd -def test_extract_meshes_from_stage_file_keeps_vertex_display_color(tmp_path): - """Test that per-vertex USD displayColor is preserved for web rendering.""" - from pxr import Gf, Usd, UsdGeom - - from ansys.tools.usdviewer.web.mesh import extract_meshes_from_stage_file - - usd_path = tmp_path / "vertex_colors.usda" - stage = Usd.Stage.CreateNew(str(usd_path)) - mesh = UsdGeom.Mesh.Define(stage, "/Mesh") - mesh.CreatePointsAttr().Set([Gf.Vec3f(0, 0, 0), Gf.Vec3f(1, 0, 0), Gf.Vec3f(0, 1, 0)]) - mesh.CreateFaceVertexCountsAttr().Set([3]) - mesh.CreateFaceVertexIndicesAttr().Set([0, 1, 2]) - - display_color = mesh.CreateDisplayColorPrimvar(UsdGeom.Tokens.vertex) - display_color.Set([Gf.Vec3f(1, 0, 0), Gf.Vec3f(0, 1, 0), Gf.Vec3f(0, 0, 1)]) - stage.Save() - - meshes = extract_meshes_from_stage_file(usd_path) - - assert len(meshes) == 1 - assert meshes[0]["colors"] == [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0] - assert meshes[0]["color"] is None From 3b0a1336d72f89740088f69cf46049dff92f1dc2 Mon Sep 17 00:00:00 2001 From: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com> Date: Mon, 25 May 2026 08:40:59 +0000 Subject: [PATCH 04/18] chore: adding changelog file 75.added.md [dependabot-skip] --- pyproject.toml | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ca4ed74..d51097d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -114,6 +114,11 @@ template = "doc/changelog.d/changelog_template.jinja" title_format = "`{version} `_ - {project_date}" issue_format = "`#{issue} `_" +[[tool.towncrier.type]] +directory = "breaking" +name = "Breaking" +showcontent = true + [[tool.towncrier.type]] directory = "added" name = "Added" @@ -125,8 +130,8 @@ name = "Fixed" showcontent = true [[tool.towncrier.type]] -directory = "changed" -name = "Changed" +directory = "documentation" +name = "Documentation" showcontent = true [[tool.towncrier.type]] @@ -135,23 +140,23 @@ name = "Dependencies" showcontent = true [[tool.towncrier.type]] -directory = "miscellaneous" -name = "Miscellaneous" +directory = "maintenance" +name = "Maintenance" showcontent = true [[tool.towncrier.type]] -directory = "documentation" -name = "Documentation" +directory = "miscellaneous" +name = "Miscellaneous" showcontent = true [[tool.towncrier.type]] -directory = "maintenance" -name = "Maintenance" +directory = "test" +name = "Test" showcontent = true [[tool.towncrier.type]] -directory = "test" -name = "Test" +directory = "changed" +name = "Changed" showcontent = true [tool.uv] From 40513cd14318797afeaec438d50770986a48f54d Mon Sep 17 00:00:00 2001 From: Alex Fernandez Date: Mon, 25 May 2026 14:39:46 +0200 Subject: [PATCH 05/18] fix: Split code into modules --- examples/03-web-view.py | 2 +- examples/assets/robot_walk_idle.usdz | Bin 12327748 -> 0 bytes src/ansys/tools/usdviewer/web/glb.py | 761 +----------------- src/ansys/tools/usdviewer/web/glb_builder.py | 75 ++ src/ansys/tools/usdviewer/web/glb_lights.py | 114 +++ .../tools/usdviewer/web/glb_materials.py | 285 +++++++ src/ansys/tools/usdviewer/web/glb_mesh.py | 285 +++++++ 7 files changed, 780 insertions(+), 742 deletions(-) delete mode 100644 examples/assets/robot_walk_idle.usdz create mode 100644 src/ansys/tools/usdviewer/web/glb_builder.py create mode 100644 src/ansys/tools/usdviewer/web/glb_lights.py create mode 100644 src/ansys/tools/usdviewer/web/glb_materials.py create mode 100644 src/ansys/tools/usdviewer/web/glb_mesh.py diff --git a/examples/03-web-view.py b/examples/03-web-view.py index c0ac2e9..2aedd39 100644 --- a/examples/03-web-view.py +++ b/examples/03-web-view.py @@ -9,7 +9,7 @@ if __name__ == "__main__": - html_path = export_viewer_html("assets/display_color_vtk.usda") + html_path = export_viewer_html("assets/complex_scene/scene.usda") print(f"Viewer page generated at: {html_path}") print("Controls: left-drag orbit, right-drag pan, mouse wheel zoom.") webbrowser.open(html_path.resolve().as_uri()) diff --git a/examples/assets/robot_walk_idle.usdz b/examples/assets/robot_walk_idle.usdz deleted file mode 100644 index 1c6b1c01c43c2c7ac2afe88712667742a60d0051..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12327748 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

}=%aj@{Uw&Sxu4>NozC+)(eDmfHs_8Ssor zk6)sILm)>Z-$l~D_ctU7L~|UVh7VxjV6xgAs!Fh0FV*KTsX@D<4}l2j#x?j(+b%vi zXwRZhAm*aQKl%?OWH!L14*pX1et9_Y9$-rUUkNB_s`Xj+qzgUb?tTQ4g%5qAZC^hV zG^ZWmrxP`_rHF;vPC?X8z6T8lYot6daeL)93@jse@VPnK&S6h9%!fU7B;^+1wm3z0 z7)A)R>co|?^f5964sV>cNjETUN-;vhiJ!2|HJw1XTSH zdz@5QC$6@39p7@bUH3C+JAR{uST~7<#BZm8qpl%r$KpKGzQHL3XWB-3`XW1Jv7H3G z_9t0{zo?q$i8-Ize zNsN72k9f6d*Kc*_?{&u?_YPW@$OpN*`Tg>(>~C$_e9)-X`?~+LiHUFOp)cq$58D|F zPhPc?O>$pJ%pq+2&WHXLQ2GZhsGsf~NRvhpRAlIj|FZ0Mpxarxofb@NR4tpoSI{Co(r;v+fq%ajWp% zao7$h;KoI7vm2|0<6~;GODzlrO^=^qXF53ojE1ssNoAX65AvEX-0rl`M4q|oG~zZ} z-F?G%UhM|(H6c($e&A53H+#W9A5Kqi(sXplct_Kk@wX35i=2gH43yTtu`?dnoEmQT zaW?$yg3h_mOS2@FpF3*tIcbYzm^$GorjI^{e;)>R4j#Upj3wxrc4s^J_%_~oQ0@%h z2x@zT44x-xR}PnDkIK=Ri^3D-v8g#S_$f)dAV-$1l%tbYh0l{0$I|z*qz%fEy!CSQ z{C7p|QR(65WCoK@H@oMr$GTmIBma>E`s9-TmDJxQ?;N}5=Q`b11HM=%fdS*~o^|89 z#6Tf^EP*p;+C5KSdTm^N4L@epo;yVzy#oHfK-=>{$h*%=k(jHWg-OPy>WiPEnk zU3L=Z{_@7zJSC}ahM9fO&BV)Z`iuQcB=tzPdaYB7B?}*L+kP{jF!4o&tm-p9PXo)3xCmSa)byMN0$iDqP>Az3b zw4cw$N|Cz-Q)IDhYCjRmm;PIz+lOcf^MYp)>LFrw+e}-yh-mO=LQX_uyJ-zu5se+D z6>)GHJ55WTDjL6-7P2!pMGiMzFe+CbzKSD#d-<}J6z0M98`aIJyqd7#oTp`Q(+})9 zvf&&^D%|dxLw*BRjQAZmF@mbthKN1DhY@>$4I`j6$YS@+eWL5Xlyy#TFFx`wd29UY zP2i;MR#rf7%u0}LKzzYx@Q9Yec<(!GEx-uia3$(F=7qZFEAR>|1;Zg@WtAMJz#0Hr z8?8QapJjPrcOMCzqrU7f>3xHJ%lPD3YCTH#ZGCvqgPj%GbslU$IQ^x2FSTo?^P<#7J9;lh!D9zP2l=`4iP}0@kC?G}-@tKASM1Ok9**WnL-xO3q<|43u%=LVi zRww$)>Jtti!IvG8l);&CcwBLHILQRa0#5SproGGf3f0`=vAFaaFRMv>_vKI�?zE;@l@7R~5zeWsX!^%X2tw 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