OBJ loader

    Add new abstract class. It will load 3D models.

    class slMeshLoader
    {
    public:
    	slMeshLoader() {}
    	virtual ~slMeshLoader() {}
    
    	virtual uint32_t GetSupportedFilesCount() = 0;
    	virtual slString GetSupportedFileExtension(uint32_t) = 0;
    	virtual slString GetSupportedFileName(uint32_t) = 0;
    
    	virtual void Load(const char* path, slMeshLoaderCallback*) = 0;
    };
    

    OBJ can have many models. Each new model will be sent into callback.

    class slMeshLoaderCallback
    {
    public:
    	slMeshLoaderCallback() {}
    	virtual ~slMeshLoaderCallback() {}
    
    	virtual void OnMaterial(slMaterial* m, slString* name);
    	virtual void OnMesh(slMesh* newMesh, slString* name, slString* materialName);
    };
    

    3D files can have materials. I need new class. This class is like in IrrLicht. I know this engine so long time, and, almost all things I write in my own way, but not materials, I can't imagine more easy way to set shader, textures and other parameters.

    enum class slShaderType
    {
    	Solid,
    	BumpMap,
    	SphereMap,
    	User
    };
    
    // Graphics System
    struct slMaterial
    {
    	slShaderType m_shader = slShaderType::Solid;
    	float m_opacity = 1.f;
    	slColor m_colorDiffuse = ColorWhite;
    	slColor m_colorAmbient = ColorGray;
    	slColor m_colorSpecular = ColorWhite;
    	slVec3 m_sunPosition;
    	bool m_wireframe = false;
    	bool m_cullBackFace = false;
    
    	struct map{
    		slTexture* m_texture = 0;
    	}m_maps[16];
    
    	slString m_name;
    };
    

    Shader type. FIrst will be shader without any effects like bumpmap. I just wrote next shaders that I want to write. User shader is that shader that user can write, but in GS lib side. I don't want to add ability to user to write shaders in app side.

    In framework add new methods

    static uint32_t GetMeshLoadersNum();
    static slMeshLoader* GetMeshLoader(uint32_t);
    static void LoadMesh(const char*, slMeshLoaderCallback*);
    

    Same like with gs

    uint32_t slFramework::GetMeshLoadersNum()
    {
    	return (uint32_t)g_framework->m_meshLoaders.size();
    }
    slMeshLoader* slFramework::GetMeshLoader(uint32_t i)
    {
    	SL_ASSERT_ST(i < g_framework->m_meshLoaders.size());
    	return g_framework->m_meshLoaders[i];
    }
    

    Load mesh

    void slFramework::LoadMesh(const char* path, slMeshLoaderCallback* cb)
    {
    	slStringA stra;
    	std::filesystem::path p = path;
    	auto e = p.extension();
    	uint32_t mln = GetMeshLoadersNum();
    	for (uint32_t i = 0; i < mln; ++i)
    	{
    		auto ml = GetMeshLoader(i);
    		auto sfc = ml->GetSupportedFilesCount();
    		for (uint32_t o = 0; o < sfc; ++o)
    		{
    			slString sfe = ml->GetSupportedFileExtension(o);
    			sfe.to_utf8(stra);
    
    			if (strcmp((const char*)stra.m_data, e.generic_string().c_str()) == 0)
    			{
    				ml->Load(path, cb);
    				return;
    			}
    		}
    	}
    }
    

    Now I need to add meshloader library into slowlib

    
    extern "C"
    {
    	slGS* SL_CDECL slGSD3D11_create();
    	slMeshLoader* SL_CDECL slMeshLoaderOBJ_create(); // new
    }
    SL_LINK_LIBRARY("slowlib.d3d11");
    SL_LINK_LIBRARY("slowlib.meshloader");
    

    OBJ loader library

    I copy pasted d3d library and changed name.

    Basic functions

    
    #include "slowlib.h"
    #include "slowlib.meshloaderimpl.h"
    
    extern "C"
    {
    	slMeshLoader* SL_CDECL slMeshLoaderOBJ_create()
    	{
    		slMeshLoaderImpl* ml = slCreate<slMeshLoaderImpl>();
    		return ml;
    	}
    }
    

    Mesh loader class

    
    class slMeshLoaderImpl : public slMeshLoader
    {
    	void ImportOBJ_MTL(slArray<OBJMaterial*>& materials, const char* obj_fileName, const char* mtl_fileName);
    	void LoadOBJ(const char* path, slMeshLoaderCallback*);
    public:
    	slMeshLoaderImpl();
    	virtual ~slMeshLoaderImpl();
    
    	virtual uint32_t GetSupportedFilesCount() final;
    	virtual slString GetSupportedFileExtension(uint32_t) final;
    	virtual slString GetSupportedFileName(uint32_t) final;
    
    	virtual void Load(const char* path, slMeshLoaderCallback*) final;
    };
    

    Methods

    
    slMeshLoaderImpl::slMeshLoaderImpl(){}
    slMeshLoaderImpl::~slMeshLoaderImpl(){}
    
    uint32_t slMeshLoaderImpl::GetSupportedFilesCount()
    {
    	return 1;
    }
    
    slString slMeshLoaderImpl::GetSupportedFileExtension(uint32_t i)
    {
    	switch (i)
    	{
    	case 0:
    		return "obj";
    	}
    	return "-";
    }
    
    slString slMeshLoaderImpl::GetSupportedFileName(uint32_t i)
    {
    	switch (i)
    	{
    	case 0:
    		return "Wavefront OBJ";
    	}
    	return "-";
    }
    
    void slMeshLoaderImpl::Load(const char* path, slMeshLoaderCallback* cb)
    {
    	size_t len = strlen(path);
    	if (len > 4)
    	{
    		// find last '.'
    		size_t last_dot = 0;
    		for (size_t i = 0; i < len; ++i)
    		{
    			if (path[i] == '.')
    				last_dot = i;
    		}
    		if (last_dot)
    		{
    			if (strcmp(".obj", &path[last_dot]) == 0)
    				LoadOBJ(path, cb);
    		}
    	}
    }
    

    LoadOBJ

    Code is very large.

    Old version is here OBJ Import Export

    Interesting parts. I read whole text into buffer, and then read this buffer using functions

    
    unsigned char* OBJNextLine(unsigned char* ptr);
    unsigned char* OBJSkipSpaces(unsigned char* ptr);
    unsigned char* OBJReadVec2(unsigned char* ptr, slVec2f& vec2);
    unsigned char* OBJReadFloat(unsigned char* ptr, float& value);
    unsigned char* OBJReadVec3(unsigned char* ptr, slVec3f& vec3);
    unsigned char* OBJReadFace(unsigned char* ptr, OBJFace& f, char* s);
    unsigned char* OBJReadWord(unsigned char* ptr, slStringA& str);
    unsigned char* OBJReadLastWord(unsigned char* ptr, slStringA& str);
    

    For example

    
    unsigned char* OBJReadVec2(unsigned char* ptr, slVec2f& vec2)
    {
    	ptr = OBJSkipSpaces(ptr);
    	float x, y;
    	if (*ptr == '\n')
    	{
    		ptr++;
    	}
    	else
    	{
    		ptr = OBJReadFloat(ptr, x);
    		ptr = OBJSkipSpaces(ptr);
    		ptr = OBJReadFloat(ptr, y);
    		ptr = OBJNextLine(ptr);
    		vec2.x = x;
    		vec2.y = y;
    	}
    	return ptr;
    }
    

    Actually need to put this functions somewhere in library because they are usefull.

    I have some helper structs

    
    enum class OBJFaceType
    {
    	p,
    	pu,
    	pun,
    	pn
    };
    
    struct OBJSimpleArr
    {
    	OBJSimpleArr() {
    		sz = 0;
    	}
    
    	int data[0x100];
    	unsigned int sz;
    
    	void push_back(int v) { data[sz++] = v; }
    	unsigned int size() { return sz; }
    	void reset() { sz = 0; }
    };
    
    struct OBJFace
    {
    	OBJFace() {
    		ft = OBJFaceType::pun;
    	}
    
    	OBJSimpleArr p, u, n;
    	OBJFaceType ft;
    
    	void reset()
    	{
    		ft = OBJFaceType::pun;
    		p.reset();
    		u.reset();
    		n.reset();
    	}
    };
    

    For MTL

    
    struct OBJMaterial
    {
    	OBJMaterial() {
    		m_specularExponent = 10.f;
    		m_opacity = 1.f;
    		m_transparency = 0.f;
    		m_refraction = 1.f;
    	}
    
    	slStringA m_name; // newmtl
    	slVec3f m_ambient;   // Ka
    	slVec3f m_diffuse;   // Kd
    	slVec3f m_specular;  // Ks
    	float m_specularExponent; // Ns
    	float m_opacity;    // d
    	float m_transparency; // Tr
    	float m_refraction;  // Ni
    
    	slStringA m_map_diffuse; // map_Kd  
    	slStringA m_map_ambient; // map_Ka 
    	slStringA m_map_specular; // map_Ks   
    	slStringA m_map_specularHighlight; // map_Ns    
    	slStringA m_map_alpha; // map_d     
    	slStringA m_map_bump; // map_bump bump 
    	slStringA m_map_displacement; // disp 
    	slStringA m_map_reflection; // refl  
    };
    

    I show face step

    
    case 'f':
    {
    	isModel = true;
    	f.reset();
    	ptr = OBJReadFace(++ptr, f, s);
    
    	polygonCreator.Clear();
    
    	bool genNormals = true;
    
    	for (size_t sz2 = f.p.size(), i2 = 0; i2 < sz2; ++i2)
    	{
    		auto index = i2;
    		auto pos_index = f.p.data[index];
    
    
    		if (pos_index < 0)
    		{
    			// если индекс отрицательный то он указывает на позицию относительно последнего элемента
    			// -1 = последний элемент
    			// if index is negative then he points on position relative last element
    			// -1 = last element
    			pos_index = last_counter[0] + pos_index + 1;
    		}
    
    		auto v = position[pos_index];
    
    		slVec3f pcPos, pcNorm;
    		slVec2f pcUV;
    
    		pcPos = v;
    
    		if (f.ft == OBJFaceType::pu || f.ft == OBJFaceType::pun)
    		{
    			auto uv_index = f.u.data[index];
    
    			if (uv_index < 0)
    			{
    				uv_index = last_counter[1] + uv_index + 1;
    			}
    
    			auto u = uv[uv_index];
    			pcUV.x = u.x;
    			pcUV.y = 1.f - u.y;
    		}
    
    		if (f.ft == OBJFaceType::pn || f.ft == OBJFaceType::pun)
    		{
    			auto nor_index = f.n.data[index];
    
    			if (nor_index < 0)
    			{
    				nor_index = last_counter[2] + nor_index + 1;
    			}
    
    			auto n = normal[nor_index];
    			//	geometry_creator->AddNormal(n.x, n.y, n.z);
    			pcNorm = n;
    			genNormals = false;
    		}
    		polygonCreator.SetPosition(pcPos);
    		polygonCreator.SetNormal(pcNorm);
    		polygonCreator.SetUV(pcUV);
    		polygonCreator.AddVertex();
    	}
    
    	if (!polygonMesh)
    		polygonMesh = slFramework::SummonPolygonMesh();
    	polygonMesh->AddPolygon(&polygonCreator, genNormals);
    
    }break;
    

    slMesh creation can be when 'o' or 'g', or without them.

    
    if (polygonMesh)
    {
    	if (polygonMesh->m_first_polygon)
    	{
    		slMaterial* m = 0;
    		if (currMaterial)
    		{
    			/*m = g_sdk->CreateMaterial(currMaterial->m_name.data());
    			if (currMaterial->m_map_diffuse.size())
    				m->m_maps[m->mapSlot_Diffuse].m_texturePath = currMaterial->m_map_diffuse;*/
    		}
    
    		cb->OnMesh(polygonMesh->CreateMesh(), &prev_word, 0);
    
    		slDestroy(polygonMesh);
    		polygonMesh = 0;
    	}
    }
    

    It works, now I need to add GPU mesh buffer and shader.

    GPU mesh

    class slGPUMesh
    {
    public:
    	slGPUMesh() {}
    	virtual ~slGPUMesh() {}
    };
    

    This is all what I need now.

    Create D3D11 mesh buffer

    
    class slGSD3D11Mesh : public slGPUMesh
    {
    public:
    	slGSD3D11Mesh();
    	virtual ~slGSD3D11Mesh();
    
    	ID3D11Buffer* m_vBuffer = 0;
    	ID3D11Buffer* m_iBuffer = 0;
    	
    	DXGI_FORMAT m_indexType = DXGI_FORMAT_R16_UINT;
    
    	slMeshInfo m_meshInfo;
    };
    

    Implementation

    
    slGSD3D11Mesh::slGSD3D11Mesh() {}
    slGSD3D11Mesh::~slGSD3D11Mesh() 
    {
    	if (m_vBuffer)
    	{
    		m_vBuffer->Release();
    		m_vBuffer = 0;
    	}
    
    	if (m_iBuffer)
    	{
    		m_iBuffer->Release();
    		m_iBuffer = 0;
    	}
    }
    

    Creating GPU mesh will be in GS. Add method

    
    virtual slGPUMesh* SummonMesh(slMesh*) = 0;
    

    Implementation. Creating GPU mesh.

    
    slGPUMesh* slGSD3D11::SummonMesh(slMesh* m)
    {
    	SL_ASSERT_ST(m);
    	SL_ASSERT_ST(m->m_vertices);
    	SL_ASSERT_ST(m->m_indices);
    	SL_ASSERT_ST(m->m_info.m_iCount);
    	SL_ASSERT_ST(m->m_info.m_vCount);
    
    	slGSD3D11Mesh* newMesh = slCreate<slGSD3D11Mesh>();
    	newMesh->m_meshInfo = m->m_info;
    
    	slZeroDecl(D3D11_BUFFER_DESC, vbd);
    	slZeroDecl(D3D11_BUFFER_DESC, ibd);
    
    	vbd.Usage = D3D11_USAGE_DEFAULT;
    	vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_STREAM_OUTPUT;
    
    	ibd.Usage = D3D11_USAGE_DEFAULT;
    	ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;
    
    	slZeroDecl(D3D11_SUBRESOURCE_DATA, vData);
    	slZeroDecl(D3D11_SUBRESOURCE_DATA, iData);
    
    	vbd.ByteWidth = m->m_info.m_stride * m->m_info.m_vCount;
    	vData.pSysMem = &m->m_vertices[0];
    
    	auto hr = m_d3d11Device->CreateBuffer(&vbd, &vData, &newMesh->m_vBuffer);
    	if (FAILED(hr))
    	{
    		slLog::PrintError("Can't create Direct3D 11 vertex buffer [%u]\n", hr);
    		return nullptr;
    	}
    
    	uint32_t index_sizeof = sizeof(uint16_t);
    	newMesh->m_indexType = DXGI_FORMAT_R16_UINT;
    	if (m->m_info.m_indexType == slMeshIndexType::u32)
    	{
    		newMesh->m_indexType = DXGI_FORMAT_R32_UINT;
    		index_sizeof = sizeof(uint32_t);
    	}
    	ibd.ByteWidth = index_sizeof * newMesh->m_meshInfo.m_iCount;
    	iData.pSysMem = &m->m_indices[0];
    
    
    	hr = m_d3d11Device->CreateBuffer(&ibd, &iData, &newMesh->m_iBuffer);
    	if (FAILED(hr))
    	{
    		slLog::PrintError("Can't create Direct3D 11 index buffer [%u]\n", hr);
    		return nullptr;
    	}
    
    	return newMesh;
    }
    

    Drawing. Add methods in GS

    
    virtual void SetMesh(slGPUMesh*) = 0;
    virtual void SetMaterial(slMaterial*) = 0;
    virtual void Draw() = 0;
    

    Implementation

    
    void slGSD3D11::SetMesh(slGPUMesh* m)
    {
    	m_currMesh = (slGSD3D11Mesh*)m;
    }
    
    void slGSD3D11::SetMaterial(slMaterial* m)
    {
    	m_currMaterial = m;
    }
    
    void slGSD3D11::Draw()
    {
    	SL_ASSERT_ST(m_currMesh);
    	SL_ASSERT_ST(m_currMaterial);
    
    	if (m_currMaterial->m_wireframe)
    	{
    		if (m_currMaterial->m_cullBackFace)
    			m_d3d11DevCon->RSSetState(m_RasterizerWireframe);
    		else
    			m_d3d11DevCon->RSSetState(m_RasterizerWireframeNoBackFaceCulling);
    	}
    	else
    	{
    		if (m_currMaterial->m_cullBackFace)
    			m_d3d11DevCon->RSSetState(m_RasterizerSolid);
    		else
    			m_d3d11DevCon->RSSetState(m_RasterizerSolidNoBackFaceCulling);
    	}
    
    	switch (m_currMesh->m_meshInfo.m_vertexType)
    	{
    	case slMeshVertexType::Triangle:
    	{
    		switch (m_currMaterial->m_shader)
    		{
    		case slShaderType::Solid:
    			SetActiveShader(m_shaderSolid);
    			m_shaderSolid->SetData(*slFramework::GetMatrix(slMatrixType::WorldViewProjection),
    				*slFramework::GetMatrix(slMatrixType::World));
    			m_shaderSolid->SetConstants(m_currMaterial);
    			break;
    		}
    	}break;
    	}
    
    	uint32_t offset = 0u;
    	m_d3d11DevCon->IASetVertexBuffers(0, 1, &m_currMesh->m_vBuffer, &m_currMesh->m_meshInfo.m_stride, &offset);
    
    	switch (m_currMesh->m_meshInfo.m_vertexType)
    	{
    	default:
    	case slMeshVertexType::Triangle:
    		m_d3d11DevCon->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    		m_d3d11DevCon->IASetIndexBuffer(m_currMesh->m_iBuffer, m_currMesh->m_indexType, 0);
    		m_d3d11DevCon->DrawIndexed(m_currMesh->m_meshInfo.m_iCount, 0, 0);
    		break;
    	}
    }
    

    Write new shader

    
    class slD3D11ShaderSolid : public slGSD3D11ShaderBase
    {
    public:
    	slD3D11ShaderSolid(slGSD3D11* gs);
    	virtual ~slD3D11ShaderSolid();
    
    	slGSD3D11* m_gs = 0;
    
    	ID3D11Buffer* m_cbV = 0;
    	ID3D11Buffer* m_cbP = 0;
    
    	struct cbV
    	{
    		slMat4 WVP;
    		slMat4 W;
    	}m_cbDataV;
    	struct cbP
    	{
    		slVec4 SunPosition;
    		slColor BaseColor;
    	}m_cbDataP;
    	virtual void SetConstants(slMaterial* material);
    
    	void SetData(const slMat4& WVP, const slMat4& W);
    
    	bool init();
    };
    

    Simple implementation

    
    slD3D11ShaderSolid::slD3D11ShaderSolid(slGSD3D11* gs)
    	:
    	m_gs(gs)
    {}
    
    slD3D11ShaderSolid::~slD3D11ShaderSolid() 
    {
    	if (m_cbV)m_cbV->Release();
    	if (m_cbP)m_cbP->Release();
    }
    
    bool slD3D11ShaderSolid::init(){
    	const char* text =
    		"struct VSIn{\n"
    		"   float3 position : POSITION;\n"
    		"	float2 uv : TEXCOORD;\n"
    		"   float3 normal : NORMAL;\n"
    		"   float3 binormal : BINORMAL;\n"
    		"   float3 tangent : TANGENT;\n"
    		"   float4 color : COLOR;\n"
    		"};\n"
    		"cbuffer cbVertex{\n"
    		"	double4x4 WVP;\n"
    		"	double4x4 W;\n"
    		"};\n"
    		"cbuffer cbPixel{\n"
    		"	double4 SunPosition;\n"
    		"	float4 BaseColor;\n"
    		"};\n"
    		"struct VSOut{\n"
    		"   float4 pos : SV_POSITION;\n"
    		"	float2 uv : TEXCOORD0;\n"
    		"	float4 vColor : COLOR0;\n"
    		"	float3 normal : NORMAL0;\n"
    		"	float4 fragPos : NORMAL1;\n"
    		"};\n"
    		"struct PSOut{\n"
    		"    float4 color : SV_Target;\n"
    		"};\n"
    		"VSOut VSMain(VSIn input){\n"
    		"   VSOut output;\n"
    		"	output.pos   = mul(WVP, double4(input.position.x, input.position.y, input.position.z, 1.0));\n"
    		"	output.uv.x    = input.uv.x;\n"
    		"	output.uv.y    = input.uv.y;\n"
    		"	output.vColor    = input.color;\n"
    		"	output.normal    = normalize(mul((double3x3)W, input.normal));\n"
    		"	output.fragPos    = mul(W, double4(input.position.x, input.position.y, input.position.z, 1.0));\n"
    		"	return output;\n"
    		"}\n"
    		"PSOut PSMain(VSOut input){\n"
    		"	float3 lightDir = normalize(-SunPosition.xyz);\n"
    		"	float diff = max(dot(input.normal, -lightDir), 0.0);\n"
    
    		"   PSOut output;\n"
    		//"   output.color = tex2d_1.Sample(tex2D_sampler_1, input.uv) * BaseColor;\n"
    		"   output.color = BaseColor;\n"
    		//"	output.color.w += input.vColor.w;\n"
    		//"	if(output.color.w>1.f) output.color.w = 1.f;\n"
    		//"	output.color.xyz = lerp(output.color.xyz, input.vColor.xyz, input.vColor.www);\n"
    		"	if(diff>1.f) diff = 1.f;\n"
    		"	output.color.xyz *= diff;\n"
    		"	output.color.w = 1.f;\n"
    		//"	if(output.color.w == 0.f) if(input.vColor.w!=1.f)discard;\n"
    		"    return output;\n"
    		"}\n";
    	if (!m_gs->createShaders(
    		"vs_5_0",
    		"ps_5_0",
    		text,
    		text,
    		"VSMain",
    		"PSMain",
    		slMeshVertexType::Triangle,
    		&this->m_vShader,
    		&this->m_pShader,
    		&this->m_vLayout))
    		return false;
    
    	if (!m_gs->createConstantBuffer(sizeof(cbV), &m_cbV))
    		return false;
    	if (!m_gs->createConstantBuffer(sizeof(cbP), &m_cbP))
    		return false;
    
    	return true;
    }
    
    void slD3D11ShaderSolid::SetData(const slMat4& WVP, const slMat4& W)
    {
    	m_cbDataV.W = W;
    	m_cbDataV.WVP = WVP;
    }
    
    void slD3D11ShaderSolid::SetConstants(slMaterial* material){
    
    	{
    		D3D11_MAPPED_SUBRESOURCE mappedResource;
    		D3D11_BUFFER_DESC d;
    		m_gs->m_d3d11DevCon->Map(m_cbV, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
    		m_cbV->GetDesc(&d);
    		memcpy(mappedResource.pData, &m_cbDataV, d.ByteWidth);
    		m_gs->m_d3d11DevCon->Unmap(m_cbV, 0);
    		m_gs->m_d3d11DevCon->VSSetConstantBuffers(0, 1, &m_cbV);
    	}
    
    	m_cbDataP.BaseColor = material->m_colorDiffuse;
    	m_cbDataP.SunPosition = material->m_sunPosition;
    
    	{
    		D3D11_MAPPED_SUBRESOURCE mappedResource;
    		D3D11_BUFFER_DESC d;
    		m_gs->m_d3d11DevCon->Map(m_cbP, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
    		m_cbP->GetDesc(&d);
    		memcpy(mappedResource.pData, &m_cbDataP, d.ByteWidth);
    		m_gs->m_d3d11DevCon->Unmap(m_cbP, 0);
    		m_gs->m_d3d11DevCon->PSSetConstantBuffers(0, 1, &m_cbP);
    	}
    }
    

    Update slGSD3D11::createShaders

    Need to create input layout. I don't know how it works, I think every shader must have unique input layout. And I think I can create static global arrays instead if this:

    
    int ind = 0;
    switch (vertexType)
    {
    case slMeshVertexType::Triangle:
    	ind = 0;
    	vertexLayout[ind].SemanticName = "POSITION";
    	vertexLayout[ind].SemanticIndex = 0;
    	vertexLayout[ind].Format = DXGI_FORMAT_R32G32B32_FLOAT;
    	vertexLayout[ind].InputSlot = 0;
    	vertexLayout[ind].AlignedByteOffset = 0;
    	vertexLayout[ind].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
    	vertexLayout[ind].InstanceDataStepRate = 0;
    
    	ind++;
    	vertexLayout[ind].SemanticName = "TEXCOORD";
    	vertexLayout[ind].SemanticIndex = 0;
    	vertexLayout[ind].Format = DXGI_FORMAT_R32G32_FLOAT;
    	vertexLayout[ind].InputSlot = 0;
    	vertexLayout[ind].AlignedByteOffset = 12;
    	vertexLayout[ind].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
    	vertexLayout[ind].InstanceDataStepRate = 0;
    
    	ind++;
    	vertexLayout[ind].SemanticName = "NORMAL";
    	vertexLayout[ind].SemanticIndex = 0;
    	vertexLayout[ind].Format = DXGI_FORMAT_R32G32B32_FLOAT;
    	vertexLayout[ind].InputSlot = 0;
    	vertexLayout[ind].AlignedByteOffset = 20;
    	vertexLayout[ind].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
    	vertexLayout[ind].InstanceDataStepRate = 0;
    
    	ind++;
    	vertexLayout[ind].SemanticName = "BINORMAL";
    	vertexLayout[ind].SemanticIndex = 0;
    	vertexLayout[ind].Format = DXGI_FORMAT_R32G32B32_FLOAT;
    	vertexLayout[ind].InputSlot = 0;
    	vertexLayout[ind].AlignedByteOffset = 32;
    	vertexLayout[ind].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
    	vertexLayout[ind].InstanceDataStepRate = 0;
    
    	ind++;
    	vertexLayout[ind].SemanticName = "TANGENT";
    	vertexLayout[ind].SemanticIndex = 0;
    	vertexLayout[ind].Format = DXGI_FORMAT_R32G32B32_FLOAT;
    	vertexLayout[ind].InputSlot = 0;
    	vertexLayout[ind].AlignedByteOffset = 44;
    	vertexLayout[ind].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
    	vertexLayout[ind].InstanceDataStepRate = 0;
    
    	ind++;
    	vertexLayout[ind].SemanticName = "COLOR";
    	vertexLayout[ind].SemanticIndex = 0;
    	vertexLayout[ind].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    	vertexLayout[ind].InputSlot = 0;
    	vertexLayout[ind].AlignedByteOffset = 56;
    	vertexLayout[ind].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
    	vertexLayout[ind].InstanceDataStepRate = 0;
    	break;
    }
    vertexLayoutSize = ind + 1;
    

    How to use

    This is not very simple way to load a model. The Idea of this library is write simple layer, then write anoter layer like above first layer.

    For exmaple, model class in Demo

    
    class MyModel {
    	slGS* m_gs = 0;
    	slArray<slGPUMesh*> m_meshBuffers;
    	slMaterial m_material;
    	slMat4 m_W;
    	slMat4 m_WVP;
    public:
    	MyModel(slGS* gs):m_gs(gs) {}
    	~MyModel() 
    	{
    		for (size_t i = 0; i < m_meshBuffers.m_size; ++i)
    		{
    			slDestroy(m_meshBuffers.m_data[i]);
    		}
    	}
    
    	class cb : public slMeshLoaderCallback
    	{
    	public:
    		cb(){}
    		virtual ~cb() {}
    
    		virtual void OnMaterial(slMaterial* m, slString* name) override{}
    		virtual void OnMesh(slMesh* newMesh, slString* name, slString* materialName) override{
    			if (newMesh)
    			{
    				slGPUMesh* gpumesh = m_model->m_gs->SummonMesh(newMesh);
    				if(gpumesh)
    					m_model->m_meshBuffers.push_back(gpumesh);
    				slDestroy(newMesh);
    			}
    		}
    
    		MyModel* m_model = 0;
    	}
    	m_cb;
    
    	bool Load(const char* p)
    	{
    		m_cb.m_model = this;
    		slFramework::LoadMesh(p, &m_cb);
    		return m_meshBuffers.m_size > 0;
    	}
    
    	void Draw(slCamera* camera)
    	{
    		m_WVP = camera->m_projectionMatrix * camera->m_viewMatrix * m_W;
    		m_material.m_sunPosition.set(0.f, 1.f, 0.f);
    
    		slFramework::SetMatrix(slMatrixType::World, &m_W);
    		slFramework::SetMatrix(slMatrixType::WorldViewProjection, &m_WVP);
    		for (size_t i = 0; i < m_meshBuffers.m_size; ++i)
    		{			
    			m_gs->SetMesh(m_meshBuffers.m_data[i]);
    			m_gs->SetMaterial(&m_material);
    			m_gs->Draw();
    		}
    	}
    
    	void SetPosition(const slVec3& p) 
    	{
    		m_W.m_data[3] = p;
    		m_W.m_data[3].w = 1.f;
    	}
    };
    

    ...

    
    MyModel* mm = slCreate<MyModel>(gs);
    if (mm->Load(slFramework::GetPathA("..\\data\\4_objs.obj").c_str()))
    {
    	printf("LOADED!\n");
    }
    mm->SetPosition(globalPosition);
    
    ...
    // drawing
    mm->Draw(camera);
    

    4 models

    Other things

    Using this function I can open files even if program have other working directory (for example if it launched from cmd)

    
    slString slFramework::GetAppPath()
    {
    	return g_framework->m_appPath;
    }
    
    slStringA slFramework::GetPathA(const slString& v)
    {
    	slString p = g_framework->m_appPath;
    	p.append(v);
    	slStringA stra;
    	p.to_utf8(stra);
    	return stra;
    }
    
    Download