ansys-pyensight-core 0.8.7__py3-none-any.whl → 0.8.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ansys-pyensight-core might be problematic. Click here for more details.

Files changed (34) hide show
  1. ansys/pyensight/core/dockerlauncher.py +6 -0
  2. ansys/pyensight/core/launcher.py +7 -2
  3. ansys/pyensight/core/locallauncher.py +4 -0
  4. ansys/pyensight/core/session.py +1 -1
  5. ansys/pyensight/core/utils/dsg_server.py +227 -35
  6. ansys/pyensight/core/utils/omniverse.py +84 -24
  7. ansys/pyensight/core/utils/omniverse_cli.py +481 -0
  8. ansys/pyensight/core/utils/omniverse_dsg_server.py +236 -426
  9. ansys/pyensight/core/utils/omniverse_glb_server.py +279 -0
  10. {ansys_pyensight_core-0.8.7.dist-info → ansys_pyensight_core-0.8.9.dist-info}/METADATA +12 -6
  11. ansys_pyensight_core-0.8.9.dist-info/RECORD +34 -0
  12. ansys/pyensight/core/exts/ansys.geometry.service/ansys/geometry/service/__init__.py +0 -1
  13. ansys/pyensight/core/exts/ansys.geometry.service/ansys/geometry/service/extension.py +0 -407
  14. ansys/pyensight/core/exts/ansys.geometry.service/config/extension.toml +0 -59
  15. ansys/pyensight/core/exts/ansys.geometry.service/data/icon.png +0 -0
  16. ansys/pyensight/core/exts/ansys.geometry.service/data/preview.png +0 -0
  17. ansys/pyensight/core/exts/ansys.geometry.service/docs/CHANGELOG.md +0 -11
  18. ansys/pyensight/core/exts/ansys.geometry.service/docs/README.md +0 -13
  19. ansys/pyensight/core/exts/ansys.geometry.service/docs/index.rst +0 -18
  20. ansys/pyensight/core/exts/ansys.geometry.serviceui/ansys/geometry/serviceui/__init__.py +0 -1
  21. ansys/pyensight/core/exts/ansys.geometry.serviceui/ansys/geometry/serviceui/extension.py +0 -193
  22. ansys/pyensight/core/exts/ansys.geometry.serviceui/config/extension.toml +0 -49
  23. ansys/pyensight/core/exts/ansys.geometry.serviceui/data/icon.png +0 -0
  24. ansys/pyensight/core/exts/ansys.geometry.serviceui/data/preview.png +0 -0
  25. ansys/pyensight/core/exts/ansys.geometry.serviceui/docs/CHANGELOG.md +0 -11
  26. ansys/pyensight/core/exts/ansys.geometry.serviceui/docs/README.md +0 -13
  27. ansys/pyensight/core/exts/ansys.geometry.serviceui/docs/index.rst +0 -18
  28. ansys/pyensight/core/utils/resources/Materials/Fieldstone/Fieldstone_BaseColor.png +0 -0
  29. ansys/pyensight/core/utils/resources/Materials/Fieldstone/Fieldstone_N.png +0 -0
  30. ansys/pyensight/core/utils/resources/Materials/Fieldstone/Fieldstone_ORM.png +0 -0
  31. ansys/pyensight/core/utils/resources/Materials/Fieldstone.mdl +0 -54
  32. ansys_pyensight_core-0.8.7.dist-info/RECORD +0 -52
  33. {ansys_pyensight_core-0.8.7.dist-info → ansys_pyensight_core-0.8.9.dist-info}/LICENSE +0 -0
  34. {ansys_pyensight_core-0.8.7.dist-info → ansys_pyensight_core-0.8.9.dist-info}/WHEEL +0 -0
@@ -24,98 +24,68 @@
24
24
  #
25
25
  ###############################################################################
26
26
 
27
- import argparse
28
27
  import logging
29
28
  import math
30
29
  import os
31
30
  import shutil
32
- import sys
31
+ import tempfile
33
32
  from typing import Any, Dict, List, Optional
34
33
 
35
- import omni.client
34
+ from ansys.pyensight.core.utils.dsg_server import Part, UpdateHandler
36
35
  import png
37
36
  from pxr import Gf, Sdf, Usd, UsdGeom, UsdLux, UsdShade
38
37
 
39
- sys.path.append(os.path.dirname(__file__))
40
- from dsg_server import DSGSession, Part, UpdateHandler # noqa: E402
41
38
 
42
-
43
- class OmniverseWrapper:
44
- verbose = 0
45
-
46
- @staticmethod
47
- def logCallback(threadName: None, component: Any, level: Any, message: str) -> None:
48
- """
49
- The logger method registered to handle async messages from Omniverse
50
-
51
- If running in verbose mode, reroute the messages to Python Logging.
52
- """
53
- if OmniverseWrapper.verbose:
54
- logging.info(message)
55
-
56
- @staticmethod
57
- def connectionStatusCallback(
58
- url: Any, connectionStatus: "omni.client.ConnectionStatus"
59
- ) -> None:
60
- """
61
- If no connection to Omniverse can be made, shut down the service.
62
- """
63
- if connectionStatus is omni.client.ConnectionStatus.CONNECT_ERROR:
64
- sys.exit("[ERROR] Failed connection, exiting.")
65
-
66
- def __init__(
67
- self,
68
- live_edit: bool = False,
69
- path: str = "omniverse://localhost/Users/test",
70
- verbose: int = 0,
71
- ) -> None:
39
+ class OmniverseWrapper(object):
40
+ def __init__(self, live_edit: bool = False, destination: str = "") -> None:
72
41
  self._cleaned_index = 0
