[SOLVED] Animated model, shadow filter and shader compilation error

Hi,

yesterday I stumbled upon this error and I don’t really have a clue how to solve it.

This is the error:

2018-04-28 11:32:40,833 ERROR [jME3 Main] com.jme3.app.LegacyApplication (LegacyApplication.java:617) - Uncaught exception thrown in Thread[jME3 Main,5,main]
com.jme3.renderer.RendererException: compile error in: ShaderSource[name=Common/MatDefs/Shadow/PreShadow.vert, defines, type=Vertex, language=GLSL100]
ERROR: 0:158: '' :    NUM_BONES must be between 1 and 255.

I have a scene with an animated character in it and a DirectionalLightShadowFilter, when I load both of them I get this error. Loading the character and playing the animation without the ShadowFilter works perfect. I checked the character in Blender, and I made sure the location, scale and rotation are normalized.

I also added the NumberOfBones material parameter on the animated character, with the correct number of bones (26, so it is definatly in the range [1-255]).

These are the relevant code parts:
The ShadowFilter (ps: I also tried it with a ShadowRenderer, I get the same error)

DirectionalLightShadowFilter dlsf = new DirectionalLightShadowFilter(getApplication().getAssetManager(), 1024, 4);
dlsf.setLight(directionalLight);
dlsf.setEnabled(true);
dlsf.setEdgeFilteringMode(EdgeFilteringMode.PCFPOISSON);
dlsf.setEdgesThickness(2);
dlsf.setShadowIntensity(0.75f);
dlsf.setLambda(0.65f);
dlsf.setShadowZExtend(50);

getState(SceneProcessorState.class).getFpp().addFilter(dlsf);

VisualSystem (I added some debug info):

entities.forEach(entity -> {
            Model model = entity.get(Model.class);
            Spatial spatial = getApplication().getAssetManager().loadModel(model.getModel());
            SpatialUtils.getControl(spatial, SkeletonControl.class).ifPresent(skeletonControl -> {
                System.out.println("Total bone count: " + skeletonControl.getSkeleton().getBoneCount());
            });
            SpatialUtils.getGeometry(spatial).ifPresent(geometry -> {
                System.out.println("NumberOfBones: " + geometry.getMaterial().getParam("NumberOfBones"));
            });
            modelMap.put(entity.getId(), spatial);
            node.attachChild(spatial);
        });

System info (mac osx):

2018-04-28 11:41:37,579 INFO  [main] com.jme3.system.JmeDesktopSystem:345 - Running on jMonkeyEngine 3.2-stable
 * Branch: HEAD
 * Git Hash: f85624a
 * Build Date: 2018-01-21
2018-04-28 11:41:38,146 INFO  [jME3 Main] com.jme3.system.lwjgl.LwjglContext:101 - LWJGL 2.9.3 context running on thread jME3 Main
 * Graphics Adapter: null
 * Driver Version: null
 * Scaling Factor: 1
2018-04-28 11:41:38,217 INFO  [jME3 Main] com.jme3.renderer.opengl.GLRenderer:483 - OpenGL Renderer Information
 * Vendor: Intel Inc.
 * Renderer: Intel(R) Iris(TM) Graphics 6100
 * OpenGL Version: 2.1 INTEL-10.32.48
 * GLSL Version: 1.20
 * Profile: Compatibility

The full stacktrace:

