Jump to content
  • entries
    943
  • comments
    5,899
  • views
    924,391

Some sample LE3 code


Josh

14,815 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



ArBuz is correct. The extended classes also contain different attributes. For example, the OpenGLShader class has an attribute "int glprogram" which a DirectX shader class would not have.

 

Maybe it would be nice to use different name spaces for each class like this:

Mesh* mesh = Mesh::Load();

Material* material = Material::Create();

 

Although it's not really clear to me why Mesh::Load() would be any better than LoadMesh().

Link to comment

Mesh::Load() is for the OOP interface, LoadMesh() is for the procedural interface. You make the procedural interface by using the OOP interface internally, just like you did in LE2 with BlitzMax.

Link to comment

I think static methods is a good way to go. I think constructor doesn't control memory allocation of the object itself. The compiler with the "new" command does it.

All, that work with pointers must work with objects. I mean both:

Mesh* meshObj=new Mesh("mesh.gmf");
Mesh meshObj("mesh.gmf");

must work. But with the exceptions you can not use second line, right?

 

It will be similar with what Roland suggested

KittenFactory fact;
Kitten* k = fact.CreateFluffy()

But factory will be encapsulated in the class.

Kitten* k=Kitten::CreateFluffy();

 

That is only my thoughts. I'm not as good in programing as you guys. :)

 

[Edit] Damn I type too slow. Two more posts are made :D

Link to comment

ArBuz is correct. The extended classes also contain different attributes. For example, the OpenGLShader class has an attribute "int glprogram" which a DirectX shader class would not have.

 

Maybe it would be nice to use different name spaces for each class like this:

Mesh* mesh = Mesh::Load();

Material* material = Material::Create();

 

Although it's not really clear to me why Mesh::Load() would be any better than LoadMesh().

 

I don't quite understand what the problem is here.

The implementations of the abstract base (Shader) can have as many extra attributes and methods they need as long as they implements the abstract methods in Shader. The user only handles the Shader class without knowing if its a OpenGL or a DirectX implementation. The Shader base class should be designed as a device independent class. The implementations can do what ever they need to do, and have any kinds of variables and methods to fulfill their mission. The user of Shader should not need to know anything about that.

 

There should not be a static Mesh::Load in my world. The file argument should be in the constructor

Mesh myMesh( "abstract::theThing.gmf" ) ;

or

Mesh* pMyMesh = new Mesh( "abstract::theThing.gmf" ) ;

 

fails to load a mesh will be taken care of in a try/catch statement somewhere.

Link to comment

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);

Shader* shader = new Shader(graphicsdriver); 

is of course the correct approach.

 

There is a possibility to simplify all this.

Implementing GraphicsDriver as a singleton would eliminate the need for passing the driver as an argument to all objects as shown above. I normally avoid globals or singletons, but in this case it could a possibility, as there will never been more that one (and one only) GraphicsDriver in an application. Just an idea....

Link to comment

I can not even try to argue with you because you are by a lot of heads taller than me in programming :)

I just thought that Shader is a base class. And OpenGLShader and maybe DirectXShader are derived classes.

But as I understand

Shader* shader=new Shader();

allays returns Shader object and it can not return any other derived type. That what I meant.

Link to comment

Does BMax have a try/catch? I mean out of all the languages LE wants to support is it just Lua that doesn't have a try/catch? There is probably a Lua extension that someone wrote to give try/catch functionality.

Link to comment

I can not even try to argue with you because you are by a lot of heads taller than me in programming :)

I just thought that Shader is a base class. And OpenGLShader and maybe DirectXShader are derived classes.

But as I understand

Shader* shader=new Shader();

allays returns Shader object and it can not return any other derived type. That what I meant.

 

The returned shader is either a OpenGLShader or DirectXShander.

You as user doesn't need to know. Just use the methods in Shader.

Link to comment
The implementations of the abstract base (Shader) can have as many extra attributes and methods they need as long as they implements the abstract methods in Shader.

This is how things currently work:

class Shader {};

class OpenGLShader : public Shader
{
GLenum glprogram;
};

class DirectXShader : public Shader //purely theoretical for now
{
DXShader dxshader; // I made this up
};

Are you suggesting I do this?:

class Shader {
GLenum glprogram;
DXShader dxshader;
};

class OpenGLShader : public Shader {};

class DirectXShader : public Shader {};

 

Does BMax have a try/catch? I mean out of all the languages LE wants to support is it just Lua that doesn't have a try/catch? There is probably a Lua extension that someone wrote to give try/catch functionality.

I don't think try/catch will work across languages, i.e. C# try/catch is not going to catch C++ exceptions.

Link to comment

@Josh's last post: Ew, no, keep your current implementation.

@Roland: You beat me to everything I want to say. I can only +1 all of your posts. :)

Link to comment

This is how things currently work:

class Shader {};

class OpenGLShader : public Shader
{
GLenum glprogram;
};

class DirectXShader : public Shader //purely theoretical for now
{
DXShader dxshader; // I made this up
};

Are you suggesting I do this?:

class Shader {
GLenum glprogram;
DXShader dxshader;
};

class OpenGLShader : public Shader {};

class DirectXShader : public Shader {};

 

 

I don't think try/catch will work across languages, i.e. C# try/catch is not going to catch C++ exceptions.

 

This is an outline on what I mean.

