How do you get motion blur working in SceneKit? - objective-c

According to WWDC 2017, motionBlurIntensity was added as a property to SCNCamera. I've tried the following and failed to get SceneKit to blur my scene when the camera is moved:
Set wantsHDR to true
Add SCNDisableWideGamut as a Boolean with the value of YES in every Info.plist in my Xcode project
Move a SCNBox by changing its SCNNode's position in front of the camera with motionBlurIntensity set to 1.0
Move the camera itself by changing its SCNNode's position with motionBlurIntensity set to 1.0
Animate the camera using an SCNTransaction with motionBlurIntensity set to 1.0 instead of changing its position each frame
Do the above with motionBlurIntensity set to 500 or greater
I run the following code every rendered frame like so:
camNode.position = SCNVector3Make(cx, cy, cz);
camNode.eulerAngles = SCNVector3Make(rotx, roty, rotz);
camNode.camera.wantsDepthOfField = enableDOF;
camNode.camera.wantsHDR = enableHDR;
camNode.camera.zNear = camNearVal;
camNode.camera.zFar = camFarVal;
camNode.camera.focalLength = camFocalLength;
camNode.camera.usesOrthographicProjection = usingOrthoProjection;
if(!usingOrthoProjection)
{
camNode.camera.projectionTransform = SCNMatrix4FromGLKMatrix4(GLKMatrix4MakeWithArray(projection));
}
else
{
// Ortho options
camNode.camera.orthographicScale = orthoScale;
if(cam_projectionDir == 1)
camNode.camera.projectionDirection = SCNCameraProjectionDirectionHorizontal;
else
camNode.camera.projectionDirection = SCNCameraProjectionDirectionVertical;
}
// DOF
camNode.camera.sensorHeight = dof_sensorHeight;
camNode.camera.focusDistance = dof_focusDistance;
camNode.camera.fStop = dof_fStop;
camNode.camera.apertureBladeCount = dof_apertureBladeCount;
camNode.camera.focalBlurSampleCount = dof_focalBlurSampleCount;
// Motion blur
camNode.camera.motionBlurIntensity = motionBlurIntensity;
And here is where the SCNRenderer sets its pointOfView to the camera:
mainRenderer.scene = mainScene;
mainRenderer.pointOfView = camNode;
id<MTLCommandBuffer> myCommandBuffer = [_commandQueue commandBuffer];
[mainRenderer updateAtTime: currentFrame];
[mainRenderer renderWithViewport:CGRectMake(0,0,(CGFloat)_viewWidth, (CGFloat)_viewHeight) commandBuffer:myCommandBuffer passDescriptor:_renderPassDescriptor];
[myCommandBuffer commit];
HDR effects like Bloom and SSAO work properly, just not motion blur.
I'm using Xcode Version 10.1 on macOS Mojave.
I ran the Badger sample app and the motion blur in that project works on my computer.
Am I missing something here? Any insight would be greatly appreciated.

The default value of a motionBlurIntensity property is 0.0. It results in no motion blur effect. Higher values (toward a maximum of 1.0) create more pronounced motion blur effects. Motion blur is not supported when wide-gamut color rendering is enabled. Wide-gamut rendering is enabled by default on supported devices; to opt out, set the SCNDisableWideGamut key in your app's Info.plist file.
Test it with my code:
let cameraNode1 = SCNNode()
cameraNode1.camera = SCNCamera()
cameraNode1.camera?.motionBlurIntensity = 3 // MOTION BLUR
scene.rootNode.addChildNode(cameraNode1)
let move = CABasicAnimation(keyPath: "translation")
move.fromValue = NSValue(scnVector3: SCNVector3(x: 7, y: -5, z: 0))
move.toValue = NSValue(scnVector3: SCNVector3(x: 7, y: 5, z: 0))
move.duration = 0.5
move.repeatCount = 20
move.autoreverses = true
move.repeatCount = .infinity
let sphereNode1 = SCNNode()
sphereNode1.geometry = SCNSphere(radius: 2)
sphereNode1.addAnimation(move, forKey: "back and forth")
scene.rootNode.addChildNode(sphereNode1)
Works perfectly.

