LWJGL Forum

Programming => OpenGL => Topic started by: sasha-san on November 04, 2013, 20:05:21

Title: Help me make my game engine.
Post by: sasha-san on November 04, 2013, 20:05:21
Since StackOverflow is a too confining medium for this topic. I beseech all of you, LWJGL users, to help me in building my on game engine and learn a bit of Java/OpenGL programming along the way.

I've actually built some functioning elements: an obj/mtl loader, non-POT map loader, other tools for loading data, some drawing methods that don't use a shader and more. I got stuck at skeleton transformations, too a 4 months break and forgot almost everything. Even looking at my code makes me confused. But maybe a fresh start is what I need.

Inspired by thebennybox LWJGL tutorials on YT I decided to use a shader for most of the work. And a shader-centered graphic engine is what I like to make.

So form time to time I'll be asking some questions regarding a part of my engine. I hope  you'll help me avoid some pitfalls and dead ends.

And so it begins...

BTW

This engine is supposed to be for all kind of games(mostly 3d), but I started learning java/opengl with one game in mind: a pc version of "Talisman". So atm it's a pc adaptation of a board game. If anyone here played the game and would like to on pc, maybe even online, please support my endevour.
Title: Sending data to shader.
Post by: sasha-san on November 04, 2013, 20:11:25
I'm thinking on my mesh class drawing method. Is this an acceptable way of doing things?

Code: [Select]
public void draw()
{
glEnableVertexAttribArray(0); //position
glEnableVertexAttribArray(1); //normal
glEnableVertexAttribArray(2); //color ambient
glEnableVertexAttribArray(3); //color diffused
glEnableVertexAttribArray(4); //color specular
glEnableVertexAttribArray(5); //color emission
glEnableVertexAttribArray(6); //texture 1 coordinates

   glActiveTexture(GL_TEXTURE0);
   glBindTexture(GL_TEXTURE_2D, colorMap);
   glActiveTexture(GL_TEXTURE1);
   glBindTexture(GL_TEXTURE_2D, normalMap);
   glActiveTexture(GL_TEXTURE2);
   glBindTexture(GL_TEXTURE_2D, displacementMap);
   glActiveTexture(GL_TEXTURE3);
   glBindTexture(GL_TEXTURE_2D, colorMapMod);

glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, false, Vertex.SIZE * 4, 0);
glVertexAttribPointer(1, 3, GL_FLOAT, false, Vertex.SIZE * 4, 12);
glVertexAttribPointer(2, 4, GL_FLOAT, false, Vertex.SIZE * 4, 24);
glVertexAttribPointer(3, 4, GL_FLOAT, false, Vertex.SIZE * 4, 40);
glVertexAttribPointer(4, 4, GL_FLOAT, false, Vertex.SIZE * 4, 56);
glVertexAttribPointer(5, 4, GL_FLOAT, false, Vertex.SIZE * 4, 72);
glVertexAttribPointer(6, 2, GL_FLOAT, false, Vertex.SIZE * 4, 88);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glDrawElements(GL_TRIANGLES, size, GL_UNSIGNED_INT, 0);

glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
glDisableVertexAttribArray(4);
glDisableVertexAttribArray(5);
glDisableVertexAttribArray(6);

   glActiveTexture(GL_TEXTURE0);
   glBindTexture(GL_TEXTURE_2D, 0);
   glActiveTexture(GL_TEXTURE1);
   glBindTexture(GL_TEXTURE_2D, 0);
   glActiveTexture(GL_TEXTURE2);
   glBindTexture(GL_TEXTURE_2D, 0);
   glActiveTexture(GL_TEXTURE3);
   glBindTexture(GL_TEXTURE_2D, 0);

}

Am I missing something? Should I make room for more data like maps. The Idea is to send everything that doesn't change at once. Variables would be sent as uniforms.

Also, should I include bone index/weight data here? And how about the bone matrices themselves? Send each as a uniform matrix4f or is there a better way of sending a group of matrices at once?
Title: Sending data to shader.
Post by: sasha-san on November 04, 2013, 23:42:48
Now that I think of it, I have to decide on how an object(3d object from Blender for example) will be imported, converted to a vbo & ibo, modified and sent to the shader.

Lets begin with color. It would be easier if I assume that all 3d object are made of other objects/groups which have their specific or shared material. This way the colors would be sent as mat4f uniforms for an object part. An example would be a 3d model of a car with some textures and various materials. While the whole car could be imported as one unified object, I would have to include the colors for every vertex. The textures would be inapplicable. So am I to assume that this approach is wrong? Is vertex-painted objects even necessary?

