//========================================================================================================
// Plugin for EQUINOX-3D
//
// Simple plugin code example that:
// - Creates a Model
// - Adds a Mesh to it, and adds 3 Vertices to the Mesh
// - Adds a PolyGroup to the Mesh
// - Adds a single triangle to the PolyGroup
// - Adds the Model to the main Scene and calls the required update functions
//
// AUTHOR:  Gabor Nagy
// DATE:    2025-Sep-11 01:52:50
//
// You are free to modify and redistribute this code.
// To avoid cluttering the UI, example plugins are not loaded by default, when EQUINOX-3D is started.
// You can manually load them from the Plugin manager, or you can edit your Plugins/Plugins.list file,
// and comment out (e.g. with a trailing "#") or remove the "Load = FALSE;" line for a plugin.
//
// EQUINOX-3D and contained libraries are Copyright (C) 2014-2026 by Gabor Nagy. All rights reserved.
//========================================================================================================
#include <assert.h>
#include <stdio.h>

#include <ELists.h>
#include <EMalloc.h>
#include <EPlugins.h>

#include <E3D/Scene.h>

#include <E3D/UI/Panel.h>
#include <E3D/UI/StatusPanel.h>

#include <Xe/PushButton.h>

char*             E_PluginInfo = "Simple Polygon example";    // This shows up in the information dialog box of the plugin manager (System->Plugins)

static EPlugin*   _Plugin = NULL;

static XeDWidget	  _Buttons[2];static int  _Buttons_Count = 0;  // We collect buttons into an array here, so we can easily free them when the plugin exits


// Don't forget to add this to Plugins/Plugins.list, so EQUINOX-3D actually loads your plugin!
/*
{
Name	= Examples/Simple Polygon;
Object	= Examples/SimplePolygon;
}
*/


