Offset SCNPlane mesh geometry

Hello,


I have a sophisticated task: I need to offset vertices of SCNPlane instance. Approaches like the following do not feet my needs:


- Changing position or pivot point of host SCNNode

- Using shader modifier or vertex shader where each individual vertex position is shiftted, simulating offset effect


I came to a conclusion that geometry data needs to be preprocessed. SCNPlane verticies need to be iterated and offset. I would be really gladful if someone exaplins how to fetch vertex, normal and texture coords data from already made SCNPlane instance and modify them.



Upd. 0

As it turned out, data cannot be modified in existing SCNPlane. As an alternative option it can be copied, then offset and created new SCNPlane.

Accepted Reply

I figured out how to approach the task. This might be helpful for some developers:

// 1.Perform the following instructions for each available geometry source.
//   In many cases there will be just a singe geometry source. That is why and for the sake of simplicity,
//   the sample code demonstrates work with singe source.
//   FYI: you might think why there might be several geometry sources. Short answer is - performance. The other one -
//.  geometry object composed from several independent pieces.
SCNGeometrySource *vertexSource = sources.firstObject;
// 2. The number of bytes from a vector to the next one in the data
NSInteger stride = vertexSource.dataStride;
// 3. The offset, in bytes, from the beginning of the data to the first vector component to be used in the geometry source
NSInteger offset = vertexSource.dataOffset;
// 4. Get the number of scalar components per vector
NSInteger componentsPerVector = vertexSource.componentsPerVector;
// 5. Compute the number of bytes per vector
NSInteger bytesPerVector = componentsPerVector * vertexSource.bytesPerComponent;
// 6. Get the total number of vectors in vertex data
NSInteger vectorCount = vertexSource.vectorCount;
// 7. Used as a storage of fetched vectors
SCNVector3 vertices[vectorCount];
// 8. Read the underlying bytes for each singe vector
for (NSInteger i=0; i<vectorCount; i++) {

     // 9. Temp storage for vertex data. Please note that "float" takes 4 bytes and is most likely used a founamental data type for vertex component.
     //    However there might be cases when data type should be explicitly fetched. Be careful here.
     float vectorData[componentsPerVector];
  
     // 10. Form a range of bytes that will be compied from vertex source
     NSRange byteRange = NSMakeRange(i * stride + offset, bytesPerVector); 
  
     // 11. Copy vector data from a specified range
     [vertexSource.data getBytes:&vectorData range:byteRange];
  
     // 12. Access each vertex component individually
     float x = vectorData[0];
     float y = vectorData[1];
     float z = vectorData[2];
     // 12,1. Answering my own question: the vertex offset can be easily perfomed right here, before storing vertex.
     //.      Basically each vertex component (x, y, z) can be modified as you like. In my case I used some algorithm
     //.      to modify surface of the geometry before it will be used by shaders. Sometimes it is hard to do some sort of
     //.      geometry manipulations using only shaders. That is you such sort of programmatic preprocessing may be extrimely useful.
     //       As an example, you may change the pivot point of you geometry and then, perform geometry deformations (which involve rotations) using geometry shader modifiers.
   
     // 13. Store the vector in array
     vertices[i] = vector;
     // 13,1. Remember that only vertex data was stored and preprocessed.
     //.      There typically are normal and texture coordinates data that need to be considered as well.
}

Source code can be easily converted into the latest version of Swift. Basically this is the answer on my own question.

Replies

I figured out how to approach the task. This might be helpful for some developers:

// 1.Perform the following instructions for each available geometry source.
//   In many cases there will be just a singe geometry source. That is why and for the sake of simplicity,
//   the sample code demonstrates work with singe source.
//   FYI: you might think why there might be several geometry sources. Short answer is - performance. The other one -
//.  geometry object composed from several independent pieces.
SCNGeometrySource *vertexSource = sources.firstObject;
// 2. The number of bytes from a vector to the next one in the data
NSInteger stride = vertexSource.dataStride;
// 3. The offset, in bytes, from the beginning of the data to the first vector component to be used in the geometry source
NSInteger offset = vertexSource.dataOffset;
// 4. Get the number of scalar components per vector
NSInteger componentsPerVector = vertexSource.componentsPerVector;
// 5. Compute the number of bytes per vector
NSInteger bytesPerVector = componentsPerVector * vertexSource.bytesPerComponent;
// 6. Get the total number of vectors in vertex data
NSInteger vectorCount = vertexSource.vectorCount;
// 7. Used as a storage of fetched vectors
SCNVector3 vertices[vectorCount];
// 8. Read the underlying bytes for each singe vector
for (NSInteger i=0; i<vectorCount; i++) {

     // 9. Temp storage for vertex data. Please note that "float" takes 4 bytes and is most likely used a founamental data type for vertex component.
     //    However there might be cases when data type should be explicitly fetched. Be careful here.
     float vectorData[componentsPerVector];
  
     // 10. Form a range of bytes that will be compied from vertex source
     NSRange byteRange = NSMakeRange(i * stride + offset, bytesPerVector); 
  
     // 11. Copy vector data from a specified range
     [vertexSource.data getBytes:&vectorData range:byteRange];
  
     // 12. Access each vertex component individually
     float x = vectorData[0];
     float y = vectorData[1];
     float z = vectorData[2];
     // 12,1. Answering my own question: the vertex offset can be easily perfomed right here, before storing vertex.
     //.      Basically each vertex component (x, y, z) can be modified as you like. In my case I used some algorithm
     //.      to modify surface of the geometry before it will be used by shaders. Sometimes it is hard to do some sort of
     //.      geometry manipulations using only shaders. That is you such sort of programmatic preprocessing may be extrimely useful.
     //       As an example, you may change the pivot point of you geometry and then, perform geometry deformations (which involve rotations) using geometry shader modifiers.
   
     // 13. Store the vector in array
     vertices[i] = vector;
     // 13,1. Remember that only vertex data was stored and preprocessed.
     //.      There typically are normal and texture coordinates data that need to be considered as well.
}

Source code can be easily converted into the latest version of Swift. Basically this is the answer on my own question.