def __init__(self, uv, tex, normalizeUV = False, powerOfTwo = False): self.uv = uv.copy() # Make the parametric domain stretch from (0, 0) to (1, 1) if (normalizeUV): self.uv -= np.min(self.uv, axis=0) dim = np.max(self.uv, axis=0) self.uv /= dim h, w = tex.shape[0:2] s = max(w, h) if (powerOfTwo): s = int(np.exp2(np.ceil(np.log2(s)))) padded = np.pad(tex, [(s - h, 0), (0, s - w), (0, 0)], 'constant', constant_values=128) # pad top, right self.dataTex = pythreejs.DataTexture(data=padded, format='RGBAFormat', type='UnsignedByteType') self.dataTex.wrapS = 'ClampToEdgeWrapping' self.dataTex.magFilter = 'LinearFilter' self.dataTex.minFilter = 'LinearMipMapLinearFilter' self.dataTex.generateMipmaps = True self.dataTex.flipY = True self.uv *= np.array([float(w) / s, float(h) / s])
def to_surf_mesh(actor, surf, mapper, prop, add_attr={}): """Convert a pyvista surface to a buffer geometry. General Notes ------------- * THREE.BufferGeometry expects position and index attributes representing a triangulated mesh points and face indices or just a position array representing individual faces of a mesh. * The normals attribute is needed for physically based rendering, but not for the other mesh types. * Colors must be a RGB array with one value per point. Shading Notes ------------- To match VTK, the following materials are used to match VTK's shading: * MeshPhysicalMaterial when physically based rendering is enabled * MeshPhongMaterial when physically based rendering is disabled, but lighting is enabled. * MeshBasicMaterial when lighting is disabled. """ # convert to an all-triangular surface if surf.is_all_triangles(): trimesh = surf else: trimesh = surf.triangulate() position = array_to_float_buffer(trimesh.points) # convert to minimum index type face_ind = trimesh.faces.reshape(-1, 4)[:, 1:] index = cast_to_min_size(face_ind, trimesh.n_points) attr = { 'position': position, 'index': index, } if prop.GetInterpolation(): # something other than flat shading attr['normal'] = buffer_normals(trimesh) # extract point/cell scalars for coloring colors = None scalar_mode = mapper.GetScalarModeAsString() if scalar_mode == 'UsePointData': colors = map_scalars(mapper, trimesh.point_data.active_scalars) elif scalar_mode == 'UseCellData': # special handling for RGBA if mapper.GetColorMode() == 2: scalars = trimesh.cell_data.active_scalars.repeat(3, axis=0) scalars = scalars.astype(np.float32, copy=False) colors = scalars[:, :3] / 255 # ignore alpha else: # must repeat for each triangle scalars = trimesh.cell_data.active_scalars.repeat(3) colors = map_scalars(mapper, scalars) position = array_to_float_buffer(trimesh.points[face_ind]) attr = {'position': position} # add colors to the buffer geometry attributes if colors is not None: attr['color'] = array_to_float_buffer(colors) # texture coordinates t_coords = trimesh.active_t_coords if t_coords is not None: attr['uv'] = array_to_float_buffer(t_coords) # TODO: Convert PBR textures # base_color_texture = prop.GetTexture("albedoTex") # orm_texture = prop.GetTexture("materialTex") # anisotropy_texture = prop.GetTexture("anisotropyTex") # normal_texture = prop.GetTexture("normalTex") # emissive_texture = prop.GetTexture("emissiveTex") # coatnormal_texture = prop.GetTexture("coatNormalTex") if prop.GetNumberOfTextures(): # pragma: no cover warnings.warn( 'pythreejs converter does not support PBR textures (yet).') # create base buffer geometry surf_geo = tjs.BufferGeometry(attributes=attr) # add texture to the surface buffer if available texture = actor.GetTexture() tjs_texture = None if texture is not None: wrapped_tex = pv.wrap(texture.GetInput()) data = wrapped_tex.active_scalars dim = (wrapped_tex.dimensions[0], wrapped_tex.dimensions[1], data.shape[1]) data = data.reshape(dim) fmt = "RGBFormat" if data.shape[1] == 3 else "RGBAFormat" # Create data texture and catch invalid warning with warnings.catch_warnings(): warnings.filterwarnings("ignore", message="Given trait value dtype") tjs_texture = tjs.DataTexture(data=data, format="RGBFormat", type="UnsignedByteType") # these attributes are always used regardless of the material shared_attr = { 'vertexColors': get_coloring(mapper, trimesh), 'wireframe': prop.GetRepresentation() == 1, 'opacity': prop.GetOpacity(), 'wireframeLinewidth': prop.GetLineWidth(), # 'side': 'DoubleSide' # enabling seems to mess with textures } if colors is None: shared_attr['color'] = color_to_hex(prop.GetColor()) if tjs_texture is not None: shared_attr['map'] = tjs_texture else: shared_attr['side'] = 'DoubleSide' if prop.GetOpacity() < 1.0: shared_attr['transparent'] = True if prop.GetInterpolation() == 3: # using physically based rendering material = tjs.MeshPhysicalMaterial(flatShading=False, roughness=prop.GetRoughness(), metalness=prop.GetMetallic(), reflectivity=0, **shared_attr, **add_attr) elif prop.GetLighting(): # specular disabled to fix lighting issues material = tjs.MeshPhongMaterial( shininess=0, flatShading=prop.GetInterpolation() == 0, specular=color_to_hex((0, 0, 0)), reflectivity=0, **shared_attr, **add_attr) else: # no lighting material = tjs.MeshBasicMaterial(**shared_attr, **add_attr) return tjs.Mesh(geometry=surf_geo, material=material)
def add_mesh(self, v, f, c=None, uv=None, shading={}, texture_data=None): sh = self.__get_shading(shading) mesh_obj = {} #it is a tet if v.shape[1] == 3 and f.shape[1] == 4: f_tmp = np.ndarray([f.shape[0]*4, 3], dtype=f.dtype) for i in range(f.shape[0]): f_tmp[i*4+0] = np.array([f[i][1], f[i][0], f[i][2]]) f_tmp[i*4+1] = np.array([f[i][0], f[i][1], f[i][3]]) f_tmp[i*4+2] = np.array([f[i][1], f[i][2], f[i][3]]) f_tmp[i*4+3] = np.array([f[i][2], f[i][0], f[i][3]]) f = f_tmp if v.shape[1] == 2: v = np.append(v, np.zeros([v.shape[0], 1]), 1) # Type adjustment vertices v = v.astype("float32", copy=False) # Color setup colors, coloring = self.__get_colors(v, f, c, sh) # Type adjustment faces and colors c = colors.astype("float32", copy=False) # Material and geometry setup ba_dict = {"color": p3s.BufferAttribute(c)} if coloring == "FaceColors": verts = np.zeros((f.shape[0]*3, 3), dtype="float32") for ii in range(f.shape[0]): #print(ii*3, f[ii]) verts[ii*3] = v[f[ii,0]] verts[ii*3+1] = v[f[ii,1]] verts[ii*3+2] = v[f[ii,2]] v = verts else: f = f.astype("uint32", copy=False).ravel() ba_dict["index"] = p3s.BufferAttribute(f, normalized=False) ba_dict["position"] = p3s.BufferAttribute(v, normalized=False) if type(uv) != type(None): uv = (uv - np.min(uv)) / (np.max(uv) - np.min(uv)) if texture_data is None: texture_data = gen_checkers(20, 20) tex = p3s.DataTexture(data=texture_data, format="RGBFormat", type="FloatType") material = p3s.MeshStandardMaterial(map=tex, reflectivity=sh["reflectivity"], side=sh["side"], roughness=sh["roughness"], metalness=sh["metalness"], flatShading=sh["flat"], polygonOffset=True, polygonOffsetFactor= 1, polygonOffsetUnits=5) ba_dict["uv"] = p3s.BufferAttribute(uv.astype("float32", copy=False)) else: material = p3s.MeshStandardMaterial(vertexColors=coloring, reflectivity=sh["reflectivity"], side=sh["side"], roughness=sh["roughness"], metalness=sh["metalness"], flatShading=sh["flat"], polygonOffset=True, polygonOffsetFactor= 1, polygonOffsetUnits=5) geometry = p3s.BufferGeometry(attributes=ba_dict) if coloring == "VertexColors": geometry.exec_three_obj_method('computeVertexNormals') else: geometry.exec_three_obj_method('computeFaceNormals') # Mesh setup mesh = p3s.Mesh(geometry=geometry, material=material) # Wireframe setup mesh_obj["wireframe"] = None if sh["wireframe"]: wf_geometry = p3s.WireframeGeometry(mesh.geometry) # WireframeGeometry wf_material = p3s.LineBasicMaterial(color=sh["wire_color"], linewidth=sh["wire_width"]) wireframe = p3s.LineSegments(wf_geometry, wf_material) mesh.add(wireframe) mesh_obj["wireframe"] = wireframe # Bounding box setup if sh["bbox"]: v_box, f_box = self.__get_bbox(v) _, bbox = self.add_edges(v_box, f_box, sh, mesh) mesh_obj["bbox"] = [bbox, v_box, f_box] # Object setup mesh_obj["max"] = np.max(v, axis=0) mesh_obj["min"] = np.min(v, axis=0) mesh_obj["geometry"] = geometry mesh_obj["mesh"] = mesh mesh_obj["material"] = material mesh_obj["type"] = "Mesh" mesh_obj["shading"] = sh mesh_obj["coloring"] = coloring mesh_obj["arrays"] = [v, f, c] # TODO replays with proper storage or remove if not needed return self.__add_object(mesh_obj)
def __init__(self, U, grid, render_size, color_map, title, bounding_box=([0, 0], [1, 1]), codim=2, vmin=None, vmax=None): assert grid.reference_element in (triangle, square) assert grid.dim == 2 assert codim in (0, 2) self.layout = Layout(min_width=str(render_size[0]), min_height=str(render_size[1]), margin='0px 0px 0px 20px ') self.grid = grid self.codim = codim self.vmin, self.vmax = vmin, vmax subentities, coordinates, self.entity_map = flatten_grid(grid) if grid.reference_element == triangle: if codim == 2: vertices = np.zeros((len(coordinates), 3)) vertices[:, :-1] = coordinates indices = subentities else: vertices = np.zeros((len(subentities) * 3, 3)) VERTEX_POS = coordinates[subentities] vertices[:, 0:2] = VERTEX_POS.reshape((-1, 2)) indices = np.arange(len(subentities) * 3, dtype=np.uint32) else: if codim == 2: vertices = np.zeros((len(coordinates), 3)) vertices[:, :-1] = coordinates indices = np.vstack( (subentities[:, 0:3], subentities[:, [0, 2, 3]])) else: num_entities = len(subentities) vertices = np.zeros((num_entities * 6, 3)) VERTEX_POS = coordinates[subentities] vertices[0:num_entities * 3, 0:2] = VERTEX_POS[:, 0:3, :].reshape((-1, 2)) vertices[num_entities * 3:, 0:2] = VERTEX_POS[:, [0, 2, 3], :].reshape((-1, 2)) indices = np.arange(len(subentities) * 6, dtype=np.uint32) max_tex_size = 512 cm = color_map(np.linspace(0, 1, max_tex_size)).astype(np.float32) cm.resize((max_tex_size, 1, 4)) color_map = p3js.DataTexture(cm, format='RGBAFormat', width=max_tex_size, height=1, type='FloatType') uniforms = dict(colormap={'value': color_map, 'type': 'sampler2D'}, ) self.material = p3js.ShaderMaterial( vertexShader=RENDER_VERTEX_SHADER, fragmentShader=RENDER_FRAGMENT_SHADER, uniforms=uniforms, ) self.buffer_vertices = p3js.BufferAttribute(vertices.astype( np.float32), normalized=False) self.buffer_faces = p3js.BufferAttribute(indices.astype( np.uint32).ravel(), normalized=False) self.meshes = [] self._setup_scene(bounding_box, render_size) if config.is_nbconvert(): # need to ensure all data is loaded before cell execution is over class LoadDummy: def done(self): return True self._load_data(U) self.load = LoadDummy() else: self.load = asyncio.ensure_future(self._async_load_data(U)) self._last_idx = None super().__init__(children=[ self.renderer, ])