Blender 2.6 JSON exporter, texture is wrong only one side of the cube

I am making a simple exporter for Blender 2.6x for custom JSON format (mainly for use with WebGL), because existing ones that I can find on the Internet do not work with Blender 2.6.

I almost earned, but there remains one error that I can not understand. On a simple cube, the texture on one of its sides is in the wrong orientation. The rest of the cube is textured correctly.

Here you can see the image of the problem (the left side on the left side is in the wrong orientation compared to the correct cube on the right side):

the bug

Are there any common misconceptions or errors that can cause this behavior?

This is a function that exports from Blender 2.65 to my custom JSON format (the error should be here, but I cannot find it):

def get_json(objects, scene): """ Currently only supports one scene. Exports with -Z forward, Y up. """ object_number = -1 scene_data = [] # for every object in the scene for object in bpy.context.scene.objects: # if the object is a mesh if object.type == 'MESH': object_number += 1 # convert all the mesh faces to triangles bpy.ops.object.mode_set(mode='OBJECT') object.select = True bpy.context.scene.objects.active = object # triangulate using new Blender 2.65 Triangulate modifier bpy.ops.object.modifier_add(type='TRIANGULATE') object.modifiers["Triangulate"].use_beauty = False bpy.ops.object.modifier_apply(apply_as="DATA", modifier="Triangulate") bpy.ops.object.mode_set(mode='OBJECT') object.select = False # add data to scene_data structure scene_data.append({ "name" : object.name, "vertices" : [], "indices" : [], "normals" : [], "tex_coords" : [] }) vertex_number = -1 # for each face in the object for face in object.data.polygons: vertices_in_face = face.vertices[:] # for each vertex in the face for vertex in vertices_in_face: vertex_number += 1 # store vertices in scene_data structure scene_data[object_number]["vertices"].append( object.data.vertices[vertex].co.x + object.location.x ) scene_data[object_number]["vertices"].append( object.data.vertices[vertex].co.z + object.location.z ) scene_data[object_number]["vertices"].append( -(object.data.vertices[vertex].co.y + object.location.y) ) # store normals in scene_data structure scene_data[object_number]["normals"].append( object.data.vertices[vertex].normal.x ) scene_data[object_number]["normals"].append( object.data.vertices[vertex].normal.z ) scene_data[object_number]["normals"].append( -(object.data.vertices[vertex].normal.y) ) # store indices in scene_data structure scene_data[object_number]["indices"].append(vertex_number) # texture coordinates # bug: for a simple cube, one face texture is warped mesh = object.to_mesh(bpy.context.scene, True, 'PREVIEW') if len(mesh.tessface_uv_textures) > 0: for data in mesh.tessface_uv_textures.active.data: scene_data[object_number]["tex_coords"].append( data.uv1.x ) scene_data[object_number]["tex_coords"].append( data.uv1.y ) scene_data[object_number]["tex_coords"].append( data.uv2.x ) scene_data[object_number]["tex_coords"].append( data.uv2.y ) scene_data[object_number]["tex_coords"].append( data.uv3.x ) scene_data[object_number]["tex_coords"].append( data.uv3.y ) return json.dumps(scene_data, indent=4) 

