Jump to content
  • entries
    945
  • comments
    5,899
  • views
    930,137

Some sample LE3 code


Josh

15,070 views

 Share

Here's some LE3 code for a simple program. The Graphics / Window stuff is not worked out 100%, and it's a little tricky because of the different operating systems and different kinds of windows.

 

I think we'll see a little more consistency across various languages with the LE3 syntax. It was suggested that the C syntax use the following scheme:

verb-class-noun

SetEntityPosition()
GetMaterialTexture()

I agree with this suggestion.

 

SetGraphicsDriver( OpenGLGraphicsDriver() );
CreateGraphics(1024,768,4);

World* world = CreateWorld();

//Load a shader
Shader* shader = LoadShader("Shaders/minimal.shader");

//Create a material;
Material* mat = CreateMaterial();    
mat->SetShader(shader);
mat->SetColor(1,0,0,1);

//Create a box;
Mesh* box = CreateMeshBox(1,1,1);
box->SetMaterial(mat);

Camera* camera = CreateCamera();
camera->SetClearColor( Vec4(0,1,0,1) );

box->SetPosition(0,0,-2,false);
float yaw = 0.0;

while (!window->Closed())
{        
   yaw++;
   box->SetRotation(yaw,0,0,false);
   camera->Render();
   window->Flip();
}

 Share

76 Comments


Recommended Comments



That's an idea. It's a pretty fundamental change in design. Typically, you would code in such a way that your program can handle missing files, etc. :)

 

Yes. Listen to Rick..

try and catch will handle those situation greatly

Link to comment

I thought the demonstrated syntax was C, I wonder how you get classes in there Lumooja/Roland.

Besides, the "object.Load" approach is really bad, as Josh demonstrated.

 

Also, I also want to precise I would use pointers and not objects, but Josh beat me to that too.

Lastly, I don't know why you fear constructors, Lumooja. Setting the graphics properties after its creation implies that it exists but is empty, which is strange in my opinion.

 

Here's my ideal code syntax for C++, not C (I feel Josh's one is perfect for C).

Graphics* graphics;
World* world;
Mesh* box;
Camera* camera;

graphics = new Graphics(OpenGLGraphicsDriver());
graphics->Create(1024, 768, 4);

world = new World();

box = new Box(1, 1, 1); // Box : Mesh
box->GetMaterial()->SetShader(new Shader("Shaders/minimal.shader"));
box->GetMaterial()->SetColor(new Color(255, 0, 0, 255));
// Basically Color is a Vector4 represented as bytes

camera = new Camera();
camera->SetClearColor(new Color(0, 255, 0, 255));

box->SetPosition(0, 0, -2, false);

float yaw = 0.0f;

while (!window->IsClosed())
{        
   yaw++;
   box->SetRotation(yaw, 0, 0, false);

   camera->Render();
   window->Flip();
}

 

Just want to straighten this up.

I have absolutely nothing against pointers (its not possible to make a program without them)

and of course its the best thing to have the needed arguments in a constructor. This makes it

impossible to create an object without supplying the needs for it.

 

I do think this misunderstanding comes from the design of LEO, which I originally did as a thin

wrapper of the C-API. My own originally design had no Create-methods. All was in the constructors,

this was changed on request, I cant quite remember the actual arguments for this, but I have a

feeling it was something like fear of pointers and having to delete the objects. But thats history.

Just wanted to tell the story behind the Create's in LEO as an explanation of why it was done that way.

 

I do think the understanding of C++ now has increased at Leadwerks and cant see why constructor

arguments should be used. The great thing about the constructor arguments is that, if an object

needs another object exist, the user is forced to give it in the constructor and does not even has

the possibility of forget to give access to the other object.

 

So ... pointers and constructor arguments is the way to go, and I have never stated anything else.

 

Cheers

Roland :)

Link to comment

The -> doesn't seem handy at all to me. The keys are not next to each other. Why not the dotnet way: box.GetMaterial() or the lua way box:GetMaterial()?

 

What is the asterisk (*) doing after each datatype?

Specifying vector 2, 3 or 4 is no longer necessary?

 

 

For the rest everything looks as clean as always.

Link to comment

The -> doesn't seem handy at all to me. The keys are not next to each other. Why not the dotnet way: box.GetMaterial() or the lua way box:GetMaterial()?

 

What is the asterisk (*) doing after each datatype?

Specifying vector 2, 3 or 4 is no longer necessary?

 

 

For the rest everything looks as clean as always.

-> is used when you are dealing with a variable that is a pointer to an object.

. is used when you are dealing an object

 

The * stands for 'pointer'

Box box ; // box is an object ;

Box* box ; // box is a pointer to a box object

 

