Jump to content

Josh

Staff
  • Posts

    23,145
  • Joined

  • Last visited

Posts posted by Josh

  1. I am considering implementing our own file format and using a pipeline more like Leadwerks, where glTF and FBX models get automatically converted by the editor. You can still use glTF files for your final game files if you wish, and the converter can be disabled in the program settings if you wish.

    The file format is designed to be very simple to read and write, while loading fast enough for game use.

    Features:

    • LODs
    • Skeletal animation
    • External material files
    • Vertex morphs
    • User-defined entity properties
    • Embedded collider
    • Embedded picking structure

    I think it is easiest to understand the file format just by looking at the loading code:

    #include "UltraEngine.h"
    
    using namespace UltraEngine;
    
    namespace UltraEngine::Core
    {
    	String G3DModelLoader::ReadText(shared_ptr<Stream> stream)
    	{
    		int len = stream->ReadInt();
    		auto pos = stream->GetPosition();
    		String s;
    		if (len)
    		{
    			s = stream->ReadString(len);
    			stream->Seek(pos + len);
    		}
    		return s;
    	}
    	
    	bool G3DModelLoader::Reload(shared_ptr<Stream> stream, shared_ptr<Object> o, const LoadFlags flags)
    	{
    		auto modelbase = o->As<ModelBase>();
    		if (modelbase == NULL) return false;
    		modelbase->model = CreateModel(NULL);
    		auto model = modelbase->model->As<Model>();
    
    		if (stream->ReadString(4) != "G3D") return false;
    		int version = stream->ReadInt();
    		if (version != 100)
    		{
    			Print("Error: G3D version " + String(version) + " not supported");
    			return false;
    		}
    
    		return LoadNode(stream, model, flags);
    	}
    
    	bool G3DModelLoader::LoadNode(shared_ptr<Stream> stream, shared_ptr<Model> model, const LoadFlags flags)
    	{
    		Vec3 pos, scale;
    		Quat rot;
    		String s;
    		if (stream->ReadString(4) != "NODE") return false;
    
    		model->name = ReadText(stream);
    		model->properties = ParseJson(ReadText(stream));
    		ParseJson(ReadText(stream));
    
    		pos.x = stream->ReadFloat();
    		pos.y = stream->ReadFloat();
    		pos.z = stream->ReadFloat();
    
    		rot.x = stream->ReadFloat();
    		rot.y = stream->ReadFloat();
    		rot.z = stream->ReadFloat();
    		rot.w = stream->ReadFloat();
    
    		scale.x = stream->ReadFloat();
    		scale.y = stream->ReadFloat();
    		scale.z = stream->ReadFloat();
    
    		model->SetPosition(pos);
    		model->SetRotation(rot);
    		model->SetScale(scale);
    
    		int countlods = stream->ReadInt();
    		for (int level = 0; level < countlods; ++level)
    		{
    			if (not LoadLod(stream, model, level, flags)) return false;
    		}
    
    		int countkids = stream->ReadInt();
    		for (int n = 0; n < countkids; ++n)
    		{
    			auto child = CreateModel(NULL);
    			child->SetParent(model);
    			if (not LoadNode(stream, child, flags)) return false;
    		}
    
    		// Animations
    		int animcount = stream->ReadInt();
    		for (int n = 0; n < animcount; ++n)
    		{
    			stream->ReadInt();// length
    			stream->ReadFloat();// speed
    			auto name = ReadText(stream);
    		}
    
    		// Skeleton
    		int bones = stream->ReadInt();
    		if (bones)
    		{
    			if (model->GetParent())
    			{
    				Print("Error: Skeleton can only appear in the model root node");
    				return false;
    			}
    			if (bones != 1)
    			{
    				Print("Error: Skeleton root must have one bone");
    				return false;
    			}
    			auto skeleton = CreateSkeleton(nullptr);
    			model->SetSkeleton(skeleton);
    			for (int n = 0; n < bones; ++n)
    			{
    				auto bone = std::make_shared<Bone>(nullptr, skeleton);
    				LoadBone(stream, skeleton, bone, animcount, flags);
    			}
    			skeleton->UpdateSkinning();
    			model->SetSkeleton(skeleton);
    		}
    
    		// Collider
    		int partscount = stream->ReadInt();
    
    		model->UpdateBounds();
    		return true;
    	}
    
    	bool G3DModelLoader::LoadLod(shared_ptr<Stream> stream, shared_ptr<Model> model, const int level, const LoadFlags flags)
    	{
    		if (stream->ReadString(4) != "LOD ") return false;
    		if (level >= model->lods.size()) model->AddLod();
    		float loddistance = stream->ReadFloat();
    		int countmeshes = stream->ReadInt();
    		for (int m = 0; m < countmeshes; ++m)
    		{
    			if (not LoadMesh(stream, model, level, flags)) return false;
    		}
    		return true;
    	}
    
    	bool G3DModelLoader::LoadMesh(shared_ptr<Stream> stream, shared_ptr<Model> model, const int level, const LoadFlags flags)
    	{
    		if (stream->ReadString(4) != "MESH") return false;
    		
    		MeshPrimitives type = MeshPrimitives(stream->ReadInt());
    		if (type < 1 or type > 4)
    		{
    			Print("Error: Mesh type must be between one and four");
    			return false;
    		}
    
    		auto mesh = model->AddMesh(type, level);
    
    		mesh->name = ReadText(stream);
    
    		WString mtlpath = ReadText(stream);
    		if (not mtlpath.empty())
    		{
    			if (mtlpath.Left(2) == "./" and not stream->path.empty())
    			{
    				mtlpath = ExtractDir(stream->path) + "/" + mtlpath;
    			}
    			auto mtl = LoadMaterial(mtlpath);
    			if (mtl) mesh->SetMaterial(mtl);
    		}
    
    		int vertexstride = stream->ReadInt();
    		if (vertexstride != 84) return false;
    		int vertexcount = stream->ReadInt();
    
    		mesh->m_vertices.resize(vertexcount);
    		for (int v = 0; v < vertexcount; ++v)
    		{
    			mesh->m_vertices[v].position.x = stream->ReadFloat();
    			mesh->m_vertices[v].position.y = stream->ReadFloat();
    			mesh->m_vertices[v].position.z = stream->ReadFloat();
    			mesh->m_vertices[v].normal.x = stream->ReadFloat();
    			mesh->m_vertices[v].normal.y = stream->ReadFloat();
    			mesh->m_vertices[v].normal.z = stream->ReadFloat();
    			mesh->m_vertices[v].texcoords.x = stream->ReadFloat();
    			mesh->m_vertices[v].texcoords.y = stream->ReadFloat();
    			mesh->m_vertices[v].texcoords.z = stream->ReadFloat();
    			mesh->m_vertices[v].texcoords.w = stream->ReadFloat();
    			mesh->m_vertices[v].color.r = float(stream->ReadByte()) / 255.0f;
    			mesh->m_vertices[v].color.g = float(stream->ReadByte()) / 255.0f;
    			mesh->m_vertices[v].color.b = float(stream->ReadByte()) / 255.0f;
    			mesh->m_vertices[v].color.a = float(stream->ReadByte()) / 255.0f;
    			mesh->m_vertices[v].displacement = stream->ReadFloat();
    			mesh->m_vertices[v].tangent.x = stream->ReadFloat();
    			mesh->m_vertices[v].tangent.y = stream->ReadFloat();
    			mesh->m_vertices[v].tangent.z = stream->ReadFloat();
    			mesh->m_vertices[v].bitangent.x = stream->ReadFloat();
    			mesh->m_vertices[v].bitangent.y = stream->ReadFloat();
    			mesh->m_vertices[v].bitangent.z = stream->ReadFloat();
    			mesh->m_vertices[v].boneindices[0] = stream->ReadShort();
    			mesh->m_vertices[v].boneindices[1] = stream->ReadShort();
    			mesh->m_vertices[v].boneindices[2] = stream->ReadShort();
    			mesh->m_vertices[v].boneindices[3] = stream->ReadShort();
    			mesh->m_vertices[v].boneweights.x = float(stream->ReadByte()) / 255.0f;
    			mesh->m_vertices[v].boneweights.y = float(stream->ReadByte()) / 255.0f;
    			mesh->m_vertices[v].boneweights.z = float(stream->ReadByte()) / 255.0f;
    			mesh->m_vertices[v].boneweights.w = float(stream->ReadByte()) / 255.0f;
    		}
    
    		int indicesize = stream->ReadInt();
    		int indicecount = stream->ReadInt();
    
    		uint32_t index;		
    		switch (indicesize)
    		{
    		case 2:
    			mesh->m_indices.reserve(indicecount);
    			for (int i = 0; i < indicecount; ++i) mesh->AddIndice(stream->ReadShort());
    			break;
    		case 4:
    			mesh->m_indices.resize(indicecount);
    			stream->Read(mesh->m_indices.data(), indicecount * sizeof(mesh->indices[0]));
    			break;
    		default:
    			return false;
    		}
    
    		// Pick structure cache
    		int pickcachesize = stream->ReadInt();
    		if (pickcachesize) stream->Seek(stream->GetPosition() + pickcachesize);
    
    		//Vertex morphs
    		int morphcount = stream->ReadInt();
    		for (int m = 0; m < morphcount; ++m)
    		{
    			if (stream->ReadString(4) != "MORP") return false;
    			if (stream->ReadInt() != 48) return false;
    
    			for (int v = 0; v < vertexcount; ++v)
    			{
    				// Position
    				stream->ReadFloat();
    				stream->ReadFloat();
    				stream->ReadFloat();
    
    				// Normal
    				stream->ReadFloat();
    				stream->ReadFloat();
    				stream->ReadFloat();
    
    				// Tangent
    				stream->ReadFloat();
    				stream->ReadFloat();
    				stream->ReadFloat();
    
    				// Bitangent
    				stream->ReadFloat();
    				stream->ReadFloat();
    				stream->ReadFloat();
    			}
    		}
    
    		mesh->UpdateBounds();
    		return true;
    	}
    
    	bool G3DModelLoader::LoadBone(shared_ptr<Stream> stream, shared_ptr<Skeleton> skeleton, shared_ptr<Bone> bone, const int animcount, const LoadFlags flags)
    	{
    		bone->name = ReadText(stream);
    		bone->position.x = stream->ReadFloat();
    		bone->position.y = stream->ReadFloat();
    		bone->position.z = stream->ReadFloat();
    		bone->quaternion.x = stream->ReadFloat();
    		bone->quaternion.y = stream->ReadFloat();
    		bone->quaternion.z = stream->ReadFloat();
    		bone->quaternion.w = stream->ReadFloat();
    		bone->scale = stream->ReadFloat();
    		stream->ReadFloat();// scale y
    		stream->ReadFloat();// scale z
    
    		int count = stream->ReadInt();
    		if (count != animcount)
    		{
    			Print("Error: Bone animation count must match that of the root node");
    			return false;
    		}
    
    		for (int anim = 0; anim < count; ++anim)
    		{
    			if (stream->ReadString(4) != "ANIM") return false;
    			int keyflags = stream->ReadInt();
    			int keyframes = stream->ReadInt();
    			if (keyflags)
    			{
    				for (int k = 0; k < keyframes; ++k)
    				{
    					if ((1 & keyflags) != 0)
    					{
    						stream->ReadFloat();
    						stream->ReadFloat();
    						stream->ReadFloat();
    					}
    					if ((2 & keyflags) != 0)
    					{
    						stream->ReadFloat();
    						stream->ReadFloat();
    						stream->ReadFloat();
    						stream->ReadFloat();
    					}
    					if ((4 & keyflags) != 0)
    					{
    						stream->ReadFloat();
    						stream->ReadFloat();
    						stream->ReadFloat();
    					}
    				}
    			}
    		}
    
    		return true;
    	}
    }

     

    • Upvote 2
  2. 0.9.5

    • Removed assimp library due to this issue.
    • Removed FBX to glTF converter script.
    • Removed Collada to glTF converter script.
    • Added FBX to MDL converter tool and script.
    • Fixed bug that would cause launched processes to hang forever. This could affect both your launched games, converters, and possibly the preview tool.
    • Like 1
    • Thanks 1
  3. 0.9.5

    • Some bug fixes
    • Added experimental support for FBX and X files. Animation is not yet supported.
    • You must add this header search path to existing C++ projects in the project settings:
      $(ULTRAENGINE)\Include\Libraries\assimp\include
    • You will also see noticeably smoother camera movement, best with release mode and VSync on.
    • Like 1
    • Thanks 1
  4. I correct the function definition:

    "Point", sol::overload
    (
      [](Entity& e, shared_ptr<Entity> target) { e.Point(target); },
      [](Entity& e, shared_ptr<Entity> target, int a) { e.Point(target, a); },
      [](Entity& e, shared_ptr<Entity> target, int a, float r) { e.Point(target, a, r); },
      [](Entity& e, shared_ptr<Entity> target, int a, float r, float z) { e.Point(target, a, r, z); },
      [](Entity& e, Vec3& target) { e.Point(target); },
      [](Entity& e, Vec3& target, int a) { e.Point(target, a); },
      [](Entity& e, Vec3& target, int a, float r) { e.Point(target, a, r); },
      [](Entity& e, Vec3& target, int a, float r, float z) { e.Point(target, a, r, z); },
      [](Entity& e, float x, float y, float z) { e.Point(x,y,z); },
      [](Entity& e, float x, float y, float z, int a) { e.Point(x, y, z, a); },
      [](Entity& e, float x, float y, float z, int a, float r) { e.Point(x, y, z, a, r); },
      [](Entity& e, float x, float y, float z, int a, float r, float roll) { e.Point(x, y, z, a, r, roll); }
    ),

     

    • Thanks 1
  5. Test:

    local world = CreateWorld()
    
    local box = CreateBox(world)
    local box2 = CreateBox(world);
    
    box:SetPosition(2,0,0)
    box:Point(box2, 2, 1, 0)

    result:

    sol: no matching function call takes this number of arguments and the specified types

  6. This is a protected member that was meant for possible future development. I am making this member private.

    I also added a read-only font member to the Interface class. This will only be non-NULL when a 3D GUI is created.

    • Thanks 1
  7. This is looking pretty good. Everything except animations is loading now.

    I tried the Step file loading, but it failed on my first try, and there are a lot of posts saying it's not really supported, as I suspected.

    #include "UltraEngine.h"
    #include "ComponentSystem.h"
    #include "assimp/include/assimp/Importer.hpp"
    #include "assimp/include/assimp/scene.h"
    #include "assimp/include/assimp/postprocess.h"
    #include "assimp/include/assimp/pbrmaterial.h"
    
    using namespace UltraEngine;
    
    shared_ptr<Model> AILoadModel(const aiScene* scene, aiNode* node, shared_ptr<World> world, shared_ptr<Entity> parent, const std::vector<shared_ptr<Material> >& materials)
    {
        auto model = CreateModel(world);
        model->SetParent(parent);
        model->name = std::string(node->mName.C_Str());
    
        // Process meshes attached to the current node (if any)
        for (unsigned int i = 0; i < node->mNumMeshes; i++)
        {
            MeshPrimitives mode = MeshPrimitives(0);
            aiMesh* aimesh = scene->mMeshes[node->mMeshes[i]];
            switch (aimesh->mPrimitiveTypes)
            {
            case aiPrimitiveType_POINT:
                mode = MESH_POINTS;
                break;
            case aiPrimitiveType_LINE:
                mode = MESH_LINES;
                break;
            case aiPrimitiveType_POLYGON:
                if (aimesh->mNumFaces > 0 and aimesh->mFaces[0].mNumIndices == 4) mode = MESH_QUADS;
                break;
            case aiPrimitiveType_TRIANGLE:
                mode = MESH_TRIANGLES;
                break;
            }
            if (mode != 0 and aimesh->HasPositions())
            {
                auto mesh = model->AddMesh(mode);
                Vec3 pos, norm;
                Vec2 texcoords;
                Vec4 color;
                std::array<int, 4> bones;
                std::array<float, 4> weights;
                for (int v = 0; v < aimesh->mNumVertices; ++v)
                {
                    pos.x = aimesh->mVertices[v].x;
                    pos.y = aimesh->mVertices[v].y;
                    pos.z = aimesh->mVertices[v].z;
                    if (aimesh->HasNormals())
                    {
                        norm.x = aimesh->mNormals[v].x;
                        norm.y = aimesh->mNormals[v].y;
                        norm.z = aimesh->mNormals[v].z;
                    }
                    if (aimesh->GetNumUVChannels() > 0)
                    {
                        texcoords.x = aimesh->mTextureCoords[0][v].x;
                        texcoords.y = 1.0f - aimesh->mTextureCoords[0][v].y;
                    }
                    mesh->AddVertex(pos, norm, texcoords);
                    if (aimesh->HasVertexColors(0) and aimesh->GetNumColorChannels() == 4)
                    {
                        color.r = aimesh->mColors[0][v].r;
                        color.g = aimesh->mColors[0][v].g;
                        color.b = aimesh->mColors[0][v].b;
                        color.a = aimesh->mColors[0][v].a;
                        mesh->SetVertexColor(v, color);
                    }
                    if (aimesh->GetNumUVChannels() > 1)
                    {
                        texcoords.x = aimesh->mTextureCoords[1][v].x;
                        texcoords.y = 1.0f - aimesh->mTextureCoords[1][v].y;
                        mesh->SetVertexTexCoords(v, texcoords, 1);
                    }
                    if (aimesh->HasBones())
                    {
                        for (int n = 0; n < Min(aimesh->mNumBones, 4); ++n)
                        {
                            //bones[n] = aimesh->mBones[n]->mNode;
                            weights[n] = aimesh->mBones[n]->mWeights->mWeight;
                        }
                        //mesh->SetVertexBones();
                    }
                    if (mode == MESH_POINTS) mesh->AddIndice(v);
                }
                if (mode != MESH_POINTS)
                {
                    for (int v = 0; v < aimesh->mNumFaces; ++v)
                    {
                        switch (mode)
                        {
                        case MESH_LINES:
                            mesh->AddPrimitive(aimesh->mFaces[v].mIndices[0], aimesh->mFaces[v].mIndices[1]);
                            break;
                        case MESH_TRIANGLES:
                            mesh->AddPrimitive(aimesh->mFaces[v].mIndices[0], aimesh->mFaces[v].mIndices[1], aimesh->mFaces[v].mIndices[2]);
                            break;
                        case MESH_QUADS:
                            mesh->AddPrimitive(aimesh->mFaces[v].mIndices[3], aimesh->mFaces[v].mIndices[2], aimesh->mFaces[v].mIndices[1], aimesh->mFaces[v].mIndices[0]);
                            break;
                        }
                    }
                }
    
                mesh->SetMaterial(materials[aimesh->mMaterialIndex]);
                mesh->UpdateBounds();
                if (aimesh->HasNormals() and aimesh->GetNumUVChannels() > 0) mesh->UpdateTangents();
            }
        }
        for (int n = 0; n < node->mNumChildren; ++n) AILoadModel(scene, node->mChildren[n], world, model, materials);
        model->UpdateBounds();
        return model;
    }
    
    shared_ptr<Model> ImportModel(shared_ptr<World> world, const WString& path)
    {
        Assimp::Importer importer;
        auto d = CurrentDir();
        
        const aiScene* scene = importer.ReadFile(path.ToUtf8String().c_str(), aiProcess_JoinIdenticalVertices | aiProcess_SortByPType);
    
        if (not scene or scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE or not scene->mRootNode)
        {
            return nullptr;
        }
    
        std::vector<shared_ptr<Texture> > textures;
        if (scene->HasTextures())
        {
            for (unsigned int i = 0; i < scene->mNumTextures; ++i)
            {
                aiTexture* aitex = scene->mTextures[i];
                auto buffer = CreateBuffer(aitex->mWidth * aitex->mHeight * 4);
                memcpy(buffer->Data(), aitex->pcData, buffer->GetSize());
                auto pixmap = CreatePixmap(aitex->mWidth, aitex->mHeight, TEXTURE_RGBA, buffer);
                auto mipchain = pixmap->BuildMipchain();
                auto tex = CreateTexture(TEXTURE_2D, aitex->mWidth, aitex->mHeight, TEXTURE_RGBA, mipchain);
            }
        }
    
        std::vector<shared_ptr<Material> > materials;
        if (scene->HasMaterials())
        {
            for (unsigned int i = 0; i < scene->mNumMaterials; ++i)
            {
                auto mtl = CreateMaterial();
                
                auto aimtl = scene->mMaterials[i];
                aiString path;
                if (aimtl->GetTextureCount(aiTextureType_DIFFUSE) > 0)
                {
                    if (aimtl->GetTexture(aiTextureType_DIFFUSE, 0, &path) == aiReturn_SUCCESS)
                    {
                        auto tex = LoadTexture(std::string(path.C_Str()));
                        mtl->SetTexture(tex, TEXTURE_BASE);
                    }
                }
    
                if (aimtl->GetTextureCount(aiTextureType_NORMALS) > 0)
                {
                    if (aimtl->GetTexture(aiTextureType_NORMALS, 0, &path) == aiReturn_SUCCESS)
                    {
                        auto tex = LoadTexture(std::string(path.C_Str()));
                        mtl->SetTexture(tex, TEXTURE_NORMAL);
                    }
                }
    
                if (aimtl->GetTextureCount(aiTextureType_EMISSIVE) > 0)
                {
                    if (aimtl->GetTexture(aiTextureType_EMISSIVE, 0, &path) == aiReturn_SUCCESS)
                    {
                        auto tex = LoadTexture(std::string(path.C_Str()));
                        mtl->SetTexture(tex, TEXTURE_EMISSION);
                    }
                }
    
                float metalness;;            
                if (AI_SUCCESS == aimtl->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, metalness))
                {
                    mtl->SetMetalness(metalness);
                }
    
                float roughness;;
                if (AI_SUCCESS == aimtl->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, roughness))
                {
                    mtl->SetRoughness(roughness);
                }
    
                aiColor4D pbrSpecularGlossiness;
                if (AI_SUCCESS == aimtl->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS, pbrSpecularGlossiness))
                {
                    mtl->SetShaderFamily(LoadShaderFamily("Shaders/SpecularGloss.fam"));
                    mtl->SetSpecular(Vec3(pbrSpecularGlossiness.r, pbrSpecularGlossiness.g, pbrSpecularGlossiness.b));
                    mtl->SetGlossiness(pbrSpecularGlossiness.a);
                }
    
                int unlit;
                if (AI_SUCCESS == aimtl->Get(AI_MATKEY_GLTF_UNLIT, unlit) && unlit == 1)
                {
                    mtl->SetShaderFamily(LoadShaderFamily("Shaders/Unlit.fam"));
                }
    
                aiColor4D diffuseColor(0.f, 0.f, 0.f, 0.f);
                aimtl->Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor);
    
                aiColor3D emissiveColor(0.f, 0.f, 0.f);
                aimtl->Get(AI_MATKEY_COLOR_EMISSIVE, emissiveColor);
    
                mtl->SetColor(diffuseColor.r, diffuseColor.g, diffuseColor.b, diffuseColor.a);
                mtl->SetEmission(emissiveColor.r, emissiveColor.g, emissiveColor.b);
    
                materials.push_back(mtl);
            }
        }
    
        auto model = AILoadModel(scene, scene->mRootNode, world, NULL, materials);
    
        //for (int n = 0; n < scene->mNumAnimations; ++n)
        //{
        //    aiAnimation* animation = scene->mAnimations[n];
        //    animation->mChannels[0].
        //}
    
        return model;
    }
    
    int main(int argc, const char* argv[])
    {
        RegisterComponents();
    
        auto cl = ParseCommandLine(argc, argv);
        
        //Load FreeImage plugin (optional)
        auto fiplugin = LoadPlugin("Plugins/FITextureLoader");
    
        //Get the displays
        auto displays = GetDisplays();
    
        //Create a window
        auto window = CreateWindow("Ultra Engine", 0, 0, 1280 * displays[0]->scale, 720 * displays[0]->scale, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR);
    
        //Create a framebuffer
        auto framebuffer = CreateFramebuffer(window);
    
        //Create a world
        auto world = CreateWorld();
    
        auto camera = CreateCamera(world);
        camera->AddComponent<CameraControls>();
        camera->Move(0, 0, 3);
        camera->Turn(0, 180, 0);
        camera->SetClearColor(0.25);
    
        //Load a model
        auto model = ImportModel(world, "test.gltf");//load your own model here
    
        auto light = CreateDirectionalLight(world);
        light->SetRotation(34, 36+180, 0);
    
        //Set environment maps
        const WString remotepath = "https://raw.githubusercontent.com/UltraEngine/Documentation/master/Assets";
        auto specmap = LoadTexture(remotepath + "/Materials/Environment/Storm/specular.dds");
        auto diffmap = LoadTexture(remotepath + "/Materials/Environment/Storm/diffuse.dds");
        world->SetEnvironmentMap(specmap, ENVIRONMENTMAP_SPECULAR);
        world->SetEnvironmentMap(diffmap, ENVIRONMENTMAP_DIFFUSE);
    
        //Main loop
        while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false)
        {
            world->Update();
            world->Render(framebuffer);
        }
        return 0;
    }

    image.thumb.png.e349f5f1051611a94d3e18b7cdc17e86.png

  8. It seems pretty easy to load models with this:

    #include "UltraEngine.h"
    #include "ComponentSystem.h"
    #include "assimp/include/assimp/Importer.hpp"
    #include "assimp/include/assimp/scene.h"
    #include "assimp/include/assimp/postprocess.h"
    
    using namespace UltraEngine;
    
    shared_ptr<Model> AILoadModel(const aiScene* scene, aiNode* node, shared_ptr<World> world, shared_ptr<Entity> parent)
    {
        auto model = CreateModel(world);
        model->SetParent(parent);
        model->name = std::string(node->mName.C_Str());
    
        // Process meshes attached to the current node (if any)
        for (unsigned int i = 0; i < node->mNumMeshes; i++)
        {
            MeshPrimitives mode = MeshPrimitives(0);
            aiMesh* aimesh = scene->mMeshes[node->mMeshes[i]];
            switch (aimesh->mPrimitiveTypes)
            {
            case aiPrimitiveType_POINT:
                mode = MESH_POINTS;
                break;
            case aiPrimitiveType_LINE:
                mode = MESH_LINES;
                break;
            case aiPrimitiveType_POLYGON:
                mode = MESH_QUADS;
                break;
            case aiPrimitiveType_TRIANGLE:
                mode = MESH_TRIANGLES;
                break;
            }
            if (mode != 0 and aimesh->HasPositions())
            {
                auto mesh = model->AddMesh(mode);
                Vec3 pos, norm;
                Vec2 texcoords;
                for (int v = 0; v < aimesh->mNumVertices; ++v)
                {
                    pos.x = aimesh->mVertices[v].x;
                    pos.y = aimesh->mVertices[v].y;
                    pos.z = aimesh->mVertices[v].z;
                    if (aimesh->HasNormals())
                    {
                        norm.x = aimesh->mNormals[v].x;
                        norm.y = aimesh->mNormals[v].y;
                        norm.z = aimesh->mNormals[v].z;
                    }
                    if (aimesh->GetNumUVChannels() > 0)
                    {
                        texcoords.x = aimesh->mTextureCoords[0]->x;
                        texcoords.y = aimesh->mTextureCoords[0]->y;
                    }
                    mesh->AddVertex(pos, norm, texcoords);
                    if (aimesh->GetNumUVChannels() > 1)
                    {
                        texcoords.x = aimesh->mTextureCoords[1]->x;
                        texcoords.y = aimesh->mTextureCoords[1]->y;
                        mesh->SetVertexTexCoords(v, texcoords);
                    }
                    if (mode == MESH_POINTS) mesh->AddIndice(v);
                }
                if (mode != MESH_POINTS)
                {
                    for (int v = 0; v < aimesh->mNumFaces; ++v)
                    {
                        switch (mode)
                        {
                        case MESH_LINES:
                            mesh->AddPrimitive(aimesh->mFaces[v].mIndices[0], aimesh->mFaces[v].mIndices[1]);
                            break;
                        case MESH_TRIANGLES:
                            mesh->AddPrimitive(aimesh->mFaces[v].mIndices[2], aimesh->mFaces[v].mIndices[1], aimesh->mFaces[v].mIndices[0]);
                            break;
                        case MESH_QUADS:
                            mesh->AddPrimitive(aimesh->mFaces[v].mIndices[3], aimesh->mFaces[v].mIndices[2], aimesh->mFaces[v].mIndices[1], aimesh->mFaces[v].mIndices[0]);
                            break;
                        }
                    }
                }
                if (aimesh->mMaterialIndex)
                {
                    auto aimtl = scene->mMaterials[aimesh->mMaterialIndex];
                    
                    aiString diffuseTexturePath, normalTexturePath, metalTexturePath, roughnessTexturePath, displacementTexturePath, occlusionTexturePath, specularTexturePath, glossTexturePath;
                    
                    if (aimtl->GetTextureCount(aiTextureType_DIFFUSE)) aimtl->GetTexture(aiTextureType_DIFFUSE, 0, &diffuseTexturePath);             
                    if (aimtl->GetTextureCount(aiTextureType_NORMALS)) aimtl->GetTexture(aiTextureType_NORMALS, 0, &normalTexturePath);
                    if (aimtl->GetTextureCount(aiTextureType_METALNESS)) aimtl->GetTexture(aiTextureType_METALNESS, 0, &metalTexturePath);
                    if (aimtl->GetTextureCount(aiTextureType_DIFFUSE_ROUGHNESS)) aimtl->GetTexture(aiTextureType_NORMALS, 0, &roughnessTexturePath);
                    if (aimtl->GetTextureCount(aiTextureType_SPECULAR)) aimtl->GetTexture(aiTextureType_SPECULAR, 0, &specularTexturePath);
                    if (aimtl->GetTextureCount(aiTextureType_SHININESS)) aimtl->GetTexture(aiTextureType_SPECULAR, 0, &glossTexturePath);
                    if (aimtl->GetTextureCount(aiTextureType_DISPLACEMENT)) aimtl->GetTexture(aiTextureType_DISPLACEMENT, 0, &displacementTexturePath);
                    if (aimtl->GetTextureCount(aiTextureType_AMBIENT_OCCLUSION)) aimtl->GetTexture(aiTextureType_AMBIENT_OCCLUSION, 0, &displacementTexturePath);
    
                    aiColor3D diffuseColor(0.f, 0.f, 0.f);
                    aimtl->Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor);
                    
                    aiColor3D emissiveColor(0.f, 0.f, 0.f);
                    aimtl->Get(AI_MATKEY_COLOR_EMISSIVE, emissiveColor);
                }
                mesh->UpdateBounds();
                if (aimesh->HasNormals() and aimesh->GetNumUVChannels() > 0) mesh->UpdateTangents();
            }
        }
        for (int n = 0; n < node->mNumChildren; ++n) AILoadModel(scene, node->mChildren[n], world, model);
        model->UpdateBounds();
        return model;
    }
    
    shared_ptr<Model> ImportModel(shared_ptr<World> world, const WString& path)
    {
        Assimp::Importer importer;
        auto d = CurrentDir();
        
        const aiScene* scene = importer.ReadFile(path.ToUtf8String().c_str(), aiProcess_JoinIdenticalVertices | aiProcess_SortByPType);
    
        if (not scene or scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE or not scene->mRootNode)
        {
            return nullptr;
        }
    
        auto model = AILoadModel(scene, scene->mRootNode, world, NULL);
    
        return model;
    }
    
    int main(int argc, const char* argv[])
    {
        RegisterComponents();
    
        auto cl = ParseCommandLine(argc, argv);
        
        //Load FreeImage plugin (optional)
        auto fiplugin = LoadPlugin("Plugins/FITextureLoader");
    
        //Get the displays
        auto displays = GetDisplays();
    
        //Create a window
        auto window = CreateWindow("Ultra Engine", 0, 0, 1280 * displays[0]->scale, 720 * displays[0]->scale, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR);
    
        //Create a framebuffer
        auto framebuffer = CreateFramebuffer(window);
    
        //Create a world
        auto world = CreateWorld();
    
        auto camera = CreateCamera(world);
        camera->AddComponent<CameraControls>();
        camera->Move(0, 0, -3);
    
        //Load a model
        auto model = ImportModel(world, "test.fbx");//load your own model here
    
        auto light = CreateDirectionalLight(world);
        light->SetRotation(34, 36, 0);
    
        //Main loop
        while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false)
        {
            world->Update();
            world->Render(framebuffer);
        }
        return 0;
    }

     

×
×
  • Create New...