External lightmap workflow working

This commit is contained in:
lawnjelly 2019-12-03 15:50:30 +00:00 committed by GitHub
parent b6cbce0621
commit 2a7933879c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 786 additions and 56 deletions

447
ldae_exporter.cpp Normal file
View File

@ -0,0 +1,447 @@
#include "ldae_exporter.h"
#include "scene/main/node.h"
#include "scene/3d/light.h"
#include "ldebug.h"
#include "lportal.h"
#define FSL(a) m_File.store_line(a)
#define FSS(a) m_File.store_string(a)
#define FSL_T(a, b) {for (int n=0; n<a; n++) {FSS("\t");}\
FSL(b);}
#define FSS_T(a, b) {for (int n=0; n<a; n++) {FSS("\t");}\
FSS(b);}
LDAEExporter::LDAEExporter()
{
m_pMergedMI = 0;
m_bAddRootNode = true;
m_Stage = ST_DATA;
m_bRemovePortals = true;
}
bool LDAEExporter::SaveScene(Node * pNode, String szFilename, bool bRemovePortals)
{
m_bAddRootNode = true;
m_bRemovePortals = bRemovePortals;
// open the output file
Error err = m_File.open(szFilename, _File::WRITE);
if (err != OK)
return false;
Save_Preamble();
m_Stage = ST_DATA;
// initially find all the meshes, lights etc
SaveScene_Recursive(pNode, 0);
// lights
FSL("\t<library_lights>");
for (int n=0; n<m_Lights.size(); n++)
SaveData_Light(*m_Lights[n]);
FSL("\t</library_lights>");
// mesh instances
FSL("\t<library_geometries>");
// merged mesh?
if (m_pMergedMI)
SaveData_MeshInstance(*m_pMergedMI);
for (int n=0; n<m_MeshInstances.size(); n++)
SaveData_MeshInstance(*m_MeshInstances[n]);
FSL("\t</library_geometries>");
m_Stage = ST_SCENE_GRAPH;
Save_Mid();
// now save the scene graph
SaveScene_Recursive(pNode, 0);
Save_Final();
m_File.close();
return true;
}
bool LDAEExporter::Save_Preamble()
{
FSL("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
FSL("<COLLADA xmlns=\"http://www.collada.org/2005/11/COLLADASchema\" version=\"1.4.1\">");
return true;
}
bool LDAEExporter::Save_Mid()
{
FSL("\t<library_visual_scenes>");
FSL("\t\t<visual_scene id=\"Scene\" name=\"Scene\">");
if (m_bAddRootNode)
{
FSL_T(3, "<node id=\"l_root\" name=\"l_root\" type=\"NODE\">");
// rotate x by 90 degrees
FSL_T(3, "<matrix sid=\"transform\">1 0 0 0 0 0 -1 0 0 1 0 0 0 0 0 1</matrix>");
}
if (m_pMergedMI)
{
String sz = SaveScene_MeshInstance(*m_pMergedMI, 3);
FSL(sz); // merged node close </node>
FSL_T(3, "<node id=\"l_level\" name=\"l_level\" type=\"NODE\">");
}
return true;
}
void LDAEExporter::TransformToMatrix(const Transform &tr, float * m)
{
const Basis &b = tr.basis;
m[0] = b.elements[0][0];
m[1] = b.elements[0][1];
m[2] = b.elements[0][2];
m[3] = tr.origin.x;
m[4] = b.elements[1][0];
m[5] = b.elements[1][1];
m[6] = b.elements[1][2];
m[7] = tr.origin.y;
m[8] = b.elements[2][0];
m[9] = b.elements[2][1];
m[10] = b.elements[2][2];
m[11] = tr.origin.z;
m[12] = 0.0f;
m[13] = 0.0f;
m[14] = 0.0f;
m[15] = 1.0f;
}
String LDAEExporter::SaveScene_Light(const Light &l, int depth)
{
int tab_depth = 3 + depth;
String name = l.get_name();
String long_name = name + "-light";
FSL_T(tab_depth, "<node id=\"" + name + "\" name=\"" + name + "\" type=\"NODE\">");
WriteMatrix(l.get_transform(), tab_depth+1);
FSL_T(tab_depth+1, "<instance_light url=\"#" + long_name + "\" />");
String sz = "";
for (int n=0; n<tab_depth; n++)
sz += "\t";
sz += "</node>";
return sz;
}
void LDAEExporter::WriteMatrix(const Transform &tr, int tab_depth)
{
float mat[16];
TransformToMatrix(tr, mat);
FSS_T(tab_depth, "<matrix sid=\"transform\">");
for (int n=0; n<16; n++)
{
FSS(ftos(mat[n]) + " ");
}
FSS("</matrix>\n");
}
String LDAEExporter::SaveScene_MeshInstance(const MeshInstance &mi, int depth)
{
int tab_depth = 3 + depth;
String name = mi.get_name();
String long_name = name + "-mesh";
FSL_T(tab_depth, "<node id=\"" + name + "\" name=\"" + name + "\" type=\"NODE\">");
WriteMatrix(mi.get_transform(), tab_depth+1);
FSL_T(tab_depth+1, "<instance_geometry url=\"#" + long_name + "\" name=\"" + name + "\">");
FSL_T(tab_depth+1, "</instance_geometry>");
String sz = "";
for (int n=0; n<tab_depth; n++)
sz += "\t";
sz += "</node>";
// FSL_T(tab_depth, "</node>");
return sz;
}
String LDAEExporter::SaveScene_Spatial(const Spatial &sp, int depth)
{
int tab_depth = 3 + depth;
String name = sp.get_name();
FSL_T(tab_depth, "<node id=\"" + name + "\" name=\"" + name + "\" type=\"NODE\">");
WriteMatrix(sp.get_transform(), tab_depth+1);
String sz = "";
for (int n=0; n<tab_depth; n++)
sz += "\t";
sz += "</node>";
return sz;
}
bool LDAEExporter::Save_Final()
{
if (m_pMergedMI)
{
FSL_T(3, "</node>"); // level
}
if (m_bAddRootNode)
{
FSL_T(3, "</node>"); // root
}
FSL("\t\t</visual_scene>");
FSL("\t</library_visual_scenes>");
FSL("\t<scene>");
FSL("\t\t<instance_visual_scene url=\"#Scene\"/>");
FSL("\t</scene>");
FSL("</COLLADA>");
return true;
}
bool LDAEExporter::IsPortal(const MeshInstance &mi)
{
if (LPortal::NameStartsWith(&mi, "portal_"))
return true;
return false;
}
bool LDAEExporter::SaveScene_Recursive(Node * pNode, int depth)
{
String szClose = "";
// mesh instance?
MeshInstance * pMI = Object::cast_to<MeshInstance>(pNode);
if (pMI)
{
if (m_bRemovePortals && (IsPortal(*pMI)))
{
// portal .. not exporting
}
else
{
if (m_Stage == ST_DATA)
m_MeshInstances.push_back(pMI);
else
szClose = SaveScene_MeshInstance(*pMI, depth);
}
}
// light?
Light * pLight = Object::cast_to<Light>(pNode);
if (pLight)
{
if (m_Stage == ST_DATA)
m_Lights.push_back(pLight);
else
szClose = SaveScene_Light(*pLight, depth);
}
// spatial? (and only a spatial)
if (pNode->get_class() == "Spatial")
{
if (m_Stage == ST_SCENE_GRAPH)
{
szClose = SaveScene_Spatial(*Object::cast_to<Spatial>(pNode), depth);
}
}
// go through the children
for (int n=0; n<pNode->get_child_count(); n++)
{
SaveScene_Recursive(pNode->get_child(n), depth+1);
}
// a closing xml expression
if (szClose != "")
{
FSL(szClose);
}
return true;
}
bool LDAEExporter::SaveData_Light(const Light &light)
{
String name = light.get_name();
String long_name = name + "-light";
int t = 1;
String szLightType = "point";
//if (light.is_class("OmniLight")
if (light.is_class("SpotLight"))
szLightType = "spot";
if (light.is_class("DirectionalLight"))
szLightType = "directional";
FSL_T(t, "<light id=\"" + long_name + "\" name=\"" + name + "\">");
FSL_T(t+1, "<technique_common>");
FSL_T(t+2, "<" + szLightType + ">");
FSL_T(t+2, "</" + szLightType + ">");
FSL_T(t+1, "</technique_common>");
FSL_T(t, "</light>");
return true;
}
//static bool g_SingleDAETest = false;
bool LDAEExporter::SaveData_MeshInstance(const MeshInstance &mi)
{
// if (g_SingleDAETest)
// return true;
// g_SingleDAETest = true;
Ref<Mesh> rmesh = mi.get_mesh();
Array arrays = rmesh->surface_get_arrays(0);
PoolVector<Vector3> verts = arrays[VS::ARRAY_VERTEX];
PoolVector<Vector3> norms = arrays[VS::ARRAY_NORMAL];
PoolVector<Vector2> uv1s = arrays[VS::ARRAY_TEX_UV];
PoolVector<int> inds = arrays[VS::ARRAY_INDEX];
int nVerts = verts.size();
int nInds = inds.size();
int nFaces = nInds / 3;
String name = mi.get_name();
String long_name = name + "-mesh";
FSL("\t<geometry id=\"" + long_name + "\" name=\"" + name + "\">");
FSL("\t\t<mesh>");
// positions
FSL("\t\t\t<source id=\"" + long_name + "-positions\">");
FSS("\t\t\t\t<float_array id=\"" + long_name + "-positions-array\" count=\"");
FSS(itos(nVerts*3) + "\">");
for (int n=0; n<nVerts; n++)
{
Vector3 pos = verts[n];
FSS(ftos(pos.x) + " " + ftos(pos.y) + " " + ftos(pos.z) + " ");
}
FSS("</float_array>\n");
FSL("\t\t\t\t<technique_common>");
FSL("\t\t\t\t\t<accessor source=\"#" + long_name + "-positions-array\" count=\""
+ itos(nVerts) + "\" stride=\"3\">");
FSL("\t\t\t\t\t\t<param name=\"X\" type=\"float\" />");
FSL("\t\t\t\t\t\t<param name=\"Y\" type=\"float\" />");
FSL("\t\t\t\t\t\t<param name=\"Z\" type=\"float\" />");
FSL("\t\t\t\t\t</accessor>");
FSL("\t\t\t\t</technique_common>");
FSL("\t\t\t</source>");
// normals
if (norms.size())
{
FSL("\t\t\t<source id=\"" + long_name + "-normals\">");
FSS("\t\t\t\t<float_array id=\"" + long_name + "-normals-array\" count=\"");
FSS(itos(nVerts*3) + "\">");
for (int n=0; n<nVerts; n++)
{
Vector3 norm = norms[n];
FSS(ftos(norm.x) + " " + ftos(norm.y) + " " + ftos(norm.z) + " ");
}
FSS("</float_array>\n");
FSL("\t\t\t\t<technique_common>");
FSL("\t\t\t\t\t<accessor source=\"#" + long_name + "-normals-array\" count=\""
+ itos(nVerts) + "\" stride=\"3\">");
FSL("\t\t\t\t\t\t<param name=\"X\" type=\"float\" />");
FSL("\t\t\t\t\t\t<param name=\"Y\" type=\"float\" />");
FSL("\t\t\t\t\t\t<param name=\"Z\" type=\"float\" />");
FSL("\t\t\t\t\t</accessor>");
FSL("\t\t\t\t</technique_common>");
FSL("\t\t\t</source>");
}
FSL("\t\t\t<vertices id=\"" + long_name + "-vertices\">");
FSL("\t\t\t\t<input semantic=\"POSITION\" source=\"#" + long_name + "-positions\" />");
FSL("\t\t\t</vertices>");
FSL("\t\t\t<triangles count=\"" + itos(nFaces) + "\">");
FSL("\t\t\t\t<input semantic=\"VERTEX\" source=\"#" + long_name + "-vertices\" offset=\"0\"/>");
if (norms.size())
{
FSL("\t\t\t\t<input semantic=\"NORMAL\" source=\"#" + long_name + "-normals\" offset=\"1\"/>");
}
FSS("\t\t\t\t<p>");
// DAE has face winding reversed compared to godot
for (int f=0; f<nFaces; f++)
// for (int n=0; n<nInds; n++)
{
int base = f * 3;
// one index for position, one for normal
FSS(itos(inds[base+2]) + " ");
FSS(itos(inds[base+2]) + " ");
FSS(itos(inds[base+1]) + " ");
FSS(itos(inds[base+1]) + " ");
FSS(itos(inds[base+0]) + " ");
FSS(itos(inds[base+0]) + " ");
}
FSS("</p>\n");
FSL("\t\t\t</triangles>");
FSL("\t\t</mesh>");
FSL("\t</geometry>");
return true;
}
#undef FSL
#undef FSS

59
ldae_exporter.h Normal file
View File

@ -0,0 +1,59 @@
#pragma once
#include "core/bind/core_bind.h"
#include "scene/3d/mesh_instance.h"
class Light;
class LDAEExporter
{
enum eStage
{
ST_DATA,
ST_SCENE_GRAPH,
};
public:
LDAEExporter();
bool SaveScene(Node * pNode, String szFilename, bool bRemovePortals = false);
void SetMergedMeshInstance(MeshInstance * pMI) {m_pMergedMI = pMI;}
private:
bool SaveScene_Recursive(Node * pNode, int depth);
String SaveScene_MeshInstance(const MeshInstance &mi, int depth);
String SaveScene_Light(const Light &l, int depth);
String SaveScene_Spatial(const Spatial &sp, int depth);
bool SaveData_Light(const Light &light);
bool SaveData_MeshInstance(const MeshInstance &mi);
bool Save_Preamble();
bool Save_Mid();
bool Save_Final();
void WriteMatrix(const Transform &tr, int tab_depth);
void TransformToMatrix(const Transform &tr, float * m);
bool IsPortal(const MeshInstance &mi);
Vector<const MeshInstance *> m_MeshInstances;
Vector<const Light *> m_Lights;
_File m_File;
eStage m_Stage;
// export the DAE with everything under a root node .. this is sometimes more convenient
bool m_bAddRootNode;
// remove portals
bool m_bRemovePortals;
// if we are exporting the level with a merged mesh too
MeshInstance * m_pMergedMI;
};

View File

@ -7,6 +7,11 @@
// for ::free // for ::free
#include <stdlib.h> #include <stdlib.h>
LHelper::LHelper()
{
SetUnMergeParams(0.1f, 0.95f);
}
String LHelper::LFace::ToString() const String LHelper::LFace::ToString() const
{ {
@ -18,7 +23,7 @@ String LHelper::LFace::ToString() const
sz += ", "; sz += ", ";
} }
sz += " norm : "; sz += "norm : ";
for (int c=0; c<3; c++) for (int c=0; c<3; c++)
{ {
sz += String(Variant(m_Norm[c])); sz += String(Variant(m_Norm[c]));
@ -97,8 +102,15 @@ bool LHelper::FillMergedFromMesh(LMerged &merged, const MeshInstance &mesh)
if (merged.m_UV2s.size() == 0) if (merged.m_UV2s.size() == 0)
{ {
LWARN(5, "Merged mesh has no secondary UVs"); LPRINT(5, "Merged mesh has no secondary UVs, using primary UVs");
return false;
merged.m_UV2s = arrays[VS::ARRAY_TEX_UV];
if (merged.m_UV2s.size() == 0)
{
LWARN(5, "Merged mesh has no UVs");
return false;
}
} }
int miCount = 0; int miCount = 0;
@ -120,6 +132,13 @@ bool LHelper::FillMergedFromMesh(LMerged &merged, const MeshInstance &mesh)
} }
void LHelper::SetUnMergeParams(float thresh_dist, float thresh_dot)
{
m_MergeParams.m_fThresholdDist = thresh_dist;
m_MergeParams.m_fThresholdDist_Squared = thresh_dist * thresh_dist;
m_MergeParams.m_fThresholdDot = thresh_dot;
}
// take the UV2 coords from the merged mesh and attach these to the SOB meshes // take the UV2 coords from the merged mesh and attach these to the SOB meshes
bool LHelper::UnMergeSOBs(LRoomManager &manager, MeshInstance * pMerged) bool LHelper::UnMergeSOBs(LRoomManager &manager, MeshInstance * pMerged)
{ {
@ -134,8 +153,13 @@ bool LHelper::UnMergeSOBs(LRoomManager &manager, MeshInstance * pMerged)
return false; return false;
// go through each sob mesh // go through each sob mesh
// for (int n=1; n<2; n++)
for (int n=0; n<manager.m_SOBs.size(); n++) for (int n=0; n<manager.m_SOBs.size(); n++)
{ {
#ifdef LDEBUG_UNMERGE
LPRINT(5, "Unmerge SOB " + itos(n));
#endif
LSob &sob = manager.m_SOBs[n]; LSob &sob = manager.m_SOBs[n];
GeometryInstance * pGI = sob.GetGI(); GeometryInstance * pGI = sob.GetGI();
if (!pGI) if (!pGI)
@ -182,71 +206,120 @@ unsigned int LHelper::FindMatchingVertex(const PoolVector<Vector2> &uvs, const V
return -1; return -1;
} }
bool LHelper::DoFaceVertsApproxMatch(const LFace& sob_f, const LFace &m_face, int c0, int c1) const bool LHelper::DoFaceVertsApproxMatch(const LFace& sob_f, const LFace &m_face, int c0, int c1, bool bDebug) const
{ {
return DoPosNormsApproxMatch(sob_f.m_Pos[c0], sob_f.m_Norm[c0], m_face.m_Pos[c1], m_face.m_Norm[c1]); return DoPosNormsApproxMatch(sob_f.m_Pos[c0], sob_f.m_Norm[c0], m_face.m_Pos[c1], m_face.m_Norm[c1], bDebug);
} }
bool LHelper::DoPosNormsApproxMatch(const Vector3 &a_pos, const Vector3 &a_norm, const Vector3 &b_pos, const Vector3 &b_norm) const bool LHelper::DoPosNormsApproxMatch(const Vector3 &a_pos, const Vector3 &a_norm, const Vector3 &b_pos, const Vector3 &b_norm, bool bDebug) const
{ {
bDebug = false;
float thresh_diff = m_MergeParams.m_fThresholdDist;
float thresh_diff_squared = m_MergeParams.m_fThresholdDist_Squared;
float x_diff = fabs (b_pos.x - a_pos.x); float x_diff = fabs (b_pos.x - a_pos.x);
if (x_diff > 0.2f) if (x_diff > thresh_diff)
{
#ifdef LDEBUG_UNMERGE
if (bDebug)
LPRINT(5, "\t\t\t\tRejecting x_diff " + ftos(x_diff));
#endif
return false; return false;
}
float z_diff = fabs (b_pos.z - a_pos.z);
if (z_diff > thresh_diff)
{
#ifdef LDEBUG_UNMERGE
if (bDebug)
LPRINT(5, "\t\t\t\tRejecting z_diff " + ftos(z_diff));
#endif
return false;
}
float y_diff = fabs (b_pos.y - a_pos.y);
if (y_diff > thresh_diff)
{
#ifdef LDEBUG_UNMERGE
if (bDebug)
LPRINT(5, "\t\t\t\tRejecting y_diff " + ftos(y_diff));
#endif
return false;
}
Vector3 pos_diff = b_pos - a_pos; Vector3 pos_diff = b_pos - a_pos;
if (pos_diff.length_squared() > 0.1f) if (pos_diff.length_squared() > thresh_diff_squared) // 0.1
{
#ifdef LDEBUG_UNMERGE
if (bDebug)
LPRINT(5, "\t\t\t\tRejecting length squared " + ftos(pos_diff.length_squared()));
#endif
return false; return false;
}
// make sure both are normalized // make sure both are normalized
Vector3 na = a_norm;//.normalized(); Vector3 na = a_norm;//.normalized();
Vector3 nb = b_norm;//.normalized(); Vector3 nb = b_norm;//.normalized();
float norm_dot = na.dot(nb); float norm_dot = na.dot(nb);
if (norm_dot < 0.95f) if (norm_dot < m_MergeParams.m_fThresholdDot)
{
#ifdef LDEBUG_UNMERGE
if (bDebug)
LPRINT(5, "\t\t\t\tRejecting normal " + ftos(norm_dot) + " na : " + String(na) + ", nb : " + String(nb));
#endif
return false; return false;
}
return true; return true;
} }
int LHelper::DoFacesMatch_Offset(const LFace& sob_f, const LFace &m_face, int offset) const
{
#ifdef LDEBUG_UNMERGE
// debug
String sz = "\t\tPOSS match sob : ";
sz += sob_f.ToString();
sz += "\n\t\t\tmerged : ";
sz += m_face.ToString();
LPRINT(2, sz);
#endif
// does 2nd and third match?
int offset1 = (offset + 1) % 3;
if (!DoFaceVertsApproxMatch(sob_f, m_face, 1, offset1, true))
return -1;
int offset2 = (offset + 2) % 3;
if (!DoFaceVertsApproxMatch(sob_f, m_face, 2, offset2, true))
return -1;
return offset;
}
// -1 for no match, or 0 for 0 offset match, 1 for +1, 2 for +2 offset match... // -1 for no match, or 0 for 0 offset match, 1 for +1, 2 for +2 offset match...
int LHelper::DoFacesMatch(const LFace& sob_f, const LFace &m_face) const int LHelper::DoFacesMatch(const LFace& sob_f, const LFace &m_face) const
{ {
// match one // match one
int offset = 0; for (int offset = 0; offset < 3; offset++)
bool bMatch = false;
for (offset = 0; offset < 3; offset++)
{ {
if (DoFaceVertsApproxMatch(sob_f, m_face, 0, offset)) if (DoFaceVertsApproxMatch(sob_f, m_face, 0, offset, false))
{ {
bMatch = true; int res = DoFacesMatch_Offset(sob_f, m_face, offset);
break;
if (res != -1)
return res;
} }
} }
// none found that match, most common scenario return -1; // no match
if (!bMatch)
return -1;
// debug
// String sz = "\t\tposs match sob : ";
// sz += sob_f.ToString();
// sz += " merged : ";
// sz += m_face.ToString();
// LPRINT(2, sz);
// does 2nd and third match?
int offset1 = (offset + 1) % 3;
if (!DoFaceVertsApproxMatch(sob_f, m_face, 1, offset1))
return -1;
int offset2 = (offset + 2) % 3;
if (!DoFaceVertsApproxMatch(sob_f, m_face, 2, offset2))
return -1;
return offset;
} }
@ -318,6 +391,10 @@ bool LHelper::UnMerge_SOB(MeshInstance &mi, LMerged &merged)
lf.m_index[c] = ind; lf.m_index[c] = ind;
} }
#ifdef LDEBUG_UNMERGE
LPRINT(5, "lface : " + lf.ToString());
#endif
// find matching face // find matching face
// int miCount = 0; // int miCount = 0;
bool bMatchFound = false; bool bMatchFound = false;
@ -533,7 +610,7 @@ finish:
} }
bool LHelper::MergeSOBs(LRoomManager &manager, MeshInstance * pMerged) bool LHelper::MergeSOBs(LRoomManager &manager, MeshInstance * pMerged, bool bLightmapUnwrap)
{ {
PoolVector<Vector3> verts; PoolVector<Vector3> verts;
PoolVector<Vector3> normals; PoolVector<Vector3> normals;
@ -554,12 +631,12 @@ bool LHelper::MergeSOBs(LRoomManager &manager, MeshInstance * pMerged)
continue; continue;
// to get the transform, the node has to be in the tree, so temporarily show if hidden // to get the transform, the node has to be in the tree, so temporarily show if hidden
bool bShowing = sob.m_bShow; //bool bShowing = sob.m_bShow;
sob.Show(true); //sob.Show(true);
Merge_MI(*pMI, verts, normals, inds); Merge_MI(*pMI, verts, normals, inds);
sob.Show(bShowing); //sob.Show(bShowing);
} }
@ -608,7 +685,8 @@ bool LHelper::MergeSOBs(LRoomManager &manager, MeshInstance * pMerged)
am->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr, Array(), Mesh::ARRAY_COMPRESS_DEFAULT); am->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr, Array(), Mesh::ARRAY_COMPRESS_DEFAULT);
LightmapUnwrap(am, pMerged->get_global_transform()); if (bLightmapUnwrap)
LightmapUnwrap(am, pMerged->get_global_transform());
// duplicate the UV2 to uv1 just in case they are needed // duplicate the UV2 to uv1 just in case they are needed
arr[Mesh::ARRAY_TEX_UV] = arr[Mesh::ARRAY_TEX_UV2]; arr[Mesh::ARRAY_TEX_UV] = arr[Mesh::ARRAY_TEX_UV2];

