如何在SCNScene

时间:2016-03-23 10:52:13

标签: swift sprite-kit overlay scenekit

我正在处理的应用程序应该显示一个3D对象,用户可以选择一种颜色来为它着色。我有一个带有多个网格的SCNScene,可以创建一个3D模型。我需要构建一个带有颜色的侧面交互式面板,用户可以使用它来为3D模型着色。代码为here on github

我告诉你我的代码(目前只在一个班级,我知道这很糟糕)

import UIKit
import QuartzCore
import SceneKit
import SpriteKit

class GameViewController: UIViewController {

var cameraOrbit = SCNNode()
let cameraNode = SCNNode()
let camera = SCNCamera()

let floorNode = SCNNode()
var wallNode = SCNNode()
var lateralWallRight = SCNNode()
var lateralWallLeft = SCNNode()


var spotLightNode = SCNNode()


//HANDLE PAN CAMERA
var initialPositionCamera = SCNVector3(x: -25, y: 70, z: 1450)
var translateEnabled = false
var lastXPos:Float = 0.0
var lastYPos:Float = 0.0
var xPos:Float = 0.0
var yPos:Float = 0.0
var lastWidthRatio: Float = 0
var lastHeightRatio: Float = 0.1
var widthRatio: Float = 0
var heightRatio: Float = 0.1
var fingersNeededToPan = 1 //change this from GUI
var panAttenuation: Float = 10 //5.0: very fast ---- 40.0 very slow
let maxWidthRatioRight: Float = 0.2
let maxWidthRatioLeft: Float = -0.2
let maxHeightRatioXDown: Float = 0.065
let maxHeightRatioXUp: Float = 0.4

//HANDLE PINCH CAMERA
var pinchAttenuation = 1.0  //1.0: very fast ---- 100.0 very slow
var lastFingersNumber = 0
let maxPinch = 146.0
let minPinch = 40.0

//OVERLAY
var colorPanelScene = SKScene()
var pickedColor: UIColor = UIColor.whiteColor()
var NodesToColors = [SKSpriteNode: UIColor]()
var didPickColor = false
var OverlayBackground: SKSpriteNode = SKSpriteNode()

func setColors() {
    //Color Setup
    let ColorWhite = colorPanelScene.childNodeWithName("ColorWhite") as! SKSpriteNode
    let ColorRed = colorPanelScene.childNodeWithName("ColorRed") as! SKSpriteNode
    let ColorBrown = colorPanelScene.childNodeWithName("ColorBrown")as! SKSpriteNode
    let ColorDarkBrown = colorPanelScene.childNodeWithName("ColorDarkBrown")as! SKSpriteNode


    let white = UIColor(red:1, green:0.95, blue:0.71, alpha:1)
    let brown = UIColor(red:0.49, green:0.26, blue:0.17, alpha:1)
    let red = UIColor(red:0.67, green:0.32, blue:0.21, alpha:1)
    let darkBrown = UIColor(red:0.27, green:0.25, blue:0.21, alpha:1)

    NodesToColors = [
        ColorWhite: white,
        ColorRed: red,
        ColorBrown: brown,
        ColorDarkBrown: darkBrown
    ]

    OverlayBackground = colorPanelScene.childNodeWithName("OverlayBackground")as! SKSpriteNode
}

func blur(image image: UIImage) -> UIImage {
    let radius: CGFloat = 20;
    let context = CIContext(options: nil);
    let inputImage = CIImage(CGImage: image.CGImage!);
    let filter = CIFilter(name: "CIGaussianBlur");
    filter?.setValue(inputImage, forKey: kCIInputImageKey);
    filter?.setValue("\(radius)", forKey:kCIInputRadiusKey);
    let result = filter?.valueForKey(kCIOutputImageKey) as! CIImage;
    let rect = CGRectMake(radius * 2, radius * 2, image.size.width - radius * 4, image.size.height - radius * 4)
    let cgImage = context.createCGImage(result, fromRect: rect);
    let returnImage = UIImage(CGImage: cgImage);

    return returnImage;
}

override func viewDidLoad() {
    super.viewDidLoad()

    // create a new scene
    let scene = SCNScene(named: "art.scnassets/Figure.scn")!

    // MARK: Lights
    //create and add a light to the scene
    let lightNode = SCNNode()
    lightNode.light = SCNLight()
    lightNode.light!.type = SCNLightTypeOmni
    lightNode.position = SCNVector3(x: 0, y: 1000, z: 1000)
    scene.rootNode.addChildNode(lightNode)

    // create and add an ambient light to the scene
    let ambientLightNode = SCNNode()
    ambientLightNode.light = SCNLight()
    ambientLightNode.light!.type = SCNLightTypeAmbient
    ambientLightNode.light!.color = UIColor.darkGrayColor()
    scene.rootNode.addChildNode(ambientLightNode)

    //MARK: Camera
    camera.usesOrthographicProjection = true
    camera.orthographicScale = 100
    camera.zNear = 10
    camera.zFar = 3000

    cameraNode.position = initialPositionCamera
    cameraNode.camera = camera
    cameraOrbit = SCNNode()
    cameraOrbit.addChildNode(cameraNode)
    scene.rootNode.addChildNode(cameraOrbit)

    //initial camera setup
    self.cameraOrbit.eulerAngles.y = Float(-2 * M_PI) * lastWidthRatio
    self.cameraOrbit.eulerAngles.x = Float(-M_PI) * lastHeightRatio
    lastXPos = self.cameraNode.position.x
    lastYPos = self.cameraNode.position.y

    //MARK: Floor
    let floor = SCNFloor()
    floor.reflectionFalloffEnd = 0
    floor.reflectivity = 0

    floorNode.geometry = floor
    floorNode.name = "Floor"
    floorNode.geometry!.firstMaterial!.diffuse.contents = "art.scnassets/floor.png"
    floorNode.geometry!.firstMaterial!.locksAmbientWithDiffuse = true
    floorNode.geometry!.firstMaterial!.diffuse.wrapS = SCNWrapMode.Repeat
    floorNode.geometry!.firstMaterial!.diffuse.wrapT = SCNWrapMode.Repeat
    floorNode.geometry!.firstMaterial!.diffuse.mipFilter =  SCNFilterMode.Nearest
    floorNode.geometry!.firstMaterial!.doubleSided = false
    floorNode.castsShadow = true

    scene.rootNode.addChildNode(floorNode)

    //MARK: Walls
    // create the wall geometry
    let wallGeometry = SCNPlane.init(width: 500.0, height: 300.0)
    wallGeometry.firstMaterial!.diffuse.contents = "art.scnassets/background.jpg"
    wallGeometry.firstMaterial!.diffuse.mipFilter =  SCNFilterMode.Nearest
    wallGeometry.firstMaterial!.diffuse.wrapS = SCNWrapMode.Repeat
    wallGeometry.firstMaterial!.diffuse.wrapT = SCNWrapMode.Repeat
    wallGeometry.firstMaterial!.doubleSided = false
    wallGeometry.firstMaterial!.locksAmbientWithDiffuse = true

    wallNode = SCNNode.init(geometry: wallGeometry)
    wallNode.name = "FrontWall"
    wallNode.position = SCNVector3Make(0, 120, -300) //this moves all 3 walls
    wallNode.castsShadow = true

    //  RIGHT LATERAL WALL
    lateralWallRight = SCNNode.init(geometry: wallGeometry)
    lateralWallRight.name = "lateralWallRight"
    lateralWallRight.position = SCNVector3Make(-300, -20, 150);
    lateralWallRight.rotation = SCNVector4(x: 0, y: 1, z: 0, w: Float(M_PI/3))
    lateralWallRight.castsShadow = true
    wallNode.addChildNode(lateralWallRight)

    // LEFT LATERAL WALL
    lateralWallLeft = SCNNode.init(geometry: wallGeometry)
    lateralWallLeft.name = "lateralWallLeft"
    lateralWallLeft.position = SCNVector3Make(300, -20, 150);
    lateralWallLeft.rotation = SCNVector4(x: 0, y: -1, z: 0, w: Float(M_PI/3))
    lateralWallLeft.castsShadow = true
    wallNode.addChildNode(lateralWallLeft)

    //front walls
    scene.rootNode.addChildNode(wallNode)

    // retrieve the SCNView
    let scnView = self.view as! SCNView

    // set the scene to the view
    scnView.scene = scene

    // allows the user to manipulate the camera
    scnView.allowsCameraControl = false  //not needed

    // configure the view
    scnView.backgroundColor = UIColor.grayColor()


    //MARK: Gesture Recognizer in SceneView

    // add a pan gesture recognizer
    let panGesture = UIPanGestureRecognizer(target: self, action: #selector(GameViewController.handlePan(_:)))
    scnView.addGestureRecognizer(panGesture)

    // add a tap gesture recognizer
    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(GameViewController.handleTap(_:)))
    scnView.addGestureRecognizer(tapGesture)

    // add a pinch gesture recognizer
    let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(GameViewController.handlePinch(_:)))
    scnView.addGestureRecognizer(pinchGesture)

