/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2013 Tuomas Vaherkoski * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ /** * @file x3dmodelparser.cpp */ #include #include #include #include #include #include #include #include #include #include <3d_struct.h> #include #include /** * Trace mask used to enable or disable the trace output of the X3D parser code. * The debug output can be turned on by setting the WXTRACE environment variable to * "KI_TRACE_X3D_PARSER". See the wxWidgets documentation on wxLogTrace for * more information. */ static const wxChar* traceX3DParser = wxT( "KI_TRACE_X3D_PARSER" ); X3D_MODEL_PARSER::X3D_MODEL_PARSER( S3D_MASTER* aMaster ) : S3D_MODEL_PARSER( aMaster ) { m_model = NULL; } X3D_MODEL_PARSER::~X3D_MODEL_PARSER() { for( unsigned int idx = 0; idx < childs.size(); idx++ ) { if( childs[idx] ) { delete childs[idx]; childs[idx] = 0; } } } bool X3D_MODEL_PARSER::Load( const wxString& aFilename ) { wxLogTrace( traceX3DParser, wxT( "Loading: %s" ), GetChars( aFilename ) ); wxXmlDocument doc; if( !doc.Load( aFilename ) ) { wxLogTrace( traceX3DParser, wxT( "Error while parsing file: %s" ), GetChars( aFilename ) ); return false; } if( doc.GetRoot()->GetName() != wxT( "X3D" ) ) { wxLogTrace( traceX3DParser, wxT( "Filetype is not X3D: %s" ), GetChars( aFilename ) ); return false; } // Switch the locale to standard C (needed to print floating point numbers) LOCALE_IO toggle; childs.clear(); // Shapes are inside of Transform nodes // Transform node contains information about // transition, scale and rotation of the shape NODE_LIST transforms; GetChildsByName( doc.GetRoot(), wxT( "Transform" ), transforms ); for( NODE_LIST::iterator node_it = transforms.begin(); node_it != transforms.end(); node_it++ ) { m_model = new S3D_MESH(); childs.push_back( m_model ); readTransform( *node_it ); } return true; } wxString X3D_MODEL_PARSER::VRML2_representation() { wxString output; for( unsigned i = 0; i < vrml_points.size(); i++ ) { output += wxT( "Shape {\n" " appearance Appearance {\n" " material Material {\n" ) + vrml_materials[i] + wxT( " }\n" " }\n" " geometry IndexedFaceSet {\n" " solid TRUE\n" " coord Coordinate {\n" " point [\n" ) + vrml_points[i] + wxT( " ]\n" " }\n" " coordIndex [\n" ) + vrml_coord_indexes[i] + wxT( " ]\n" " }\n" "},\n" ); } return output; } void X3D_MODEL_PARSER::GetChildsByName( wxXmlNode* aParent, const wxString aName, std::vector& aResult ) { // Breadth-first search (BFS) std::queue found; found.push( aParent ); while( !found.empty() ) { wxXmlNode* elem = found.front(); for( wxXmlNode* child = elem->GetChildren(); child != NULL; child = child->GetNext() ) { if( child->GetName() == aName ) { aResult.push_back( child ); } found.push( child ); } found.pop(); } } void X3D_MODEL_PARSER::GetNodeProperties( wxXmlNode* aNode, PROPERTY_MAP& aProps ) { wxXmlAttribute* prop; for( prop = aNode->GetAttributes(); prop != NULL; prop = prop->GetNext() ) { aProps[ prop->GetName() ] = prop->GetValue(); } } /* Private ----- */ void X3D_MODEL_PARSER::readTransform( wxXmlNode* aTransformNode ) { NODE_LIST childnodes; GetChildsByName( aTransformNode, wxT( "Material" ), childnodes ); for( NODE_LIST::iterator node = childnodes.begin(); node != childnodes.end(); node++ ) { readMaterial( *node ); } childnodes.clear(); PROPERTY_MAP properties; GetNodeProperties( aTransformNode, properties ); GetChildsByName( aTransformNode, wxT( "IndexedFaceSet" ), childnodes ); for( NODE_LIST::iterator node = childnodes.begin(); node != childnodes.end(); node++ ) { readIndexedFaceSet( *node, properties ); } childnodes.clear(); } void X3D_MODEL_PARSER::readMaterial( wxXmlNode* aMatNode ) { glm::vec3 color; PROPERTY_MAP properties; GetNodeProperties( aMatNode, properties ); // DEFine new Material named as value of DEF if( properties.find( wxT( "DEF" ) ) != properties.end() ) { double amb, shine, transp; S3D_MATERIAL* material = new S3D_MATERIAL( GetMaster(), properties[ wxT( "DEF" ) ] ); GetMaster()->Insert( material ); m_model->m_Materials = material; if( !parseDoubleTriplet( properties[ wxT( "diffuseColor" ) ], color ) ) { // DBG( printf( "diffuseColor parsing error" ) ); } else { m_model->m_Materials->m_DiffuseColor.push_back( color ); } if( !parseDoubleTriplet( properties[ wxT( "specularColor" ) ], color ) ) { // DBG( printf( "specularColor parsing error" ) ); } else { m_model->m_Materials->m_SpecularColor.push_back( color ); } if( !parseDoubleTriplet( properties[ wxT( "emissiveColor" ) ], color ) ) { // DBG( printf( "emissiveColor parsing error" ) ); } else { m_model->m_Materials->m_EmissiveColor.push_back( color ); } wxStringTokenizer values; values.SetString( properties[ wxT( "ambientIntensity" ) ] ); if( values.GetNextToken().ToDouble( &amb ) ) { m_model->m_Materials->m_AmbientColor.push_back( glm::vec3( amb, amb, amb ) ); } else { // DBG( printf( "ambienterror" ) ); } values.SetString( properties[ wxT( "shininess" ) ] ); if( values.GetNextToken().ToDouble( &shine ) ) { // VRML value is normalized and openGL expects a value 0 - 128 if( shine > 1.0 ) { shine = 1.0; } else if( shine < 0.0 ) { shine = 0.0; } shine = shine * 128.0f; m_model->m_Materials->m_Shininess.push_back( shine ); } else { // DBG( printf( "shininess error" ) ); } values.SetString( properties[ wxT( "transparency" ) ] ); if( values.GetNextToken().ToDouble( &transp ) ) { m_model->m_Materials->m_Transparency.push_back( transp ); } else { // DBG( printf( "trans error" ) ); } // VRML wxString vrml_material; PROPERTY_MAP::const_iterator p = ++properties.begin(); // skip DEF for( ; p != properties.end(); p++ ) { vrml_material.Append( p->first + wxT( " " ) + p->second + wxT( "\n" ) ); } vrml_materials.push_back( vrml_material ); } // USE existing material named by value of USE else if( properties.find( wxT( "USE" ) ) != properties.end() ) { S3D_MATERIAL* material = NULL; wxString mat_name = properties[ wxT( "USE" ) ]; for( material = GetMaster()->m_Materials; material; material = material->Next() ) { if( material->m_Name == mat_name ) { wxString vrml_material; vrml_material.Append( wxString::Format( wxT( "specularColor %f %f %f\n" ), material->m_SpecularColor[0].x, material->m_SpecularColor[0].y, material->m_SpecularColor[0].z ) ); vrml_material.Append( wxString::Format( wxT( "diffuseColor %f %f %f\n" ), material->m_DiffuseColor[0].x, material->m_DiffuseColor[0].y, material->m_DiffuseColor[0].z ) ); vrml_material.Append( wxString::Format( wxT( "emissiveColor %f %f %f\n" ), material->m_EmissiveColor[0].x, material->m_EmissiveColor[0].y, material->m_EmissiveColor[0].z ) ); vrml_material.Append( wxString::Format( wxT( "ambientIntensity %f\n" ), material->m_AmbientColor[0].x ) ); vrml_material.Append( wxString::Format( wxT( "shininess %f\n" ), material->m_Shininess[0] ) ); vrml_material.Append( wxString::Format( wxT( "transparency %f\n" ), material->m_Transparency[0] ) ); vrml_materials.push_back( vrml_material ); m_model->m_Materials = material; return; } } // DBG( printf( "ReadMaterial error: material not found\n" ) ); } } bool X3D_MODEL_PARSER::parseDoubleTriplet( const wxString& aData, S3D_VERTEX& aResult ) { wxStringTokenizer tokens( aData ); double x = 0; double y = 0; double z = 0; bool ret = tokens.GetNextToken().ToDouble( &x ) && tokens.GetNextToken().ToDouble( &y ) && tokens.GetNextToken().ToDouble( &z ); aResult.x = x; aResult.y = y; aResult.z = z; return ret; } void X3D_MODEL_PARSER::rotate( S3D_VERTEX& aV, S3D_VERTEX& aU, double angle ) { S3D_VERTEX rotated; double C = cos( angle ); double S = sin( angle ); double t = 1.0 - C; rotated.x = ( t * aU.x * aU.x + C ) * aV.x + ( t * aU.x * aU.y - S * aU.z ) * aV.y + ( t * aU.x * aU.z + S * aU.y ) * aV.z; rotated.y = ( t * aU.x * aU.y + S * aU.z ) * aV.x + ( t * aU.y * aU.y + C ) * aV.y + ( t * aU.y * aU.z - S * aU.x ) * aV.z; rotated.z = ( t * aU.x * aU.z - S * aU.y ) * aV.x + ( t * aU.y * aU.z + S * aU.x ) * aV.y + ( t * aU.z * aU.z + C) * aV.z; aV.x = rotated.x; aV.y = rotated.y; aV.z = rotated.z; } /* Steps: * 1. Read transform data * 2. Read vertex triplets * 3. Read coordinate indexes * 4. Apply geometry to Master object */ void X3D_MODEL_PARSER::readIndexedFaceSet( wxXmlNode* aFaceNode, PROPERTY_MAP& aTransformProps ) { /* Step 1: Read transform data * --------------------------- */ S3D_VERTEX translation; parseDoubleTriplet( aTransformProps[ wxT( "translation" ) ], translation ); S3D_VERTEX scale; parseDoubleTriplet( aTransformProps[ wxT( "scale" ) ], scale ); S3D_VERTEX rotation; double angle = 0.0; wxStringTokenizer tokens( aTransformProps[ wxT( "rotation" ) ] ); double x, y, z; if( !( tokens.GetNextToken().ToDouble( &x ) && tokens.GetNextToken().ToDouble( &y ) && tokens.GetNextToken().ToDouble( &z ) && tokens.GetNextToken().ToDouble( &angle ) ) ) { // DBG( printf( "rotation read error" ) ); } else { rotation.x = x; rotation.y = y; rotation.z = z; } /* Step 2: Read all coordinate points * ---------------------------- */ std::vector points; NODE_LIST coordinates; GetChildsByName( aFaceNode, wxT( "Coordinate" ), coordinates ); PROPERTY_MAP coordinate_properties; // IndexedFaceSet has one Coordinate child node GetNodeProperties( coordinates[0], coordinate_properties ); // Save points to vector as doubles wxStringTokenizer point_tokens( coordinate_properties[ wxT( "point" ) ] ); double point = 0.0; while( point_tokens.HasMoreTokens() ) { if( point_tokens.GetNextToken().ToDouble( &point ) ) { points.push_back( point ); } else { wxLogTrace( traceX3DParser, wxT( "Error converting to double" ) ); } } if( points.size() % 3 != 0 ) { // DBG( printf( "Number of points is incorrect" ) ); return; } /* Create 3D vertex from 3 points and * apply transforms in order of SCALE, ROTATION, TRANSLATION */ wxString vrml_pointlist; std::vector triplets; for( unsigned id = 0; id < points.size() / 3; id++ ) { int triplet_indx = id * 3; S3D_VERTEX point( points[ triplet_indx ], points[ triplet_indx + 1 ], points[ triplet_indx + 2 ] ); point.x *= scale.x; point.y *= scale.y; point.z *= scale.z; rotate( point, rotation, angle ); point.x += translation.x; point.y += translation.y; point.z += translation.z; m_model->m_Point.push_back( point ); // VRML vrml_pointlist.Append( wxString::Format( wxT( "%f %f %f\n" ), point.x, point.y, point.z ) ); } vrml_points.push_back( vrml_pointlist ); /* Step 3: Read all color points * ---------------------------- */ std::vector color_points; NODE_LIST color; GetChildsByName( aFaceNode, wxT( "Color" ), color ); // Some models lack color information, need to handle this safely if( !color.empty() ) { PROPERTY_MAP color_properties; // IndexedFaceSet has one Coordinate child node GetNodeProperties( color[0], color_properties ); // Save points to vector as doubles wxStringTokenizer colorpoint_tokens( color_properties[ wxT( "color" ) ] ); double color_point = 0.0; while( colorpoint_tokens.HasMoreTokens() ) { if( colorpoint_tokens.GetNextToken().ToDouble( &color_point ) ) { color_points.push_back( color_point ); } else { wxLogTrace( traceX3DParser, wxT( "Error converting to double" ) ); } } if( color_points.size() % 3 != 0 ) { // DBG( printf( "Number of points is incorrect" ) ); return; } /* Create 3D face color from 3 color points */ m_model->m_Materials->m_DiffuseColor.clear(); for( unsigned id = 0; id < color_points.size() / 3; id++ ) { m_model->m_MaterialIndex.push_back( id ); int color_triplet_indx = id * 3; glm::vec3 colorface( color_points[ color_triplet_indx + 0 ], color_points[ color_triplet_indx + 1 ], color_points[ color_triplet_indx + 2 ] ); m_model->m_Materials->m_DiffuseColor.push_back( colorface ); } } /* -- Read coordinate indexes -- */ PROPERTY_MAP faceset_properties; GetNodeProperties( aFaceNode, faceset_properties ); wxString coordIndex_str = faceset_properties[ wxT( "coordIndex" ) ]; wxStringTokenizer index_tokens( coordIndex_str ); wxString vrml_coord_indx_list; std::vector coord_list; coord_list.clear(); while( index_tokens.HasMoreTokens() ) { long index = 0; index_tokens.GetNextToken().ToLong( &index ); // -1 marks the end of polygon if( index < 0 ) { /* Step 4: Apply geometry to Master object * --------------------------------------- */ m_model->m_CoordIndex.push_back( coord_list ); coord_list.clear(); vrml_coord_indx_list.Append( wxT( "-1\n" ) ); } else { coord_list.push_back( index ); vrml_coord_indx_list.Append( wxString::Format( wxT( "%ld " ), index ) ); } } vrml_coord_indexes.push_back( vrml_coord_indx_list ); }