Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Imported Model is rotated by 90 degrees #849

Closed
ruzannakamalyan opened this issue Apr 5, 2016 · 39 comments · Fixed by #5494
Closed

Imported Model is rotated by 90 degrees #849

ruzannakamalyan opened this issue Apr 5, 2016 · 39 comments · Fixed by #5494
Labels
Bug Global flag to mark a deviation from expected behaviour Postprocessing Issues regarding the post-processing steps.
Milestone

Comments

@ruzannakamalyan
Copy link

Hi,

I'm using the latest version of Assimp and Assimp.Net 3.0 release.
After Importing I'm getting the model rotated by 90 degrees.

Any suggestions on what I'm missing?

@kimkulling
Copy link
Member

Have you used the transformation matrix of the root node?

@ruzannakamalyan
Copy link
Author

Hi Kim, thanks for your prompt answer. I'll post the processing function. The first call is for model.RootNode, where the model is the imported model.

public GameObject processNodes(Assimp.Node rootNode) {
        GameObject unityRoot  = new GameObject( rootNode.Name );
                 if ( rootNode.HasChildren ) {
            foreach ( Assimp.Node childNode in rootNode.Children ) {
                GameObject unityChild = processNodes( childNode );
                unityChild.transform.parent = unityRoot .transform;
            }
        }
        Assimp.Vector3D position, scaling;
        Assimp.Quaternion rotation;
        rootNode.Transform.Decompose( out scaling, out rotation, out position );
        unityRoot.transform.localPosition = new Vector3( position.X, position.Y, position.Z );
        unityRoot.transform.localRotation = new UnityEngine.Quaternion( rotation.X, rotation.Y, rotation.Z, rotation.W );
        unityRoot.transform.localScale = new Vector3( scaling.X, scaling.Y, scaling.Z );
} 

@kimkulling
Copy link
Member

Hm, which loader are you using. Maybe there is something wrong. Your code looks fine for me.

@ruzannakamalyan
Copy link
Author

I'm trying to load an fbx model with Assimp in Unity. And I'm sorry, there was a little mistake in my first question, I have edited it. Actually I'm using Assimp.Net version 3.0, because with the latest release of it or the code from the master branch an exception is thrown at the file importing step.

AssimpImporter importer = new AssimpImporter();
MeshVertexLimitConfig m = new MeshVertexLimitConfig (meshVertexLimit);
importer.SetConfig (m);
Scene model = importer.ImportFile(path + '/' + modelName,
            PostProcessSteps.SplitLargeMeshes |
            PostProcessSteps.OptimizeGraph |
            PostProcessSteps.OptimizeMeshes |
            PostProcessSteps.Triangulate |
            PostProcessSteps.SortByPrimitiveType |
            PostProcessPreset.TargetRealTimeMaximumQuality
        );

@ruzannakamalyan
Copy link
Author

ruzannakamalyan commented Apr 8, 2016

Hi @kimkulling ,

I have found some points about the issue. My exported model has Z-up axis, instead of Y-up axis (unlike assimp), that's why the model is rotated. Is there a way I can get that info from the model (how the axises are set up in the model)?
Thanks.

@smalcom
Copy link
Contributor

smalcom commented Aug 1, 2016

Up-axis usually taken from file format specification. Or from description of a camera if scene is contain it.
Also you must pay attention to your graphics library. For example - OpenGL by default has Y-up axis.

@BBlayne
Copy link

BBlayne commented Oct 17, 2016

Hey I have a similar issue but annoyingly everything LOOKS fine when imported, but the Armature itself when I peek at the code is rotated by 90 degrees, this means when I try to get the world space coordinates of the bones, all non-root bones have the Y and Z columns flipped and with the wrong sign.

@ne0ndrag0n
Copy link

I think Assimp may be applying the wrong transform to mRootNode. Isn't a scene-level, root node transform supposed to be identity? Here's my debug output for scene->mRootNode->mTransform for a Z_UP COLLADA file containing two sibling boxes at the scene level:

(1.000000, 0.000000, 0.000000, 0.000000)
(0.000000, 0.000000, -1.000000, 0.000000) 
(0.000000, 1.000000, 0.000000, 0.000000) 
(0.000000, 0.000000, 0.000000, 1.000000)

Inside the COLLADA file, the only transform matricies specified are on the boxes themselves, not on the scene node, so I expected this to be an identity mat4. If I manually do scene->mRootNode->mTransformation = aiMatrix4x4(); before processing the tree, everything works as intended. If this isn't supposed to be an identity matrix, what is it?

I can send the COLLADA file if it assists in reproducing the (possible) issue.

@BBlayne
Copy link

BBlayne commented Dec 31, 2016

@ne0ndrag0n I think its because whatever program you used uses different coordinates; its similar I think to my problem above; for me, blender is Z up and Y forward; but when imported it's rotated 90 degrees if I don't have "Experimental apply transforms" enabled.

Similarly I get problems with the Blender bone orientations; depending on their bone roll ASSIMP doesn't import them in properly.

@kimkulling
Copy link
Member

Any chance to get the Collada model? And sorry for my late response!

Kim

@catdsnny
Copy link

Had the same issue when loading a model into opengl based C++ engine. Flipping Y and Z coordinates on load solved the problem for me.

@Alexees
Copy link

Alexees commented Mar 29, 2018

@ruzannakamalyan @kimkulling I just came across the same issue. A model (fbx) that is correctly rotated when dragging into Unity's project folder structure is rotated 90 degree around the x axis relative to its coordinate system when imported through TriLib. When I do the following in the AssetLoader.cs (703) its correct, but since it depends on the model I can't do that:

unityVertices[v] = AssimpInterop.aiMesh_GetVertex(mesh, v);
unityVertices[v] = Matrix4x4.Rotate(Quaternion.Euler(-90, 0, 0)) * unityVertices[v];

Since this is an AssimpInterop call (native) I don't even know how to fix that.
Did you guys have a solution for this, implemented or recommended or anything?

@JrIndie
Copy link

JrIndie commented Jun 18, 2018

I also am currently facing this issue. Is there any solution? Other than manually fixing matrix while parsing? I am using aiProcess_MakeLeftHanded, since I am using DirectX. however, its still rotated sideways. You would have to flip the y, and z axis, but internally it might be calculating using wrong rotation.

I am using blender exporting to collada.

@kimkulling kimkulling added the Bug Global flag to mark a deviation from expected behaviour label Dec 8, 2018
@kimkulling
Copy link
Member

I was pretty sure that this one should be fixed already. But unfortunately it seems to be still there.

@Kerilk
Copy link

Kerilk commented Jan 30, 2019

I encountered a strange version of this bug where only the mesh y and z are swapped, the skeleton is properly oriented.

The model is a collada file, that seemingly got exported from Max. It can be found here:
https://www.models-resource.com/wii_u/supersmashbrosforwiiu/model/11907/

The flags I used to import the model are:
[:JoinIdenticalVertices, :CalcTangentSpace, :FlipWindingOrder, :Triangulate, :FlipUVs]
(from my ruby bindings)

I would add that, though the model is facing upwards in blender, the skeleton is aligned to the model.

@rickomax
Copy link
Contributor

rickomax commented Apr 8, 2019

@kimkulling I guess the error is caused because the root node is transformed when a different Y axis value is found, but the application that loads the model isn't aware of this orientation and reset the root node rotation according the values user passes. I've fixed it in TriLib by adding a new node on the top of the root node, to mantain the original matrix.

@dfb
Copy link

dfb commented May 1, 2019

I'm having the same issue using latest assimp code built from source in git. The problem shows up in open3dmod too - in both cases the object is on its side.

But if I open the same file in Blender (2.79 or 2.80b) or in the Windows 3D file viewer, it is oriented properly.

File is attached.

plantSnakePlant.zip

Edit: I also imported it into UE4 and uploaded it to https://viewer.autodesk.com and they also show it oriented as expected.

@codiemorgan
Copy link

codiemorgan commented Sep 17, 2019

encountering the same issue with Assimp.Net (3) (also loading via Unity), when I perform -90 rotation to correct for YZ flip.

To solve I have to flip each vectors X-Axis manually, and also apply reverse winding order.
(winding order to fix faces becoming inverted).
This also requires me to invert my normals manually as well.
This seems like a matrix issue within assimp.

Unity Workaround (Assimp.Net) (assumes you dont need tangents, you you can just flip X axis there too), you do not need to adjust UVs.

// Per Vertex:
#if ASSIMP_BUG_WORKAROUND
vert.x = -vert.x;
nrm .x = -nrm.x;
#endif

#if ASSIMP_BUG_WORKAROUND
// Assimp Config (to solve faces flipping and facing inwards)
steps |= PostProcessSteps.FlipWindingOrder;
#endif

@mlavik1
Copy link
Contributor

mlavik1 commented Oct 7, 2019

I also have this issue on a few models. On these models, this seems to be caused by the"UpAxis" and "FrontAxis" (GlobalSettings) properties not being used. Assimp reads these and saves them, but does not seem to use them for anything. Should these be applied to the root node transform maybe?

I can't find much documentation about "UpAxis" and "FrontAxis", but after some testing I have the impression that:
0 = X
1= Y
2= Z

Example:

GlobalSettings:  {
	Version: 1000
	Properties70:  {
		P: "UpAxis", "int", "Integer", "",2
		P: "UpAxisSign", "int", "Integer", "",1
		P: "FrontAxis", "int", "Integer", "",1
		P: "FrontAxisSign", "int", "Integer", "",-1
		P: "CoordAxis", "int", "Integer", "",0
		P: "CoordAxisSign", "int", "Integer", "",1
		P: "OriginalUpAxis", "int", "Integer", "",2
		P: "OriginalUpAxisSign", "int", "Integer", "",1
		.... more...
	}
}

@mlavik1
Copy link
Contributor

mlavik1 commented Oct 9, 2019

Update:
Assimp reads the axis properties (GlobalSettings), and stores them in in the aiScene's metadata.
They can be accessed like this:

int upAxis = 0;
scene->mMetaData->Get<int>("UpAxis", upAxis);
int upAxisSign = 1;
scene->mMetaData->Get<int>("UpAxisSign", upAxisSign);

etc..

Converting the 0,1,2 values to unit vectors vectors (right, up, forward), constructing a matrix from these and multiplying the scene's root node transform by it solved the issue for me.

Creating the matrix:

aiVector3D upVec = upAxis == 0 ? aiVector3D(upAxisSign,0,0) : upAxis == 1 ? aiVector3D(0, upAxisSign,0) : aiVector3D(0, 0, upAxisSign);
aiVector3D forwardVec = frontAxis == 0 ? aiVector3D(frontAxisSign, 0, 0) : frontAxis == 1 ? aiVector3D(0, frontAxisSign, 0) : aiVector3D(0, 0, frontAxisSign);
aiVector3D rightVec = coordAxis == 0 ? aiVector3D(coordAxisSign, 0, 0) : coordAxis == 1 ? aiVector3D(0, coordAxisSign, 0) : aiVector3D(0, 0, coordAxisSign);
aiMatrix4x4 mat(rightVec.x, rightVec.y, rightVec.z, 0.0f,
   upVec.x, upVec.y, upVec.z, 0.0f,
   forwardVec.x, forwardVec.y, forwardVec.z, 0.0f,
   0.0f, 0.0f, 0.0f, 1.0f);

And then finally, multiply the scene->mRootNode->mTransformation with this matrix.

@kimkulling should this be done automatically, or is it better to leave it up to the developer? It seems like FBXSDK does this automatically.

@mlavik1
Copy link
Contributor

mlavik1 commented Oct 9, 2019

And here are some test models:
XYZ arrows.zip

To test, open "XYZ arrows ascii.FBX" and modify the axis information (under "GlobalSettings").

If anyone wants to test that their results are correct, you can open the model in FBX Review to compare.

@skylineMax
Copy link

@mlavik1 thanks for your job, works fine

@kkulling
Copy link
Contributor

I would say do it automatically makes sense for me. As far as I know previous users encountered the sme problem before.

@Dariusz1989
Copy link

+1 to that issue. If any1 could add a setting/config where we could specify up/front/right axis and let the system auto convert it to correct axis that would be great!

And if any1 can remove the darn "$AssimpFbx$" transform hierarchies for FBX imports that would be a lot of help too... half of my data import out of wack :- )

@kimkulling kimkulling added the Postprocessing Issues regarding the post-processing steps. label May 29, 2020
@livinamuk
Copy link

+1 would kill for a real fix

@medranSolus
Copy link

+1, guys it's been over 4 years since it was opened..

@jpincho
Copy link

jpincho commented Jan 27, 2021

+1

1 similar comment
@msnh2012
Copy link

+1

@bomkallo
Copy link