And in case this helps to find out, here are the exported JSON data obtained from running my export script (the same data as for displaying the cube on the left in the image above):

 [ { "vertices": [ -1.0203653573989868, 1.0320179611444473, 0.669445663690567, -1.0203653573989868, 1.0320179611444473, -1.330554336309433, -1.0203653573989868, -0.9679820388555527, 0.669445663690567, -1.0203653573989868, 1.0320179611444473, -1.330554336309433, 0.9796346426010132, 1.0320179611444473, -1.330554336309433, -1.0203653573989868, -0.9679820388555527, -1.330554336309433, 0.9796346426010132, 1.0320179611444473, -1.330554336309433, 0.9796346426010132, 1.0320179611444473, 0.669445663690567, 0.9796346426010132, -0.9679820388555527, -1.330554336309433, 0.9796346426010132, 1.0320179611444473, 0.669445663690567, -1.0203653573989868, 1.0320179611444473, 0.669445663690567, 0.9796346426010132, -0.9679820388555527, 0.669445663690567, -1.0203653573989868, -0.9679820388555527, 0.669445663690567, -1.0203653573989868, -0.9679820388555527, -1.330554336309433, 0.9796346426010132, -0.9679820388555527, 0.669445663690567, 0.9796346426010132, 1.0320179611444473, 0.669445663690567, 0.9796346426010132, 1.0320179611444473, -1.330554336309433, -1.0203653573989868, 1.0320179611444473, 0.669445663690567, -1.0203653573989868, 1.0320179611444473, -1.330554336309433, -1.0203653573989868, -0.9679820388555527, -1.330554336309433, -1.0203653573989868, -0.9679820388555527, 0.669445663690567, 0.9796346426010132, 1.0320179611444473, -1.330554336309433, 0.9796346426010132, -0.9679820388555527, -1.330554336309433, -1.0203653573989868, -0.9679820388555527, -1.330554336309433, 0.9796346426010132, 1.0320179611444473, 0.669445663690567, 0.9796346426010132, -0.9679820388555527, 0.669445663690567, 0.9796346426010132, -0.9679820388555527, -1.330554336309433, -1.0203653573989868, 1.0320179611444473, 0.669445663690567, -1.0203653573989868, -0.9679820388555527, 0.669445663690567, 0.9796346426010132, -0.9679820388555527, 0.669445663690567, -1.0203653573989868, -0.9679820388555527, -1.330554336309433, 0.9796346426010132, -0.9679820388555527, -1.330554336309433, 0.9796346426010132, -0.9679820388555527, 0.669445663690567, 0.9796346426010132, 1.0320179611444473, -1.330554336309433, -1.0203653573989868, 1.0320179611444473, -1.330554336309433, -1.0203653573989868, 1.0320179611444473, 0.669445663690567 ], "normals": [ -0.5773491859436035, 0.5773491859436035, 0.5773491859436035, -0.5773491859436035, 0.5773491859436035, -0.5773491859436035, -0.5773491859436035, -0.5773491859436035, 0.5773491859436035, -0.5773491859436035, 0.5773491859436035, -0.5773491859436035, 0.5773491859436035, 0.5773491859436035, -0.5773491859436035, -0.5773491859436035, -0.5773491859436035, -0.5773491859436035, 0.5773491859436035, 0.5773491859436035, -0.5773491859436035, 0.5773491859436035, 0.5773491859436035, 0.5773491859436035, 0.5773491859436035, -0.5773491859436035, -0.5773491859436035, 0.5773491859436035, 0.5773491859436035, 0.5773491859436035, -0.5773491859436035, 0.5773491859436035, 0.5773491859436035, 0.5773491859436035, -0.5773491859436035, 0.5773491859436035, -0.5773491859436035, -0.5773491859436035, 0.5773491859436035, -0.5773491859436035, -0.5773491859436035, -0.5773491859436035, 0.5773491859436035, -0.5773491859436035, 0.5773491859436035, 0.5773491859436035, 0.5773491859436035, 0.5773491859436035, 0.5773491859436035, 0.5773491859436035, -0.5773491859436035, -0.5773491859436035, 0.5773491859436035, 0.5773491859436035, -0.5773491859436035, 0.5773491859436035, -0.5773491859436035, -0.5773491859436035, -0.5773491859436035, -0.5773491859436035, -0.5773491859436035, -0.5773491859436035, 0.5773491859436035, 0.5773491859436035, 0.5773491859436035, -0.5773491859436035, 0.5773491859436035, -0.5773491859436035, -0.5773491859436035, -0.5773491859436035, -0.5773491859436035, -0.5773491859436035, 0.5773491859436035, 0.5773491859436035, 0.5773491859436035, 0.5773491859436035, -0.5773491859436035, 0.5773491859436035, 0.5773491859436035, -0.5773491859436035, -0.5773491859436035, -0.5773491859436035, 0.5773491859436035, 0.5773491859436035, -0.5773491859436035, -0.5773491859436035, 0.5773491859436035, 0.5773491859436035, -0.5773491859436035, 0.5773491859436035, -0.5773491859436035, -0.5773491859436035, -0.5773491859436035, 0.5773491859436035, -0.5773491859436035, -0.5773491859436035, 0.5773491859436035, -0.5773491859436035, 0.5773491859436035, 0.5773491859436035, 0.5773491859436035, -0.5773491859436035, -0.5773491859436035, 0.5773491859436035, -0.5773491859436035, -0.5773491859436035, 0.5773491859436035, 0.5773491859436035 ], "indices": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35 ], "name": "Cube", "tex_coords": [ 0.008884529583156109, 0.6587533354759216, 0.3244488537311554, 0.3412468135356903, 0.32541996240615845, 0.657782256603241, 0.008884510956704617, 0.32541996240615845, 0.007913422770798206, 0.008884549140930176, 0.32541996240615845, 0.3244488537311554, 0.9920865893363953, 0.32444891333580017, 0.675551176071167, 0.32541996240615845, 0.9911155700683594, 0.00791349820792675, 0.3412467837333679, 0.008884538896381855, 0.6577821969985962, 0.007913422770798206, 0.34221789240837097, 0.32541996240615845, 0.6587532758712769, 0.6577821969985962, 0.3422178626060486, 0.6587533354759216, 0.6577821373939514, 0.3412468135356903, 0.6745801568031311, 0.34221789240837097, 0.9911155700683594, 0.3412468135356903, 0.6755512356758118, 0.6587533354759216, 0.007913460955023766, 0.34221789240837097, 0.3244488537311554, 0.3412468135356903, 0.008884529583156109, 0.6587533354759216, 0.007913422770798206, 0.008884549140930176, 0.324448823928833, 0.007913422770798206, 0.32541996240615845, 0.3244488537311554, 0.675551176071167, 0.32541996240615845, 0.6745801568031311, 0.008884529583156109, 0.9911155700683594, 0.00791349820792675, 0.6577821969985962, 0.007913422770798206, 0.6587533354759216, 0.3244488835334778, 0.34221789240837097, 0.32541996240615845, 0.3422178626060486, 0.6587533354759216, 0.3412467837333679, 0.34221789240837097, 0.6577821373939514, 0.3412468135356903, 0.9911155700683594, 0.3412468135356903, 0.99208664894104, 0.6577821969985962, 0.6755512356758118, 0.6587533354759216 ] } ] 