Of course you still have to know what you are dealing with, a Vec3 or a Vec4 ...

Link to comment

But how will you bake try/catch in to the engine? As I understand its talked about our (customer) side of programming.

 


try
{
Graphic* pGraphics=new Graphics(OpenGLGraphicsDriver(),1024,768,4);
// assuming that Grahics will throw a GraphicsException on failure

//	code 
//	code 
//	code 
//	code 
          delete pGraphics ;
}
catch ( GraphicsException e )
{
// Failed to initialze graphics
}

Link to comment

-> is used when you are dealing with a variable that is a pointer to an object.

. is used when you are dealing an object

 

The * stands for 'pointer'

Box box ; // box is an object ;

Box* box ; // box is a pointer to a box object

 

Of course you still have to know what you are dealing with, a Vec3 or a Vec4 ...

TY Roland,

 

with Vector 3 I meant that you don't have to use the keyword Vec3. box->SetPosition(0,0,-2,false); a

Link to comment

TY Roland,

 

with Vector 3 I meant that you don't have to use the keyword Vec3. box->SetPosition(0,0,-2,false); a

 

It depends on how Box (or its parent object) is designed.

Its possible to design in such a way that you can use both methods

 

box->SetPosition( Vec3(0,0,-2), false ) ;

or

box->SetPosition( 0,0,-2, false ) ;

 

Thats is if the Entity object (the parent of Box) is designed with both type of SetPosition methods like this

class Entity
{
public:
      void SetPosition( float x, float y, float z, bool b ) ;  
// New pos using floats
      void SetPosition( const Vec3& pos, bool b ) ;      
// New pos using Vec3
};

 

But how the design will be in the end is up to Josh

Link to comment

try/catch is not really meant for actual programming, but only for theoretical programming and teaching purposes. You should rather use checking of NULL pointers in your code, or use references.

Link to comment

Yes, most commands will have overloads like this:

SetPosition(x,y,z)

SetPosition(Vec3)

 

For inputting floats, the default function will be to send the float values, and then internally the overloaded functions will just call that, i.e.:

void Entity::SetPosition(const Vec3& position, const bool& global)
{
SetPosition(position.x,position.y,position.z,global);
}

LuaBind works nicely with C++ and allows function overloading, something that was not possible with Lua in LE2, so in Lua you can do this:

entity:SetPosition(1,2,3)
entity:SetPosition(entity2.position)
x = entity.position.x

After reviewing the alternatives, I am 100% happy with Lua. It works really well together with C++, and it runs on everything.

 

I agree, the -> separator is ugly and inconvenient, but that's really a complaint of the C++ language itself. When Codewerks arrives, you'll be able to produce the same executables with the same C++ compilers, using a saner syntax.

Link to comment

try/catch is not really meant for actual programming, but only for theoretical programming and teaching purposes. You should rather use checking of NULL pointers in your code, or use references.

Well. I have used try/catch for many many projects in commercial products, and they are real I can assure you :)

Link to comment

try/catch is not really meant for actual programming, but only for theoretical programming and teaching purposes. You should rather use checking of NULL pointers in your code, or use references.

 

I can see the advantage of try-catch: As soon as the exception occurs, the code jumps straight to the first matching catch statement, (so the general "anything goes" catch should always be last). With if null checks, these checks are only done when you say so. The code could have gone wrong but carry on executing. If for some reason you forget the if null check, the error could go undetected until the whole programs comes crashing down.

 

...That said, so far I've only used if null checks in C, but in time, I probably should be moving to the try catch system

 

I assume like Java (what I learned at uni .. my first taste of try-catches) that you can make it so that some functions must have exception checking. Either by try catch blocks, or by making the current function throw the exception up the call stack for something else to deal with.

Link to comment

Since LE3 renders things in separate layers, worlds are not really needed for anything anymore. The only reason you would want one is if you had two different "simulations" running that you don't want mixed up. So let's say you had an editor, and in one of the windows you had a model preview, and you just wanted that model and camera by themselves in one world. Well, that's a good case for still using a world class.

 

We're using Portals use for walk-in vehicles and tents, interiors have their own world, including "inventory", hand carried objects to get around accuracy problems which layers wouldn't solve at all. Although if a streaming solution can overcome this problem then it wouldn't be necessary.

Link to comment

Lumooja, you're crazy man :) try/catch are very real and they are much more informational than a null check. Josh can make very detailed exceptions that actually tell you why (in string form) it failed. With a null check you don't get that.

 

