mirror of
https://github.com/Relintai/godot-lportal.git
synced 2024-11-11 10:52:09 +01:00
Add files via upload
Work in progress, conversion spatials to rooms / portals
This commit is contained in:
parent
ac1e507aa6
commit
f02b5f42d6
24
SCsub
Normal file
24
SCsub
Normal file
@ -0,0 +1,24 @@
|
||||
# SCsub
|
||||
Import('env')
|
||||
|
||||
sources = [
|
||||
"register_types.cpp",
|
||||
"lroom.cpp",
|
||||
"lroom_manager.cpp",
|
||||
"lportal.cpp",
|
||||
]
|
||||
|
||||
module_env = env.Clone()
|
||||
module_env.Append(CXXFLAGS=['-O2', '-std=c++11'])
|
||||
|
||||
if ARGUMENTS.get('lportal_shared', 'no') == 'yes':
|
||||
# Shared lib compilation
|
||||
module_env.Append(CXXFLAGS='-fPIC')
|
||||
module_env['LIBS'] = []
|
||||
shared_lib = module_env.SharedLibrary(target='#bin/lportal', source=sources)
|
||||
shared_lib_shim = shared_lib[0].name.rsplit('.', 1)[0]
|
||||
env.Append(LIBS=[shared_lib_shim])
|
||||
env.Append(LIBPATH=['#bin'])
|
||||
else:
|
||||
# Static compilation
|
||||
module_env.add_source_files(env.modules_sources, sources)
|
7
config.py
Normal file
7
config.py
Normal file
@ -0,0 +1,7 @@
|
||||
# config.py
|
||||
|
||||
def can_build(env, platform):
|
||||
return True
|
||||
|
||||
def configure(env):
|
||||
pass
|
307
lportal.cpp
Normal file
307
lportal.cpp
Normal file
@ -0,0 +1,307 @@
|
||||
// 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"
|
||||
|
||||
|
||||
//#define SMOOTHCLASS Smooth
|
||||
//#define SMOOTHNODE Spatial
|
||||
//#include "smooth_body.inl"
|
||||
|
||||
bool LPortal::NameStartsWith(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(sl);
|
||||
|
||||
if (szStart == szSearch)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
String LPortal::FindNameAfter(Node * pNode, int CharsToMiss)
|
||||
{
|
||||
String szRes;
|
||||
String name = pNode->get_name();
|
||||
szRes = name.substr(CharsToMiss);
|
||||
return szRes;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
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.0)
|
||||
nOutside++;
|
||||
}
|
||||
|
||||
if (nOutside == nPoints)
|
||||
return CLIP_OUTSIDE;
|
||||
|
||||
if (nOutside == 0)
|
||||
return CLIP_INSIDE;
|
||||
|
||||
return CLIP_PARTIAL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// use the name of the portal to find a room to link to
|
||||
void LPortal::Link(LRoom * pParentRoom)
|
||||
{
|
||||
// should start with 'portal_'
|
||||
if (!NameStartsWith(this, "portal_"))
|
||||
{
|
||||
WARN_PRINT("Portal name should begin with lportal_");
|
||||
return;
|
||||
}
|
||||
|
||||
String szRoom = FindNameAfter(this, 8);
|
||||
|
||||
// find the room group
|
||||
Spatial * pGroup = Object::cast_to<Spatial>(pParentRoom->get_parent());
|
||||
if (!pGroup)
|
||||
{
|
||||
WARN_PRINT("Room parent is not a spatial");
|
||||
return;
|
||||
}
|
||||
|
||||
// attempt to find a child of the group that has the name specified
|
||||
int nChildren = pGroup->get_child_count();
|
||||
|
||||
for (int n=0; n<nChildren; n++)
|
||||
{
|
||||
Node * pChild = pGroup->get_child(n);
|
||||
|
||||
String szChildName = pChild->get_name();
|
||||
|
||||
// is the name correct for the desired room?
|
||||
if (szRoom != szChildName)
|
||||
continue;
|
||||
|
||||
LRoom * pTargetRoom = Object::cast_to<LRoom>(pChild);
|
||||
|
||||
if (!pTargetRoom)
|
||||
{
|
||||
WARN_PRINT("Portal target is not a room");
|
||||
return;
|
||||
}
|
||||
|
||||
// found! link
|
||||
pTargetRoom->MakeOppositePortal(this, pParentRoom);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LPortal::CreateGeometry(PoolVector<Vector3> p_vertices)
|
||||
{
|
||||
int nPoints = p_vertices.size();
|
||||
ERR_FAIL_COND(nPoints < 3);
|
||||
|
||||
m_ptsWorld.resize(nPoints);
|
||||
|
||||
for (int n=0; n<nPoints; n++)
|
||||
{
|
||||
m_ptsWorld.set(n, p_vertices[n]);
|
||||
}
|
||||
|
||||
PlaneFromPoints();
|
||||
}
|
||||
|
||||
void LPortal::CopyReversedGeometry(const LPortal &source)
|
||||
{
|
||||
// points are the same but reverse winding order
|
||||
int nPoints = source.m_ptsWorld.size();
|
||||
m_ptsWorld.resize(nPoints);
|
||||
|
||||
for (int n=0; n<nPoints; n++)
|
||||
{
|
||||
m_ptsWorld.set(n, source.m_ptsWorld[nPoints - n - 1]);
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
|
||||
bool LPortal::AddRoom(NodePath path)
|
||||
{
|
||||
if (has_node(path))
|
||||
{
|
||||
LRoom * pNode = Object::cast_to<LRoom>(get_node(path));
|
||||
if (pNode)
|
||||
{
|
||||
ObjectID id = pNode->get_instance_id();
|
||||
|
||||
m_room_path = path;
|
||||
m_room_ID = id;
|
||||
|
||||
// make the portal name correct and feature the room name
|
||||
int num_names = path.get_name_count();
|
||||
if (num_names < 1)
|
||||
{
|
||||
WARN_PRINT("LPortal::AddRoom : Path too short");
|
||||
return false;
|
||||
}
|
||||
String szRoom = path.get_name(num_names-1);
|
||||
|
||||
String szPortal = "lportal_" + szRoom;
|
||||
set_name(szPortal);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_PRINT("not a room");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LPortal::LPortal() {
|
||||
// unset
|
||||
m_room_ID = 0;
|
||||
}
|
||||
|
||||
|
||||
void LPortal::_bind_methods() {
|
||||
|
||||
// BIND_ENUM_CONSTANT(MODE_LOCAL);
|
||||
// BIND_ENUM_CONSTANT(MODE_GLOBAL);
|
||||
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("teleport"), &SMOOTHCLASS::teleport);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_enabled"), &SMOOTHCLASS::set_enabled);
|
||||
// ClassDB::bind_method(D_METHOD("is_enabled"), &SMOOTHCLASS::is_enabled);
|
||||
// ClassDB::bind_method(D_METHOD("set_smooth_translate"), &SMOOTHCLASS::set_interpolate_translation);
|
||||
// ClassDB::bind_method(D_METHOD("get_smooth_translate"), &SMOOTHCLASS::get_interpolate_translation);
|
||||
// ClassDB::bind_method(D_METHOD("set_smooth_rotate"), &SMOOTHCLASS::set_interpolate_rotation);
|
||||
// ClassDB::bind_method(D_METHOD("get_smooth_rotate"), &SMOOTHCLASS::get_interpolate_rotation);
|
||||
// ClassDB::bind_method(D_METHOD("set_smooth_scale"), &SMOOTHCLASS::set_interpolate_scale);
|
||||
// ClassDB::bind_method(D_METHOD("get_smooth_scale"), &SMOOTHCLASS::get_interpolate_scale);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_input_mode", "mode"), &SMOOTHCLASS::set_input_mode);
|
||||
// ClassDB::bind_method(D_METHOD("get_input_mode"), &SMOOTHCLASS::get_input_mode);
|
||||
// ClassDB::bind_method(D_METHOD("set_output_mode", "mode"), &SMOOTHCLASS::set_output_mode);
|
||||
// ClassDB::bind_method(D_METHOD("get_output_mode"), &SMOOTHCLASS::get_output_mode);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_target", "target"), &SMOOTHCLASS::set_target);
|
||||
// ClassDB::bind_method(D_METHOD("set_target_path", "path"), &SMOOTHCLASS::set_target_path);
|
||||
// ClassDB::bind_method(D_METHOD("get_target_path"), &SMOOTHCLASS::get_target_path);
|
||||
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target"), "set_target_path", "get_target_path");
|
||||
|
||||
|
||||
// ADD_GROUP("Components", "");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_translate"), "set_smooth_translate", "get_smooth_translate");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_rotate"), "set_smooth_rotate", "get_smooth_rotate");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_scale"), "set_smooth_scale", "get_smooth_scale");
|
||||
// ADD_GROUP("Coords", "");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::INT, "input", PROPERTY_HINT_ENUM, "Local,Global"), "set_input_mode", "get_input_mode");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::INT, "output", PROPERTY_HINT_ENUM, "Local,Global"), "set_output_mode", "get_output_mode");
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::BOOL, "lerp"), "set_lerp", "get_lerp");
|
||||
|
||||
// finish the bind with custom stuff
|
||||
//BIND_ENUM_CONSTANT(METHOD_SLERP);
|
||||
//BIND_ENUM_CONSTANT(METHOD_LERP);
|
||||
//ClassDB::bind_method(D_METHOD("set_method", "method"), &Room::set_method);
|
||||
//ClassDB::bind_method(D_METHOD("get_method"), &Room::get_method);
|
||||
|
||||
//ADD_GROUP("Misc", "");
|
||||
//ADD_PROPERTY(PropertyInfo(Variant::INT, "method", PROPERTY_HINT_ENUM, "Slerp,Lerp"), "set_method", "get_method");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//void Smooth::set_method(eMethod p_method)
|
||||
//{
|
||||
//ChangeFlags(SF_LERP, p_method == METHOD_LERP);
|
||||
//}
|
||||
|
||||
//Smooth::eMethod Smooth::get_method() const
|
||||
//{
|
||||
//if (TestFlags(SF_LERP))
|
||||
//return METHOD_LERP;
|
||||
|
||||
//return METHOD_SLERP;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//bool Smooth::FindVisibility() const
|
||||
//{
|
||||
// const Spatial *s = this;
|
||||
|
||||
// int count = 0;
|
||||
// while (s) {
|
||||
|
||||
// if (!s->data.visible)
|
||||
// {
|
||||
// print_line(itos(count++) + " hidden");
|
||||
// return false;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// print_line(itos(count++) + " visible");
|
||||
// }
|
||||
// s = s->data.parent;
|
||||
// }
|
||||
|
||||
// return true;
|
||||
//}
|
103
lportal.h
Normal file
103
lportal.h
Normal file
@ -0,0 +1,103 @@
|
||||
// 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.
|
||||
|
||||
/* portal.h */
|
||||
#ifndef LPORTAL_H
|
||||
#define LPORTAL_H
|
||||
|
||||
/**
|
||||
@author lawnjelly <lawnjelly@gmail.com>
|
||||
*/
|
||||
|
||||
|
||||
#include "scene/3d/spatial.h"
|
||||
|
||||
|
||||
// Smooth node allows fixed timestep interpolation without having to write any code.
|
||||
// It requires a proxy node (which is moved on physics tick), e.g. a rigid body or manually moved spatial..
|
||||
// and instead of having MeshInstance as a child of this, you add Smooth node to another part of the scene graph,
|
||||
// make the MeshInstance a child of the smooth node, then choose the proxy as the target for the smooth node.
|
||||
|
||||
// Note that in the special case of manually moving the proxy to a completely new location, you should call
|
||||
// 'teleport' on the smooth node after setting the proxy node transform. This will ensure that the current AND
|
||||
// previous transform records are reset, so it moves instantaneously.
|
||||
|
||||
class LRoom;
|
||||
|
||||
class LPortal : public Spatial {
|
||||
GDCLASS(LPortal, Spatial);
|
||||
|
||||
friend class LRoom;
|
||||
private:
|
||||
|
||||
enum eClipResult
|
||||
{
|
||||
CLIP_OUTSIDE,
|
||||
CLIP_PARTIAL,
|
||||
CLIP_INSIDE,
|
||||
};
|
||||
|
||||
|
||||
ObjectID m_room_ID;
|
||||
NodePath m_room_path;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
LPortal::eClipResult ClipWithPlane(const Plane &p) const;
|
||||
|
||||
public:
|
||||
// normal determined by winding order
|
||||
Vector<Vector3> m_ptsWorld;
|
||||
Plane m_Plane;
|
||||
|
||||
//enum eMethod
|
||||
//{
|
||||
//METHOD_SLERP,
|
||||
//METHOD_LERP,
|
||||
//};
|
||||
|
||||
ObjectID GetLinkedRoomID() const {return m_room_ID;}
|
||||
|
||||
LPortal();
|
||||
|
||||
// void set_method(eMethod p_method);
|
||||
// eMethod get_method() const;
|
||||
private:
|
||||
// use the name of the portal to find a room to link to
|
||||
void Link(LRoom * pParentRoom);
|
||||
bool AddRoom(NodePath path);
|
||||
void CopyReversedGeometry(const LPortal &source);
|
||||
void CreateGeometry(PoolVector<Vector3> p_vertices);
|
||||
void PlaneFromPoints();
|
||||
|
||||
// useful funcs
|
||||
public:
|
||||
static bool NameStartsWith(Node * pNode, String szSearch);
|
||||
static String FindNameAfter(Node * pNode, int CharsToMiss);
|
||||
};
|
||||
|
||||
//VARIANT_ENUM_CAST(Smooth::eMode);
|
||||
//VARIANT_ENUM_CAST(Smooth::eMethod);
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
362
lroom.cpp
Normal file
362
lroom.cpp
Normal file
@ -0,0 +1,362 @@
|
||||
// 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 "lroom.h"
|
||||
#include "core/engine.h"
|
||||
#include "scene/3d/mesh_instance.h"
|
||||
#include "lportal.h"
|
||||
|
||||
|
||||
//#define SMOOTHCLASS Smooth
|
||||
//#define SMOOTHNODE Spatial
|
||||
//#include "smooth_body.inl"
|
||||
|
||||
|
||||
LRoom::LRoom() {
|
||||
// m_Flags = 0;
|
||||
// SetFlags(SF_ENABLED | SF_TRANSLATE | SF_ROTATE);
|
||||
}
|
||||
|
||||
void LRoom::DetermineVisibility_Recursive(LCamera &cam, const Vector<Plane> &planes, ObjectID portalID_from)
|
||||
{
|
||||
// clip all objects in this room to the clipping planes
|
||||
// NYI
|
||||
|
||||
// go through each portal out of here
|
||||
int nPortals = m_portal_IDs.size();
|
||||
|
||||
ObjectID this_room_id = get_instance_id();
|
||||
|
||||
for (int p=0; p<nPortals; p++)
|
||||
{
|
||||
ObjectID id = m_portal_IDs[p];
|
||||
|
||||
// ignore if the portal we are looking in from
|
||||
if (id == portalID_from)
|
||||
continue;
|
||||
|
||||
// get the portal
|
||||
Object *pObj = ObjectDB::get_instance(id);
|
||||
|
||||
// assuming is a portal
|
||||
LPortal * pPortal = Object::cast_to<LPortal>(pObj);
|
||||
|
||||
if (!pPortal)
|
||||
{
|
||||
WARN_PRINT_ONCE("LRoom::DetermineVisibility_Recursive : Not a portal");
|
||||
continue;
|
||||
}
|
||||
|
||||
const Vector3 &portal_normal = pPortal->m_Plane.normal;
|
||||
|
||||
// direction with the camera? (might not need to check)
|
||||
float dot = cam.m_ptDir.dot(portal_normal);
|
||||
if (dot <= 0.0f)
|
||||
continue;
|
||||
|
||||
// is it culled by the planes?
|
||||
LPortal::eClipResult overall_res = LPortal::eClipResult::CLIP_INSIDE;
|
||||
|
||||
for (int l=0; l<planes.size(); l++)
|
||||
{
|
||||
LPortal::eClipResult res = pPortal->ClipWithPlane(planes[l]);
|
||||
|
||||
switch (res)
|
||||
{
|
||||
case LPortal::eClipResult::CLIP_OUTSIDE:
|
||||
overall_res = res;
|
||||
break;
|
||||
case LPortal::eClipResult::CLIP_PARTIAL:
|
||||
overall_res = res;
|
||||
break;
|
||||
}
|
||||
|
||||
if (overall_res == LPortal::eClipResult::CLIP_OUTSIDE)
|
||||
break;
|
||||
}
|
||||
|
||||
// this portal is culled
|
||||
if (overall_res == LPortal::eClipResult::CLIP_OUTSIDE)
|
||||
continue;
|
||||
|
||||
// else recurse into that portal
|
||||
Vector<Plane> new_planes = planes;
|
||||
|
||||
// add the planes for the portal
|
||||
// NYI
|
||||
|
||||
DetermineVisibility_Recursive(cam, new_planes, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// initial setup, allows importing portals as meshes from modelling program,
|
||||
// which will be auto converted to LPortals with this method
|
||||
void LRoom::DetectPortalMeshes()
|
||||
{
|
||||
bool bFoundOne = true;
|
||||
|
||||
while (bFoundOne)
|
||||
{
|
||||
bFoundOne = false;
|
||||
|
||||
for (int n=0; n<get_child_count(); n++)
|
||||
{
|
||||
Node * pChild = get_child(n);
|
||||
|
||||
MeshInstance * pMesh = Object::cast_to<MeshInstance>(pChild);
|
||||
if (pMesh)
|
||||
{
|
||||
// name must start with portal_
|
||||
if (LPortal::NameStartsWith(pMesh, "portal_"))
|
||||
{
|
||||
String szLinkRoom = LPortal::FindNameAfter(pMesh, 8);
|
||||
DetectedPortalMesh(pMesh, szLinkRoom);
|
||||
bFoundOne = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bFoundOne)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LRoom::DetectedPortalMesh(MeshInstance * pMeshInstance, String szLinkRoom)
|
||||
{
|
||||
Ref<Mesh> rmesh = pMeshInstance->get_mesh();
|
||||
|
||||
Array arrays = rmesh->surface_get_arrays(0);
|
||||
PoolVector<Vector3> p_vertices = arrays[VS::ARRAY_VERTEX];
|
||||
|
||||
LPortal * pNew = memnew(LPortal);
|
||||
pNew->set_name("lportal");
|
||||
add_child(pNew);
|
||||
|
||||
NodePath temppath = "../../" + szLinkRoom;
|
||||
pNew->AddRoom(szLinkRoom);
|
||||
|
||||
// create the portal geometry
|
||||
pNew->CreateGeometry(p_vertices);
|
||||
|
||||
// delete the original child
|
||||
pMeshInstance->get_parent()->remove_child(pMeshInstance);
|
||||
|
||||
pMeshInstance->queue_delete();
|
||||
}
|
||||
|
||||
void LRoom::MakePortalsTwoWay()
|
||||
{
|
||||
for (int n=0; n<get_child_count(); n++)
|
||||
{
|
||||
Node * pChild = get_child(n);
|
||||
|
||||
LPortal * pPortal = Object::cast_to<LPortal>(pChild);
|
||||
if (pPortal)
|
||||
{
|
||||
pPortal->Link(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// assuming that portals are a child of the room, detect these and make them 2 way
|
||||
void LRoom::MakePortalQuickList()
|
||||
{
|
||||
// this function could be called more than one time...
|
||||
m_portal_IDs.clear();
|
||||
|
||||
for (int n=0; n<get_child_count(); n++)
|
||||
{
|
||||
Node * pChild = get_child(n);
|
||||
|
||||
LPortal * pPortal = Object::cast_to<LPortal>(pChild);
|
||||
if (pPortal)
|
||||
{
|
||||
ObjectID id = pPortal->get_instance_id();
|
||||
m_portal_IDs.push_back(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure there is a back (opposite) portal leading from the portal from to the roomto, from this room
|
||||
void LRoom::MakeOppositePortal(LPortal * pPortalFrom, LRoom * pRoomTo)
|
||||
{
|
||||
ObjectID room_to_id = pRoomTo->get_instance_id();
|
||||
|
||||
// does an opposite portal exist already?
|
||||
for (int n=0; n<get_child_count(); n++)
|
||||
{
|
||||
Node * pChild = get_child(n);
|
||||
|
||||
LPortal * pPortal = Object::cast_to<LPortal>(pChild);
|
||||
if (pPortal)
|
||||
{
|
||||
if (pPortal->GetLinkedRoomID() == room_to_id)
|
||||
// already linked
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if we got to here it isn't linked...
|
||||
// add a new portal
|
||||
// NYI
|
||||
LPortal * pNew = memnew(LPortal);
|
||||
pNew->set_name("lportal");
|
||||
add_child(pNew);
|
||||
|
||||
pNew->AddRoom(pRoomTo->get_path());
|
||||
pNew->CopyReversedGeometry(*pPortalFrom);
|
||||
}
|
||||
|
||||
|
||||
void LRoom::_bind_methods() {
|
||||
|
||||
// BIND_ENUM_CONSTANT(MODE_LOCAL);
|
||||
// BIND_ENUM_CONSTANT(MODE_GLOBAL);
|
||||
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("teleport"), &SMOOTHCLASS::teleport);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_enabled"), &SMOOTHCLASS::set_enabled);
|
||||
// ClassDB::bind_method(D_METHOD("is_enabled"), &SMOOTHCLASS::is_enabled);
|
||||
// ClassDB::bind_method(D_METHOD("set_smooth_translate"), &SMOOTHCLASS::set_interpolate_translation);
|
||||
// ClassDB::bind_method(D_METHOD("get_smooth_translate"), &SMOOTHCLASS::get_interpolate_translation);
|
||||
// ClassDB::bind_method(D_METHOD("set_smooth_rotate"), &SMOOTHCLASS::set_interpolate_rotation);
|
||||
// ClassDB::bind_method(D_METHOD("get_smooth_rotate"), &SMOOTHCLASS::get_interpolate_rotation);
|
||||
// ClassDB::bind_method(D_METHOD("set_smooth_scale"), &SMOOTHCLASS::set_interpolate_scale);
|
||||
// ClassDB::bind_method(D_METHOD("get_smooth_scale"), &SMOOTHCLASS::get_interpolate_scale);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_input_mode", "mode"), &SMOOTHCLASS::set_input_mode);
|
||||
// ClassDB::bind_method(D_METHOD("get_input_mode"), &SMOOTHCLASS::get_input_mode);
|
||||
// ClassDB::bind_method(D_METHOD("set_output_mode", "mode"), &SMOOTHCLASS::set_output_mode);
|
||||
// ClassDB::bind_method(D_METHOD("get_output_mode"), &SMOOTHCLASS::get_output_mode);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_target", "target"), &SMOOTHCLASS::set_target);
|
||||
// ClassDB::bind_method(D_METHOD("set_target_path", "path"), &SMOOTHCLASS::set_target_path);
|
||||
// ClassDB::bind_method(D_METHOD("get_target_path"), &SMOOTHCLASS::get_target_path);
|
||||
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target"), "set_target_path", "get_target_path");
|
||||
|
||||
|
||||
// ADD_GROUP("Components", "");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_translate"), "set_smooth_translate", "get_smooth_translate");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_rotate"), "set_smooth_rotate", "get_smooth_rotate");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_scale"), "set_smooth_scale", "get_smooth_scale");
|
||||
// ADD_GROUP("Coords", "");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::INT, "input", PROPERTY_HINT_ENUM, "Local,Global"), "set_input_mode", "get_input_mode");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::INT, "output", PROPERTY_HINT_ENUM, "Local,Global"), "set_output_mode", "get_output_mode");
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::BOOL, "lerp"), "set_lerp", "get_lerp");
|
||||
|
||||
// finish the bind with custom stuff
|
||||
//BIND_ENUM_CONSTANT(METHOD_SLERP);
|
||||
//BIND_ENUM_CONSTANT(METHOD_LERP);
|
||||
//ClassDB::bind_method(D_METHOD("set_method", "method"), &LRoom::set_method);
|
||||
//ClassDB::bind_method(D_METHOD("get_method"), &LRoom::get_method);
|
||||
|
||||
//ADD_GROUP("Misc", "");
|
||||
//ADD_PROPERTY(PropertyInfo(Variant::INT, "method", PROPERTY_HINT_ENUM, "Slerp,Lerp"), "set_method", "get_method");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//void LRoom::SetupPortal(LPortal * pPortal)
|
||||
//{
|
||||
// ObjectID id = pPortal->get_instance_id();
|
||||
// m_portal_IDs.push_back(id);
|
||||
|
||||
//}
|
||||
|
||||
//void LRoom::AddPortal(ObjectID id)
|
||||
//{
|
||||
//}
|
||||
|
||||
//bool LRoom::AddPortal(LPortal * pNode)
|
||||
//{
|
||||
// if (has_node(path))
|
||||
// {
|
||||
// LPortal * pNode = Object::cast_to<LPortal>(get_node(path));
|
||||
// if (pNode)
|
||||
// {
|
||||
// ObjectID id = pNode->get_instance_id();
|
||||
|
||||
// m_portal_paths.push_back(path);
|
||||
// m_portal_IDs.push_back(id);
|
||||
|
||||
// // add the room to the portal automatically
|
||||
// NodePath self_path = get_path_to(this);
|
||||
// pNode->AddRoom(self_path);
|
||||
// return true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// WARN_PRINT("not a portal");
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// return false;
|
||||
//}
|
||||
|
||||
//void Smooth::set_method(eMethod p_method)
|
||||
//{
|
||||
//ChangeFlags(SF_LERP, p_method == METHOD_LERP);
|
||||
//}
|
||||
|
||||
//Smooth::eMethod Smooth::get_method() const
|
||||
//{
|
||||
//if (TestFlags(SF_LERP))
|
||||
//return METHOD_LERP;
|
||||
|
||||
//return METHOD_SLERP;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//bool Smooth::FindVisibility() const
|
||||
//{
|
||||
// const Spatial *s = this;
|
||||
|
||||
// int count = 0;
|
||||
// while (s) {
|
||||
|
||||
// if (!s->data.visible)
|
||||
// {
|
||||
// print_line(itos(count++) + " hidden");
|
||||
// return false;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// print_line(itos(count++) + " visible");
|
||||
// }
|
||||
// s = s->data.parent;
|
||||
// }
|
||||
|
||||
// return true;
|
||||
//}
|
115
lroom.h
Normal file
115
lroom.h
Normal file
@ -0,0 +1,115 @@
|
||||
// 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.
|
||||
|
||||
/* room.h */
|
||||
#ifndef LROOM_H
|
||||
#define LROOM_H
|
||||
|
||||
/**
|
||||
@author lawnjelly <lawnjelly@gmail.com>
|
||||
*/
|
||||
|
||||
|
||||
#include "scene/3d/spatial.h"
|
||||
|
||||
|
||||
// Smooth node allows fixed timestep interpolation without having to write any code.
|
||||
// It requires a proxy node (which is moved on physics tick), e.g. a rigid body or manually moved spatial..
|
||||
// and instead of having MeshInstance as a child of this, you add Smooth node to another part of the scene graph,
|
||||
// make the MeshInstance a child of the smooth node, then choose the proxy as the target for the smooth node.
|
||||
|
||||
// Note that in the special case of manually moving the proxy to a completely new location, you should call
|
||||
// 'teleport' on the smooth node after setting the proxy node transform. This will ensure that the current AND
|
||||
// previous transform records are reset, so it moves instantaneously.
|
||||
|
||||
class LPortal;
|
||||
class MeshInstance;
|
||||
|
||||
class LCamera
|
||||
{
|
||||
public:
|
||||
// all in world space, culling done in world space
|
||||
Vector3 m_ptPos;
|
||||
Vector3 m_ptDir;
|
||||
};
|
||||
|
||||
|
||||
class LRoom : public Spatial {
|
||||
GDCLASS(LRoom, Spatial);
|
||||
|
||||
friend class LPortal;
|
||||
public:
|
||||
|
||||
// custom
|
||||
private:
|
||||
//class STransform
|
||||
//{
|
||||
//public:
|
||||
//Transform m_Transform;
|
||||
//Quat m_qtRotate;
|
||||
//Vector3 m_ptScale;
|
||||
//};
|
||||
|
||||
//Vector3 m_ptTranslateDiff;
|
||||
|
||||
// a quick list of object IDs of child portals of this room
|
||||
Vector<ObjectID> m_portal_IDs;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
//enum eMethod
|
||||
//{
|
||||
//METHOD_SLERP,
|
||||
//METHOD_LERP,
|
||||
//};
|
||||
// bool AddPortal(LPortal * pNode);
|
||||
|
||||
// initial setup, allows importing portals as meshes from modelling program,
|
||||
// which will be auto converted to LPortals with this method
|
||||
void DetectPortalMeshes();
|
||||
// assuming that portals are a child of the room, detect these and make them 2 way
|
||||
void MakePortalsTwoWay();
|
||||
void MakePortalQuickList();
|
||||
|
||||
// main function
|
||||
void DetermineVisibility_Recursive(LCamera &cam, const Vector<Plane> &planes, ObjectID portalID_from = 0);
|
||||
|
||||
// specific
|
||||
public:
|
||||
LRoom();
|
||||
|
||||
// void set_method(eMethod p_method);
|
||||
// eMethod get_method() const;
|
||||
private:
|
||||
|
||||
|
||||
// void SetupPortal(LPortal * pPortal);
|
||||
void MakeOppositePortal(LPortal * pPortalFrom, LRoom * pRoomTo);
|
||||
void DetectedPortalMesh(MeshInstance * pMeshInstance, String szLinkRoom);
|
||||
// void AddPortal(ObjectID id);
|
||||
// void LerpBasis(const Basis &from, const Basis &to, Basis &res, float f) const;
|
||||
};
|
||||
|
||||
//VARIANT_ENUM_CAST(Smooth::eMode);
|
||||
//VARIANT_ENUM_CAST(Smooth::eMethod);
|
||||
|
||||
#endif
|
138
lroom_manager.cpp
Normal file
138
lroom_manager.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
// 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 "lroom_manager.h"
|
||||
#include "lportal.h"
|
||||
#include "lroom.h"
|
||||
|
||||
LRoomManager::LRoomManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
// convert empties and meshes to rooms and portals
|
||||
void LRoomManager::Convert()
|
||||
{
|
||||
Convert_Rooms();
|
||||
Convert_Portals();
|
||||
}
|
||||
|
||||
void LRoomManager::Convert_Rooms()
|
||||
{
|
||||
bool bConvertedOne = true;
|
||||
|
||||
// instead of recursive routine
|
||||
while (bConvertedOne)
|
||||
{
|
||||
bConvertedOne = false;
|
||||
|
||||
// first find all room empties and convert to LRooms
|
||||
for (int n=0; n<get_child_count(); n++)
|
||||
{
|
||||
Node * pChild = get_child(n);
|
||||
|
||||
// don't want to handle already converted rooms
|
||||
LRoom * pRoom = Object::cast_to<LRoom>(pChild);
|
||||
if (pRoom)
|
||||
continue;
|
||||
|
||||
if (LPortal::NameStartsWith(pChild, "room_"))
|
||||
{
|
||||
if (Convert_Room(pChild))
|
||||
bConvertedOne = true;
|
||||
}
|
||||
|
||||
if (bConvertedOne)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LRoomManager::Convert_Portals()
|
||||
{
|
||||
for (int pass=0; pass<3; pass++)
|
||||
{
|
||||
// first find all room empties and convert to LRooms
|
||||
for (int n=0; n<get_child_count(); n++)
|
||||
{
|
||||
Node * pChild = get_child(n);
|
||||
|
||||
// don't want to handle already converted rooms
|
||||
LRoom * pRoom = Object::cast_to<LRoom>(pChild);
|
||||
if (pRoom)
|
||||
{
|
||||
switch (pass)
|
||||
{
|
||||
case 0:
|
||||
pRoom->DetectPortalMeshes();
|
||||
break;
|
||||
case 1:
|
||||
pRoom->MakePortalsTwoWay();
|
||||
break;
|
||||
case 2:
|
||||
pRoom->MakePortalQuickList();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool LRoomManager::Convert_Room(Node * pNode)
|
||||
{
|
||||
// get the room part of the name
|
||||
String szFullName = pNode->get_name();
|
||||
String szRoom = LPortal::FindNameAfter(pNode, 6);
|
||||
|
||||
// create a new LRoom to exchange the children over to, and delete the original empty
|
||||
LRoom * pNew = memnew(LRoom);
|
||||
pNew->set_name(szRoom);
|
||||
add_child(pNew);
|
||||
|
||||
int nChildren = pNode->get_child_count();
|
||||
|
||||
for (int n=0; n<nChildren; n++)
|
||||
{
|
||||
// reverse count
|
||||
int c = nChildren - n - 1;
|
||||
|
||||
Node * pChild = pNode->get_child(c);
|
||||
pNode->remove_child(pChild);
|
||||
|
||||
// add the child to the new node
|
||||
pNew->add_child(pChild);
|
||||
}
|
||||
|
||||
// all moved .. now finally delete the empty
|
||||
remove_child(pNode);
|
||||
pNode->queue_delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void LRoomManager::_bind_methods()
|
||||
{
|
||||
|
||||
}
|
49
lroom_manager.h
Normal file
49
lroom_manager.h
Normal file
@ -0,0 +1,49 @@
|
||||
// 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.
|
||||
|
||||
/* room_manager.h */
|
||||
#ifndef LROOM_MANAGER_H
|
||||
#define LROOM_MANAGER_H
|
||||
|
||||
/**
|
||||
@author lawnjelly <lawnjelly@gmail.com>
|
||||
*/
|
||||
|
||||
#include "scene/3d/spatial.h"
|
||||
|
||||
class LRoomManager : public Spatial {
|
||||
GDCLASS(LRoomManager, Spatial);
|
||||
|
||||
public:
|
||||
LRoomManager();
|
||||
|
||||
// convert empties and meshes to rooms and portals
|
||||
void Convert();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
void Convert_Rooms();
|
||||
bool Convert_Room(Node * pNode);
|
||||
void Convert_Portals();
|
||||
};
|
||||
|
||||
#endif
|
20
register_types.cpp
Normal file
20
register_types.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
/* register_types.cpp */
|
||||
|
||||
#include "register_types.h"
|
||||
|
||||
#include "core/class_db.h"
|
||||
#include "lroom.h"
|
||||
#include "lroom_manager.h"
|
||||
#include "lportal.h"
|
||||
|
||||
|
||||
void register_lportal_types() {
|
||||
|
||||
ClassDB::register_class<LRoom>();
|
||||
ClassDB::register_class<LRoomManager>();
|
||||
ClassDB::register_class<LPortal>();
|
||||
}
|
||||
|
||||
void unregister_lportal_types() {
|
||||
//nothing to do here
|
||||
}
|
5
register_types.h
Normal file
5
register_types.h
Normal file
@ -0,0 +1,5 @@
|
||||
/* register_types.h */
|
||||
|
||||
void register_lportal_types();
|
||||
void unregister_lportal_types();
|
||||
/* yes, the word in the middle must be the same as the module folder name */
|
Loading…
Reference in New Issue
Block a user