ncca-ngl 0.1.0__tar.gz
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.
- ncca_ngl-0.1.0/LICENSE.txt +7 -0
- ncca_ngl-0.1.0/PKG-INFO +23 -0
- ncca_ngl-0.1.0/README.md +22 -0
- ncca_ngl-0.1.0/pyproject.toml +47 -0
- ncca_ngl-0.1.0/setup.cfg +4 -0
- ncca_ngl-0.1.0/src/ncca/ngl/PrimData/pack_arrays.py +20 -0
- ncca_ngl-0.1.0/src/ncca/ngl/__init__.py +100 -0
- ncca_ngl-0.1.0/src/ncca/ngl/abstract_vao.py +89 -0
- ncca_ngl-0.1.0/src/ncca/ngl/base_mesh.py +170 -0
- ncca_ngl-0.1.0/src/ncca/ngl/base_mesh.pyi +11 -0
- ncca_ngl-0.1.0/src/ncca/ngl/bbox.py +224 -0
- ncca_ngl-0.1.0/src/ncca/ngl/bezier_curve.py +75 -0
- ncca_ngl-0.1.0/src/ncca/ngl/first_person_camera.py +174 -0
- ncca_ngl-0.1.0/src/ncca/ngl/image.py +94 -0
- ncca_ngl-0.1.0/src/ncca/ngl/log.py +44 -0
- ncca_ngl-0.1.0/src/ncca/ngl/mat2.py +128 -0
- ncca_ngl-0.1.0/src/ncca/ngl/mat3.py +466 -0
- ncca_ngl-0.1.0/src/ncca/ngl/mat4.py +456 -0
- ncca_ngl-0.1.0/src/ncca/ngl/multi_buffer_vao.py +49 -0
- ncca_ngl-0.1.0/src/ncca/ngl/obj.py +416 -0
- ncca_ngl-0.1.0/src/ncca/ngl/plane.py +47 -0
- ncca_ngl-0.1.0/src/ncca/ngl/primitives.py +706 -0
- ncca_ngl-0.1.0/src/ncca/ngl/pyside_event_handling_mixin.py +318 -0
- ncca_ngl-0.1.0/src/ncca/ngl/quaternion.py +112 -0
- ncca_ngl-0.1.0/src/ncca/ngl/random.py +167 -0
- ncca_ngl-0.1.0/src/ncca/ngl/shader.py +229 -0
- ncca_ngl-0.1.0/src/ncca/ngl/shader_lib.py +536 -0
- ncca_ngl-0.1.0/src/ncca/ngl/shader_program.py +816 -0
- ncca_ngl-0.1.0/src/ncca/ngl/simple_index_vao.py +65 -0
- ncca_ngl-0.1.0/src/ncca/ngl/simple_vao.py +42 -0
- ncca_ngl-0.1.0/src/ncca/ngl/text.py +346 -0
- ncca_ngl-0.1.0/src/ncca/ngl/texture.py +75 -0
- ncca_ngl-0.1.0/src/ncca/ngl/transform.py +95 -0
- ncca_ngl-0.1.0/src/ncca/ngl/util.py +128 -0
- ncca_ngl-0.1.0/src/ncca/ngl/vao_factory.py +34 -0
- ncca_ngl-0.1.0/src/ncca/ngl/vec2.py +350 -0
- ncca_ngl-0.1.0/src/ncca/ngl/vec2_array.py +106 -0
- ncca_ngl-0.1.0/src/ncca/ngl/vec3.py +401 -0
- ncca_ngl-0.1.0/src/ncca/ngl/vec3_array.py +110 -0
- ncca_ngl-0.1.0/src/ncca/ngl/vec4.py +229 -0
- ncca_ngl-0.1.0/src/ncca/ngl/vec4_array.py +106 -0
- ncca_ngl-0.1.0/src/ncca_ngl.egg-info/PKG-INFO +23 -0
- ncca_ngl-0.1.0/src/ncca_ngl.egg-info/SOURCES.txt +71 -0
- ncca_ngl-0.1.0/src/ncca_ngl.egg-info/dependency_links.txt +1 -0
- ncca_ngl-0.1.0/src/ncca_ngl.egg-info/requires.txt +6 -0
- ncca_ngl-0.1.0/src/ncca_ngl.egg-info/top_level.txt +1 -0
- ncca_ngl-0.1.0/tests/test_base_mesh.py +249 -0
- ncca_ngl-0.1.0/tests/test_bbox.py +153 -0
- ncca_ngl-0.1.0/tests/test_bezier_curve.py +78 -0
- ncca_ngl-0.1.0/tests/test_first_person_camera.py +98 -0
- ncca_ngl-0.1.0/tests/test_image.py +66 -0
- ncca_ngl-0.1.0/tests/test_logging.py +9 -0
- ncca_ngl-0.1.0/tests/test_mat2.py +70 -0
- ncca_ngl-0.1.0/tests/test_mat3.py +241 -0
- ncca_ngl-0.1.0/tests/test_mat4.py +287 -0
- ncca_ngl-0.1.0/tests/test_obj.py +313 -0
- ncca_ngl-0.1.0/tests/test_plane.py +74 -0
- ncca_ngl-0.1.0/tests/test_primitives.py +121 -0
- ncca_ngl-0.1.0/tests/test_pyside_event_handling_mixin.py +644 -0
- ncca_ngl-0.1.0/tests/test_quaternion.py +133 -0
- ncca_ngl-0.1.0/tests/test_random.py +130 -0
- ncca_ngl-0.1.0/tests/test_shaderlib.py +1207 -0
- ncca_ngl-0.1.0/tests/test_text.py +15 -0
- ncca_ngl-0.1.0/tests/test_texture.py +75 -0
- ncca_ngl-0.1.0/tests/test_transform.py +160 -0
- ncca_ngl-0.1.0/tests/test_util.py +103 -0
- ncca_ngl-0.1.0/tests/test_vao.py +150 -0
- ncca_ngl-0.1.0/tests/test_vec2.py +216 -0
- ncca_ngl-0.1.0/tests/test_vec2_array.py +112 -0
- ncca_ngl-0.1.0/tests/test_vec3.py +324 -0
- ncca_ngl-0.1.0/tests/test_vec3_array.py +112 -0
- ncca_ngl-0.1.0/tests/test_vec4.py +281 -0
- ncca_ngl-0.1.0/tests/test_vec4_array.py +112 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2024 Jon Macey
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
ncca_ngl-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ncca-ngl
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python version of the NGL graphics library.
|
|
5
|
+
Author-email: Jon Macey <jmacey@bournemouth.ac.uk>
|
|
6
|
+
License: Copyright 2024 Jon Macey
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
11
|
+
|
|
12
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
13
|
+
Project-URL: Homepage, https://github.com/NCCA/PyNGL
|
|
14
|
+
Project-URL: Issues, https://github.com/NCCA/PyNGL/issues
|
|
15
|
+
Requires-Python: >=3.13
|
|
16
|
+
License-File: LICENSE.txt
|
|
17
|
+
Requires-Dist: numpy>=2.3.3
|
|
18
|
+
Requires-Dist: PyOpenGL
|
|
19
|
+
Requires-Dist: Pillow
|
|
20
|
+
Requires-Dist: glfw>=2.9.0
|
|
21
|
+
Requires-Dist: freetype-py>=2.5.1
|
|
22
|
+
Requires-Dist: pyside6>=6.9.2
|
|
23
|
+
Dynamic: license-file
|
ncca_ngl-0.1.0/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# PyNGL
|
|
2
|
+
|
|
3
|
+
This is the code for the full python version of [NGL](https://github.com/NCCA/NGL) the ncca graphics library.
|
|
4
|
+
|
|
5
|
+
More details soon as this is work in progress.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Test
|
|
9
|
+
|
|
10
|
+
use
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
uv run pytest
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
To run tests,
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
uv run pytest --cov=src --cov-report=term-missing
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
For coverage reports.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ncca-ngl"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A Python version of the NGL graphics library."
|
|
5
|
+
authors = [{ name = "Jon Macey", email = "jmacey@bournemouth.ac.uk" }]
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
license = { file = "LICENSE.txt" }
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
"numpy>=2.3.3",
|
|
11
|
+
"PyOpenGL",
|
|
12
|
+
"Pillow",
|
|
13
|
+
"glfw>=2.9.0",
|
|
14
|
+
"freetype-py>=2.5.1",
|
|
15
|
+
"pyside6>=6.9.2",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.urls]
|
|
19
|
+
Homepage = "https://github.com/NCCA/PyNGL"
|
|
20
|
+
Issues = "https://github.com/NCCA/PyNGL/issues"
|
|
21
|
+
|
|
22
|
+
[tool.setuptools.packages.find]
|
|
23
|
+
where = ["src"]
|
|
24
|
+
include = ["ncca.*"]
|
|
25
|
+
|
|
26
|
+
[tool.pytest.ini_options]
|
|
27
|
+
pythonpath = ["src","tests"]
|
|
28
|
+
|
|
29
|
+
# [build-system]
|
|
30
|
+
# requires = ["uv_build>=0.8.14,<0.9.0"]
|
|
31
|
+
# build-backend = "uv_build"
|
|
32
|
+
# module-name = ["ncca.ngl"]
|
|
33
|
+
# module-root = "src/ncca/"
|
|
34
|
+
|
|
35
|
+
[build-system]
|
|
36
|
+
requires = ["setuptools>=61.0"]
|
|
37
|
+
build-backend = "setuptools.build_meta"
|
|
38
|
+
|
|
39
|
+
[dependency-groups]
|
|
40
|
+
dev = [
|
|
41
|
+
"coverage>=7.10.6",
|
|
42
|
+
"pytest>=8.4.2",
|
|
43
|
+
"pytest-cov>=7.0.0",
|
|
44
|
+
"ruff>=0.13.0",
|
|
45
|
+
"pre-commit>=4.3.0",
|
|
46
|
+
|
|
47
|
+
]
|
ncca_ngl-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pathlib
|
|
5
|
+
|
|
6
|
+
files = pathlib.Path(".").glob("*.npy")
|
|
7
|
+
|
|
8
|
+
data = {}
|
|
9
|
+
for f in files:
|
|
10
|
+
data[str(f.stem)] = np.load(f)
|
|
11
|
+
# arrays.append(np.load(f))
|
|
12
|
+
# names.append(str(f.stem))
|
|
13
|
+
print(data.keys())
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
np.savez_compressed("Primitives", **data)
|
|
17
|
+
|
|
18
|
+
loaded = np.load("Primitives.npz")
|
|
19
|
+
print(loaded)
|
|
20
|
+
print(loaded["dragon"])
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from .abstract_vao import AbstractVAO, VertexData
|
|
2
|
+
from .base_mesh import BaseMesh, Face
|
|
3
|
+
from .bbox import BBox
|
|
4
|
+
from .bezier_curve import BezierCurve
|
|
5
|
+
from .first_person_camera import FirstPersonCamera
|
|
6
|
+
from .image import Image, ImageModes
|
|
7
|
+
from .log import logger
|
|
8
|
+
from .mat2 import Mat2
|
|
9
|
+
from .mat3 import Mat3, Mat3Error, Mat3NotSquare
|
|
10
|
+
from .mat4 import Mat4, Mat4Error, Mat4NotSquare
|
|
11
|
+
from .multi_buffer_vao import MultiBufferVAO
|
|
12
|
+
from .obj import (
|
|
13
|
+
Obj,
|
|
14
|
+
ObjParseFaceError,
|
|
15
|
+
ObjParseNormalError,
|
|
16
|
+
ObjParseUVError,
|
|
17
|
+
ObjParseVertexError,
|
|
18
|
+
)
|
|
19
|
+
from .plane import Plane
|
|
20
|
+
from .primitives import Primitives, Prims
|
|
21
|
+
from .pyside_event_handling_mixin import PySideEventHandlingMixin
|
|
22
|
+
from .quaternion import Quaternion
|
|
23
|
+
from .random import Random
|
|
24
|
+
from .shader import MatrixTranspose, Shader, ShaderType
|
|
25
|
+
from .shader_lib import DefaultShader, ShaderLib
|
|
26
|
+
from .shader_program import ShaderProgram
|
|
27
|
+
from .simple_index_vao import IndexVertexData, SimpleIndexVAO
|
|
28
|
+
from .simple_vao import SimpleVAO
|
|
29
|
+
from .text import Text
|
|
30
|
+
from .texture import Texture
|
|
31
|
+
from .transform import Transform, TransformRotationOrder
|
|
32
|
+
from .util import calc_normal, clamp, frustum, lerp, look_at, ortho, perspective
|
|
33
|
+
from .vao_factory import VAOFactory, VAOType
|
|
34
|
+
from .vec2 import Vec2
|
|
35
|
+
from .vec2_array import Vec2Array
|
|
36
|
+
from .vec3 import Vec3
|
|
37
|
+
from .vec3_array import Vec3Array
|
|
38
|
+
from .vec4 import Vec4
|
|
39
|
+
from .vec4_array import Vec4Array
|
|
40
|
+
|
|
41
|
+
all = [
|
|
42
|
+
AbstractVAO,
|
|
43
|
+
VertexData,
|
|
44
|
+
BaseMesh,
|
|
45
|
+
Face,
|
|
46
|
+
BBox,
|
|
47
|
+
BezierCurve,
|
|
48
|
+
Image,
|
|
49
|
+
ImageModes,
|
|
50
|
+
Mat2,
|
|
51
|
+
Mat3,
|
|
52
|
+
Mat4,
|
|
53
|
+
MultiBufferVAO,
|
|
54
|
+
Obj,
|
|
55
|
+
Plane,
|
|
56
|
+
Quaternion,
|
|
57
|
+
MatrixTranspose,
|
|
58
|
+
Shader,
|
|
59
|
+
ShaderProgram,
|
|
60
|
+
ShaderType,
|
|
61
|
+
ShaderLib,
|
|
62
|
+
IndexVertexData,
|
|
63
|
+
SimpleIndexVAO,
|
|
64
|
+
SimpleVAO,
|
|
65
|
+
Texture,
|
|
66
|
+
VAOFactory,
|
|
67
|
+
Vec2,
|
|
68
|
+
Vec3,
|
|
69
|
+
Vec4,
|
|
70
|
+
Vec3Array,
|
|
71
|
+
Vec2Array,
|
|
72
|
+
Vec4Array,
|
|
73
|
+
ObjParseVertexError,
|
|
74
|
+
ObjParseNormalError,
|
|
75
|
+
ObjParseUVError,
|
|
76
|
+
ObjParseFaceError,
|
|
77
|
+
clamp,
|
|
78
|
+
lerp,
|
|
79
|
+
look_at,
|
|
80
|
+
perspective,
|
|
81
|
+
ortho,
|
|
82
|
+
frustum,
|
|
83
|
+
Transform,
|
|
84
|
+
TransformRotationOrder,
|
|
85
|
+
Random,
|
|
86
|
+
Text,
|
|
87
|
+
calc_normal,
|
|
88
|
+
Mat3Error,
|
|
89
|
+
Mat4Error,
|
|
90
|
+
Mat3NotSquare,
|
|
91
|
+
Mat4NotSquare,
|
|
92
|
+
Mat4NotSquare,
|
|
93
|
+
VAOType,
|
|
94
|
+
DefaultShader,
|
|
95
|
+
logger,
|
|
96
|
+
Primitives,
|
|
97
|
+
Prims,
|
|
98
|
+
FirstPersonCamera,
|
|
99
|
+
PySideEventHandlingMixin,
|
|
100
|
+
]
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import ctypes
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import OpenGL.GL as gl
|
|
6
|
+
|
|
7
|
+
from .log import logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class VertexData:
|
|
11
|
+
def __init__(self, data, size, mode=gl.GL_STATIC_DRAW):
|
|
12
|
+
if isinstance(data, np.ndarray):
|
|
13
|
+
self.data = data
|
|
14
|
+
else:
|
|
15
|
+
self.data = np.array(data, dtype=np.float32)
|
|
16
|
+
self.size = size
|
|
17
|
+
self.mode = mode
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AbstractVAO(abc.ABC):
|
|
21
|
+
def __init__(self, mode=gl.GL_TRIANGLES):
|
|
22
|
+
self.id = gl.glGenVertexArrays(1)
|
|
23
|
+
self.mode = mode
|
|
24
|
+
self.bound = False
|
|
25
|
+
self.allocated = False
|
|
26
|
+
self.indices_count = 0
|
|
27
|
+
|
|
28
|
+
def bind(self):
|
|
29
|
+
gl.glBindVertexArray(self.id)
|
|
30
|
+
self.bound = True
|
|
31
|
+
|
|
32
|
+
def unbind(self):
|
|
33
|
+
gl.glBindVertexArray(0)
|
|
34
|
+
self.bound = False
|
|
35
|
+
|
|
36
|
+
def __enter__(self):
|
|
37
|
+
self.bind()
|
|
38
|
+
return self
|
|
39
|
+
|
|
40
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
41
|
+
self.unbind()
|
|
42
|
+
|
|
43
|
+
@abc.abstractmethod
|
|
44
|
+
def draw(self):
|
|
45
|
+
raise NotImplementedError
|
|
46
|
+
|
|
47
|
+
@abc.abstractmethod
|
|
48
|
+
def set_data(self, data):
|
|
49
|
+
raise NotImplementedError
|
|
50
|
+
|
|
51
|
+
@abc.abstractmethod
|
|
52
|
+
def remove_vao(self):
|
|
53
|
+
raise NotImplementedError
|
|
54
|
+
|
|
55
|
+
def set_vertex_attribute_pointer(
|
|
56
|
+
self, id, size, type, stride, offset, normalize=False
|
|
57
|
+
):
|
|
58
|
+
if not self.bound:
|
|
59
|
+
logger.error("VAO not bound in set_vertex_attribute_pointer")
|
|
60
|
+
gl.glVertexAttribPointer(
|
|
61
|
+
id, size, type, normalize, stride, ctypes.c_void_p(offset)
|
|
62
|
+
)
|
|
63
|
+
gl.glEnableVertexAttribArray(id)
|
|
64
|
+
|
|
65
|
+
def set_num_indices(self, count):
|
|
66
|
+
self.indices_count = count
|
|
67
|
+
|
|
68
|
+
def num_indices(self):
|
|
69
|
+
return self.indices_count
|
|
70
|
+
|
|
71
|
+
def get_mode(self):
|
|
72
|
+
return self.mode
|
|
73
|
+
|
|
74
|
+
def set_mode(self, mode):
|
|
75
|
+
self.mode = mode
|
|
76
|
+
|
|
77
|
+
@abc.abstractmethod
|
|
78
|
+
def get_buffer_id(self, index=0):
|
|
79
|
+
raise NotImplementedError
|
|
80
|
+
|
|
81
|
+
@abc.abstractmethod
|
|
82
|
+
def map_buffer(self, index=0, access_mode=gl.GL_READ_WRITE):
|
|
83
|
+
raise NotImplementedError
|
|
84
|
+
|
|
85
|
+
def unmap_buffer(self):
|
|
86
|
+
gl.glUnmapBuffer(gl.GL_ARRAY_BUFFER)
|
|
87
|
+
|
|
88
|
+
def get_id(self):
|
|
89
|
+
return self.id
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import OpenGL.GL as gl
|
|
5
|
+
|
|
6
|
+
from . import vao_factory
|
|
7
|
+
from .abstract_vao import VertexData
|
|
8
|
+
from .bbox import BBox
|
|
9
|
+
from .log import logger
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Face:
|
|
13
|
+
"""
|
|
14
|
+
Simple face structure for mesh geometry.
|
|
15
|
+
Holds indices for vertices, UVs, and normals.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
slots = ("vertex", "uv", "normal")
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
self.vertex: list[int] = []
|
|
22
|
+
self.uv: list[int] = []
|
|
23
|
+
self.normal: list[int] = []
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class BaseMesh:
|
|
27
|
+
"""
|
|
28
|
+
Base class for mesh geometry.
|
|
29
|
+
Provides storage for vertices, normals, UVs, faces, and VAO management.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self):
|
|
33
|
+
self.vertex: list = []
|
|
34
|
+
self.normals: list = []
|
|
35
|
+
self.uv: list = []
|
|
36
|
+
self.faces: list[Face] = []
|
|
37
|
+
self.vao = None
|
|
38
|
+
self.bbox = None
|
|
39
|
+
self.min_x: float = 0.0
|
|
40
|
+
self.max_x: float = 0.0
|
|
41
|
+
self.min_y: float = 0.0
|
|
42
|
+
self.max_y: float = 0.0
|
|
43
|
+
self.min_z: float = 0.0
|
|
44
|
+
self.max_z: float = 0.0
|
|
45
|
+
self.texture_id: int = 0
|
|
46
|
+
self.texture: bool = False
|
|
47
|
+
|
|
48
|
+
def is_triangular(self) -> bool:
|
|
49
|
+
"""
|
|
50
|
+
Check if all faces in the mesh are triangles.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
bool: True if all faces are triangles, False otherwise.
|
|
54
|
+
"""
|
|
55
|
+
return all(len(f.vertex) == 3 for f in self.faces)
|
|
56
|
+
|
|
57
|
+
def create_vao(self, reset_vao: bool = False) -> None:
|
|
58
|
+
"""
|
|
59
|
+
Create a Vertex Array Object (VAO) for the mesh.
|
|
60
|
+
Only supports triangular meshes.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
reset_vao: If True, will not create a new VAO if one already exists.
|
|
64
|
+
Raises:
|
|
65
|
+
RuntimeError: If the mesh is not composed entirely of triangles.
|
|
66
|
+
"""
|
|
67
|
+
if reset_vao:
|
|
68
|
+
if self.vao is not None:
|
|
69
|
+
logger.warning("VAO exist so returning")
|
|
70
|
+
return
|
|
71
|
+
else:
|
|
72
|
+
if self.vao is not None:
|
|
73
|
+
logger.warning("Creating new VAO")
|
|
74
|
+
|
|
75
|
+
data_pack_type = 0
|
|
76
|
+
if self.is_triangular():
|
|
77
|
+
data_pack_type = gl.GL_TRIANGLES
|
|
78
|
+
if data_pack_type == 0:
|
|
79
|
+
logger.error("Can only create VBO from all Triangle data at present")
|
|
80
|
+
raise RuntimeError("Can only create VBO from all Triangle data at present")
|
|
81
|
+
|
|
82
|
+
@dataclass
|
|
83
|
+
class VertData:
|
|
84
|
+
"""
|
|
85
|
+
Structure for a single vertex's data, including position, normal, and UV.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
x: float = 0.0
|
|
89
|
+
y: float = 0.0
|
|
90
|
+
z: float = 0.0
|
|
91
|
+
nx: float = 0.0
|
|
92
|
+
ny: float = 0.0
|
|
93
|
+
nz: float = 0.0
|
|
94
|
+
u: float = 0.0
|
|
95
|
+
v: float = 0.0
|
|
96
|
+
|
|
97
|
+
def as_array(self) -> np.ndarray:
|
|
98
|
+
return np.array(
|
|
99
|
+
[self.x, self.y, self.z, self.nx, self.ny, self.nz, self.u, self.v],
|
|
100
|
+
dtype=np.float32,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
vbo_mesh: list[VertData] = []
|
|
104
|
+
for face in self.faces:
|
|
105
|
+
for i in range(3):
|
|
106
|
+
d = VertData()
|
|
107
|
+
d.x = self.vertex[face.vertex[i]].x
|
|
108
|
+
d.y = self.vertex[face.vertex[i]].y
|
|
109
|
+
d.z = self.vertex[face.vertex[i]].z
|
|
110
|
+
if self.normals and self.uv:
|
|
111
|
+
d.nx = self.normals[face.normal[i]].x
|
|
112
|
+
d.ny = self.normals[face.normal[i]].y
|
|
113
|
+
d.nz = self.normals[face.normal[i]].z
|
|
114
|
+
d.u = self.uv[face.uv[i]].x
|
|
115
|
+
d.v = 1 - self.uv[face.uv[i]].y # Flip V for OpenGL
|
|
116
|
+
elif self.normals and not self.uv:
|
|
117
|
+
d.nx = self.normals[face.normal[i]].x
|
|
118
|
+
d.ny = self.normals[face.normal[i]].y
|
|
119
|
+
d.nz = self.normals[face.normal[i]].z
|
|
120
|
+
elif not self.normals and self.uv:
|
|
121
|
+
d.u = self.uv[face.uv[i]].x
|
|
122
|
+
d.v = 1 - self.uv[face.uv[i]].y
|
|
123
|
+
vbo_mesh.append(d)
|
|
124
|
+
|
|
125
|
+
mesh_data = np.concatenate([v.as_array() for v in vbo_mesh]).astype(np.float32)
|
|
126
|
+
self.vao = vao_factory.VAOFactory.create_vao(
|
|
127
|
+
vao_factory.VAOType.SIMPLE, data_pack_type
|
|
128
|
+
)
|
|
129
|
+
with self.vao as vao:
|
|
130
|
+
mesh_size = len(mesh_data) // 8
|
|
131
|
+
vao.set_data(VertexData(mesh_data, mesh_size))
|
|
132
|
+
# vertex
|
|
133
|
+
vao.set_vertex_attribute_pointer(0, 3, gl.GL_FLOAT, 8 * 4, 0)
|
|
134
|
+
# normals
|
|
135
|
+
vao.set_vertex_attribute_pointer(1, 3, gl.GL_FLOAT, 8 * 4, 3 * 4)
|
|
136
|
+
# uvs
|
|
137
|
+
vao.set_vertex_attribute_pointer(2, 2, gl.GL_FLOAT, 8 * 4, 6 * 4)
|
|
138
|
+
vao.set_num_indices(mesh_size)
|
|
139
|
+
self.calc_dimensions()
|
|
140
|
+
self.bbox = BBox.from_extents(
|
|
141
|
+
self.min_x, self.max_x, self.min_y, self.max_y, self.min_z, self.max_z
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
def calc_dimensions(self) -> None:
|
|
145
|
+
"""
|
|
146
|
+
Calculate the bounding box extents for the mesh.
|
|
147
|
+
Updates min_x, max_x, min_y, max_y, min_z, max_z.
|
|
148
|
+
"""
|
|
149
|
+
if not self.vertex:
|
|
150
|
+
return
|
|
151
|
+
self.min_x = self.max_x = self.vertex[0].x
|
|
152
|
+
self.min_y = self.max_y = self.vertex[0].y
|
|
153
|
+
self.min_z = self.max_z = self.vertex[0].z
|
|
154
|
+
for v in self.vertex:
|
|
155
|
+
self.min_x = min(self.min_x, v.x)
|
|
156
|
+
self.max_x = max(self.max_x, v.x)
|
|
157
|
+
self.min_y = min(self.min_y, v.y)
|
|
158
|
+
self.max_y = max(self.max_y, v.y)
|
|
159
|
+
self.min_z = min(self.min_z, v.z)
|
|
160
|
+
self.max_z = max(self.max_z, v.z)
|
|
161
|
+
|
|
162
|
+
def draw(self) -> None:
|
|
163
|
+
"""
|
|
164
|
+
Draw the mesh using its VAO and bound texture (if any).
|
|
165
|
+
"""
|
|
166
|
+
if self.vao:
|
|
167
|
+
if self.texture_id:
|
|
168
|
+
gl.glBindTexture(gl.GL_TEXTURE_2D, self.texture_id)
|
|
169
|
+
with self.vao as vao:
|
|
170
|
+
vao.draw()
|