private static void InitProgress(Module.Import.Assimp.Context context, Module.ProgressCallback callback, aiScene scene) { uint nb_textures = 0; using (aiMaterialArray assimp_materials = scene.Materials) { uint nb_materials = assimp_materials.Size(); for (uint i = 0; i < nb_materials; i++) { using (aiMaterial material = assimp_materials.Get(i)) { foreach (KeyValuePair <string, aiTextureType> pair in Assimp.Convert.textureTypes) { using (aiString texture_name = new aiString()) { if (material.GetTexturePath(pair.Value, 0, texture_name)) { nb_textures++; } } } } } } uint nb_steps = Node.ASSIMP_PROGRESS_FACTOR * CountNodes(scene.mRootNode); nb_steps += Mesh.ASSIMP_PROGRESS_FACTOR * scene.Meshes.Size(); nb_steps += Material.ASSIMP_PROGRESS_FACTOR * scene.Materials.Size(); nb_steps += Texture.ASSIMP_PROGRESS_FACTOR * nb_textures; nb_steps = (uint)(nb_steps / (1f - assimpNativeLoadingPrecentage)); context.progress.Init(nb_steps, callback); }
public static void FromAssimp(Module.Import.Assimp.Context context, aiScene scene) { if (scene.HasMaterials()) { using (aiMaterialArray assimp_materials = scene.Materials) { uint material_size = assimp_materials.Size(); // Reserve the right amount of memory context.scene.materials = new Material[(int)material_size + 1]; // Create a material for lines Material line_material = new Material(); line_material.name = "Line"; line_material.shader = defaultAssimpLineShader; line_material.floats = new Dictionary <string, float>(); line_material.colors = new Dictionary <string, Color>(); line_material.floats.Add(defaultAssimpLineShaderWidth, unityLineWidth); line_material.colors.Add(Assimp.Convert.unityDiffuseColorName, Color.black); context.scene.materials[(int)material_size] = line_material; // Load all the materials for (uint i = 0; i < material_size; i++) { aiMaterial material = assimp_materials.Get(i); // LoadMaterial must dispose of the given material afterward // We must use a proxy method for saving the result into the array because the index i is captured by the lambda otherwise and it's value is indefinite across multiple threads. context.threads.ExecAndSaveToArray(context.scene.materials, (int)i, () => FromAssimp(context, scene, material)); } } } }
private static void FromAssimpAlphaTexture(Module.Import.Assimp.Context context, Material material, aiMaterial material_data, aiScene scene, string unity_property, aiTextureType texture_type, Color default_color, Func <Color, float> op) { if (material_data.GetTextureCount(texture_type) > 0) { using (aiString texture_name = new aiString()) { if (material_data.GetTexturePath(texture_type, 0, texture_name)) { Texture alpha = new Texture(context, texture_name.ToString(), scene); Texture base_tex = null; Material.TextureParams param = material.GetTextureParams(unity_property); if (param != null) { CLARTE.Backport.Tuple <Texture, uint> res = context.scene.GetAssimpTexture(param.index); if (res != null) { base_tex = res.Item1; } else { Debug.LogErrorFormat("Invalid texture index. '{0}' was registered for material '{1}' as texture with index '{2}'. However no texture was found with this index.", unity_property, material.Name, param.index); } } else { CLARTE.Backport.Tuple <Texture, uint> assimp_tex = context.scene.GetAssimpTexture(Guid.NewGuid().ToString(), () => new Texture("**E", alpha.width, alpha.height, default_color)); material.AddTextureParams(unity_property, new Material.TextureParams(assimp_tex.Item2)); base_tex = assimp_tex.Item1; } if (base_tex != null) { base_tex.AddToAlpha(alpha, op); base_tex.filename = string.Format("**A{0}|{1}", base_tex.filename, texture_name.C_Str()); } } } } }
private void LoadFromAssimp(Module.Import.Assimp.Context context, string filename, aiScene scene) { if (!string.IsNullOrEmpty(filename)) { byte[] tex; if (filename[0] == '*') { // Embeded texture string index_str = filename.Remove(0, 1); uint index; if (uint.TryParse(index_str, out index)) { using (aiTextureArray array = scene.Textures) { if (index < array.Size()) { using (aiTexture texture = array.Get(index)) { DecodeTexture(filename, texture.data); } } else { Debug.LogError("Invalid embeded texture index \"" + index + "\" (out of bound)."); } } } else { Debug.LogError("Invalid embeded texture name \"" + filename + "\" (not an index)."); } } else if (context.importer != null && (tex = context.importer.GetTexture(filename)) != null) { DecodeTexture(filename, tex); } else { LoadFromFile(context.path, filename); } } }
private static void FromAssimpNormalsFromHeightmap(Module.Import.Assimp.Context context, Material material, aiMaterial material_data, aiScene scene) { if (material_data.GetTextureCount(Assimp.Convert.textureTypes[Assimp.Convert.unityBumpName]) <= 0) { using (aiString texture_name = new aiString()) { if (material_data.GetTexturePath(aiTextureType.aiTextureType_HEIGHT, 0, texture_name)) { string filename = string.Format("**N{0}", texture_name.C_Str()); material.AddTextureParams(Assimp.Convert.unityBumpName, new Material.TextureParams(context.scene.GetAssimpTexture(filename, () => { Texture texture = new Texture(context, texture_name.C_Str(), scene).HeightmapToNormals(0.5).Blur(0.5); texture.filename = filename; return(texture); }).Item2)); } } } }
public static void FromAssimp(Module.Import.Assimp.Context context, aiScene scene) { if (scene.HasMeshes()) { using (aiMeshArray meshes = scene.Meshes) { uint meshes_size = meshes.Size(); // Reserve the right amount of memory context.scene.meshes = new Mesh[(int)meshes_size]; // Load all the meshes for (uint i = 0; i < meshes_size; i++) { aiMesh mesh = meshes.Get(i); // LoadGeometry must dispose of the given mesh afterward // We must use a proxy method for saving the result into the array because the index i is captured by the lambda otherwise and it's value is indefinite across multiple threads. context.threads.ExecAndSaveToArray(context.scene.meshes, (int)i, () => FromAssimp(context, mesh)); } } } }
public static IEnumerator FromAssimp(Module.Import.Assimp.Context context, global::Assimp.Importer importer, Func <aiScene> loader, Module.ImporterReturnCallback return_callback, Module.ProgressCallback progress_callback) { if (progress_callback != null) { progress_callback(0.01f); } aiScene scene = null; // Async loading does not work in editor when not in play mode context.threads.AddTask(() => scene = loader()); IEnumerator it = context.threads.WaitForTasksCompletion(); while (it.MoveNext()) { context.progress.Display(); yield return(it.Current); } if (scene != null) { InitProgress(context, progress_callback, scene); context.progress.Update((uint)(assimpNativeLoadingPrecentage * context.progress.TotalSteps)); context.scene = new Scene(); context.scene.assimpTextures = new Dictionary <string, CLARTE.Backport.Tuple <Texture, uint> >(); context.threads.AddTask(() => Mesh.FromAssimp(context, scene)); context.threads.AddTask(() => Material.FromAssimp(context, scene)); context.threads.AddTask(() => context.scene.root_node = Node.FromAssimp(context, scene, scene.mRootNode)); it = context.threads.WaitForTasksCompletion(); while (it.MoveNext()) { context.progress.Display(); yield return(it.Current); } // Assign materials to meshes now that all data is available context.threads.AddTask(() => Node.SetAssimpMeshesMaterials(context.scene, context.scene.root_node)); // Assign textures to final array context.threads.AddTask(() => { context.scene.textures = new Texture[context.scene.assimpTextures.Count]; foreach (KeyValuePair <string, CLARTE.Backport.Tuple <Texture, uint> > pair in context.scene.assimpTextures) { context.scene.textures[pair.Value.Item2] = pair.Value.Item1; } }); // We can safelly dispose of the scene because all tasks must have been completed to reach this point, therefore we do not risque deallocating data used in another thread. // this method can take a few hundred milliseconds, therefore, we do it async and wait for it to complete before allowing new imports. context.threads.AddTask(scene.Dispose); context.threads.AddTask(importer.FreeScene); it = context.threads.WaitForTasksCompletion(); while (it.MoveNext()) { context.progress.Display(); yield return(it.Current); } // Clean up context.scene.assimpTextures = null; if (return_callback != null) { return_callback(context.scene); } context.Clean(); } else { Debug.LogErrorFormat("Failed to open file: {0}{1}{2}.\nThe importer reported the following error: {3}", context.path, System.IO.Path.PathSeparator, context.filename, importer.GetErrorString()); } }
private static Material FromAssimp(Module.Import.Assimp.Context context, aiScene scene, aiMaterial material_data) { Material material = new Material(); // Initialize dictionaries before hand because we do not know in advance wich ones we will need material.floats = new Dictionary <string, float>(); material.colors = new Dictionary <string, Color>(); material.textures = new Dictionary <string, TextureParams>(); // Name using (aiString material_name = new aiString()) { if (material_data.GetName(material_name)) { material.name = material_name.ToString(); } } // shader material.shader = Constants.defaultAssimpShader; // Shininess float shininess; if (material_data.GetShininess(out shininess)) { aiShadingMode shading; if (material_data.GetShadingModel(out shading)) { if (shading != aiShadingMode.aiShadingMode_Blinn && shading != aiShadingMode.aiShadingMode_Phong) { // Unsupported shading model Debug.LogWarningFormat("The shading model for material {0} is not supported. The value for the shininess is likely to be incorrect.", material.name); } } const int factor = 128; // unity shader factor shininess /= factor; } // Gloss float gloss; if (material_data.GetShininessStrength(out gloss)) { shininess *= gloss; } // Reflectivity float reflectivity; if (material_data.GetReflectivity(out reflectivity)) { material.floats.Add(Assimp.Convert.unityMetallicValueName, reflectivity); } material.floats.Add(Assimp.Convert.unityGlossinessValueName, Smoothness(shininess, reflectivity)); // Colors foreach (KeyValuePair <string, Assimp.Convert.GetColor> pair in Assimp.Convert.GetColors(material_data)) { using (aiColor4D color = new aiColor4D()) { if (pair.Value(color)) { Color unity_color = Assimp.Convert.AssimpToUnity.Color(color); bool set_color = true; switch (pair.Key) { case Assimp.Convert.unityDiffuseColorName: // Global opacity float opacity; if (material_data.GetOpacity(out opacity) && opacity < 1.0f) { unity_color.a = opacity; material.floats.Add(Assimp.Convert.unityRenderModeName, (float)CLARTE.Shaders.Standard.Utility.BlendMode.TRANSPARENT); } break; case Assimp.Convert.unitySpecularColorName: // Specular color must be very close to black unity_color = 0.1f * unity_color; break; case Assimp.Convert.unityEmissiveColorName: if (!CLARTE.Shaders.Standard.Utility.ShouldEmissionBeEnabled(unity_color)) { set_color = false; } break; } if (set_color) { material.colors.Add(pair.Key, unity_color); } } } } // Textures foreach (KeyValuePair <string, aiTextureType> pair in Assimp.Convert.textureTypes) { // Make a copy to avoid problem of loop variable captured by reference by lambda expression string texture_key = pair.Key; aiTextureType texture_type = pair.Value; context.threads.AddTask(() => Texture.FromAssimp(context, material, scene, material_data, texture_key, texture_type, reflectivity)); } // We must dispose of the given parameter to free unused memory. However other tasks may still be using this material (i.e. textures), so we will let the garbage collector do it's job. //material_data.Dispose(); context.progress.Update(ASSIMP_PROGRESS_FACTOR); return(material); }
public static Node FromAssimp(Module.Import.Assimp.Context context, aiScene scene, aiNode assimp_node) { // Create new node object Node node = new Node(); // Get node ID node.id = context.id++; // Get node name node.name = Assimp.Convert.Name(assimp_node.mName, "node"); // Get node metadata using (aiMetadata meta = assimp_node.mMetaData) { UnityComponent metadata = UnityComponent.FromAssimpMetadata(meta); if (metadata != null) { node.components = new UnityComponent[] { metadata }; } } // Parse children recursively using (aiNodeArray children = assimp_node.Children) { uint children_size = children.Size(); if (children_size > 0) { node.children = new Node[children_size]; for (uint i = 0; i < children_size; i++) { aiNode child = children.Get(i); // ParseNode must dispose of the given node afterward // We must use a proxy method for saving the result into the array because the index i is captured by the lambda otherwise and it's value is indefinite across multiple threads. context.threads.ExecAndSaveToArray(node.children, (int)i, () => FromAssimp(context, scene, child)); } } } // Parse meshes associated to this node using (aiUIntArray meshes = assimp_node.Meshes) { uint meshes_size = meshes.Size(); if (meshes_size > 0) { int global_meshes_size = (int)scene.Meshes.Size(); node.meshes = new GraphicMesh[meshes_size]; for (uint j = 0; j < meshes_size; j++) { node.meshes[j] = new GraphicMesh(); if (j < global_meshes_size) { uint mesh_index = meshes.Get(j); node.meshes[j].meshIndex = (int)mesh_index; } } } } // Get the transform of this node using (aiVector3D position = new aiVector3D()) { using (aiVector3D scaling = new aiVector3D()) { using (aiQuaternion rotation = new aiQuaternion()) { assimp_node.mTransformation.Decompose(scaling, rotation, position); node.position = Assimp.Convert.AssimpToUnity.Vector3(position); node.rotation = Assimp.Convert.AssimpToUnity.Quaternion(rotation); node.scale = Assimp.Convert.AssimpToUnity.Vector3(scaling); } } } // We must dispose of the given parameter to avoid memory leaks assimp_node.Dispose(); context.progress.Update(ASSIMP_PROGRESS_FACTOR); return(node); }
public static void FromAssimp(Module.Import.Assimp.Context context, Material material, aiScene scene, aiMaterial material_data, string texture_key, aiTextureType texture_type, float reflectivity) { if (texture_type != aiTextureType.aiTextureType_NONE && material_data.GetTextureCount(texture_type) > 0) { using (aiString texture_name = new aiString()) { if (material_data.GetTexturePath(texture_type, 0, texture_name)) { string filename = texture_name.C_Str(); uint index = context.scene.GetAssimpTexture(filename, () => new Texture(context, filename, scene)).Item2; material.AddTextureParams(texture_key, new Material.TextureParams(index)); context.progress.Update(ASSIMP_PROGRESS_FACTOR); } } } Color default_color; // Add textures as alpha channel of existing textures, or compute normal map from heightmap if not defined. switch (texture_key) { case Assimp.Convert.unityMainTexName: default_color = Color.white; Color?diffuse = material.GetColor(Assimp.Convert.unityDiffuseColorName); if (diffuse.HasValue) { default_color = diffuse.Value; } // Opacity as main texture alpha channel FromAssimpAlphaTexture(context, material, material_data, scene, Assimp.Convert.unityMainTexName, aiTextureType.aiTextureType_OPACITY, default_color, c => c.grayscale); break; case Assimp.Convert.unityMetallicGlossName: default_color = Color.black; float?metallic = material.GetFloat(Assimp.Convert.unityMetallicValueName); if (metallic.HasValue) { default_color = new Color(metallic.Value, metallic.Value, metallic.Value, 1f); } // Shininess as alpha channel of metallic gloss map FromAssimpAlphaTexture(context, material, material_data, scene, Assimp.Convert.unityMetallicGlossName, aiTextureType.aiTextureType_SHININESS, default_color, c => Material.Smoothness(c.grayscale, reflectivity)); break; case Assimp.Convert.unitySpecGlossName: default_color = Color.black; Color?specular = material.GetColor(Assimp.Convert.unitySpecularColorName); if (specular.HasValue) { default_color = specular.Value; } // Shininess as alpha channel of specular gloss map FromAssimpAlphaTexture(context, material, material_data, scene, Assimp.Convert.unitySpecGlossName, aiTextureType.aiTextureType_SHININESS, default_color, c => Material.Smoothness(c.grayscale, reflectivity)); break; case Assimp.Convert.unityBumpName: // Bump mapping from heightmap if not defined from normals FromAssimpNormalsFromHeightmap(context, material, material_data, scene); break; } }
private Texture(Module.Import.Assimp.Context context, string filename, aiScene scene) { LoadFromAssimp(context, filename, scene); }
private static Mesh FromAssimp(Module.Import.Assimp.Context context, aiMesh mesh_data) { // Create new mesh Mesh mesh = new Mesh(); // Assimp does not support submeshes mesh.submeshes = new SubMesh[1]; mesh.submeshes[0] = new SubMesh(); // Get material associated to this mesh mesh.assimpMaterial = (int)mesh_data.mMaterialIndex; // Get mesh name using (aiString mesh_name = mesh_data.mName) { mesh.name = Assimp.Convert.Name(mesh_name, "mesh"); } // Get vertices if (mesh_data.HasPositions()) { using (aiVector3DArray vertices = mesh_data.Vertices) { mesh.vertices = Assimp.Convert.AssimpToUnity.Array <aiVector3D, Vector3>(Assimp.Convert.AssimpToUnity.Vector3, vertices); } } // Get normals if (mesh_data.HasNormals()) { using (aiVector3DArray normals = mesh_data.Normals) { mesh.normals = Assimp.Convert.AssimpToUnity.Array <aiVector3D, Vector3>(Assimp.Convert.AssimpToUnity.Vector3, normals); } } // Get tangents if (mesh_data.HasTangentsAndBitangents()) { using (aiVector3DArray tangents = mesh_data.Tangents) { mesh.tangents = Assimp.Convert.AssimpToUnity.Array <aiVector3D, Vector4>(Assimp.Convert.AssimpToUnity.Tangent, tangents); } } // Get faces if (mesh_data.HasFaces()) { using (aiFaceArray faces = mesh_data.Faces) { mesh.submeshes[0].triangles = Assimp.Convert.AssimpToUnity.Face(faces, out mesh.submeshes[0].topology); } } // Get UV coords if (mesh_data.GetNumUVChannels() > 0 && mesh_data.HasTextureCoords(0)) { using (aiVector3DMultiArray texture_coords = mesh_data.TextureCoords) { using (aiVector3DArray texture_coords0 = texture_coords.Get(0)) { mesh.uv1 = Assimp.Convert.AssimpToUnity.Array <aiVector3D, Vector2>(Assimp.Convert.AssimpToUnity.UV, texture_coords0); } if (mesh_data.GetNumUVChannels() > 1 && mesh_data.HasTextureCoords(1)) { using (aiVector3DArray texture_coords1 = texture_coords.Get(1)) { mesh.uv2 = Assimp.Convert.AssimpToUnity.Array <aiVector3D, Vector2>(Assimp.Convert.AssimpToUnity.UV, texture_coords1); } } } } else { // No texture UVs. We need to generate some to avoid problems with most default unity shaders int size = mesh.vertices.Length; mesh.uv1 = new Vector2[size]; } // Get vertex colors if (mesh_data.GetNumColorChannels() > 0 && mesh_data.HasVertexColors(0)) { using (aiColor4DMultiArray colors = mesh_data.Colors) { using (aiColor4DArray colors0 = colors.Get(0)) { mesh.colors = Assimp.Convert.AssimpToUnity.Array <aiColor4D, Color>(Assimp.Convert.AssimpToUnity.Color, colors0); } } } // TODO: anims + bones // We must dispose of the given parameter to free unused memory mesh_data.Dispose(); context.progress.Update(ASSIMP_PROGRESS_FACTOR); return(mesh); }