前一篇文章讲述了几个刚接触SceneKit的三个小问题,今天准备稍微深刻一点。
在3D模型中,实际的项目中并不会像demo中的那样只有一个模型(飞机模型),如果有多个模型组合成的一个大模型,我们如何完成?
首先一个大模型包括数个小模型在加载的时候并不会有不同。今天讲述的主要是多个小模型点击以后的效果,即:视角切换的问题,刚刚找到了一个方法,这个方法比较笨,但是网上还没有相关的文档,所以只能如此解决了。

今天遇到的问题是:在大模型中,点击小模块,视角切近,这样的需求相信很多项目都会遇到,下面开始结合代码讲述。

第一步:加载大模型

//声明中间的3D背景view
    self.sceneView = [[QFscnView alloc]init];
    _sceneView.passthroughViews = @[self.backBtn];
    _sceneView.backgroundColor = QFClearColor;
    [self.view addSubview:_sceneView];
    _sceneView.sd_layout.leftSpaceToView(self.leftMiddle,10).topSpaceToView(self.topView,-30).rightSpaceToView(self.rightMiddle,10).bottomSpaceToView(self.bottomView,-60);
    //声明3D场景
    self.scenePlace = [SCNScene sceneNamed:@"scene.scnassets/dae_test.DAE"];
    _sceneView.scene = _scenePlace;

我上面是自定义的QFscnView,这个类似于前文穿透图片响应下层image事件 。由于模型下面有其它控件,而模型是可以缩放的,所以在模型放大的时候不至于影响观看效果,缩小后又可以触发其它控件的功能,所以才重新写了下这样的效果。
第一步补充:穿过模型响应模型view下面的其它控件的事件

-(UIView*) hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if(testHits){
        return nil;
    }

    if(!self.passthroughViews|| (self.passthroughViews && self.passthroughViews.count == 0)){
        return self;
    } else {

        UIView *hitView = [super hitTest:point withEvent:event];

        if (hitView == self) {
            //Test whether any of the passthrough views would handle this touch
            testHits = YES;
            CGPoint superPoint = [self.superview convertPoint:point fromView:self];
            UIView *superHitView = [self.superview hitTest:superPoint withEvent:event];
            testHits = NO;

            if ([self isPassthroughView:superHitView]) {
                hitView = superHitView;
            }
        }

        return hitView;
    }
}

- (BOOL)isPassthroughView:(UIView *)view {

    if (view == nil) {
        return NO;
    }

    if ([self.passthroughViews containsObject:view]) {
        return YES;
    }

    return [self isPassthroughView:view.superview];
}

第二步:给scnview添加手势

//添加手势
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
    NSMutableArray *gestureRecognizers = [NSMutableArray array];
    [gestureRecognizers addObject:tapGesture];
    [gestureRecognizers addObjectsFromArray:_sceneView.gestureRecognizers];
    _sceneView.gestureRecognizers = gestureRecognizers;
    //添加双击手势
    UITapGestureRecognizer *doubleTapGestureRecognizer = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(doubleTap:)];
    [doubleTapGestureRecognizer setNumberOfTapsRequired:2];
    [_sceneView addGestureRecognizer:doubleTapGestureRecognizer];

这两个方法想必大家都很熟悉,第一个方法是上文 SceneKit一个加载3D模型的神奇框架
中,也是demo中提到的一个手势,第二个是我添加的一个双击方法。

第三步:点击时根据获取的模型name去切换到响应的视角,双击恢复至初始状态

- (void) handleTap:(UIGestureRecognizer*)gestureRecognize
{
    //查找出点击的节点
    CGPoint hitPoint = [gestureRecognize locationInView:_sceneView];
    NSArray *hitResults = [_sceneView hitTest:hitPoint options:nil];

    // check that we clicked on at least one object
    if([hitResults count] > 0){
        //测试相机
        SCNNode *camera_origin = [_scenePlace.rootNode childNodeWithName:@"camera" recursively:YES];
        SCNNode *camera = [_scenePlace.rootNode childNodeWithName:@"oneModelCamera" recursively:YES];
        SCNNode *twoModelCamera = [_scenePlace.rootNode childNodeWithName:@"twoModelCamera" recursively:YES];
        SCNNode *threeModelCamera = [_scenePlace.rootNode childNodeWithName:@"threeModelCamera" recursively:YES];
        CGFloat positionX,positionY,positionZ,rotationX,rotationY,rotationZ,rotationW;
        if (camera.position.x == camera_origin.position.x) {
            positionX = twoModelCamera.position.x;
            positionY = twoModelCamera.position.y;
            positionZ = twoModelCamera.position.z;
            rotationX = twoModelCamera.rotation.x;
            rotationY = twoModelCamera.rotation.y;
            rotationZ = twoModelCamera.rotation.z;
            rotationW = twoModelCamera.rotation.w;
        }else if(camera_origin.position.x == twoModelCamera.position.x)
        {
            positionX = threeModelCamera.position.x;
            positionY = threeModelCamera.position.y;
            positionZ = threeModelCamera.position.z;
            rotationX = threeModelCamera.rotation.x;
            rotationY = threeModelCamera.rotation.y;
            rotationZ = threeModelCamera.rotation.z;
            rotationW = threeModelCamera.rotation.w;
        }else
        {
            positionX = camera.position.x;
            positionY = camera.position.y;
            positionZ = camera.position.z;
            rotationX = camera.rotation.x;
            rotationY = camera.rotation.y;
            rotationZ = camera.rotation.z;
            rotationW = camera.rotation.w;
        }

        camera_origin.position = SCNVector3Make(positionX, positionY, positionZ);
        camera_origin.rotation = SCNVector4Make(rotationX, rotationY, rotationZ, rotationW);

    }
}
-(void)doubleTap:(UIGestureRecognizer*)gesture
{
    //打印scnview的位置并设定为初始的状态
    NSLog(@"Camera position: %f %f %f", _sceneView.pointOfView.position.x, _sceneView.pointOfView.position.y, _sceneView.pointOfView.position.z);
    NSLog(@"Camera rotation: %f %f %f %f", _sceneView.pointOfView.rotation.x, _sceneView.pointOfView.rotation.y, _sceneView.pointOfView.rotation.z, _sceneView.pointOfView.rotation.w);
    NSLog(@"Camera orientat: %f %f %f %f", _sceneView.pointOfView.orientation.x, _sceneView.pointOfView.orientation.y, _sceneView.pointOfView.orientation.z,_sceneView.pointOfView.orientation.w);

    //声明3D场景
    self.scenePlace = [SCNScene sceneNamed:@"scene.scnassets/dae_test.DAE"];
    _sceneView.scene = _scenePlace;
    _sceneView.allowsCameraControl = YES;

}

我上面的方法中的if else语句只是为了在这里给大家列举demo随便写的,大家在项目中可以根据点击方法中获取的小模型name确定某一个模型,再确定需要转换的position和rotation。

最后:关于模型中相机位置是如何获取的,这里简单再补充下,其实我们再dae文件中就可以添加并编辑(移动、修改)相机。然后在代码中打印出position,由于我这边美工给的模型坐标系跟xcode默认的坐标系不一样,所以拖拽的position跟代码打印出来的是不同的,所以我这里需要先打印下你的相机位置,再给它设置为打印所得的position。

到此就可以结束了。如果谁有更好的办法,希望可以多多交流。