I wonder whether blender export module for fbx is broken or it's still assimp malfunction. The thing is that if you export .obj in blender and you set export axes properly you will get correct output in desired coordinate system. In case of FBX - blender allows for setting up axes but during assimp import you'll get no results.

As someone stated in #456 - assimp makes no transformations so it means blender exporter is invalid and if you import any fbx you need to fix its orientation. Well, I don't know the fbx internals, but it's also possible that this format tends to live in Z-up.

PS: I know it's old, but it's possible that someone will have problm with blender export and rotated axes.

@BradfordMedeiros
Copy link

Also encountering the issue with blender / assimp combo (just using a build of assimp of head).
he model looks correct but the transforms of the nodes are rotated -90 degrees on the x-axis.

I haven't verified if it's assimp or the fbx file being exported from blender.
However, when I use the experimental "apply transform" button in blender, the rotations are correct, but apparently this causes issues with parented nodes/ armatures/animations?

Might be useful for the above people to reply if this is also happening only in blender or elsewhere as well.

Maybe not in the scope of assimp, but esp since fbx model format harder to read, I assume this gets confusing for people using the library. Wonder if there are any good analysis tools to deconstruct the fbx file so I can definitely say this is on blender

@jpincho
Copy link

jpincho commented Jul 7, 2021

I'm currently using this snippet of code to fix that orientation, and it works perfectly for me.

	if (Scene->mMetaData)
	{
		int32_t UpAxis = 1, UpAxisSign = 1, FrontAxis = 2, FrontAxisSign = 1, CoordAxis = 0, CoordAxisSign = 1;
		double UnitScaleFactor = 1.0;
		for (unsigned MetadataIndex = 0; MetadataIndex < Scene->mMetaData->mNumProperties; ++MetadataIndex)
		{
			if (strcmp(Scene->mMetaData->mKeys[MetadataIndex].C_Str(), "UpAxis") == 0)
			{
				Scene->mMetaData->Get<int32_t>(MetadataIndex, UpAxis);
			}
			if (strcmp(Scene->mMetaData->mKeys[MetadataIndex].C_Str(), "UpAxisSign") == 0)
			{
				Scene->mMetaData->Get<int32_t>(MetadataIndex, UpAxisSign);
			}
			if (strcmp(Scene->mMetaData->mKeys[MetadataIndex].C_Str(), "FrontAxis") == 0)
			{
				Scene->mMetaData->Get<int32_t>(MetadataIndex, FrontAxis);
			}
			if (strcmp(Scene->mMetaData->mKeys[MetadataIndex].C_Str(), "FrontAxisSign") == 0)
			{
				Scene->mMetaData->Get<int32_t>(MetadataIndex, FrontAxisSign);
			}
			if (strcmp(Scene->mMetaData->mKeys[MetadataIndex].C_Str(), "CoordAxis") == 0)
			{
				Scene->mMetaData->Get<int32_t>(MetadataIndex, CoordAxis);
			}
			if (strcmp(Scene->mMetaData->mKeys[MetadataIndex].C_Str(), "CoordAxisSign") == 0)
			{
				Scene->mMetaData->Get<int32_t>(MetadataIndex, CoordAxisSign);
			}
			if (strcmp(Scene->mMetaData->mKeys[MetadataIndex].C_Str(), "UnitScaleFactor") == 0)
			{
				Scene->mMetaData->Get<double>(MetadataIndex, UnitScaleFactor);
			}
		}

		aiVector3D upVec, forwardVec, rightVec;

		upVec[UpAxis] = UpAxisSign * (float)UnitScaleFactor;
		forwardVec[FrontAxis] = FrontAxisSign * (float)UnitScaleFactor;
		rightVec[CoordAxis] = CoordAxisSign * (float)UnitScaleFactor;

		aiMatrix4x4 mat(rightVec.x, rightVec.y, rightVec.z, 0.0f,
			upVec.x, upVec.y, upVec.z, 0.0f,
			forwardVec.x, forwardVec.y, forwardVec.z, 0.0f,
			0.0f, 0.0f, 0.0f, 1.0f);
		Scene->mRootNode->mTransformation = mat;
	}

Then obviously reading the value from mTransformation :)

@jpincho
Copy link

jpincho commented Jul 7, 2021

Apparently, FBX allows some arbitrary orientations of the default axis, blender also supports it, but when loading it through assimp, those orientations are not reflected by default. You have to read them from the metadata.
I think assimp should do this automatically. It's just checking if that data is present, and if so, assign it to the root node.
It may be format specific, but that's why we're using assimp, to abstract from that.

