Cómo crear una malla 3D con Aspose.3D en Python
Aspose.3D FOSS for Python le permite crear geometría 3D completamente en código: no se requiere ninguna herramienta de modelado externa. Usted crea un Mesh, lo rellena con posiciones de vértices (control_points) y definiciones de caras (polygons), adjunta atributos opcionales de vértices como normales, y luego guarda la escena en cualquier formato compatible.
Guía paso a paso
Paso 1: Instalar el paquete
Instale Aspose.3D FOSS desde PyPI. No se requieren extensiones nativas ni cadena de herramientas del compilador.
pip install aspose-3d-fossVerifique la instalación:
from aspose.threed import Scene
print("Aspose.3D FOSS ready")Versiones de Python compatibles: 3.7, 3.8, 3.9, 3.10, 3.11, 3.12.
Paso 2: Crear una escena y un nodo
Cada malla debe vivir dentro de un grafo de escena. Crea un Scene y agrega un Node con nombre para contener la malla:
from aspose.threed import Scene
scene = Scene()
node = scene.root_node.create_child_node("triangle")El nombre del nodo se conserva en el archivo exportado y es útil para depuración y recuperación posterior mediante node.get_child("triangle").
Paso 3: Crear un objeto Mesh
Instanciar un Mesh con un nombre descriptivo opcional:
from aspose.threed.entities import Mesh
mesh = Mesh("triangle")La malla está inicialmente vacía: sin vértices, sin polígonos. La rellenas en los siguientes pasos.
Paso 4: Añadir puntos de control (vértices)
Los puntos de control son las posiciones de los vértices. Cada vértice se almacena como un Vector4(x, y, z, w) donde w=1 indica un punto en el espacio 3D:
from aspose.threed.utilities import Vector4
##Vertex 0: origin
# Note: control_points returns a copy of the internal vertex list.
# Appending to the returned copy discards the vertex silently.
# Use _control_points to mutate the backing list directly.
# This is a known library limitation — a public add_control_point() API is not yet available.
mesh._control_points.append(Vector4(0.0, 0.0, 0.0, 1.0))
##Vertex 1: 1 unit along X
mesh._control_points.append(Vector4(1.0, 0.0, 0.0, 1.0))
##Vertex 2: apex
mesh._control_points.append(Vector4(0.5, 1.0, 0.0, 1.0))
print(f"Vertices added: {len(mesh.control_points)}")Important: mesh.control_points devuelve una copia de la lista interna de vértices (el getter ejecuta list(self._control_points)). Llamar a mesh.control_points.append(v) agrega a la copia, no al mesh, por lo que el vértice se descarta silenciosamente. Siempre use mesh._control_points.append(v) para agregar vértices. Acceder al estado privado mediante _control_points es una solución conocida; la interfaz puede cambiar en una futura versión de la biblioteca.
Paso 5: Crear caras de polígono
Defina la topología de la cara usando índices de vértices. Pase los índices de vértices a create_polygon(). Tres índices producen un triángulo; cuatro producen un cuadrilátero:
##Triangle: connect vertices 0 → 1 → 2
mesh.create_polygon(0, 1, 2)
print(f"Polygon count: {mesh.polygon_count}")Para una malla de cuadriláteros pasarías cuatro índices: mesh.create_polygon(0, 1, 2, 3).
Los índices deben ser posiciones válidas en control_points (basado en cero, dentro del rango). El winding order es counter-clockwise para normales orientadas hacia afuera.
Paso 6: Añadir normales de vértice
Los normales de vértice se almacenan como un VertexElement adjunto a la malla. Use mesh.create_element() con VertexElementType.NORMAL, MappingMode.CONTROL_POINT y ReferenceMode.DIRECT:
from aspose.threed.entities import VertexElementType, MappingMode, ReferenceMode, VertexElementNormal
from aspose.threed.utilities import Vector4, FVector4
##Create the normal element (returns VertexElementNormal, a VertexElementFVector subclass)
normals: VertexElementNormal = mesh.create_element(
VertexElementType.NORMAL,
MappingMode.CONTROL_POINT,
ReferenceMode.DIRECT
)
##One normal per vertex: all pointing out of the XY plane (0, 0, 1)
normals.set_data([
FVector4(0, 0, 1, 0), # vertex 0
FVector4(0, 0, 1, 0), # vertex 1
FVector4(0, 0, 1, 0), # vertex 2
])
print("Normal layer attached.")MappingMode.CONTROL_POINT significa una normal por vértice. ReferenceMode.DIRECT significa que los datos de normales se leen en el mismo orden que los puntos de control (sin búfer de índices adicional).
Los vectores normales usan FVector4(x, y, z, w) con w=0 para indicar una dirección en lugar de una posición. FVector4 es un vector de punto flotante de precisión simple; los datos de atributos de vértice en las subclases VertexElementFVector usan este tipo.
Paso 7: Adjuntar la malla al nodo y guardar
Agrega la malla al nodo, luego guarda la escena:
node.add_entity(mesh)
scene.save("triangle.gltf")
print("Saved triangle.gltf")El script completo que funciona (todos los pasos combinados):
from aspose.threed import Scene
from aspose.threed.entities import Mesh, VertexElementType, MappingMode, ReferenceMode, VertexElementNormal
from aspose.threed.utilities import Vector3, Vector4, FVector4
scene = Scene()
node = scene.root_node.create_child_node("triangle")
mesh = Mesh("triangle")
##Add 3 vertices (x, y, z, w)
# Use _control_points to mutate the backing list directly (control_points returns a copy)
mesh._control_points.append(Vector4(0.0, 0.0, 0.0, 1.0))
mesh._control_points.append(Vector4(1.0, 0.0, 0.0, 1.0))
mesh._control_points.append(Vector4(0.5, 1.0, 0.0, 1.0))
##Create a triangle polygon
mesh.create_polygon(0, 1, 2)
##Add normals (create_element returns VertexElementNormal, a VertexElementFVector subclass)
normals: VertexElementNormal = mesh.create_element(VertexElementType.NORMAL, MappingMode.CONTROL_POINT, ReferenceMode.DIRECT)
normals.set_data([
FVector4(0, 0, 1, 0),
FVector4(0, 0, 1, 0),
FVector4(0, 0, 1, 0),
])
node.add_entity(mesh)
scene.save("triangle.gltf")Problemas comunes
| Issue | Resolution |
|---|---|
IndexError in create_polygon | Verifique que todos los índices estén dentro de range(len(mesh.control_points)). Los índices son de base 0. |
| Mesh exports with zero vertices | mesh.control_points.append(...) descarta silenciosamente los vértices porque la propiedad devuelve una copia. Use mesh._control_points.append(...) en su lugar. |
| Normals count does not match vertex count | Al usar MappingMode.CONTROL_POINT + ReferenceMode.DIRECT, normals.data debe tener exactamente len(control_points) entradas. |
| Mesh missing from saved file | Confirme que node.add_entity(mesh) se llamó antes de scene.save(). Una malla que no está adjunta a ningún nodo no se exporta. |
| Wrong winding order (face appears invisible) | El orden de vértices en sentido antihorario produce una normal orientada hacia afuera. Invierta el orden de los índices en create_polygon para cambiarlo. |
polygon_count returns 0 | polygon_count lee la misma lista que polygons. Si create_polygon no se llamó, la lista está vacía. |
| Normals appear incorrect in viewer | Asegúrese de que todos los vectores normales tengan longitud unitaria. Calcule con n / abs(n) o pase valores prenormalizados. |
Preguntas frecuentes
¿Cuál es la diferencia entre Vector3 y Vector4 para los puntos de control?
control_points almacena objetos Vector4. El componente w es la coordenada homogénea: use w=1 para posiciones de vértices y w=0 para vectores de dirección como normales. Vector3 se utiliza para transformaciones (traslación, escala) pero no para el almacenamiento de geometría.
¿Puedo crear una malla con quads en lugar de triángulos?
Sí. Llame a mesh.create_polygon(0, 1, 2, 3) con cuatro índices para definir un quad. Algunos destinos de guardado (STL, 3MF) requieren triángulos y triangularán los quads automáticamente. glTF y COLLADA conservan los quads.
¿Cómo añado coordenadas UV?
Use mesh.create_element_uv(TextureMapping.DIFFUSE, MappingMode.POLYGON_VERTEX) para crear un VertexElementUV para el canal difuso, luego rellene su lista data con Vector4 entradas. Las coordenadas UV usan x y y; z y w suelen ser 0. El primer argumento debe ser una constante TextureMapping (p. ej., TextureMapping.DIFFUSE) que identifica a qué ranura de textura pertenece la capa UV.
¿Necesita la malla normales para exportarse correctamente?
No. Las normales son opcionales. Si se omiten, la mayoría de los visores calculan normales por cara a partir del orden de los vértices del polígono. Añadir normales explícitas por vértice produce un sombreado más suave.
¿Puedo añadir varios meshes a un nodo?
Sí. Llama a node.add_entity(mesh) varias veces. Cada llamada agrega una nueva entidad a node.entities. Algunos formatos pueden aplanar varias entidades en una al exportar.
¿Cómo triangulo una malla con tipos de polígonos mixtos?
Llama a mesh.triangulate() para convertir todos los quads y N‑gons a triángulos en el mismo lugar. Esto es útil antes de guardar en formatos que solo admiten triángulos.