Main Menu
Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - Redman

#1
Support / Re: glError 1281 exception
July 21, 2016, 05:09:11 PM
I haven't forgotten to look into this!  I've just been very busy with work and haven't had free time.  I would like to figure out if there's a bug to be fixed, or if its just a driver issue
#2
QuoteHmm looks interesting but how can one detect when an InstanceObject3D is touched?
By default the mesh of an InstanceObject3D is not touched by IBR.  If you do alter the mesh data, you already have access to the Mesh / Object3D as you've altered the vertices / uvs.  If you do alter the mesh, note that it will affect all instances as it uses a shared mesh.  If you need to get the Object3D of an InstanceObject3D type, use the InstanceManager.getObject3DOfInstanceType().

As for the transform matrices of the InstanceObject3D, by default it does a sort of lazy-transformations.  Any time you do a scale/translation/rotation, it sets a protected boolean changed to true.  On call of getTransformMatrix(), it will re-create the transform matrix if changed==true, otherwise it just returns the previously calculated transform matrix.  getTransformMatrix() get called when rendering each instance (every frame).  I have added a method to the code called hasChanged(), which will return true if there has been a translate, scale or rotation since last getTransformMatrix().  I hope this answers your question.


QuoteAnother question, does the order in which the InstanceObject3D's are drawn matter?

It can, and Egon may be able to better explain some of this.  It should only matter if your Object3D uses transparency or you set the OpenGL to disable the depth test, in which case it will render in synchronous order.  To put it simply, I don't believe transparency InstanceObject3D's are going to play nicely with other transparent Object3D's, other transparent InstanceObject3D types, or overlaps of the multiple IntanceObject3D of that type.  I believe jPCT has software-side ordering (based off the Object3D's origin distance from camera?) when it comes to transparent objects, and the transparent objects are rendered last as transparency requires the solid pixels behind it for the different pixel write modes (add, modulate, blend, etc...).  Because all Object3D's for this method are stacked a certain distance from the camera, they will be picked up by jPCT render pipeline in regards to that distance from the camera.  All InstanceObject3D's that are rendered would be synchronously rendered on top of one another according to the order when added.  I could add simple sorting, but it won't solve all problems, as transparency is a complex subject that requires work arounds and tweaks for speed.  But it will never work great with IBR as the distance from the camera will mess up jPCT's sorting order.

Egon, did I miss anything or is anything inaccurate?
#3
Support / Re: glError 1281 exception
July 16, 2016, 01:05:50 PM
Thank you for the prompt response!   I'll look into it and get back
#4
Support / Re: glError 1281 exception
July 15, 2016, 06:22:45 PM
Another interesting note... if I loop through rendering once (leaving it at 0,0,0), and then translate the Object3D, it does not crash.
#5
Support / glError 1281 exception
July 15, 2016, 06:14:53 PM
I'm getting this really strange crash exception from jPCT-AE.  I'm manually creating a bunch of Object3D's...  and it all renders fine, unless I translate the Object3D z position <= -16.0f, in which case I get this exception:

07-15 12:09:49.709 423-454/com.xxxxxxxxxx I/jPCT-AE: Creating buffers...
07-15 12:09:49.710 423-454/com.xxxxxxxxxx E/jPCT-AE: [ 1468598989710 ] - ERROR: before: glError 1281
07-15 12:09:49.740 423-454/com.xxxxxxxxxx E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7f8a22e180
07-15 12:09:49.752 423-454/com.xxxxxxxxxx E/AndroidRuntime: FATAL EXCEPTION: GLThread 10571
                                                                          Process: com.redman.xxxxxxxxxx, PID: 423
                                                                          java.lang.RuntimeException: [ 1468598989710 ] - ERROR: before: glError 1281
                                                                              at com.threed.jpct.Logger.log(Logger.java:206)
                                                                              at com.threed.jpct.GL20.checkError(GL20.java:159)
                                                                              at com.threed.jpct.GL20.glGenBuffers(GL20.java:1375)
                                                                              at com.threed.jpct.CompiledInstance.compileToVBO(CompiledInstance.java:1479)
                                                                              at com.threed.jpct.CompiledInstance.render(CompiledInstance.java:607)
                                                                              at com.threed.jpct.GLRenderer.drawVertexArray(GLRenderer.java:2442)
                                                                              at com.threed.jpct.World.draw(World.java:1426)
                                                                              at com.threed.jpct.World.draw(World.java:1109)
                                                                              at com.xxxxxxxxxx.GameActivity$MyRenderer.onDrawFrame(GameActivity.java:199)
                                                                              at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1535)
                                                                              at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1240)


