2017-03-05 15:47:28 +01:00
/*************************************************************************/
/* resource_importer_obj.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
2017-04-08 00:11:42 +02:00
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
2017-03-05 15:47:28 +01:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2017-02-03 04:08:50 +01:00
# include "resource_importer_obj.h"
# include "io/resource_saver.h"
2017-03-05 16:44:50 +01:00
# include "os/file_access.h"
2017-07-18 02:05:38 +02:00
# include "scene/3d/mesh_instance.h"
# include "scene/3d/spatial.h"
2017-02-03 04:08:50 +01:00
# include "scene/resources/mesh.h"
# include "scene/resources/surface_tool.h"
2017-07-18 02:05:38 +02:00
uint32_t EditorOBJImporter : : get_import_flags ( ) const {
2017-02-03 04:08:50 +01:00
2017-07-18 02:05:38 +02:00
return IMPORT_SCENE ;
2017-02-03 04:08:50 +01:00
}
2017-07-18 02:05:38 +02:00
void EditorOBJImporter : : get_extensions ( List < String > * r_extensions ) const {
2017-02-03 04:08:50 +01:00
2017-07-18 02:05:38 +02:00
r_extensions - > push_back ( " obj " ) ;
2017-02-03 04:08:50 +01:00
}
2017-07-18 02:05:38 +02:00
Error EditorOBJImporter : : _parse_material_library ( const String & p_path , Map < String , Ref < SpatialMaterial > > & material_map , List < String > * r_missing_deps ) {
2017-02-03 04:08:50 +01:00
2017-07-18 02:05:38 +02:00
FileAccessRef f = FileAccess : : open ( p_path , FileAccess : : READ ) ;
ERR_FAIL_COND_V ( ! f , ERR_CANT_OPEN ) ;
2017-02-03 04:08:50 +01:00
2017-07-18 02:05:38 +02:00
Ref < SpatialMaterial > current ;
String current_name ;
String base_path = p_path . get_base_dir ( ) ;
while ( true ) {
2017-02-03 04:08:50 +01:00
2017-07-18 02:05:38 +02:00
String l = f - > get_line ( ) . strip_edges ( ) ;
2017-02-03 04:08:50 +01:00
2017-07-18 02:05:38 +02:00
if ( l . begins_with ( " newmtl " ) ) {
//vertex
2017-02-03 04:08:50 +01:00
2017-07-18 02:05:38 +02:00
current_name = l . replace ( " newmtl " , " " ) . strip_edges ( ) ;
current . instance ( ) ;
material_map [ current_name ] = current ;
} else if ( l . begins_with ( " Ka " ) ) {
//uv
print_line ( " Warning: Ambient light for material ' " + current_name + " ' is ignored in PBR " ) ;
2017-02-03 04:08:50 +01:00
2017-07-18 02:05:38 +02:00
} else if ( l . begins_with ( " Kd " ) ) {
//normal
ERR_FAIL_COND_V ( current . is_null ( ) , ERR_FILE_CORRUPT ) ;
Vector < String > v = l . split ( " " , false ) ;
ERR_FAIL_COND_V ( v . size ( ) < 4 , ERR_INVALID_DATA ) ;
Color c = current - > get_albedo ( ) ;
c . r = v [ 1 ] . to_float ( ) ;
c . g = v [ 2 ] . to_float ( ) ;
c . b = v [ 3 ] . to_float ( ) ;
current - > set_albedo ( c ) ;
} else if ( l . begins_with ( " Ks " ) ) {
//normal
ERR_FAIL_COND_V ( current . is_null ( ) , ERR_FILE_CORRUPT ) ;
Vector < String > v = l . split ( " " , false ) ;
ERR_FAIL_COND_V ( v . size ( ) < 4 , ERR_INVALID_DATA ) ;
float r = v [ 1 ] . to_float ( ) ;
float g = v [ 2 ] . to_float ( ) ;
float b = v [ 3 ] . to_float ( ) ;
float metalness = MAX ( r , MAX ( g , b ) ) ;
current - > set_metallic ( metalness ) ;
} else if ( l . begins_with ( " Ns " ) ) {
//normal
ERR_FAIL_COND_V ( current . is_null ( ) , ERR_FILE_CORRUPT ) ;
Vector < String > v = l . split ( " " , false ) ;
ERR_FAIL_COND_V ( v . size ( ) ! = 2 , ERR_INVALID_DATA ) ;
float s = v [ 1 ] . to_float ( ) ;
current - > set_metallic ( ( 1000.0 - s ) / 1000.0 ) ;
} else if ( l . begins_with ( " d " ) ) {
//normal
ERR_FAIL_COND_V ( current . is_null ( ) , ERR_FILE_CORRUPT ) ;
Vector < String > v = l . split ( " " , false ) ;
ERR_FAIL_COND_V ( v . size ( ) ! = 2 , ERR_INVALID_DATA ) ;
float d = v [ 1 ] . to_float ( ) ;
Color c = current - > get_albedo ( ) ;
c . a = d ;
current - > set_albedo ( c ) ;
if ( c . a < 0.99 ) {
current - > set_feature ( SpatialMaterial : : FEATURE_TRANSPARENT , true ) ;
}
} else if ( l . begins_with ( " Tr " ) ) {
//normal
ERR_FAIL_COND_V ( current . is_null ( ) , ERR_FILE_CORRUPT ) ;
Vector < String > v = l . split ( " " , false ) ;
ERR_FAIL_COND_V ( v . size ( ) ! = 2 , ERR_INVALID_DATA ) ;
float d = v [ 1 ] . to_float ( ) ;
Color c = current - > get_albedo ( ) ;
c . a = 1.0 - d ;
current - > set_albedo ( c ) ;
if ( c . a < 0.99 ) {
current - > set_feature ( SpatialMaterial : : FEATURE_TRANSPARENT , true ) ;
}
} else if ( l . begins_with ( " map_Ka " ) ) {
//uv
print_line ( " Warning: Ambient light texture for material ' " + current_name + " ' is ignored in PBR " ) ;
} else if ( l . begins_with ( " map_Kd " ) ) {
//normal
ERR_FAIL_COND_V ( current . is_null ( ) , ERR_FILE_CORRUPT ) ;
String p = l . replace ( " map_Kd " , " " ) . replace ( " \\ " , " / " ) . strip_edges ( ) ;
String path = base_path . plus_file ( p ) ;
Ref < Texture > texture = ResourceLoader : : load ( path ) ;
if ( texture . is_valid ( ) ) {
current - > set_texture ( SpatialMaterial : : TEXTURE_ALBEDO , texture ) ;
} else {
r_missing_deps - > push_back ( path ) ;
}
} else if ( l . begins_with ( " map_Ks " ) ) {
//normal
ERR_FAIL_COND_V ( current . is_null ( ) , ERR_FILE_CORRUPT ) ;
String p = l . replace ( " map_Ks " , " " ) . replace ( " \\ " , " / " ) . strip_edges ( ) ;
String path = base_path . plus_file ( p ) ;
Ref < Texture > texture = ResourceLoader : : load ( path ) ;
if ( texture . is_valid ( ) ) {
current - > set_texture ( SpatialMaterial : : TEXTURE_METALLIC , texture ) ;
} else {
r_missing_deps - > push_back ( path ) ;
}
} else if ( l . begins_with ( " map_Ns " ) ) {
//normal
ERR_FAIL_COND_V ( current . is_null ( ) , ERR_FILE_CORRUPT ) ;
String p = l . replace ( " map_Ns " , " " ) . replace ( " \\ " , " / " ) . strip_edges ( ) ;
String path = base_path . plus_file ( p ) ;
Ref < Texture > texture = ResourceLoader : : load ( path ) ;
if ( texture . is_valid ( ) ) {
current - > set_texture ( SpatialMaterial : : TEXTURE_ROUGHNESS , texture ) ;
} else {
r_missing_deps - > push_back ( path ) ;
}
} else if ( l . begins_with ( " map_bump " ) ) {
//normal
ERR_FAIL_COND_V ( current . is_null ( ) , ERR_FILE_CORRUPT ) ;
String p = l . replace ( " map_bump " , " " ) . replace ( " \\ " , " / " ) . strip_edges ( ) ;
String path = base_path . plus_file ( p ) ;
2017-02-03 04:08:50 +01:00
2017-07-18 02:05:38 +02:00
Ref < Texture > texture = ResourceLoader : : load ( path ) ;
if ( texture . is_valid ( ) ) {
current - > set_feature ( SpatialMaterial : : FEATURE_NORMAL_MAPPING , true ) ;
current - > set_texture ( SpatialMaterial : : TEXTURE_NORMAL , texture ) ;
} else {
r_missing_deps - > push_back ( path ) ;
}
} else if ( f - > eof_reached ( ) ) {
break ;
}
}
return OK ;
2017-02-03 04:08:50 +01:00
}
2017-07-18 02:05:38 +02:00
Node * EditorOBJImporter : : import_scene ( const String & p_path , uint32_t p_flags , int p_bake_fps , List < String > * r_missing_deps , Error * r_err ) {
2017-02-03 04:08:50 +01:00
2017-07-18 02:05:38 +02:00
FileAccessRef f = FileAccess : : open ( p_path , FileAccess : : READ ) ;
if ( r_err ) {
* r_err = ERR_CANT_OPEN ;
}
ERR_FAIL_COND_V ( ! f , NULL ) ;
if ( r_err ) {
* r_err = OK ;
}
Spatial * scene = memnew ( Spatial ) ;
Ref < ArrayMesh > mesh ;
mesh . instance ( ) ;
2017-02-03 04:08:50 +01:00
2017-03-05 16:44:50 +01:00
Map < String , Ref < Material > > name_map ;
2017-02-03 04:08:50 +01:00
2017-07-18 02:05:38 +02:00
bool generate_tangents = p_flags & IMPORT_GENERATE_TANGENT_ARRAYS ;
bool flip_faces = false ;
//bool flip_faces = p_options["force/flip_faces"];
//bool force_smooth = p_options["force/smooth_shading"];
//bool weld_vertices = p_options["force/weld_vertices"];
//float weld_tolerance = p_options["force/weld_tolerance"];
2017-02-03 04:08:50 +01:00
Vector < Vector3 > vertices ;
Vector < Vector3 > normals ;
Vector < Vector2 > uvs ;
String name ;
2017-07-18 02:05:38 +02:00
Map < String , Map < String , Ref < SpatialMaterial > > > material_map ;
2017-03-05 16:44:50 +01:00
Ref < SurfaceTool > surf_tool = memnew ( SurfaceTool ) ;
2017-02-03 04:08:50 +01:00
surf_tool - > begin ( Mesh : : PRIMITIVE_TRIANGLES ) ;
2017-07-18 02:05:38 +02:00
String current_material_library ;
String current_material ;
String current_group ;
2017-02-03 04:08:50 +01:00
2017-03-05 16:44:50 +01:00
while ( true ) {
2017-02-03 04:08:50 +01:00
String l = f - > get_line ( ) . strip_edges ( ) ;
if ( l . begins_with ( " v " ) ) {
//vertex
2017-03-05 16:44:50 +01:00
Vector < String > v = l . split ( " " , false ) ;
2017-07-18 02:05:38 +02:00
ERR_FAIL_COND_V ( v . size ( ) < 4 , NULL ) ;
2017-02-03 04:08:50 +01:00
Vector3 vtx ;
2017-03-05 16:44:50 +01:00
vtx . x = v [ 1 ] . to_float ( ) ;
vtx . y = v [ 2 ] . to_float ( ) ;
vtx . z = v [ 3 ] . to_float ( ) ;
2017-02-03 04:08:50 +01:00
vertices . push_back ( vtx ) ;
2017-03-05 16:44:50 +01:00
} else if ( l . begins_with ( " vt " ) ) {
2017-02-03 04:08:50 +01:00
//uv
2017-03-05 16:44:50 +01:00
Vector < String > v = l . split ( " " , false ) ;
2017-07-18 02:05:38 +02:00
ERR_FAIL_COND_V ( v . size ( ) < 3 , NULL ) ;
2017-02-03 04:08:50 +01:00
Vector2 uv ;
2017-03-05 16:44:50 +01:00
uv . x = v [ 1 ] . to_float ( ) ;
uv . y = 1.0 - v [ 2 ] . to_float ( ) ;
2017-02-03 04:08:50 +01:00
uvs . push_back ( uv ) ;
} else if ( l . begins_with ( " vn " ) ) {
//normal
2017-03-05 16:44:50 +01:00
Vector < String > v = l . split ( " " , false ) ;
2017-07-18 02:05:38 +02:00
ERR_FAIL_COND_V ( v . size ( ) < 4 , NULL ) ;
2017-02-03 04:08:50 +01:00
Vector3 nrm ;
2017-03-05 16:44:50 +01:00
nrm . x = v [ 1 ] . to_float ( ) ;
nrm . y = v [ 2 ] . to_float ( ) ;
nrm . z = v [ 3 ] . to_float ( ) ;
2017-02-03 04:08:50 +01:00
normals . push_back ( nrm ) ;
2017-02-23 09:28:09 +01:00
} else if ( l . begins_with ( " f " ) ) {
2017-03-05 16:44:50 +01:00
//vertex
2017-02-03 04:08:50 +01:00
2017-03-05 16:44:50 +01:00
Vector < String > v = l . split ( " " , false ) ;
2017-07-18 02:05:38 +02:00
ERR_FAIL_COND_V ( v . size ( ) < 4 , NULL ) ;
2017-02-03 04:08:50 +01:00
//not very fast, could be sped up
Vector < String > face [ 3 ] ;
face [ 0 ] = v [ 1 ] . split ( " / " ) ;
face [ 1 ] = v [ 2 ] . split ( " / " ) ;
2017-07-18 02:05:38 +02:00
ERR_FAIL_COND_V ( face [ 0 ] . size ( ) = = 0 , NULL ) ;
ERR_FAIL_COND_V ( face [ 0 ] . size ( ) ! = face [ 1 ] . size ( ) , NULL ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 2 ; i < v . size ( ) - 1 ; i + + ) {
2017-02-03 04:08:50 +01:00
2017-03-05 16:44:50 +01:00
face [ 2 ] = v [ i + 1 ] . split ( " / " ) ;
2017-07-18 02:05:38 +02:00
ERR_FAIL_COND_V ( face [ 0 ] . size ( ) ! = face [ 2 ] . size ( ) , NULL ) ;
2017-03-05 16:44:50 +01:00
for ( int j = 0 ; j < 3 ; j + + ) {
2017-02-03 04:08:50 +01:00
2017-03-05 16:44:50 +01:00
int idx = j ;
2017-02-03 04:08:50 +01:00
2017-03-05 16:44:50 +01:00
if ( ! flip_faces & & idx < 2 ) {
idx = 1 ^ idx ;
2017-02-03 04:08:50 +01:00
}
2017-03-05 16:44:50 +01:00
if ( face [ idx ] . size ( ) = = 3 ) {
int norm = face [ idx ] [ 2 ] . to_int ( ) - 1 ;
2017-04-27 10:24:09 +02:00
if ( norm < 0 )
norm + = normals . size ( ) + 1 ;
2017-07-18 02:05:38 +02:00
ERR_FAIL_INDEX_V ( norm , normals . size ( ) , NULL ) ;
2017-02-03 04:08:50 +01:00
surf_tool - > add_normal ( normals [ norm ] ) ;
}
2017-03-05 16:44:50 +01:00
if ( face [ idx ] . size ( ) > = 2 & & face [ idx ] [ 1 ] ! = String ( ) ) {
int uv = face [ idx ] [ 1 ] . to_int ( ) - 1 ;
2017-04-27 10:24:09 +02:00
if ( uv < 0 )
uv + = uvs . size ( ) + 1 ;
2017-07-18 02:05:38 +02:00
ERR_FAIL_INDEX_V ( uv , uvs . size ( ) , NULL ) ;
2017-02-03 04:08:50 +01:00
surf_tool - > add_uv ( uvs [ uv ] ) ;
}
2017-03-05 16:44:50 +01:00
int vtx = face [ idx ] [ 0 ] . to_int ( ) - 1 ;
2017-04-27 10:24:09 +02:00
if ( vtx < 0 )
vtx + = vertices . size ( ) + 1 ;
2017-07-18 02:05:38 +02:00
ERR_FAIL_INDEX_V ( vtx , vertices . size ( ) , NULL ) ;
2017-02-03 04:08:50 +01:00
Vector3 vertex = vertices [ vtx ] ;
2017-07-18 02:05:38 +02:00
//if (weld_vertices)
// vertex.snap(Vector3(weld_tolerance, weld_tolerance, weld_tolerance));
2017-02-03 04:08:50 +01:00
surf_tool - > add_vertex ( vertex ) ;
}
2017-03-05 16:44:50 +01:00
face [ 1 ] = face [ 2 ] ;
2017-02-03 04:08:50 +01:00
}
2017-07-18 02:05:38 +02:00
} else if ( l . begins_with ( " s " ) ) { //smoothing
2017-03-05 16:44:50 +01:00
String what = l . substr ( 2 , l . length ( ) ) . strip_edges ( ) ;
if ( what = = " off " )
2017-02-03 04:08:50 +01:00
surf_tool - > add_smooth_group ( false ) ;
else
surf_tool - > add_smooth_group ( true ) ;
2017-07-18 02:05:38 +02:00
} else if ( l . begins_with ( " g " ) | | l . begins_with ( " usemtl " ) | | ( l . begins_with ( " o " ) | | f - > eof_reached ( ) ) ) { //commit group to mesh
2017-02-03 04:08:50 +01:00
2017-07-18 02:05:38 +02:00
if ( surf_tool - > get_vertex_array ( ) . size ( ) ) {
//another group going on, commit it
if ( normals . size ( ) = = 0 ) {
2017-02-03 04:08:50 +01:00
surf_tool - > generate_normals ( ) ;
2017-07-18 02:05:38 +02:00
}
if ( generate_tangents & & uvs . size ( ) ) {
2017-02-03 04:08:50 +01:00
surf_tool - > generate_tangents ( ) ;
2017-07-18 02:05:38 +02:00
}
2017-02-03 04:08:50 +01:00
surf_tool - > index ( ) ;
2017-07-18 02:05:38 +02:00
print_line ( " current material library " + current_material_library + " has " + itos ( material_map . has ( current_material_library ) ) ) ;
print_line ( " current material " + current_material + " has " + itos ( material_map . has ( current_material_library ) & & material_map [ current_material_library ] . has ( current_material ) ) ) ;
if ( material_map . has ( current_material_library ) & & material_map [ current_material_library ] . has ( current_material ) ) {
surf_tool - > set_material ( material_map [ current_material_library ] [ current_material ] ) ;
}
2017-02-03 04:08:50 +01:00
mesh = surf_tool - > commit ( mesh ) ;
2017-07-18 02:05:38 +02:00
if ( current_material ! = String ( ) ) {
mesh - > surface_set_name ( mesh - > get_surface_count ( ) - 1 , current_material . get_basename ( ) ) ;
} else if ( current_group ! = String ( ) ) {
mesh - > surface_set_name ( mesh - > get_surface_count ( ) - 1 , current_group ) ;
}
print_line ( " Added surface : " + mesh - > surface_get_name ( mesh - > get_surface_count ( ) - 1 ) ) ;
2017-02-03 04:08:50 +01:00
surf_tool - > clear ( ) ;
surf_tool - > begin ( Mesh : : PRIMITIVE_TRIANGLES ) ;
2017-07-18 02:05:38 +02:00
}
if ( l . begins_with ( " o " ) | | f - > eof_reached ( ) ) {
MeshInstance * mi = memnew ( MeshInstance ) ;
mi - > set_name ( name ) ;
mi - > set_mesh ( mesh ) ;
2017-02-03 04:08:50 +01:00
2017-07-18 02:05:38 +02:00
scene - > add_child ( mi ) ;
mi - > set_owner ( scene ) ;
2017-02-03 04:08:50 +01:00
2017-07-18 02:05:38 +02:00
mesh . instance ( ) ;
current_group = " " ;
current_material = " " ;
2017-02-03 04:08:50 +01:00
}
2017-07-18 02:05:38 +02:00
if ( f - > eof_reached ( ) ) {
break ;
}
if ( l . begins_with ( " o " ) ) {
2017-03-05 16:44:50 +01:00
name = l . substr ( 2 , l . length ( ) ) . strip_edges ( ) ;
2017-07-18 02:05:38 +02:00
}
if ( l . begins_with ( " usemtl " ) ) {
current_material = l . replace ( " usemtl " , " " ) . strip_edges ( ) ;
}
if ( l . begins_with ( " g " ) ) {
current_group = l . substr ( 2 , l . length ( ) ) . strip_edges ( ) ;
}
} else if ( l . begins_with ( " mtllib " ) ) { //parse material
current_material_library = l . replace ( " mtllib " , " " ) . strip_edges ( ) ;
if ( ! material_map . has ( current_material_library ) ) {
Map < String , Ref < SpatialMaterial > > lib ;
Error err = _parse_material_library ( current_material_library , lib , r_missing_deps ) ;
2017-07-18 23:21:51 +02:00
if ( err = = ERR_CANT_OPEN ) {
String dir = p_path . get_base_dir ( ) ;
err = _parse_material_library ( dir . plus_file ( current_material_library ) , lib , r_missing_deps ) ;
}
2017-07-18 02:05:38 +02:00
if ( err = = OK ) {
material_map [ current_material_library ] = lib ;
}
}
2017-02-03 04:08:50 +01:00
}
}
2017-03-05 16:44:50 +01:00
/*
2017-02-03 04:08:50 +01:00
TODO , check existing materials and merge ?
//re-apply materials if exist
for ( int i = 0 ; i < mesh - > get_surface_count ( ) ; i + + ) {
String n = mesh - > surface_get_name ( i ) ;
if ( name_map . has ( n ) )
mesh - > surface_set_material ( i , name_map [ n ] ) ;
}
*/
2017-07-18 02:05:38 +02:00
return scene ;
2017-02-03 04:08:50 +01:00
}
2017-07-18 02:05:38 +02:00
Ref < Animation > EditorOBJImporter : : import_animation ( const String & p_path , uint32_t p_flags ) {
2017-02-03 04:08:50 +01:00
2017-07-18 02:05:38 +02:00
return Ref < Animation > ( ) ;
}
EditorOBJImporter : : EditorOBJImporter ( ) {
2017-02-03 04:08:50 +01:00
}