private void BuildVisualizationsOfNode(SCNNode node, ref SCNNode verticesNode, ref SCNNode normalsNode) { // A material that will prevent the nodes from being lit var noLightingMaterial = SCNMaterial.Create(); noLightingMaterial.LightingModelName = SCNLightingModel.Constant; var normalMaterial = SCNMaterial.Create(); normalMaterial.LightingModelName = SCNLightingModel.Constant; normalMaterial.Diffuse.Contents = NSColor.Red; // Create nodes to represent the vertex and normals var positionVisualizationNode = SCNNode.Create(); var normalsVisualizationNode = SCNNode.Create(); // Retrieve the vertices and normals from the model var positionSource = node.Geometry.GetGeometrySourcesForSemantic(SCNGeometrySourceSemantic.Vertex) [0]; var normalSource = node.Geometry.GetGeometrySourcesForSemantic(SCNGeometrySourceSemantic.Normal) [0]; // Get vertex and normal bytes var vertexBufferByte = (byte[])positionSource.Data.ToArray(); var vertexBuffer = new float[vertexBufferByte.Length / 4]; Buffer.BlockCopy(vertexBufferByte, 0, vertexBuffer, 0, vertexBufferByte.Length); var normalBufferByte = (byte[])normalSource.Data.ToArray(); var normalBuffer = new float[normalBufferByte.Length / 4]; Buffer.BlockCopy(normalBufferByte, 0, normalBuffer, 0, normalBufferByte.Length); // Iterate and create geometries to represent the positions and normals for (var i = 0; i < positionSource.VectorCount; i++) { // One new node per normal/vertex var vertexNode = SCNNode.Create(); var normalNode = SCNNode.Create(); // Attach one sphere per vertex var sphere = SCNSphere.Create(0.5f); sphere.SegmentCount = 4; // use a small segment count for better performances sphere.FirstMaterial = noLightingMaterial; vertexNode.Geometry = sphere; // And one pyramid per normal var pyramid = SCNPyramid.Create(0.1f, 0.1f, 8); pyramid.FirstMaterial = normalMaterial; normalNode.Geometry = pyramid; // Place the position node var componentsPerVector = positionSource.ComponentsPerVector; vertexNode.Position = new SCNVector3(vertexBuffer [i * componentsPerVector], vertexBuffer [i * componentsPerVector + 1], vertexBuffer [i * componentsPerVector + 2]); // Place the normal node normalNode.Position = vertexNode.Position; // Orientate the normal var up = new Vector3(0, 0, 1); var normalVec = new Vector3(normalBuffer [i * 3], normalBuffer [i * 3 + 1], normalBuffer [i * 3 + 2]); var axis = Vector3.Normalize(Vector3.Cross(up, normalVec)); var dotProduct = Vector3.Dot(up, normalVec); normalNode.Rotation = new SCNVector4(axis.X, axis.Y, axis.Z, NMath.Acos(dotProduct)); // Add the nodes to their parent positionVisualizationNode.AddChildNode(vertexNode); normalsVisualizationNode.AddChildNode(normalNode); } // We must flush the transaction in order to make sure that the parametric geometries (sphere and pyramid) // are up-to-date before flattening the nodes SCNTransaction.Flush(); // Flatten the visualization nodes so that they can be rendered with 1 draw call verticesNode = positionVisualizationNode.FlattenedClone(); normalsNode = normalsVisualizationNode.FlattenedClone(); }