Difficult-Rocket/libs/pyglet/model/__init__.py

322 lines
9.1 KiB
Python
Raw Normal View History

2021-04-16 23:21:06 +08:00
"""Loading of 3D models.
A :py:class:`~pyglet.model.Model` is an instance of a 3D object.
The following example loads a ``"teapot.obj"`` model::
import pyglet
window = pyglet.window.Window()
teapot = pyglet.model.load('teapot.obj')
@window.event
def on_draw():
teapot.draw()
pyglet.app.run()
You can also load models with :py:meth:`~pyglet.resource.model`.
See :py:mod:`~pyglet.resource` for more information.
Efficient Drawing
=================
As with Sprites or Text, Models can be added to a
:py:class:`~pyglet.graphics.Batch` for efficient drawing. This is
preferred to calling their ``draw`` methods individually. To do this,
simply pass in a reference to the :py:class:`~pyglet.graphics.Batch`
instance when loading the Model::
import pyglet
window = pyglet.window.Window()
batch = pyglet.graphics.Batch()
teapot = pyglet.model.load('teapot.obj', batch=batch)
@window.event
def on_draw():
batch.draw()
pyglet.app.run()
.. versionadded:: 1.4
"""
2022-03-22 23:20:07 +08:00
import pyglet
2021-04-16 23:21:06 +08:00
from pyglet import gl
2021-04-16 23:21:06 +08:00
from pyglet import graphics
from pyglet.gl import current_context
from pyglet.math import Mat4, Vec3
from pyglet.graphics import shader
2021-04-16 23:21:06 +08:00
2022-04-08 23:07:41 +08:00
from .codecs import registry as _codec_registry
from .codecs import add_default_codecs as _add_default_codecs
2021-04-16 23:21:06 +08:00
2022-04-08 23:07:41 +08:00
def load(filename, file=None, decoder=None, batch=None, group=None):
2021-04-16 23:21:06 +08:00
"""Load a 3D model from a file.
:Parameters:
`filename` : str
Used to guess the model format, and to load the file if `file` is
unspecified.
`file` : file-like object or None
Source of model data in any supported format.
`decoder` : ModelDecoder or None
If unspecified, all decoders that are registered for the filename
extension are tried. An exception is raised if no codecs are
registered for the file extension, or if decoding fails.
`batch` : Batch or None
An optional Batch instance to add this model to.
2022-04-08 23:07:41 +08:00
`group` : Group or None
An optional top level Group.
2021-04-16 23:21:06 +08:00
:rtype: :py:mod:`~pyglet.model.Model`
"""
2022-04-08 23:07:41 +08:00
if decoder:
return decoder.decode(filename, file, batch=batch, group=group)
else:
return _codec_registry.decode(filename, file, batch=batch, group=group)
2021-04-16 23:21:06 +08:00
def get_default_shader():
2023-09-24 23:57:26 +08:00
return pyglet.gl.current_context.create_program((MaterialGroup.default_vert_src, 'vertex'),
(MaterialGroup.default_frag_src, 'fragment'))
def get_default_textured_shader():
2023-09-24 23:57:26 +08:00
return pyglet.gl.current_context.create_program((TexturedMaterialGroup.default_vert_src, 'vertex'),
(TexturedMaterialGroup.default_frag_src, 'fragment'))
2021-04-16 23:21:06 +08:00
class Model:
"""Instance of a 3D object.
See the module documentation for usage.
"""
def __init__(self, vertex_lists, groups, batch):
"""Create a model.
:Parameters:
`vertex_lists` : list
A list of `~pyglet.graphics.VertexList` or
`~pyglet.graphics.IndexedVertexList`.
`groups` : list
A list of `~pyglet.model.TexturedMaterialGroup`, or
`~pyglet.model.MaterialGroup`. Each group corresponds to
a vertex list in `vertex_lists` of the same index.
`batch` : `~pyglet.graphics.Batch`
Optional batch to add the model to. If no batch is provided,
2021-12-26 23:06:03 +08:00
the model will maintain its own internal batch.
2021-04-16 23:21:06 +08:00
"""
self.vertex_lists = vertex_lists
self.groups = groups
2021-04-16 23:21:06 +08:00
self._batch = batch
2022-03-22 23:20:07 +08:00
self._modelview_matrix = Mat4()
2021-04-16 23:21:06 +08:00
@property
def batch(self):
"""The graphics Batch that the Model belongs to.
The Model can be migrated from one batch to another, or removed from
a batch (for individual drawing). If not part of any batch, the Model
2021-12-26 23:06:03 +08:00
will keep its own internal batch. Note that batch migration can be
2021-04-16 23:21:06 +08:00
an expensive operation.
:type: :py:class:`pyglet.graphics.Batch`
"""
return self._batch
@batch.setter
def batch(self, batch):
if self._batch == batch:
return
if batch is None:
batch = graphics.Batch()
2021-04-16 23:21:06 +08:00
for group, vlist in zip(self.groups, self.vertex_lists):
self._batch.migrate(vlist, gl.GL_TRIANGLES, group, batch)
2021-04-16 23:21:06 +08:00
self._batch = batch
@property
2022-03-22 23:20:07 +08:00
def matrix(self):
return self._modelview_matrix
2021-04-16 23:21:06 +08:00
2022-03-22 23:20:07 +08:00
@matrix.setter
def matrix(self, matrix):
self._modelview_matrix = matrix
2021-04-16 23:21:06 +08:00
for group in self.groups:
2022-03-22 23:20:07 +08:00
group.matrix = matrix
2021-04-16 23:21:06 +08:00
def draw(self):
"""Draw the model.
This is not recommended. See the module documentation
for information on efficient drawing of multiple models.
"""
gl.current_context.window_block.bind(0)
2021-04-16 23:21:06 +08:00
self._batch.draw_subset(self.vertex_lists)
class Material:
__slots__ = ("name", "diffuse", "ambient", "specular", "emission", "shininess", "texture_name")
def __init__(self, name, diffuse, ambient, specular, emission, shininess, texture_name=None):
self.name = name
self.diffuse = diffuse
self.ambient = ambient
self.specular = specular
self.emission = emission
2021-04-16 23:21:06 +08:00
self.shininess = shininess
self.texture_name = texture_name
def __eq__(self, other):
return (self.name == other.name and
self.diffuse == other.diffuse and
self.ambient == other.ambient and
self.specular == other.specular and
self.emission == other.emission and
self.shininess == other.shininess and
self.texture_name == other.texture_name)
2021-04-16 23:21:06 +08:00
2022-04-08 23:07:41 +08:00
class BaseMaterialGroup(graphics.Group):
default_vert_src = None
default_frag_src = None
2022-03-22 23:20:07 +08:00
matrix = Mat4()
2021-04-16 23:21:06 +08:00
def __init__(self, material, program, order=0, parent=None):
2022-04-08 23:07:41 +08:00
super().__init__(order, parent)
2021-04-16 23:21:06 +08:00
self.material = material
2022-04-08 23:07:41 +08:00
self.program = program
class TexturedMaterialGroup(BaseMaterialGroup):
default_vert_src = """#version 330 core
2023-03-22 00:08:03 +08:00
in vec3 position;
in vec3 normals;
in vec2 tex_coords;
in vec4 colors;
out vec4 vertex_colors;
out vec3 vertex_normals;
out vec2 texture_coords;
out vec3 vertex_position;
uniform WindowBlock
{
mat4 projection;
mat4 view;
} window;
2022-03-22 23:20:07 +08:00
uniform mat4 model;
void main()
{
2023-03-22 00:08:03 +08:00
vec4 pos = window.view * model * vec4(position, 1.0);
gl_Position = window.projection * pos;
2022-03-22 23:20:07 +08:00
mat3 normal_matrix = transpose(inverse(mat3(model)));
vertex_position = pos.xyz;
vertex_colors = colors;
texture_coords = tex_coords;
vertex_normals = normal_matrix * normals;
}
"""
default_frag_src = """#version 330 core
in vec4 vertex_colors;
in vec3 vertex_normals;
in vec2 texture_coords;
in vec3 vertex_position;
out vec4 final_colors;
uniform sampler2D our_texture;
void main()
{
float l = dot(normalize(-vertex_position), normalize(vertex_normals));
final_colors = (texture(our_texture, texture_coords) * vertex_colors) * l * 1.2;
}
"""
2021-04-16 23:21:06 +08:00
2022-03-22 23:20:07 +08:00
def __init__(self, material, program, texture, order=0, parent=None):
super().__init__(material, program, order, parent)
self.texture = texture
2021-04-16 23:21:06 +08:00
def set_state(self):
gl.glActiveTexture(gl.GL_TEXTURE0)
gl.glBindTexture(self.texture.target, self.texture.id)
self.program.use()
2022-03-22 23:20:07 +08:00
self.program['model'] = self.matrix
2021-04-16 23:21:06 +08:00
def __hash__(self):
return hash((self.texture.target, self.texture.id, self.program, self.order, self.parent))
2021-04-16 23:21:06 +08:00
def __eq__(self, other):
return (self.__class__ is other.__class__ and
self.material == other.material and
self.texture.target == other.texture.target and
self.texture.id == other.texture.id and
self.program == other.program and
self.order == other.order and
self.parent == other.parent)
class MaterialGroup(BaseMaterialGroup):
default_vert_src = """#version 330 core
2023-03-22 00:08:03 +08:00
in vec3 position;
in vec3 normals;
in vec4 colors;
out vec4 vertex_colors;
out vec3 vertex_normals;
out vec3 vertex_position;
uniform WindowBlock
{
mat4 projection;
mat4 view;
} window;
2022-03-22 23:20:07 +08:00
uniform mat4 model;
void main()
{
2023-03-22 00:08:03 +08:00
vec4 pos = window.view * model * vec4(position, 1.0);
gl_Position = window.projection * pos;
2022-03-22 23:20:07 +08:00
mat3 normal_matrix = transpose(inverse(mat3(model)));
vertex_position = pos.xyz;
vertex_colors = colors;
vertex_normals = normal_matrix * normals;
}
"""
default_frag_src = """#version 330 core
in vec4 vertex_colors;
in vec3 vertex_normals;
in vec3 vertex_position;
out vec4 final_colors;
void main()
{
float l = dot(normalize(-vertex_position), normalize(vertex_normals));
final_colors = vertex_colors * l * 1.2;
}
"""
2021-04-16 23:21:06 +08:00
def set_state(self):
self.program.use()
2022-03-22 23:20:07 +08:00
self.program['model'] = self.matrix
2021-04-16 23:21:06 +08:00
2022-04-08 23:07:41 +08:00
_add_default_codecs()