Total bone count: 26
NumberOfBones: Int NumberOfBones : 26
2018-04-28 11:41:45,822 WARN  [jME3 Main] com.jme3.renderer.opengl.GLRenderer (GLRenderer.java:1266) - Bad compile of:
1	#version 110
2	#define SRGB 1
3	#define VERTEX_SHADER 1
4	#define NUM_BONES 0
5	// -- begin import Common/ShaderLib/GLSLCompat.glsllib --
6	#if defined GL_ES
7	#  define hfloat highp float
8	#  define hvec2  highp vec2
9	#  define hvec3  highp vec3
10	#  define hvec4  highp vec4
11	#  define lfloat lowp float
12	#  define lvec2 lowp vec2
13	#  define lvec3 lowp vec3
14	#  define lvec4 lowp vec4
15	#else
16	#  define hfloat float
17	#  define hvec2  vec2
18	#  define hvec3  vec3
19	#  define hvec4  vec4
20	#  define lfloat float
21	#  define lvec2  vec2
22	#  define lvec3  vec3
23	#  define lvec4  vec4
24	#endif
25	
26	#if __VERSION__ >= 130
27	out vec4 outFragColor;
28	#  define texture1D texture
29	#  define texture2D texture
30	#  define texture3D texture
31	#  define textureCube texture
32	#  define texture2DLod textureLod
33	#  define textureCubeLod textureLod
34	#  if defined VERTEX_SHADER
35	#    define varying out
36	#    define attribute in
37	#  elif defined FRAGMENT_SHADER
38	#    define varying in
39	#    define gl_FragColor outFragColor
40	#  endif
41	#endif
42	// -- end import Common/ShaderLib/GLSLCompat.glsllib --
43	// -- begin import Common/ShaderLib/Instancing.glsllib --
44	// Instancing GLSL library.
45	// 
46	// When the INSTANCING define is set in the shader, 
47	// all global matrices are replaced with "instanced" versions.
48	// One exception is g_NormalMatrix which becomes unusable,
49	// instead the function ApplyNormalTransform is used to transform
50	// the normal and tangent vectors into world view space.
51	
52	// The world matrix and normal transform quaternion need to be passed
53	// as vertex attributes "inWorldMatrix" and "inNormalRotationQuaternion"
54	// respectively. 
55	// The VertexBuffers for those two attributes 
56	// need to be configured into instanced mode (VertexBuffer.setInstanced(true)). 
57	//  - inWorldMatrix should have 12 * numInstances floats.
58	//  - inNormalRotationQuaternion should have 4 * numInstances.
59	// Thus, instancing data occupies 4 vertex attributes (16 / 4 = 4).
60	// 
61	// The GL_ARB_draw_instanced and GL_ARB_instanced_arrays extensions 
62	// are required (OGL 3.3).
63	
64	uniform mat4 g_WorldMatrix;
65	uniform mat4 g_ViewMatrix;
66	uniform mat4 g_ProjectionMatrix;
67	uniform mat4 g_WorldViewMatrix;
68	uniform mat4 g_WorldViewProjectionMatrix;
69	uniform mat4 g_ViewProjectionMatrix;
70	uniform mat3 g_NormalMatrix;
71	uniform mat3 g_WorldNormalMatrix;
72	
73	#if defined INSTANCING
74	
75	// World Matrix + Normal Rotation Quaternion. 
76	// The World Matrix is the top 3 rows - 
77	//     since the bottom row is always 0,0,0,1 for this transform.
78	// The bottom row is the transpose of the inverse of WorldView Transform 
79	//     as a quaternion. i.e. g_NormalMatrix converted to a quaternion.
80	//
81	// Using a quaternion instead of a matrix here allows saving approximately
82	// 2 vertex attributes which now can be used for additional per-vertex data.
83	attribute mat4 inInstanceData;
84	
85	
86	
87	vec4 TransformWorld(vec4 position)
88	{
89	    // Extract the world matrix out of the instance data, leaving out the
90	    // quaternion at the end.
91	    mat4 worldMatrix = mat4(vec4(inInstanceData[0].xyz, 0.0),
92	    vec4(inInstanceData[1].xyz, 0.0),
93	    vec4(inInstanceData[2].xyz, 0.0),
94	    vec4(inInstanceData[3].xyz, 1.0));
95	    return (worldMatrix * position);
96	}
97	
98	vec4 TransformWorldView(vec4 position)
99	{
100	    return g_ViewMatrix * TransformWorld(position);
101	}
102	
103	vec4 TransformWorldViewProjection(vec4 position)
104	{
105	    return g_ViewProjectionMatrix * TransformWorld(position);
106	}
107	
108	vec3 TransformWorldNormal(vec3 vec) {
109	    vec4 quat = vec4(inInstanceData[0].w, inInstanceData[1].w,
110	                     inInstanceData[2].w, inInstanceData[3].w);
111	
112	    return vec + vec3(2.0) * cross(cross(vec, quat.xyz) + vec3(quat.w) * vec, quat.xyz);
113	}
114	
115	vec3 TransformNormal(vec3 vec)
116	{
117	    return (g_ViewMatrix * vec4(TransformWorldNormal(vec), 0.0)).xyz;
118	}
119	
120	// Prevent user from using g_** matrices which will have invalid data in this case.
121	#define g_WorldMatrix               Use_the_instancing_functions_for_this
122	#define g_WorldViewMatrix           Use_the_instancing_functions_for_this
123	#define g_WorldViewProjectionMatrix Use_the_instancing_functions_for_this
124	#define g_NormalMatrix              Use_the_instancing_functions_for_this
125	
126	#else
127	
128	vec4 TransformWorld(vec4 position)
129	{
130	    return g_WorldMatrix * position;
131	}
132	
133	vec4 TransformWorldView(vec4 position)
134	{
135	    return g_WorldViewMatrix * position;
136	}
137	
138	vec4 TransformWorldViewProjection(vec4 position)
139	{
140	    return g_WorldViewProjectionMatrix * position;
141	}
142	
143	vec3 TransformNormal(vec3 normal) {
144		return g_NormalMatrix * normal;
145	}
146	
147	vec3 TransformWorldNormal(vec3 normal) {
148	    return normalize(g_WorldNormalMatrix * normal);
149	}
150	
151	 
152	#endif
153	// -- end import Common/ShaderLib/Instancing.glsllib --
154	// -- begin import Common/ShaderLib/Skinning.glsllib --
155	#ifdef NUM_BONES
156	
157	#if NUM_BONES < 1 || NUM_BONES > 255
158	#error NUM_BONES must be between 1 and 255.
159	#endif
160	
161	#define NUM_WEIGHTS_PER_VERT 4
162	 
163	attribute vec4 inHWBoneWeight;
164	attribute vec4 inHWBoneIndex;
165	uniform mat4 m_BoneMatrices[NUM_BONES];
166	
167	void Skinning_Compute(inout vec4 position){
168	    if (inHWBoneWeight.x != 0.0) {
169	#if NUM_WEIGHTS_PER_VERT == 1
170	        position = m_BoneMatrices[int(inHWBoneIndex.x)] * position;
171	#else
172	        mat4 mat = mat4(0.0);
173	        mat += m_BoneMatrices[int(inHWBoneIndex.x)] * inHWBoneWeight.x;
174	        mat += m_BoneMatrices[int(inHWBoneIndex.y)] * inHWBoneWeight.y;
175	        mat += m_BoneMatrices[int(inHWBoneIndex.z)] * inHWBoneWeight.z;
176	        mat += m_BoneMatrices[int(inHWBoneIndex.w)] * inHWBoneWeight.w;
177	        position = mat * position;
178	#endif
179	    }
180	}
181	 
182	void Skinning_Compute(inout vec4 position, inout vec3 normal){
183	    if (inHWBoneWeight.x != 0.0) {
184	#if NUM_WEIGHTS_PER_VERT == 1
185	        position = m_BoneMatrices[int(inHWBoneIndex.x)] * position;
186	        normal = (mat3(m_BoneMatrices[int(inHWBoneIndex.x)][0].xyz,
187	                       m_BoneMatrices[int(inHWBoneIndex.x)][1].xyz,
188	                       m_BoneMatrices[int(inHWBoneIndex.x)][2].xyz) * normal);
189	#else
190	        mat4 mat = mat4(0.0);
191	        mat += m_BoneMatrices[int(inHWBoneIndex.x)] * inHWBoneWeight.x;
192	        mat += m_BoneMatrices[int(inHWBoneIndex.y)] * inHWBoneWeight.y;
193	        mat += m_BoneMatrices[int(inHWBoneIndex.z)] * inHWBoneWeight.z;
194	        mat += m_BoneMatrices[int(inHWBoneIndex.w)] * inHWBoneWeight.w;
195	        position = mat * position;
196	
197	        mat3 rotMat = mat3(mat[0].xyz, mat[1].xyz, mat[2].xyz);
198	        normal = rotMat * normal;
199	#endif
200	    }
201	}
202	 
203	void Skinning_Compute(inout vec4 position, inout vec3 normal, inout vec3 tangent){
204	    if (inHWBoneWeight.x != 0.0) {
205	#if NUM_WEIGHTS_PER_VERT == 1
206	        position = m_BoneMatrices[int(inHWBoneIndex.x)] * position;
207	        tangent = m_BoneMatrices[int(inHWBoneIndex.x)] * tangent;
208	        normal = (mat3(m_BoneMatrices[int(inHWBoneIndex.x)][0].xyz,
209	                       m_BoneMatrices[int(inHWBoneIndex.x)][1].xyz,
210	                       m_BoneMatrices[int(inHWBoneIndex.x)][2].xyz) * normal);
211	#else
212	        mat4 mat = mat4(0.0);
213	        mat += m_BoneMatrices[int(inHWBoneIndex.x)] * inHWBoneWeight.x;
214	        mat += m_BoneMatrices[int(inHWBoneIndex.y)] * inHWBoneWeight.y;
215	        mat += m_BoneMatrices[int(inHWBoneIndex.z)] * inHWBoneWeight.z;
216	        mat += m_BoneMatrices[int(inHWBoneIndex.w)] * inHWBoneWeight.w;
217	        position = mat * position;
218	
219	        mat3 rotMat = mat3(mat[0].xyz, mat[1].xyz, mat[2].xyz);
220	        tangent = rotMat * tangent;
221	        normal = rotMat * normal;
222	#endif
223	    }
224	}
225	
226	#endif
227	// -- end import Common/ShaderLib/Skinning.glsllib --
228	attribute vec3 inPosition;
229	attribute vec2 inTexCoord;
230	
231	varying vec2 texCoord;
232	
233	void main(){
234	    vec4 modelSpacePos = vec4(inPosition, 1.0);
235	  
236	   #ifdef NUM_BONES
237	       Skinning_Compute(modelSpacePos);
238	   #endif
239	    gl_Position = TransformWorldViewProjection(modelSpacePos);
240	    texCoord = inTexCoord;
241	}

