# Custom Geometry Example#

This example makes use of QQuick3DGeometry and the geometry property of Model to render a mesh with vertex, normal, and texture coordinates specified from Python instead of a pre-baked asset.

In addition, the GridGeometry is also demonstrated. GridGeometry is a built-in QQuick3DGeometry implementation that provides a mesh with line primitives suitable for displaying a grid.

The focus on this example will be on the code that provides the custom geometry.

```import random

import numpy as np
from PySide6.QtGui import QVector3D
from PySide6.QtQml import QmlElement
from PySide6.QtQuick3D import QQuick3DGeometry

QML_IMPORT_NAME = "ExamplePointGeometry"
QML_IMPORT_MAJOR_VERSION = 1

@QmlElement
class ExamplePointGeometry(QQuick3DGeometry):
def __init__(self, parent=None):
QQuick3DGeometry.__init__(self, parent)
self.updateData()

def updateData(self):
self.clear()

# We use numpy arrays to handle the vertex data,
# but still we need to consider the 'sizeof(float)'
# from C to set the Stride, and Attributes for the
# underlying Qt methods
FLOAT_SIZE = 4
NUM_POINTS = 2000
stride = 3

vertexData = np.zeros(NUM_POINTS * stride, dtype=np.float32)

p = 0
for i in range(NUM_POINTS):
vertexData[p] = random.uniform(-5.0, +5.0)
p += 1
vertexData[p] = random.uniform(-5.0, +5.0)
p += 1
vertexData[p] = 0.0
p += 1

self.setVertexData(vertexData.tobytes())
self.setStride(stride * FLOAT_SIZE)
self.setBounds(QVector3D(-5.0, -5.0, 0.0), QVector3D(+5.0, +5.0, 0.0))

self.setPrimitiveType(QQuick3DGeometry.PrimitiveType.Points)

QQuick3DGeometry.Attribute.PositionSemantic, 0, QQuick3DGeometry.Attribute.F32Type
)
```
```import numpy as np
from PySide6.QtCore import Property, Signal
from PySide6.QtGui import QVector3D
from PySide6.QtQml import QmlElement
from PySide6.QtQuick3D import QQuick3DGeometry

QML_IMPORT_NAME = "ExampleTriangleGeometry"
QML_IMPORT_MAJOR_VERSION = 1

@QmlElement
class ExampleTriangleGeometry(QQuick3DGeometry):

normalsChanged = Signal()
normalXYChanged = Signal()
uvChanged = Signal()

def __init__(self, parent=None):
QQuick3DGeometry.__init__(self, parent)
self._hasNormals = False
self._normalXY = 0.0
self._hasUV = False

self.updateData()

@Property(bool, notify=normalsChanged)
def normals(self):
return self._hasNormals

@normals.setter
def normals(self, enable):
if self._hasNormals == enable:
return

self._hasNormals = enable
self.normalsChanged.emit()
self.updateData()
self.update()

@Property(float, notify=normalXYChanged)
def normalXY(self):
return self._normalXY

@normalXY.setter
def normalXY(self, xy):
if self._normalXY == xy:
return

self._normalXY = xy
self.normalXYChanged.emit()
self.updateData()
self.update()

@Property(bool, notify=uvChanged)
def uv(self):
return self._hasUV

@uv.setter
def uv(self, enable):
if self._hasUV == enable:
return

self._hasUV = enable
self.uvChanged.emit()
self.updateData()
self.update()

return

self.updateData()
self.update()

def updateData(self):
self.clear()

stride = 3
if self._hasNormals:
stride += 3
if self._hasUV:
stride += 2

# We use numpy arrays to handle the vertex data,
# but still we need to consider the 'sizeof(float)'
# from C to set the Stride, and Attributes for the
# underlying Qt methods
FLOAT_SIZE = 4
vertexData = np.zeros(3 * stride, dtype=np.float32)

# a triangle, front face = counter-clockwise
p = 0
vertexData[p] = -1.0
p += 1
vertexData[p] = -1.0
p += 1
vertexData[p] = 0.0
p += 1

if self._hasNormals:
vertexData[p] = self._normalXY
p += 1
vertexData[p] = self._normalXY
p += 1
vertexData[p] = 1.0
p += 1

if self._hasUV:
p += 1
p += 1

vertexData[p] = 1.0
p += 1
vertexData[p] = -1.0
p += 1
vertexData[p] = 0.0
p += 1

if self._hasNormals:
vertexData[p] = self._normalXY
p += 1
vertexData[p] = self._normalXY
p += 1
vertexData[p] = 1.0
p += 1

if self._hasUV:
p += 1
p += 1

vertexData[p] = 0.0
p += 1
vertexData[p] = 1.0
p += 1
vertexData[p] = 0.0
p += 1

if self._hasNormals:
vertexData[p] = self._normalXY
p += 1
vertexData[p] = self._normalXY
p += 1
vertexData[p] = 1.0
p += 1

if self._hasUV:
p += 1
p += 1

self.setVertexData(vertexData.tobytes())
self.setStride(stride * FLOAT_SIZE)
self.setBounds(QVector3D(-1.0, -1.0, 0.0), QVector3D(+1.0, +1.0, 0.0))
self.setPrimitiveType(QQuick3DGeometry.PrimitiveType.Triangles)
QQuick3DGeometry.Attribute.PositionSemantic, 0, QQuick3DGeometry.Attribute.F32Type
)

if self._hasNormals:
QQuick3DGeometry.Attribute.NormalSemantic,
3 * FLOAT_SIZE,
QQuick3DGeometry.Attribute.F32Type,
)

if self._hasUV:
QQuick3DGeometry.Attribute.TexCoordSemantic,
6 * FLOAT_SIZE if self._hasNormals else 3 * FLOAT_SIZE,
QQuick3DGeometry.Attribute.F32Type,
)
```
```import os
import sys

from PySide6.QtCore import QUrl
from PySide6.QtGui import QGuiApplication, QSurfaceFormat
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtQuick3D import QQuick3D

# Imports to trigger the resources and registration of QML elements
import resources_rc
from examplepoint import ExamplePointGeometry
from exampletriangle import ExampleTriangleGeometry

if __name__ == "__main__":
os.environ["QT_QUICK_CONTROLS_STYLE"] = "Basic"
app = QGuiApplication(sys.argv)

QSurfaceFormat.setDefaultFormat(QQuick3D.idealSurfaceFormat())

engine = QQmlApplicationEngine()
if not engine.rootObjects():
sys.exit(-1)

sys.exit(app.exec())
```
```import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick3D
import QtQuick3D.Helpers
import ExamplePointGeometry
import ExampleTriangleGeometry

Window {
id: window
width: 1280
height: 720
visible: true
color: "#848895"

View3D {
id: v3d
anchors.fill: parent
camera: camera

PerspectiveCamera {
id: camera
position: Qt.vector3d(0, 0, 600)
}

DirectionalLight {
position: Qt.vector3d(-500, 500, -100)
color: Qt.rgba(0.4, 0.2, 0.6, 1.0)
ambientColor: Qt.rgba(0.1, 0.1, 0.1, 1.0)
}

PointLight {
position: Qt.vector3d(0, 0, 100)
color: Qt.rgba(0.1, 1.0, 0.1, 1.0)
ambientColor: Qt.rgba(0.2, 0.2, 0.2, 1.0)
}

Model {
scale: Qt.vector3d(100, 100, 100)
geometry: GridGeometry {
id: grid
horizontalLines: 20
verticalLines: 20
}
materials: [
DefaultMaterial {
lineWidth: sliderLineWidth.value
}
]
}

//! [model triangle]
Model {
scale: Qt.vector3d(100, 100, 100)
geometry: ExampleTriangleGeometry {
normals: cbNorm.checked
normalXY: sliderNorm.value
uv: cbUV.checked
}
materials: [
DefaultMaterial {
Texture {
id: baseColorMap
source: "qt_logo_rect.png"
}
cullMode: DefaultMaterial.NoCulling
diffuseMap: cbTexture.checked ? baseColorMap : null
specularAmount: 0.5
}
]
}
//! [model triangle]

Model {
scale: Qt.vector3d(100, 100, 100)
geometry: ExamplePointGeometry { }
materials: [
DefaultMaterial {
lighting: DefaultMaterial.NoLighting
cullMode: DefaultMaterial.NoCulling
diffuseColor: "yellow"
pointSize: sliderPointSize.value
}
]
}
}

WasdController {
controlledObject: camera
}

ColumnLayout {
Label {
text: "Use WASD and mouse to navigate"
font.bold: true
}
ButtonGroup {
}
text: "GridGeometry"
checked: true
focusPolicy: Qt.NoFocus
}
text: "Custom geometry from application (triangle)"
checked: false
focusPolicy: Qt.NoFocus
}
text: "Custom geometry from application (points)"
checked: false
focusPolicy: Qt.NoFocus
}
RowLayout {
ColumnLayout {
Button {
text: "More X cells"
onClicked: grid.verticalLines += 1
focusPolicy: Qt.NoFocus
}
Button  {
text: "Fewer X cells"
onClicked: grid.verticalLines -= 1
focusPolicy: Qt.NoFocus
}
}
ColumnLayout {
Button {
text: "More Y cells"
onClicked: grid.horizontalLines += 1
focusPolicy: Qt.NoFocus
}
Button  {
text: "Fewer Y cells"
onClicked: grid.horizontalLines -= 1
focusPolicy: Qt.NoFocus
}
}
}
RowLayout {
Label {
text: "Line width (if supported)"
}
Slider {
id: sliderLineWidth
from: 1.0
to: 10.0
stepSize: 0.5
value: 1.0
focusPolicy: Qt.NoFocus
}
}
RowLayout {
CheckBox {
id: cbNorm
text: "provide normals in geometry"
checked: false
focusPolicy: Qt.NoFocus
}
RowLayout {
Label {
}
Slider {
id: sliderNorm
from: 0.0
to: 1.0
stepSize: 0.01
value: 0.0
focusPolicy: Qt.NoFocus
}
}
}
RowLayout {
CheckBox {
id: cbTexture
text: "enable base color map"
checked: false
focusPolicy: Qt.NoFocus
}
CheckBox {
id: cbUV
text: "provide UV in geometry"
checked: false
focusPolicy: Qt.NoFocus
}
RowLayout {
Label {
}
Slider {
id: sliderUV
from: 0.0
to: 1.0
stepSize: 0.01
value: 0.0
focusPolicy: Qt.NoFocus
}
}
}
RowLayout {
ColumnLayout {
RowLayout {
Label {
text: "Point size (if supported)"
}
Slider {
id: sliderPointSize
from: 1.0
to: 16.0
stepSize: 1.0
value: 1.0
focusPolicy: Qt.NoFocus
}
}
}
}
TextArea {
id: infoText
```<RCC>