Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1444 from asnt/mesh-texture
Add TextureFilter for adding textures to MeshVisuals
- Loading branch information
Showing
5 changed files
with
211 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import argparse | ||
|
||
import numpy as np | ||
from vispy import app, scene | ||
from vispy.io import imread, load_data_file, read_mesh | ||
from vispy.scene.visuals import Mesh | ||
from vispy.visuals.filters import TextureFilter | ||
|
||
|
||
parser = argparse.ArgumentParser() | ||
parser.add_argument('--shading', default='smooth', | ||
choices=['none', 'flat', 'smooth'], | ||
help="shading mode") | ||
args = parser.parse_args() | ||
|
||
mesh_path = load_data_file('spot/spot.obj.gz') | ||
texture_path = load_data_file('spot/spot.png') | ||
vertices, faces, normals, texcoords = read_mesh(mesh_path) | ||
texture = np.flipud(imread(texture_path)) | ||
|
||
canvas = scene.SceneCanvas(keys='interactive', bgcolor='white', | ||
size=(800, 600)) | ||
view = canvas.central_widget.add_view() | ||
|
||
view.camera = 'arcball' | ||
# Adapt the depth to the scale of the mesh to avoid rendering artefacts. | ||
view.camera.depth_value = 10 * (vertices.max() - vertices.min()) | ||
|
||
shading = None if args.shading == 'none' else args.shading | ||
mesh = Mesh(vertices, faces, shading=shading, color='white') | ||
mesh.shininess = 1e-3 | ||
view.add(mesh) | ||
|
||
texture_filter = TextureFilter(texture, texcoords) | ||
mesh.attach(texture_filter) | ||
|
||
|
||
@canvas.events.key_press.connect | ||
def on_key_press(event): | ||
if event.key == "t": | ||
texture_filter.enabled = not texture_filter.enabled | ||
mesh.update() | ||
|
||
|
||
def attach_headlight(mesh, view, canvas): | ||
light_dir = (0, -1, 0, 0) | ||
mesh.light_dir = light_dir[:3] | ||
initial_light_dir = view.camera.transform.imap(light_dir) | ||
|
||
@view.scene.transform.changed.connect | ||
def on_transform_change(event): | ||
transform = view.camera.transform | ||
mesh.light_dir = transform.map(initial_light_dir)[:3] | ||
|
||
|
||
attach_headlight(mesh, view, canvas) | ||
|
||
|
||
canvas.show() | ||
|
||
if __name__ == "__main__": | ||
app.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
# -*- coding: utf-8 -*- | ||
# Copyright (c) Vispy Development Team. All Rights Reserved. | ||
# Distributed under the (new) BSD License. See LICENSE.txt for more info. | ||
import numpy as np | ||
from vispy.gloo import Texture2D, VertexBuffer | ||
from vispy.visuals.shaders import Function, Varying | ||
from vispy.visuals.filters import Filter | ||
|
||
|
||
class TextureFilter(Filter): | ||
"""Filter to apply a texture to a mesh. | ||
Note the texture is applied by multiplying the texture color by the | ||
Visual's produced color. By specifying `color="white"` when creating | ||
a `MeshVisual` the result will be the unaltered texture value. Any | ||
other color, including the default, will result in a blending of that | ||
color and the color of the texture. | ||
""" | ||
|
||
def __init__(self, texture, texcoords, enabled=True): | ||
"""Apply a texture on a mesh. | ||
Parameters | ||
---------- | ||
texture : (M, N) or (M, N, C) array | ||
The 2D texture image. | ||
texcoords : (N, 2) array | ||
The texture coordinates. | ||
enabled : bool | ||
Whether the display of the texture is enabled. | ||
""" | ||
vfunc = Function(""" | ||
void pass_coords() { | ||
$v_texcoords = $texcoords; | ||
} | ||
""") | ||
ffunc = Function(""" | ||
void apply_texture() { | ||
if ($enabled == 1) { | ||
gl_FragColor *= texture2D($u_texture, $texcoords); | ||
} | ||
} | ||
""") | ||
self._texcoord_varying = Varying('v_texcoord', 'vec2') | ||
vfunc['v_texcoords'] = self._texcoord_varying | ||
ffunc['texcoords'] = self._texcoord_varying | ||
self._texcoords_buffer = VertexBuffer( | ||
np.zeros((0, 2), dtype=np.float32) | ||
) | ||
vfunc['texcoords'] = self._texcoords_buffer | ||
super().__init__(vcode=vfunc, vhook='pre', fcode=ffunc) | ||
|
||
self.enabled = enabled | ||
self.texture = texture | ||
self.texcoords = texcoords | ||
|
||
@property | ||
def enabled(self): | ||
"""True to display the texture, False to disable.""" | ||
return self._enabled | ||
|
||
@enabled.setter | ||
def enabled(self, enabled): | ||
self._enabled = enabled | ||
self.fshader['enabled'] = 1 if enabled else 0 | ||
|
||
@property | ||
def texture(self): | ||
"""The texture image.""" | ||
return self._texture | ||
|
||
@texture.setter | ||
def texture(self, texture): | ||
self._texture = texture | ||
self.fshader['u_texture'] = Texture2D(texture) | ||
|
||
@property | ||
def texcoords(self): | ||
"""The texture coordinates as an (N, 2) array of floats.""" | ||
return self._texcoords | ||
|
||
@texcoords.setter | ||
def texcoords(self, texcoords): | ||
self._texcoords = texcoords | ||
self._update_texcoords_buffer(texcoords) | ||
|
||
def _update_texcoords_buffer(self, texcoords): | ||
if not self._attached or self._visual is None: | ||
return | ||
|
||
# FIXME: Indices for texture coordinates might be different than face | ||
# indices, although in some cases they are the same. Currently, | ||
# vispy.io.read_mesh assumes face indices and texture coordinates are | ||
# the same. | ||
# TODO: | ||
# 1. Add reading and returning of texture coordinate indices in | ||
# read_mesh. | ||
# 2. Add texture coordinate indices in MeshData from | ||
# vispy.geometry.meshdata | ||
# 3. Use mesh_data.get_texcoords_indices() here below. | ||
tc = texcoords[self._visual.mesh_data.get_faces()] | ||
self._texcoords_buffer.set_data(tc, convert=True) | ||
|
||
def _attach(self, visual): | ||
super()._attach(visual) | ||
self._update_texcoords_buffer(self._texcoords) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters