mirror of
https://github.com/Relintai/godot-lportal.git
synced 2024-11-11 10:52:09 +01:00
414 lines
9.1 KiB
C++
414 lines
9.1 KiB
C++
// Copyright (c) 2019 Lawnjelly
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
// SOFTWARE.
|
|
|
|
#include "lportal.h"
|
|
#include "core/engine.h"
|
|
#include "lroom.h"
|
|
#include "ldebug.h"
|
|
#include "lroom_manager.h"
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
bool LPortal::NameStartsWith(const Node * pNode, String szSearch)
|
|
{
|
|
int sl = szSearch.length();
|
|
|
|
String name = pNode->get_name();
|
|
int l = name.length();
|
|
|
|
if (l < sl)
|
|
return false;
|
|
|
|
String szStart = name.substr(0, sl);
|
|
|
|
//print_line("\t\tNameStartsWith szStart is " + szStart);
|
|
|
|
if (szStart == szSearch)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
String LPortal::FindNameAfter(Node * pNode, String szStart)
|
|
{
|
|
String szRes;
|
|
String name = pNode->get_name();
|
|
szRes = name.substr(szStart.length());
|
|
|
|
// because godot doesn't support multiple nodes with the same name, we will strip e.g. a number
|
|
// after an @ on the end of the name...
|
|
// e.g. portal_kitchen@2
|
|
for (int c=0; c<szRes.length(); c++)
|
|
{
|
|
if (szRes[c] == '*')
|
|
{
|
|
// remove everything after and including this character
|
|
szRes = szRes.substr(0, c);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//print("\t\tNameAfter is " + szRes);
|
|
return szRes;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
|
|
void LPortal::Debug_CheckPlaneValidity(const Plane &p) const
|
|
{
|
|
assert (p.distance_to(m_ptCentre) < 0.0f);
|
|
}
|
|
|
|
|
|
// preprocess
|
|
void LPortal::AddLightPlanes(LRoomManager &manager, const LLight &light, LVector<Plane> &planes, bool bReverse) const
|
|
{
|
|
const Vector<Vector3> &pts = m_ptsWorld;
|
|
|
|
int nPoints = pts.size();
|
|
ERR_FAIL_COND(nPoints < 3);
|
|
|
|
if (light.m_Source.m_eType == LSource::ST_DIRECTIONAL)
|
|
{
|
|
// assuming ortho light
|
|
const int max_points = 32;
|
|
Vector3 pushed_pts[max_points];
|
|
|
|
if (nPoints > max_points)
|
|
nPoints = max_points;
|
|
|
|
// transform pushed points
|
|
Vector3 ptPush = light.m_Source.m_ptDir * 2.0;
|
|
|
|
for (int n=0; n<nPoints; n++)
|
|
{
|
|
pushed_pts[n] = pts[n] + ptPush;
|
|
}
|
|
|
|
Plane p;
|
|
|
|
for (int n=0; n<nPoints; n++)
|
|
{
|
|
int nPLUS = (n + 1) % nPoints;
|
|
p = Plane(pts[n], pts[nPLUS], pushed_pts[n]);
|
|
if (bReverse) p = -p;
|
|
planes.push_back(p);
|
|
Debug_CheckPlaneValidity(p);
|
|
}
|
|
|
|
// first and last
|
|
// p = Plane(pts[nPoints-1], pts[0], pushed_pts[0]);
|
|
// if (bReverse) p = -p;
|
|
// planes.push_back(p);
|
|
// Debug_CheckPlaneValidity(p);
|
|
|
|
// debug
|
|
if (manager.m_bDebugLights)
|
|
{
|
|
for (int n=1; n<nPoints; n++)
|
|
{
|
|
if (n == 2)
|
|
continue;
|
|
|
|
manager.m_DebugPortalLightPlanes.push_back(pts[n-1]);
|
|
manager.m_DebugPortalLightPlanes.push_back(pts[n]);
|
|
manager.m_DebugPortalLightPlanes.push_back(pushed_pts[n]);
|
|
manager.m_DebugPortalLightPlanes.push_back(pushed_pts[n]);
|
|
manager.m_DebugPortalLightPlanes.push_back(pushed_pts[n-1]);
|
|
manager.m_DebugPortalLightPlanes.push_back(pts[n-1]);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// use a point for the light for omni and spotlight
|
|
Plane p;
|
|
|
|
for (int n=0; n<nPoints; n++)
|
|
{
|
|
int nPLUS = (n + 1) % nPoints;
|
|
p = Plane(pts[nPLUS], pts[n], light.m_Source.m_ptPos);
|
|
if (bReverse) p = -p;
|
|
planes.push_back(p);
|
|
Debug_CheckPlaneValidity(p);
|
|
}
|
|
|
|
// first and last
|
|
// p = Plane(pts[0], pts[nPoints-1], light.m_ptPos);
|
|
// if (bReverse) p = -p;
|
|
// planes.push_back(p);
|
|
// Debug_CheckPlaneValidity(p);
|
|
|
|
}
|
|
|
|
|
|
// add clipping planes to the vector formed by each portal edge and the camera
|
|
void LPortal::AddPlanes(LRoomManager &manager, const Vector3 &ptCam, LVector<Plane> &planes) const
|
|
{
|
|
// short version
|
|
const Vector<Vector3> &pts = m_ptsWorld;
|
|
|
|
int nPoints = pts.size();
|
|
ERR_FAIL_COND(nPoints < 3);
|
|
|
|
Plane p;
|
|
|
|
for (int n=1; n<nPoints; n++)
|
|
{
|
|
p = Plane(ptCam, pts[n], pts[n-1]);
|
|
|
|
// detect null plane
|
|
// if (p.normal.length_squared() < 0.1f)
|
|
// {
|
|
// print("NULL plane detected from points : ");
|
|
// print(ptCam + pts[n] + pts[n-1]);
|
|
// }
|
|
planes.push_back(p);
|
|
Debug_CheckPlaneValidity(p);
|
|
}
|
|
|
|
// first and last
|
|
p = Plane(ptCam, pts[0], pts[nPoints-1]);
|
|
planes.push_back(p);
|
|
Debug_CheckPlaneValidity(p);
|
|
|
|
// debug
|
|
if (!manager.m_bDebugPlanes)
|
|
return;
|
|
|
|
for (int n=0; n<nPoints; n++)
|
|
{
|
|
manager.m_DebugPlanes.push_back(pts[n]);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
LPortal::eClipResult LPortal::ClipWithPlane(const Plane &p) const
|
|
{
|
|
int nOutside = 0;
|
|
int nPoints = m_ptsWorld.size();
|
|
|
|
for (int n=0; n<nPoints; n++)
|
|
{
|
|
float d = p.distance_to(m_ptsWorld[n]);
|
|
|
|
if (d >= 0.0f)
|
|
nOutside++;
|
|
}
|
|
|
|
if (nOutside == nPoints)
|
|
{
|
|
LPRINT_RUN(2, "\t\tOutside plane " + p);
|
|
return CLIP_OUTSIDE;
|
|
}
|
|
|
|
if (nOutside == 0)
|
|
return CLIP_INSIDE;
|
|
|
|
return CLIP_PARTIAL;
|
|
}
|
|
|
|
|
|
void LPortal::CreateGeometry(PoolVector<Vector3> p_vertices, const Transform &trans, bool bPortalPlane_Convention)
|
|
{
|
|
int nPoints = p_vertices.size();
|
|
ERR_FAIL_COND(nPoints < 3);
|
|
|
|
m_ptsWorld.clear();
|
|
|
|
//print("\t\t\tLPortal::CreateGeometry nPoints : " + itos(nPoints));
|
|
|
|
//Vector3 ptFirstThree[3];
|
|
|
|
for (int n=0; n<nPoints; n++)
|
|
{
|
|
Vector3 ptWorld = trans.xform(p_vertices[n]);
|
|
|
|
// store the first three to make the plane, irrespective of duplicates
|
|
// if (n < 3)
|
|
// ptFirstThree[n] = ptWorld;
|
|
|
|
// new!! test for duplicates. Some geometry may contain duplicate verts in portals which will muck up
|
|
// the winding etc...
|
|
bool bDuplicate = false;
|
|
for (int m=0; m<m_ptsWorld.size(); m++)
|
|
{
|
|
Vector3 ptDiff = ptWorld - m_ptsWorld[m];
|
|
if (ptDiff.length() < 0.001f)
|
|
{
|
|
bDuplicate = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bDuplicate)
|
|
{
|
|
m_ptsWorld.push_back(ptWorld);
|
|
m_ptCentre += ptWorld;
|
|
}
|
|
|
|
//print("\t\t\t\t" + itos(n) + "\tLocal : " + Variant(p_vertices[n]) + "\tWorld : " + ptWorld);
|
|
}
|
|
|
|
// Plane portal_plane = Plane(ptFirstThree[0], ptFirstThree[1], ptFirstThree[2]);
|
|
|
|
SortVertsClockwise(bPortalPlane_Convention);
|
|
PlaneFromPoints();
|
|
}
|
|
|
|
void LPortal::SortVertsClockwise(bool bPortalPlane_Convention)
|
|
{
|
|
Vector<Vector3> &verts = m_ptsWorld;
|
|
|
|
// We first assumed first 3 determine the desired normal
|
|
|
|
// find normal
|
|
Plane plane;
|
|
if (bPortalPlane_Convention)
|
|
plane = Plane(verts[0], verts[2], verts[1]);
|
|
else
|
|
plane = Plane(verts[0], verts[1], verts[2]);
|
|
|
|
Vector3 ptNormal = plane.normal;
|
|
|
|
// find centroid
|
|
int nPoints = verts.size();
|
|
|
|
Vector3 ptCentre = Vector3(0, 0, 0);
|
|
|
|
for (int n=0; n<nPoints; n++)
|
|
{
|
|
ptCentre += verts[n];
|
|
}
|
|
ptCentre /= nPoints;
|
|
m_ptCentre = ptCentre;
|
|
|
|
|
|
// now algorithm
|
|
for (int n=0; n<nPoints-2; n++)
|
|
{
|
|
Vector3 a = verts[n] - ptCentre;
|
|
a.normalize();
|
|
|
|
Plane p = Plane(verts[n], ptCentre, ptCentre + ptNormal);
|
|
|
|
double SmallestAngle = -1;
|
|
int Smallest = -1;
|
|
|
|
for (int m=n+1; m<nPoints; m++)
|
|
{
|
|
if (p.distance_to(verts[m]) > 0.0f)
|
|
{
|
|
Vector3 b = verts[m] - ptCentre;
|
|
b.normalize();
|
|
|
|
double Angle = a.dot(b);
|
|
|
|
if (Angle > SmallestAngle)
|
|
{
|
|
SmallestAngle = Angle;
|
|
Smallest = m;
|
|
}
|
|
} // which side
|
|
|
|
} // for m
|
|
|
|
// swap smallest and n+1 vert
|
|
if (Smallest != -1)
|
|
{
|
|
Vector3 temp = verts[Smallest];
|
|
verts.set(Smallest, verts[n+1]);
|
|
verts.set(n+1, temp);
|
|
}
|
|
} // for n
|
|
|
|
|
|
// the vertices are now sorted, but may be in the opposite order to that wanted.
|
|
// we detect this by calculating the normal of the poly, then flipping the order if the normal is pointing
|
|
// the wrong way.
|
|
plane = Plane(verts[0], verts[1], verts[2]);
|
|
|
|
if (ptNormal.dot(plane.normal) < 0.0f)
|
|
{
|
|
// reverse order of verts
|
|
ReverseWindingOrder();
|
|
}
|
|
|
|
}
|
|
|
|
void LPortal::ReverseWindingOrder()
|
|
{
|
|
Vector<Vector3> &verts = m_ptsWorld;
|
|
Vector<Vector3> copy = verts;
|
|
|
|
for (int n=0; n<verts.size(); n++)
|
|
{
|
|
verts.set(n, copy[verts.size() - n - 1]);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void LPortal::CopyReversedGeometry(const LPortal &source)
|
|
{
|
|
//print("\t\t\tCopyReversedGeometry");
|
|
// points are the same but reverse winding order
|
|
int nPoints = source.m_ptsWorld.size();
|
|
m_ptCentre = source.m_ptCentre;
|
|
|
|
m_ptsWorld.resize(nPoints);
|
|
|
|
for (int n=0; n<nPoints; n++)
|
|
{
|
|
m_ptsWorld.set(n, source.m_ptsWorld[nPoints - n - 1]);
|
|
//print("\t\t\t\t" + itos(n) + "\t: " + Variant(m_ptsWorld[n]));
|
|
}
|
|
|
|
PlaneFromPoints();
|
|
}
|
|
|
|
void LPortal::PlaneFromPoints()
|
|
{
|
|
if (m_ptsWorld.size() < 3)
|
|
{
|
|
WARN_PRINT("Portal must have at least 3 vertices");
|
|
return;
|
|
}
|
|
// create plane from points
|
|
m_Plane = Plane(m_ptsWorld[0], m_ptsWorld[1], m_ptsWorld[2]);
|
|
|
|
//print("\t\t\t\t\tPlane normal world space : " + m_Plane);
|
|
|
|
}
|
|
|
|
|
|
LPortal::LPortal() {
|
|
// unset
|
|
m_iRoomNum = -1;
|
|
m_bMirror = false;
|
|
// m_uiFrameTouched_Blocked = 0;
|
|
}
|
|
|
|
|
|
|