Jump to content

Our entity system


Josh
 Share

Recommended Posts

Can you provide examples why our entity system is better than other engines that force you to use difficult math or scene graph commands? I am making a case why our engine is easier to program with. I would like to show code needed to do something with another engine and compare it to ours, and I am not that familiar with other approaches. Thanks!

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Making the camera move and rotate in all directions is very difficult without entities.

You can try to make a simple demo where you move with WASDEC keys (E and C for up/down), and rotate with numpad 845671 keys.

Then add the Shift key as modifier to move and rotate on global axis.

 

That's few lines of code in entity system, and hundreds of lines of code in traditional trigonometry math, and even more in quaternion math since trigometric rotations will not work in the end due to the gimbal lock problem.

 

 

 

Another very difficult problem without entity system is to make the camera orbit around another object so that it always faces the orbited object.

Ryzen 9 RX 6800M ■ 16GB XF8 Windows 11 ■
Ultra ■ LE 2.53DWS 5.6  Reaper ■ C/C++ C# ■ Fortran 2008 ■ Story ■
■ Homepage: https://canardia.com ■

Link to comment
Share on other sites

What kind of engine wouldn't have an entity system that did that for you? Can you name any engine where you have to calculate the math yourself like you describe?

 

Most importantly, why do you find Leadwerks easier to program with than Torque and Unity, or other systems?

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

I find LE easier to program in than other engines that use their own scripting languages. I'm not a big fan of having to learn some proprietary language that won't help my programming skills outside of that engine. This is why I gravitate towards engines that are C/C++. The fact that is supports so many other languages is a bonus also, but personally I only use C/C++ and Lua. 2 very industry standard languages. I generally won't touch an engine that implements it's own language because I view it as a waste of time. That's just my view though.

Link to comment
Share on other sites

No other engines out there derive from Blitz3D, so they had to figure out somekind of coding interface, but most of them are horrible.

The inventor of the entity system is a god (Mark Sibly), and that's why nobody can beat his invention.

 

I had many times in mind actually to write a decent entity system wrapper for some other engines I own, but as long they lack of technical features compared to LE, there's really no point to do that. The only exception I could consider is Ogre3D or Irrlicht since they have one benefit over LE: low-end support. But since that's coming to LE too, I rather focus on supporting Leadwerks with cross-platform technologies.

 

Torque is known as the ultimate spaghetti code engine, and they suggest you should use their ugly scripting language instead.

 

Unity has no C++ support, so it's kinda useless for a real programmer.

 

Leadwerks Engine requires the most minimal amount of coding due to its entity system, and the coding syntax is exactly the same in any of the 350 languages you can use with Leadwerks Engine. I would like to see more officially supported headers for each language, but it seems nobody cares about love to languages anymore, and all newbies just use C#, Java and .NET. What I would really like to see is some dedicated ADA, Fortran, COBOL, Oberon, Prolog language lovers. They would truly make this community outstanding.

Ryzen 9 RX 6800M ■ 16GB XF8 Windows 11 ■
Ultra ■ LE 2.53DWS 5.6  Reaper ■ C/C++ C# ■ Fortran 2008 ■ Story ■
■ Homepage: https://canardia.com ■

Link to comment
Share on other sites

I am often told that the LE API is much easier to use and doesn't require complicated math. I would like to have a concrete example so I can show why this is, instead of me just making s statement with nothing to back it up.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Personally it's probably been 2 years since I've used any other engine so I can't remember the exact details of them, but in general I know if a few I would need to get direction vectors for movement or for pointing something at something else. Finding distances was left up to me.

 

I simply remember having to do math with the engines I've tried, and with LE I can make an entire game without having to do any math myself at all. Truevision comes to mind for some reason on being bigger on math, but I can't recall the details and I don't really want to relive that library again.

Link to comment
Share on other sites

Well I think Ogre's API is pretty tough to use. It's kind of a polar opposite to Leadwerks. The reason I can't give you any specifics is because I could never figure anything out. Not even how to make a render window. Now either Ogre is really complicated, I'm dumb, or maybe it's a combination of the 3. But I could make a render window within 10 minutes of Leadwerks. Although I'm sure some people could do it within 5.

Core I5 2.67 / 16GB RAM / GTX 670

Zbrush/ Blender / Photoshop CS6 / Renoise / Genetica / Leadwerks 3

