Swift: setting SCNMaterial works for SCNBox, but not for SCNGeometry loaded from Wings3D DAE

This piece of code (scene, camera, light, etc. cut from the code) works in Swift on an iOS simulator:

let boxNode = SCNNode() // Create a box boxNode.geometry = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0.1) let numFaces = 6 scene.rootNode.addChildNode(boxNode) // create and configure a material for each face var materials: [SCNMaterial] = Array() for i in 1...numFaces { let material = SCNMaterial() material.diffuse.contents = UIImage(named: "texture") materials += material } // set the material to the 3d object geometry boxNode.geometry.materials = materials 

It generates a field in which each face is a checkered image.

SCNBox in App

Trying to do the same with simple stock geometry created in Wings3D, stored in DAE, and loaded into the application gives me a suitable shape, but without the shades and images on the faces:

  let boxNode = SCNNode() // Load the geometry let urlToColladaFile = NSBundle.mainBundle().URLForResource("Objects", withExtension:"dae") let sceneSource = SCNSceneSource(URL:urlToColladaFile, options:nil) boxNode.geometry = sceneSource.entryWithIdentifier("dodecahedron3-0", withClass:SCNGeometry.self) as SCNGeometry let numFaces = 10 scene.rootNode.addChildNode(boxNode) // create and configure a material for each face var materials: [SCNMaterial] = Array() for i in 1...numFaces { let material = SCNMaterial() material.diffuse.contents = UIImage(named: "texture") materials += material } // set the material to the 3d object geometry boxNode.geometry.materials = materials 

Dodecahedron from Wings3D

What am I missing?

+6
source share
3 answers

Thanks, mnuages โ€‹โ€‹and rickster. Your findings led me to explore my options in Wings3D, and I was able to solve the problem inside Wings3D.

In short, I need to create UV mapping for each solid surface.

For the right polyhedrons, I had several options:

  • Select the whole body (and therefore all faces), do one UV mapping and make one image stretched across all faces. There are online tutorials for this.
  • For each face, select it, do UV mapping (usually I chose Projection Normal for this), and then each face will get the same display, regardless of how many images you put in the array of materials in my code.
  • For each face, do it higher. Then go to the Outliner window and duplicate the AUV (Automatic UV) file so that each face is unique. Then select each AUV and assign it to another person. This would give me as many faces as I wanted, allowing me, for example, to create a new image for each face of a dying person. I had to find out - it seems that there is a one-to-one mapping of AUV mappings inside Wings3D to what SceneKit calls geometry elements when setting up Materials.
  • The combination is higher, so that several persons can share the same display, and then automatically assign the same image.

It all gets a little more complicated if I start adding frames or smoother chamfers to the edges, and now I need to make sure that UV matching makes sense for every set of faces I'm working on. Again, there are educational materials on the Internet.

Thanks again!

Cheers, Henry

+1
source

Does your geometry have texture coordinates? You can check this programmatically (by checking if a source exists with SCNGeometrySourceSemanticTexcoord semantics) or in the SceneKit editor built into Xcode.

Also note that you do not need to create one material for a geometry element. If they are all the same, just create an array of the same material, and it will be used for the entire geometry.

+6
source

First you will need to define the material when developing the 3D object.

then programmatically assign a texture to each material.

  let material1 = SCNMaterial() material1.diffuse.contents = UIImage(named: "1")?.xFlipped material1.locksAmbientWithDiffuse = true let material2 = SCNMaterial() material2.diffuse.contents = UIImage(named: "2")?.xFlipped material2.locksAmbientWithDiffuse = true ... let material12 = SCNMaterial() material12.diffuse.contents = UIImage(named: "12")?.xFlipped material12.locksAmbientWithDiffuse = true scene.rootNode.childNodeWithName("Dice", recursively: false)?.geometry?.materials = [material1, material2, material3, material4, material5, material6, material7, material8, material9, material10, material11, material12] 

Where "Dice" is the name of the object that you specified in the Property inspector.

 extension UIImage { var xFlipped: UIImage { let tempImageView = UIImageView(frame: CGRectMake(0, 0, self.size.width, self.size.height)) tempImageView.image = self let viewTempImage = UIView(frame: tempImageView.frame) viewTempImage.addSubview(tempImageView) tempImageView.transform = CGAffineTransformMakeScale(-1, 1) let img = viewTempImage.screenshot return img } } extension UIView { var screenshot: UIImage{ UIGraphicsBeginImageContext(self.bounds.size); let context = UIGraphicsGetCurrentContext(); self.layer.renderInContext(context!) let screenShot = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return screenShot } } 

Note: In my case, the images when I add the texture were flipped horizontally. So I turned the handle over as shown above.

+1
source

Source: https://habr.com/ru/post/972115/


All Articles