If you don't like -> operator then do something like (*params).MyFunction(), although I prefer -> over that. I guess you could make a macro to make that easier #define POINTER_VAR(p) (*p), then you can say POINTER_VAR(param).MyFunction(); Clearly you could shorten the name of that macro, just wanted to give it a descriptive name. It could be P(param).MyFunction() if you like. This also is something I wouldn't do, but if it makes you more comfortable you surely can.

 

But how will you bake try/catch in to the engine? As I understand its talked about our (customer) side of programming.

 

He would do it by having the classes throw exceptions that we can catch. That's how try/catch works. Your users use try/catch but the classes and functions throw the error for us to catch.

 

Typically, you would code in such a way that your program can handle missing files

 

Yeah, it would handle it by exiting gracefully. If you don't have a model file then there isn't much you can do about it. If anything try/catch lets you handle errors better, because if you throw more descriptive errors, we as the users can code for those specific errors and handle it better. Let's say we try to open a file but it's in use by another program and won't let us open it. With null checks we don't know why it return null. With try/catch you can throw a "file already in use" exception OR "file not found" exception. This tells us much more information than null.

Link to comment

Why not the dotnet way: box.GetMaterial()

 

The problem with .NET is that all objects are created as basically pointers. You don't have much of a choice. They are treated like pointers in C++ as far as where they are created in memory. C++ gives you the choice of where to create objects. Heap or Stack. It gives you more freedom, which is why it's slightly different.

 

Trust me though, like any other language you get used to it, and even end up liking it :)

Link to comment

The problem with .NET is that all objects are created as basically pointers. You don't have much of a choice. They are treated like pointers in C++ as far as where they are created in memory. C++ gives you the choice of where to create objects. Heap or Stack. It gives you more freedom, which is why it's slightly different.

 

Trust me though, like any other language you get used to it, and even end up liking it :)

I am sure I will Rick :D

Link to comment

I am 200% for try/catch and the pointer-oriented syntax. For those who are not used with the -> operator, you've been in garbage-collected environments for way too long :)

 

Actually, if the syntax turns out like what I posted + try/catch/Exceptions, I might even switch to C++ for development.

 

Introduction to pointers: Link

Introduction to exceptions: Link

Link to comment
So, if we have 10 models to load we will need write try/catch 10 times for each model, right?

Wouldn't you need to check to see if LoadMesh() returns NULL anyways?

 

Of course, we have the problem of worrying about how this is going to work with Lua and other languages.

Link to comment

So, if we have 10 models to load we will need write try/catch 10 times for each model, right?

try
{
   Model* a = new Model("a.gmf");
   Model* b = new Model("b.gmf");
   Model* c = new Model("c.gmf");
   Model* d = new Model("d.gmf");
}
catch (LoadingException)
{
   // Do something.
}

 

But as Josh pointed out, constructors and exceptions can not be exposed to other languages, so that design can not be done.

Link to comment

Since try/catch can't be done in all languages, there should be an advanced version of the LE2 engine logfile. One that writes all information instantly to the logfile, no matter what errors occurs.

(Is not the case in LE2)

 

And what about an error callback. You could predefine a bunch of errorcodes and the user gets an callback method called, when an error occurs. The method gets the errorcode as a parameter and we could react to a "MeshNotFound" error for example. Would only work if those errors don't kill the engine, though. But it could work in all languages i think.

 

Like most people here i have no problem with -> at all.

Link to comment

Another problem with constructors:

 

This returns an OpenGLShader object when the OpenGLDriver is in use:

Shader* shader = graphicsdriver->CreateShader();

 

This would just create the base Shader class, which has no actual code in it that makes a shader work:

Shader* shader = new Shader();

 

You would have to do this:

Shader* shader = new OpenGLShader();

 

And now your code becomes specific to one graphics driver.

Link to comment

Maybe you should place all Create/Load methods in classes as static methods.

 

For example:

 

Model* myModel=Model::LoadModel("myModel.gmf");
Shader* myShader=Shader::LoadShader();

 

And this static functions could return NULL or object itself.

And in LoadShader() function you can decide what exactly type of shader to return

Link to comment

Another problem with constructors:

 

This returns an OpenGLShader object when the OpenGLDriver is in use:

Shader* shader = graphicsdriver->CreateShader();

 

This would just create the base Shader class, which has no actual code in it that makes a shader work:

Shader* shader = new Shader();

 

You would have to do this:

Shader* shader = new OpenGLShader();

 

And now your code becomes specific to one graphics driver.

 

 

You would pass the graphicsdriver to the constructor of Shader(). You would need the graphicsdriver object above anyway in both situations, so instead of saying graphicsdriver->CreateShader() you would do Shader* shader = new Shader(graphicsdriver);

Link to comment

Guest
Add a comment...

×   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.

×
×
  • Create New...