Jump to content

Josh

Staff
  • Posts

    24,627
  • Joined

  • Last visited

Everything posted by Josh

  1. Tags are being saved in the map file, and if I load the prefab like a map (File > Open) the tags load, but when objects are placed in the scene the tags are missing. { "scene": { "entities": [ { "castshadows": true, "collisiontype": 1, "extras": {}, "friction": [ 0.5, 0.8999999761581421 ], "group": "1663675869920", "kids": [ { "castshadows": true, "collisiontype": 1, "extras": {}, "friction": [ 0.5, 0.8999999761581421 ], "matrix": [ "0x42c80000", "0x0", "0x0", "0x0", "0x0", "0xb747ffff", "0x42c7fffe", "0x0", "0x0", "0xc2c7fffe", "0xb747ffff", "0x0", "0x0", "0x0", "0x0", "0x3f800000" ], "model": { "islimb": true }, "name": "Cube", "physicsmode": 0, "pickmode": 1, "quaternion": [ "0x3f3504f4", "0x0", "0x0", "0xbf3504f2" ], "reflection": true, "rotation": [ "0x42b3f5df", "0x80000000", "0x0" ], "scale": [ "0x42c80000", "0x42c7ffff", "0x42c7ffff" ], "tags": [ "test2" ], "uuid": "d0a578c6-4ac8-4de5-83e0-85db59a8a57b" } ], "model": { "islimb": false, "path": "Models/test.mdl" }, "name": "STATIC_MESH", "physicsmode": 0, "pickmode": 1, "reflection": true, "tags": [ "test1" ], "uuid": "bc1b377d-116b-45d8-b6dd-5eb8f8330079" } ] } }
  2. It's working fine for me within the editor, with this test: tagtest.zip Note that if you just click on the object, both the parent and child will be selected, and the tags property field will appear empty because the two selected objects have different values.
  3. Make sure you compare your shader code to my current shader. I had a similar problem with outline colors in the editor, due to some errors in the logic for the loops.
  4. Was this only occurring when the game is launched from the editor? We had a problem where the process was being blocked because its output pipe was not being read by the editor after launching, which would cause the game to freeze on a print command. This was recently fixed. Does this happen when running the game from Visual Studio, or only when launching from in the editor?
  5. Josh

    Quake BSP Loading

    Here is an example that uses another plugin to load PNG images in a glTF file: https://www.ultraengine.com/learn/LoadPlugin?lang=cpp
  6. Josh

    Quake BSP Loading

    Just call LoadPlugin("Plugins/QuakeLoader.dll"), make sure you keep it in memory, and call LoadModel() to load a quake bsp file as a model.
  7. This helps show the problem: model->Turn(0, 0.2, 0);
  8. Josh

    Jungle Scene

    I decided to upload my jungle scene as-is so you can play around with it. We are all learning. island.zip
  9. Josh

    Quake BSP Loading

    Some of the older builds of Ultra included a Quake file format plugin, which should work. You can revert to an older version, get the DLL from the plugins folder, and copy it into the latest version. You can also use the source code here to read Quake BSP files and construct a model: https://github.com/UltraEngine/PluginSDK/tree/master/Plugins/Quake Loader
  10. 0.9.6 Updated the thumbnail utility to support the newer version 301 model format. It's probably a little faster now too.
  11. A small change was needed for LOD distances. The current version is now 301. A Vec3 was added in the node properties. The old per-LOD value will be ignored. If you don't know what the LOD distances should be, just fill it with NAN values. See G3DModelLoader::LoadNode() below. The original version 300 models will continue to load with no changes. #include "UltraEngine.h" using namespace UltraEngine; namespace UltraEngine::Core { G3DModelLoader::G3DModelLoader() { extensions = { L"mdl" }; } 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>(); auto start = stream->GetPosition(); if (stream->ReadString(12) != "Ultra Model") return false; Assert(stream->GetPosition() == start + 12); this->version = stream->ReadInt(); if (version != 300 and version != 301) { Print("Error: MDL 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, lodrange; Vec4 color; Quat rot; String s; if (stream->ReadString(4) != "NODE") { Print("Error: Expected NODE tag"); 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(); color.x = stream->ReadFloat(); color.y = stream->ReadFloat(); color.z = stream->ReadFloat(); color.a = stream->ReadFloat(); if (version > 300) { lodrange.x = stream->ReadFloat(); lodrange.y = stream->ReadFloat(); lodrange.z = stream->ReadFloat(); } model->SetPosition(pos); model->SetRotation(rot); model->SetScale(scale); model->SetColor(color); if (version > 300) { auto range = model->GetLodDistance(); if (not isnan(lodrange.x)) range.x = lodrange.x; if (not isnan(lodrange.y)) range.y = lodrange.y; if (not isnan(lodrange.z)) range.z = lodrange.z; model->SetLodDistance(range.x, range.y, range.z); } int countlods = stream->ReadInt(); for (int level = 0; level < countlods; ++level) { if (not LoadLod(stream, model, level, flags)) return false; } // Skeleton if (stream->ReadString(4) != "SKEL") { Print("Error: Expected SKEL tag"); return false; } int bones = stream->ReadInt(); if (bones) { if (model->GetParent()) { Print("Error: Skeleton can only appear in the model root node"); return false; } if (bones < 0) { Print("Error: Skeleton bones must be more than zero"); return false; } auto skeleton = CreateSkeleton(nullptr); skeleton->root = std::make_shared<Bone>(nullptr, skeleton); skeleton->bones.resize(bones); if (not LoadBone(stream, skeleton, skeleton->root, 0, flags)) return false; skeleton->root->UpdateMatrix(); skeleton->bones[0] = skeleton->root; skeleton->UpdateSkinning(); for (int n = 0; n < skeleton->bones.size(); ++n) { if (skeleton->bones[n] == NULL) continue; auto bone = skeleton->bones[n]; bone->inversebindmatrix = bone->matrix.Inverse(); bone->animbone->inversebindmatrix = bone->inversebindmatrix; bone->Finalize(); } model->SetSkeleton(skeleton); } //Attachment if (stream->ReadString(4) != "ATCH") { Print("Error: Expected ATCH tag"); return false; } int attachmentboneid = stream->ReadInt(); if (attachmentboneid != -1) { stream->Seek(stream->GetPosition() + 64); } // Animations if (stream->ReadString(4) != "ASET") { Print("Error: Expected ASET tag"); return false; } int animations = stream->ReadInt(); if (animations) { if (model->GetParent()) { Print("Error: Animations can only appear in the model root node"); return false; } for (int anim = 0; anim < animations; ++anim) { if (stream->ReadString(4) != "ANIM") { Print("Error: Expected ANIM tag"); return false; } auto seq = std::make_shared<Sequence>(); model->skeleton->root->animations.push_back(seq); model->skeleton->root->animbone->animations.push_back(seq); WString animname = ReadText(stream); float speed = stream->ReadFloat(); int keyframes = stream->ReadInt(); float duration = float(keyframes) / 60.0f * speed; int bones = stream->ReadInt(); for (int b = 0; b < bones; ++b) { if (stream->ReadString(4) != "BONE") { Print("Error: Expected BONE tag"); return false; } int keyflags = stream->ReadInt(); auto bone = model->skeleton->bones[b]; bone->animations.resize(animations); bone->animations[anim] = std::make_shared<Sequence>(); bone->animations[anim]->name = animname; bone->animations[anim]->speed = speed; bone->animations[anim]->keyframes.reserve(keyframes); bone->animations[anim]->duration = duration; KeyFrame key; for (int k = 0; k < keyframes; ++k) { if ((1 & keyflags) != 0) { key.position.x = stream->ReadFloat(); key.position.y = stream->ReadFloat(); key.position.z = stream->ReadFloat(); } if ((2 & keyflags) != 0) { key.rotation.x = stream->ReadFloat(); key.rotation.y = stream->ReadFloat(); key.rotation.z = stream->ReadFloat(); key.rotation.w = stream->ReadFloat(); } if ((4 & keyflags) != 0) { key.scale = stream->ReadFloat(); stream->ReadFloat(); stream->ReadFloat(); } bone->animations[anim]->keyframes.push_back(key); } bone->animbone->animations = bone->animations; } } } // Collider if (stream->ReadString(4) != "PHYS") { auto pos = stream->GetPosition() - 4; Print("Error: Expected PHYS tag at position " + String(pos)); return false; } int colliderdatasize = stream->ReadInt(); auto colliderstartposition = stream->GetPosition(); if (colliderdatasize) { Vec3 position, scale, euler; Quat rotation; std::vector<shared_ptr<Collider> > parts; int partcount = stream->ReadInt(); for (int n = 0; n < partcount; ++n) { if (stream->ReadString(4) != "PART") { auto pos = stream->GetPosition() - 4; Print("Error: Expected PART tag at position " + String(pos)); return false; } shared_ptr<Collider> part; auto tag = stream->ReadString(4); if (tag == "HULL") { float tol = stream->ReadFloat();// tolerance std::vector<Vec3> points; Vec3 p; int count = stream->ReadInt(); for (int n = 0; n < count; ++n) { p.x = stream->ReadFloat(); p.y = stream->ReadFloat(); p.z = stream->ReadFloat(); points.push_back(p); } part = CreateConvexHullCollider(points, 0.0f); if (part) part->tolerance = tol; } else if (tag == "MESH") { int opt = stream->ReadInt();// optimize flag int count = stream->ReadInt(); int vcount; std::vector<Vec3> face; Vec3 p; std::vector<std::vector<Vec3> > meshfaces; for (int n = 0; n < count; ++n) { vcount = stream->ReadInt(); face.clear(); for (int v = 0; v < vcount; ++v) { p.x = stream->ReadFloat(); p.y = stream->ReadFloat(); p.z = stream->ReadFloat(); face.push_back(p); } meshfaces.push_back(face); } part = CreateMeshCollider(meshfaces, false); if (part) part->optimizemesh = opt; } else { position.x = stream->ReadFloat(); position.y = stream->ReadFloat(); position.z = stream->ReadFloat(); rotation.x = stream->ReadFloat(); rotation.y = stream->ReadFloat(); rotation.z = stream->ReadFloat(); rotation.w = stream->ReadFloat(); scale.x = stream->ReadFloat(); scale.y = stream->ReadFloat(); scale.z = stream->ReadFloat(); if (tag == "BOX_") { part = CreateBoxCollider(scale, position, rotation.Euler()); } else if (tag == "CYLI") { part = CreateCylinderCollider(scale.x, scale.y, position, rotation.Euler()); } else if (tag == "CONE") { part = CreateConeCollider(scale.x, scale.y, position, rotation.Euler()); } else if (tag == "CCYL") { part = CreateChamferCylinderCollider(scale.x, scale.y, position, rotation.Euler()); } else if (tag == "CAPS") { part = CreateCapsuleCollider(scale.x, scale.y, position, rotation.Euler()); } else if (tag == "SPHE") { part = CreateSphereCollider(scale.x, position); } else { Print("Error: Unknown collider type \"" + tag + "\""); return false; } } if (part) parts.push_back(part); } if (parts.size()) { if (parts.size() == 1) { model->SetCollider(parts[0]); } else { auto c = CreateCompoundCollider(parts); model->SetCollider(c); } } stream->Seek(colliderstartposition + colliderdatasize); } // Load children if (stream->ReadString(4) != "KIDS") { Print("Error: Expected KIDS tag"); 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; } 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_") { Print("Error: Expected LOD_ tag"); return false; } if (level >= model->lods.size()) model->AddLod(); float loddistance = stream->ReadFloat(); if (version < 301) { if (loddistance > 0.0f and model->lods.size() > 0 and model->lods.size() <= 4) { auto lods = model->GetLodDistance(); int i = int(model->lods.size()) - 2; lods[i] = loddistance; model->SetLodDistance(lods.x, lods.y, lods.z); } } 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") { Print("Error: Expected MESH tag"); 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, flags); if (mtl) mesh->SetMaterial(mtl); } int vertexstride = stream->ReadInt(); if (vertexstride != 88) { Print("Vertext stride must be 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; //SubD normals stream->ReadFloat(); stream->ReadFloat(); stream->ReadFloat(); } Vec4 zero; for (const auto& vertex : mesh->vertices) { if (vertex.boneweights != zero) { mesh->SetSkinned(true); break; } } 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; } //Primitives flags if (stream->ReadString(4) != "PRIM") { Print("Error: Expected PRIM tag"); return false; } int primcount = stream->ReadInt(); if (primcount) { if (primcount != indicecount / type) { Print("Error: Primitives count must be equal to the number of primitives in the mesh, or zero"); return false; } stream->Seek(stream->GetPosition() + primcount); } //Vertex morphs if (stream->ReadString(4) != "MSET") { Print("Error: Expected MORP tag"); return false; } int morphcount = stream->ReadInt(); for (int m = 0; m < morphcount; ++m) { if (stream->ReadString(4) != "MORP") { Print("Error: Expected MORP tag"); 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(); } } // Pick structure cache if (stream->ReadString(4) != "PICK") { Print("Error: Expected PICK tag"); return false; } int pickcachesize = stream->ReadInt(); if (pickcachesize) stream->Seek(stream->GetPosition() + pickcachesize); 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) { if (stream->ReadString(4) != "BONE") { Print("Error: Expected BONE tag"); return false; } bone->m_id = stream->ReadInt(); bone->animbone->id = bone->id; //Print(bone->m_id); if (bone->id >= skeleton->bones.size()) skeleton->bones.resize(bone->id + 1); skeleton->bones[bone->id] = bone; if (bone->id >= skeleton->animskeleton->bones.size()) skeleton->animskeleton->bones.resize(bone->id + 1); skeleton->animskeleton->bones[bone->id] = bone->animbone; 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(); stream->ReadFloat();// scale y and z not supported /*int count = stream->ReadInt(); if (bone->GetParent() and count != animcount) { Print("Error: Bone animation count must match that of the root node"); return false; } int i = stream->GetPosition(); for (int anim = 0; anim < count; ++anim) { auto seq = std::make_shared<Sequence>(); bone->animations.push_back(seq); bone->animbone->animations.push_back(seq); if (stream->ReadString(4) != "ANIM") { Print("Error: Expected ANIM tag"); return false; } bone->animations[anim]->name = ReadText(stream); bone->animations[anim]->speed = stream->ReadFloat(); int keyflags = stream->ReadInt(); int keyframes = stream->ReadInt(); if (not keyflags or not keyframes) continue; bone->animations[anim]->duration = float(keyframes) / 60.0f * bone->animations[anim]->speed; KeyFrame key; bone->animations[anim]->keyframes.reserve(keyframes); for (int k = 0; k < keyframes; ++k) { if ((1 & keyflags) != 0) { key.position.x = stream->ReadFloat(); key.position.y = stream->ReadFloat(); key.position.z = stream->ReadFloat(); } if ((2 & keyflags) != 0) { key.rotation.x = stream->ReadFloat(); key.rotation.y = stream->ReadFloat(); key.rotation.z = stream->ReadFloat(); key.rotation.w = stream->ReadFloat(); } if ((4 & keyflags) != 0) { key.scale = stream->ReadFloat(); stream->ReadFloat(); stream->ReadFloat(); } bone->animations[anim]->keyframes.push_back(key); } } bone->animbone->animations = bone->animations; */ if (stream->ReadString(4) != "KIDS") { Print("Error: Expected KIDS tag"); return false; } int childcount = stream->ReadInt(); for (int n = 0; n < childcount; ++n) { auto child = std::make_shared<Bone>(bone, skeleton); bone->kids.push_back(child); if (not LoadBone(stream, skeleton, child, bone->animations.size(), flags)) { return false; } } return true; } }
  12. I just tried converting the same model and got three materials. Maybe this is already fixed?
  13. It actually looks fine on my AMD 6600, but you can try this to disable linear filtering: auto sz = framebuffer->GetSize(); auto texbuffer = CreateTextureBuffer(sz.x, sz.y); auto pixels = CreatePixmap(sz.x, sz.y, TEXTURE_RGBA); auto tex = CreateTexture(TEXTURE_2D, sz.x, sz.y, pixels->format, { pixels }, 1, TEXTURE_DEFAULT, TEXTUREFILTER_NEAREST); texbuffer->SetColorAttachment(tex); cam2->SetRenderTarget(texbuffer); Now if I enable MSAA I do see an outline, and that is what I would expect, because the pixels are combined from multiple samples: You might be able to eliminate that by ignoring colors that have a red value over 0.99, or something like that. I'm not sure if there is really any way around that though.
  14. I think this is fixed on current build of beta branch, please sync to get the shader update.
  15. 0.9.6 Shader update improves the default offsets for shadow maps. I think it's good, let me know what you think. Directional lights now have a longer view distance.
  16. Your final rotation must be calculated arbitrarily then. You want to turn the entity 90 degrees over the edge. You have to rotate around the axis of that edge.
  17. 0.9.6 Full build with all recent bug fixes.
  18. Thanks for reporting this. The issue will be resolved in the next build that goes up. This was caused by an "improvement" I tried to make, so it was easier to jump while running up stairs, but the problem here clearly show the "fix" was not worth it.
  19. Josh

    PBR Bug

    Okay, I changed the pixel format these images get stored in, and it seems to work correctly now. Thank you for reporting this issue.
  20. Josh

    PBR Bug

    It looks like the normal map is causing this. If you remove the normal map the model looks like it should. The texture looks okay, but I notice it is in RGBA16 format. Maybe it it being loaded into the wrong pixel format...
  21. Josh

    PBR Bug

    Here is what I see, side by side: Inspecting the model now...
  22. I am going to mark this as solved because I think it is not occurring in the current build. I recently made some changes with colliders stored in the map file, and that probably eliminated this problem. Please let me know if anything needs further investigation.
×
×
  • Create New...