Assimp: Load .blend file with correct rotation - coordinates

I have a simple .blend level which looks like it's flipped when importing with Assimp:
Blender uses Z as its Up vector, my engine uses Y. There are no export/save options in Blender that allows me to set that in .blend
Afaik Assimp should handle this for me. Here's how I'm loading the model, converting it to a runtime format for my engine:
// vertices, normals and uvs
{
u32 VertexCount = SrcMesh->mNumVertices;
u32 ByteCount = VertexCount * sizeof(f32) * 3;
DstMesh->VerticesCount = VertexCount;
DstMesh->Vertices = new vec[VertexCount];
memcpy(DstMesh->Vertices, SrcMesh->mVertices, ByteCount);
FAIL_IF_NOT(SrcMesh->mNormals, "Model doesn't have normals");
DstMesh->Normals = new vec[VertexCount];
memcpy(DstMesh->Normals, SrcMesh->mNormals, ByteCount);
FAIL_IF_NOT(SrcMesh->mTextureCoords[0], "Model doesn't have UVs");
DstMesh->UVs = new vec[VertexCount];
memcpy(DstMesh->UVs, SrcMesh->mTextureCoords[0], ByteCount);
}
// faces
{
DstMesh->FacesCount = SrcMesh->mNumFaces;
DstMesh->Faces = new mesh_face[DstMesh->FacesCount];
For(i32, F, DstMesh->FacesCount)
{
aiFace *SrcFace = &SrcMesh->mFaces[F];
mesh_face *DstFace = &DstMesh->Faces[F];
FAIL_IF_NOT(SrcFace->mNumIndices == 3, "Model is not triangulated. Face index %d has %d indices", F, SrcFace->mNumIndices);
memcpy(DstFace->Indices, SrcFace->mIndices, 3 * sizeof(u32));
}
}
Note I also tried finding the node that mesh's belong to, and transforming the vertices/normals by that node's final transform (by final I mean I recurse all the node's parents and apply their transforms and finally the node's local transform) -- But that didn't do anything because the mesh has no parent.
My questions are:
What is the right way to load .blend files?
Shouldn't Assimp be handling this on its own on import?
or do I need to manually rotate/flip things?
Here's the referenced model: https://www.dropbox.com/s/hwsyfyqip5pqhrh/StusRoom.blend?dl=0
Thanks for any help!

I figured out that Blender expect .glb files to be in XZ-Y axis order, so perhaps this is true for .blend files as well.
Regarding the AssImp - I was dealing with this issue myself on my work, and I figured out that the problem is the AssImp - check the link: https://github.com/assimp/assimp/issues/849 (as you can see this is still open, even though it's over 5 years old now)
I believe you need to figure out some workaround to somehow manually rotate the file, since assimp will rotate it for you.
Hope it helps! Best regards

Related

Ironpython script in Ansys Customization tool

I'm a beginner in Python and I'm working with the Ansys Customization Tool (ACT) to add my own extension.
Is there a direct way to fill a file with every node's coordinates after deformation?
hopefully in 3 lines or columns: x , y , z
So far I only found the GetNodeValue object but it only gives me the displacement and I need the deformed coordinates for the entire model.
My first idea was to add the displacements to the initial coordinates but I didn't manage to do it.
Many thanks for your help
Lara
APDL Snippet
Add an APDL Snippet in the solution part of the tree:
/prep7
UPGEOM,1,1,1,file,rst ! adds the displacements to the nodal coordinates.
cdwrite,geom,nodesAndelements,geom ! Writes node and element data to nodesAndelement.geom
I'm not sure if you can work with the output format from cdwrite, but this is the quickest solution i can think of.
If you want to automate you have to insert the command snippet via
solution = ExtAPI.DataModel.Project.Model.Analyses[0].Solution
fullPath = "path//to//snippet"
snippet = solution.AddCommandSnippet()
snippet.ImportTextFile(fullPath)
ACT
If you want to stay in ACT it could be done like this:
global nodeResults
import units
analysis = ExtAPI.DataModel.Project.Model.Analyses[0]
mesh = analysis.MeshData
# Get nodes
allNodes = mesh.Nodes
# get the result data
reader = analysis.GetResultsData()
# get the deformation result
myDeformation = reader.GetResult("U")
nodeResultsTemp = []
result_unit = myDeformation.GetComponentInfo("X").Unit
for node in allNodes:
# get node deformation and convert values in meter
deformationNode1 = myDeformation.GetNodeValues(node.Id)
deformationNode1[0] = units.ConvertUnit(deformationNode1[0],result_unit,"m","Length")
deformationNode1[1] = units.ConvertUnit(deformationNode1[1],result_unit,"m","Length")
deformationNode1[2] = units.ConvertUnit(deformationNode1[2],result_unit,"m","Length")
# add node coordinates (in meter) to the displacement
mesh_unit = mesh.Unit
node1 = mesh.NodeById(node.Id)
node1CoorX = units.ConvertUnit(node1.X,mesh_unit,"m","Length")
node1CoorY = units.ConvertUnit(node1.Y,mesh_unit,"m","Length")
node1CoorZ = units.ConvertUnit(node1.Z,mesh_unit,"m","Length")
deformationNode1[0] = deformationNode1[0]+node1CoorX
deformationNode1[1] = deformationNode1[1]+node1CoorY
deformationNode1[2] = deformationNode1[2]+node1CoorZ
nodeResultsTemp.append([node1.X,node1.Y,node1.Z,deformationNode1[0],deformationNode1[1],deformationNode1[2]])
nodeResults = nodeResultsTemp