I’m not currently looking for ways to make more filled out functionality or a more efficient exporter, but I would just like to finally throw away this error so that I can tackle more interesting things like creating a game in WebGL and find out about collision detection and etc. Any help or advice regarding this issue that I have will be greatly appreciated!

Edit:

In case it could be my rendering code, and not the exporter, that the problem is, here is the part of my WebGL code related to initializing buffers and painting a scene (a modified version of some code found at http://learningwebgl.com )

 var gl; var current_shader_program; var per_vertex_shader_program; var per_fragment_shader_program; var modelview_matrix = mat4.create(); var modelview_matrix_stack = []; var projection_matrix = mat4.create(); var teapot_vertex_position_buffer = new Array(); var teapot_vertex_tex_coord_buffer = new Array(); var teapot_vertex_normal_buffer = new Array(); var teapot_vertex_index_buffer = new Array(); var earth_texture; var galvanized_texture; var teapot_angle = 180; var last_time = 0; function createProgram(vertex_shader_filename, fragment_shader_filename) { var vertex_shader_text = readFromUrl(vertex_shader_filename); var fragment_shader_text = readFromUrl(fragment_shader_filename); var vertex_shader = gl.createShader(gl.VERTEX_SHADER); var fragment_shader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(vertex_shader, vertex_shader_text); gl.shaderSource(fragment_shader, fragment_shader_text); gl.compileShader(vertex_shader); if (!gl.getShaderParameter(vertex_shader, gl.COMPILE_STATUS)) { alert(gl.getShaderInfoLog(vertex_shader)); } gl.compileShader(fragment_shader); if (!gl.getShaderParameter(fragment_shader, gl.COMPILE_STATUS)) { alert(gl.getShaderInfoLog(fragment_shader)); } var shader_program = gl.createProgram(); gl.attachShader(shader_program, vertex_shader); gl.attachShader(shader_program, fragment_shader); gl.linkProgram(shader_program); if (!gl.getProgramParameter(shader_program, gl.LINK_STATUS)) { alert("Error: Unable to link shaders!"); } shader_program.vertex_position_attribute = gl.getAttribLocation(shader_program, "a_vertex_position"); gl.enableVertexAttribArray(shader_program.vertex_position_attribute); shader_program.vertex_normal_attribute = gl.getAttribLocation(shader_program, "a_vertex_normal"); gl.enableVertexAttribArray(shader_program.vertex_normal_attribute); shader_program.tex_coord_attribute = gl.getAttribLocation(shader_program, "a_tex_coord"); gl.enableVertexAttribArray(shader_program.tex_coord_attribute); shader_program.projection_matrix_uniform = gl.getUniformLocation(shader_program, "u_projection_matrix"); shader_program.modelview_matrix_uniform = gl.getUniformLocation(shader_program, "u_modelview_matrix"); shader_program.normal_matrix_uniform = gl.getUniformLocation(shader_program, "u_normal_matrix"); shader_program.sampler_uniform = gl.getUniformLocation(shader_program, "u_sampler"); shader_program.material_shininess_uniform = gl.getUniformLocation(shader_program, "u_material_shininess"); shader_program.show_specular_highlights_uniform = gl.getUniformLocation(shader_program, "u_show_specular_highlights"); shader_program.use_textures_uniform = gl.getUniformLocation(shader_program, "u_use_textures"); shader_program.use_lighting_uniform = gl.getUniformLocation(shader_program, "u_use_lighting"); shader_program.ambient_color_uniform = gl.getUniformLocation(shader_program, "u_ambient_color"); shader_program.point_lighting_location_uniform = gl.getUniformLocation(shader_program, "u_point_lighting_location"); shader_program.point_lighting_specular_color_uniform = gl.getUniformLocation(shader_program, "u_point_lighting_specular_color"); shader_program.point_lighting_diffuse_color_uniform = gl.getUniformLocation(shader_program, "u_point_lighting_diffuse_color"); return shader_program; } function initShaders() { per_fragment_shader_program = createProgram("per_fragment_lighting.vs", "per_fragment_lighting.fs"); } function handleLoadedTexture(texture) { gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST); gl.generateMipmap(gl.TEXTURE_2D); gl.bindTexture(gl.TEXTURE_2D, null); } function initTextures() { earth_texture = gl.createTexture(); earth_texture.image = new Image(); earth_texture.image.onload = function() { handleLoadedTexture(earth_texture); } earth_texture.image.src = "earth.jpg"; galvanized_texture = gl.createTexture(); galvanized_texture.image = new Image(); galvanized_texture.image.onload = function() { handleLoadedTexture(galvanized_texture); }; galvanized_texture.image.src = "galvanized.jpg"; } function setMatrixUniforms() { gl.uniformMatrix4fv(current_shader_program.projection_matrix_uniform, false, projection_matrix); gl.uniformMatrix4fv(current_shader_program.modelview_matrix_uniform, false, modelview_matrix); var normal_matrix = mat3.create(); mat4.toInverseMat3(modelview_matrix, normal_matrix); mat3.transpose(normal_matrix); gl.uniformMatrix3fv(current_shader_program.normal_matrix_uniform, false, normal_matrix); } function handleLoadedTeapot(teapot_data) { for (var i = 0; i < teapot_data.length; i++) { teapot_vertex_normal_buffer[i] = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, teapot_vertex_normal_buffer[i]); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(teapot_data[i].normals), gl.STATIC_DRAW); teapot_vertex_normal_buffer[i].item_size = 3; teapot_vertex_normal_buffer[i].num_items = teapot_data[i].normals.length / teapot_vertex_normal_buffer[i].item_size; teapot_vertex_tex_coord_buffer[i] = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, teapot_vertex_tex_coord_buffer[i]); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(teapot_data[i].tex_coords), gl.STATIC_DRAW); teapot_vertex_tex_coord_buffer[i].item_size = 2; teapot_vertex_tex_coord_buffer[i].num_items = teapot_data[i].tex_coords.length / teapot_vertex_tex_coord_buffer[i].item_size; teapot_vertex_position_buffer[i] = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, teapot_vertex_position_buffer[i]); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(teapot_data[i].vertices), gl.STATIC_DRAW); teapot_vertex_position_buffer[i].item_size = 3; teapot_vertex_position_buffer[i].num_items = teapot_data[i].vertices.length / teapot_vertex_position_buffer[i].item_size; teapot_vertex_index_buffer[i] = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, teapot_vertex_index_buffer[i]); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(teapot_data[i].indices), gl.STATIC_DRAW) teapot_vertex_index_buffer[i].item_size = 1; teapot_vertex_index_buffer[i].num_items = teapot_data[i].indices.length / teapot_vertex_index_buffer[i].item_size; } document.getElementById("loading_text").textContent = ""; } function loadTeapot() { var request = new XMLHttpRequest(); request.open("GET", "untitled.json"); request.onreadystatechange = function() { if (request.readyState == 4) { handleLoadedTeapot(JSON.parse(request.responseText)); } }; request.send(); } function drawScene() { gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); if (teapot_vertex_position_buffer[0] == null || teapot_vertex_normal_buffer[0] == null || teapot_vertex_tex_coord_buffer[0] == null || teapot_vertex_index_buffer[0] == null) { return; } current_shader_program = per_fragment_shader_program; gl.useProgram(current_shader_program); var specular_highlights = document.getElementById("specular").checked; gl.uniform1i(current_shader_program.show_specular_highlights_uniform, specular_highlights); var lighting = document.getElementById("lighting").checked; gl.uniform1i(current_shader_program.use_lighting_uniform, lighting); if (lighting) { gl.uniform3f(current_shader_program.ambient_color_uniform, parseFloat(document.getElementById("ambient_r").value), parseFloat(document.getElementById("ambient_g").value), parseFloat(document.getElementById("ambient_b").value)); gl.uniform3f(current_shader_program.point_lighting_location_uniform, parseFloat(document.getElementById("light_pos_x").value), parseFloat(document.getElementById("light_pos_y").value), parseFloat(document.getElementById("light_pos_z").value)); gl.uniform3f(current_shader_program.point_lighting_specular_color_uniform, parseFloat(document.getElementById("specular_r").value), parseFloat(document.getElementById("specular_g").value), parseFloat(document.getElementById("specular_b").value)); gl.uniform3f(current_shader_program.point_lighting_diffuse_color_uniform, parseFloat(document.getElementById("diffuse_r").value), parseFloat(document.getElementById("diffuse_g").value), parseFloat(document.getElementById("diffuse_b").value)); } var texture = document.getElementById("texture").value; gl.uniform1i(current_shader_program.use_textures_uniform, texture != "none"); mat4.identity(modelview_matrix); mat4.translate(modelview_matrix, [0, 0, -10]); mat4.rotate(modelview_matrix, degToRad(23.4), [1, 0, 0]); mat4.rotate(modelview_matrix, degToRad(teapot_angle), [0, 1, 0]); gl.activeTexture(gl.TEXTURE0); if (texture == "earth") { gl.bindTexture(gl.TEXTURE_2D, earth_texture); } else if (texture == "galvanized") { gl.bindTexture(gl.TEXTURE_2D, galvanized_texture); } gl.uniform1i(current_shader_program.sampler_uniform, 0); gl.uniform1f(current_shader_program.material_shininess_uniform, parseFloat(document.getElementById("shininess").value)); for (var i = 0; i < teapot_vertex_position_buffer.length; i++) { gl.bindBuffer(gl.ARRAY_BUFFER, teapot_vertex_position_buffer[i]); gl.vertexAttribPointer(current_shader_program.vertex_position_attribute, teapot_vertex_position_buffer[i].item_size, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ARRAY_BUFFER, teapot_vertex_tex_coord_buffer[i]); gl.vertexAttribPointer(current_shader_program.tex_coord_attribute, teapot_vertex_tex_coord_buffer[i].item_size, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ARRAY_BUFFER, teapot_vertex_normal_buffer[i]); gl.vertexAttribPointer(current_shader_program.vertex_normal_attribute, teapot_vertex_normal_buffer[i].item_size, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, teapot_vertex_index_buffer[i]); setMatrixUniforms(); gl.drawElements(gl.TRIANGLES, teapot_vertex_index_buffer[i].num_items, gl.UNSIGNED_SHORT, 0); } } 

I know this is a hell of a lot because I haven't published it before, but it has been suggested that the visualization code might be to blame. Please note that the β€œkettle” mentioned in the code is really an exported model (the cube I'm trying to execute).

+8
json python blender opengl webgl
source share
1 answer

As ejrowley commented, the indexing of your vertices and texture coordinates are not aligned.

Also, using to_mesh after exporting vertices can lead to a lot of madness.

You can determine the correct indexing using your tessfaces grid.

+1
source share

All Articles