void MaterialPrecache::PrecacheMaterial(const char *path)
{
    QString shortPath(path);
    shortPath.remove(".xml");

    if (materials.contains(shortPath))
    {
        DBGWARNING("!! Already precached:" << path);
        return;
    }

    std::string fullPath(PATH_MATERIAL_ROOT);
    fullPath += shortPath.toStdString();
    fullPath += ".xml";

    Material *material = ParseMaterial(fullPath.c_str());

    if (material == nullptr)
    {
        DBGWARNING("!! Failed precaching material:" << path);
        return;
    }

    materials.insert(shortPath, material);
}
int LuaObjectRenderingImpl::SetMaterial(lua_State* L)
{
	// args=<objID, lodMatNum, matName, matRef>
	CSolidObject* obj = ParseSolidObject(L, __FUNCTION__, 1, GetObjectType());

	if (obj == nullptr)
		return 0;

	const string matName = luaL_checkstring(L, 3);
	const LuaMatType matType = ParseMaterialType(matName);

	LuaObjectMaterial* objMat = GetObjectMaterial(obj, matName);

	if (objMat == nullptr)
		return 0;

	LuaObjectLODMaterial* lodMat = objMat->GetMaterial(luaL_checknumber(L, 2) - 1);

	if (lodMat == nullptr)
		return 0;

	if (lua_isuserdata(L, 4)) {
		LuaMatRef** matRef = (LuaMatRef**) luaL_checkudata(L, 4, "MatRef");
		if (matRef) {
			lodMat->matref = **matRef;
		}
	} else {
		lodMat->matref = ParseMaterial(L, 4, matType);
	}

	return 0;
}
Example #3
0
/**
* Initializes the MaterialDoc instance with a specific idMaterial. This method will 
* parse the material into the internal dictionary representation and optionally 
* allow the idMaterial object to reparse the source.
* @param material The idMaterial instance to use.
* @param parseMaterial Flag to determine if the material should be parsed into the editor representation.
* @param parseRenderMaterial Flag to determine if the idMaterial object should be reparsed.
*/
void MaterialDoc::SetRenderMaterial(idMaterial* material, bool parseMaterial, bool parseRenderMatierial) {

	renderMaterial = material;


	if(!parseMaterial ||  !renderMaterial)	
		return;

	if(parseRenderMatierial) {
		char *declText = (char *) _alloca( material->GetTextLength() + 1 );
		material->GetText( declText );

		renderMaterial->GetText(declText);
		ParseMaterialText(declText);

	}

	ClearEditMaterial();

	name = material->GetName();

	idLexer		src;

	char *declText = (char *) _alloca( material->GetTextLength() + 1 );
	material->GetText( declText );

	renderMaterial->GetText(declText);
	src.LoadMemory(declText, strlen(declText), "Material");

	ParseMaterial(&src);
}
Example #4
0
bool ObjectImporter::Import		(std::wstring file, __out std::vector<int>* identifiers)
{
	if(file == L"")
		return false;

	std::wifstream in (file);
	
	if(!in.is_open())
		return false;

	std::wstring buff;
	int mid = -1;

	while (!in.eof())
	{
		in >> buff;

		if(buff == ObjImpFormat::material)
		{
			mid = ParseMaterial(in);
			if(mid != -1)
			{
				identifiers->push_back(mid);
			}
		}
		in.close();
	}

	return true;
}
int LuaObjectRenderingImpl::GetMaterial(lua_State* L)
{
	const LuaMatType matType = ParseMaterialType(luaL_checkstring(L, 1));

	if (!lua_istable(L, 2))
		luaL_error(L, "Incorrect arguments to GetMaterial");

	LuaMatRef** matRef = (LuaMatRef**) lua_newuserdata(L, sizeof(LuaMatRef*));
	luaL_getmetatable(L, "MatRef");
	lua_setmetatable(L, -2);

	*matRef = new LuaMatRef;
	**matRef = ParseMaterial(L, 2, matType);

	return 1;
}
Example #6
0
bool XFileUtils::LoadMesh(
	ID3DXFileData*		pFileData, 
	IDirect3DDevice9*	pDevice, 
	ID3DXMesh**			pNewMesh, 
	ID3DXBuffer**		pMaterial,
	DWORD*				pNumAttribute,
	GSkinInfo**			pNewSkininfo)
{
	//检查类型
	_XCheckType(pFileData, "Mesh", false);
	DWORD nVertices = 0;
	DWORD nFaces = 0;
	_XCheckExcute(ParseBasemesh(pFileData, &nVertices, &nFaces, 
		nullptr, nullptr, sizeof(XVertex_p3_n3_t1)));
	ID3DXMesh* pMesh = nullptr;
	if (FAILED(D3DXCreateMeshFVF(nFaces, nVertices, 
		D3DXMESH_32BIT | D3DXMESH_MANAGED, XVertex_p3_n3_t1::fvf, pDevice, &pMesh)))
		return false;
	void* pVertices = nullptr;
	DWORD* pIndices = nullptr;
	DWORD* pAttribtues = nullptr;
	pMesh->LockVertexBuffer(0, &pVertices);
	pMesh->LockIndexBuffer(0, (void**)&pIndices);
	pMesh->LockAttributeBuffer(0, &pAttribtues);
	_XCheckExcute(ParseBasemesh(pFileData, &nVertices, &nFaces, 
		pVertices, pIndices, sizeof(XVertex_p3_n3_t1)));

	_XCheckExcute(ParseNormals(pFileData, nVertices, pVertices, pIndices,
		sizeof(XVertex_p3_n3_t1), sizeof(D3DXVECTOR3)));

	_XCheckExcute(ParseTexcoord(pFileData, pVertices,
		sizeof(XVertex_p3_n3_t1), sizeof(D3DXVECTOR3) * 2));

	_XCheckExcute(ParseMaterial(pFileData, pAttribtues, nFaces, pNumAttribute, pMaterial));

	pMesh->UnlockAttributeBuffer();
	pMesh->UnlockIndexBuffer();
	pMesh->UnlockVertexBuffer();

	*pNewMesh = pMesh;
	
	ParseSkinInfo(pFileData, pNewSkininfo);
	return true;
}
Example #7
0
bool ParseStandardFile			(std::wifstream& in, ImportedObjectData* d)
{
	std::wstring flag;
	bool result = true;
	while(!in.eof())
	{
		in >> flag;
		if(!in.eof())
		{
			if(flag == ObjImpFormat::vertexCount)	
			{
				int count = 0;
				if(!ParseInteger(in, count))
				{
					DisplayText("File:\n" + in.getloc().name() + "\nDoes not contain any vertex information");
					return false;
				}
				else if(!count)
				{
					DisplayText("File:\n" + in.getloc().name() + "\nHas invalid vertex count (0)");
					return false;
				}

				//No animations, then only one mesh is valid
				d->objects.resize(1);
				d->objects[0].vertex = new std::vector<VERTEX::VertexPNT>(count);
				d->objects[0].material = 0;
			}
			else if(flag == ObjImpFormat::material)		{ d->objects[0].material = ParseMaterial(in); }
			else if(flag == ObjImpFormat::v)			{ result = ParseV(in,d->objects[0]);  }
			else if(flag == ObjImpFormat::vt)			{ result = ParseVT(in,d->objects[0]); }
			else if(flag == ObjImpFormat::vn)			{ result = ParseVN(in,d->objects[0]); }
			else if(flag == ObjImpFormat::comment)		{ ParseLine(in, true); }
			
			if(!result)
				return result;
		}
	}

	return result;
}
Example #8
0
/**
* Applies any source changes to the edit representation of the material.
*/
void MaterialDoc::ApplySourceModify(idStr& text) {
	
	if(sourceModify) {
		
		//Changes in the source need to clear any undo redo buffer because we have no idea what has changed
		manager->ClearUndo();
		manager->ClearRedo();

		ClearEditMaterial();

		idLexer		src;
		src.LoadMemory(text, text.Length(), "Material");

		src.SetFlags( 
			LEXFL_NOSTRINGCONCAT |			// multiple strings seperated by whitespaces are not concatenated
			LEXFL_NOSTRINGESCAPECHARS |		// no escape characters inside strings
			LEXFL_ALLOWPATHNAMES |			// allow path seperators in names
			LEXFL_ALLOWMULTICHARLITERALS |	// allow multi character literals
			LEXFL_ALLOWBACKSLASHSTRINGCONCAT |	// allow multiple strings seperated by '\' to be concatenated
			LEXFL_NOFATALERRORS				// just set a flag instead of fatal erroring
			);

		idToken token;
		if(!src.ReadToken(&token)) {
			src.Warning( "Missing decl name" );
			return;
		}
		
		ParseMaterial(&src);
		sourceModify = false;

		//Check to see if the name has changed
		if(token.Icmp(name)) {
			SetMaterialName(token, false);
		}
	}
}
Example #9
0
ModelData* ModelLoader::LoadModelFile(std::string filePath)
{
	ifstream file;
	file.open(filePath + ".obj");
        
	if (!file)
		return 0;
	string str;

	while (!file.eof())
	{
		file >> str;

		if (str == "#" || str == "s")	ParseComment(file);
		else if (str == "v")			ParsePosition(file);	//position
		else if (str == "vn")			ParseNormal(file);		//normal
		else if (str == "vt")			ParseTexCoord(file);	//texturkoordinat
		else if (str == "f")			ParseFace(file);		//face
		else if (str == "usemtl")		ParseMaterial(file);	//material
		else if (str == "g")			ParseGroup(file);		//group

		else if (str == "mtllib")								//materialfile
		{
			ParseMaterialFile(file, filePath);
		}
                str = "";
	}
	//ParseFace2(file);

	ModelData* model = new ModelData();
	for (auto it = m_groups.begin(); it != m_groups.end(); ++it)
		model->Groups.push_back(it->second);
        
        
	return model;
}
Example #10
0
void ObjParser::Load(LPCSTR Filename, LPDIRECT3DDEVICE9 pDevice)
{
		FILE* fileHandle = fopen(Filename, "r+");
		if( fileHandle == NULL )
			return;

		char line[256];
		D3DXVECTOR3 vec;

		while( !feof(fileHandle) )//Foreach Line
		{
			fgets(line, 256, fileHandle);

			#pragma region Line Read
			switch(line[0])
			{
			case 'm':
				{
				LPCSTR name = new CHAR[256];
				sscanf_s(line, "mtllib %s", name, 255);

				ParseMaterial(name);
				}
				break;
			case 'v'://Vertex Item
				{
				switch(line[1])
					{
					case ' '://Vertex
						{
						sscanf_s(line, "v %f %f %f", &vec.x, &vec.y, &vec.z );

						Vertexs.push_back(vec);
						mVertexsHT.push_back(NULL);//expand HashTable
						}
						break;
					case 't'://Texture Coordinates
						{
						D3DXVECTOR3 tex;
						sscanf_s(line, "vt %f %f %f", &vec.x, &vec.y, &vec.z);

						Textures.push_back(vec);
						}
						break;
					case 'n'://Normal
						{
						D3DXVECTOR3 nor;
						sscanf_s(line, "vn %f %f %f", &vec.x, &vec.y, &vec.z );

						Normals.push_back(vec);
						}
						break;
					default:// Not supposed to happen
						assert( false );
					}

				}
				break;
			case 'f'://Face Item
				{
				DWORD quadP[4];//Position Index quad
				DWORD quadT[4];//Texture  Index quad
				DWORD quadN[4];//Normal   Index quad
				memset(quadP, -1, sizeof(DWORD)*4);
				memset(quadT, -1, sizeof(DWORD)*4);
				memset(quadN, -1, sizeof(DWORD)*4);

				int size = strlen(line);
				int barCount = std::count(line, line+size, '/');

				int readed = 0;

				//By default .obj file puts faces with CW winding
				switch( barCount )
					{
					case 0:// tri/quad	pos
						{
							if( m_Winding == VertexWinding::CCW )
								readed = sscanf_s(line, "f %d %d %d %d", &quadP[3], &quadP[2], &quadP[1], &quadP[0] );
							else
								readed = sscanf_s(line, "f %d %d %d %d", &quadP[0], &quadP[1], &quadP[2], &quadP[3] );
							assert( readed == 3 || readed == 4 );
						}
						break;
					case 3:// tri	pos/tex
						{
							if( m_Winding == VertexWinding::CCW )
								readed = sscanf_s(line, "f %d/%d %d/%d %d/%d", 
								&quadP[2], &quadT[2],
								&quadP[1], &quadT[1],
								&quadP[0], &quadT[0]
								);
							else
								readed = sscanf_s(line, "f %d/%d %d/%d %d/%d", 
								&quadP[0], quadT[0],
								&quadP[1], quadT[1],
								&quadP[2], quadT[2]
								);
							assert( readed == 6 );
							readed /= 2;
						}
						break;
					case 4:// quad	pos/tex
						{
							if( m_Winding == VertexWinding::CCW )
								readed = sscanf_s(line, "f %d/%d %d/%d %d/%d %d/%d", 
								&quadP[3], &quadT[3],
								&quadP[2], &quadT[2], 
								&quadP[1], &quadT[1],
								&quadP[0], &quadT[0] 
								);
							else
								readed = sscanf_s(line, "f %d/%d %d/%d %d/%d %d/%d", 
								&quadP[0], &quadT[0],
								&quadP[1], &quadT[1],
								&quadP[2], &quadT[2],
								&quadP[3], &quadT[3]
								);
							assert( readed == 8 );
							readed /= 2;
						}
						break;
					case 6:// tri	pos/tex/nor
						{
							if( m_Winding == VertexWinding::CCW )
								readed = sscanf_s(line, "f %d/%d/%d %d/%d/%d %d/%d/%d", 
								&quadP[2], &quadT[2], &quadN[2],
								&quadP[1], &quadT[1], &quadN[1],
								&quadP[0], &quadT[0], &quadN[0]
								);
							else
								readed = sscanf_s(line, "f %d/%d/%d %d/%d/%d %d/%d/%d", 
								&quadP[0], &quadT[0], &quadN[0],
								&quadP[1], &quadT[1], &quadN[1],
								&quadP[2], &quadT[2], &quadN[2]
								);
							assert( readed == 9 );
							readed /= 3;
						}
						break;
					case 8:// quad	pos/tex/nor
						{
							if( m_Winding == VertexWinding::CCW )
								readed = sscanf_s(line, "f %d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d", 
								&quadP[3], &quadT[3], &quadN[3],
								&quadP[2], &quadT[2], &quadN[2],
								&quadP[1], &quadT[1], &quadN[1],
								&quadP[0], &quadT[0], &quadN[0]
								);
							else
								readed = sscanf_s(line, "f %d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d", 
								&quadP[0], &quadT[0], &quadN[0],
								&quadP[1], &quadT[1], &quadN[1],
								&quadP[2], &quadT[2], &quadN[2],
								&quadP[3], &quadT[3], &quadN[3]
								);
							assert( readed == 12 );
							readed /= 3;
						}
						break;
					default:// Not supposed to happen
						assert( false );
					}

				//The indexs are in 1 Base, we transform to 0 Base
				for( int i=0; i < 4 ; ++i )
				{
					quadP[i]--; quadT[i]--; quadN[i]--;
				}

				for(int j=0; j < 3 ; ++j)
					{
						VertexTextureNormal NewVertex;
						NewVertex.SetPosition( Vertexs[quadP[j]] );
						if( quadT[j] != (DWORD(-1)-1) )
							NewVertex.SetTexture ( Textures[quadT[j]].x, Textures[quadT[j]].y );
						if( quadN[j] != (DWORD(-1)-1) )
							NewVertex.SetNormal  ( Normals[quadN[j]] );
						
						DWORD index = AddVertex(quadP[j], NewVertex);
						mIndexs.push_back(index);
					}
 
				if( readed == 4 )// quad readed
					{
						quadP[1] = quadP[2]; quadT[1] = quadT[2]; quadN[1] = quadN[2]; 
						quadP[2] = quadP[3]; quadT[2] = quadT[3]; quadN[1] = quadN[2]; 
												
						for(int j=0; j < 3 ; ++j)
							{
							VertexTextureNormal NewVertex;
							NewVertex.SetPosition( Vertexs[quadP[j]] );
							if( quadT[j] != (DWORD(-1)-1) )
								NewVertex.SetTexture ( Textures[quadT[j]].x, Textures[quadT[j]].y );
							if( quadN[j] != (DWORD(-1)-1) )
								NewVertex.SetNormal  ( Normals[quadN[j]] );
						
							DWORD index = AddVertex(quadP[j], NewVertex);
							mIndexs.push_back(index);
							}
					}

				}
				break;

			}
			#pragma endregion

		}
			
		fclose(fileHandle);
		
		DWORD FVF = NULL;
		D3DVERTEXELEMENT9* pMeshVDeclaration = NULL;
		int code = 0;
		IdentifieLoadedFormat(FVF, pMeshVDeclaration, code);
		if( code == 0 )
			return;
		
		//Setup Mesh with VertexDeclaration corresponding to the loaded data
		LPD3DXMESH pMesh = NULL;
		HRESULT hr = NULL;
		
		int FacesCount = mIndexs.size()/3;
		switch( m_VertexMetaFormat )
		{
		case VertexMetaFormat::VertexDeclaration:
			{
				if( FacesCount > 65535 )// if huge mesh, 32 bits face index
					hr = D3DXCreateMesh(FacesCount, mVertexs.size(), D3DXMESH_32BIT | D3DXMESH_VB_SYSTEMMEM | D3DXMESH_IB_SYSTEMMEM | D3DXMESH_SYSTEMMEM , 
						pMeshVDeclaration, pDevice, &pMesh);
				else
					hr = D3DXCreateMesh(FacesCount, mVertexs.size(), D3DXMESH_VB_SYSTEMMEM | D3DXMESH_IB_SYSTEMMEM | D3DXMESH_SYSTEMMEM , 
						pMeshVDeclaration, pDevice, &pMesh);
			}
			break;
		case VertexMetaFormat::FVF:
			{
				if( FacesCount > 65535 )// if huge mesh, 32 bits face index
					hr = D3DXCreateMeshFVF(FacesCount, Vertexs.size(), D3DXMESH_32BIT | D3DXMESH_VB_SYSTEMMEM | D3DXMESH_IB_SYSTEMMEM | D3DXMESH_SYSTEMMEM ,
						FVF, pDevice, &pMesh);
				else
					hr = D3DXCreateMeshFVF(FacesCount, Vertexs.size(), D3DXMESH_VB_SYSTEMMEM | D3DXMESH_IB_SYSTEMMEM | D3DXMESH_SYSTEMMEM ,
						FVF, pDevice, &pMesh);
			}
			break;
		default:
			assert( false );
		}

		assert( !FAILED(hr) );
		
		//Puts vertex data inside loadedData in the smallest format needed
		//(not nesesarily VertexTextureNormal)
		void* loadedData = NULL;
		void* loadedIndex = NULL;
		size_t size = 0;
		//Pass to our vertex format
		PutLoadedDataInVertexDeclarationFormat(loadedData,loadedIndex,size,code, FacesCount);

		//Free Auxiliary Arrays
		Vertexs.clear();
		Textures.clear();
		Normals.clear();
		mVertexsHT.clear();
		
		void* data = NULL;
		//Loads the Vertex Buffer
		if( FAILED(pMesh->LockVertexBuffer(NULL, &data)) )
			return;

			memcpy(data, loadedData, size*mVertexs.size());

		pMesh->UnlockVertexBuffer();

		//Loads the Index Buffer
		if( FAILED(pMesh->LockIndexBuffer(NULL, &data)) )
			return;

			if( FacesCount > 65535 )
				memcpy(data, loadedIndex, sizeof(DWORD)*mIndexs.size());
			else
				memcpy(data, loadedIndex, sizeof(WORD)*mIndexs.size());

		pMesh->UnlockIndexBuffer();

		//Free main Arrays
		mVertexs.clear();
		mIndexs.clear();

		//Mesh data ready
		m_RootMeshContainer = new D3DXMESHCONTAINER;
		m_RootMeshContainer->MeshData.pMesh = pMesh;

		return;
	}