class Shader
{
public:
    virtual void SomeMethod() = 0;     

protected:
   Shader() {}
};

class OpenGLShader: public Shader
{
    GLenum glprogram; // this one is used by OpenGLShader only

public:
    OpenGLShader() {} 
    void SomeMethod() 
    {
        // use glprogram ...
    }
}

class DirectXShader: public Shader
{
    IDirectXDevice9* pdev ; // just as examle. this one is used by DirectXShader only

public:
    DirectXShader() {} 
    void SomeMethod() 
    {
        // use pdev ....
    }
}

// fictional class to produce graphic objects
class GraphicsFactory
{
   Shader* CreateShader()
   {
        if( weAreDirectX )
            return new DirectXShader() ;
       else
            return new OpenGLShader() ;
    }
}


// usage
void AFuncSomewhereInApp( GraphicsFactory* pFactory )
{
   Shader* myShader = pFactory->CreateShader() ;
   myShader->SomeMethod() ;  // will call OpenGLShader::SomeMethod if we are in OpenGL mode 
                                            // and DirectXShader::SomeMethod if we are in Direct mode
}

 

A little note: Making GraphicsFactory (or what you call it) a singleton would eliminate the need for

sending a GraphicsFactory* around.

 

In this case the usage would be

// usage
void AFuncSomewhereInApp()
{
   Shader* myShader = GraphicsFactory::Instance()->CreateShader() ;
   myShader->SomeMethod() ;  // will call OpenGLShader::SomeMethod if we are in OpenGL mode 
                                            // and DirectXShader::SomeMethod if we are in Direct mode
}

 

Remember that this is no absolute truth. Its more suggestions on different approaches.

All this can be done in so many different ways. The most important part is to hide the

DirectX/OpenGL nitty-gritty details from the user. The user should not have to deal with that,

nor have to handle any such graphics-system specific variables.

Link to comment

Roland, that is pretty much how it works now, although I am calling it a GraphicsDriver.

 

So I guess we have thoroughly thrashed out the reasoning why constructors really are not very useful? :)

Link to comment

Roland, that is pretty much how it works now, although I am calling it a GraphicsDriver.

 

So I guess we have thoroughly thrashed out the reasoning why constructors really are not very useful? :)

 

Well. Constructors are in fact used in the method CreateShader... right! :D

Link to comment

I hope someone will write a procedural C++ interface for LE3, since that starts to look just like Ogre3D to me :)

Procedural... Buy the GG mess then :D:)

Just joking :D;)

Link to comment

ctors are very useful. It just so happens you want to target languages that don't handle try/catch and possibly other advanced OO concepts. If the scripting was C# it would be all good. You want to create a library in an OO fashion but still be able to use it with non OO languages. If that's what you are after then there is almost no point in using C++ in an OO fashion because you have to do everything in the lowest common denominator. In that respect you should just keep the C syntax that exists as there is little point in making it OO.

Link to comment

I have an idea. It's funny when your design just perfectly fits something you did not originally intend, but it's as if it was meant to be.

Link to comment

Been thinking about this a bit.

 

If try/catch want work - its not the whole world, there is a possibility to solve this by giving us some error-callback that will be called in case of errors, in constructors or elsewhere. Callbacks are no problem from C++ to C#

Link to comment

I think callbacks would get messy. The implementation of them would probably be use and most likely a big switch statement to figure out what made the calling error.

 

The only other thing I've seen libraries do is have a GetLastError() function, but I'm not thrilled about that approach either.

Link to comment

Try/catch isn't a problem, but there's simply no way to use the Buffer() constructor to create an OpenGLBuffer. I could do something like this:

Buffer::Buffer()

{

this->internalbufferclasstheuserneversees = GetGraphicsDriver()->CreateRealBuffer(); //Creates some OpenGL-specific class

}

 

But that is an incredible workaround just so constructors can be used to do something they weren't designed for.

Link to comment

You were saying it was a problem because not every language supports it.

 

You are right, you would have to do something like

Buffer* b = new OpenGLBuffer();

 

But you don't want to do that because changing to use DX would require changing all those statements to some DX class.

 

It does seem that if you want to support multiple libraries you would have to create different graphics devices and then have each of those have CreateX() methods to them. Then at that point there is no point to use try/catch because you can check for null.

 

I can't really think of a way to do it otherwise. The factory design might help but it's pretty much the same idea as just using a normal function (for the most part)

Link to comment

I have an idea for exposed constructors function.

Have the default function be LoadMesh and return a Mesh (null on failure), this being exposed.

Now, in the C++ API, make a Mesh constructor:

Mesh::Mesh()
{
   if (!this = LoadMesh()) throw new LoadingException();
}

 

Oh wait, can you assign this in a C++ constructor?

Link to comment

@Lazlo: If Josh wants to use LWE on a wide variety of platform he won't use try-catch error handling (Nintendo ao.).

@Rick: Buffer* b = new OpenGLBuffer; (without brackets, brackets mean a function. It is valid C++, but not so clean code.)

 

And I see a lot of assignment overhead...

Shader* myShader = pFactory->CreateShader();

(sorry Roland, I just copied it from you because it's on this page :D).

= a constructor + assignment.

Shader* myShader(pFactory->CreateShader());

= only constructor.

 

I know in most cases you really won't see the difference. But all bits help in game performance...

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