听起来你有两个问题。(
甚至没有使用正则表达式。 :))
首先,你需要获取点击点的纹理坐标——也就是物体表面上2D纹理空间中的点。你已经接近正确了。
SCNHitTestResult
通过
textureCoordinatesWithMappingChannel
方法提供这些内容。(你正在使用
localCoordinates
,它会给你一个在命中测试结果中节点拥有的3D空间中的点。)而且你似乎已经找到了关于映射通道的业务,所以你知道要传递什么参数给该方法。
问题#2是如何绘制。
您正在以获取材料内容作为
UIImage
的正确方式。一旦你得到了这个,你可以尝试使用
UIGraphics
和
CGContext
函数进行绘制--使用
UIGraphicsBeginImageContext
创建一个图像,在其中绘制现有图像,然后在点击点上绘制任何新内容。之后,您可以通过
UIGraphicsGetImageFromCurrentImageContext
获取您正在绘制的图像,并将其设置为您的材料的新
diffuse.contents
。但是,那可能不是最好的方式--您需要在CPU上搬运一堆图像数据,并且代码也有点笨重。
更好的方法可能是利用SceneKit和SpriteKit之间的集成。这样,您所有的2D绘制都发生在与3D绘制相同的GPU上下文中--而且代码也更简洁。
你可以将材质的
diffuse.contents
设置为SpriteKit场景。(要使用当前用作纹理的
UIImage
,只需将其放在填充场景的
SKSpriteNode
上。)一旦获得纹理坐标,就可以在该点向场景中添加一个精灵。
var nodeToDrawOn: SCNNode!
var skScene: SKScene!
func mySetup() {
nodeToDrawOn = myScene.rootNode.childNodeWithName("ID12", recursively: true)
let currentImage = nodeToDrawOn.geometry!.firstMaterial!.diffuse.contents as UIImage
skScene = SKScene(size: currentImage.size)
nodeToDrawOn.geometry!.firstMaterial!.diffuse.contents = skScene
let background = SKSpriteNode(texture: SKTexture(image: currentImage))
background.position = CGPoint(x: skScene.frame.midX, y: skScene.frame.midY)
skScene.addChild(background)
}
@IBAction func tap(sender: UITapGestureRecognizer) {
let results = my3dView.hitTest(sender.locationInView(my3dView), options: [SCNHitTestFirstFoundOnlyKey: true]) as [SCNHitTestResult]
if let result = results.first {
if result.node === nodeToDrawOn {
let channel = nodeToDrawOn.geometry!.firstMaterial!.diffuse.mappingChannel
let texcoord = result.textureCoordinatesWithMappingChannel(channel)
let sprite = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width: 10, height: 10))
sprite.position.x = texcoord.x * skScene.size.width
sprite.position.y = texcoord.y * skScene.size.height
skScene.addChild(sprite)
}
}
}
如需了解有关SpriteKit方法(使用Objective-C)的更多详细信息,请参见WWDC14的SceneKit State of the Union Demo。该演示展示了一个SpriteKit场景用作环面体的纹理映射,带有被抛向它的油漆球体 - 每当一个球体与环面体碰撞时,它会获得一个SCNHitTestResult
并使用其纹理坐标在SpriteKit场景中创建油漆飞溅。
最后,关于您的代码的Swift风格注释(与问题和答案无关):
- 在不需要重新分配值的情况下,请使用
let
而不是var
,优化器会使您的代码更快。
- 显式类型注释(
res: SCNHitTestResult
)很少必要。
- Swift字典被桥接到
NSDictionary
,因此您可以直接将它们传递给接受NSDictionary
的API。
- 转换为Swift类型数组(
hitTest(...) as [SCNHitTestResult]
)可以避免您必须转换内容。