mirror of
https://github.com/yeicor-3d/yet-another-cad-viewer.git
synced 2025-12-19 22:24:17 +01:00
work
This commit is contained in:
@@ -21,7 +21,16 @@ class GLTFMgr:
|
||||
samplers=[Sampler(magFilter=NEAREST)],
|
||||
textures=[Texture(source=0, sampler=0)],
|
||||
images=[Image(bufferView=0, mimeType='image/png')],
|
||||
materials=[Material(pbrMetallicRoughness=PbrMetallicRoughness(baseColorTexture=TextureInfo(index=0)))],
|
||||
materials=[
|
||||
Material(name="face", pbrMetallicRoughness=PbrMetallicRoughness(
|
||||
baseColorTexture=TextureInfo(index=0), baseColorFactor=[1, 1, 0.5, 1])),
|
||||
Material(name="edge", pbrMetallicRoughness=PbrMetallicRoughness(
|
||||
baseColorTexture=TextureInfo(index=0), baseColorFactor=[0, 0, 0.5, 1])),
|
||||
Material(name="vertex", pbrMetallicRoughness=PbrMetallicRoughness(
|
||||
baseColorTexture=TextureInfo(index=0), baseColorFactor=[0.5, 0.5, 0.5, 1])),
|
||||
Material(name="selected", pbrMetallicRoughness=PbrMetallicRoughness(
|
||||
baseColorTexture=TextureInfo(index=0), baseColorFactor=[1, 0, 0, 1])),
|
||||
],
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
@@ -29,45 +38,52 @@ class GLTFMgr:
|
||||
|
||||
def add_face(self, vertices: np.ndarray, indices: np.ndarray, tex_coord: np.ndarray):
|
||||
"""Add a face to the GLTF as a new primitive of the unique mesh"""
|
||||
self._add_any(vertices, indices, tex_coord, mode=TRIANGLES)
|
||||
self._add_any(vertices, indices, tex_coord, mode=TRIANGLES, material=0)
|
||||
|
||||
def add_edge(self, vertices: np.ndarray):
|
||||
"""Add an edge to the GLTF as a new primitive of the unique mesh"""
|
||||
indices = np.array(list(map(lambda i: [i, i + 1], range(len(vertices) - 1))), dtype=np.uint8)
|
||||
tex_coord = np.array([[i / (len(vertices) - 1), 0] for i in range(len(vertices))], dtype=np.float32)
|
||||
self._add_any(vertices, indices, tex_coord, mode=LINE_STRIP)
|
||||
tex_coord = np.array([])
|
||||
self._add_any(vertices, indices, tex_coord, mode=LINE_STRIP, material=1)
|
||||
|
||||
def add_vertex(self, vertex: Vector):
|
||||
"""Add a vertex to the GLTF as a new primitive of the unique mesh"""
|
||||
vertices = np.array([[vertex.X, vertex.Y, vertex.Z]])
|
||||
indices = np.array([0], dtype=np.uint8)
|
||||
tex_coord = np.array([[0, 0]], dtype=np.float32)
|
||||
self._add_any(vertices, indices, tex_coord, mode=POINTS)
|
||||
indices = np.array([[0]], dtype=np.uint8)
|
||||
tex_coord = np.array([], dtype=np.float32)
|
||||
self._add_any(vertices, indices, tex_coord, mode=POINTS, material=2)
|
||||
|
||||
def _add_any(self, vertices: np.ndarray, indices: np.ndarray, tex_coord: np.ndarray, mode: int = TRIANGLES):
|
||||
def _add_any(self, vertices: np.ndarray, indices: np.ndarray, tex_coord: np.ndarray, mode: int = TRIANGLES,
|
||||
material: int = 0):
|
||||
assert vertices.ndim == 2
|
||||
assert vertices.shape[1] == 3
|
||||
vertices = vertices.astype(np.float32)
|
||||
vertices_blob = vertices.tobytes()
|
||||
|
||||
assert indices.ndim == 2
|
||||
assert indices.shape[1] == 3 and mode == TRIANGLES or indices.shape[1] == 2 and mode == LINE_STRIP or \
|
||||
indices.shape[1] == 1 and mode == POINTS
|
||||
indices = indices.astype(np.uint8)
|
||||
indices_blob = indices.flatten().tobytes()
|
||||
|
||||
assert len(tex_coord) == 0 or tex_coord.ndim == 2
|
||||
assert len(tex_coord) == 0 or tex_coord.shape[1] == 2
|
||||
tex_coord = tex_coord.astype(np.float32)
|
||||
tex_coord_blob = tex_coord.tobytes()
|
||||
|
||||
accessor_base = len(self.gltf.accessors)
|
||||
self.gltf.meshes[0].primitives.append(
|
||||
Primitive(
|
||||
attributes=Attributes(POSITION=accessor_base + 1, TEXCOORD_0=accessor_base + 2),
|
||||
attributes=Attributes(POSITION=accessor_base + 1, TEXCOORD_0=accessor_base + 2)
|
||||
if len(tex_coord) > 0 else Attributes(POSITION=accessor_base + 1),
|
||||
indices=accessor_base,
|
||||
mode=mode,
|
||||
material=0, # TODO special selected material and face/edge/vertex default materials
|
||||
material=material,
|
||||
)
|
||||
)
|
||||
|
||||
buffer_view_base = len(self.gltf.bufferViews)
|
||||
self.gltf.accessors.extend([
|
||||
self.gltf.accessors.extend([it for it in [
|
||||
Accessor(
|
||||
bufferView=buffer_view_base,
|
||||
componentType=UNSIGNED_BYTE,
|
||||
@@ -91,31 +107,31 @@ class GLTFMgr:
|
||||
type=VEC2,
|
||||
max=tex_coord.max(axis=0).tolist(),
|
||||
min=tex_coord.min(axis=0).tolist(),
|
||||
)
|
||||
])
|
||||
) if len(tex_coord) > 0 else None
|
||||
] if it is not None])
|
||||
|
||||
binary_blob = self.gltf.binary_blob()
|
||||
binary_blob_base = len(binary_blob)
|
||||
self.gltf.bufferViews.extend([
|
||||
byte_offset_base = len(binary_blob)
|
||||
self.gltf.bufferViews.extend([it for it in [
|
||||
BufferView(
|
||||
buffer=0,
|
||||
byteOffset=binary_blob_base,
|
||||
byteOffset=byte_offset_base,
|
||||
byteLength=len(indices_blob),
|
||||
target=ELEMENT_ARRAY_BUFFER,
|
||||
),
|
||||
BufferView(
|
||||
buffer=0,
|
||||
byteOffset=binary_blob_base + len(indices_blob),
|
||||
byteOffset=byte_offset_base + len(indices_blob),
|
||||
byteLength=len(vertices_blob),
|
||||
target=ARRAY_BUFFER,
|
||||
),
|
||||
BufferView(
|
||||
buffer=0,
|
||||
byteOffset=binary_blob_base + len(indices_blob) + len(vertices_blob),
|
||||
byteOffset=byte_offset_base + len(indices_blob) + len(vertices_blob),
|
||||
byteLength=len(tex_coord_blob),
|
||||
target=ARRAY_BUFFER,
|
||||
)
|
||||
])
|
||||
) if len(tex_coord) > 0 else None
|
||||
] if it is not None])
|
||||
|
||||
self.gltf.set_binary_blob(binary_blob + indices_blob + vertices_blob + tex_coord_blob)
|
||||
|
||||
|
||||
@@ -1,56 +1,20 @@
|
||||
import concurrent
|
||||
import copy
|
||||
import hashlib
|
||||
import io
|
||||
import re
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
from dataclasses import dataclass
|
||||
from typing import Tuple, Generator
|
||||
|
||||
import numpy as np
|
||||
from OCP.BRep import BRep_Tool
|
||||
from OCP.BRepAdaptor import BRepAdaptor_Curve
|
||||
from OCP.GCPnts import GCPnts_TangentialDeflection
|
||||
from OCP.TopExp import TopExp
|
||||
from OCP.TopLoc import TopLoc_Location
|
||||
from OCP.TopTools import TopTools_IndexedMapOfShape
|
||||
from OCP.TopoDS import TopoDS_Face, TopoDS_Edge, TopoDS_Shape, TopoDS_Vertex
|
||||
from build123d import Face, Vector, Shape, Vertex
|
||||
from pygltflib import LINE_STRIP, GLTF2, Material, PbrMetallicRoughness, TRIANGLES, POINTS, TextureInfo
|
||||
from build123d import Shape, Vertex
|
||||
from pygltflib import GLTF2
|
||||
|
||||
import mylogger
|
||||
from gltf import GLTFMgr
|
||||
|
||||
|
||||
@dataclass
|
||||
class TessellationUpdate:
|
||||
"""Tessellation update"""
|
||||
progress: float
|
||||
"""Progress in percent"""
|
||||
|
||||
# Current shape
|
||||
shape: TopoDS_Shape
|
||||
"""(Sub)shape that was tessellated"""
|
||||
gltf: GLTF2
|
||||
"""The valid GLTF containing only the current shape"""
|
||||
|
||||
@property
|
||||
def kind(self) -> str:
|
||||
"""The kind of the shape"""
|
||||
if isinstance(self.shape, TopoDS_Face):
|
||||
return "face"
|
||||
elif isinstance(self.shape, TopoDS_Edge):
|
||||
return "edge"
|
||||
elif isinstance(self.shape, TopoDS_Vertex):
|
||||
return "vertex"
|
||||
else:
|
||||
raise ValueError(f"Unknown shape type: {self.shape}")
|
||||
|
||||
|
||||
def tessellate_count(ocp_shape: TopoDS_Shape) -> int:
|
||||
"""Count the number of elements that will be tessellated"""
|
||||
shape = Shape(ocp_shape)
|
||||
return len(shape.faces()) + len(shape.edges()) + len(shape.vertices())
|
||||
# TODO: Migrate to ocp-tessellate to reuse the tessellation logic
|
||||
|
||||
|
||||
def tessellate(
|
||||
@@ -65,16 +29,20 @@ def tessellate(
|
||||
mgr = GLTFMgr()
|
||||
shape = Shape(ocp_shape)
|
||||
|
||||
# Triangulate all faces at the same time
|
||||
# shape.mesh(tolerance, angular_tolerance)
|
||||
_tessellate_face(mgr, shape.wrapped)
|
||||
|
||||
# Perform tessellation tasks
|
||||
if faces:
|
||||
for face in shape.faces():
|
||||
_tessellate_face(mgr, face.wrapped, tolerance, angular_tolerance)
|
||||
if edges:
|
||||
for edge in shape.edges():
|
||||
_tessellate_edge(mgr, edge.wrapped, tolerance, angular_tolerance)
|
||||
if vertices:
|
||||
for vertex in shape.vertices():
|
||||
_tessellate_vertex(mgr, vertex.wrapped)
|
||||
# if faces:
|
||||
# for face in shape.faces():
|
||||
# _tessellate_face(mgr, face.wrapped)
|
||||
# if edges:
|
||||
# for edge in shape.edges():
|
||||
# _tessellate_edge(mgr, edge.wrapped, angular_tolerance, angular_tolerance)
|
||||
# if vertices:
|
||||
# for vertex in shape.vertices():
|
||||
# _tessellate_vertex(mgr, vertex.wrapped)
|
||||
|
||||
return mgr.gltf
|
||||
|
||||
@@ -85,20 +53,20 @@ def _tessellate_face(
|
||||
tolerance: float = 1e-3,
|
||||
angular_tolerance: float = 0.1
|
||||
):
|
||||
face = Face(ocp_face)
|
||||
face.mesh(tolerance, angular_tolerance)
|
||||
loc = TopLoc_Location()
|
||||
poly = BRep_Tool.Triangulation_s(face.wrapped, loc)
|
||||
if poly is None:
|
||||
mylogger.logger.warn("No triangulation found for face")
|
||||
return GLTF2()
|
||||
face = Shape(ocp_face)
|
||||
# loc = TopLoc_Location()
|
||||
# poly = BRep_Tool.Triangulation_s(face.wrapped, loc)
|
||||
# if poly is None:
|
||||
# mylogger.logger.warn("No triangulation found for face")
|
||||
# return GLTF2()
|
||||
tri_mesh = face.tessellate(tolerance, angular_tolerance)
|
||||
|
||||
# Get UV of each face from the parameters
|
||||
uv = [
|
||||
[v.X(), v.Y()]
|
||||
for v in (poly.UVNode(i) for i in range(1, poly.NbNodes() + 1))
|
||||
]
|
||||
# uv = [
|
||||
# [v.X(), v.Y()]
|
||||
# for v in (poly.UVNode(i) for i in range(1, poly.NbNodes() + 1))
|
||||
# ]
|
||||
uv = []
|
||||
|
||||
vertices = np.array(list(map(lambda v: [v.X, v.Y, v.Z], tri_mesh[0])))
|
||||
indices = np.array(tri_mesh[1])
|
||||
@@ -109,7 +77,7 @@ def _tessellate_face(
|
||||
def _tessellate_edge(
|
||||
mgr: GLTFMgr,
|
||||
ocp_edge: TopoDS_Edge,
|
||||
angular_deflection: float = 1e-3,
|
||||
angular_deflection: float = 0.1,
|
||||
curvature_deflection: float = 0.1,
|
||||
):
|
||||
curve = BRepAdaptor_Curve(ocp_edge)
|
||||
|
||||
Reference in New Issue
Block a user