Or should I use an "Object" class containing a map of all its meshes and materials, with a vbo&ibo for each component separately?




Title: Skeleton transformations.
Post by: sasha-san on November 05, 2013, 00:01:17
Is this how I should go about sending vertex bone indices and weights?

Code: [Select]
public void draw()
{
glEnableVertexAttribArray(0); //position
glEnableVertexAttribArray(1); //normal
glEnableVertexAttribArray(2); //texture coordinates
for(int i = 0; i < numberOfBones; i++) {
glEnableVertexAttribArray(i+3);
}

glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, false, Vertex.SIZE * 4, 0);
glVertexAttribPointer(1, 3, GL_FLOAT, false, Vertex.SIZE * 4, 12);
glVertexAttribPointer(2, 2, GL_FLOAT, false, Vertex.SIZE * 4, 24);
for(int i = 0; i < numberOfBones; i++) {
glVertexAttribPointer(i+3, 2, GL_FLOAT, false,  Vertex.SIZE * 4, 32+i*8);//first float is the bone index, the second is the weight
}

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glDrawElements(GL_TRIANGLES, size, GL_UNSIGNED_INT, 0);

glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
for(int i = 0; i < numberOfBones; i++) {
glDisableVertexAttribArray(i+3);
}

}

Not sure if I wrote it correctly, but it's the idea that matters. But the variable numberOfBones would have to be a static value. From the get-go there would have to be an exact number of bones a mesh vertex in my engine is influenced by. I would be reserving slots in the vbo for bone data, whether there are any bones or not. If this is a correct approach, what number of bones should be the limit?
Title: Re: Help me make my game engine.
Post by: quew8 on November 05, 2013, 19:49:22
So I've got a couple of thoughts.

