Setting Position and Scale of 3-D objects in SceneKit - scenekit

I'm trying to make a simple application in which 3-D objects are placed in the scene through ARKit.
The application works perfectly when I build SCNNode objects through Objective-C code. However, when I try to place SCNNode objects loaded through a .obj file, the position and scale of the objects seems to be bizarre.
The way I create an SCNNode from a .obj file is as follows:
NSString *path = [[NSBundle mainBundle] pathForResource:#"model" ofType:#"obj"];
NSURL *URL = [NSURL fileURLWithPath:path];
MDLAsset *asset = [[MDLAsset alloc] initWithURL:URL];
SCNNode *node = [asset objectAtIndex:0];
Now the problem is that when I set the node's transform to a feature point on a detected plane, the object is placed far above where it should be. Moreover its scale is absurdly large, through when viewed through model editing software, it is supposed to be only a few centimeters long/wide.
What am I missing? I'd appreciate any pointers towards understanding models (.objs specifically) better.

1 - The scale of the objects being wrong is likely due to the fact that different 3D modelling software use different scaling units. The easy way to fix it is to just rescale the node in SceneKit:
yourNode.scale = SCNVector3(x: 0.5, y: 0.5, z: 0.5)
The more accurate option is to scale your model correctly in the 3D modelling software and export again.
2 - The reason your model is not being placed in the correct position is that the pivot point is not centred correctly. This can be done using your 3D modelling software or in Scenekit as explained in this post:
https://stackoverflow.com/a/48979201/3975207

Related

Big SCNGeometry SceneKit for iOS

I am working on a cocoa/iOS projet.
I have a common swift class which manage a Scenekit scene.
I want to draw a big terrain (about 5000x5000 points).
I have 2 triangles per 4 points. I have created a scngeometry object for the whole terrain (is it a good thing ?)
I decided to store those points in a 6-Float structure (x,y,z and r,g,b). I tried to create an empty array or to allocate a big array at the begining : i got the same issue.
I work with Int datatype for indices array.
The project works fine on Cocoa but i get memory errors on iOS. I think this is because of the need to have a big and contigous array for vertex.
I tried to create several chunks of geometry objects but scene kit does not like if we erase a previous buffer.
What is the best practice in this case ?
Is there a way to store vertex on the mass storage instead of memory arrays/buffers ?
Thanks
So...twice as many terrain points as there are pixels on a shiny new 5K display? That's a huge amount of memory to be using at once on iOS. And you won't be able to see that resolution on an iOS device.
So how about:
Break your 25 million pixel terrain into smaller tiles, each in its own SCNNode. Loop through the tiles, create one SCNNode, throw away the 6-Float array for that tile and move to the next.
Use SCNLevelOfDetail to produce much simpler versions of those nodes, for display when they're very far away.
Do the construction work on OS X. Archive your scene (NSSecureCoding). Bundle that scene into the iOS app.
Consider using reference nodes in your main SCNScene, and archive each tile as a separate SCNScene file.
Hopefully you're already using triangle strips, not triangles, to build your geometry.

Leaflet JS: Custom 2D projection that uses meters instead of lat,long

I am working on a custom game map. This map is basically a raster image, overlayed with some paths and markers. I want to use Leaflet to display the map.
What I am struggling with, is that Leaflet uses Latitude and Longitude to calculate positions, while it uses meters for distances (path lengths, radii of circles, etc).
This is very understandable when dealing with a spherical world like our Earth, but it complicates the custom map, which is flat a lot.
I would like to be able to specify the positions in the same unit as the distances.
Now, by default Leaflet uses a Spherical Mercator projection. According to the Docs, it is possible to define your own projections and coordinate reference systems, but I have been unable to do this thus far.
How would this be possible? Or is there a simpler way?
You should take a look at the simple coordinate reference system (L.CRS.Simple) included with Leaflet:
A simple CRS that maps longitude and latitude into x and y directly. May be used for maps of flat surfaces (e.g. game maps).
You can define the CRS of your L.Map instead upon initialization like so:
new L.Map('myDiv', {
crs: L.CRS.Simple
});
Some further elaboration: As #ghybs pointed out in the comment below and the comment to your question the default sperical mercator projection (L.CRS.EPSG3857) already works in meters. When you calculate the distance between two coordinates, Leaflet returns meters, example:
var startCoordinate = new L.LatLng(0, -1);
var endCoordinate = new L.LatLng(0, 1);
var distance = startCoordinate.distanceTo(endCoordinate);
console.log(distance);
The above will print 222638.98158654713 to your console which is the distance between those two coordinates in meters. Problem is that when using spherical projection, distance between two coordinates will become less the further you get from the equator which will become problematic when creating a flat gameworld. That's why you should use L.CRS.Simple, you won't have said problem.

Moving an SCNNode relative to the root node

I’m learning SceneKit by writing a game where you’re flying through an asteroid field dodging objects. Initially, I did this by moving/rotating the camera, but I realized that at some point I’d run out of coordinate space and it’s probably better to move all of the objects toward the camera (and dispose of them when I’ve “passed” them).
But I can’t seem to get them to move. My original code that moved the camera looked like this:
[cameraNode setTransform:CATransform3DTranslate(cameraNode.transform, 0.f, 0.f, -2.f)];
I thought I could do something similar with each asteroid node:
[asteroidNode setTransform:CATransform3DTranslate(cameraNode.transform, 0.f, 0.f, 2.f)];
but they don’t move. If I add a basic animation:
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"position.z"];
anim.byValue = #10;
anim.duration = 1.0;
[asteroidNode addAnimation:anim forKey:#"move forward"];
the asteroids move but predictably snap back to their original location when it’s done.
This feels like a rookie mistake but I can’t find anything addressing this problem online. Am I going about this the wrong way?
Thanks,
Jeff
moving the cameraNode the way you do it should work but make sure "cameraNode" is your current pointOfView or it will have no effect (check that scnView.pointOfView == cameraNode).
If you want to move the nodes instead you should translate "node.transform" (not "cameraNode.transform"). But Actually it's simpler to just do:
node.position = SCNVector3Make(node.position.x, node.position.y, node.position.z+2.0);
Also make sure there is no animation or physics running on these nodes that could override your changes.

Set Parent of Map Axes in Matlab GUI

I am programming a basic GUI in MATLAB that utilizes the mapping toolbox. The GUI will display a grayscale image and then plot discrete points over the data, all of this over the necessary map projection. It is important that I plot onto map axes (those created by the axesm command) rather than the vanilla cartesian space. I have no problem doing all this from the command line, but I cannot find a way to implement a GUI version and its driving me nuts.
The problem is that I need to specify the map axes as being the child of the parent figure. The normal axes has a property that can be set, doing something like:
axesHandle = axes('Parent', parentHandle, ...);
or
set(axesHandle, 'Parent', parentHandle);
However, there is no equivalent parent property for the map axes created by the axesm function, so I have no way to manipulate the axes within the figure. How can I do this?
Update: If I create a plot within the map axes in an empty figure, get(figureHandle, 'Children') returns the handle of the axesm object (thanks #slayton!), so the map axes object must be implicitly added to the children of the figure by MATLAB.
Should I be concerned that the map axes do not refer back to the parent figure, or should I just let it be? I wonder if this is a classic case of MATLAB forcing me to not comply with the standards the manual tells me to implement.
From reading your question what I think you are trying to do is grab the handle of the axes object. This can be done as the axes is created using either axes or subplot
a = axes();
a = subplot(x,y,z);
% both return an handle to the newly created axes object
Additionally if the axes is created automagically by a function call like plot or image you can get the axes handle that too:
p = plot(1:10); %returns a handle to a line object
a = get(p,'Parent');
i = image(); %returns a handle to an image object
a = get(i, 'Parent');
Finally, neither of those two options is available you can always get the axes handle from its containing figure with:
a = get(figureHandle, 'Children');
Remember though that this will return a vector of axes handles if your figure contains more than one axes.
Finally when it comes time to draw draw your points to the axes that contains your map image you simply need to call:
line(xPoints, yPoints, 'linestyle', 'none', 'marker', '.', 'color', 'r', 'size', 15)
This will draw the vertices of the line using large red dots.
I'm not sure if this answers your question because the code you provided doesn't line up with the question you asked.
The code you provided looks like you are trying to move an axes from one figure to another. You can totally do this!
f = figure('Position', [100 100 100 100]);
a = axes('Parent', f);
pause
f2 = figure('Position', [250 100 100 100]);
set(a,'Parent', f2);
After much trial and error and reading of documentation, I have found that there is no way to explicitly specify the parent of the map axes. Instead, they are implicitly added on top of the current axes. In the instance that no axes exist in the current figure, calling axesm creates an axes object and then places the axesm object inside. When you take this route, you have to grab the axes object handle by calling gca:
mapAxesHandle = axesm(...);
axesHandle = gca(...);
This makes it frustrating to use the mapping toolbox when writing a GUI from scratch, but that's the way Mathworks makes it happen. Thanks to #slayton for useful info. I'd upvote but my reputation is <15 :(

What vertex shader code should be used for a pixel shader used for simple 2D SpriteBatch drawing in XNA?

Preface
First of all, why is a vertex shader required for a SilverlightEffect (.slfx file) in Silverlight 5? I'm trying to port a simple 2D XNA game to Silverlight 5 RC, and I would like to use a basic pixel shader. This shader works great in XNA for Windows and Xbox, but I can't get it to compile with Silverlight as a SilverlightEffect.
The MS blog for the Silverlight Toolkit says that "there is no difference between .slfx and .fx", but apparently this isn't quite true -- or at least SpriteBatch is working some magic for us in "regular XNA", and it isn't in "Silverlight XNA".
If I try to directly copy my pixel shader file into a Silverlight project (and change it to the supported "Effect - Silverlight" importer/processor), when I try to compile I see the following error message:
Invalid effect file. Unable to find vertex shader in pass "P0"
Indeed, there isn't a vertex shader in my pixel shader file. I haven't needed one with my other 2D XNA apps since I'm just doing basic SpriteBatch drawing.
I tried adding a vertex shader to my shader file, using Remi Gillig's comment on this Shawn Hargreaves blog post for guidance, but it doesn't quite work. The shader file successfully compiles, and I see some semblance of my game on screen, but it's tiny, twisted, repeated, and all jumbled up. So clearly something's not quite right.
The Real Question
So that brings me to my real question: Since a vertex shader is required, is there a basic vertex shader function that works for simple 2D SpriteBatch drawing?
And if the vertex shader requires world/view/project matricies as parameters, what values am I supposed to use for a 2D game?
Can any shader pros help? Thanks!
In XNA, SpriteBatch is providing its own vertex shader. Because there is no SpriteBatch in Silverlight 5, you have to provide your own.
You may want to look at the source code for the shader that SpriteBatch uses (it's in SpriteEffect.fx). Here is its vertex shader, it's pretty simple:
void SpriteVertexShader(inout float4 color : COLOR0,
inout float2 texCoord : TEXCOORD0,
inout float4 position : SV_Position)
{
position = mul(position, MatrixTransform);
}
So now you just need the input position and the transformation matrix (and the texture co-ordinates, but those are fairly simple).
The same blog post you linked, towards the end, tells you how to set up the matrix (also here). Here it is again:
Matrix projection = Matrix.CreateOrthographicOffCenter(0, viewport.Width, viewport.Height, 0, 0, 1);
Matrix halfPixelOffset = Matrix.CreateTranslation(-0.5f, -0.5f, 0);
Matrix transformation = halfPixelOffset * projection;
(Note: I'm not sure if Silverlight actually requires the half-pixel offset to maintain texel alignment (ref). It probably does.)
The tricky bit is that the positioning of the sprite is done outside of the shader, on the CPU. Here's the order of what SpriteBatch does:
Start with four vertices in a rectangle, with (0,0) being the top-left and the texture's (width,height) as the bottom right
Translate backwards by origin
Scale
Rotate
Translate by position
This places the sprite vertices in "client space", and then the transformation matrix transforms those vertices from client space into projection space (which is used for drawing).
I've got a highly-inlined version of this transformation in ExEn (SpriteBatch.InternalDraw in SpriteBatchOpenGL.cs) that you could easily adapt for your use.
I came across this question today and I want to add that you can do all of the calculations in the vertex shader itself:
float2 Viewport; //Set to viewport size from application code
void SpriteVertexShader(inout float4 color : COLOR0,
inout float2 texCoord : TEXCOORD0,
inout float4 position : POSITION0)
{
// Half pixel offset for correct texel centering.
position.xy -= 0.5;
// Viewport adjustment.
position.xy = position.xy / Viewport;
position.xy *= float2(2, -2);
position.xy -= float2(1, -1);
}
(I didn't write this, credit goes to the comments in the article mensioned above)

Resources