View File

@ -5,6 +5,8 @@
class LHelper class LHelper
{ {
public: public:
LHelper();
struct LFace struct LFace
{ {
Vector3 m_Pos[3]; Vector3 m_Pos[3];
@ -43,11 +45,13 @@ public:
MeshInstance * CreateLightmapProxy(LRoomManager &manager); MeshInstance * CreateLightmapProxy(LRoomManager &manager);
// for lightmapping // for lightmapping
bool MergeSOBs(LRoomManager &manager, MeshInstance * pMerged); bool MergeSOBs(LRoomManager &manager, MeshInstance * pMerged, bool bLightmapUnwrap = true);
// take the UV2 coords from the merged mesh and attach these to the SOB meshes // take the UV2 coords from the merged mesh and attach these to the SOB meshes
bool UnMergeSOBs(LRoomManager &manager, MeshInstance * pMerged); bool UnMergeSOBs(LRoomManager &manager, MeshInstance * pMerged);
void SetUnMergeParams(float thresh_dist, float thresh_dot);
// bool UnMergeSOBs(LRoomManager &manager, const PoolVector<Vector2> &uv2s); // bool UnMergeSOBs(LRoomManager &manager, const PoolVector<Vector2> &uv2s);
// main function for getting merged uv2 back to sobs // main function for getting merged uv2 back to sobs
@ -71,8 +75,10 @@ private:
void Transform_Norms(const PoolVector<Vector3> &normsLocal, PoolVector<Vector3> &normsWorld, const Transform &tr) const; void Transform_Norms(const PoolVector<Vector3> &normsLocal, PoolVector<Vector3> &normsWorld, const Transform &tr) const;
int DoFacesMatch(const LFace& sob_f, const LFace &m_face) const; int DoFacesMatch(const LFace& sob_f, const LFace &m_face) const;
bool DoFaceVertsApproxMatch(const LFace& sob_f, const LFace &m_face, int c0, int c1) const; int DoFacesMatch_Offset(const LFace& sob_f, const LFace &m_face, int offset) const;
bool DoPosNormsApproxMatch(const Vector3 &a_pos, const Vector3 &a_norm, const Vector3 &b_pos, const Vector3 &b_norm) const;
bool DoFaceVertsApproxMatch(const LFace& sob_f, const LFace &m_face, int c0, int c1, bool bDebug) const;
bool DoPosNormsApproxMatch(const Vector3 &a_pos, const Vector3 &a_norm, const Vector3 &b_pos, const Vector3 &b_norm, bool bDebug) const;
int FindOrAddVert(LVector<LVert> &uni_verts, const LVert &vert) const; int FindOrAddVert(LVector<LVert> &uni_verts, const LVert &vert) const;
@ -80,4 +86,15 @@ private:
bool FillMergedFromMesh(LMerged &merged, const MeshInstance &mesh); bool FillMergedFromMesh(LMerged &merged, const MeshInstance &mesh);
// bool xatlas_mesh_lightmap_unwrap(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y); // bool xatlas_mesh_lightmap_unwrap(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y);
// internal data
// merging params
struct LMergeParams
{
float m_fThresholdDist;
float m_fThresholdDist_Squared;
float m_fThresholdDot;
} m_MergeParams;
}; };

View File

@ -26,7 +26,7 @@
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
bool LPortal::NameStartsWith(Node * pNode, String szSearch) bool LPortal::NameStartsWith(const Node * pNode, String szSearch)
{ {
int sl = szSearch.length(); int sl = szSearch.length();

View File

@ -82,7 +82,7 @@ public:
void ReverseWindingOrder(); void ReverseWindingOrder();
// useful funcs // useful funcs
static bool NameStartsWith(Node * pNode, String szSearch); static bool NameStartsWith(const Node * pNode, String szSearch);
static String FindNameAfter(Node * pNode, String szStart); static String FindNameAfter(Node * pNode, String szStart);
private: private:

View File

@ -5,6 +5,8 @@
#define LDEBUG_LIGHTS #define LDEBUG_LIGHTS
#define LDEBUG_LIGHT_AFFECTED_ROOMS #define LDEBUG_LIGHT_AFFECTED_ROOMS
//#define LDEBUG_UNMERGE
// single compilation unit // single compilation unit
#include "register_types.cpp" #include "register_types.cpp"
#include "ldebug.cpp" #include "ldebug.cpp"
@ -21,4 +23,5 @@
#include "ltrace.cpp" #include "ltrace.cpp"
#include "lmain_camera.cpp" #include "lmain_camera.cpp"
#include "larea.cpp" #include "larea.cpp"
#include "ldae_exporter.cpp"

View File

@ -30,6 +30,7 @@
#include "lroom.h" #include "lroom.h"
#include "lhelper.h" #include "lhelper.h"
#include "lscene_saver.h" #include "lscene_saver.h"
#include "ldae_exporter.h"
#define LROOMLIST m_pRoomList #define LROOMLIST m_pRoomList
#define CHECK_ROOM_LIST if (!CheckRoomList())\ #define CHECK_ROOM_LIST if (!CheckRoomList())\
@ -68,6 +69,15 @@ LRoomManager::LRoomManager()
m_pRoomList = 0; m_pRoomList = 0;
// loses detail at approx .. 0.00001
// dot 0.9999
// however, this will depend on the map so we will use some more approximate figures, and allow
// the user to change in rare cases where this fails
m_fLightmapUnMerge_ThresholdDist = 0.001f;
m_fLightmapUnMerge_ThresholdDot = 0.99f;
if (!Engine::get_singleton()->is_editor_hint()) if (!Engine::get_singleton()->is_editor_hint())
{ {
CreateDebug(); CreateDebug();
@ -1233,25 +1243,65 @@ bool LRoomManager::rooms_transfer_uv2s(Node * pMeshInstance_From, Node * pMeshIn
} }
bool LRoomManager::rooms_unmerge_sobs(Node * pMergeMeshInstance) bool LRoomManager::rooms_unmerge_sobs(Node * pMergeMeshInstance, float thresh_dist, float thresh_dot)
{ {
MeshInstance * pMI = Object::cast_to<MeshInstance>(pMergeMeshInstance); MeshInstance * pMI = Object::cast_to<MeshInstance>(pMergeMeshInstance);
LHelper helper; LHelper helper;
helper.SetUnMergeParams(thresh_dist, thresh_dot);
Lawn::LDebug::m_bRunning = false; Lawn::LDebug::m_bRunning = false;
bool res = helper.UnMergeSOBs(*this, pMI); bool res = helper.UnMergeSOBs(*this, pMI);
Lawn::LDebug::m_bRunning = true; Lawn::LDebug::m_bRunning = true;
return res; return res;
} }
bool LRoomManager::export_scene_DAE(Node * pNode, String szFilename)
{
LDAEExporter dae;
return dae.SaveScene(pNode, szFilename);
}
bool LRoomManager::rooms_save_scene(Node * pNode, String szFilename) bool LRoomManager::rooms_save_scene(Node * pNode, String szFilename)
{ {
LSceneSaver saver; LSceneSaver saver;
return saver.SaveScene(pNode, szFilename); return saver.SaveScene(pNode, szFilename);
} }
void LRoomManager::lightmap_set_unmerge_params(float thresh_dist, float thresh_dot)
{
// sanitize? NYI
m_fLightmapUnMerge_ThresholdDist = thresh_dist;
m_fLightmapUnMerge_ThresholdDot = thresh_dot;
}
MeshInstance * LRoomManager::rooms_convert_lightmap_internal(String szProxyFilename, String szLevelFilename) bool LRoomManager::lightmap_external_unmerge(Node * pMergeMeshInstance, String szLevelFilename)
{
ResolveRoomListPath();
if (!CheckRoomList())
{
WARN_PRINT_ONCE("rooms_convert_lightmap_external : rooms not set");
return 0;
}
LRoomConverter conv;
conv.Convert(*this, true, true, false);
bool res = rooms_unmerge_sobs(pMergeMeshInstance, m_fLightmapUnMerge_ThresholdDist, m_fLightmapUnMerge_ThresholdDot);
if (res)
{
// save the UV2 mapped level
if (szLevelFilename != "")
rooms_save_scene(LROOMLIST, szLevelFilename);
return true;
}
return false;
}
MeshInstance * LRoomManager::lightmap_internal(String szProxyFilename, String szLevelFilename)
{ {
ResolveRoomListPath(); ResolveRoomListPath();
if (!CheckRoomList()) if (!CheckRoomList())
@ -1264,6 +1314,10 @@ MeshInstance * LRoomManager::rooms_convert_lightmap_internal(String szProxyFilen
conv.Convert(*this, true, true, false); conv.Convert(*this, true, true, false);
LHelper helper; LHelper helper;
// as we are doing an unmerge, we want to allow the user to override the parameters of the unmerge
helper.SetUnMergeParams(m_fLightmapUnMerge_ThresholdDist, m_fLightmapUnMerge_ThresholdDot);
Lawn::LDebug::m_bRunning = false; Lawn::LDebug::m_bRunning = false;
MeshInstance * pMI = helper.CreateLightmapProxy(*this); MeshInstance * pMI = helper.CreateLightmapProxy(*this);
@ -1283,15 +1337,70 @@ MeshInstance * LRoomManager::rooms_convert_lightmap_internal(String szProxyFilen
return pMI; return pMI;
} }
bool LRoomManager::lightmap_external_export(String szFilename) // DAE filename
{
ResolveRoomListPath();
CHECK_ROOM_LIST
LRoomConverter conv;
conv.Convert(*this, true, true, false, true);
// LRoomConverter conv;
// conv.Convert(*this, true, true, false);
//#m_Manager.rooms_single_room_convert(true, false)
// rooms_single_room_convert(
// if (!m_pRoomList)
// return false;
rooms_set_active(false);
// first create a temporary mesh instance
MeshInstance * pMerged = memnew(MeshInstance);
pMerged->set_name("Merged");
add_child(pMerged);
LHelper helper;
Lawn::LDebug::m_bRunning = false;
bool res = helper.MergeSOBs(*this, pMerged, false);
Lawn::LDebug::m_bRunning = true;
// create the merged geometry
if (res)
{
// save as a DAE
LDAEExporter dae;
dae.SetMergedMeshInstance(pMerged);
res = dae.SaveScene(m_pRoomList, szFilename, true);
}
pMerged->queue_delete();
rooms_set_active(true);
return res;
}
bool LRoomManager::rooms_merge_sobs(Node * pMergeMeshInstance) bool LRoomManager::rooms_merge_sobs(Node * pMergeMeshInstance)
{ {
MeshInstance * pMI = Object::cast_to<MeshInstance>(pMergeMeshInstance); MeshInstance * pMI = Object::cast_to<MeshInstance>(pMergeMeshInstance);
// first make sure all SOBs are showing by deactivating LPortal
rooms_set_active(false);
LHelper helper; LHelper helper;
Lawn::LDebug::m_bRunning = false; Lawn::LDebug::m_bRunning = false;
bool res = helper.MergeSOBs(*this, pMI); bool res = helper.MergeSOBs(*this, pMI);
Lawn::LDebug::m_bRunning = true; Lawn::LDebug::m_bRunning = true;
// finally reactivate LPortal
rooms_set_active(true);
return res; return res;
} }
@ -1881,10 +1990,17 @@ void LRoomManager::_bind_methods()
ClassDB::bind_method(D_METHOD("rooms_save_scene", "node", "filename"), &LRoomManager::rooms_save_scene); ClassDB::bind_method(D_METHOD("rooms_save_scene", "node", "filename"), &LRoomManager::rooms_save_scene);
// stuff for external workflow .. works but don't expose yet // stuff for external workflow .. works but don't expose yet
// ClassDB::bind_method(D_METHOD("rooms_merge_sobs"), &LRoomManager::rooms_merge_sobs); //ClassDB::bind_method(D_METHOD("rooms_merge_sobs"), &LRoomManager::rooms_merge_sobs);
// ClassDB::bind_method(D_METHOD("rooms_unmerge_sobs"), &LRoomManager::rooms_unmerge_sobs); //ClassDB::bind_method(D_METHOD("rooms_unmerge_sobs"), &LRoomManager::rooms_unmerge_sobs);
// ClassDB::bind_method(D_METHOD("rooms_transfer_uv2s"), &LRoomManager::rooms_transfer_uv2s); //ClassDB::bind_method(D_METHOD("rooms_transfer_uv2s"), &LRoomManager::rooms_transfer_uv2s);
ClassDB::bind_method(D_METHOD("lightmap_external_export", "DAE filename"), &LRoomManager::lightmap_external_export);
ClassDB::bind_method(D_METHOD("lightmap_external_unmerge", "merged mesh instance", "output level filename"), &LRoomManager::lightmap_external_unmerge);
ClassDB::bind_method(D_METHOD("lightmap_set_unmerge_params", "threshold distance (e.g. 0.001)", "threshold dot (e.g. 0.99)"), &LRoomManager::lightmap_set_unmerge_params);
// lightmapping
ClassDB::bind_method(D_METHOD("lightmap_internal", "output proxy filename", "output level filename"), &LRoomManager::lightmap_internal);
// debugging // debugging
ClassDB::bind_method(D_METHOD("rooms_set_logging", "level 0 to 6"), &LRoomManager::rooms_set_logging); ClassDB::bind_method(D_METHOD("rooms_set_logging", "level 0 to 6"), &LRoomManager::rooms_set_logging);
@ -1899,8 +2015,6 @@ void LRoomManager::_bind_methods()
ClassDB::bind_method(D_METHOD("rooms_set_debug_frame_string", "active"), &LRoomManager::rooms_set_debug_frame_string); ClassDB::bind_method(D_METHOD("rooms_set_debug_frame_string", "active"), &LRoomManager::rooms_set_debug_frame_string);
ClassDB::bind_method(D_METHOD("rooms_get_debug_frame_string"), &LRoomManager::rooms_get_debug_frame_string); ClassDB::bind_method(D_METHOD("rooms_get_debug_frame_string"), &LRoomManager::rooms_get_debug_frame_string);
// lightmapping
ClassDB::bind_method(D_METHOD("rooms_convert_lightmap_internal", "proxy filename", "level filename"), &LRoomManager::rooms_convert_lightmap_internal);
// functions to add dynamic objects to the culling system // functions to add dynamic objects to the culling system
// Note that these should not be placed directly in rooms, the system will 'soft link' to them // Note that these should not be placed directly in rooms, the system will 'soft link' to them
@ -1934,6 +2048,9 @@ void LRoomManager::_bind_methods()
ClassDB::bind_method(D_METHOD("set_rooms_path", "rooms"), &LRoomManager::set_rooms_path); ClassDB::bind_method(D_METHOD("set_rooms_path", "rooms"), &LRoomManager::set_rooms_path);
ClassDB::bind_method(D_METHOD("get_rooms_path"), &LRoomManager::get_rooms_path); ClassDB::bind_method(D_METHOD("get_rooms_path"), &LRoomManager::get_rooms_path);
ClassDB::bind_method(D_METHOD("export_scene_DAE", "node", "filename"), &LRoomManager::export_scene_DAE);
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "rooms"), "set_rooms_path", "get_rooms_path"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "rooms"), "set_rooms_path", "get_rooms_path");
} }

View File

@ -106,12 +106,16 @@ public:
// LIGHTMAPS // LIGHTMAPS
// helper function to merge SOB meshes for producing lightmaps VIA external blender workflow // helper function to merge SOB meshes for producing lightmaps VIA external blender workflow
bool rooms_merge_sobs(Node * pMergeMeshInstance); bool rooms_merge_sobs(Node * pMergeMeshInstance);
bool rooms_unmerge_sobs(Node * pMergeMeshInstance); bool rooms_unmerge_sobs(Node * pMergeMeshInstance, float thresh_dist, float thresh_dot);
bool rooms_transfer_uv2s(Node * pMeshInstance_From, Node * pMeshInstance_To); bool rooms_transfer_uv2s(Node * pMeshInstance_From, Node * pMeshInstance_To);
bool lightmap_external_export(String szFilename); // DAE filename
bool lightmap_external_unmerge(Node * pMergeMeshInstance, String szLevelFilename);
void lightmap_set_unmerge_params(float thresh_dist, float thresh_dot);
// one function to do all the uv mapping and lightmap creation in one // one function to do all the uv mapping and lightmap creation in one
// (for godot lightmap workflow) // (for godot lightmap workflow)
MeshInstance * rooms_convert_lightmap_internal(String szProxyFilename, String szLevelFilename); MeshInstance * lightmap_internal(String szProxyFilename, String szLevelFilename);
//______________________________________________________________________________________ //______________________________________________________________________________________
// HELPERS // HELPERS
@ -123,6 +127,7 @@ public:
Array rooms_get_visible_rooms() const; Array rooms_get_visible_rooms() const;
// helper func, not needed usually as dob_update returns the room // helper func, not needed usually as dob_update returns the room
int dob_get_room_id(Node * pDOB); int dob_get_room_id(Node * pDOB);
bool export_scene_DAE(Node * pNode, String szFilename);
//______________________________________________________________________________________ //______________________________________________________________________________________
@ -284,6 +289,10 @@ private:
// for debugging, can emulate view frustum culling // for debugging, can emulate view frustum culling
bool m_bFrustumOnly; bool m_bFrustumOnly;
// lightmap unmerge params
float m_fLightmapUnMerge_ThresholdDist;
float m_fLightmapUnMerge_ThresholdDot;
private: private:
// PRIVATE FUNCS // PRIVATE FUNCS
// this is where we do all the culling // this is where we do all the culling