    //MARK: OverLay

    colorPanelScene = SKScene(fileNamed: "art.scnassets/ColorPanelScene")!
    scnView.overlaySKScene = colorPanelScene
    scnView.overlaySKScene!.userInteractionEnabled = true;

    didPickColor = false

    setColors()

    //let OverlayBackground = colorPanelScene.childNodeWithName("OverlayBackground")as! SKSpriteNode

}

func handlePan(gestureRecognize: UIPanGestureRecognizer) {

    let numberOfTouches = gestureRecognize.numberOfTouches()

    let translation = gestureRecognize.translationInView(gestureRecognize.view!)


    if (numberOfTouches==fingersNeededToPan) {

        widthRatio = Float(translation.x) / Float(gestureRecognize.view!.frame.size.width) + lastWidthRatio
        heightRatio = Float(translation.y) / Float(gestureRecognize.view!.frame.size.height) + lastHeightRatio

        //  HEIGHT constraints
        if (heightRatio >= maxHeightRatioXUp ) {
            heightRatio = maxHeightRatioXUp
        }
        if (heightRatio <= maxHeightRatioXDown ) {
            heightRatio = maxHeightRatioXDown
        }


        //  WIDTH constraints
        if(widthRatio >= maxWidthRatioRight) {
            widthRatio = maxWidthRatioRight
        }
        if(widthRatio <= maxWidthRatioLeft) {
            widthRatio = maxWidthRatioLeft
        }

        self.cameraOrbit.eulerAngles.y = Float(-2 * M_PI) * widthRatio
        self.cameraOrbit.eulerAngles.x = Float(-M_PI) * heightRatio
        lastFingersNumber = fingersNeededToPan

        //TRANSLATION pan
    } else if numberOfTouches == (fingersNeededToPan+1) {

        if translateEnabled {
            xPos = (lastXPos + Float(-translation.x))/(panAttenuation)
            yPos = (lastYPos + Float(translation.y))/(panAttenuation)

            self.cameraNode.position.x = xPos
            self.cameraNode.position.y = yPos
        }

        lastFingersNumber = fingersNeededToPan+1

    }

    if (lastFingersNumber == fingersNeededToPan && numberOfTouches != fingersNeededToPan) {
        lastWidthRatio = widthRatio
        lastHeightRatio = heightRatio

    }

    if lastFingersNumber != (fingersNeededToPan+1) && numberOfTouches != (fingersNeededToPan+1) {
        lastXPos = xPos
        lastYPos = yPos
    }

    if (gestureRecognize.state == .Ended) {
        if (lastFingersNumber==fingersNeededToPan) {
            lastWidthRatio = widthRatio
            lastHeightRatio = heightRatio
            //print("lastHeight: \(round(lastHeightRatio*100))")
            //print("lastWidth: \(round(lastWidthRatio*100))")

        }

        if lastFingersNumber==(fingersNeededToPan+1) {
            lastXPos = xPos
            lastYPos = yPos
            print("lastX: \(xPos)")
            print("lastY: \(yPos)")
        }

        print("Pan with \(lastFingersNumber) finger\(lastFingersNumber>1 ? "s" : "")")
    }
}