Related

How to animate geometry using the primitiveRange property in SceneKit?

I am using Apple's SceneKit to render a tube which consists of 50 triangle strips as illustrated here:
I want to animate a "growing" tube by using the primitiveRange property.
let tube = createTube() // create's tube.geometry which has 50 elements
let tubeNode = SCNNode(geometry: tube.geometry)
...
let animation = CABasicAnimation(keyPath: "geometry.elements[0].primitiveRange")
animation.fromValue = NSValue(range: NSRange(location: 0, length: 0))
animation.toValue = NSValue(range: NSRange(location: 0, length: tube.geometry.elements.count))
animation.duration = 5
animation.repeatCount = Float.infinity
tubeNode.geometry?.addAnimation(animation, forKey: "don't care")
scene.rootNode.addChildNode(tubeNode)
Nothing is animating (I simply see the full tube above) and I am not getting any warnings on the console. If I change the key "geometry.elements.primitiveRange" to "foo" I do get the following warning on the console
ExtrudedTube[98737:24354579] [SceneKit] Error: _C3DModelPathResolverRegistryResolvePathWithClassName unknown path (
foo
)
Perhaps this property is not animatable? I don't see documentation that says it is or isn't. Any idea how to create the "growing tube" animation using this mesh in SceneKit?
I was never able to pull off the animation using the "keypath" (I think there probably is a way to do it, but there is no documentation on the details). I can use SCNAction though it it works well:
if let numPrimitives = tubeNode.geometry?.elements[0].primitiveCount {
tubeNode.geometry?.elements[0].primitiveRange = NSRange(location: 0, length: 0)
let duration : Double = 5.0
let growTube = SCNAction.customAction(duration: duration, action: {node, elapsedTime in
let f = Double(elapsedTime)/duration
let n = Int(f*Double(numPrimitives))
node.geometry?.elements[0].primitiveRange = NSRange(location: 0, length: n)
})
tubeNode.runAction(growTube)
}

Render object inside debugger to image

In IntelliJ debugger, there's a “Show image” feature for AWT BufferedImage and Android Bitmap.
I want this feature for AWT Shape, too. I've created my own renderer which returns a BufferedImage:
Rectangle bounds = getBounds();
BufferedImage img = new BufferedImage(
(int) (bounds.getX() + bounds.getWidth()),
(int) (bounds.getY() + bounds.getHeight()),
BufferedImage.TYPE_INT_ARGB
);
Graphics2D g = (Graphics2D) img.getGraphics();
g.setColor(Color.RED);
g.fill(this);
return img;
The renderer works and returns an image but there's no option to show it; instead, I can only view its toString(): BufferedImage#bb3ca24: type = 2 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=ff000000 IntegerInterleavedRaster: width = 27 height = 24 #Bands = 4 xOff = 0 yOff = 0 dataOffset[0] 0.
How can I make IntelliJ to enable “Show image“ feature for BufferedImage returned from a custom renderer?

Godot: advanced swiping mechanics

