【问题标题】:Get "up" side of SCNNode from Orientation从 Orientation 获取 SCNNode 的“向上”侧
【发布时间】:2014-07-29 10:34:50
【问题描述】:

我在 SCNScene 中有一个 SCNBox。一旦场景动画化,SCNBox 就会改变方向,可以通过检查其presentationNode.orientation 来查看。此值在 SCNVector4 中返回。如何根据 SCNVector4 中返回的值确定 SCNBox 的哪一侧是朝上的一侧?

我尝试对数据进行规范化并得出以下数据

Side 1 = (-x, 0, 0, +x)
Side 2 = (0, -x, 0 +y)
Side 3 = (-x, +x, -y, y)
Side 4 = (x, x, y, y)
Side 5 = (-x, 0, +y, 0)
Side 6 = (-x, -y, +y, -x) 

不幸的是,这并不总是正确的,检查它有时会偶尔返回无效的边。

是否有可靠的方法可以根据几何的方向属性确定SCNBox 的朝上侧?

编辑: 根据Toyos的回答,我想出了以下代码,但它不起作用。希望这将有助于更接近最终目标。我还使用了Extracting vertices from scenekit 的代码来获取我的 SCNBoxNode 的顶点。

- (NSNumber *)valueForRotation:(SCNVector4)rotation andGeometry:(SCNGeometry*)geometry {
    SCNVector4 inverse = SCNVector4Make(rotation.x, rotation.y, rotation.z, -rotation.w);

    CATransform3D transform = CATransform3DMakeRotation(inverse.w, inverse.x, inverse.y, inverse.z);

    GLKMatrix4 matrix = GLKMatrix4Make(transform.m11, transform.m12, transform.m13, transform.m14, transform.m21, transform.m22, transform.m23, transform.m24, transform.m31, transform.m32, transform.m33, transform.m34, transform.m41, transform.m42, transform.m43, transform.m44);

    GLKVector4 vector = GLKVector4Make(rotation.x, rotation.y, rotation.z, rotation.w);

    GLKVector4 finalVector = GLKMatrix4MultiplyVector4(matrix, vector);

    NSArray *vertexSources = [geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticVertex];

    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
    NSLog(@"vetor count %i",vectorCount);
    float highestProduct = 0;
    int highestVector = -1;
    NSMutableArray *highVectors;
    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);
        float product = (x * finalVector.x) + (y * finalVector.y) +  (z * finalVector.z);
        if (product > highestProduct) {
            highestProduct = product;
            highestVector = i;
        }

    }

    NSLog(@"highestProduct = %f",highestProduct);
    NSLog(@"highestVector = %i",highestVector);
    NSLog(@"top verticy = %f, %f, %f",vertices[highestVector].x,vertices[highestVector].y,vertices[highestVector].z);

    return [NSNumber numberWithInt:highestVector];
}

【问题讨论】:

    标签: ios objective-c scenekit


    【解决方案1】:

    这是一个返回面朝上的人脸索引的方法。它假设“boxNode”是一个由 6 个面组成的盒子,具有以下(任意)顺序:前/右/后/左/上/下。它返回面朝上的人脸的索引。 然后不要忘记导入。 对于任意网格,您必须使用面法线而不是“boxNormals”(这对于计算并不明显,因为 SceneKit 网格每个顶点有一个法线,而不是每个面一个法线,因此您必须计算每个面的法线自己)。

    - (NSUInteger) boxUpIndex:(SCNNode *)boxNode
    {
        SCNVector4 rotation = boxNode.rotation;
        SCNVector4 invRotation = rotation; invRotation.w = -invRotation.w;
    
        SCNVector3 up = SCNVector3Make(0,1,0);
    
        //rotate up by invRotation
        SCNMatrix4 transform = SCNMatrix4MakeRotation(invRotation.w, invRotation.x, invRotation.y, invRotation.z);
        GLKMatrix4 glkTransform = SCNMatrix4ToGLKMatrix4(transform);
        GLKVector3 glkUp = SCNVector3ToGLKVector3(up);
        GLKVector3 rotatedUp = GLKMatrix4MultiplyVector3(glkTransform, glkUp);
    
        //build box normals (arbitrary order here)
        GLKVector3 boxNormals[6] = {{{0,0,1}},
            {{1,0,0}},
            {{0,0,-1}},
            {{-1,0,0}},
            {{0,1,0}},
            {{0,-1,0}},
        };
    
        int bestIndex = 0;
        float maxDot = -1;
    
        for(int i=0; i<6; i++){
            float dot = GLKVector3DotProduct(boxNormals[i], rotatedUp);
            if(dot > maxDot){
                maxDot = dot;
                bestIndex = i;
            }
        }
    
        return bestIndex;
    }
    

    【讨论】:

    • 这不起作用,每边返回 4。这就是我创建节点的方式,也许我在这里做错了什么? SCNNode *block = [SCNNode node]; block.position = SCNVector3Make(20, 0, 0); block.rotation = SCNVector4Make(0, 0, 0, 0); block.geometry = [SCNBox boxWithWidth:8 height:8 length:8 chamferRadius:0]; SCNPhysicsBody *body = [SCNPhysicsBody dynamicBody]; body.mass = 5; body.restitution = .7; body.friction = 0.5; block.physicsBody = body; [[scene rootNode] addChildNode:block];
    • 编辑:想通了,我需要将boxNode.rotation 更改为boxNode.presentationNode.node。非常感谢!我什至没有接近我的代码:(
    【解决方案2】:

    node.orientation 会给你一个四元数。你也可以使用 node.eulerAngles 或 node.rotation (轴角),如果这样可以让你的数学更容易。

    在所有情况下,我认为您需要将反向旋转应用于向量 [0, 1, 0](向上向量)。结果向量 (V) 将为您提供向上的方向。 然后要找到朝上的面,比较盒子面和 V 的点积。最高点积是朝上的面。

    (另一种方法是旋转盒子的面并使用 [0,1,0] 进行点积。)

    【讨论】:

    • 您能否提供更多关于我如何“将反向旋转应用于矢量”的信息?
    • 最简单的是轴角(node.rotation)。 [x, y, z, angle]的逆旋转为[x, y, z, -angle]。然后将此旋转应用于向量首先构建旋转矩阵(通过使用GLKit,SIMD或CATransform3DMakeRotation(角度,x,y,z)。一旦你有了你的矩阵,通过将向量与矩阵相乘来用这个矩阵变换向量(这里最好使用 GLKit 或 SIMD,因为 CA 没有内置函数)。请注意,SceneKit 提供了将 SCNVector3 转换为 GLKit、CA 或 SIMD 的实用函数)
    • 老实说,我仍然完全迷失了方向。但是,你清楚地知道你在说什么,所以你的答案 +1。感谢您花时间为我进一步解释。
    • 另外,用我当前的代码进度对我的问题添加了一个编辑(或者我猜,没有。)
    • @Toyos:如果你能帮助我解决类似的情况,我将不胜感激。我已经使用您的代码让脸朝上,但是,它不起作用。 stackoverflow.com/questions/62155439/…
    猜你喜欢
    • 2020-09-21
    • 2015-01-05
    • 2019-03-17
    • 2017-04-15
    • 1970-01-01
    • 2016-01-18
    • 2018-03-19
    • 2018-08-24
    • 2017-12-12
    相关资源
    最近更新 更多