73
42
  self._cleaned_names: dict = {}
74
43
  self._connectionStatusSubscription = None
75
44
  self._stage = None
76
- self._destinationPath: str = path
45
+ self._destinationPath: str = ""
77
46
  self._old_stages: list = []
78
47
  self._stagename = "dsg_scene.usd"
79
48
  self._live_edit: bool = live_edit
80
49
  if self._live_edit:
81
50
  self._stagename = "dsg_scene.live"
82
- OmniverseWrapper.verbose = verbose
83
-
84
- omni.client.set_log_callback(OmniverseWrapper.logCallback)
85
- if verbose > 1:
86
- omni.client.set_log_level(omni.client.LogLevel.DEBUG)
51
+ # USD time slider will have 120 tick marks per second of animation time
52
+ self._time_codes_per_second = 120.0
87
53
 
88
- if not omni.client.initialize():
89
- sys.exit("[ERROR] Unable to initialize Omniverse client, exiting.")
54
+ if destination:
55
+ self.destination = destination
90
56
 
91
- self._connectionStatusSubscription = omni.client.register_connection_status_callback(
92
- OmniverseWrapper.connectionStatusCallback
93
- )
94
-
95
- if not self.isValidOmniUrl(self._destinationPath):
96
- self.log("Note technically the Omniverse URL {self._destinationPath} is not valid")
57
+ @property
58
+ def destination(self) -> str:
59
+ """The current output directory."""
60
+ return self._destinationPath
97
61
 
98
- def log(self, msg: str) -> None:
99
- """
100
- Local method to dispatch to whatever logging system has been enabled.
101
- """
102
- if OmniverseWrapper.verbose:
103
- logging.info(msg)
62
+ @destination.setter
63
+ def destination(self, directory: str) -> None:
64
+ self._destinationPath = directory
65
+ if not self.is_valid_destination(directory):
66
+ logging.warning(f"Invalid destination path: {directory}")
104
67
 
105
68
  def shutdown(self) -> None:
106
69
  """
107
70
  Shutdown the connection to Omniverse cleanly.
108
71
  """
109
- omni.client.live_wait_for_pending_updates()
110
72
  self._connectionStatusSubscription = None
111
- omni.client.shutdown()
112
73
 
113
74
  @staticmethod
114
- def isValidOmniUrl(url: str) -> bool:
115
- omniURL = omni.client.break_url(url)
116
- if omniURL.scheme == "omniverse" or omniURL.scheme == "omni":
117
- return True
118
- return False
75
+ def is_valid_destination(path: str) -> bool:
76
+ """
77
+ Verify that the target path is a writeable directory.
78
+
79
+ Parameters
80
+ ----------
81
+ path
82
+ The path to check
83
+
84
+ Returns
85
+ -------
86
+ True if the path is a writeable directory, False otherwise.
87
+ """
88
+ return os.access(path, os.W_OK)
119
89
 
120
90
  def stage_url(self, name: Optional[str] = None) -> str:
121
91
  """
@@ -131,21 +101,31 @@ class OmniverseWrapper:
131
101
  """
132
102
  if name is None:
133
103
  name = self._stagename
134
- return self._destinationPath + "/" + name
104
+ return os.path.join(self._destinationPath, name)
135
105
 
136
106
  def delete_old_stages(self) -> None:
137
107
  """
138
108
  Remove all the stages included in the "_old_stages" list.
109
+ If a stage is in use and cannot be removed, keep its name in _old_stages
110
+ to retry later.
139
111
  """
112
+ stages_unremoved = list()
140
113
  while self._old_stages:
141
114
  stage = self._old_stages.pop()
142
- omni.client.delete(stage)
115
+ try:
116
+ if os.path.isfile(stage):
117
+ os.remove(stage)
118
+ else:
119
+ shutil.rmtree(stage, ignore_errors=True, onerror=None)
120
+ except OSError:
121
+ stages_unremoved.append(stage)
122
+ self._old_stages = stages_unremoved
143
123
 
144
124
  def create_new_stage(self) -> None:
145
125
  """
146
126
  Create a new stage. using the current stage name.
147
127
  """
148
- self.log(f"Creating Omniverse stage: {self.stage_url()}")
128
+ logging.info(f"Creating Omniverse stage: {self.stage_url()}")
149
129
  if self._stage:
150
130
  self._stage.Unload()
151
131
  self._stage = None
@@ -156,7 +136,7 @@ class OmniverseWrapper:
156
136
  UsdGeom.SetStageUpAxis(self._stage, UsdGeom.Tokens.y)
157
137
  # in M
158
138
  UsdGeom.SetStageMetersPerUnit(self._stage, 1.0)
