前一篇文章讲述了几个刚接触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。
到此就可以结束了。如果谁有更好的办法,希望可以多多交流。