Skip to content

Commit 99eb5bb

Browse files
authored
Add simApplyTexture & simGetShapeViz (#104)
* Add pyrep.backend.sim.simApplyTexture * Add Shape.apply_texture * Add sim.simGetShapeViz and Shape.get_shape_viz * Add test_apply_texture
1 parent 03c2423 commit 99eb5bb

File tree

4 files changed

+187
-0
lines changed

4 files changed

+187
-0
lines changed

pyrep/backend/sim.py

+62
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
11
from .simConst import *
22
from ._sim_cffi import ffi, lib
33
import numpy as np
4+
import collections
5+
6+
7+
SShapeVizInfo = collections.namedtuple(
8+
'SShapeVizInfo',
9+
[
10+
'vertices',
11+
'indices',
12+
'normals',
13+
'shadingAngle',
14+
'colors',
15+
'texture',
16+
'textureId',
17+
'textureRes',
18+
'textureCoords',
19+
'textureApplyMode',
20+
'textureOptions',
21+
],
22+
)
423

524

625
def _check_return(ret):
@@ -881,6 +900,41 @@ def simGetShapeMesh(shapeHandle):
881900
return retVerticies, retIndices, outNormals
882901

883902

903+
def simGetShapeViz(shapeHandle, index):
904+
info = ffi.new('struct SShapeVizInfo *')
905+
ret = lib.simGetShapeViz(shapeHandle, index, info)
906+
_check_return(ret)
907+
908+
vertices = [info.vertices[i] for i in range(info.verticesSize)]
909+
indices = [info.indices[i] for i in range(info.indicesSize)]
910+
normals = [info.normals[i] for i in range(info.indicesSize * 3)]
911+
colors = list(info.colors)
912+
textureSize = info.textureRes[0] * info.textureRes[1] * 4
913+
if textureSize == 0:
914+
texture = []
915+
textureCoords = []
916+
else:
917+
texture = np.frombuffer(
918+
ffi.buffer(info.texture, textureSize), np.uint8)
919+
texture = texture.tolist()
920+
textureCoords = [info.textureCoords[i] for i in
921+
range(info.indicesSize * 2)]
922+
923+
return SShapeVizInfo(
924+
vertices=vertices,
925+
indices=indices,
926+
normals=normals,
927+
shadingAngle=info.shadingAngle,
928+
colors=colors,
929+
texture=texture,
930+
textureId=info.textureId,
931+
textureRes=info.textureRes,
932+
textureCoords=textureCoords,
933+
textureApplyMode=info.textureApplyMode,
934+
textureOptions=info.textureOptions,
935+
)
936+
937+
884938
def simConvexDecompose(shapeHandle, options, intParams, floatParams):
885939
return lib.simConvexDecompose(shapeHandle, options, intParams, floatParams)
886940

@@ -931,6 +985,14 @@ def simGetScriptAssociatedWithObject(objectHandle):
931985
return ret
932986

933987

988+
def simApplyTexture(shapeHandle, textureCoordinates, textCoordSize,
989+
texture, textureResolution, options):
990+
ret = lib.simApplyTexture(shapeHandle, textureCoordinates, textCoordSize,
991+
texture, textureResolution, options)
992+
_check_return(ret)
993+
return ret
994+
995+
934996
def simCreateTexture(fileName, options):
935997
# The textureID param that is returned from simCreateTexture seems
936998
# to be incorrect (in regards to calling simGetShapeTextureId on the

pyrep/objects/shape.py

+97
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
11
from typing import List, Tuple
2+
import numpy as np
23
from pyrep.backend import sim
34
from pyrep.objects.object import Object, object_type_to_class
45
from pyrep.const import ObjectType, PrimitiveShape, TextureMappingMode
56
from pyrep.textures.texture import Texture
67
import os
8+
import collections
9+
10+
11+
SShapeVizInfo = collections.namedtuple(
12+
'SShapeVizInfo',
13+
[
14+
'vertices',
15+
'indices',
16+
'normals',
17+
'shading_angle',
18+
'colors',
19+
'texture',
20+
'texture_id',
21+
'texture_coords',
22+
'texture_apply_mode',
23+
'texture_options',
24+
],
25+
)
726

827

928
class Shape(Object):
@@ -357,5 +376,83 @@ def ungroup(self) -> List['Shape']:
357376
handles = sim.simUngroupShape(self.get_handle())
358377
return [Shape(handle) for handle in handles]
359378

379+
def apply_texture(self, texture_coords: np.ndarray, texture: np.ndarray,
380+
interpolate: bool = True, decal_mode: bool = False,
381+
is_rgba: bool = False, fliph: bool = False,
382+
flipv: bool = False) -> None:
383+
"""Apply texture to the shape.
384+
385+
:param texture_coords: A list of (u, v) values that indicate the
386+
vertex position on the shape. For each of the shape's triangle,
387+
there should be exactly 3 UV texture coordinate pairs
388+
:param texture: The RGB or RGBA texture.
389+
:param interpolate: A flag to interpolate adjacent texture pixels.
390+
:param decal_mode: Texture is applied as a decal (its appearance
391+
won't be influenced by light conditions).
392+
:param is_rgba: A flag to use RGBA texture.
393+
:param fliph: A flag to flip texture horizontally.
394+
:param flipv: A flag to flip texture vertically. Note that CoppeliaSim
395+
texture coordinates are flipped vertically compared with Pillow
396+
and OpenCV and this flag must be true in general.
397+
"""
398+
texture_coords = np.asarray(texture_coords)
399+
if not isinstance(texture, np.ndarray):
400+
raise TypeError('texture must be np.ndarray type')
401+
height, width = texture.shape[:2]
402+
403+
options = 0
404+
if not interpolate:
405+
options |= 1
406+
if decal_mode:
407+
options |= 2
408+
if is_rgba:
409+
options |= 16
410+
if fliph:
411+
options |= 32
412+
if flipv:
413+
options |= 64
414+
415+
sim.simApplyTexture(
416+
self._handle,
417+
textureCoordinates=texture_coords.flatten().tolist(),
418+
textCoordSize=texture_coords.size,
419+
texture=texture.flatten().tolist(),
420+
textureResolution=(width, height),
421+
options=options,
422+
)
423+
424+
def get_shape_viz(self, index):
425+
"""Retrieves a shape's visual information.
426+
427+
:param index: 0-based index of the shape element to retrieve
428+
(compound shapes contain more than one shape element)
429+
430+
:return: SShapeVizInfo.
431+
"""
432+
info = sim.simGetShapeViz(shapeHandle=self._handle, index=index)
433+
434+
vertices = np.array(info.vertices, dtype=float).reshape(-1, 3)
435+
indices = np.array(info.indices, dtype=float).reshape(-1, 3)
436+
normals = np.array(info.normals, dtype=float).reshape(-1, 3)
437+
colors = np.array(info.colors, dtype=float)
438+
texture = np.array(info.texture, dtype=np.uint8).reshape(
439+
info.textureRes[1], info.textureRes[0], 4)
440+
textureCoords = np.array(info.textureCoords, dtype=float).reshape(
441+
-1, 2)
442+
443+
res = SShapeVizInfo(
444+
vertices=vertices,
445+
indices=indices,
446+
normals=normals,
447+
shading_angle=info.shadingAngle,
448+
colors=colors,
449+
texture=texture,
450+
texture_id=info.textureId,
451+
texture_coords=textureCoords,
452+
texture_apply_mode=info.textureApplyMode,
453+
texture_options=info.textureOptions,
454+
)
455+
return res
456+
360457

361458
object_type_to_class[ObjectType.SHAPE] = Shape

tests/assets/test_scene.ttt

1.29 MB
Binary file not shown.

tests/test_shapes.py

+28
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,34 @@ def test_set_texture(self):
8585
self.assertEqual(texture.get_texture_id(),
8686
self.dynamic_cube.get_texture().get_texture_id())
8787

88+
def test_get_shape_viz(self):
89+
visual = Shape('cracker_box_visual')
90+
info = visual.get_shape_viz(index=0)
91+
self.assertIsInstance(info.vertices, np.ndarray)
92+
self.assertEqual(info.vertices.shape[1], 3)
93+
self.assertIsInstance(info.indices, np.ndarray)
94+
self.assertEqual(info.indices.shape[1], 3)
95+
self.assertIsInstance(info.normals, np.ndarray)
96+
self.assertEqual(info.normals.shape[1], 3)
97+
self.assertIsInstance(info.shading_angle, float)
98+
self.assertIsInstance(info.colors, np.ndarray)
99+
self.assertTupleEqual(info.colors.shape, (9,))
100+
self.assertIsInstance(info.texture, np.ndarray)
101+
self.assertTupleEqual(info.texture.shape, (512, 512, 4))
102+
self.assertIsInstance(info.texture_id, int)
103+
self.assertIsInstance(info.texture_coords, np.ndarray)
104+
self.assertEqual(info.texture_coords.shape[1], 2)
105+
self.assertIsInstance(info.texture_apply_mode, int)
106+
self.assertIsInstance(info.texture_options, int)
107+
108+
def test_apply_texture(self):
109+
visual = Shape('cracker_box_visual')
110+
info = visual.get_shape_viz(index=0)
111+
self.assertNotEqual(info.texture_coords.size, 0)
112+
self.assertNotEqual(info.texture.size, 0)
113+
texture = info.texture[:, :, [2, 1, 0, 3]] # rgba -> bgra
114+
visual.apply_texture(info.texture_coords, texture, is_rgba=True)
115+
88116

89117
if __name__ == '__main__':
90118
unittest.main()

0 commit comments

Comments
 (0)