159
- self.log(f"Created stage: {self.stage_url()}")
139
+ logging.info(f"Created stage: {self.stage_url()}")
160
140
 
161
141
  def save_stage(self, comment: str = "") -> None:
162
142
  """
@@ -165,45 +145,6 @@ class OmniverseWrapper:
165
145
  Presently, live connections are disabled.
166
146
  """
167
147
  self._stage.GetRootLayer().Save() # type:ignore
168
- omni.client.live_process()
169
-
170
- # This function will add a commented checkpoint to a file on Nucleus if
171
- # the Nucleus server supports checkpoints
172
- def checkpoint(self, comment: str = "") -> None:
173
- """
174
- Add a checkpoint to the current stage.
175
-
176
- Parameters
177
- ----------
178
- comment: str
179
- If not empty, the comment to be added to the stage
180
- """
181
- if not comment:
182
- return
183
- result, serverInfo = omni.client.get_server_info(self.stage_url())
184
- if result and serverInfo and serverInfo.checkpoints_enabled:
185
- bForceCheckpoint = True
186
- self.log(f"Adding checkpoint comment <{comment}> to stage <{self.stage_url()}>")
187
- omni.client.create_checkpoint(self.stage_url(), comment, bForceCheckpoint)
188
-
189
- def username(self, display: bool = True) -> Optional[str]:
190
- """
191
- Get the username of the current user.
192
-
193
- Parameters
194
- ----------
195
- display : bool, optional if True, send the username to the logging system.
196
-
197
- Returns
198
- -------
199
- The username or None.
200
- """
201
- result, serverInfo = omni.client.get_server_info(self.stage_url())
202
- if serverInfo:
203
- if display:
204
- self.log(f"Connected username:{serverInfo.username}")
205
- return serverInfo.username
206
- return None
207
148
 
208
149
  def clear_cleaned_names(self) -> None:
209
150
  """
@@ -305,6 +246,7 @@ class OmniverseWrapper:
305
246
  self,
306
247
  name,
307
248
  id,
249
+ part_hash,
308
250
  parent_prim,
309
251
  verts,
310
252
  conn,
@@ -318,46 +260,64 @@ class OmniverseWrapper:
318
260
  ):
319
261
  # 1D texture map for variables https://graphics.pixar.com/usd/release/tut_simple_shading.html
320
262
  # create the part usd object
321
- partname = self.clean_name(name + str(id) + str(timeline[0]))
263
+ partname = self.clean_name(name + part_hash.hexdigest())
322
264
  stage_name = "/Parts/" + partname + ".usd"
323
- part_stage_url = self.stage_url(stage_name)
324
- omni.client.delete(part_stage_url)
325
- part_stage = Usd.Stage.CreateNew(part_stage_url)
326
- self._old_stages.append(part_stage_url)
327
- xform = UsdGeom.Xform.Define(part_stage, "/" + partname)
328
- mesh = UsdGeom.Mesh.Define(part_stage, "/" + partname + "/Mesh")
329
- # mesh.CreateDisplayColorAttr()
330
- mesh.CreateDoubleSidedAttr().Set(True)
331
- mesh.CreatePointsAttr(verts)
332
- mesh.CreateNormalsAttr(normals)
333
- mesh.CreateFaceVertexCountsAttr([3] * int(conn.size / 3))
334
- mesh.CreateFaceVertexIndicesAttr(conn)
335
- if (tcoords is not None) and variable:
336
- # USD 22.08 changed the primvar API
337
- if hasattr(mesh, "CreatePrimvar"):
338
- texCoords = mesh.CreatePrimvar(
339
- "st", Sdf.ValueTypeNames.TexCoord2fArray, UsdGeom.Tokens.varying
340
- )
341
- else:
342
- primvarsAPI = UsdGeom.PrimvarsAPI(mesh)
343
- texCoords = primvarsAPI.CreatePrimvar(
344
- "st", Sdf.ValueTypeNames.TexCoord2fArray, UsdGeom.Tokens.varying
345
- )
346
- texCoords.Set(tcoords)
347
- texCoords.SetInterpolation("vertex")
348
- # sphere = part_stage.DefinePrim('/' + partname + '/sphere', 'Sphere')
349
- part_prim = part_stage.GetPrimAtPath("/" + partname)
350
- part_stage.SetDefaultPrim(part_prim)
351
-
352
- # Currently, this will never happen, but it is a setup for rigid body transforms
353
- # At present, the group transforms have been cooked into the vertices so this is not needed
354
- matrixOp = xform.AddXformOp(UsdGeom.XformOp.TypeTransform, UsdGeom.XformOp.PrecisionDouble)
355
- matrixOp.Set(Gf.Matrix4d(*matrix).GetTranspose())
356
-
357
- self.create_dsg_material(
358
- part_stage, mesh, "/" + partname, diffuse=diffuse, variable=variable
359
- )
265
+ part_stage_url = self.stage_url(os.path.join("Parts", partname + ".usd"))
266
+ part_stage = None
267
+
268
+ if not os.path.exists(part_stage_url):
269
+ part_stage = Usd.Stage.CreateNew(part_stage_url)
270
+ self._old_stages.append(part_stage_url)
271
+ xform = UsdGeom.Xform.Define(part_stage, "/" + partname)
272
+ mesh = UsdGeom.Mesh.Define(part_stage, "/" + partname + "/Mesh")
273
+ # mesh.CreateDisplayColorAttr()
274
+ mesh.CreateDoubleSidedAttr().Set(True)
275
+ mesh.CreatePointsAttr(verts)
276
+ mesh.CreateNormalsAttr(normals)
277
+ mesh.CreateFaceVertexCountsAttr([3] * int(conn.size / 3))
278
+ mesh.CreateFaceVertexIndicesAttr(conn)
279
+ if (tcoords is not None) and variable:
280
+ # USD 22.08 changed the primvar API
281
+ if hasattr(mesh, "CreatePrimvar"):
282
+ texCoords = mesh.CreatePrimvar(
283
+ "st", Sdf.ValueTypeNames.TexCoord2fArray, UsdGeom.Tokens.varying
284
+ )
285
+ else:
286
+ primvarsAPI = UsdGeom.PrimvarsAPI(mesh)
287
+ texCoords = primvarsAPI.CreatePrimvar(
288
+ "st", Sdf.ValueTypeNames.TexCoord2fArray, UsdGeom.Tokens.varying
289
+ )
290
+ texCoords.Set(tcoords)
291
+ texCoords.SetInterpolation("vertex")
292
+ part_prim = part_stage.GetPrimAtPath("/" + partname)
293
+ part_stage.SetDefaultPrim(part_prim)
294
+
295
+ # Currently, this will never happen, but it is a setup for rigid body transforms
296
+ # At present, the group transforms have been cooked into the vertices so this is not needed
297
+ matrixOp = xform.AddXformOp(
298
+ UsdGeom.XformOp.TypeTransform, UsdGeom.XformOp.PrecisionDouble
299
+ )
300
+ matrixOp.Set(Gf.Matrix4d(*matrix).GetTranspose())
301
+
302
+ self.create_dsg_material(
303
+ part_stage, mesh, "/" + partname, diffuse=diffuse, variable=variable
304
+ )
360
305
 
306
+ timestep_prim = self.add_timestep_group(parent_prim, timeline, first_timestep)
307
+
308
+ # glue it into our stage
309
+ path = timestep_prim.GetPath().AppendChild("part_ref_" + partname)
310
+ part_ref = self._stage.OverridePrim(path)
311
+ part_ref.GetReferences().AddReference("." + stage_name)
312
+
313
+ if part_stage is not None:
314
+ part_stage.GetRootLayer().Save()
315
+
316
+ return part_stage_url
317
+
318
+ def add_timestep_group(
319
+ self, parent_prim: UsdGeom.Xform, timeline: List[float], first_timestep: bool
320
+ ) -> UsdGeom.Xform:
361
321
  # add a layer in the group hierarchy for the timestep
362
322
  timestep_group_path = parent_prim.GetPath().AppendChild(
363
323
  self.clean_name("t" + str(timeline[0]), None)
@@ -368,17 +328,72 @@ class OmniverseWrapper:
368
328
  visibility_attr.Set("inherited", Usd.TimeCode.EarliestTime())
369
329
  else:
370
330
  visibility_attr.Set("invisible", Usd.TimeCode.EarliestTime())
371
- visibility_attr.Set("inherited", timeline[0])
331
+ visibility_attr.Set("inherited", timeline[0] * self._time_codes_per_second)
372
332
  # Final timestep has timeline[0]==timeline[1]. Leave final timestep visible.
373
333
  if timeline[0] < timeline[1]:
374
- visibility_attr.Set("invisible", timeline[1])
334
+ visibility_attr.Set("invisible", timeline[1] * self._time_codes_per_second)
335
+ return timestep_prim
336
+
337
+ def create_dsg_points(
338
+ self,
339
+ name,
340
+ id,
341
+ part_hash,
342
+ parent_prim,
343
+ verts,
344
+ sizes,
345
+ colors,
346
+ matrix=[1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0],
347
+ default_size=1.0,
348
+ default_color=[1.0, 1.0, 1.0, 1.0],
349
+ timeline=[0.0, 0.0],
350
+ first_timestep=False,
351
+ ):
352
+ # create the part usd object
353
+ partname = self.clean_name(name + part_hash.hexdigest())
354
+ stage_name = "/Parts/" + partname + ".usd"
355
+ part_stage_url = self.stage_url(os.path.join("Parts", partname + ".usd"))
356
+ part_stage = None
357
+
358
+ if not os.path.exists(part_stage_url):
359
+ part_stage = Usd.Stage.CreateNew(part_stage_url)
360
+ self._old_stages.append(part_stage_url)
361
+ xform = UsdGeom.Xform.Define(part_stage, "/" + partname)
362
+
363
+ points = UsdGeom.Points.Define(part_stage, "/" + partname + "/Points")
364
+ # points.GetPointsAttr().Set(Vt.Vec3fArray(verts.tolist()))
365
+ points.GetPointsAttr().Set(verts)
366
+ if sizes is not None and sizes.size == (verts.size // 3):
367
+ points.GetWidthsAttr().Set(sizes)
368
+ else:
369
+ points.GetWidthsAttr().Set([default_size] * (verts.size // 3))
370
+
371
+ colorAttr = points.GetPrim().GetAttribute("primvars:displayColor")
372
+ colorAttr.SetMetadata("interpolation", "vertex")
373
+ if colors is not None and colors.size == verts.size:
374
+ colorAttr.Set(colors)
375
+ else:
376
+ colorAttr.Set([default_color[0:3]] * (verts.size // 3))
377
+
378
+ part_prim = part_stage.GetPrimAtPath("/" + partname)
379
+ part_stage.SetDefaultPrim(part_prim)
380
+
381
+ # Currently, this will never happen, but it is a setup for rigid body transforms
382
+ # At present, the group transforms have been cooked into the vertices so this is not needed
383
+ matrixOp = xform.AddXformOp(
384
+ UsdGeom.XformOp.TypeTransform, UsdGeom.XformOp.PrecisionDouble
385
+ )
386
+ matrixOp.Set(Gf.Matrix4d(*matrix).GetTranspose())
387
+
388
+ timestep_prim = self.add_timestep_group(parent_prim, timeline, first_timestep)
375
389
 
376
390
  # glue it into our stage
377
391
  path = timestep_prim.GetPath().AppendChild("part_ref_" + partname)
378
392
  part_ref = self._stage.OverridePrim(path)
379
393
  part_ref.GetReferences().AddReference("." + stage_name)
380
394
 
381
- part_stage.GetRootLayer().Save()
395
+ if part_stage is not None:
396
+ part_stage.GetRootLayer().Save()
382
397
 
383
398
  return part_stage_url
384
399
 
@@ -424,25 +439,25 @@ class OmniverseWrapper:
424
439
  return material
425
440
 
426
441
  def create_dsg_variable_textures(self, variables):
427
- # make folder: scratch/Textures/{palette_*.png}
428
- shutil.rmtree("scratch", ignore_errors=True, onerror=None)
429
- os.makedirs("scratch/Textures", exist_ok=True)
430
- for var in variables.values():
431
- data = bytearray(var.texture)
432
- n_pixels = int(len(data) / 4)
433
- row = []
434
- for i in range(n_pixels):
435
- row.append(data[i * 4 + 0])
436
- row.append(data[i * 4 + 1])
437
- row.append(data[i * 4 + 2])
438
- io = png.Writer(width=n_pixels, height=2, bitdepth=8, greyscale=False)
439
- rows = [row, row]
440
- name = self.clean_name(var.name)
441
- with open(f"scratch/Textures/palette_{name}.png", "wb") as fp:
442
- io.write(fp, rows)
443
- uriPath = self._destinationPath + "/Parts/Textures"
444
- omni.client.delete(uriPath)
445
- omni.client.copy("scratch/Textures", uriPath)
442
+ with tempfile.TemporaryDirectory() as tempdir:
443
+ # make folder: {tempdir}/scratch/Textures/{palette_*.png}
444
+ os.makedirs(f"{tempdir}/scratch/Textures", exist_ok=True)
445
+ for var in variables.values():
446
+ data = bytearray(var.texture)
447
+ n_pixels = int(len(data) / 4)
448
+ row = []
449
+ for i in range(n_pixels):
450
+ row.append(data[i * 4 + 0])
451
+ row.append(data[i * 4 + 1])
452
+ row.append(data[i * 4 + 2])
453
+ io = png.Writer(width=n_pixels, height=2, bitdepth=8, greyscale=False)
454
+ rows = [row, row]
455
+ name = self.clean_name(var.name)
456
+ with open(f"{tempdir}/scratch/Textures/palette_{name}.png", "wb") as fp:
457
+ io.write(fp, rows)
458
+ uriPath = self._destinationPath + "/Parts/Textures"
459
+ shutil.rmtree(uriPath, ignore_errors=True, onerror=None)
460
+ shutil.copytree(f"{tempdir}/scratch/Textures", uriPath)
446
461
 
447
462
  def create_dsg_root(self):
448
463
  root_name = "/Root"
@@ -521,99 +536,14 @@ class OmniverseWrapper:
521
536
  UsdGeom.XformOp.TypeTransform, UsdGeom.XformOp.PrecisionDouble
522
537
  )
523
538
  matrixOp.Set(Gf.Matrix4d(*matrix).GetTranspose())
524
- self.log(f"Created group:'{name}' {str(obj_type)}")
539
+ logging.info(f"Created group:'{name}' {str(obj_type)}")
525
540
  return group_prim
526
541
 
527
542
  def uploadMaterial(self):
528
543
  uriPath = self._destinationPath + "/Materials"
529
- omni.client.delete(uriPath)
544
+ shutil.rmtree(uriPath, ignore_errors=True, onerror=None)
530
545
  fullpath = os.path.join(os.path.dirname(__file__), "resources", "Materials")
531
- omni.client.copy(fullpath, uriPath)
532
-
533
- def createMaterial(self, mesh):
534
- # Create a material instance for this in USD
535
- materialName = "Fieldstone"
536
- newMat = UsdShade.Material.Define(self._stage, "/Root/Looks/Fieldstone")
537
-
538
- matPath = "/Root/Looks/Fieldstone"
539
-
540
- # MDL Shader
541
- # Create the MDL shader
542
- mdlShader = UsdShade.Shader.Define(self._stage, matPath + "/Fieldstone")
543
- mdlShader.CreateIdAttr("mdlMaterial")
544
-
545
- mdlShaderModule = "./Materials/Fieldstone.mdl"
546
- mdlShader.SetSourceAsset(mdlShaderModule, "mdl")
547
- # mdlShader.GetPrim().CreateAttribute("info:mdl:sourceAsset:subIdentifier",
548
- # Sdf.ValueTypeNames.Token, True).Set(materialName)
549
- # mdlOutput = newMat.CreateSurfaceOutput("mdl")
550
- # mdlOutput.ConnectToSource(mdlShader, "out")
551
- mdlShader.SetSourceAssetSubIdentifier(materialName, "mdl")
552
- shaderOutput = mdlShader.CreateOutput("out", Sdf.ValueTypeNames.Token)
553
- shaderOutput.SetRenderType("material")
554
- newMat.CreateSurfaceOutput("mdl").ConnectToSource(shaderOutput)
555
- newMat.CreateDisplacementOutput("mdl").ConnectToSource(shaderOutput)
556
- newMat.CreateVolumeOutput("mdl").ConnectToSource(shaderOutput)
557
-
558
- # USD Preview Surface Shaders
559
-
560
- # Create the "USD Primvar reader for float2" shader
561
- primStShader = UsdShade.Shader.Define(self._stage, matPath + "/PrimST")
562
- primStShader.CreateIdAttr("UsdPrimvarReader_float2")
563
- primStShader.CreateOutput("result", Sdf.ValueTypeNames.Float2)
564
- primStShader.CreateInput("varname", Sdf.ValueTypeNames.Token).Set("st")
565
-
566
- # Create the "Diffuse Color Tex" shader
567
- diffuseColorShader = UsdShade.Shader.Define(self._stage, matPath + "/DiffuseColorTex")
568
- diffuseColorShader.CreateIdAttr("UsdUVTexture")
569
- texInput = diffuseColorShader.CreateInput("file", Sdf.ValueTypeNames.Asset)
570
- texInput.Set("./Materials/Fieldstone/Fieldstone_BaseColor.png")
571
- texInput.GetAttr().SetColorSpace("RGB")
572
- diffuseColorShader.CreateInput("st", Sdf.ValueTypeNames.Float2).ConnectToSource(
573
- primStShader.CreateOutput("result", Sdf.ValueTypeNames.Float2)
574
- )
575
- diffuseColorShaderOutput = diffuseColorShader.CreateOutput("rgb", Sdf.ValueTypeNames.Float3)
576
-
577
- # Create the "Normal Tex" shader
578
- normalShader = UsdShade.Shader.Define(self._stage, matPath + "/NormalTex")
579
- normalShader.CreateIdAttr("UsdUVTexture")
580
- normalTexInput = normalShader.CreateInput("file", Sdf.ValueTypeNames.Asset)
581
- normalTexInput.Set("./Materials/Fieldstone/Fieldstone_N.png")
582
- normalTexInput.GetAttr().SetColorSpace("RAW")
583
- normalShader.CreateInput("st", Sdf.ValueTypeNames.Float2).ConnectToSource(
584
- primStShader.CreateOutput("result", Sdf.ValueTypeNames.Float2)
585
- )
586
- normalShaderOutput = normalShader.CreateOutput("rgb", Sdf.ValueTypeNames.Float3)
587
-
588
- # Create the USD Preview Surface shader
589
- usdPreviewSurfaceShader = UsdShade.Shader.Define(self._stage, matPath + "/PreviewSurface")
590
- usdPreviewSurfaceShader.CreateIdAttr("UsdPreviewSurface")
591
- diffuseColorInput = usdPreviewSurfaceShader.CreateInput(
592
- "diffuseColor", Sdf.ValueTypeNames.Color3f
593
- )
594
- diffuseColorInput.ConnectToSource(diffuseColorShaderOutput)
595
- normalInput = usdPreviewSurfaceShader.CreateInput("normal", Sdf.ValueTypeNames.Normal3f)
596
- normalInput.ConnectToSource(normalShaderOutput)
597
-
598
- # Set the linkage between material and USD Preview surface shader
599
- # usdPreviewSurfaceOutput = newMat.CreateSurfaceOutput()
600
- # usdPreviewSurfaceOutput.ConnectToSource(usdPreviewSurfaceShader, "surface")
601
- # UsdShade.MaterialBindingAPI(mesh).Bind(newMat)
602
-
603
- usdPreviewSurfaceShaderOutput = usdPreviewSurfaceShader.CreateOutput(
604
- "surface", Sdf.ValueTypeNames.Token
605
- )
606
- usdPreviewSurfaceShaderOutput.SetRenderType("material")
607
- newMat.CreateSurfaceOutput().ConnectToSource(usdPreviewSurfaceShaderOutput)
608
-
609
- UsdShade.MaterialBindingAPI.Apply(mesh.GetPrim()).Bind(newMat)
610
-
611
- # Create a distant light in the scene.
612
- def createDistantLight(self):
613
- newLight = UsdLux.DistantLight.Define(self._stage, "/Root/DistantLight")
614
- newLight.CreateAngleAttr(0.53)
615
- newLight.CreateColorAttr(Gf.Vec3f(1.0, 1.0, 0.745))
616
- newLight.CreateIntensityAttr(500.0)
546
+ shutil.copytree(fullpath, uriPath)
617
547
 
618
548
  # Create a dome light in the scene.
619
549
  def createDomeLight(self, texturePath):
@@ -627,13 +557,6 @@ class OmniverseWrapper:
627
557
  rotateOp = xForm.AddXformOp(UsdGeom.XformOp.TypeRotateZYX, UsdGeom.XformOp.PrecisionFloat)
628
558
  rotateOp.Set(Gf.Vec3f(270, 0, 0))
629
559
 
630
- def createEmptyFolder(self, emptyFolderPath):
631
- folder = self._destinationPath + emptyFolderPath
632
- self.log(f"Creating new folder: {folder}")
633
- result = omni.client.create_folder(folder)
634
- self.log(f"Finished creating: {result.name}")
635
- return result.name
636
-
637
560
 
638
561
  class OmniverseUpdateHandler(UpdateHandler):
639
562
  """