2018-04-28 11:41:45,842 ERROR [jME3 Main] com.jme3.app.LegacyApplication:617 - Uncaught exception thrown in Thread[jME3 Main,5,main]
com.jme3.renderer.RendererException: compile error in: ShaderSource[name=Common/MatDefs/Shadow/PreShadow.vert, defines, type=Vertex, language=GLSL100]
ERROR: 0:158: '' :    NUM_BONES must be between 1 and 255.

	at com.jme3.renderer.opengl.GLRenderer.updateShaderSourceData(GLRenderer.java:1269) ~[jme3-core-3.2.1-stable.jar:3.2-stable]
	at com.jme3.renderer.opengl.GLRenderer.updateShaderData(GLRenderer.java:1296) ~[jme3-core-3.2.1-stable.jar:3.2-stable]
	at com.jme3.renderer.opengl.GLRenderer.setShader(GLRenderer.java:1360) ~[jme3-core-3.2.1-stable.jar:3.2-stable]
	at com.jme3.material.logic.DefaultTechniqueDefLogic.render(DefaultTechniqueDefLogic.java:94) ~[jme3-core-3.2.1-stable.jar:3.2-stable]
	at com.jme3.material.Technique.render(Technique.java:166) ~[jme3-core-3.2.1-stable.jar:3.2-stable]
	at com.jme3.material.Material.render(Material.java:974) ~[jme3-core-3.2.1-stable.jar:3.2-stable]
	at com.jme3.renderer.RenderManager.renderGeometry(RenderManager.java:598) ~[jme3-core-3.2.1-stable.jar:3.2-stable]
	at com.jme3.renderer.queue.RenderQueue.renderGeometryList(RenderQueue.java:266) ~[jme3-core-3.2.1-stable.jar:3.2-stable]
	at com.jme3.renderer.queue.RenderQueue.renderShadowQueue(RenderQueue.java:275) ~[jme3-core-3.2.1-stable.jar:3.2-stable]
	at com.jme3.shadow.AbstractShadowRenderer.renderShadowMap(AbstractShadowRenderer.java:439) ~[jme3-core-3.2.1-stable.jar:3.2-stable]
	at com.jme3.shadow.AbstractShadowRenderer.postQueue(AbstractShadowRenderer.java:412) ~[jme3-core-3.2.1-stable.jar:3.2-stable]
	at com.jme3.shadow.AbstractShadowFilter.postQueue(AbstractShadowFilter.java:118) ~[jme3-core-3.2.1-stable.jar:3.2-stable]
	at com.jme3.post.FilterPostProcessor.postQueue(FilterPostProcessor.java:241) ~[jme3-core-3.2.1-stable.jar:3.2-stable]
	at com.jme3.renderer.RenderManager.renderViewPort(RenderManager.java:1103) ~[jme3-core-3.2.1-stable.jar:3.2-stable]
	at com.jme3.renderer.RenderManager.render(RenderManager.java:1158) ~[jme3-core-3.2.1-stable.jar:3.2-stable]
	at com.jme3.app.SimpleApplication.update(SimpleApplication.java:253) ~[jme3-core-3.2.1-stable.jar:3.2-stable]
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151) ~[jme3-lwjgl-3.2.1-stable.jar:3.2-stable]
	at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:197) ~[jme3-lwjgl-3.2.1-stable.jar:3.2-stable]
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:232) ~[jme3-lwjgl-3.2.1-stable.jar:3.2-stable]
	at java.lang.Thread.run(Thread.java:748) [?:1.8.0_144]

Hope you guys can help me solve this :slight_smile:

edit: the problem is apparantly somewhere in the creation of the j3o file of the character. I have written a tool that converts .blend files to .j3o files. it does this by loading the blender file: assetmanager.loadmodel("path/to/blend")and then using the BinaryExporter.getInstance().save() method to create a .j3o file. I now used the SDK to convert the blend file and the error is gone.
How does the converter in the SDK work? Or what method should I use to convert my blender models to a jme binary?

1 Like

In case anyone else ever stumbles upon this issue, I believe the root cause was MatParamOverride reading null integer parameters as 0. This issue was never documented at GitHub, but it was fixed by the following commit:
Fixes mat param override reading null integenr params as 0 · jMonkeyEngine/jmonkeyengine@631794f · GitHub
This fix made it into JME 3.2.2 but not JME 3.2.1.

Here’s the chain of events as I understand it:

NUM_BONES is an integer parameter used by SkeletonControl. Its value isn’t set until SkeletonControl.switchToHardware() is invoked. A conversion utility has no call to render the model, so it never invokes the method, and the value is never set. The material-parameter override is serialized to the .j3o file with a null value. When deserialized, it gets read as 0. Attempting to render an animated model with NUM_BONES=0 triggers a RendererException.

3 Likes