Link to comment
Share on other sites

The Leadwerks engine is incredibly similar in style to the Blitz3D language:

 

int main(int argc, char** argv)
{
Initialize();
Graphics(640, 480);
CreateWorld(); 
TCamera cam = CreateCamera();

while(!KeyHit(KEY_ESCAPE))
{
	TurnEntity(cam,Vec3(mx,my,0));
	UpdateWorld();
	RenderWorld();
	Flip();
}
return Terminate();
}

 

translates to:

 

Graphics3D 640,480
Global cam = CreateCamera()

While Not KeyHit(88)

Cls
TurnEntity(cam,mx,my,0)
UpdateWorld
RenderWorld
Flip

Wend

End

AMD Phenom 9850 (X4 2.5GHz) | 1066MHz CL5 GEIL Black Dragon (800MHz) | Powercolor ATI 4870 1GB | DFI 790FXB-M2RSH | 24" 1920x1200 | Windows Vista 64-bit Ultimate

Link to comment
Share on other sites

I am still a complete newbie to Leadwerks and programming. I have only really spent most of my past scripting in other game engines ( Quake / Neverwinter ) and before that cramming programs into 64k on the C64. For me the power of the entity system is that yesterday I created a new Skybox system which features a moving sun tied to a directional light and cloud sphere. All done with entities and no real maths at all.

BMax 1.38 * Leadwerks 2.4 * Unity 3.0 Pro * Intel Core 2 Duo 6600 - Win7 64bit - ATI 4870

Link to comment
Share on other sites

  • 3 weeks later...

- There's no type-casting

- No need for vector math-fu between different spaces, gimbal locks, ..

- Very elegant and simple but complete command set allows for easy and quick complex 3D operations an all entities (lights, meshes, bodies, .. ) while the code remains very minimal and very readable (i'm with previous user on Ogre3d:)

 

..just a few things that come to mind.. i've been using the engine only for 3 days but it already feels like we're pals from way back :) . It reminds me of way back when i got started with programming in turbo pascal :)

 

anyway Josh, kudos for very sleek design! I love it..

Link to comment
Share on other sites

I like the C++ version of the engine even more. You can do stuff like this in C++ or Lua:

 

a = b + Vec3(1)

 

entity.SetPosition(1,2,3)

entity.SetPosition(Vec3(1,2,3))

 

b = TFormPoint(1,2,3,src,dst)

b = TFormPoint(Vec3(1,2,3),src,dst)

 

p = Plane(1,0,0,1)

p = Plane(Vec3(1,0,0),Vec3(-1,0,0))

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

A big one for me is that we don't have to calc the velocity vector. I'm working in Unity and you have to calc the velocity vector by subtracting your vector from another one then normalize it, then multiple by your speed. It's much easier to just pass a value to UpdateController() or MoveEntity() instead of doing that. Also easier to do rotations in this entity system compared to Unity because.

Link to comment
Share on other sites

I don't have a comparison to other engines, but one thing I do like about the Leadwerks entity system design is that it's very easy to use, it's intuitive, and it's complete*.(In my limited experiences, I've not found something that wasn't possible with our current command set yet.)

 

Right now, I am working on some network stuff and I want to design a Lag Compensation system as described in the Source Multiplayer Networking article. With our current entity command set, the task is so trivial it's almost laughable how easy it is to implement with Leadwerks. For that task, all it takes is use of SetEntityMatrix, GetEntityMatrix, SetEntityCallback, GetEntityUserData, and SetEntityUserData. From there, it's just a matter of putting together the logic for storing object history in a circular buffer design.

 

With that system in place, I can now just use the other engine commands for picking and ray casting without having to do any real 'work' myself when it comes to math or restoring objects to a previous position. I will have to interpolate between two of those matrix states, but that's trivial since Linear Interpolation will be "good enough" for my tech demo. Basically, I can just focus on implementing logic rather than having to figure out everything that comes up to that point. It's great!

 

This is a rough draft example of what I am talking about. This is basically the code I ended up with to see if implementing lag compensation would be feasible or not with Leadwerks.

#include <engine.h>

float global_start_time = 0;

const unsigned char MAX_HISTORY_STATES = 125;

bool updateHistory = true;
bool update = true;

TEntity camera;