@@ -670,10 +593,13 @@ class OmniverseUpdateHandler(UpdateHandler):
670
593
  self._group_prims[id] = self._root_prim
671
594
 
672
595
  if self._omni._stage is not None:
673
- self._omni._stage.SetStartTimeCode(self.session.time_limits[0])
674
- self._omni._stage.SetEndTimeCode(self.session.time_limits[1])
675
- self._omni._stage.SetTimeCodesPerSecond(1)
676
- self._omni._stage.SetFramesPerSecond(1)
596
+ self._omni._stage.SetStartTimeCode(
597
+ self.session.time_limits[0] * self._omni._time_codes_per_second
598
+ )
599
+ self._omni._stage.SetEndTimeCode(
600
+ self.session.time_limits[1] * self._omni._time_codes_per_second
601
+ )
602
+ self._omni._stage.SetTimeCodesPerSecond(self._omni._time_codes_per_second)
677
603
 
678
604
  # Send the variable textures. Safe to do so once the first view is processed.
679
605
  if not self._sent_textures:
@@ -685,34 +611,56 @@ class OmniverseUpdateHandler(UpdateHandler):
685
611
 
686
612
  def finalize_part(self, part: Part) -> None:
687
613
  # generate an Omniverse compliant mesh from the Part
688
- command, verts, conn, normals, tcoords, var_cmd = part.nodal_surface_rep()
689
- if command is None:
614
+ if part is None or part.cmd is None:
690
615
  return