Importing obj file with a large offset

I have a series of obj files which were produced by photogrammetry by my coworkers who specialize in dealing with GIS (Geographic Information Systems) data. The first few data points in the files look something like:
v 445077.679 4460688.700 61.371
v 445077.340 4460686.317 61.367
v 445077.296 4460686.024 61.416
I believe the file is valid because I can open the files in an online viewer and I get what I expect to see using the viewer at http://masc.cs.gmu.edu/wiki/ObjViewer:
When I open the same file in Blender, Unity or Unreal Engine, the object is very far from the world origin. I can center it by moving the origin to the center of mass and then resetting the object location, but when I recenter the object I always see something that looks like:
What am I doing wrong, or what could be wrong with my file?
The reason for the problem with these files is the large offset combined with 32-bit float values. In this case the objects all use the same geographic origin, probably at a lat/long of 0.000N/0.000E
Nearly all 3D graphics programs use 32-bit floating point values to store each points location, and the combination of the offset and the 32-bit value causes some of the precision to be lost. 32-bit floats have about 7 decimal digits of precision, so the offset of 4460688 in the example file completely dominates, and effectively cuts the model from 1mm resolution to 1m resolution data. The reason for the long triangles is that there is more data lost in one direction due to the asymmetry of the offset.
The solution is to apply some offset to bring the objects close to the origin BEFORE importing them with the 3D software.
I wrote a quick python script that can help with this: https://gitlab.umich.edu/lsa-ts-rsp/xr-shiftOBJ/-/blob/main/shiftOBJ.py
import re # regex
def shiftFile(inFileName, outFileName, offset):
with open(inFileName) as myInFile:
with open(outFileName, 'w') as myOutFile:
for line in myInFile:
myOutFile.write(shiftLine(line, offset))
def shiftLine(inLine, offset):
#if a line is a vertex then apply the shift and drop vertex colors
lineRegex = re.compile('v (\d+\.\d+) (\d+\.\d+) (\d+\.\d+)')
m = lineRegex.match(inLine)
if m and len(m.groups()) >= 3:
outLine = 'v ' + "{:.3f}".format(float(m.groups()[0]) + offset[0]) + ' ' + "{:.3f}".format(float(m.groups()[1]) + offset[1]) + ' ' + "{:.3f}".format(float(m.groups()[2]) + offset[2]) + '\n'
return outLine
else:
return inLine
if __name__ == '__main__':
inFile = '/Users/crstock/Documents/Unreal Projects/Olynthos Data/B88DW18.obj'
outFile = '/Users/crstock/Documents/Unreal Projects/Olynthos Data/B88DW18_shifted.obj'
offset = [-445070, -4460680, -59.0]
shiftFile(inFile, outFile, offset)
This applies an offset to all vertex lines and leaves the other lines alone. By using the same offset values for multiple input files you can maintain the relative shift so that related objects fit together appropriately.

How to measure distance in crs.simple?