func handlePinch(gestureRecognize: UIPinchGestureRecognizer) {
    let pinchVelocity = Double.init(gestureRecognize.velocity)
    //print("PinchVelocity \(pinchVelocity)")

    camera.orthographicScale -= (pinchVelocity/pinchAttenuation)

    if camera.orthographicScale <= minPinch {
        camera.orthographicScale = minPinch
    }

    if camera.orthographicScale >= maxPinch {
        camera.orthographicScale = maxPinch
    }

    if (gestureRecognize.state == .Ended) {
        print("\nPinch: \(round(camera.orthographicScale))\n")
    }

}

func handleTap(gestureRecognize: UIGestureRecognizer) {
    print("---------------TAP-----------------")
    // retrieve the SCNView
    let scnView = self.view as! SCNView

    let touchedPointInScene = gestureRecognize.locationInView(scnView)
    let hitResults = scnView.hitTest(touchedPointInScene, options: nil)

    let OverlayView = colorPanelScene.view! as SKView
    let touchedPointInOverlay = gestureRecognize.locationInView(OverlayView)


    // if button color are touched
    if OverlayBackground.containsPoint(touchedPointInOverlay) {
        print("OVERLAY: tap in \(touchedPointInOverlay)")

        for (node, color) in NodesToColors {
            // Check if the location of the touch is within the button's bounds
            if node.containsPoint(touchedPointInOverlay) {
                print("\(node.name!) -> color picked \(color.description)")
                pickedColor = color
                didPickColor = true
            }
        }
    } else {//if figure is touched

        // check that we clicked on at least one object
        if hitResults.count > 0 && didPickColor {
            // retrieved the first clicked object
            let result: AnyObject! = hitResults[0]

            print("OBJECT tap: \(result.node.name!)")

            //Exclude floor and wall from color
            if result.node! != floorNode && result.node! != wallNode && result.node! != lateralWallRight && result.node! != lateralWallLeft {
                // get its material
                let material = result.node!.geometry!.firstMaterial!
                print("material: \(material.name!)")

                // begin coloration
                SCNTransaction.begin()
                SCNTransaction.setAnimationDuration(0.5)

                // on completion - keep color
                SCNTransaction.setCompletionBlock {
                    SCNTransaction.begin()
                    SCNTransaction.setAnimationDuration(0.3)

                    material.diffuse.contents = self.pickedColor

                    SCNTransaction.commit()
                }

                SCNTransaction.commit()

                material.diffuse.contents = pickedColor
            }
        }
    }
    print("-----------------------------------\n")
}