Any idea?  I've tried the current jPCT AE JAR and the beta.  Both crash on this...
#6
Anybody want to try this out to see how my documentation is:
http://www.jpct.net/wiki/index.php?title=Instance_Batch_Rendering

I'm a little tired writing it   :P
#7
I have finished my todo list of adding rotations & animation support (and moving the MVM to software instead of vertex shader calc ).  I will add something to the Wiki soon.  More to follow.
#8
I don't get much free time, but here's an update:

  • Refactored the code for easy integration.
  • Support for translation and scaling.
  • Tested adding a grid of 15x15 of the same 64 polygon object (225 objects totaling 14,400 poly's).  On my device it held at the 60 fps cap.


Todo:

  • add rotation support
  • add animation support
  • move ModelViewMatrix to the software from hardware vertex shader.  not the proper approach to calculate on GPU as it will be recalculated for every vertex. Speed will vary greatly based off the poly count of the model currently.

More to follow.
#9
Sure.  I also thought of another good use of repeatRendering() that I'm going to need... a particle emitter.  I'll release a GLSL particle emitter next (no plan for collision detection particles).
#10
I'm currently working on an RTS project for Android using jPCT-AE.  There could possibly be up to 300+ objects on screen at one time, and I found I needed to lighten the CPU processing and memory load.  I am working on something I'm dubbing "Instance-Batch-Rendering" and I wanted to get thoughts and suggestions.

This method is for speeding up the process of rendering an Object3D multiple times on screen.

How it works:

  • Instead of cloning an Object3D, reusing the Mesh data, and sharing compiled data, this method only creates and adds 1 Object3D to the world for each unique mesh.  The Object3D must always be visible by the camera for this method to work, but it doesn't matter where its positioned.  It just matters that jPCT adds it to the visList for rendering.
  • I have created a lighter weight Instance3D object, whose main functionality is maintaining the world transform matrix (rotations, translations, scales) as well as integer ID of which model instance it represents.  It's basically an Object3D, but has no mesh data and doesn't take up much memory at all.  On each UI render frame, I manually create my own visList of Instance3D's based off the camera.  Because my project is an RTS with a top down camera, its extremely easy to calculate which objects are roughly on screen just by their x-y(z) coordinates as it's a grid.  The visLists for each type (different Meshes) of Instance get stored and calculated on every frame.  If there are no visible Instances for an Object3D, it sets the Object3D visibility to false so it doesn't get picked up in the jPCT render pipeline.
  • This Object3D has an IRenderHook attached to it.  On render, the IRenderHook.beforeRendering gets called in which it read fetches the first item in the visList.  Uniforms are passed into a Shader for the camera back matrix, the camera position, as well as a uniform for the the instance's world transform matrix.  The IRenderHook.repeatRendering gets called, increments the render index for the instance, and fetches the next instance if there are more.  It sets the transform matrix uniform of the instance and returns true.  If there are no more, it returns false.
  • A custom shader is added to the Object3D which uses the passed in Uniforms to calculate the instance's ModelViewMatrix and ModelViewProjectionMatrix on the GPU-side (offloading matrix calculations to the GPU).

So far, I have seen pretty good performance improvements using this method.  I have not tested everything out, and this is certainly not for everybody.  I have a bit more work to do on this methodology, but I wanted to gather opinions on it.  If anybody is interested, I will publish the work in this Thread, but it will be a use at your own risk as it will not support everything that jPCT-AE does and you will definitely need to build your own logic to fit your project.

I will be adding support for my hybrid-GPU-bones-animated objects for this methodology so it can support animations as well as static objects.

This is only for GLES 2.0+ as it uses Shaders at its core.
#11
I have made one change (and updated the Wiki) to the GPUAnimated3D constructor:


    public GPUAnimated3D(Animated3D object) {
        super(object);
        setSkeletonPose(new SkeletonPose(getSkeleton()));
        BonesNamespaceUtils.setSkinAttributes(this);
        setRenderHook(this);
    }


I have added "setSkeletonPose(new SkeletonPose(getSkeleton()));".  I am now setting a new instance of the SkeletonPose on creation.  It was referencing / reusing the same Skeletal Pose object, and therefore all clones were animating the same.  Now each GPUAnimated3D will animate independently of one another.
#12
I have setup the Wiki page here:
http://www.jpct.net/wiki/index.php?title=Hybrid_GPU_Shader_Animations_for_Bones

And it is now referenced at the bottom of the "Bones for jPCT/jPCT-AE" section of the landing page.

Enjoy!
#13
Sure.  I'll gladly add it to the wiki.

I think it would be useful to be able to read the name of VertexAttributes, but that's up to you.  A helper utility works just as well.
#14
GPUAnimated3D

package yournamespacegoeshere;

import yournamespacegoeshere.GPUAnimated3DShader;
import com.threed.jpct.GLSLShader;
import com.threed.jpct.IRenderHook;
import com.threed.jpct.Object3D;

import raft.jpct.bones.Animated3D;
import raft.jpct.bones.BonesNamespaceUtils;
import raft.jpct.bones.SkinClip;

/**
* Created by Dougie on 6/21/16.
*/
public class GPUAnimated3D extends Animated3D implements IRenderHook {
    public GPUAnimated3D(Animated3D object) {
        super(object);
        BonesNamespaceUtils.setSkinAttributes(object);
        setRenderHook(this);
    }

    public void animateSkin(float index, int sequence) {
        if(getSkinClipSequence() != null) {
            if(sequence == 0) {
                BonesNamespaceUtils.animate(this, index*getSkinClipSequence().getTime(), getSkeletonPose());
            } else {
                SkinClip clip = getSkinClipSequence().getClip(sequence - 1);
                clip.applyTo(index * clip.getTime(), getSkeletonPose());
            }
            getSkeletonPose().updateTransforms();
        }
    }
    public void animatePose(float index, int sequence, float weight) {
        BonesNamespaceUtils.animatePoseDontApply(this, index, sequence, weight);
    }

    @Override
    public void beforeRendering(int i) { ; }
    @Override
    public void afterRendering(int i) { ; }
    @Override
    public void setCurrentObject3D(Object3D object3D) { ; }
    @Override
    public void setCurrentShader(GLSLShader glslShader) {
        if(glslShader!=null && glslShader instanceof GPUAnimated3DShader) {
            ((GPUAnimated3DShader)glslShader).updateBeforeRenderingObject(this);
        }
    }
    @Override
    public void setTransparency(float v) { ; }
    @Override
    public void onDispose() { ; }
    @Override
    public boolean repeatRendering() { return false; }
}


GPUAnimated3DShader

package yournamespacegoeshere;

import android.opengl.GLES20;

import com.threed.jpct.GLSLShader;
import com.threed.jpct.Matrix;

import raft.jpct.bones.Animated3D;
import raft.jpct.bones.BonesNamespaceUtils;

/**
* Created by Dougie on 6/21/16.
*/
public class GPUAnimated3DShader extends GLSLShader {
    int skinWeightsHandle = -1;
    int jointIndicesHandle = -1;

    public GPUAnimated3DShader(String vertexShaderSource, String fragmentShaderSource) {
        super(vertexShaderSource, fragmentShaderSource);

        skinWeightsHandle = GLES20.glGetAttribLocation(getProgram(), BonesNamespaceUtils.ATTR_SKIN_WEIGHTS);
        jointIndicesHandle = GLES20.glGetAttribLocation(getProgram(), BonesNamespaceUtils.ATTR_JOINT_INDICES);
    }

    public void updateBeforeRenderingObject(Animated3D pObject) {
        if(pObject!=null) {
            Matrix[] skelPose = BonesNamespaceUtils.getSkeletonPosePallete(pObject.getSkeletonPose());
            if(skelPose!=null && skelPose.length>0) {
                setUniform(BonesNamespaceUtils.UNIFORM_SKEL_POSE_SIZE, skelPose.length);
                setUniform(BonesNamespaceUtils.UNIFORM_SKEL_POSE, skelPose);
            } else {
                setUniform(BonesNamespaceUtils.UNIFORM_SKEL_POSE_SIZE, 0);
            }
        }
    }
}


JPCTNamespaceUtils

package com.threed.jpct;

/**
* Created by Dougie on 6/18/16.
*/
public class JPCTNamespaceUtils {
    public static boolean VertexAttributesNameIs(VertexAttributes pVertAttrs, String pName) {
        if(pVertAttrs!=null && pName!=null && pVertAttrs.name!=null && pVertAttrs.name.equals(pName))
            return true;
        return false;
    }
}


BonesNamespaceUtils

package raft.jpct.bones;

import com.threed.jpct.Matrix;
import com.threed.jpct.JPCTNamespaceUtils;
import com.threed.jpct.VertexAttributes;

/**
* Created by Dougie on 6/17/16.
*/
public class BonesNamespaceUtils {
    public static String ATTR_SKIN_WEIGHTS = "skinWeights";
    public static String ATTR_JOINT_INDICES = "jointIndices";
    public static String UNIFORM_SKEL_POSE = "skelPose";
    public static String UNIFORM_SKEL_POSE_SIZE = "skelPoseSize";

    public static void animate(Animated3D pAnimated3D, float pTime, SkeletonPose pPose) {
        if(pAnimated3D!=null && pAnimated3D.getSkinClipSequence()!=null) {
            pAnimated3D.getSkinClipSequence().animate(pTime, pPose);
        }
    }
    public static void animatePoseDontApply(Animated3D pAnimated3D, float index, int sequence, float weight) {
        if(pAnimated3D!=null) {
            pAnimated3D.animatePoseDontApply(index, sequence, weight);
        }
    }
    public static Matrix[] getSkeletonPosePallete(SkeletonPose pPose) {
        if(pPose!=null) {
            return pPose.palette;
        }
        return null;
    }
    public static void setSkinAttributes(Animated3D pAnimated3D) {
        int i, j, k, len;
        if(pAnimated3D != null && pAnimated3D.getMesh()!=null) {
            boolean hasWeights = false, hasJointIndices = false;
            VertexAttributes[] vertAttrs = pAnimated3D.getMesh().getVertexAttributes();
            if(vertAttrs!=null) {
                //Loop backwards as they should be the last added ones if shared mesh
                for(i=vertAttrs.length-1;i>=0;--i) {
                    if(JPCTNamespaceUtils.VertexAttributesNameIs(vertAttrs[i], ATTR_SKIN_WEIGHTS)) {
                        hasWeights = true;
                    }
                    if(JPCTNamespaceUtils.VertexAttributesNameIs(vertAttrs[i], ATTR_JOINT_INDICES)) {
                        hasJointIndices = true;
                    }
                    if(hasWeights && hasJointIndices) {
                        break;
                    }
                }
            }

            if(!hasWeights) {
                float[][] weights = pAnimated3D.skin.weights;
                if(weights != null && weights.length > 0) {
                    len = weights[0].length;
                    float[] weightsArr = new float[pAnimated3D.getMesh().getUniqueVertexCount() * len];
                    for (j = 0; j < weights.length; ++j) {
                        for (k = 0; k < len; ++k) {
                            weightsArr[j * len + k] = weights[j][k];
                        }
                    }
                    if (weights.length < pAnimated3D.getMesh().getUniqueVertexCount()) {
                        for (j = weights.length; j < pAnimated3D.getMesh().getUniqueVertexCount(); ++j) {
                            for (k = 0; k < len; ++k) {
                                weightsArr[j * len + k] = 0f;
                            }
                        }
                    }
                    pAnimated3D.getMesh().addVertexAttributes(new VertexAttributes(ATTR_SKIN_WEIGHTS, weightsArr, len));
                }
            }
            if(!hasJointIndices) {
                short[][] jointIndices = pAnimated3D.skin.jointIndices;
                if (jointIndices != null && jointIndices.length > 0) {
                    len = jointIndices[0].length;
                    float[] jointIndicesArr = new float[pAnimated3D.getMesh().getUniqueVertexCount() * len];
                    for (j = 0; j < jointIndices.length; ++j) {
                        for (k = 0; k < len; ++k) {
                            jointIndicesArr[j * len + k] = jointIndices[j][k];
                        }
                    }
                    if (jointIndices.length < pAnimated3D.getMesh().getUniqueVertexCount()) {
                        for (j = jointIndices.length; j < pAnimated3D.getMesh().getUniqueVertexCount(); ++j) {
                            for (k = 0; k < len; ++k) {
                                jointIndicesArr[j * len + k] = 0f;
                            }
                        }
                    }
                    pAnimated3D.getMesh().addVertexAttributes(new VertexAttributes(ATTR_JOINT_INDICES, jointIndicesArr, len));
                }
            }
        }
    }
}


Vertex Shader Snippet

...
uniform mat4 modelViewProjectionMatrix;
...
uniform mat4 skelPose[50];
uniform int skelPoseSize;

attribute vec4 position;
attribute vec3 normal;
...
attribute vec4 skinWeights;
attribute vec4 jointIndices;

void main(void)
{
vec4 newPosition = vec4(0.0,0.0,0.0, position[3]);
vec3 newNormal = vec3(0.0,0.0,0.0);
if(skelPoseSize>0) {
float weight;
for(int j=0; j<4; ++j) {
weight = skinWeights[j];
//newPosition[j] += weight;
if(weight != 0.0) {
mat4 boneMat = skelPose[int(floor(jointIndices[j]+0.5))];
newPosition.xyz += vec3(position.x*boneMat[0][0] + position.y*boneMat[1][0] + position.z*boneMat[2][0] + boneMat[3][0],
position.x*boneMat[0][1] + position.y*boneMat[1][1] + position.z*boneMat[2][1] + boneMat[3][1],
position.x*boneMat[0][2] + position.y*boneMat[1][2] + position.z*boneMat[2][2] + boneMat[3][2])*weight;
newNormal += vec3(normal.x*boneMat[0][0] + normal.y*boneMat[1][0] + normal.z*boneMat[2][0],
  normal.x*boneMat[0][1] + normal.y*boneMat[1][1] + normal.z*boneMat[2][1],
  normal.x*boneMat[0][2] + normal.y*boneMat[1][2] + normal.z*boneMat[2][2])*weight;
}
}
newPosition.yz *= -1.0;
newNormal.yz *= -1.0;
} else {
newPosition = position;
newNormal = normal;
}

...

gl_Position = modelViewProjectionMatrix * newPosition;
}


Usage:
Use the GPUAnimated3D constructor passing in the Animated3D you want it to be.  It will clone the Animated3D reusing the Mesh.
Afterwards, call GPUAnimated3D.setShader() passing in a GPUAnimated3DShader using the integrated version of the vertex shader above.
Call your Bones animateSkin() methods as you normally would and watch it animate using the GPU instead.
#15
Egon, I think the only JPCT namespaced variable that I won't be able to access without a helper class is VertexAttributes.name

Because objects can reuse mesh data, I want to validate if my new VertexAttributes already exist in the mesh.

When I get on tonight, I will refactor and give you a concrete response of what's namespace inaccessible.