struct THistory
{
unsigned char viewHistoryIndex;
unsigned char realHistoryIndex;
unsigned char maxHistoryIndex;
float times[MAX_HISTORY_STATES];
TVec16 history[MAX_HISTORY_STATES];
};

void __stdcall FreeEntityCallback(TEntity entity)
{
THistory * pEntityHistory = reinterpret_cast<THistory *>(GetEntityUserData(entity));
SetEntityUserData(entity, 0);
delete pEntityHistory;
}

void __stdcall UpdateEntityCallback(TEntity entity)
{
THistory * pEntityHistory = reinterpret_cast<THistory *>(GetEntityUserData(entity));
if(pEntityHistory && updateHistory)
{
	pEntityHistory->history[pEntityHistory->viewHistoryIndex] = GetEntityMatrix(entity);
	pEntityHistory->times[pEntityHistory->viewHistoryIndex] = AppTime() - global_start_time;
	printf("pEntityHistory->viewHistoryIndex: %i\n", pEntityHistory->viewHistoryIndex);
	pEntityHistory->viewHistoryIndex++;
	if(pEntityHistory->maxHistoryIndex != MAX_HISTORY_STATES)
		pEntityHistory->maxHistoryIndex++;
	if(pEntityHistory->viewHistoryIndex == MAX_HISTORY_STATES)
	{
		updateHistory = false;
		update = false;
	}
}
}

int __stdcall CleanupEntity(TEntity entity, byte* extra) 
{
printf("Free: %s\n", GetEntityKey(entity, "name"));
FreeEntity(entity);
return 1;
}

void TrackEntity(TEntity entity)
{
THistory * pEntityHistory = reinterpret_cast<THistory *>(malloc(sizeof(THistory)));
memset(pEntityHistory, 0, sizeof(THistory));
SetEntityUserData(entity, (byte *)pEntityHistory);
SetEntityCallback(entity, (byte *)FreeEntityCallback, ENTITYCALLBACK_FREE);
SetEntityCallback(entity, (byte *)UpdateEntityCallback, ENTITYCALLBACK_UPDATE);
}

void pickedAddForce(const TPick& pick)
{
TBody body = GetParent(pick.entity);
TVec3 campos = EntityPosition(camera);
TVec3 force = Vec3(pick.X - campos.X, pick.Y - campos.Y, pick.Z - campos.Z);
Normalize(force);
force.X *= 30.0f;
force.Y *= 30.0f;
force.Z *= 30.0f;
AddBodyForceAtPoint(body,force,Vec3(pick.X, pick.Y, pick.Z));
}

