Stroke Width with a Scenekit Line Primitive Type

Stroke Width with a SceneKit line primitive type

SceneKit doesn't provide controls for this. However, SceneKit draws using OpenGL ES, which does.

When you're drawing with GL in the GL_LINES mode, the glLineWidth call changes the line width. (Watch out: the argument is in actual pixels, not UI-layout points, so you'll need a larger width than you might think if you don't want super-thin hairlines on a Retina display.)

So, where do you call that in your SceneKit app? You have a few options there. In a simple scene like yours, where you're only rendering one thing, you can just set it before the scene renders. Set a delegate for your view, then implement renderer:willRenderSceneAtTime: and call glLineWidth there.

However, OpenGL line rendering is pretty limited — if you want to customize rendering more, you'll need a different approach. Just which approach works best depends on exactly what you're going for, so here are a few ideas for you to research:

  • Make your "lines" from narrow triangle strips
  • Make them from primitive SCN geometries like boxes and cylinders
  • Keep a simple cube geometry, but use a fragment shader (or shader modifier snippet) to draw only near the edges of each polygon

iOS Metal line width

Short answer would be there is no way to control line width in the same way as a point size in Metal. Even in OpenGL graphics API, the function to do this (which used to exist as gllinewidth function) is now deprecated.

An option would be to draw the line as a quad (a box), with two triangles. This would let you control the width of the line.

If you want to stick to the line primitive itself, for some particular reason, the equivalent OpenGL question has been asked on StackOverflow already as seen here. The shader can be simple translated to Metal shading API.

SceneKit- Cannot query using bitmask

SceneKit

In SceneKit you can use bitmasks in context of [SCNHitTestResult]. hitTest(_:options:) instance method is not deprecated yet and it works in iOS 15.4.

let sceneView = ARSCNView(frame: .zero)

enum HitTestType: Int {
case object_A = 0b00000001
case object_B = 0b00000010
}

let point: CGPoint = gesture.location(in: self.sceneView)

let bitMask = HitTestType.object_A.rawValue | HitTestType.object_B.rawValue

let results = sceneView.hitTest(point, options: [.categoryBitMask: bitMask])

P.S.

Only hitTest(_:types:) is deprecated at the moment.


RealityKit

In RealityKit you can use bitmasks in CollisionCastHit's context:

let arView = ARView(frame: .zero)

let point: CGPoint = gesture.location(in: self.arView)

let (origin, direction) = arView.ray(through: point)!

let raycasts: [CollisionCastHit] = arView.scene.raycast(origin: origin,
direction: direction,
length: 50,
query: .any,
mask: .default,
relativeTo: nil)

...or this way:

let raycasts: [CollisionCastHit]  = arView.hitTest(point, 
query: .any,
mask: .default)

I need help to draw an icosahedron 3D object in an UIKit app

to build an icosahedron you can use SCNSphere and set its geodesic property to YES.

Using shader modifiers to draw the wireframe (as described in Stroke Width with a SceneKit line primitive type) is a good idea.

But in your case lines are not always plain or dotted — it depends on the orientation of the icosahedron. To solve that you can rely on gl_FrontFacing to determine whether the edge belongs to a front-facing or back-facing triangle.

SCNGeometry / SCNCylinder render edges / border only (Store color, clear content)

The WWDC 2014 presentation showed orbiting cubes that had only wireframes. The technique was to use an image with green edges but transparent interior as the material. From AAPLSlideScenegraphSummary.m:

        // A node that will help visualize the position of the stars
_wireframeBoxNode = [SCNNode node];
_wireframeBoxNode.rotation = SCNVector4Make(0, 1, 0, M_PI_4);
_wireframeBoxNode.geometry = [SCNBox boxWithWidth:1 height:1 length:1 chamferRadius:0];
_wireframeBoxNode.geometry.firstMaterial.diffuse.contents = [NSImage imageNamed:@"box_wireframe"];
_wireframeBoxNode.geometry.firstMaterial.lightingModelName = SCNLightingModelConstant; // no lighting
_wireframeBoxNode.geometry.firstMaterial.doubleSided = YES; // double sided

Sample Image

For a similar effect with SCNCylinder, you might need to pass an array of materials, some with border and some without.

Edit

For High Sierra/iOS 11 and higher, the answer by @mnuages is a simpler/better approach.



Related Topics



Leave a reply



Submit