2014-08-31 84 views
3

我想操縱3D SceneKit場景中的2D紋理。 所以我用這個代碼來獲取本地座標:SceneKit在用Swift觸摸後得到紋理座標

@IBAction func tap(sender: UITapGestureRecognizer) { 
    var arr:NSArray = my3dView.hitTest(sender.locationInView(my3dView), options: NSDictionary(dictionary: [SCNHitTestFirstFoundOnlyKey:true])) 
    var res:SCNHitTestResult = arr.firstObject as SCNHitTestResult 

    var vect:SCNVector3 = res.localCoordinates} 

我有紋理從我的場景中讀出:

var mat:SCNNode = myscene.rootNode.childNodes[0] as SCNNode 
    var child:SCNNode = mat.childNodeWithName("ID12", recursively: false) 
    var geo:SCNMaterial = child.geometry.firstMaterial 
    var channel = geo.diffuse.mappingChannel   
    var textureimg:UIImage = geo.diffuse.contents as UIImage 

,現在我想在接觸點紋理繪製.. 我該怎麼做?我如何將座標從觸摸轉換爲紋理圖像?

回答

6

聽起來像你有兩個問題。 (Without even having used regular expressions. :)

首先,您需要獲取點擊點的紋理座標 - 即點對象表面上的二維紋理空間。你幾乎已經有了這個權利。 SCNHitTestResult爲那些提供了textureCoordinatesWithMappingChannel方法。 (您正在使用localCoordinates,它可讓您在命中測試結果中的節點所擁有的3D空間中獲得一個點)。您似乎已經找到了關於映射頻道的業務,因此您知道要傳遞給該方法的內容。

問題2是如何繪製。

您正在做正確的事情來獲取材料的內容爲UIImage。一旦你有了這些,你可以看看UIGraphicsCGContext函數的繪圖 - 創建一個圖像與UIGraphicsBeginImageContext,繪製現有的圖像,然後繪製任何新的內容,你想添加在點擊點。之後,您可以使用UIGraphicsGetImageFromCurrentImageContext獲取您正在繪製的圖像,並將其設置爲您的素材的新diffuse.contents。但是,這可能不是最好的方法 - 你在CPU上掃描一堆圖像數據,而且代碼也有點笨拙。

更好的方法可能是利用SceneKit和SpriteKit之間的集成。這樣,所有的2D繪圖都在與3D繪圖相同的GPU上下文中進行 - 代碼更簡單一些。

您可以將素材的diffuse.contents設置爲SpriteKit場景。 (要使用目前用於該紋理的UIImage,只需將其粘貼在填充場景的SKSpriteNode上。)獲得紋理座標後,可以在該點添加一個精靈到場景中。

var nodeToDrawOn: SCNNode! 
var skScene: SKScene! 

func mySetup() { // or viewDidLoad, or wherever you do setup 
    // whatever else you're doing for setup, plus: 

    // 1. remember which node we want to draw on 
    nodeToDrawOn = myScene.rootNode.childNodeWithName("ID12", recursively: true) 

    // 2. set up that node's texture as a SpriteKit scene 
    let currentImage = nodeToDrawOn.geometry!.firstMaterial!.diffuse.contents as UIImage 
    skScene = SKScene(size: currentImage.size) 
    nodeToDrawOn.geometry!.firstMaterial!.diffuse.contents = skScene 

    // 3. put the currentImage into a background sprite for the 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 { 
      // 1. get the texture coordinates 
      let channel = nodeToDrawOn.geometry!.firstMaterial!.diffuse.mappingChannel 
      let texcoord = result.textureCoordinatesWithMappingChannel(channel) 

      // 2. place a sprite there 
      let sprite = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width: 10, height: 10)) 
      // scale coords: texcoords go 0.0-1.0, skScene space is is pixels 
      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並使用它的texcoords在SpriteKit場景中創建一個油漆飛濺。


最後,在你的代碼中雨燕風格的註釋(無關的問題和答案):

  • 使用let而不是var無論你不需要重新分配一個值,並且優化會讓你的代碼更快。
  • 顯式類型註釋(res: SCNHitTestResult)很少需要。
  • Swift詞典橋接到NSDictionary,因此您可以直接將它們傳遞給需要NSDictionary的API。
  • 投射到一個Swift類型的數組(hitTest(...) as [SCNHitTestResult])使您不必投射內容。
+0

Thx!我總是爲映射通道-1。 – Philsen 2014-09-03 18:50:09

+0

我在測試中看到0。你在分配「SKScene」作爲漫反射材料之前還是之後閱讀'mappingChannel'屬性?你使用的紋理座標是幾何嗎? – rickster 2014-09-03 20:26:24

+0

不,我以後做。我怎樣才能確定幾何有紋理座標。 – Philsen 2014-09-04 20:46:36