I have a non-geographic map aka flat image using CRS.Simple extended by a custom transformation. Everything works fine so far, but now I want to add a distance measurement button. I'm confident I could implement a distance measurement between two markers myself, but the dynamic line drawing and measuring is still a bit above my skills, so I hoped I could use a plugin. None of the ones I found, did offer this though. After looking at the plugins page of leaflet, I tried this fork https://github.com/aprilandjan/leaflet.measure of leaflet.measure originally by https://github.com/jtreml/leaflet.measure as it seemed to offer the ability to add custom units - in my case pixels.
I added this:
L.control.measure({
// distance formatter, output mile instead of km
formatDistance: function (val) {
return Math.round(1000 * val / scaleFactor) / 1000 + 'mapUnits';
}
}).addTo(map)
Unfortunately, the result is a number far too big compared to the pixelsize of the map (4096x4096). distance() returns the expected 1414.213562373095 between a point 1000,1000 and one at 2000,2000. Calculating distanctTo returns 8009572.105082839 instead though. I use this at the beginning of my file
var yx = L.latLng;
var xy = function(x, y) {
if (L.Util.isArray(x)) { // When doing xy([x, y]);
return yx(x[1], x[0]);
}
return yx(y, x); // When doing xy(x, y);
};
If I log val to the console, I get things like this:
20411385.176805027
7118674.47741132
20409736.502863288
7117025.8034695815
20409186.004645467
20409736.502863288
That's likely some problem of the function trying to calculate latlng without a proper reference system.
Anyone got an idea how to solve this? I feel like it can't be overly difficult, but I don't know exactly where to start.
I found a way to do it, even though it feels a bit 'hacky':
I replaced the line
var distance = e.latlng.distanceTo(this._lastPoint)
in the _mouseMove and the _mouseClick events of leaflet.measure with
var currentPoint = e.latlng;
var lastPoint = this._lastPoint;
var distance = map.distance(currentPoint, lastPoint);
as the distance() method of the map returns meters, or in the case of a flat image, pixel values. And those we can translate in whatever unit we want in our flat image.
If anyone has a more elegant way, I'm all ears!

AudioKit - Drawing full waveform of file

I've been going through documentation looking for an answer for this. I see that AudioKit can draw waveforms for in realtime as you record or playback, but I was wondering if you could load in a file and it draw the waveform in full so I can see the whole file's waveform without playing it back.
Any help would be greatly appreciated. Even just a pointer to what object I should look into.
You can also use the Objective C EZAudioPlot class which is in AudioKitUI:
let file = EZAudioFile(url: url)
guard let data = file?.getWaveformData() else { return }
let waveform = EZAudioPlot()
addSubview( waveform )
waveform.frame = NSMakeRect(0, 0, 200, 20)
waveform.plotType = EZPlotType.buffer
waveform.shouldFill = true
waveform.shouldMirror = true
waveform.color = NSColor.black
waveform.updateBuffer( data.buffers[0], withBufferSize: data.bufferSize )
I haven't benchmarked the two methods, but the plot version is very fast for longer files. For stereo files, make two stacked plots using buffers[0] and [1]
there is a waveform that is drawn in the tables playground example here: http://audiokit.io/playgrounds/Basics/Tables/
Basically:
let file = try AKAudioFile(readFileName: "drumloop.wav")
let fileTable = AKTable(file: file)
...UI set up...
addView(AKTableView(fileTable))

tracking position/location in test keyboard & mouse mode (not ppt) with new vizconnect

I can track location fine pre - vizconnect using code like this:-
vrpn7 = viz.add('vrpn7.dle')
posTracker = vrpn7.addTracker('PPT0#WorldViz-PC', 0 )
and then
x,y,z = posTracker.getPosition()
but I now use the new vizconnect e.g.
vizconnect.go( 'vizconnect_hmd_ppt.py' )
I'm wondering what the recommended way is to then access the trackers from my main project '.py' file and particularly when I'm using a keyboard/mouse scenario to simulate movement for during program development.
Any advice would be most welcome.
Thanks
Actually it was pretty straightforward:
first check the names of the trackers using:
print( vizconnect.getTrackerDict() )
it may return something like this
'mouse_and_keyboard_walking'
along with some others e.g. inertia cube, then do
gTracker = vizconnect.getTracker( 'mouse_and_keyboard_walking' )
or
gTracker = vizconnect.getTracker( 'PPT0#WorldViz-PC' )
then periodically call (probably on a callback() ):-
x, y, z = gTracker.getPosition()