int main(int argc, char * argv[])
{
Initialize();

Graphics(1024, 768);

TWorld world = CreateWorld();

camera = CreateCamera();
SetEntityKey(camera, "name", "camera");
PositionEntity(camera, Vec3(0, 0, -5));

TEntity light = CreateDirectionalLight();
SetEntityKey(light, "name", "light");
RotateEntity(light, Vec3(45, 45, 0));

TBuffer buffer = CreateBuffer(GraphicsWidth(), GraphicsHeight(), BUFFER_COLOR | BUFFER_DEPTH | BUFFER_NORMAL);

TBody cube_body = CreateBodyBox();
SetEntityKey(cube_body, "name", "cube_body");
SetBodyMass(cube_body, 1);

TMesh cube_mesh = CreateCube();
SetEntityKey(cube_mesh, "name", "cube_mesh");
EntityParent(cube_mesh, cube_body);

PositionEntity(cube_body, Vec3(0, 3, 0));
RotateEntity(cube_body, Vec3(45, 45, 45));
TrackEntity(cube_body);

TBody ground_body = CreateBodyBox(10, 0.1f, 10);
SetEntityKey(ground_body, "name", "ground_body");

TMesh ground_mesh = CreateCube();
SetEntityKey(ground_mesh, "name", "ground_mesh");

ScaleEntity(ground_mesh, Vec3(10, 0.1f, 10));
EntityParent(ground_mesh, ground_body);
PositionEntity(ground_body, Vec3(0, -1, 0));

Collisions(1, 1, 1);
EntityType(cube_body, 1);
EntityType(ground_body, 1);

UpdateAppTime();
global_start_time = AppTime();

TEntity e = cube_body;

while(!KeyDown(KEY_ESCAPE))
{
	SetBuffer(buffer);
	RenderWorld();

	SetBuffer(BackBuffer());
	RenderLights(buffer);

	DrawText(0, 0, "update: %i", update);

	Flip();

	if(MouseHit(1))
	{
		TPick picked;
		if(CameraPick(&picked, camera, Vec3((flt)MouseX(), (flt)MouseY(), 1000), 0, 0, 0))
		{
			if(!update)
			{
				MessageBoxA(0, GetEntityKey(picked.entity, "name", ""), "", 0);
			}
			else
			{
				pickedAddForce(picked);
			}
		}
	}

	if(!update)
	{
		if(KeyDown(KEY_LEFT) || KeyHit(KEY_UP))
		{
			THistory * pEntityHistory = reinterpret_cast<THistory *>(GetEntityUserData(e));
			if(pEntityHistory)
			{
				if(pEntityHistory->viewHistoryIndex > 0)
					pEntityHistory->viewHistoryIndex--;
				SetEntityMatrix(e, pEntityHistory->history[pEntityHistory->viewHistoryIndex]);
				printf("[%i] History from time: %.0f\n", pEntityHistory->viewHistoryIndex, pEntityHistory->times[pEntityHistory->viewHistoryIndex]);
			}
		}
		else if(KeyDown(KEY_RIGHT) || KeyHit(KEY_DOWN))
		{
			THistory * pEntityHistory = reinterpret_cast<THistory *>(GetEntityUserData(e));
			if(pEntityHistory)
			{
				if(pEntityHistory->viewHistoryIndex < MAX_HISTORY_STATES - 1)
					pEntityHistory->viewHistoryIndex++;
				SetEntityMatrix(e, pEntityHistory->history[pEntityHistory->viewHistoryIndex]);
				printf("[%i] History from time: %.0f\n", pEntityHistory->viewHistoryIndex, pEntityHistory->times[pEntityHistory->viewHistoryIndex]);
			}
		}
	}

	if(update)
	{
		UpdateAppTime();
		UpdateWorld();
	}
}
ForEachEntityDo((byte*)CleanupEntity);
return 0;
}

 

Of course, that code is going to change greatly since it was a first attempt, but it's just an example of what I am talking about.

Link to comment
Share on other sites

I used quite a lot amount of engines in last year and just a few have complicated math.

The main difference is the amount of code to do something, as for example in torque i was obly to write 10 line of code to get the controller and 3 to update it, wich is almost 2 line in leadwerks.

Unity isn't much harder then Leadwerks so just avoid to speak about it.

Another good difference is that in leadwerks we could create a game by scratch but still be able to use framew(e/o)rk, that's help really much.

I haven't really never used the tool wich are in leadwerks too, i prefer to code everything by myself.

 

3DVIA Virtools is also easy, just a little confusing, but also buggy.

 

So porbably i prefer leadwerks because leave me the possibility to write code easly, without a bad own scrript lagnuages, with a big amount of own command (so i don't need to lose my time to check in any class of the engine to understand..).

Intel Corei7-6700, NVIDIA GeForce GTX 980, 32GB DDR4, W-10.

Link to comment
Share on other sites

  • 2 months later...

No other engines out there derive from Blitz3D, so they had to figure out somekind of coding interface, but most of them are horrible.

The inventor of the entity system is a god (Mark Sibly), and that's why nobody can beat his invention.

 

I totally agree!

 

The only exception I could consider is Ogre3D or Irrlicht since they have one benefit over LE: low-end support. But since that's coming to LE too, I rather focus on supporting Leadwerks with cross-platform technologies.

 

Hey Lumooja, that's a very interesting part. When and how this will come into LE? What does that exactly mean? Will it run on older systems (pre SM 3.0) as well? That's a very, very important thing for me and the only reason why I still prefer Irrlicht over LE.

Triassic Games

Link to comment
Share on other sites

This is what I am used to from 3drad/3impact which is very easy (angelcode c-like syntax)

http://www.3drad.com/Script_reference.htm

 

Alltho I'm very new to LE, the LUA scripting of LE is very simple and easy to understand, even tho we might lack som docs.

turn, setposition, setscale, positionentity, copyentity... etc. straightforward and great.

HP Omen - 16GB - i7 - Nvidia GTX 1060 6GB

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...