Example #11
0
bool ParseAnimationFile			(std::wifstream& in, ImportedObjectData* d)
{
	std::wstring flag;
	bool result = true;
	int vCount = 0;
	int currFrame = -1;
	int currObject = -1;
	int currAnimation = -1;
	int currMaterial = -1;
	int totMeshCount = -1;

	while(!in.eof())
	{
		in >> flag;

		if(in.eof())
			break;

		if(flag == ObjImpFormat::vertexCount)	
		{
			if(!ParseInteger(in, vCount))
			{
				DisplayText("File:\n" + in.getloc().name() + "\nDoes not contain any vertex information");
				return false;
			}
		}

		else if(flag == ObjImpFormat::animation)	
		{
			currAnimation++;
			int frameCount = 0;
			if(!ParseInteger(in, d->animations[currAnimation].id))
				return false;
			if(!ParseInteger(in, frameCount))
				return false;

			if(!frameCount)
				return false;

			d->animations[currAnimation].frames.resize(frameCount);
			currFrame = -1;
		}
		else if(flag == ObjImpFormat::frame)
		{
			float frameTime = 0.0f;
			int frameNum = -1;
			if(!ParseInteger(in, frameNum))
				return false;
			if(!ParseFloat(in, frameTime))
			{
				char temp1[2];
				_itoa_s(frameNum, temp1, 10);
				DisplayText("File:\n" + in.getloc().name() + "\nFrame: " + temp1 + "\nInvalid frame time");
				return false;
			}

			if(frameTime < 0.0f)
				return false;

			d->objects.push_back(ObjectData());
			currObject = (int)d->objects.size()-1;
			currFrame++;
			totMeshCount++;

			d->animations[currAnimation].frames[currFrame].frameNumber = frameNum;
			d->animations[currAnimation].frames[currFrame].frameTime = frameTime;
			d->animations[currAnimation].frames[currFrame].objectIndex = currFrame;

			d->objects[totMeshCount].vertex = new std::vector<VERTEX::VertexPNT>(vCount);
			d->objects[totMeshCount].material = currMaterial;
		}
		else if(flag == ObjImpFormat::material)		{ currMaterial = ParseMaterial(in);	}
		else if(flag == ObjImpFormat::v)			{ result = ParseV(in,d->objects[totMeshCount]); 	}
		else if(flag == ObjImpFormat::vt)			{ result = ParseVT(in,d->objects[totMeshCount]);	}
		else if(flag == ObjImpFormat::vn)			
		{ result = ParseVN(in,d->objects[totMeshCount]);	}
		else if(flag == ObjImpFormat::comment)		{ ParseLine(in, true); }
		
		if(!result)
			return result;
	}

	return result;
}
Example #12
0
	void PartSysParser::ParseEmitter(const TabFileRecord& record) {
		auto systemName = record[COL_PARTSYS_NAME].AsString();

		auto& system = mSpecs[tolower(systemName)];
		// Create it on demand
		if (!system) {
			system = std::make_shared<PartSysSpec>(systemName);
		}

		// Add the emitter
		auto emitter = system->CreateEmitter(record[COL_EMITTER_NAME].AsString());

		ParseOptionalFloat(record, COL_DELAY, "Delay", [&] (float value) {
			                   emitter->SetDelay(value / 30.0f);
		                   });

		ParseLifespan(record, emitter);

		ParseParticleLifespan(record, emitter);

		ParseParticleRate(record, emitter);

		ParseOptionalEnum<PartSysEmitterSpace>(record, COL_EMITTER_SPACE, "emitter space", EmitterSpaceMapping, [&](auto space) {
			                                       emitter->SetSpace(space);
		                                       });

		ParseEmitterNodeName(record, emitter);

		ParseOptionalEnum<PartSysCoordSys>(record, COL_EMITTER_COORD_SYS, "emitter coord sys", CoordSysMapping, [&](auto coordSys) {
			                                   emitter->SetCoordSys(coordSys);
		                                   });

		ParseOptionalEnum<PartSysCoordSys>(record, COL_EMITTER_OFFSET_COORD_SYS, "emitter offset coord sys", CoordSysMapping, [&](auto coordSys) {
			                                   emitter->SetOffsetCoordSys(coordSys);
		                                   });

		ParseOptionalEnum<PartSysParticleType>(record, COL_PARTICLE_TYPE, "particle type", ParticleTypeMapping, [&](PartSysParticleType type) {
			                                       emitter->SetParticleType(type);
		                                       });

		ParseOptionalEnum<PartSysBlendMode>(record, COL_BLEND_MODE, "blend mode", BlendModeMapping, [&](auto mode) {
			                                    emitter->SetBlendMode(mode);
		                                    });

		ParseMaterial(record, emitter);

		ParseOptionalEnum<PartSysCoordSys>(record, COL_PARTICLE_POS_COORD_SYS, "particle pos coord sys", CoordSysMapping, [&](auto coordSys) {
			                                   emitter->SetParticlePosCoordSys(coordSys);
		                                   });
		ParseOptionalEnum<PartSysCoordSys>(record, COL_PARTICLE_VELOCITY_COORD_SYS, "particle velocity coord sys", CoordSysMapping, [&](auto coordSys) {
			                                   emitter->SetParticleVelocityCoordSys(coordSys);
		                                   });
		ParseOptionalEnum<PartSysParticleSpace>(record, COL_PARTICLE_SPACE, "particle space", ParticleSpaceMapping, [&](auto space) {
			                                        emitter->SetParticleSpace(space);
		                                        });

		ParseMesh(record, emitter);

		// Parse the bounding box
		ParseOptionalFloat(record, COL_BB_LEFT, "bb left", [&](float val) {
			                   emitter->SetBoxLeft(val);
		                   });
		ParseOptionalFloat(record, COL_BB_TOP, "bb top", [&](float val) {
			                   emitter->SetBoxTop(val);
		                   });
		ParseOptionalFloat(record, COL_BB_RIGHT, "bb right", [&](float val) {
			                   emitter->SetBoxRight(val);
		                   });
		ParseOptionalFloat(record, COL_BB_BOTTOM, "bb bottom", [&](float val) {
			                   emitter->SetBoxBottom(val);
		                   });

		for (int paramId = 0; paramId <= part_attractorBlend; paramId++) {
			int colIdx = 22 + paramId;
			auto col = record[colIdx];
			if (col) {
				bool success;
				std::unique_ptr<PartSysParam> param(ParserParams::Parse((PartSysParamId) paramId,
				                                                        col,
				                                                        emitter->GetLifespan(),
				                                                        emitter->GetParticleLifespan(),
				                                                        success));
				if (success) {
					emitter->SetParam((PartSysParamId)paramId, param);
				} else {
					logger->warn("Unable to parse particle system param {} for particle system {} and emitter {} with value {}",
					             paramId, systemName, emitter->GetName(), col.AsString());
				}
			}
		}

	}