//========================================================
// ToolBar icon and menu button callback handler
//========================================================
static void _CB(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*  LScene = E3d_Scene;  // E3d_Scene is one of the few global variables. It points to the current Scene (the highest level container of the world) used by the application (EQUINOX-3D)
	E3dModel*  LModel;E_New(E3dModel, LModel);if(LModel == NULL)
	{
		printf("%s() %s:%d  Could not create Model\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
		return;
	}

	EObject_SetName(LModel, E3dScene_GetUniqueNodeName(LScene, "Simple Model"), FALSE);  // This will guarantee that our new Model has a unique name. Handy, if you add a bunch of these, by clicking on the button many times.
//EObject_SetName(LModel, EStrDup(LScene, "Simple Model"), FALSE);                     // This will simply set the name

	E3dMesh*  LMesh;
	E_New(E3dMesh, LMesh);
	E3dModel_ObjectAppend(LModel, (E3dObject*)LMesh, FALSE);

// We could also the above two steps this with just one call.
// Note that this is a function that takes a dynamic class pointer, so you can pass classes defined in plugins as well.
//
//E3dModel_ObjectAddNew(LModel, E3dMesh_class);


// Let's get a unique name for the Mesh too. Note that this is an E3dObject derivative, which is distintct from E3dModel (E3dNode derivative), so name uniqueness is checked separately.
// It's just a convention to have capitalized names for Models and lower case names for Objects (Lights, Cameras, Geometries, etc.)
//
	EObject_SetName(LMesh, E3dScene_GetUniqueObjectName(LScene, "Simple Mesh"), FALSE);


// Add 16 Vertices to the Mesh and set their positions (the status flags are initialized to E3dVtxDEFAULT by E3dMesh_VerticesAdd)
// We'll make this a concave pentagon, just to show off that EQX has no trouble with such things.
// Polygons can even have holes!
//
	E3dVertex*  LVertices = E3dMesh_VerticesAdd(LMesh, 16);E3dVertex*  LVertex = LVertices;const E3dVertexSize  LVertexSize = LMesh->VertexSize;

// We could just do LVertex->Position.X =, LVertex->Position.Y =..., but this is more convenient
// The '_s' suffix of the setter macro just means "struct", (as opposed to '_p', meaning pointer to struct)
//
	LVertex->Position = E3dCoord3_s( 1.0 - 1.1,  0.000, 0.5);EStruct_Inc(LVertex, LVertexSize);
	LVertex->Position = E3dCoord3_s( 0.5 - 1.1,  0.866, 0.5);EStruct_Inc(LVertex, LVertexSize);
	LVertex->Position = E3dCoord3_s(-0.5 - 1.1,  0.866, 0.5);EStruct_Inc(LVertex, LVertexSize);
	LVertex->Position = E3dCoord3_s(-1.0 - 1.1,  0.000, 0.5);EStruct_Inc(LVertex, LVertexSize);
	LVertex->Position = E3dCoord3_s(-0.5 - 1.1, -0.866, 0.5);EStruct_Inc(LVertex, LVertexSize);
	LVertex->Position = E3dCoord3_s( 0.5 - 1.1, -0.866, 0.5);EStruct_Inc(LVertex, LVertexSize);

	LVertex->Position = E3dCoord3_s( 1.0 + 1.1,  0.000, 0.5);EStruct_Inc(LVertex, LVertexSize);
	LVertex->Position = E3dCoord3_s( 0.5 + 1.1,  0.866, 0.5);EStruct_Inc(LVertex, LVertexSize);
	LVertex->Position = E3dCoord3_s(-0.5 + 1.1,  0.866, 0.5);EStruct_Inc(LVertex, LVertexSize);
	LVertex->Position = E3dCoord3_s(-1.0 + 1.1,  0.000, 0.5);EStruct_Inc(LVertex, LVertexSize);
	LVertex->Position = E3dCoord3_s(-0.5 + 1.1, -0.866, 0.5);EStruct_Inc(LVertex, LVertexSize);
	LVertex->Position = E3dCoord3_s( 0.5 + 1.1, -0.866, 0.5);EStruct_Inc(LVertex, LVertexSize);

	LVertex->Position = E3dCoord3_s( 0.125 + 1.1,  0.555, 0.5);EStruct_Inc(LVertex, LVertexSize);
	LVertex->Position = E3dCoord3_s( 0.125 + 1.1,  0.305, 0.5);EStruct_Inc(LVertex, LVertexSize);
	LVertex->Position = E3dCoord3_s(-0.125 + 1.1,  0.305, 0.5);EStruct_Inc(LVertex, LVertexSize);
	LVertex->Position = E3dCoord3_s(-0.125 + 1.1,  0.555, 0.5);EStruct_Inc(LVertex, LVertexSize);

// Finally, add the Polygons
// You can also directly manipulate the LPolyGroup->Polygons array, if you need high performance.
// Just make sure LPolyGroup->Polygons_Count is valid, when you are done.
//

// Add the Vertices to the Polygon (VertexNodes, to be exact).
// EQUINOX-3D, as most advanced 3D modeling and CAD systems, use a shared Vertex array on the Mesh, of the Vertices. Mesh Vertices have at least a Position attribute, but they may have additional ones.
// This has 2 main advantages, vs. storing positions on the Polygon Vertices (a.k.a. "disconnected Polygon soup", yikes!):
// - You save memory (sometimes, a lot) when several Polygons share a Vertex ('high valence' Vertex)
// - You get *exact* Vertex identity checks, by comparing integer Vertex indices, rather than the ugly and expensive business of comparing float/double triplets (thou shalt not compare floats for identity, lest you will be cursed with bugs, due to rounding errors!)

// A VertexNode of a Polygon, always has an index to the appropriate Mesh Vertex. It may have any number of additional Vertex attributes.
// EQUINOX-3D allows a completely flexible system for Vertex attributes, so the size of a VertexNode may vary.
// LPolyGroup->VertexNodeSize indicates how many bytes each VertexNode is on a given PolyGroup.
// For example, each VertexNode on the Polygons of a PolyGroup may have a vertex index, a normal, a tangent, 3 sets of texture coordinates and a pair of U,V coordinates (not to be confused with texture coordinates (S,T)
// This allows for 3 different texture mappings on this PolyGroup, plus a set of UV coordinats that may be used to generate one or more of the texture coorinate sets (UV mapping), or to distribute grass blades on the surface, etc.
// Each PolyGroup on a Mesh may have a different set of VertexAttributes.
//

// When you create a PolyGroup, its Vertex attribute list defaults to just a Vertex index and a vertex flags (no normals, texture coordinates (ST), tangents etc.)
// Additional attributes would need to be added via E3dPolyGroup_VertexAttributeAdd(().
// This sample code only specifies Vertex indices.
	E3dPolyGroup*  LPolyGroup = E3dMesh_PolyGroupAdd(LMesh);

// Add 2 Polygons to the PolyGroup
	E3dPolygon*  LPolygon = E3dPolyGroup_AddPolygons(LPolyGroup, 2);

// Now, allocate and set the Polygon's VertexNodes
// The two branches below create equivalent Polygons
//
	if(0)
	{
// Easy way: use the E3dPolygon_SetVa() convenience function to create the Polygons.
// Note that this will only set the Vertex index. It's great for quick geometry prototyping or when you don't need texture coordinates, vertex normals etc.
//

// Create a 6-sided Polygon (hexagon)
		E3dPolygon_SetVa(LPolygon, LPolyGroup->VertexNodeSize,  6,  0, 1, 2, 3, 4, 5);
		LPolygon++;

// Create another hexagon with a 4-sided hole in it.
// A Polygon's exterior contour is defined with a counter-clockwise vertex order, while holes are defined with a clock-wise Vertex order.
// Hole definitions begin with a negative number, indicating the number of Vertices in tha hole. (-4). This saves memory by not requiring another field in E3dVertexNode to indicate the beginning of a hole.
		E3dPolygon_SetVa(LPolygon, LPolyGroup->VertexNodeSize,  11,  6, 7, 8, 9, 10, 11,  -4,  15, 14, 13, 12);    // 6 Vertices in the exterior contour and 4 in the hole
	}
	else
	{
// More advanded way: manually allocate and set the Polygon's VertexNodes
// We step through VertexNodes with EStruct_Inc(LVertexNode, LPolyGroup->VertexNodeSize) macro, instead of just LVertexNode++ because they may have any size that is not known at compile time (the user may add new VertexAttributes to a PolyGroup)
//
		{
			const EIndex  LVertexNodeSize = LPolyGroup->VertexNodeSize;

			{
				const EIndex    LNVertexNodes = 6;
				E3dVertexNode*  LVertexNodes = (E3dVertexNode*)EMallocClr(LVertexNodeSize * LNVertexNodes);  // Allocate VertexNodes and clear them to 0, so we don't leave things uninitialized

				E3dVertexNode*  LVertexNode = LVertexNodes;
				LVertexNode->VertexID = 0;EStruct_Inc(LVertexNode, LVertexNodeSize);
				LVertexNode->VertexID = 1;EStruct_Inc(LVertexNode, LVertexNodeSize);
				LVertexNode->VertexID = 2;EStruct_Inc(LVertexNode, LVertexNodeSize);
				LVertexNode->VertexID = 3;EStruct_Inc(LVertexNode, LVertexNodeSize);
				LVertexNode->VertexID = 4;EStruct_Inc(LVertexNode, LVertexNodeSize);
				LVertexNode->VertexID = 5;EStruct_Inc(LVertexNode, LVertexNodeSize);

				LPolygon->VertexNodes = LVertexNodes;
				LPolygon->NumOfVertexNodes = LNVertexNodes;    // Total number of VertexNodes
				LPolygon->NumOfVertices = 6;                   // Total number of Vertices
				LPolygon->NumOfExteriorVertices = 6;
				LPolygon++;
			}

			{
				const EIndex    LNVertexNodes = 11;
				E3dVertexNode*  LVertexNodes = (E3dVertexNode*)EMallocClr(LVertexNodeSize * LNVertexNodes);  // Allocate VertexNodes and clear them to 0, so we don't leave things uninitialized

				E3dVertexNode*  LVertexNode = LVertexNodes;
				LVertexNode->VertexID = 6;EStruct_Inc(LVertexNode, LVertexNodeSize);
				LVertexNode->VertexID = 7;EStruct_Inc(LVertexNode, LVertexNodeSize);
				LVertexNode->VertexID = 8;EStruct_Inc(LVertexNode, LVertexNodeSize);
				LVertexNode->VertexID = 9;EStruct_Inc(LVertexNode, LVertexNodeSize);
				LVertexNode->VertexID = 10;EStruct_Inc(LVertexNode, LVertexNodeSize);
				LVertexNode->VertexID = 11;EStruct_Inc(LVertexNode, LVertexNodeSize);

				LVertexNode->VertexID = -4;EStruct_Inc(LVertexNode, LVertexNodeSize);
				LVertexNode->VertexID = 15;EStruct_Inc(LVertexNode, LVertexNodeSize);
				LVertexNode->VertexID = 14;EStruct_Inc(LVertexNode, LVertexNodeSize);
				LVertexNode->VertexID = 13;EStruct_Inc(LVertexNode, LVertexNodeSize);
				LVertexNode->VertexID = 12;EStruct_Inc(LVertexNode, LVertexNodeSize);

				LPolygon->VertexNodes = LVertexNodes;
				LPolygon->NumOfVertexNodes = LNVertexNodes;    // Total number of VertexNodes, inlcuding contour separators
				LPolygon->NumOfVertices = 6 + 4;               // Total number of Vertices (contour separators are "pseudo Vertices", so they are not counted)
				LPolygon->NumOfExteriorVertices = 6;
				LPolygon++;
			}

			LPolyGroup->Polygons.Count = LPolygon - LPolyGroup->Polygons.Items;

E3dPolyGroup_PrintInfo(LPolyGroup, 0,  LMesh->Vertices.Items, LMesh->Vertices.Count, LMesh->VertexSize,  NULL, 0);
		}
	}

// Be safe and do a full update to the internal accelerators / caches of LMesh
// For performance reasons, you can change a Mesh to your heart's content, then call update once, rather than it being called for every Vertex or Polygon change.
//
	E3dObject_UpdateForDisplay((E3dObject*)LMesh,  E3dUpdateALL, E3dCallALL,  LScene);


// Add the new Model to the Scene. Note that you can build up a hierarchy before adding it to the Scene (or without ever adding it).
//
	E3dScene_NodeHrcAdd(LScene, (E3dNode*)LModel, E3dCallSCENE_CALLBACKS);


// This needs to be called, so instancing will work correctly. It's separate from E3dScene_NodeHrcAdd, for efficiency reasons
//
	E3dScene_ObjectsUpdateModelList(LScene);


// Now, call the automatic layout routine for the Schematic views in EQUINOX-3D (we added a new Model)
// TRUE means we want to automatically frame the Cameras of Schemanic views to show all hierarchies
	E3dScene_LayoutOnSchematics(LScene, TRUE);


// Finally, redraw 3D Windows that are affected by this change.
// EQUINOX-3D has a high-performance rendering system that is optimized to only redraw things that actually changed.
// These flags will tell it that we changed the scene graph, so schematic views will have to be redrawn.
// We also changed a Mesh, so 3DWindows will also need to be redrawn.
//
	E3dWindows_RedrawByChangeFlags(E3d_Scene, E3dChgHIERARCHY|E3dChgMeshSHAPE);

	E3dp_PrintMessage(ELogMESSAGE, 5000, "Added Model to the Scene");  // Message type, auto-removal timeout (ms), vararg list (like printf)
}


//========================================
// Entry point of the plugin
//========================================
int E_PluginInit(EPlugin* PPlugin)
{
	_Plugin = PPlugin;

// Use default plugin icon
//
// Location ("Create" tool panel, tooltip, etc.)
	XePushButton_AddImg3(_Buttons[_Buttons_Count++], Plugin, "Tool->Create", "Simple Polygon example", '\0', NULL, NULL, FALSE, "Simple Polygon example. Create a PolyGroup with 2 Polygons", _CB, (EPointer)0);

// Add a button to the 'Create' menu as well
//
	_Buttons[_Buttons_Count++] = XePushButton_Add(Xe_App, "Menu->Create", "Simple Polygon example", '\0', NULL, NULL, FALSE, NULL, _CB, (EPointer)0);

	return(E_SUCCESS);
}


//========================================
// Exit method of the plugin
//========================================
int E_PluginExit(const EBool PExiting)
{
	if(!PExiting || Xe_GUIFreeOnExit)
	{
		int  LI = 0;for(;LI < _Buttons_Count;LI++) { XeItemFree(_Buttons[LI]); }
		_Buttons_Count = 0;
	}

	return(E_SUCCESS);
}