691
- parent_prim = self._group_prims[command.parent_id]
616
+ parent_prim = self._group_prims[part.cmd.parent_id]
692
617
  obj_id = self.session.mesh_block_count
693
- matrix = command.matrix4x4
694
- name = command.name
618
+ matrix = part.cmd.matrix4x4
619
+ name = part.cmd.name
695
620
  color = [
696
- command.fill_color[0] * command.diffuse,
697
- command.fill_color[1] * command.diffuse,
698
- command.fill_color[2] * command.diffuse,
699
- command.fill_color[3],
621
+ part.cmd.fill_color[0] * part.cmd.diffuse,
622
+ part.cmd.fill_color[1] * part.cmd.diffuse,
623
+ part.cmd.fill_color[2] * part.cmd.diffuse,
624
+ part.cmd.fill_color[3],
700
625
  ]
701
- # Generate the mesh block
702
- _ = self._omni.create_dsg_mesh_block(
703
- name,
704
- obj_id,
705
- parent_prim,
706
- verts,
707
- conn,
708
- normals,
709
- tcoords,
710
- matrix=matrix,
711
- diffuse=color,
712
- variable=var_cmd,
713
- timeline=self.session.cur_timeline,
714
- first_timestep=(self.session.cur_timeline[0] == self.session.time_limits[0]),
715
- )
626
+
627
+ if part.cmd.render == part.cmd.CONNECTIVITY:
628
+ command, verts, conn, normals, tcoords, var_cmd = part.nodal_surface_rep()
629
+ if command is not None:
630
+ # Generate the mesh block
631
+ _ = self._omni.create_dsg_mesh_block(
632
+ name,
633
+ obj_id,
634
+ part.hash,
635
+ parent_prim,
636
+ verts,
637
+ conn,
638
+ normals,
639
+ tcoords,
640
+ matrix=matrix,
641
+ diffuse=color,
642
+ variable=var_cmd,
643
+ timeline=self.session.cur_timeline,
644
+ first_timestep=(self.session.cur_timeline[0] == self.session.time_limits[0]),
645
+ )
646
+
647
+ elif part.cmd.render == part.cmd.NODES:
648
+ command, verts, sizes, colors, var_cmd = part.point_rep()
649
+ if command is not None:
650
+ _ = self._omni.create_dsg_points(
651
+ name,
652
+ obj_id,
653
+ part.hash,
654
+ parent_prim,
655
+ verts,
656
+ sizes,
657
+ colors,
658
+ matrix=matrix,
659
+ default_size=part.cmd.node_size_default,
660
+ default_color=color,
661
+ timeline=self.session.cur_timeline,
662
+ first_timestep=(self.session.cur_timeline[0] == self.session.time_limits[0]),
663
+ )
716
664
  super().finalize_part(part)
