Jump to content

Josh

Staff
  • Posts

    23,099
  • Joined

  • Last visited

Everything posted by Josh

  1. Wait, if it is using a fragment shader in a depth pass, it must be using alpha discard...
  2. Bug fixes. Added Camera::SetUniform, for post-effect parameters.
  3. I am not able to produce this bug in a new project, using the player robot model, on my Nvidia card.
  4. I added some code that automatically decompresses the pixmap to another format, resizes it, and compresses it again.
  5. This will be fixed in the next build. The command is now called Camera::SetUniform, and it accepts the following values: int, ivec2, ivec3, ivec4, float, vec2, vec2, vec4, texture Textures will be passed as a uvec2 bindless handle. The texture will be kept in memory while it is used by a camera.
  6. Map loading from asset browser now supported. Added clear button in world settings environment map fields (icon has the wrong path, will be fixed in next update)
  7. https://registry.khronos.org/OpenGL/specs/gl/glspec46.core.pdf
  8. This extension automatically converts images to DDS format, taking the best guess at what type of compression to use. Build 598 or greater is required. DDSConverter.lua
  9. Yes. Some programs do saving a little differently. They'll do things like write the file somewhere else, and then copy it over once writing is finished, to prevent a file from getting corrupted. Some of these actions can trigger the file system watcher multiple times, and I do my best to prevent it.
  10. What program are you editing it in, VSCode?
  11. The camera cannot render on top of the background, because it has to render to an MSAA texture. You don't want to use MSAA on GUI cameras for a few reasons. Text is already antialiased. Edges will become blurry. There may be other situations where you do want to use multiple cameras with MSAA, but this is probably not one of them.
  12. I am attaching the last version of my assimp model loader class so you can experiment with it if you want to. AIModelLoader.cppAIModelLoader.h
  13. The PBR shader family gets loaded by default when a new material is created, and when the world is initialize (because it is used as the default shader family for the default material, when no material exists).
  14. Update is available now which I believe fixes this. I tested on the standalone.
  15. Okay, building from the engine source works, but from the lib does not. It probably is interfering with assimp...let me do another build without it. (I only updated the editor last time.)
  16. What example is not working in this example? When I run it, it loads the folder and finds a file called "è¿a¿½½¿µá.txt" in the RU folder.
  17. I guess just go without whatever format is in use. The fact that nobody understand or even knows what BC7 is probably means it isn't that important in the grand scheme of things. If someone wants BC7, we can do an auto-converter in the editor from an image file. It would be better to have something simple and reliable that works now than to have something that produces perfect results but breaks easily.
  18. Yeah, but if we got a static exporter set up, that would cover 95% of use cases.
  19. I thought you had a working exporter your wrote yourself?
  20. I have it working now: https://github.com/UltraEngine/G3D/blob/main/README.md
  21. How hard would it be to export to this format from Blender?
  22. 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; } }
  23. Seems to cause problems of its own. According to this, the Godot developers gave up and implemented their own FBX converter: https://github.com/assimp/assimp/issues/2498
×
×
  • Create New...