@seyitcemyilmaz
Copy link

I'm currently using this snippet of code to fix that orientation, and it works perfectly for me.

	if (Scene->mMetaData)
	{
		int32_t UpAxis = 1, UpAxisSign = 1, FrontAxis = 2, FrontAxisSign = 1, CoordAxis = 0, CoordAxisSign = 1;
		double UnitScaleFactor = 1.0;
		for (unsigned MetadataIndex = 0; MetadataIndex < Scene->mMetaData->mNumProperties; ++MetadataIndex)
		{
			if (strcmp(Scene->mMetaData->mKeys[MetadataIndex].C_Str(), "UpAxis") == 0)
			{
				Scene->mMetaData->Get<int32_t>(MetadataIndex, UpAxis);
			}
			if (strcmp(Scene->mMetaData->mKeys[MetadataIndex].C_Str(), "UpAxisSign") == 0)
			{
				Scene->mMetaData->Get<int32_t>(MetadataIndex, UpAxisSign);
			}
			if (strcmp(Scene->mMetaData->mKeys[MetadataIndex].C_Str(), "FrontAxis") == 0)
			{
				Scene->mMetaData->Get<int32_t>(MetadataIndex, FrontAxis);
			}
			if (strcmp(Scene->mMetaData->mKeys[MetadataIndex].C_Str(), "FrontAxisSign") == 0)
			{
				Scene->mMetaData->Get<int32_t>(MetadataIndex, FrontAxisSign);
			}
			if (strcmp(Scene->mMetaData->mKeys[MetadataIndex].C_Str(), "CoordAxis") == 0)
			{
				Scene->mMetaData->Get<int32_t>(MetadataIndex, CoordAxis);
			}
			if (strcmp(Scene->mMetaData->mKeys[MetadataIndex].C_Str(), "CoordAxisSign") == 0)
			{
				Scene->mMetaData->Get<int32_t>(MetadataIndex, CoordAxisSign);
			}
			if (strcmp(Scene->mMetaData->mKeys[MetadataIndex].C_Str(), "UnitScaleFactor") == 0)
			{
				Scene->mMetaData->Get<double>(MetadataIndex, UnitScaleFactor);
			}
		}

		aiVector3D upVec, forwardVec, rightVec;

		upVec[UpAxis] = UpAxisSign * (float)UnitScaleFactor;
		forwardVec[FrontAxis] = FrontAxisSign * (float)UnitScaleFactor;
		rightVec[CoordAxis] = CoordAxisSign * (float)UnitScaleFactor;

		aiMatrix4x4 mat(rightVec.x, rightVec.y, rightVec.z, 0.0f,
			upVec.x, upVec.y, upVec.z, 0.0f,
			forwardVec.x, forwardVec.y, forwardVec.z, 0.0f,
			0.0f, 0.0f, 0.0f, 1.0f);
		Scene->mRootNode->mTransformation = mat;
	}

Then obviously reading the value from mTransformation :)

It solved my wrong rotationed bone problem. Thank you very much.

@Danil0v3s
Copy link

Found this thread yesterday and since I'm using the AssimpNet wrapper and was having the same problem I thought maybe I could fix this in blender since I'm not really keen to change any cpp then do all the other steps to make it load properly in Unity.

Here's the gist https://gist.github.com/Danil0v3s/4c923d51d148d61a3b4feaf34b77a644

What it does is use the official valve plugin to load the SMD, then I apply the rotation, save it to a new folder and reload the entire project as a new file. That way the process won't eventually fill up and get extremely slow

@JulianKnodt
Copy link
Contributor

I think the issue is also because assimp does not respect the root node transformation of the scene hierarchy. I've been messing around with it and it seems that it will swap axes, and while it can output correctly to GLB if the root node transform rotates it, with FBX modifying the root node transform does not seem to have any effect regardless of what the transform is

@netpenthe
Copy link

+1, would be great to fix

@kimkulling
Copy link
Member

After a looong period of time I found the issue again: #5494

Feedback would be hightly appreciated.

@livinamuk
Copy link

This is huge. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Global flag to mark a deviation from expected behaviour Postprocessing Issues regarding the post-processing steps.
Projects
Development

Successfully merging a pull request may close this issue.