717
665
 
718
666
  def start_connection(self) -> None:
@@ -740,141 +688,3 @@ class OmniverseUpdateHandler(UpdateHandler):
740
688
  super().end_update()
741
689
  # Stage update complete
742
690
  self._omni.save_stage()
743
-
744
-
745
- if __name__ == "__main__":
746
- parser = argparse.ArgumentParser(
747
- description="Python Omniverse EnSight Dynamic Scene Graph Client",
748
- formatter_class=argparse.ArgumentDefaultsHelpFormatter,
749
- )
750
- parser.add_argument(
751
- "--path",
752
- action="store",
753
- default="omniverse://localhost/Users/test",
754
- help="Omniverse pathname. Default=omniverse://localhost/Users/test",
755
- )
756
- parser.add_argument(
757
- "--port",
758
- metavar="ensight_grpc_port",
759
- nargs="?",
760
- default=12345,
761
- type=int,
762
- help="EnSight gRPC port number",
763
- )
764
- parser.add_argument(
765
- "--host",
766
- metavar="ensight_grpc_host",
767
- nargs="?",
768
- default="127.0.0.1",
769
- type=str,
770
- help="EnSight gRPC hostname",
771
- )
772
- parser.add_argument(
773
- "--security",
774
- metavar="ensight_grpc_security_code",
775
- nargs="?",
776
- default="",
777
- type=str,
778
- help="EnSight gRPC security code",
779
- )
780
- parser.add_argument(
781
- "--verbose",
782
- metavar="verbose_level",
783
- default=0,
784
- type=int,
785
- help="Enable debugging information",
786
- )
787
- parser.add_argument(
788
- "--animation", dest="animation", action="store_true", help="Save all timesteps (default)"
789
- )
790
- parser.add_argument(
791
- "--no-animation",
792
- dest="animation",
793
- action="store_false",
794
- help="Save only the current timestep",
795
- )
796
- parser.set_defaults(animation=False)
797
- parser.add_argument(
798
- "--log_file",
799
- metavar="log_filename",
800
- default="",
801
- type=str,
802
- help="Save program output to the named log file instead of stdout",
803
- )
804
- parser.add_argument(
805
- "--live",
806
- dest="live",
807
- action="store_true",
808
- default=False,
809
- help="Enable continuous operation",
810
- )
811
- parser.add_argument(
812
- "--normalize_geometry",
813
- dest="normalize",
814
- action="store_true",
815
- default=False,
816
- help="Spatially normalize incoming geometry",
817
- )
818
- parser.add_argument(
819
- "--vrmode",
820
- dest="vrmode",
821
- action="store_true",
822
- default=False,
823
- help="In this mode do not include a camera or the case level matrix. Geometry only.",
824
- )
825
- args = parser.parse_args()
826
-
827
- log_args = dict(format="DSG/Omniverse: %(message)s", level=logging.INFO)
828
- if args.log_file:
829
- log_args["filename"] = args.log_file
830
- logging.basicConfig(**log_args) # type: ignore
831
-
832
- destinationPath = args.path
833
- loggingEnabled = args.verbose
834
-
835
- # Build the OmniVerse connection
836
- target = OmniverseWrapper(path=destinationPath, verbose=loggingEnabled, live_edit=args.live)
837
- # Print the username for the server
838
- target.username()
839
-
840
- if loggingEnabled:
841
- logging.info("Omniverse connection established.")
842
-
843
- # link it to a DSG session
844
- update_handler = OmniverseUpdateHandler(target)
845
- dsg_link = DSGSession(
846
- port=args.port,
847
- host=args.host,
848
- vrmode=args.vrmode,
849
- security_code=args.security,
850
- verbose=loggingEnabled,
851
- normalize_geometry=args.normalize,
852
- handler=update_handler,
853
- )
854
-
855
- if loggingEnabled:
856
- dsg_link.log(f"Making DSG connection to: {args.host}:{args.port}")
857
-
858
- # Start the DSG link
859
- err = dsg_link.start()
860
- if err < 0:
861
- sys.exit(err)
862
-
863
- # Simple pull request
864
- dsg_link.request_an_update(animation=args.animation)
865
- # Handle the update block
866
- dsg_link.handle_one_update()
867
-
868
- # Live operation
869
- if args.live:
870
- if loggingEnabled:
871
- dsg_link.log("Waiting for remote push operations")
872
- while not dsg_link.is_shutdown():
873
- dsg_link.handle_one_update()
874
-
875
- # Done...
876
- if loggingEnabled:
877
- dsg_link.log("Shutting down DSG connection")
878
- dsg_link.end()
879
-
880
- target.shutdown()