我在理解scenekit几何方面遇到了问题。
我有Blender的默认立方体,我导出为collada(DAE),可以将它带入scenekit ......一切都很好。
现在我想看到立方体的顶点。在DAE中,我可以看到以下“Cube-mesh-positions-array”,
“1 1 -1 1 -1 -1 -1 -0.9999998 -1 -0.9999997 1 -1 1 0.9999995 1 0.9999994 -1.000001 1 -1 -0.9999997 1 -1 1 1”
现在我想在scenekit中做的是使用以下内容返回顶点:
SCNGeometrySource *vertexBuffer = [[cubeNode.geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticVertex] objectAtIndex:0];
如果我处理vertexBuffer(我尝试了很多查看数据的方法),它似乎不正确。
有人可以解释“SCNGeometrySourceSemanticVertex”给我的内容,以及如何正确提取顶点数据?我想看到的是:
X = "float"
Y = "float"
Z = "float"
此外,我正在调查以下类/方法,这看起来很有前途(这里有一些好的数据值),但是来自gmpe的数据显示为空,是否有人能够解释“SCNGeometryElement”的数据属性包含什么?
SCNGeometryElement *gmpe = [theCurrentNode.geometry geometryElementAtIndex:0];
谢谢,非常感谢,
d
答案 0 :(得分:25)
当你调用geometrySourcesForSemantic:
时,你会得到一组SCNGeometrySource
个对象,在你的情况下,给定的语义是顶点数据的来源。)
此数据可能已经以多种不同方式进行编码,多个来源可以使用具有不同stride
和offset
的相同数据。源本身有一堆属性,您可以解码data
,例如
dataStride
dataOffset
vectorCount
componentsPerVector
bytesPerComponent
您可以使用这些组合来确定要读取data
的哪些部分并从中创建顶点。
步幅告诉你应该步进多少字节到达下一个向量,偏移量告诉你在到达该向量的相关数据段之前应该偏离该向量的起始偏移多少字节。您应该为每个向量读取的字节数是componentsPerVector * bytesPerComponent
读出单个几何源的所有顶点的代码看起来像这样
// Get the vertex sources
NSArray *vertexSources = [geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticVertex];
// Get the first source
SCNGeometrySource *vertexSource = vertexSources[0]; // TODO: Parse all the sources
NSInteger stride = vertexSource.dataStride; // in bytes
NSInteger offset = vertexSource.dataOffset; // in bytes
NSInteger componentsPerVector = vertexSource.componentsPerVector;
NSInteger bytesPerVector = componentsPerVector * vertexSource.bytesPerComponent;
NSInteger vectorCount = vertexSource.vectorCount;
SCNVector3 vertices[vectorCount]; // A new array for vertices
// for each vector, read the bytes
for (NSInteger i=0; i<vectorCount; i++) {
// Assuming that bytes per component is 4 (a float)
// If it was 8 then it would be a double (aka CGFloat)
float vectorData[componentsPerVector];
// The range of bytes for this vector
NSRange byteRange = NSMakeRange(i*stride + offset, // Start at current stride + offset
bytesPerVector); // and read the lenght of one vector
// Read into the vector data buffer
[vertexSource.data getBytes:&vectorData range:byteRange];
// At this point you can read the data from the float array
float x = vectorData[0];
float y = vectorData[1];
float z = vectorData[2];
// ... Maybe even save it as an SCNVector3 for later use ...
vertices[i] = SCNVector3Make(x, y, z);
// ... or just log it
NSLog(@"x:%f, y:%f, z:%f", x, y, z);
}
这将为您提供所有顶点,但不会告诉您如何使用它们来构造几何体。为此,您需要几何元素来管理顶点的索引。
您可以从geometryElementCount
属性获取几何体的几何元素数。然后,您可以使用geometryElementAtIndex:
获取不同的元素。
元素可以告诉您顶点是使用单个三角形还是三角形条带。它还告诉您每个索引的字节数(索引可能是int
s或short
s,这对于解码其data
是必需的。
答案 1 :(得分:4)
Objective-C版本与此基本相同。
let planeSources = _planeNode?.geometry?.geometrySourcesForSemantic(SCNGeometrySourceSemanticVertex)
if let planeSource = planeSources?.first {
let stride = planeSource.dataStride
let offset = planeSource.dataOffset
let componentsPerVector = planeSource.componentsPerVector
let bytesPerVector = componentsPerVector * planeSource.bytesPerComponent
let vectors = [SCNVector3](count: planeSource.vectorCount, repeatedValue: SCNVector3Zero)
let vertices = vectors.enumerate().map({
(index: Int, element: SCNVector3) -> SCNVector3 in
var vectorData = [Float](count: componentsPerVector, repeatedValue: 0)
let byteRange = NSMakeRange(index * stride + offset, bytesPerVector)
planeSource.data.getBytes(&vectorData, range: byteRange)
return SCNVector3Make(vectorData[0], vectorData[1], vectorData[2])
})
// You have your vertices, now what?
}
答案 2 :(得分:2)
使用swift 3.1,您可以更快,更短的方式从SCNGeometry中提取顶点:
func vertices(node:SCNNode) -> [SCNVector3] {
let vertexSources = node.geometry?.getGeometrySources(for: SCNGeometrySource.Semantic.vertex)
if let vertexSource = vertexSources?.first {
let count = vertexSource.data.count / MemoryLayout<SCNVector3>.size
return vertexSource.data.withUnsafeBytes {
[SCNVector3](UnsafeBufferPointer<SCNVector3>(start: $0, count: count))
}
}
return []
}
... 今天我已经注意到在osx上这不会正常工作。发生这种情况是因为在iOS SCNVector3上使用Float和osx CGFloat进行构建(只有苹果很好,所以很简单,所以很痛苦)。所以我不得不调整osx的代码,但这不会像在iOS上一样快。
func vertices() -> [SCNVector3] {
let vertexSources = sources(for: SCNGeometrySource.Semantic.vertex)
if let vertexSource = vertexSources.first {
let count = vertexSource.vectorCount * 3
let values = vertexSource.data.withUnsafeBytes {
[Float](UnsafeBufferPointer<Float>(start: $0, count: count))
}
var vectors = [SCNVector3]()
for i in 0..<vertexSource.vectorCount {
let offset = i * 3
vectors.append(SCNVector3Make(
CGFloat(values[offset]),
CGFloat(values[offset + 1]),
CGFloat(values[offset + 2])
))
}
return vectors
}
return []
}
答案 3 :(得分:2)
如果数据不连续(向量大小不等于步幅),这是一种扩展方法,这是从DAE文件加载几何时的情况。它还不使用copyByte函数。
extension SCNGeometry{
/**
Get the vertices (3d points coordinates) of the geometry.
- returns: An array of SCNVector3 containing the vertices of the geometry.
*/
func vertices() -> [SCNVector3]? {
let sources = self.sources(for: .vertex)
guard let source = sources.first else{return nil}
let stride = source.dataStride / source.bytesPerComponent
let offset = source.dataOffset / source.bytesPerComponent
let vectorCount = source.vectorCount
return source.data.withUnsafeBytes { (buffer : UnsafePointer<Float>) -> [SCNVector3] in
var result = Array<SCNVector3>()
for i in 0...vectorCount - 1 {
let start = i * stride + offset
let x = buffer[start]
let y = buffer[start + 1]
let z = buffer[start + 2]
result.append(SCNVector3(x, y, z))
}
return result
}
}
}
答案 4 :(得分:1)
Swift 3版本:
// `plane` is some kind of `SCNGeometry`
let planeSources = plane.geometry.sources(for: SCNGeometrySource.Semantic.vertex)
if let planeSource = planeSources.first {
let stride = planeSource.dataStride
let offset = planeSource.dataOffset
let componentsPerVector = planeSource.componentsPerVector
let bytesPerVector = componentsPerVector * planeSource.bytesPerComponent
let vectors = [SCNVector3](repeating: SCNVector3Zero, count: planeSource.vectorCount)
let vertices = vectors.enumerated().map({
(index: Int, element: SCNVector3) -> SCNVector3 in
let vectorData = UnsafeMutablePointer<Float>.allocate(capacity: componentsPerVector)
let nsByteRange = NSMakeRange(index * stride + offset, bytesPerVector)
let byteRange = Range(nsByteRange)
let buffer = UnsafeMutableBufferPointer(start: vectorData, count: componentsPerVector)
planeSource.data.copyBytes(to: buffer, from: byteRange)
let vector = SCNVector3Make(buffer[0], buffer[1], buffer[2])
})
// Use `vertices` here: vertices[0].x, vertices[0].y, vertices[0].z
}
答案 5 :(得分:1)
这是一个基于其他答案的 Swift 5.3 版本,它也支持与 bytesPerComponent
不同的 4
(但未测试与 4
不同的大小):
extension SCNGeometrySource {
var vertices: [SCNVector3] {
let stride = self.dataStride
let offset = self.dataOffset
let componentsPerVector = self.componentsPerVector
let bytesPerVector = componentsPerVector * self.bytesPerComponent
func vectorFromData<FloatingPoint: BinaryFloatingPoint>(_ float: FloatingPoint.Type, index: Int) -> SCNVector3 {
assert(bytesPerComponent == MemoryLayout<FloatingPoint>.size)
let vectorData = UnsafeMutablePointer<FloatingPoint>.allocate(capacity: componentsPerVector)
let buffer = UnsafeMutableBufferPointer(start: vectorData, count: componentsPerVector)
let rangeStart = index * stride + offset
self.data.copyBytes(to: buffer, from: rangeStart..<(rangeStart + bytesPerVector))
return SCNVector3(
CGFloat.NativeType(vectorData[0]),
CGFloat.NativeType(vectorData[1]),
CGFloat.NativeType(vectorData[2])
)
}
let vectors = [SCNVector3](repeating: SCNVector3Zero, count: self.vectorCount)
return vectors.indices.map { index -> SCNVector3 in
switch bytesPerComponent {
case 4:
return vectorFromData(Float32.self, index: index)
case 8:
return vectorFromData(Float64.self, index: index)
case 16:
return vectorFromData(Float80.self, index: index)
default:
return SCNVector3Zero
}
}
}
}
答案 6 :(得分:0)
对于像我这样的人,想从SCNGeometryElement
中提取面部数据。
请注意,我只考虑基本类型为三角形,索引大小为2或4。
void extractInfoFromGeoElement(NSString* scenePath){
NSURL *url = [NSURL fileURLWithPath:scenePath];
SCNScene *scene = [SCNScene sceneWithURL:url options:nil error:nil];
SCNGeometry *geo = scene.rootNode.childNodes.firstObject.geometry;
SCNGeometryElement *elem = geo.geometryElements.firstObject;
NSInteger componentOfPrimitive = (elem.primitiveType == SCNGeometryPrimitiveTypeTriangles) ? 3 : 0;
if (!componentOfPrimitive) {//TODO: Code deals with triangle primitive only
return;
}
for (int i=0; i<elem.primitiveCount; i++) {
void *idxsPtr = NULL;
int stride = 3*i;
if (elem.bytesPerIndex == 2) {
short *idxsShort = malloc(sizeof(short)*3);
idxsPtr = idxsShort;
}else if (elem.bytesPerIndex == 4){
int *idxsInt = malloc(sizeof(int)*3);
idxsPtr = idxsInt;
}else{
NSLog(@"unknow index type");
return;
}
[elem.data getBytes:idxsPtr range:NSMakeRange(stride*elem.bytesPerIndex, elem.bytesPerIndex*3)];
if (elem.bytesPerIndex == 2) {
NSLog(@"triangle %d : %d, %d, %d\n",i,*(short*)idxsPtr,*((short*)idxsPtr+1),*((short*)idxsPtr+2));
}else{
NSLog(@"triangle %d : %d, %d, %d\n",i,*(int*)idxsPtr,*((int*)idxsPtr+1),*((int*)idxsPtr+2));
}
//Free
free(idxsPtr);
}
}
答案 7 :(得分:0)
//调用此函数_ =顶点(节点:mySceneView.scene!.rootNode) //我已经在Swift 4.2中获得了音量:---此功能
String[] cmd = { "/bin/bash", "-c", "sledge connect --cluster_id=<name> --namespace=<name> --password=<pass>"};