override func prefersStatusBarHidden() -> Bool {
    return true
}

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return .Landscape
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Release any cached data, images, etc that aren't in use.
}

代码以 setColor 函数开始,该函数捕获来自ColorPanelScene.sks的图像(此SKScene有一个奇怪的y轴移动,我不知道为什么)。

func setColors() {
//Color Setup
let ColorWhite = colorPanelScene.childNodeWithName("ColorWhite") as! SKSpriteNode
let ColorRed = colorPanelScene.childNodeWithName("ColorRed") as! SKSpriteNode
let ColorBrown = colorPanelScene.childNodeWithName("ColorBrown")as! SKSpriteNode
let ColorDarkBrown = colorPanelScene.childNodeWithName("ColorDarkBrown")as! SKSpriteNode


let white = UIColor(red:1, green:0.95, blue:0.71, alpha:1)
let brown = UIColor(red:0.49, green:0.26, blue:0.17, alpha:1)
let red = UIColor(red:0.67, green:0.32, blue:0.21, alpha:1)
let darkBrown = UIColor(red:0.27, green:0.25, blue:0.21, alpha:1)

NodesToColors = [
    ColorWhite: white,
    ColorRed: red,
    ColorBrown: brown,
    ColorDarkBrown: darkBrown
]

    OverlayBackground = colorPanelScene.childNodeWithName("OverlayBackground")as! SKSpriteNode
}

然后,您可以看到我想要添加到面板背景的模糊效果功能。你知道怎么做到SKNode吗?如果我使用UIView,那将很容易,但我不知道如何返回图层视图。

func blur(image image: UIImage) -> UIImage {
    let radius: CGFloat = 20;
    let context = CIContext(options: nil);
    let inputImage = CIImage(CGImage: image.CGImage!);
    let filter = CIFilter(name: "CIGaussianBlur");
    filter?.setValue(inputImage, forKey: kCIInputImageKey);
    filter?.setValue("\(radius)", forKey:kCIInputRadiusKey);
    let result = filter?.valueForKey(kCIOutputImageKey) as! CIImage;
    let rect = CGRectMake(radius * 2, radius * 2, image.size.width - radius * 4, image.size.height - radius * 4)
    let cgImage = context.createCGImage(result, fromRect: rect);
    let returnImage = UIImage(CGImage: cgImage);

    return returnImage;

}

如果查看 ColorPanelScene.sks 上的按钮,它们的名称错误,因为我使用了一种解决方法来使该面板正常工作。它似乎以相反的方式匹配颜色节点,纹理和节点名称。

这显然是侧面板的糟糕实现。请你能帮我建立一个更好的交互式面板吗?谢谢。

0 个答案:

没有答案