/* =========================================================================== maya2q3 - export .md3 files from maya Copyright (C) 2005 HermitWorks Entertainment Corporation 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. =========================================================================== */ #include "common.h" #include "bsp-local.h" #include "shader_parser.h" namespace bsp { class BspImporter { public: BspImporter( void ); void Import( std::istream &stm, const MObject &parentTransform, const char *basePath ); private: bool m_combineMeshes; MPointArray m_verts; MIntArray m_faceCounts; MIntArray m_faceVertIndices; MIntArray m_textureUVIDs; MFloatArray m_textureU, m_textureV; MIntArray m_lightmapUVIDs; MFloatArray m_lightmapU, m_lightmapV; MColorArray m_vertexLight; MIntArray m_vertexLightIDs; MIntArray m_sourceVertices; std::vector< q3::Patch > m_patchStaging; MObjectArray m_meshes; MDagPathArray m_meshPaths; bool m_hasDeluxeMaps; std::vector< q3::Entity > m_entities; MObjectArray m_lights; MDagPathArray m_lightPaths; MDagPath m_lightGridPoints; std::vector< std::pair< int, int > > m_shaderMap; std::vector< std::vector< int > > m_shaderFaceNums; MObjectArray m_shaders; MSelectionList m_miscLmapSels; std::vector< MSelectionList > m_lmapSels; std::vector< MSelectionList > m_shaderSels; std::vector< char > m_shaderText; const char *m_basePath; q3::dheader_t m_fileHeader; BinaryReader *m_fileData; template< typename T > std::vector< T > ReadLump( int lumpIndex ) { const q3::lump_t &lump = m_fileHeader.lumps[lumpIndex]; if( lump.filelen % sizeof( T ) != 0 ) throw std::exception( "Funny lump size." ); std::vector< T > ret( lump.filelen / sizeof( T ) ); m_fileData->seek( lump.fileofs ); m_fileData->read( &ret[0], lump.filelen ); return ret; } void InitImporterObjects(); void ParseTriangles( bool generateSourceIDs, const std::vector< q3::drawVert_t > &verts, const std::vector< int > &indices, int shaderBin, int firstVert = 0, int vertexCount = -1, int firstIndex = 0, int indexCount = -1 ); void CreateMesh(); void ClearStagingArea(); void ImportShaderText(); MObject ImportTexture( const char *shaderName ); int GetShaderIndex( const std::vector< q3::dshader_t > &shaders, int shaderNum, int lightmapNum ); void ImportSurfaces(); void CreateShaderQuickSelects(); void CreateQuickSelect( const MSelectionList &sel, const MString &name ); void ImportLightGridPoints(); void ParseEntities(); void ImportEntityLights(); void CreatePointLight( const vec3 &origin, const vec3 &color, float intensity ); void GroupNodes( const MObject &parentXform ); }; #pragma warning( disable : 4100 ) //shut up unreferenced parameter warnings until dev of this file is done template< size_t n > static void MangleShaderName( sstr< n > &ret, const char *src ) { for( char c = *src++; c; c = *src++ ) { switch( c ) { case '_': ret.Append( "__" ); break; case '.': case '/': case '\\': ret.Append( "_" ); break; default: ret.Append( "%c", c ); break; } } } BspImporter::BspImporter( void ) { m_combineMeshes = false; InitImporterObjects(); } void BspImporter::InitImporterObjects() { MStatus stat; MFnMesh mesh; bool hasBlindType = mesh.isBlindDataTypeUsed( BdSourceVertexId, &stat ); check_status( stat ); if( !hasBlindType ) { MStringArray longNames, shortNames, formatNames; longNames.append( BdSourceVertexAttr ); shortNames.append( "svi" ); formatNames.append( "int" ); stat = mesh.createBlindDataType( BdSourceVertexId, longNames, shortNames, formatNames ); check_status( stat ); } } void BspImporter::ImportShaderText() { } MObject BspImporter::ImportTexture( const char *shaderName ) { ospath basePath( m_basePath ); basePath.Append( shaderName ); const char *path = 0; static const char *const exts[] = { ".tga", ".jpg" , ".dds"}; for( size_t i = 0; i < lengthof( exts ); i++ ) { ospath p2( basePath ); p2.Append( exts[i] ); struct _stat ss; if( _stat( p2, &ss ) == 0 ) { basePath = p2; path = basePath; break; } } if( !path ) return MObject::kNullObj; MStatus stat; MFnDependencyNode fnTex; fnTex.create( MString( "file" ), &stat ); check_status( stat ); sstr< 1024 > nameBuf; MangleShaderName( nameBuf, shaderName ); nameBuf.Append( "_tex" ); fnTex.setName( MString( nameBuf ), &stat ); check_status( stat ); MPlug filePlug = fnTex.findPlug( MString( "fileTextureName" ), &stat ); check_status( stat ); stat = filePlug.setString( MString( path ) ); check_status( stat ); MObject ret = fnTex.object( &stat ); check_status( stat ); return ret; } int BspImporter::GetShaderIndex( const std::vector< q3::dshader_t > &shaders, int shaderNum, int lightmapNum ) { shaderNum = clamp( shaderNum, 0, (int)shaders.size() - 1 ); if( m_hasDeluxeMaps && lightmapNum >= 0 ) lightmapNum /= 2; for( uint i = 0; i < m_shaderMap.size(); i++ ) { if( m_shaderMap[i].first == shaderNum && m_shaderMap[i].second == lightmapNum ) return (int)i; } if( shaderNum >= (int)m_shaderSels.size() ) m_shaderSels.resize( shaderNum + 1 ); if( lightmapNum >= 0 && lightmapNum >= (int)m_lmapSels.size() ) m_lmapSels.resize( lightmapNum + 1 ); const q3::dshader_t &shader = shaders[shaderNum]; sstr< 1024 > buf; MangleShaderName( buf, shader.shader ); if( lightmapNum >= 0 ) buf.Append( "_lmap_%i", lightmapNum ); MString shaderName( buf ); MString sgName = shaderName + "SG"; MStatus stat; MObject sgObj; MItDependencyNodes nodeIter( MFn::kShadingEngine, &stat ); check_status( stat ); for( ; ; ) { bool isDone = nodeIter.isDone( &stat ); check_status( stat ); if( isDone ) break; MObject obj = nodeIter.thisNode( &stat ); check_status( stat ); stat = nodeIter.next(); check_status( stat ); MFnDependencyNode depNode( obj, &stat ); check_status( stat ); MString depName = depNode.name( &stat ); check_status( stat ); if( depName != sgName ) continue; sgObj = obj; break; } if( sgObj.isNull() ) { MDGModifier dgMod; MFnLambertShader fnShader; fnShader.create( true, &stat ); check_status( stat ); fnShader.setName( shaderName, &stat ); check_status( stat ); { MFnTypedAttribute attr; MObject attrObj = attr.create( "originalShaderName", "osn", MFnData::kString, MObject::kNullObj, &stat ); check_status( stat ); stat = fnShader.addAttribute( attrObj ); check_status( stat ); MPlug attrPlug = fnShader.findPlug( attrObj, &stat ); check_status( stat ); stat = attrPlug.setString( MString( shader.shader ) ); check_status( stat ); } MPlug outColorPlug = fnShader.findPlug( MString( "outColor" ), &stat ); check_status( stat ); MObject shaderObj = fnShader.object( &stat ); check_status( stat ); MObject texObj = ImportTexture( shader.shader ); if( texObj != MObject::kNullObj ) { MFnDependencyNode fnTex( texObj, &stat ); check_status( stat ); MPlug texColorPlug = fnTex.findPlug( MString( "outColor" ), &stat ); check_status( stat ); //MPlug texTransPlug = fnTex.findPlug( MString( "outTransparency" ), &stat ); //check_status( stat ); MPlug shaderColorPlug = fnShader.findPlug( MString( "color" ), &stat ); check_status( stat ); //MPlug shaderTransPlug = fnShader.findPlug( MString( "transparency" ), &stat ); //check_status( stat ); stat = dgMod.connect( texColorPlug, shaderColorPlug ); check_status( stat ); //stat = dgMod.connect( texTransPlug, shaderTransPlug ); //check_status( stat ); } MFnSet fnGroup; MSelectionList empty; fnGroup.create( empty, MFnSet::kRenderableOnly, false, &stat ); check_status( stat ); fnGroup.setName( sgName, &stat ); check_status( stat ); MPlug surfaceShaderPlug = fnGroup.findPlug( MString( "surfaceShader" ), &stat ); check_status( stat ); sgObj = fnGroup.object( &stat ); check_status( stat ); MPlugArray connections; surfaceShaderPlug.connectedTo( connections, true, false, &stat ); check_status( stat ); for( uint i = 0; i < connections.length(); i++ ) { stat = dgMod.disconnect( connections[i], surfaceShaderPlug ); check_status( stat ); } stat = dgMod.connect( outColorPlug, surfaceShaderPlug ); check_status( stat ); stat = dgMod.doIt(); check_status( stat ); } m_shaders.append( sgObj ); m_shaderMap.push_back( std::pair< int, int >( shaderNum, lightmapNum ) ); m_shaderFaceNums.push_back( std::vector< int >() ); return (int)(m_shaderFaceNums.size() - 1); } void BspImporter::ParseTriangles( bool generateSourceIDs, const std::vector< q3::drawVert_t > &verts, const std::vector< int > &indices, int shaderBin, int firstVert, int vertexCount, int firstIndex, int indexCount ) { if( vertexCount == -1 ) vertexCount = (int)verts.size(); if( indexCount == -1 ) indexCount = (int)indices.size(); int baseVert = (int)m_verts.length(); for( int i = 0; i < vertexCount; i++ ) { const q3::drawVert_t &v = verts[firstVert + i]; m_verts.append( MPoint( q3::Q3VecToMaya( v.xyz ) ) ); m_textureU.append( v.st.x ); m_textureV.append( 1 - v.st.y ); m_lightmapU.append( v.lightmap.x ); m_lightmapV.append( 1 - v.lightmap.y ); m_vertexLight.append( MColor( MColor::kRGB, v.color[0], v.color[1], v.color[2] ) ); } //ToDo: collapse out coplanar edges (?) int numTris = indexCount / 3; for( int i = 0; i < numTris; i++ ) { int rawIdx, index; rawIdx = indices[firstIndex + i * 3 + 0]; index = baseVert + rawIdx; m_faceVertIndices.append( index ); m_textureUVIDs.append( index ); m_lightmapUVIDs.append( index ); m_vertexLightIDs.append( index ); m_sourceVertices.append( generateSourceIDs ? firstVert + rawIdx : -1 ); rawIdx = indices[firstIndex + i * 3 + 2]; index = baseVert + rawIdx; m_faceVertIndices.append( index ); m_textureUVIDs.append( index ); m_lightmapUVIDs.append( index ); m_vertexLightIDs.append( index ); m_sourceVertices.append( generateSourceIDs ? firstVert + rawIdx : -1 ); rawIdx = indices[firstIndex + i * 3 + 1]; index = baseVert + rawIdx; m_faceVertIndices.append( index ); m_textureUVIDs.append( index ); m_lightmapUVIDs.append( index ); m_vertexLightIDs.append( index ); m_sourceVertices.append( generateSourceIDs ? firstVert + rawIdx : -1 ); if( shaderBin != -1 ) m_shaderFaceNums[shaderBin].push_back( m_faceCounts.length() ); m_faceCounts.append( 3 ); } } void BspImporter::ImportSurfaces() { std::vector< q3::dsurface_t > surfs = ReadLump< q3::dsurface_t >( q3::LUMP_SURFACES ); std::vector< q3::drawVert_t > verts = ReadLump< q3::drawVert_t >( q3::LUMP_DRAWVERTS ); std::vector< int > indices = ReadLump< int >( q3::LUMP_DRAWINDEXES ); std::vector< q3::dshader_t > shaders = ReadLump< q3::dshader_t >( q3::LUMP_SHADERS ); typedef std::map< int, std::vector< int > > MapSetMap; typedef MapSetMap::iterator MapSetIter; MapSetMap materialSets; for( uint i = 0; i < surfs.size(); i++ ) { const q3::dsurface_t &surf = surfs[i]; int surfIndex; switch( surf.surfaceType ) { case q3::MST_PLANAR: case q3::MST_TRIANGLE_SOUP: surfIndex = (int)i; break; case q3::MST_PATCH: { q3::Patch p = q3::SubdividePatchToGrid( surf.patchWidth, surf.patchHeight, &verts[surf.firstVert] ); p.meshIdx = i; m_patchStaging.push_back( p ); surfIndex = -(int)m_patchStaging.size(); } break; default: continue; } int shader = GetShaderIndex( shaders, surf.shaderNum, surf.lightmapNum ); if( shader == -1 ) continue; MapSetIter matIter = materialSets.find( shader ); if( matIter == materialSets.end() ) { std::pair< MapSetIter, bool > insPos = materialSets.insert( MapSetIter::value_type( shader, std::vector< int >() ) ); if( !insPos.second ) continue; matIter = insPos.first; } matIter->second.push_back( surfIndex ); } q3::StitchAllPatches( m_patchStaging ); q3::FixSharedVertexLodError( m_patchStaging ); std::vector< int > tmpIndices; std::vector< q3::drawVert_t > tmpVerts; for( MapSetIter iter = materialSets.begin(); iter != materialSets.end(); ++iter ) { std::vector< int > &surfIdxs = iter->second; for( uint i = 0; i < surfIdxs.size(); i++ ) { if( surfIdxs[i] < 0 ) { const q3::Patch &p = m_patchStaging[-surfIdxs[i] - 1]; q3::PatchToMesh( p, tmpVerts, tmpIndices ); ParseTriangles( false, tmpVerts, tmpIndices, iter->first ); } else { const q3::dsurface_t &surf = surfs[surfIdxs[i]]; ParseTriangles( true, verts, indices, iter->first, surf.firstVert, surf.numVerts, surf.firstIndex, surf.numIndexes ); } } if( !m_combineMeshes && m_verts.length() ) { CreateMesh(); ClearStagingArea(); } } if( m_verts.length() ) CreateMesh(); ClearStagingArea(); m_patchStaging.clear(); } void BspImporter::CreateMesh( void ) { MStatus stat; MFnMesh mesh; MObject ret = mesh.create( m_verts.length(), m_faceCounts.length(), m_verts, m_faceCounts, m_faceVertIndices, MObject::kNullObj, &stat ); check_status( stat ); MString texCoordSetName( McTexCoordSet ); stat = mesh.setUVs( m_textureU, m_textureV, &texCoordSetName ); check_status( stat ); stat = mesh.assignUVs( m_faceCounts, m_textureUVIDs, &texCoordSetName ); check_status( stat ); MString lightmapSetName( McLmapCoordSet ); stat = mesh.createUVSet( lightmapSetName ); check_status( stat ); stat = mesh.setUVs( m_lightmapU, m_lightmapV, &lightmapSetName ); check_status( stat ); stat = mesh.assignUVs( m_faceCounts, m_lightmapUVIDs, &lightmapSetName ); check_status( stat ); stat = mesh.setCurrentUVSetName( texCoordSetName ); check_status( stat ); MString vertexLightName( McVertexLightSet ); stat = mesh.createColorSet( vertexLightName ); check_status( stat ); stat = mesh.setColors( m_vertexLight, &vertexLightName ); check_status( stat ); stat = mesh.assignColors( m_vertexLightIDs, &vertexLightName ); check_status( stat ); stat = mesh.updateSurface(); check_status( stat ); MString sourceVertDataName( BdSourceVertexAttr ); if( m_sourceVertices.length() ) { int iVert = 0; for( int iFace = 0; iFace < (int)m_faceCounts.length(); iFace++ ) { for( int iFaceVert = 0; iFaceVert < m_faceCounts[iFace]; iFaceVert++ ) { int blindIndex; stat = mesh.getFaceVertexBlindDataIndex( iFace, m_faceVertIndices[iVert], blindIndex ); check_status( stat ); stat = mesh.setIntBlindData( blindIndex, MFn::kMeshFaceVertComponent, BdSourceVertexId, sourceVertDataName, m_sourceVertices[iVert] ); check_status( stat ); iVert++; } } } stat = mesh.updateSurface(); check_status( stat ); MDagPath objPath; stat = mesh.getPath( objPath ); check_status( stat ); MDagPath parentPath( objPath ); stat = parentPath.pop(); check_status( stat ); MFnDagNode fnParent( parentPath, &stat ); check_status( stat ); fnParent.setName( MString( "bspSurface" ), &stat ); check_status( stat ); for( uint i = 0; i < m_shaderFaceNums.size(); i++ ) { const std::vector< int > &map = m_shaderFaceNums[i]; if( !map.size() ) continue; MSelectionList *pLmapSel = &m_miscLmapSels; MSelectionList *pShaderSel = &m_shaderSels[m_shaderMap[i].first]; if( m_shaderMap[i].second >= 0 ) pLmapSel = &m_lmapSels[m_shaderMap[i].second]; MFnSet fnSg( m_shaders[i], &stat ); check_status( stat ); if( m_combineMeshes ) { MItMeshPolygon polyIt( objPath, MObject::kNullObj, &stat ); check_status( stat ); MSelectionList sel; for( uint j = 0; j < map.size(); j++ ) { int prevIndex; stat = polyIt.setIndex( map[j], prevIndex ); check_status( stat ); MObject comp = polyIt.polygon( &stat ); check_status( stat ); stat = sel.add( objPath, comp ); check_status( stat ); stat = pLmapSel->add( objPath, comp ); check_status( stat ); stat = pShaderSel->add( objPath, comp ); check_status( stat ); } stat = fnSg.addMembers( sel ); check_status( stat ); } else { //mesh will be one shader only - this one stat = fnSg.addMember( objPath ); check_status( stat ); stat = pLmapSel->add( objPath ); check_status( stat ); stat = pShaderSel->add( objPath ); check_status( stat ); } } m_meshPaths.append( objPath ); m_meshes.append( ret ); } void BspImporter::ClearStagingArea() { m_verts.clear(); m_faceCounts.clear(); m_faceVertIndices.clear(); m_textureUVIDs.clear(); m_textureU.clear(); m_textureV.clear(); m_lightmapUVIDs.clear(); m_lightmapU.clear(); m_lightmapV.clear(); m_vertexLight.clear(); m_vertexLightIDs.clear(); m_sourceVertices.clear(); for( uint i = 0; i < m_shaderFaceNums.size(); i++ ) m_shaderFaceNums[i].clear(); } void BspImporter::CreateShaderQuickSelects() { std::vector< q3::dshader_t > shaders = ReadLump< q3::dshader_t >( q3::LUMP_SHADERS ); CreateQuickSelect( m_miscLmapSels, "lmap_none" ); for( uint i = 0; i < m_lmapSels.size(); i++ ) { if( !m_lmapSels[i].length() ) continue; sstr< 64 > buf; buf.Append( "lmap_%i", i ); CreateQuickSelect( m_lmapSels[i], MString( buf ) ); } for( uint i = 0; i < m_shaderSels.size(); i++ ) { if( !m_shaderSels[i].length() ) continue; sstr< 1024 > buf; buf.Append( "shader_" ); MangleShaderName( buf, shaders[i].shader ); CreateQuickSelect( m_shaderSels[i], MString( buf ) ); } } void BspImporter::CreateQuickSelect( const MSelectionList &sel, const MString &name ) { MStatus stat; MFnSet fnSet; fnSet.create( sel, MFnSet::kNone, false, &stat ); check_status( stat ); fnSet.setName( name, &stat ); check_status( stat ); } void BspImporter::GroupNodes( const MObject &parentXform ) { MStatus stat; MFnDagNode fnParent( parentXform, &stat ); check_status( stat ); for( uint i = 0; i < m_meshPaths.length(); i++ ) { MDagPath path = m_meshPaths[i]; stat = path.pop(); check_status( stat ); MObject obj = path.node( &stat ); check_status( stat ); stat = fnParent.addChild( obj ); check_status( stat ); } for( uint i = 0; i < m_lightPaths.length(); i++ ) { MDagPath path = m_lightPaths[i]; stat = path.pop(); check_status( stat ); MObject obj = path.node( &stat ); check_status( stat ); stat = fnParent.addChild( obj ); check_status( stat ); } { MDagPath path = m_lightGridPoints; stat = path.pop(); check_status( stat ); MObject obj = path.node( &stat ); check_status( stat ); stat = fnParent.addChild( obj ); check_status( stat ); } } void BspImporter::ParseEntities() { const q3::lump_t &lump = m_fileHeader.lumps[q3::LUMP_ENTITIES]; boost::scoped_array< char > lump_data( new char[lump.filelen + 1] ); m_fileData->seek( lump.fileofs ); m_fileData->read( lump_data.get(), lump.filelen ); lump_data[lump.filelen] = 0; const char *entStr = lump_data.get(); m_entities = q3::Entity::ParseEntities( entStr ); if( m_entities.size() == 0 || m_entities[0].AsString( EkClassName ) != EcWorldSpawn ) throw std::exception( "Missing world spawn." ); m_hasDeluxeMaps = m_entities[0].AsBool( EkDeluxeMapping, false ); } void BspImporter::CreatePointLight( const vec3 &origin, const vec3 &color, float intensity ) { MStatus stat; MFnPointLight light; light.create( true, &stat ); check_status( stat ); stat = light.setColor( MColor( color.x, color.y, color.z ) ); check_status( stat ); stat = light.setIntensity( intensity / 128 ); check_status( stat ); stat = light.setUseRayTraceShadows( true ); check_status( stat ); stat = light.setDecayRate( 2 /* quadratic */ ); check_status( stat ); MObject lightObj = light.object( &stat ); check_status( stat ); stat = m_lights.append( lightObj ); check_status( stat ); MDagPath lightPath; stat = light.getPath( lightPath ); check_status( stat ); stat = m_lightPaths.append( lightPath ); check_status( stat ); stat = lightPath.pop(); check_status( stat ); MFnTransform lightPos( lightPath, &stat ); check_status( stat ); stat = lightPos.setTranslation( MVector( origin.x, origin.y, origin.z ), MSpace::kWorld ); check_status( stat ); } void BspImporter::ImportEntityLights() { for( size_t i = 0; i < m_entities.size(); i++ ) { const q3::Entity &ent = m_entities[i]; if( ent.AsString( EkClassName ) != EcLight ) continue; vec3 origin = ent.AsVec3( EkOrigin ); vec3 color = ent.AsVec3( EkColor, vec3c( 1 ) ); float intensity = ent.AsFloat( EkLight, 1 ); CreatePointLight( q3::Q3VecToMaya( origin ), color, intensity ); } } void BspImporter::ImportLightGridPoints() { MStatus stat; std::vector< q3::dmodel_t > models = ReadLump< q3::dmodel_t >( q3::LUMP_MODELS ); if( models.size() < 1 ) throw std::exception( "Missing world model record." ); const q3::dmodel_t &wm = models[0]; vec3 size = m_entities[0].AsVec3( EkLightGridSize, vec3c( 64, 64, 128 ) ); vec3 mins = size * ceil( wm.mins / size ); vec3 maxs = size * floor( wm.maxs / size ); vec3 bounds = floor( (maxs - mins) / size + vec3c( 1 ) ); MFloatPointArray points; MIntArray counts, indices; int bx = (int)bounds.x; int by = (int)bounds.y; int bz = (int)bounds.z; for( int z = 0; z < bz; z++ ) { uint sliceBase = points.length(); for( int y = 0; y < by; y++ ) { for( int x = 0; x < bx; x++ ) { points.append( MFloatPoint( q3::Q3VecToMaya( mins + size * vec3c( (float)x, (float)y, (float)z ) ) ) ); } } for( int y = 0; y < by - 1; y++ ) { uint rowBase = sliceBase + bx * y; for( int x = 0; x < bx - 1; x++ ) { indices.append( rowBase + x ); indices.append( rowBase + x + 1 ); indices.append( rowBase + x + 1 + bx ); indices.append( rowBase + x + bx ); counts.append( 4 ); } } } MFnMesh mesh; MObject meshObj = mesh.create( points.length(), counts.length(), points, counts, indices, MObject::kNullObj, &stat ); check_status( stat ); stat = mesh.updateSurface(); check_status( stat ); MDagPath path; stat = mesh.getPath( path ); check_status( stat ); m_lightGridPoints = path; stat = path.pop(); check_status( stat ); MFnDagNode fnParent( path, &stat ); check_status( stat ); fnParent.setName( MString( "lightgrid" ), &stat ); check_status( stat ); } void BspImporter::Import( std::istream &stm, const MObject &parentXform, const char *basePath ) { ospath shaderPath(basePath); shaderPath.Append("scripts/*.shader"); size_t base_pos = stm.tellg(); BinaryReader in( stm ); in >> m_fileHeader; for( int i = 0; i < q3::HEADER_LUMPS; i++ ) m_fileHeader.lumps[i].fileofs += base_pos; m_fileData = ∈ m_basePath = basePath; ParseEntities(); ImportShaderText(); WIN32_FIND_DATA fileData; HANDLE hSearch = FindFirstFile( (char*)shaderPath, &fileData ); if( hSearch != INVALID_HANDLE_VALUE ) { do { //getShaderDeps( fileData.cFileName ); } while( FindNextFile( hSearch, &fileData ) != 0 ); } ImportSurfaces(); CreateShaderQuickSelects(); ImportEntityLights(); ImportLightGridPoints(); GroupNodes( parentXform ); } class ImportBSP : public MPxFileTranslator { public: ImportBSP( void ) { } virtual /* override */ ~ImportBSP( void ) { } static void* Creator( void ) { return new ImportBSP; } virtual bool /* override */ haveWriteMethod () const { return false; } virtual bool /* override */ haveReadMethod () const { return true; } virtual MString /* override */ defaultExtension() const { return MString( "bsp" ); } virtual MString /* override */ filter() const { return MString( "*.bsp" ); } virtual MFileKind /* override */ identifyFile( const MFileObject &file, const char *buffer, short size ) const { const char * name = file.name().asChar(); int nameLength = strlen( name ); if( size >= 4 && *(int*)buffer == q3::BSP_IDENT ) return kIsMyFileType; if( (nameLength > 4) && !strcasecmp( name + nameLength - 4, ".bsp" ) ) return kCouldBeMyFileType; return kNotMyFileType; } virtual MStatus /* override */ reader( const MFileObject& filename, const MString& optionsString, FileAccessMode mode ) { bool failed = false; std::ifstream file; file.open( filename.resolvedFullName().asChar(), std::ios::in | std::ios::binary ); MString basePath = filename.resolvedPath(); basePath = basePath.substring( 0, basePath.length() - 2 ); int idx = basePath.rindex( '/' ); if( idx > 0 ) basePath = basePath.substring( 0, idx ); try { MStatus stat; MFnTransform xform; MObject xformObj = xform.create( MObject::kNullObj, &stat ); check_status( stat ); filename.name(); xform.setName( MString( "bsp_" ) + filename.resolvedName(), &stat ); check_status( stat ); const double scale[] = { 0.01, 0.01, 0.01 }; stat = xform.setScale( scale ); check_status( stat ); BspImporter imp; imp.Import( file, xformObj, basePath.asChar() ); } catch( std::exception &ex ) { MGlobal::displayError( MString( ex.what() ) ); failed = true; } catch( ... ) { failed = true; } file.close(); if( failed ) { warn( "failed importing" ); } else { MGlobal::displayInfo( MString( "successfully imported file: " ) + filename.fullName() ); } return MS::kSuccess; } }; void* (CDECL *g_translatorCreator)( void ) = &ImportBSP::Creator; const char *g_translatorName = "importBSP"; };