I have a VboxContainer as a child of a ScrollContainer and some text and buttons in it – a long list I can scoll through by finger-swiping on a touchscreen.
The code (based on https://github.com/godotengine/godot/issues/21137#issuecomment-414959543):
extends ScrollContainer
var swiping = false
var swipe_start
var swipe_mouse_start
var swipe_mouse_times = []
var swipe_mouse_positions = []
signal finger_is_swiping
func _ready():
mouse_filter = Control.MOUSE_FILTER_IGNORE
func _input(ev):
if ev is InputEventMouseButton:
if ev.pressed:
swiping = true
swipe_start = Vector2(get_h_scroll(), get_v_scroll())
swipe_mouse_start = ev.position
swipe_mouse_times = [OS.get_ticks_msec()]
swipe_mouse_positions = [swipe_mouse_start]
else:
swipe_mouse_times.append(OS.get_ticks_msec())
swipe_mouse_positions.append(ev.position)
var source = Vector2(get_h_scroll(), get_v_scroll())
var idx = swipe_mouse_times.size() - 1
var now = OS.get_ticks_msec()
var cutoff = now - 100
for i in range(swipe_mouse_times.size() - 1, -1, -1):
if swipe_mouse_times[i] >= cutoff: idx = i
else: break
var flick_start = swipe_mouse_positions[idx]
var flick_dur = min(0.3, (ev.position - flick_start).length() / 1000)
if flick_dur > 0.0:
var tween = Tween.new()
add_child(tween)
var delta = ev.position - flick_start
var target = source - delta * flick_dur * 8.0
#tween.interpolate_method(self, 'set_h_scroll', source.x, target.x, flick_dur, Tween.TRANS_QUAD, Tween.EASE_OUT)
tween.interpolate_method(self, 'set_v_scroll', source.y, target.y, flick_dur, Tween.TRANS_QUAD, Tween.EASE_OUT)
tween.interpolate_callback(tween, flick_dur, 'queue_free')
tween.start()
swiping = false
elif swiping and ev is InputEventMouseMotion:
var delta = ev.position - swipe_mouse_start
set_h_scroll(swipe_start.x - delta.x)
set_v_scroll(swipe_start.y - delta.y)
swipe_mouse_times.append(OS.get_ticks_msec())
swipe_mouse_positions.append(ev.position)
emit_signal("finger_is_swiping")
print ("finger is swiping")
It works quite nicely, but there are three hurdles I need help with:
While swiping, the print "finger is swiping" is being printed all the time, many times, and I therefore assume that the signal "finger_is_swiping" is being emitted as often as well. How can I have the signal being emitted only once per swipe? (Or is that a problem at all?)
How can I set a signal when my finger stops swiping (so when it's lifted from the touchscreen)?
I'd like the swipe-movement not to cause scrolling at all when touching a button in the list first. (My buttons work in a way so I can hold them down and make them not trigger by dragging the finger off... in such a case I'd like the list not to scroll despite the dragging.) I'd assume that I could have the button emit a signal which causes some "ignore swipe" or set the swipe speed to 0 maybe?
EDIT:
Yes, a simple swiping = false does this trick!

SceneKit repeat texture

For some reason, using the following material on an SCNBox keeps resulting in a stretched texture. Any idea where I'm missing something?
material.diffuse.contents = [SKTexture textureWithImageNamed:#"arrowtexture.jpg"];
material.diffuse.wrapS = SCNWrapModeRepeat;
material.diffuse.wrapT = SCNWrapModeRepeat;
You can control SKTexture's stretch this way:
material.diffuse.contentsTransform = SCNMatrix4MakeScale(3, 1, 1);

How to set axis margin in WinRT XAML Toolkit line chart?

Answer
Finally I solved my answer with this.
((LineSeries)MyChart.Series[0]).IndependentAxis = new LinearAxis
{
Minimum = 1,
Maximum = 5,
Orientation = AxisOrientation.X,
Interval = 1,
Margin = new Thickness(10, 0, 10, 0)
};
((LineSeries)MyChart.Series[0]).Clip = null;
((LineSeries)MyChart.Series[0]).Margin = new Thickness(10, 0, 10, 0);
I am drawing line chart with help of WinRT XAML Toolkit. I am setting X axis manually, but when I set I am getting wierd start & end point. I tried to set margin and padding but it's not working. Will you please suggest me how can I do that ?
((LineSeries)MyChart.Series[0]).IndependentAxis = new LinearAxis
{
Minimum = 1,
Maximum = 5,
Orientation = AxisOrientation.X,
Interval = 1,
//Margin = .... Not working
//Padding = .... Not working
};
I'd use a visual tree debugger to walk up the visual tree from these data points to see where the Clip property is set. In fact I just did that on the samples project and it is set on the LineSeries. See if it's set as part of its XAML template or if it is done in C# and remove it there. Also you could change the Minimum/Maximum values on your X axis to make more space. I'll add cleaning this all up to my TODO list.