Firstly you're trying to do some quite complex stuff considering you're a bit of a novice with OpenGL (don't get me wrong you seem to be doing well but experience trumps all) and, you gave the impression, Java as well. Are you really using a displacement map and a moded colour map? I have been working with OpenGL for years, doing similar stuff to you, and I've never had cause to do either. Give it some thought and don't do anything you don't need to. It's a drain on performance and will come back to bite you. You asked:
Quote
Am I missing something? Should I make room for more data like maps.
And the answer is absolutely not. At least until you decide you need it.

Quote
I would have to include the colors for every vertex. The textures would be inapplicable. So am I to assume that this approach is wrong?

I'm sure there are some specialist situations where this is exactly what is necessary. But not for what you're doing. Seriously pick up any object in front of you and see if there is any gradual change (like what you would see in fragment interpolation) in the material properties (essentially how it reflects light). I guarantee you the answer is no. So yes, send material properties as uniforms. At most: send multiple materials and give an attribute for the index of the material to use, but I would avoid even this.

Quote
If this is a correct approach, what number of bones should be the limit?

You shouldn't use a static number of bones. The restrictions that would put on your engine would be too much. Sometimes my shader implementations require an upper limit, but I can set this at runtime to allow for any situation I need. All in all, it seems to me that your rendering engine seems very inflexible. I once had the same problem and it took a lot of refactoring to fix it but now I can replace almost any rendering code as needed without rewriting anything but what is different. I suggest you aim for a similar outcome.

That is all for now I think.
Title: Re: Help me make my game engine.
Post by: sasha-san on November 07, 2013, 08:00:29
@quew8 Thank you so much for the reply. Right now I'm gathering ideas on how my graphic engine should work.
Quote
Quote
Am I missing something? Should I make room for more data like maps.
And the answer is absolutely not. At least until you decide you need it.
I might have gone too far on that one. It appears that instead of an engine I went straight for the game. Thinking of making a shader, instead of making tools for communication and management of shader programs.

Quote
Quote
If this is a correct approach, what number of bones should be the limit?
You shouldn't use a static number of bones. The restrictions that would put on your engine would be too much. Sometimes my shader implementations require an upper limit, but I can set this at runtime to allow for any situation I need. All in all, it seems to me that your rendering engine seems very inflexible. I once had the same problem and it took a lot of refactoring to fix it but now I can replace almost any rendering code as needed without rewriting anything but what is different. I suggest you aim for a similar outcome.
This the hardest part for me since my knowledge of how to communicate with shaders is basic at best. Could you give me example of skeleton animation/transformation using LWJGL? Especially the part where data is sent to the shader and how the shader works with it.
Title: Re: Help me make my game engine.
Post by: quew8 on November 07, 2013, 18:45:17
I'm afraid I can't give you an example. Purely coincidentally, I was working on my own skeletal animation when you posted, and all the code is in place however, I've encountered a bug in the "infrastructure" of the skeletal animation. By which I mean the bit which isn't actually specific to skeletal animation, which is odd since the whole purpose of the design means that that code is actually common. But hey. The point is right now I can only give theoretical help.

In terms of communicating with the shader, I presume you are talking about the variable number of skeletal matrices. The way I do variable number of variables, is to create a fixed sized uniform array. When I load the shader into OpenGL, I replace the size of the array with the maximum size I would need. Whenever you upload a set of matrices, you also have to set a uniform integer saying how many matrices are in use. Potentially (although I don't do this) you could recreate the shader if at any time your requirements changed.

Another method (which I don't particularly like and have never implemented) is to use a uniform sampler object. That way you can send the data to OpenGL via a texture which is resizeable. I don't see this as a "fast" method, if nothing else since one must interpret the texture data as a matrix at the shader level every single vertex of every single frame. But this is a more dynamic, if problematic, method.

As for what to do with the data, take a look at COLLADA's specification: http://www.khronos.org/files/collada_spec_1_4.pdf (http://www.khronos.org/files/collada_spec_1_4.pdf). More specifically the "Skinning a Skeleton in COLLADA" section of Chapter 4. Otherwise I would need a more specific information request.

Hope this helps.
Title: Re: Help me make my game engine.
Post by: sasha-san on November 08, 2013, 10:32:43
Hmm. This might be something i should've started with... Skeletal transformations(lets leave animation for later) have two parts in the context of lwjgl. One is the weights and bone indices/names assigned to every effected vertex. The other are bone matrices. Right now I think we covered the matrices part, but what about the vertex data. I was kinda hoping for one draw() method in my Mesh class, but vbos with different amount of bones would confuse the attribute pointer. Would the code in my earlier posts be a good solution? Or am I just pulling things outta' my ass?

BTW. When I started, I used vertex/color/normal/texcoord pointers(since I could do it without a shader), but someone told me that these methods were deprecated and vertex attribute pointers were the way. True or not, which would you suggest?
Title: Re: Help me make my game engine.
Post by: abcdef on November 08, 2013, 16:09:46
I have skeletal animation working in the old open gl (open gl 1, plan is to support it in all versions for backwards compatibility)

I have


To animate I

Title: Re: Help me make my game engine.
Post by: quew8 on November 08, 2013, 23:03:31
@sasha-san I meant to use a similar method for vertex weights. For simple models I doubt you'll need more than 4 bone weights per vertex right? So an attribute ivec4 for the bone indices and an attribute vec4 for the associated bone weights. By the way, I said in the previous post that you should also use a uniform integer to see how many bones you've uploaded. But that's just stupid, not sure why I said it. It doesn't matter how many bones you've got since you're indexing the weights. Any way, I hope that helps.

As for a single draw call. I suggest that you make use of the central concept of Java: Object Orientated Programming. For my system I use  a couple of interfaces to determine how data is drawn. A little example:

GeometricDataInterpreter determines how a generic geometric object is converted into vertex and index data.
Code: [Select]
/**
 *
 * @param <T>
 * @author Quew8
 */
public interface GeometricDataInterpreter<T> {
   
    public Vector[] toPositions(T[] t);
   
    public float[] toVertexData(T[] t);
   
    public int[][] toIndexData(T[] t);
}

StaticRenderMode and DynamicRenderMode setup OpenGL ready for rendering a particular type of object and give a way to update transformation matrices. DynamicRenderMode can also act based on generic data contained within the drawable handle (In the case of skeletal animation, this would be the skeleton but its completely generic).
Code: [Select]
/**
 *
 * @author Quew8
 */
public abstract class StaticRenderMode {

    public void onPreRendering() {};

    public void onPostRendering() {};

    public abstract void updateProjectionMatrix(FloatBuffer matrix);
}

/**
 *
 * @param <T>
 * @author Quew8
 */
public abstract class DynamicRenderMode<T> extends StaticRenderMode {
   
    public void onPreDraw(T data) {};

    public void onPostDraw(T data) {};
   
    public void bindGeneralTexture(Image image) {
        image.bind();
    }
   
    public abstract void updateModelMatrix(FloatBuffer matrix);
}

And an example usage for basic 3D, position, normal, tex-coord data:

Code: [Select]
/**
 *
 * @author Quew8
 * @param <T>
 */
public class FGeneralInstanceMode<T> extends DynamicRenderMode<T> {
    private final ShaderProgram shaderProgram;
   
    private FGeneralInstanceMode(ShaderProgram shaderProgram, Class<T> clazz) {
        this.shaderProgram = shaderProgram;
    }
   
    @Override
    public void onPreRendering() {
        shaderProgram.use();
        ShaderUtils.setVertexAttribPointer(0, 3, GL_FLOAT, false, 32, 0);
        ShaderUtils.setVertexAttribPointer(1, 3, GL_FLOAT, false, 32, 12);
        ShaderUtils.setVertexAttribPointer(2, 2, GL_FLOAT, false, 32, 24);
    }
   
    @Override
    public void updateModelMatrix(FloatBuffer matrix) {
        ShaderUtils.setUniformMatrix(shaderProgram.getId(), "modelMatrix", matrix);
    }
   
    @Override
    public void updateProjectionMatrix(FloatBuffer matrix) {
        ShaderUtils.setUniformMatrix(shaderProgram.getId(), "projectionViewingMatrix", matrix);
    }
}

Hope this helps.

@abcdef Nice post, good fundamentals and impressed you can get any speed for non-hardware-accelerated skeletal animation. However I would suggest that it really isn't necessary to maintain backward compatibility woth OpenGL 1.1. I do backward compatibility too but mine is with OpenGL 2.1 (Or at least it will be very soon). With shaders, 2.0 was a bi step forward, provides an excellent speed boost and (here's the thing) I've never encountered, or heard of, a computer without at least support OpenGL 2.1 (Discounting dodgy drivers). My humble opinion.
Title: Re: Help me make my game engine.
Post by: sasha-san on November 09, 2013, 07:07:26
@abcdef I think I get the concept of skeletal animation. I made a Skeleton class to govern the bones and to easily attach a skeleton to a mesh. Bone classes with parent/child connections, calculating its end-matrix by multiplying its matrix with its parents end-matrix and so on up to the root bone. Even made some linear interpolated animations going, but only in the console since I didn't know hot to use a shader for that kind of thing. The idea is,I think, for the vbo to be unchanging and all the transformation made in the shader. One could make the transformations in java and send a new vertex array each frame, but it seams wasteful.

@quew8 Since render modes were some of the first things I learned, they were the first to go too... A rendering class... I just have a draw method in my gameloop and most of the work is done by the meshes draw(). It's something to think about.

Right now I'm working on sending material data to the shader program. Tell me what you think.

Code: [Select]
public class Material {

private Map<String, Vector4f> colors;
private Map<String, Integer> maps;
private Shader program;
public void setMaterialUniformPointers(Shader program) {
this.program = program;
for(String colorName : colors.keySet()) {
program.addUniform(colorName);
}
for(String mapName : maps.keySet()) {
program.addUniform(mapName);
}
}

private void sendColors() {
for (String colorName : colors.keySet()) {
program.setUniformVec4f(colorName, colors.get(colorName));
}
}

private void sendMaps() {
int activeTexture = 0;
for (String mapName : maps.keySet()) {
glActiveTexture(GL_TEXTURE0 + activeTexture);
glBindTexture(GL_TEXTURE_2D, maps.get(mapName));
program.setUniformi(mapName, activeTexture);
activeTexture++;
}
glActiveTexture(GL_TEXTURE0);
}

public void sendMaterial() {
sendColors();
sendMaps();
}
}

Hmm.. Now that I think about it, the set uniform part may be buggy. I could make engine constants like COLORMAP, DIFFUSEDCOLOR that would than be used as uniform pointers for the shader. Any ideas? Maybe an enum with map and color types to make things clear?

BTW What file format are you using for the skeleton? I tried write my own export script for Blender, but there must be something useful already.
Title: Re: Help me make my game engine.
Post by: quew8 on November 09, 2013, 11:25:54
Sorry, I didn't make it clear. The idea is that you can give each Mesh (I presume the mesh represents a section of a VBO) its own RenderMode at initialization which you call at appropriate times in the draw() call. So any style of rendering can be accommodated with a simple loop through the geometry.

The Material class looks fine but all your ideas also sound good. I would tend towards Enums since they're like anti-bug remedies. However they may be too static for your needs. Try it, see what happens. (This next bit might not be relevant) If by pointer you mean the integer returned by glGetUniformLocation() (or whatever it is) I might warn you that if you re-link the shader program the position of uniforms and attributes might change. Just a warning.

As for file format, I use the COLLADA format (.dae) which is an open source, xml-based format (from Khronos, the same people as the OpenGL specs). It has support for all sorts of geometric data from meshes and skinning data to animations and shader effects. Of course you can ignore as much of that as you want. It's quite a complex format to get into (I feel) and I certainly wasn't able to find any outstanding tutorials. After my learning experience, I think I'll write my own tutorial on the JGO forums so if you want to learn maybe take a look there in the near future. In the mean time there is this: http://www.wazim.com/Collada_Tutorial_1.htm (http://www.wazim.com/Collada_Tutorial_1.htm). Generally one would only use this as an intermediate format. When you deploy a game you use your own binary format with only the bare essentials to reduce file size.
Title: Re: Help me make my game engine.
Post by: abcdef on November 09, 2013, 11:29:33
@quew8

You are completely right on the "everything" supports version 2, I'm a bit of a sucker for optimising my code as much as possible so I thought I would start with the least hardware accelerated method and explore which parts of the code are slow and which are fast and to try and make things as optimal as possible. I've made things multithreaded to do certain actions in parallel and to leave the render thread to only render, I've also found what the CPU sucks at at tried to move things to the GPU. Its kind of fun to learn things and experiment. The skeletal animation (bone transformations) I can do really fast on the CPU the mesh transformation is pretty slow (like 10fps for a single skeleton). I'm slowly moving more things to the GPU and eventually I'll start using shaders. I've designed things such that upgrading will be fairly easy

@sasha-san

I understand where you are coming from, the VBO method you mention is a good one. I currently do that with plain normal state machine opengl, I look forward to the speed ups when I have ironed out all the inefficient code :)
Title: Re: Help me make my game engine.
Post by: sasha-san on November 10, 2013, 11:39:18
I'm getting no location from glunifromlocation(), the uniform exists and is used in the shader. What gives?
Title: Re: Help me make my game engine.
Post by: quew8 on November 10, 2013, 13:34:27
Make sure the shader is linked. If it is, it might even have to be in use (that's not specification defined behaviour but one can't always trust drivers). Failing that, I don't know.

Also, I just found the bug in my skeletal animation (It was in my COLLADA parser, surprise surprise). So I can give you some example code now if you'd like.
Title: Re: Help me make my game engine.
Post by: sasha-san on November 10, 2013, 14:50:12
@quew That would be awesome. The shader is compiled and active. The problem must be so simple, I can't even see it. But it's there, I can smell it.
Title: Re: Help me make my game engine.
Post by: quew8 on November 10, 2013, 15:24:00
So what is the actual error OpenGL is giving?

The Vertex Shader:
Code: [Select]
#version 120

#extension GL_EXT_gpu_shader4 : enable

uniform mat4 projectionViewingMatrix;
uniform mat4 modelMatrix;
uniform mat4 bindShapeMatrix;
uniform mat4[6] invBindMatrices;
uniform mat4[6] jointMatrices;

attribute vec3 position;
attribute vec3 normal;
attribute vec2 texCoords;
attribute ivec4 jointIndices;
attribute vec4 jointWeights;

varying vec2 fInTexCoords;
varying vec3 fInNormal;

void main(void) {
    mat4 mvp = projectionViewingMatrix * modelMatrix;
    vec4 vbsm = vec4((vec4(position.xyz, 1) * bindShapeMatrix).xyz, 1);
    vec3 animatedPosition = vec3(
        (
            ( ( vbsm * invBindMatrices[int(jointWeights[0])] * jointMatrices[int(jointWeights[0])] ).xyz * jointWeights[0] ) +
            ( ( vbsm * invBindMatrices[int(jointWeights[1])] * jointMatrices[int(jointWeights[1])] ).xyz * jointWeights[1] ) +
            ( ( vbsm * invBindMatrices[int(jointWeights[2])] * jointMatrices[int(jointWeights[2])] ).xyz * jointWeights[2] ) +
            ( ( vbsm * invBindMatrices[int(jointWeights[3])] * jointMatrices[int(jointWeights[3])] ).xyz * jointWeights[3] )
        ).xyz
    );
    vec3 nbsm = normal * mat3(bindShapeMatrix);
    vec3 animatedNormal = vec3(
        ( nbsm * mat3(invBindMatrices[int(jointWeights[0])]) * mat3(jointMatrices[int(jointWeights[0])]) * jointWeights[0] ) +
        ( nbsm * mat3(invBindMatrices[int(jointWeights[1])]) * mat3(jointMatrices[int(jointWeights[1])]) * jointWeights[1] ) +
        ( nbsm * mat3(invBindMatrices[int(jointWeights[2])]) * mat3(jointMatrices[int(jointWeights[2])]) * jointWeights[2] ) +
        ( nbsm * mat3(invBindMatrices[int(jointWeights[3])]) * mat3(jointMatrices[int(jointWeights[3])]) * jointWeights[3] )
    );
    fInTexCoords = texCoords;
    fInNormal = mat3(modelMatrix) * animatedNormal;
    gl_Position = mvp * vec4(animatedPosition.xyz, 1);
}

On the extension I use here, I have no idea why. On this computer, I get a warning saying it isn't supported in the current profile so it shouldn't be doing anything at all. However if it isn't there, I get an error saying that "ivec4 isn't supported by OpenGL," which is not true since it was added in GLSL 1.1. I've even looked at what the extension does and there is nothing about ivec4. So...

The Skeletal Render Mode (uses the interface system I posted earlier):
Code: [Select]
/**
 *
 * @author Quew8
 */
public class FSkeletalRenderMode extends DynamicRenderMode<Skeleton> {
    private final ShaderProgram shaderProgram;
    private final String bindShapeMatrixVar, invBindShapeMatrix, jointMatrix;
   
    public FSkeletalRenderMode(ShaderProgram shaderProgram, String bindShapeMatrixVar, String invBindShapeMatrix, String jointMatrix) {
        this.shaderProgram = shaderProgram;
        this.bindShapeMatrixVar = bindShapeMatrixVar;
        this.invBindShapeMatrix = invBindShapeMatrix;
        this.jointMatrix = jointMatrix;
    }
   
    @Override
    public void onPreRendering() {
        shaderProgram.use();
        ShaderUtils.setVertexAttribPointer(0, 3, GL_FLOAT, false, 52, 0);
        ShaderUtils.setVertexAttribPointer(1, 3, GL_FLOAT, false, 52, 12);
        ShaderUtils.setVertexAttribPointer(2, 2, GL_FLOAT, false, 52, 24);
        ShaderUtils.setVertexAttribPointer(3, 4, GL_BYTE, false, 52, 36);
        ShaderUtils.setVertexAttribPointer(4, 4, GL_FLOAT, false, 52, 50);
    }
   
    @Override
    public void updateModelMatrix(FloatBuffer matrix) {
        ShaderUtils.setUniformMatrix(shaderProgram.getId(), "modelMatrix", matrix);
    }
   
    @Override
    public void updateProjectionMatrix(FloatBuffer matrix) {
        ShaderUtils.setUniformMatrix(shaderProgram.getId(), "projectionViewingMatrix", matrix);
    }
   
    @Override
    public void onPreDraw(Skeleton data) {
        data.uploadSkeleton(shaderProgram.getId(), bindShapeMatrixVar, invBindShapeMatrix, jointMatrix);
    }
}

EDIT: Forgot these: Skeleton and Joint

Code: [Select]
/**
 *
 * @author Quew8
 */
public class Skeleton {
    private final FloatBuffer matrixBuffer = BufferUtils.createFloatBuffer(16);
    private final Matrix bindShapeMatrix;
    private final Joint[] joints;
    private final int nJoints;
   
    public Skeleton(Matrix bindShapeMatrix, Joint[] joints, int nJoints) {
        this.bindShapeMatrix = bindShapeMatrix;
        this.joints = joints;
        this.nJoints = nJoints;
    }
   
    public int getNJoints() {
        return nJoints;
    }
   
    public void uploadSkeleton(int programId, String bsmVar, String ibmVar, String jmVar) {
        bindShapeMatrix.putIn(matrixBuffer);
        ShaderUtils.setUniformMatrix(programId, bsmVar, matrixBuffer);
        for(int i = 0; i < joints.length; i++) {
            joints[i].uploadJoint(programId, ibmVar, jmVar, matrixBuffer);
        }
    }
}

/**
 *
 * @author Quew8
 */
public class Joint {
    private final int index;
    private final String indexString;
    private final Matrix invBindMatrix;
    private final Matrix jointMatrix;
    private final Matrix tempMatrix = new Matrix();
    private final Joint[] children;
   
    public Joint(int index, Matrix invBindMatrix, Matrix jointMatrix, Joint[] children) {
        this.index = index;
        this.indexString = "[" + Integer.toString(index) + "]";
        this.invBindMatrix = invBindMatrix;
        this.jointMatrix = jointMatrix;
        this.children = children;
    }
   
    public void uploadJoint(int programId, String ibmVar, String jmVar, Matrix parentWJM, FloatBuffer matrixBuffer) {
        Matrix.times(tempMatrix, parentWJM, jointMatrix);
        tempMatrix.putIn(matrixBuffer);
        ShaderUtils.setUniformMatrix(programId, jmVar + indexString, matrixBuffer);
        invBindMatrix.putIn(matrixBuffer);
        ShaderUtils.setUniformMatrix(programId, ibmVar + indexString, matrixBuffer);
        for(int i = 0; i < children.length; i++) {
            children[i].uploadJoint(programId, ibmVar, jmVar, tempMatrix, matrixBuffer);
        }
    }
   
    public void uploadJoint(int programId, String ibmVar, String jmVar, FloatBuffer matrixBuffer) {
        uploadJoint(programId, ibmVar, jmVar, new Matrix(), matrixBuffer);
    }
}
The Skin GeometricDataInterpreter (I've actually changed that interface slightly since you last saw it):
Code: [Select]
/**
 *
 * @author Quew8
 */
public class SkinInterpreter extends GeometricObjectInterpreter<Skin, WeightedVertex> {
    public static SkinInterpreter INSTANCE = new SkinInterpreter();
   
    private SkinInterpreter() {
       
    }
   
    @Override
    public ByteBuffer toVertexData(Skin[] skins) {
        int length = 0;
        WeightedVertex[][] vertices = new WeightedVertex[skins.length][];
        for(int i = 0; i < vertices.length; i++) {
            vertices[i] = skins[i].getVertices();
            length += skins[i].getVerticesLength();
        }
        length *= WeightedVertex.WEIGHTED_VERTEX_BYTE_SIZE;
        ByteBuffer bb = BufferUtils.createByteBuffer(length);
        for(int i = 0; i < vertices.length; i++) {
            for(int j = 0; j < vertices[i].length; j++) {
                vertices[i][j].appendData(bb);
            }
        }
        bb.flip();
        return bb;
    }
   
}

And WeightedVertex (or at least the relevant bits) to show how the data is arranged in the VBO.
Code: [Select]
/**
 *
 * @author Quew8
 */
public class WeightedVertex extends AbstractVertex<WeightedVertex> {
    public static final int WEIGHTED_VERTEX_BYTE_SIZE = 52;
   
    public WeightedVertex(float[] data, float[] weights) {
        super(ArrayUtils.concatArrays(new float[][]{data, weights}, VERTEX_SIZE + weights.length));
    }
   
    public WeightedVertex(Vector pos, Vector normal, float tx, float ty, float[] weights) {
        this(new float[]{
            pos.getX(), pos.getY(), pos.getZ(),
            normal.getX(), normal.getY(), normal.getZ(),
            tx, ty
        }, weights);
    }
   
    public int getNWeights() {
        return data.length - VERTEX_SIZE;
    }
   
    public float getWeight(int i) {
        return data[VERTEX_SIZE + i];
    }
   
    public void setWeight(int i, float weight) {
        data[VERTEX_SIZE + i] = weight;
    }
   
    @Override
    public void appendData(ByteBuffer bb) {
        for(int i = 0; i < VERTEX_SIZE; i++) {
            bb.putFloat(data[i]);
        }
        int nWeights = getNWeights();
        int nUsedWeights = 0;
        int[] jointIndices = new int[4];
        float[] usedWeights = new float[4];
        for(int i = 0; i < nWeights && nUsedWeights < 4; i++) {
            float w = getWeight(i);
            if(w != 0) {
                jointIndices[nUsedWeights] = i;
                usedWeights[nUsedWeights++] = w;
            }
        }
        for(int i = 0; i < 4; i++) {
            bb.put((byte) jointIndices[i]);
        }
        for(int i = 0; i < 4; i++) {
            bb.putFloat(usedWeights[i]);
        }
    }
   
}

Hopefully that's enough to make it clear how it works. I know there are some holes in my implementation according to the code I posted but that's due to abstraction and some slightly unclear inheritance (I haven't posted the links in the chain). I'm happy to take questions or better still suggestions.

Also notice how little code (excluding the data structures which would have to be there no matter what) it actually took for me to implement skeletal animation with my RenderMode s. In the short term they can be difficult to fit into your engine especially if you don't have them in mind from the off (which I didn't). The generic data thing in particular took me about a week of programming (I'm also in school so it's not any where near a full days work but still) but it's also the bit I'm most proud of and has been the most useful. I'm just trying to persuade you since I believe it will make life so much easier for you later.

I haven't actually done the lighting for this game yet and I've never worked out the normal matrix from the model matrix in the shader before (they are orthonormal matrices so it's just the top left 3x3 but I don't know if casting to mat3 will do that) so I have no idea if those normal calculations are correct. Cross that bridge when I come to it.
Title: Re: Help me make my game engine.
Post by: sasha-san on November 10, 2013, 16:59:20
@quew8 Found the bug. My vertex/fragment shaders were so barebone, that glsl compiler deleted the uniform because it didn't influence any output. I kinda mentioned that possibility, thinking that just mentioning the uniform would work, but the compiler is one thorough sonuva... With that behind me, let me take a gander at the code you posted.

Update
@quew8 Does the index of the attribute pointer is reflected by the order of attributes in the shader. Does the data in pointer 0 got to vec3 position because it's first in the shader?
And this:
Code: [Select]
public void uploadJoint(int programId, String ibmVar, String jmVar, Matrix parentWJM, FloatBuffer matrixBuffer) {
        Matrix.times(tempMatrix, parentWJM, jointMatrix);
        tempMatrix.putIn(matrixBuffer);
        ShaderUtils.setUniformMatrix(programId, jmVar + indexString, matrixBuffer);
        invBindMatrix.putIn(matrixBuffer);
        ShaderUtils.setUniformMatrix(programId, ibmVar + indexString, matrixBuffer);
        for(int i = 0; i < children.length; i++) {
            children[i].uploadJoint(programId, ibmVar, jmVar, tempMatrix, matrixBuffer);
        }
    }
Can you really send an array by just getting attribute location for "anArray[0]", "anArray[1]", "anArray[2]" and so on? If so, I must go through glsl documentation once again(for the first time with focus).

Update
Making my own .dae parser using jdom. Heaving a lot of fun.
Title: Re: Help me make my game engine.
Post by: quew8 on November 11, 2013, 14:04:29
That's nasty of the compiler, never heard of that before. Good to know.

@quew8 Does the index of the attribute pointer is reflected by the order of attributes in the shader. Does the data in pointer 0 got to vec3 position because it's first in the shader?

I have a feeling that is the default, but I would never rely on the defaults for something like this, take a look here: http://www.opengl.org/sdk/docs/man2/xhtml/glBindAttribLocation.xml (http://www.opengl.org/sdk/docs/man2/xhtml/glBindAttribLocation.xml). It just binds an attribute name to an index which you can then use. But generally I just set them in the order they appear in the shader - makes it all simpler.

Can you really send an array by just getting attribute location for "anArray[0]", "anArray[1]", "anArray[2]" and so on? If so, I must go through glsl documentation once again(for the first time with focus).

Indeed it it so. You can fill up an array with the glUniformXXv() functions, but generally I don't find much point. I'm sure it might be quicker if you were going for performance.

Making my own .dae parser using jdom. Heaving a lot of fun.

I used Dom4J personally. Are you using that tutorial I linked? How are you finding it?
Title: Re: Help me make my game engine.
Post by: sasha-san on November 11, 2013, 16:53:30
@quew8 Kinda wanted to reacquaint myself with jdom so I'm reading a tutorial on that. I'm finishing a material extracting method atm. If I get stuck I'll go for the tutorial you linked, but I think that since the part containing vertex data is so much different form .obj, I'll use it for sure. The armature data will be a drag too. BTW are you making contingencies for all type of lighting calculations(phong, blin etc.)?
Title: Re: Help me make my game engine.
Post by: quew8 on November 11, 2013, 17:06:45
Yeah, I only ask because looking over it again having (almost) finished my parser, it does make more sense to me than initially (obviously really) but I'm starting to wonder whether my initial thoughts on the tutorial were justified and thus whether my writing a new tutorial is really necessary. So impressions of an "impartial" (and not-me) individual would be appreciated.

are you making contingencies for all type of lighting calculations(phong, blin etc.)?

No. What I do support, I support completely but everything else I've just ignored. So that means I read geometries, controllers, armature stuff, animations, textures (materials if I ever get round to it) and ignore everything else (I think that's everything). A real game programmer would write his own binary format containing only what is necessary but I think I might just cannibalize COLLADA and just take out everything I don't read. Ultimately that will just increase the size of my game (I'll have to include the Dom4J library as well as the larger COLLADA files) but I'll cross that bridge when I come to it (story of my life).
Title: Re: Help me make my game engine.
Post by: sasha-san on November 11, 2013, 18:29:41
@quew8 My first thoughts on the tutorial are that it's in one big chunk(examples aside). I'd like an index with links to separate parts with in-depth information on a topic. It generally lacks clarity imo. Some thing I think are important are lost in the text. That's it atm.

Update
A real clusterf**k here. I'm trying to brake this thing into smaller pieces, but parsing collada is making me a bit crazy. Have to play BF4 to keep sane, so the work slows down.

@quew8 About that tutorial.. A map! It's missing a good color-coded map. There is an attempt at such thing, but I had to write my own. Have to do it again, forgot about normal/bump maps.