/** * Copyright (C) 2014-2016 Triumph LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ "use strict"; /** * Geometry internal API. * Don't forget to register GL context by setup_context() function. * @name geometry * @namespace * @exports exports as geometry */ b4w.module["__geometry"] = function(exports, require) { var m_bounds = require("__boundings"); var m_ext = require("__extensions"); var m_print = require("__print"); var m_quat = require("__quat"); var m_tsr = require("__tsr"); var m_util = require("__util"); var m_vec3 = require("__vec3"); var _vec2_tmp = new Float32Array(2); var _vec3_tmp = new Float32Array(3); var _vec3_tmp2 = new Float32Array(3); var _vec3_tmp3 = new Float32Array(3); var _quat_tmp = new Float32Array(4); var _quat_tmp2 = new Float32Array(4); var _tsr_tmp = new Float32Array(8); var MAX_SUBMESH_LENGTH = 256*256; var COMB_SORT_JUMP_COEFF = 1.247330950103979; // numbers of components per attribute var POS_NUM_COMP = 3; var NOR_NUM_COMP = 3; var TBN_QUAT_NUM_COMP = 4; var TAN_NUM_COMP = 4; var COL_NUM_COMP = 3; var SHD_TAN_NUM_COMP = 3; // deprecated var INFLUENCE_NUM_COMP = 4; // draw modes exports.DM_TRIANGLES = 10; exports.DM_DYNAMIC_TRIANGLES = 20; exports.SORT_NUMERIC = 0; exports.SORT_STRING = 1; exports.DM_DEFAULT = exports.DM_TRIANGLES; var _gl = null; // NOTE: should be strings as object keys var VBO_FLOAT = "float"; var VBO_SHORT = "short"; var VBO_UBYTE = "unsigned_byte"; exports.VBO_FLOAT = VBO_FLOAT; exports.VBO_SHORT = VBO_SHORT; exports.VBO_UBYTE = VBO_UBYTE; /** * Setup WebGL context * @param gl WebGL context */ exports.setup_context = function(gl) { _gl = gl; } function init_attr_pointer() { var p = { length: 0, frames: 1, num_comp: 0, stride: 0, offset: 0, divisor: 0 } return p; } /** * Convert mesh/material object to gl buffer data */ exports.submesh_to_bufs_data = function(submesh, draw_mode, vc_usage, batch) { if (is_long_submesh(submesh)) submesh_drop_indices(submesh); var indices = submesh.indices; var base_length = submesh.base_length; var va_frames = submesh.va_frames; var va_common = {}; for (var attr_name in submesh.va_common) if (!(attr_name in vc_usage) || vc_usage[attr_name].generate_buffer) va_common[attr_name] = submesh.va_common[attr_name]; var bufs_data = init_bufs_data(); if (submesh.shape_keys.length > 0) submesh_init_shape_keys(submesh, va_frames[0]); generate_bufs_data_arrays(bufs_data, indices, va_frames, va_common, base_length, submesh.instanced_array_data); update_draw_mode(bufs_data, draw_mode); update_gl_buffers(bufs_data); bufs_data.shape_keys = submesh.shape_keys; return bufs_data; } exports.submesh_init_shape_keys = submesh_init_shape_keys; function submesh_init_shape_keys(submesh, frame) { var f_a_tbn_quat = frame["a_tbn_quat"]; var f_a_pos = frame["a_position"]; var tbn_quat_length = f_a_tbn_quat.length; var pos_length = f_a_pos.length; // position for (var i = 1; i < submesh.shape_keys.length; i++) { var geometry = submesh.shape_keys[i].geometry; var value = submesh.shape_keys[i].init_value; var a_pos = geometry["a_position"]; for (var j = 0; j < pos_length; j++) f_a_pos[j] += value * a_pos[j]; } // tbn_quat var sum_tbn_quat = _quat_tmp; for (var i = 0; i < tbn_quat_length; i+=TBN_QUAT_NUM_COMP) { var f_quat = f_a_tbn_quat.subarray(i, i + TBN_QUAT_NUM_COMP); sum_tbn_quat.set(m_util.QUAT4_IDENT); for (var j = 1; j < submesh.shape_keys.length; j++) { var geometry = submesh.shape_keys[j].geometry; var value = submesh.shape_keys[j].init_value; var a_tbn_quat = geometry["a_tbn_quat"]; var p_f_quat = m_quat.slerp(m_util.QUAT4_IDENT, a_tbn_quat, value, _quat_tmp2); m_quat.normalize(p_f_quat, p_f_quat); m_quat.multiply(p_f_quat, sum_tbn_quat, sum_tbn_quat); } m_quat.multiply(sum_tbn_quat, f_quat, f_quat); } } exports.is_long_submesh = is_long_submesh; /** * Check is given submesh is too long to have indices. * Max index value is * @methodOf geometry */ function is_long_submesh(submesh) { var base_length = submesh.base_length; if (base_length > MAX_SUBMESH_LENGTH) { if (m_ext.get_elem_index_uint()) return false; return true; } return false; } exports.is_indexed = is_indexed; /** * Check if submesh is indexed one */ function is_indexed(submesh) { if (submesh.indices.length > 0) return true; else return false; } exports.submesh_drop_indices = submesh_drop_indices; /** * Drop indices from long submesh and recalculate all VAs */ function submesh_drop_indices(submesh, count, is_manually_dropped) { if (!is_indexed(submesh)) return submesh; count = count || 1; if (!is_manually_dropped) m_print.log("%cDEBUG max vertices exceeded for indexed submesh \"" + submesh.name + "\": " + submesh.base_length * count + ", will use drawArrays", "color: #aa0"); var indices = submesh.indices; var base_length = submesh.base_length; var va_common = submesh.va_common; var va_frames = submesh.va_frames; for (var name in va_common) { var arr = va_common[name]; var nc = num_comp(arr, base_length); va_common[name] = expand_vertex_array_i(indices, arr, nc); } for (var i = 0; i < va_frames.length; i++) { var va_frame = va_frames[i]; for (var name in va_frame) { var arr = va_frame[name]; var nc = num_comp(arr, base_length); va_frame[name] = expand_vertex_array_i(indices, arr, nc); } } for (var i = 0; i < submesh.shape_keys.length; i++) { var geometry = submesh.shape_keys[i].geometry; for (var att_name in geometry) { var arr = geometry[att_name]; var nc = num_comp(arr, base_length); geometry[att_name] = expand_vertex_array_i(indices, arr, nc); } } submesh.base_length = indices.length; submesh.indices = new Uint16Array(0); return submesh; } /** * Create a new vertex array value for each index */ function expand_vertex_array_i(indices, vertex_array, num_comp) { if (vertex_array.length == 0) return new Float32Array(0); var len = indices.length * num_comp; var new_vertex_array = new Float32Array(len); for (var i = 0; i < indices.length; i++) { var index = indices[i]; for (var j = 0; j < num_comp; j++) new_vertex_array[num_comp*i + j] = vertex_array[num_comp*index + j]; } return new_vertex_array; } /** * Update index array for buffers data * @param {Object3D} bufs_data Buffers data * @param {Number} draw_mode Buffers draw mode * @param {Uint16Array|Uint32Array} indices Indices specified for TRIANGLES rendering */ exports.update_bufs_data_index_array = function(bufs_data, draw_mode, indices) { update_draw_mode(bufs_data, draw_mode); bufs_data.count = indices.length; bufs_data.ibo_array = indices; if (indices instanceof Uint16Array) bufs_data.ibo_type = _gl.UNSIGNED_SHORT; else bufs_data.ibo_type = _gl.UNSIGNED_INT; return bufs_data; } function init_bufs_data() { return { ibo_array: null, vbo_source_data: init_vbo_source_data(), ibo_type: 0, count: 0, pointers: {}, usage: 0, debug_ibo_bytes: 0, debug_vbo_bytes: 0, vbo_data: [], ibo: null, shape_keys: null, instance_count: 1, cleanup_gl_data_on_unload: true } } /** * Append or replace attribute array * @param {Float32Array|Int16Array|Uint8Array} array Attribute array */ exports.update_bufs_data_array = function(bufs_data, attrib_name, num_comp, array) { var pointers = bufs_data.pointers; var pointer = pointers[attrib_name]; var type = get_vbo_type_by_attr_name(attrib_name); if (pointer) { // replace attribute data if (num_comp && pointer.num_comp != num_comp) m_util.panic("invalid num_comp for \"" + attrib_name + "\""); vbo_source_data_set_attr(bufs_data.vbo_source_data, attrib_name, array, pointer.offset); } else { // append new attribute data var index = search_vbo_index_by_type(bufs_data.vbo_source_data, type); if (index == -1) { bufs_data.vbo_source_data.push(create_vbo_source_obj(type, 0)); index = bufs_data.vbo_source_data.length - 1; } var vbo_source = bufs_data.vbo_source_data[index].vbo_source; var new_vbo_source = new vbo_source.constructor(vbo_source.length + array.length); new_vbo_source.set(vbo_source); bufs_data.vbo_source_data[index].vbo_source = new_vbo_source; vbo_source_data_set_attr(bufs_data.vbo_source_data, attrib_name, array, vbo_source.length); // append new pointer var p = pointers[attrib_name] = init_attr_pointer(); p.num_comp = num_comp; p.offset = vbo_source.length; p.length = array.length; } update_gl_buffers(bufs_data); return bufs_data; } exports.extract_array = extract_array; /** * Get VBO buffer view by attribute name. * @methodOf geometry * @returns Link to VBO subarray */ function extract_array(bufs_data, name) { var pointer = bufs_data.pointers[name]; if (pointer) { var type = get_vbo_type_by_attr_name(name); var vbo_source = get_vbo_source_by_type(bufs_data.vbo_source_data, type); return vbo_source.subarray(pointer.offset, pointer.offset + pointer.length); } else m_util.panic("extract_array() failed; invalid name: " + name); } /** * Update GL mode (affects draw operations) and GL usage (affects buffers update) * @methodOf geometry */ function update_draw_mode(bufs_data, draw_mode) { var usage; switch (draw_mode) { case exports.DM_DEFAULT: case exports.DM_TRIANGLES: usage = _gl.STATIC_DRAW; break; case exports.DM_DYNAMIC_TRIANGLES: usage = _gl.DYNAMIC_DRAW; break; default: m_util.panic("Wrong draw_mode"); } bufs_data.usage = usage; } exports.make_static = function(bufs_data) { bufs_data.usage = _gl.STATIC_DRAW; } exports.make_dynamic = function(bufs_data) { bufs_data.usage = _gl.DYNAMIC_DRAW; } function generate_bufs_data_arrays(bufs_data, indices, va_frames, va_common, base_length, inst_ar_data) { if (indices.length) { var count = indices.length; if (base_length <= MAX_SUBMESH_LENGTH) { // NOTE: possible transform from Uint32Array, affects performance var ibo_array = new Uint16Array(indices); var ibo_type = _gl.UNSIGNED_SHORT; } else { var ibo_array = new Uint32Array(indices); var ibo_type = _gl.UNSIGNED_INT; } } else { var count = base_length; var ibo_array = null; } var lengths = calc_vbo_lengths(va_frames, va_common, inst_ar_data); var vbo_source_data = init_vbo_source_data(lengths); var offsets = {}; // in elements for (var i = 0; i < vbo_source_data.length; i++) offsets[vbo_source_data[i].type] = 0; var pointers = {}; for (var name in va_common) { var arr = va_common[name]; var len = arr.length; if (!len) continue; // copy src arrays to vbo var type = get_vbo_type_by_attr_name(name); var p = pointers[name] = init_attr_pointer(); p.length = len; p.num_comp = num_comp(arr, base_length); p.offset = offsets[type]; vbo_source_data_set_attr(vbo_source_data, name, arr, offsets[type]); offsets[type] += len; } var frames_count = va_frames.length; for (var name in va_frames[0]) { var arr0 = va_frames[0][name]; var type = get_vbo_type_by_attr_name(name); var len = arr0.length; var ncomp = num_comp(arr0, base_length); if (!len) continue; var p = pointers[name] = init_attr_pointer(); p.length = len; p.frames = frames_count; p.num_comp = ncomp; p.offset = offsets[type]; if (frames_count > 1) { var p = pointers[name + "_next"] = init_attr_pointer(); p.length = len; p.frames = frames_count; p.num_comp = ncomp; p.offset = offsets[type] + len; } for (var i = 0; i < frames_count; i++) { var va_frame = va_frames[i]; var arr = va_frame[name]; // copy src arrays to vbo var type = get_vbo_type_by_attr_name(name); vbo_source_data_set_attr(vbo_source_data, name, arr, offsets[type]); offsets[type] += len; } } if (inst_ar_data) { append_inst_array_data(inst_ar_data, pointers, vbo_source_data, offsets); bufs_data.instance_count = inst_ar_data.tsr_array.length; } bufs_data.count = count; bufs_data.ibo_array = ibo_array; bufs_data.vbo_source_data = vbo_source_data; bufs_data.ibo_type = ibo_type; bufs_data.pointers = pointers; } function append_inst_array_data(inst_ar_data, pointers, vbo_source_data, offsets) { var tsr_array = inst_ar_data.tsr_array; var tsr_data = []; var em_tsr = inst_ar_data.stat_part_em_tsr; var part_inh_attrs = inst_ar_data.part_inh_attrs; var submesh_params = inst_ar_data.submesh_params; var tsr_array = inst_ar_data.tsr_array; var em_tsr = inst_ar_data.stat_part_em_tsr; var part_inh_attrs = inst_ar_data.part_inh_attrs; var submesh_params = inst_ar_data.submesh_params; var static_hair = inst_ar_data.static_hair; var attrs_data = { "a_part_ts": { data: [], num_comp: 4 }, "a_part_r": { data: [], num_comp: 4 } }; for (var name in part_inh_attrs) attrs_data[name] = { data: [], num_comp: part_inh_attrs[name].num_comp }; for (var name in submesh_params) attrs_data[name] = { data: [], num_comp: 1 }; for (var i = 0; i < tsr_array.length; i++) { var tsr = tsr_array[i]; if (static_hair && em_tsr && !inst_ar_data.dyn_grass) tsr = m_tsr.multiply(em_tsr, tsr_array[i], _tsr_tmp); if (!static_hair && inst_ar_data.dyn_grass) m_vec3.subtract(tsr_array[i], em_tsr, tsr_array[i]); attrs_data["a_part_ts"].data.push(tsr[0], tsr[1], tsr[2], tsr[3]); attrs_data["a_part_r"].data.push(tsr[4], tsr[5], tsr[6], tsr[7]); for (var name in part_inh_attrs) { var len = part_inh_attrs[name].num_comp; for (var j = 0; j < len; j++) attrs_data[name].data.push(part_inh_attrs[name].data[i * len + j]); } for (var name in submesh_params) attrs_data[name].data.push(submesh_params[name][0]); } for (var name in attrs_data) { var type = get_vbo_type_by_attr_name(name); vbo_source_data_set_attr(vbo_source_data, name, attrs_data[name].data, offsets[type]); var pointer = init_attr_pointer(); pointer.num_comp = attrs_data[name].num_comp; pointer.offset = offsets[type]; pointer.divisor = 1; pointer.length = attrs_data[name].data.length; pointers[name] = pointer; offsets[type] += pointer.length; } } /** * Calculate vbo length (in elements) needed to store vertex arrays */ function calc_vbo_lengths(va_frames, va_common, inst_ar_data) { var lengths = {}; var inc_length = function(type, value) { if (!lengths[type]) lengths[type] = 0; lengths[type] += value; } for (var name in va_frames[0]) { var arr = va_frames[0][name]; var type = get_vbo_type_by_attr_name(name); inc_length(type, arr.length * va_frames.length); } for (var name in va_common) { var arr = va_common[name]; var type = get_vbo_type_by_attr_name(name); inc_length(type, arr.length); } if (inst_ar_data) { var num_part = inst_ar_data.tsr_array.length; var type = get_vbo_type_by_attr_name("a_part_ts"); inc_length(type, num_part * 4); var type = get_vbo_type_by_attr_name("a_part_r"); inc_length(type, num_part * 4); var part_inh_attrs = inst_ar_data.part_inh_attrs; for (var name in part_inh_attrs) { var type = get_vbo_type_by_attr_name(name); inc_length(type, part_inh_attrs[name].num_comp * num_part); } var submesh_params = inst_ar_data.submesh_params; for (var name in submesh_params) { var type = get_vbo_type_by_attr_name(name); inc_length(type, num_part); } } return lengths; } /** * Update gl buffers */ exports.update_gl_buffers = update_gl_buffers; function update_gl_buffers(bufs_data) { // index buffer object if (bufs_data.ibo_array) { if (!bufs_data.ibo) bufs_data.ibo = _gl.createBuffer(); _gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER, bufs_data.ibo); _gl.bufferData(_gl.ELEMENT_ARRAY_BUFFER, bufs_data.ibo_array, bufs_data.usage); bufs_data.debug_ibo_bytes = bufs_data.ibo_array.byteLength; } else bufs_data.debug_ibo_bytes = 0; // vertex buffer objects bufs_data.debug_vbo_bytes = 0; for (var i = 0; i < bufs_data.vbo_source_data.length; i++) { var type = bufs_data.vbo_source_data[i].type; var index = search_vbo_index_by_type(bufs_data.vbo_data, type); if (index == -1) { bufs_data.vbo_data.push({ vbo: null, type: type, debug_id: m_util.unique_id() }); index = bufs_data.vbo_data.length - 1; } var vbo_obj = bufs_data.vbo_data[index]; if (!vbo_obj.vbo) vbo_obj.vbo = _gl.createBuffer(); _gl.bindBuffer(_gl.ARRAY_BUFFER, vbo_obj.vbo); _gl.bufferData(_gl.ARRAY_BUFFER, bufs_data.vbo_source_data[i].vbo_source, bufs_data.usage); bufs_data.debug_vbo_bytes += bufs_data.vbo_source_data[i].vbo_source.byteLength; } } /** * Delete GL Buffer Objects */ exports.cleanup_bufs_data = function(bufs_data) { if (bufs_data.ibo) _gl.deleteBuffer(bufs_data.ibo); for (var i = 0; i < bufs_data.vbo_data.length; i++) if (bufs_data.vbo_data[i].vbo) _gl.deleteBuffer(bufs_data.vbo_data[i].vbo); bufs_data.vbo_data.length = 0; } /** * Check if submesh given by index is empty */ exports.has_empty_submesh = function(mesh, index) { var bsub = mesh["submeshes"][index]; if (bsub["base_length"]) return false; else return true; } /** * Apply transforms and return */ exports.submesh_apply_transform = function(submesh, transform) { var base_length = submesh.base_length; // positions/tbn_quat for (var f = 0; f < submesh.va_frames.length; f++) { var positions = submesh.va_frames[f]["a_position"]; m_tsr.transform_vectors(positions, transform, positions, 0); var tbn_quats = submesh.va_frames[f]["a_tbn_quat"]; if (tbn_quats.length > 0) m_tsr.transform_quats(tbn_quats, transform, tbn_quats, 0); var shade_tangs = submesh.va_frames[f]["a_shade_tangs"]; if (shade_tangs && shade_tangs.length > 0) m_tsr.transform_dir_vectors(shade_tangs, transform, shade_tangs, 0); } // transform au_center_pos too // indices, influences, vertex colors, texcoords not affected var au_center_pos = submesh.va_common["au_center_pos"]; if (au_center_pos && au_center_pos.length) m_tsr.transform_vectors(au_center_pos, transform, au_center_pos, 0); bounding_data_apply_transform(submesh.submesh_bd, transform); return submesh; } /** * Apply emitter transforms to particles */ exports.submesh_apply_particle_transform = function (submesh, transform) { var au_center_pos = submesh.va_common["au_center_pos"]; if (au_center_pos && au_center_pos.length) { var cen_pos_transformed = new Float32Array(au_center_pos); m_tsr.transform_vectors(cen_pos_transformed, transform, cen_pos_transformed, 0); for (var i = 0; i < submesh.va_frames.length; i++) for (var j = 0; j < cen_pos_transformed.length; j++) submesh.va_frames[i]["a_position"][j] += cen_pos_transformed[j] - au_center_pos[j]; au_center_pos.set(cen_pos_transformed); } else m_util.panic("Attribute \"au_center_pos\" is missing in particle submesh"); bounding_data_apply_transform(submesh.submesh_bd, transform); return submesh; } exports.bounding_data_apply_transform = bounding_data_apply_transform; function bounding_data_apply_transform(bd, tsr) { bd.bb_local = m_bounds.bounding_box_transform(bd.bb_local, tsr); bd.be_local = m_bounds.bounding_ellipsoid_transform(bd.be_local, tsr); bd.bs_local = m_bounds.bounding_sphere_transform(bd.bs_local, tsr); bd.bbr_local = m_bounds.bounding_rot_box_transform(bd.bbr_local, tsr); } /** * Apply submesh params */ exports.submesh_apply_params = submesh_apply_params; function submesh_apply_params(submesh, params) { var base_length = submesh.base_length; // additional params for (var param in params) { var param_len = params[param].length; var len = params[param].length * base_length; submesh.va_common[param] = new Float32Array(param_len * base_length); for (var i = 0; i < base_length; i++) for (var j = 0; j < param_len; j++) submesh.va_common[param][i*param_len + j] = params[param][j]; } return submesh; } /** * Generate new vertex array buffer from source by * copyng source data length times */ function gen_buf_clone_source(source, n) { var len = source.length; var new_buf = new Float32Array(len * n); for (var i = 0; i < n; i++) for (var j = 0; j < len; j++) new_buf[i*len + j] = source[j]; return new_buf; } exports.submesh_list_join = submesh_list_join; /** * Join submeshes list */ function submesh_list_join(submeshes) { var submesh0 = submeshes[0]; var new_submesh = submesh_list_join_prepare_dest(submeshes); var new_submesh_bd = new_submesh.submesh_bd; new_submesh_bd.bb_local = m_bounds.clone_bb(submesh0.submesh_bd.bb_local); // indices var i_offset = 0; var v_ind_offset = 0; var keys = {}; var bounding_verts = []; var new_tsr_array = []; var new_submesh_params = {}; var new_part_inh_attrs = {}; for (var i = 0; i < submeshes.length; i++) { var submesh = submeshes[i]; var indices = submesh.indices; var base_length = submesh.base_length; var va_common = submesh.va_common; var va_frames = submesh.va_frames; var bb_local = submesh.submesh_bd.bb_local; var bs_local = submesh.submesh_bd.bs_local; m_bounds.expand_bounding_box(new_submesh_bd.bb_local, bb_local); m_bounds.expand_bounding_sphere(new_submesh_bd.bs_local, bs_local); m_bounds.extract_rot_bb_corners(submesh.submesh_bd.bbr_local, bounding_verts); // indices for (var j = 0; j < indices.length; j++) { var ind = indices[j]; new_submesh.indices[i_offset + j] = ind + v_ind_offset; } i_offset += indices.length; for (var param_name in va_common) { var arr = va_common[param_name]; var offset = v_ind_offset * num_comp(arr, base_length); new_submesh.va_common[param_name].set(arr, offset); } for (var j = 0; j < submesh.va_frames.length; j++) { var va_frame = submesh.va_frames[j]; var new_va_frame = new_submesh.va_frames[j]; for (var param_name in va_frame) { var arr = va_frame[param_name]; var offset = v_ind_offset * num_comp(arr, base_length); new_va_frame[param_name].set(arr, offset); } } v_ind_offset += base_length; } new_submesh_bd.be_local = m_bounds.create_be_by_bb( m_util.f32(bounding_verts), true); if (new_submesh.shape_keys.length > 0) for (var i = 0; i < new_submesh.shape_keys.length; i++) { var geometry = new_submesh.shape_keys[i].geometry; var a_pos_offset = 0; var a_tbn_quat_offset = 0; for (var j = 0; j < submeshes.length; j++) { var cur_key_geom = submeshes[j].shape_keys[i].geometry; geometry["a_position"].set(cur_key_geom["a_position"], a_pos_offset); geometry["a_tbn_quat"].set(cur_key_geom["a_tbn_quat"], a_tbn_quat_offset); a_pos_offset += cur_key_geom["a_position"].length; a_tbn_quat_offset += cur_key_geom["a_tbn_quat"].length; } } if (submeshes[0].instanced_array_data) new_submesh.instanced_array_data = { tsr_array : submesh0.instanced_array_data.tsr_array, stat_part_em_tsr : submesh0.instanced_array_data.stat_part_em_tsr, static_hair : true, submesh_params : submesh0.instanced_array_data.submesh_params, part_inh_attrs : submesh0.instanced_array_data.part_inh_attrs, dyn_grass : submesh0.instanced_array_data.dyn_grass }; return new_submesh; } function submesh_list_join_prepare_dest(submeshes) { var submesh0 = submeshes[0]; var new_submesh = init_submesh("JOIN_" + submeshes.length + "_SUBMESHES"); var len = 0; for (var i = 0; i < submeshes.length; i++) len += submeshes[i].indices.length; new_submesh.indices = new Uint32Array(len); var pos_len = 0; var tbn_quat_len = 0; if (submeshes[0].shape_keys.length > 0) { for (var j = 0; j < submeshes[0].shape_keys.length; j++) { for (var i = 0; i < submeshes.length; i++) { pos_len += submeshes[i].shape_keys[j].geometry["a_position"].length; tbn_quat_len += submeshes[i].shape_keys[j].geometry["a_tbn_quat"].length; } var geometry = { "a_position" : new Float32Array(pos_len), "a_tbn_quat" : new Float32Array(tbn_quat_len) }; var key = { init_value : submeshes[0].shape_keys[j].init_value, geometry : geometry }; new_submesh.shape_keys.push(key); } } len = 0; for (var i = 0; i < submeshes.length; i++) len += submeshes[i].base_length; new_submesh.base_length = len; for (var param_name in submesh0.va_common) { len = 0; for (var i = 0; i < submeshes.length; i++) len += submeshes[i].va_common[param_name].length; new_submesh.va_common[param_name] = new Float32Array(len); } for (var param_name in submesh0.va_frames[0]) { len = 0; for (var i = 0; i < submeshes.length; i++) len += submeshes[i].va_frames[0][param_name].length; for (var i = 0; i < submesh0.va_frames.length; i++) { new_submesh.va_frames[i] = new_submesh.va_frames[i] || {}; new_submesh.va_frames[i][param_name] = new Float32Array(len); } } return new_submesh; } /** * Extract and clone submesh by given transforms. * well-suited for hair particles * ignore transform/center positions */ exports.make_clone_submesh = function(src_submesh, params, transforms) { // ignore empty submeshes if (!src_submesh.base_length) return init_submesh("EMPTY"); var count = transforms.length; if (src_submesh.base_length * count > MAX_SUBMESH_LENGTH && !m_ext.get_elem_index_uint()) submesh_drop_indices(src_submesh, count); // for cloned submesh var indices = src_submesh.indices; var base_length = src_submesh.base_length; var va_common = src_submesh.va_common; var va_frames = src_submesh.va_frames; // store additional params in extracted submesh for (var param in params) { var param_len = params[param].length; var len = param_len * base_length; va_common[param] = new Float32Array(len); for (var i = 0; i < base_length; i++) for (var j = 0; j < param_len; j++) va_common[param][i*param_len + j] = params[param][j]; } // create new submesh var new_submesh = init_submesh("CLONE_"+count+"_SUBMESHES"); new_submesh.base_length = base_length * count; new_submesh.indices = new Uint32Array(indices.length * count); for (var i = 0; i < count; i++) { var i_offset = indices.length * i; var v_offset = base_length * i; // indices for (var j = 0; j < indices.length; j++) { var ind = indices[j]; new_submesh.indices[i_offset + j] = ind + v_offset; } } for (var param_name in va_common) { var arr = va_common[param_name]; var len = arr.length * count; var ncomp = num_comp(arr, base_length); new_submesh.va_common[param_name] = new Float32Array(len); for (var i = 0; i < count; i++) { var v_offset = base_length * ncomp * i; new_submesh.va_common[param_name].set(arr, v_offset); } } for (var param_name in va_frames[0]) { for (var i = 0; i < va_frames.length; i++) { var arr = va_frames[i][param_name]; var len = arr.length * count; var ncomp = num_comp(arr, base_length); new_submesh.va_frames[i] = new_submesh.va_frames[i] || {}; new_submesh.va_frames[i][param_name] = new Float32Array(len); for (var j = 0; j < count; j++) { var transform = transforms[j]; var v_offset = base_length * ncomp * j; switch(param_name) { case "a_position": m_tsr.transform_vectors(arr, transform, new_submesh.va_frames[i][param_name], v_offset); break; case "a_tbn_quat": m_tsr.transform_quats(arr, transform, new_submesh.va_frames[i][param_name], v_offset); break; case "a_shade_tangs": m_tsr.transform_tangents(arr, transform, new_submesh.va_frames[i][param_name], v_offset); break; default: m_util.panic("Wrong attribute name: " + param_name); break; } } } } calc_unit_boundings(src_submesh, new_submesh, transforms); return new_submesh; } exports.calc_unit_boundings = calc_unit_boundings; function calc_unit_boundings(src_submesh, new_submesh, transforms) { // NOTE: transforms should be in src_submesh's local space var submesh_bd = new_submesh.submesh_bd; var bb_tmp = m_bounds.create_bb(); var bbr_tmp = m_bounds.create_rot_bb(); var bounding_verts = []; for (var i = 0; i < transforms.length; i++) { m_bounds.bounding_box_transform(src_submesh.submesh_bd.bb_local, transforms[i], bb_tmp); m_bounds.expand_bounding_box(submesh_bd.bb_local, bb_tmp); m_bounds.bounding_rot_box_transform(src_submesh.submesh_bd.bbr_local, transforms[i], bbr_tmp); m_bounds.extract_rot_bb_corners(bbr_tmp, bounding_verts); } submesh_bd.be_local = m_bounds.create_be_by_bb(m_util.f32(bounding_verts), true); submesh_bd.bbr_local = m_bounds.create_bbr_by_be(submesh_bd.be_local); submesh_bd.bs_local = m_bounds.create_bs_by_be(submesh_bd.be_local); } exports.extract_submesh = extract_submesh; /** * Extract submesh from mesh with given material index * @methodOf geometry */ function extract_submesh(mesh, material_index, attr_names, bone_skinning_info, vertex_colors_usage, uv_maps_usage) { // TODO: implement caching // TODO: handle cases when submesh can't provide requested attribute name var bsub = mesh["submeshes"][material_index]; var base_length = bsub["base_length"]; var mat = mesh["materials"][material_index]; var submesh = init_submesh("SUBMESH_" + mesh["name"] + "_" + mat["name"]); submesh.base_length = base_length; var use_shape_keys = false; // TEXTURE COORDS if (has_attr(attr_names, "a_texcoord")) var texcoords = extract_texcoords(mesh, material_index); else var texcoords = new Float32Array(0); if (has_attr(attr_names, "a_orco_tex_coord")) var local_coord = extract_orco_texcoords_nodes(mesh, bsub); else var local_coord = new Float32Array(0); // INFLUENCES (SKINNING) var influences = extract_influences(attr_names, base_length, bone_skinning_info, bsub["group"]); // VERTEX COLORS if (vertex_colors_usage) var submesh_vc_usage = m_util.clone_object_r(vertex_colors_usage); else var submesh_vc_usage = {}; submesh_vc_usage["a_color"] = { generate_buffer: true }; if (has_attr(attr_names, "a_color") && mesh["active_vcol_name"]) submesh_vc_usage["a_color"].src = [ { name: mesh["active_vcol_name"], mask: 7 } ]; else submesh_vc_usage["a_color"].src = []; submesh.va_common["a_texcoord"] = texcoords; submesh.va_common["a_influence"] = influences; submesh.va_common["a_orco_tex_coord"] = local_coord; extract_vcols(submesh.va_common, submesh_vc_usage, bsub["vertex_colors"], bsub["color"], base_length, mesh["name"]); assign_node_uv_maps(mesh["uv_textures"], bsub["texcoord"], bsub["texcoord2"], uv_maps_usage, base_length, submesh.va_common, mesh["name"]); submesh.indices = new Uint32Array(bsub["indices"]); // POSITION var frames = bsub["position"].length / base_length / POS_NUM_COMP; // position always needed? if (has_attr(attr_names, "a_tbn_quat") && bsub["tbn_quat"].length) var use_tbn_quat = true; else var use_tbn_quat = false; if (has_attr(attr_names, "a_shade_tangs") && bsub["shade_tangs"].length) var use_tangent_shading = true; else var use_tangent_shading = false; if (mesh["b4w_shape_keys"].length > 0) use_shape_keys = true; for (var i = 0; i < frames; i++) { var va_frame = create_frame(bsub, base_length, use_tbn_quat, use_tangent_shading, i); if (use_shape_keys) { var sk_frame = {}; sk_frame.name = mesh["b4w_shape_keys"][i]["name"]; submesh.shape_keys.push(sk_frame); if (i != 0) { sk_frame.geometry = va_frame; sk_frame.init_value = mesh["b4w_shape_keys"][i]["value"]; continue; } else { // NOTE: create new object for base shape key geometry sk_frame.geometry = create_frame(bsub, base_length, use_tbn_quat, use_tangent_shading, i); sk_frame.init_value = 1; } } submesh.va_frames.push(va_frame); } // store first frame copy for possible cyclic vertex anim if (frames > 1 && !use_shape_keys) { var va_frame0 = submesh.va_frames[0]; var va_frame = {}; for (var prop in va_frame0) va_frame[prop] = new Float32Array(va_frame0[prop]); submesh.va_frames.push(va_frame); } if (has_attr(attr_names, "a_polyindex")) { submesh_drop_indices(submesh, 1, true); submesh.va_common["a_polyindex"] = extract_polyindices(submesh); } // extract submesh bounding data submesh_bd_to_b4w(bsub["boundings"], submesh.submesh_bd); return submesh; } function submesh_bd_to_b4w(submesh_bd, bd) { bd.bb_local = m_bounds.create_bb(); bd.bb_local.max_x = submesh_bd["bb"]["max_x"]; bd.bb_local.max_y = submesh_bd["bb"]["max_y"]; bd.bb_local.max_z = submesh_bd["bb"]["max_z"]; bd.bb_local.min_x = submesh_bd["bb"]["min_x"]; bd.bb_local.min_y = submesh_bd["bb"]["min_y"]; bd.bb_local.min_z = submesh_bd["bb"]["min_z"]; var bbr_data = submesh_bd["rbb"]; var cov_axis_x = submesh_bd["caxis_x"]; var cov_axis_y = submesh_bd["caxis_y"]; var cov_axis_z = submesh_bd["caxis_z"]; var be_axes_len = submesh_bd["be_ax"]; bd.be_local = m_bounds.be_from_values( cov_axis_x, cov_axis_y, cov_axis_z, submesh_bd["be_cen"]); m_vec3.scale(bd.be_local.axis_x, be_axes_len[0], bd.be_local.axis_x); m_vec3.scale(bd.be_local.axis_y, be_axes_len[1], bd.be_local.axis_y); m_vec3.scale(bd.be_local.axis_z, be_axes_len[2], bd.be_local.axis_z); bd.bs_local = m_bounds.create_bs_by_be(bd.be_local); m_vec3.copy(bbr_data["rbb_c"], bd.bbr_local.center); m_vec3.copy(cov_axis_x, bd.bbr_local.axis_x); m_vec3.copy(cov_axis_y, bd.bbr_local.axis_y); m_vec3.copy(cov_axis_z, bd.bbr_local.axis_z); var rbb_scales = bbr_data["rbb_s"]; m_vec3.scale(bd.bbr_local.axis_x, rbb_scales[0], bd.bbr_local.axis_x); m_vec3.scale(bd.bbr_local.axis_y, rbb_scales[1], bd.bbr_local.axis_y); m_vec3.scale(bd.bbr_local.axis_z, rbb_scales[2], bd.bbr_local.axis_z); } function create_frame(bsub, base_length, use_tbn_quat, use_tangent_shading, frame_index) { var va_frame = {}; var pos_arr = new Float32Array(base_length * POS_NUM_COMP); var tbn_quat_arr = new Float32Array(use_tbn_quat ? base_length * TBN_QUAT_NUM_COMP : 0); var from_index = frame_index * base_length * POS_NUM_COMP; var to_index = from_index + base_length * POS_NUM_COMP; pos_arr.set(bsub["position"].subarray(from_index, to_index), 0); if (use_tbn_quat) { var from_index = frame_index * base_length * TBN_QUAT_NUM_COMP; var to_index = from_index + base_length * TBN_QUAT_NUM_COMP; tbn_quat_arr.set(bsub["tbn_quat"].subarray(from_index, to_index), 0); } if (use_tangent_shading) { var shading_tan_arr = new Float32Array(base_length * SHD_TAN_NUM_COMP); var to_index = base_length * SHD_TAN_NUM_COMP; shading_tan_arr.set(bsub["shade_tangs"].subarray(0, to_index), 0); va_frame["a_shade_tangs"] = shading_tan_arr; } va_frame["a_position"] = pos_arr; va_frame["a_tbn_quat"] = tbn_quat_arr; return va_frame; } function extract_texcoords(mesh, material_index) { var texcoords = null; var material = mesh["materials"][material_index]; var submesh = mesh["submeshes"][material_index]; if (material["texture_slots"].length) { var slot = material["texture_slots"][0]; switch (slot["texture_coords"]) { case "UV": var index = mesh["uv_textures"].indexOf(slot["uv_layer"]); if (index == 0) texcoords = new Float32Array(submesh["texcoord"]); else if (index == 1) texcoords = new Float32Array(submesh["texcoord2"]); break; case "ORCO": texcoords = generate_orco_texcoords(mesh["b4w_boundings"]["bb_src"], submesh); break; } } if (texcoords === null) texcoords = new Float32Array(submesh["base_length"] * 2); return texcoords; } // NOTE: this function is used when node outputs "Orco" & "Generated" are used function extract_orco_texcoords_nodes(mesh, submesh) { var bb = mesh["b4w_boundings"]["bb_src"]; var local_coords = new Float32Array(submesh["base_length"] * 3); var pos = submesh["position"]; var size_x = bb["max_x"] - bb["min_x"]; var size_y = bb["max_y"] - bb["min_y"]; var size_z = bb["max_z"] - bb["min_z"]; var localco_index = 0; for (var i = 0; i < submesh["position"].length; i+=3) { // -1 values will be updated in fragment shader if (size_x == 0) local_coords[localco_index++] = 0.5; else local_coords[localco_index++] = m_util.clamp( parseFloat(((submesh["position"][i] - bb.min_x) / size_x).toFixed(5)), 0, 1); if (size_y == 0) local_coords[localco_index++] = 0.5; else local_coords[localco_index++] = m_util.clamp( parseFloat(((submesh["position"][i + 1] - bb.min_y) / size_y).toFixed(5)), 0, 1); if (size_z == 0) local_coords[localco_index++] = 0.5; else local_coords[localco_index++] = m_util.clamp( parseFloat(((submesh["position"][i + 2] - bb.min_z) / size_z).toFixed(5)), 0, 1); } return local_coords; } function generate_orco_texcoords(bb, submesh) { var texcoords = new Float32Array(submesh["base_length"] * 2); var pos = submesh["position"]; var center_x = (bb["max_x"] + bb["min_x"]) / 2; var center_y = (bb["max_y"] + bb["min_y"]) / 2; var size_x = bb["max_x"] - bb["min_x"]; var size_y = bb["max_y"] - bb["min_y"]; var texco_index = 0; for (var i = 0; i < submesh["position"].length; i+=3) { texcoords[texco_index++] = (submesh["position"][i] - center_x) / size_x + 0.5; texcoords[texco_index++] = (submesh["position"][i + 1] - center_y) / size_y + 0.5; } return texcoords; } function extract_vcols(va_common, vc_usage, submesh_vc, bsub_color, base_length, mesh_name) { var submesh_vc_names = submesh_vc_get_names(submesh_vc); for (var attr_name in vc_usage) { var colors_data = vc_usage[attr_name].src; if (colors_data.length) { var dst_channels_count = 0 for (var i = 0; i < colors_data.length; i++) dst_channels_count += m_util.rgb_mask_get_channels_count(colors_data[i].mask); va_common[attr_name] = new Float32Array(dst_channels_count * base_length); var dst_channel_index_offset = 0; for (var i = 0; i < colors_data.length; i++) { var color_name = colors_data[i].name; var color_mask = colors_data[i].mask; var channels_presence = m_util.rgb_mask_get_channels_presence(color_mask); var color_name_index = submesh_vc_names.indexOf(color_name); if (color_name_index == -1) m_util.panic("vertex color \"" + color_name + "\" for mesh \"" + mesh_name+ "\" not found."); var mask_exported = submesh_vc[color_name_index]["mask"]; var exported_channels_count = m_util.rgb_mask_get_channels_count(mask_exported); if ((color_mask & mask_exported) !== color_mask) m_print.error("Wrong color extraction from " + color_name + " to " + attr_name + "."); var exported_colors_offset = submesh_vc_get_offset(submesh_vc, color_name_index, base_length); for (var j = 0; j < base_length; j++) for (var k = 0; k < COL_NUM_COMP; k++) if (channels_presence[k]) { var dst_channel_index = dst_channel_index_offset + m_util.rgb_mask_get_channel_presence_index(color_mask, k); var exported_channel_index = m_util.rgb_mask_get_channel_presence_index(mask_exported, k); va_common[attr_name][j * dst_channels_count + dst_channel_index] = bsub_color[exported_colors_offset + j * exported_channels_count + exported_channel_index]; } dst_channel_index_offset += exported_channels_count; } } else va_common[attr_name] = new Float32Array(0); } } function submesh_vc_get_names(submesh_vc) { var submesh_vc_names = []; for (var i = 0; i < submesh_vc.length; i++) submesh_vc_names.push(submesh_vc[i]["name"]); return submesh_vc_names; } function submesh_vc_get_offset(submesh_vc, vc_index, base_length) { var offset = 0; for (var i = 0; i < vc_index; i++) offset += m_util.rgb_mask_get_channels_count(submesh_vc[i]["mask"]); return offset * base_length; } function assign_node_uv_maps(mesh_uvs, bsub_texcoord, bsub_texcoord2, uv_maps_usage, base_length, va_common, mesh_name) { if (!uv_maps_usage) return; for (var uv_name in uv_maps_usage) { var uv_map_index = mesh_uvs.indexOf(uv_name); if (uv_map_index == 0) var uv_node_arr = new Float32Array(bsub_texcoord); else if (uv_map_index == 1) var uv_node_arr = new Float32Array(bsub_texcoord2); va_common[uv_maps_usage[uv_name]] = uv_node_arr; } } /** * Extract halo submesh */ exports.extract_halo_submesh = function(submesh) { var base_length = submesh.base_length; var position_in = submesh.va_frames[0]["a_position"]; var pos_arr = new Float32Array(12 * base_length); var indices_out = new Uint32Array (4 * submesh.indices.length); var random_vals = new Float32Array (4 * base_length); for (var i = 0; i < base_length; i++) { // generate positions pos_arr[12 * i] = position_in[3 * i]; pos_arr[12 * i + 1] = position_in[3 * i + 1]; pos_arr[12 * i + 2] = position_in[3 * i + 2]; pos_arr[12 * i + 3] = position_in[3 * i]; pos_arr[12 * i + 4] = position_in[3 * i + 1]; pos_arr[12 * i + 5] = position_in[3 * i + 2]; pos_arr[12 * i + 6] = position_in[3 * i]; pos_arr[12 * i + 7] = position_in[3 * i + 1]; pos_arr[12 * i + 8] = position_in[3 * i + 2]; pos_arr[12 * i + 9] = position_in[3 * i]; pos_arr[12 * i + 10] = position_in[3 * i + 1]; pos_arr[12 * i + 11] = position_in[3 * i + 2]; // generate indices indices_out[6 * i] = 4 * i + 2; indices_out[6 * i + 1] = 4 * i + 1; indices_out[6 * i + 2] = 4 * i; indices_out[6 * i + 3] = 4 * i + 2; indices_out[6 * i + 4] = 4 * i; indices_out[6 * i + 5] = 4 * i + 3; var random_val = Math.random(); random_vals[4 * i] = random_val; random_vals[4 * i + 1] = random_val; random_vals[4 * i + 2] = random_val; random_vals[4 * i + 3] = random_val; } var halo_submesh = init_submesh("HALO"); halo_submesh.va_frames[0] = {}; halo_submesh.base_length = 4 * base_length; halo_submesh.va_frames[0]["a_position"] = pos_arr; halo_submesh.va_common["a_halo_bb_vertex"] = gen_bb_vertices(base_length); halo_submesh.va_common["a_random_vals"] = random_vals; halo_submesh.indices = indices_out; return halo_submesh; } /** * Convenience method for attribute name check */ exports.has_attr = has_attr; function has_attr(attr_names, name) { if (attr_names.indexOf(name) > -1) return true; else return false; } /** * Extract all materials. * common_vc_usage - vertex colors which exist for every submesh */ exports.extract_submesh_all_mats = function(mesh, attr_names, common_vc_usage) { var submeshes = []; for (var i = 0; i < mesh["submeshes"].length; i++) { var submesh = extract_submesh(mesh, i, attr_names, null, common_vc_usage, null); if (submesh.base_length) { var submesh_bd = submesh.submesh_bd; submeshes.push(submesh); } } if (submeshes.length == 0) var submesh_all = init_submesh("EMPTY"); else if (submeshes.length == 1) var submesh_all = submeshes[0]; else var submesh_all = submesh_list_join(submeshes); return submesh_all; } function extract_influences(attr_names, base_length, bone_skinning_info, groups) { if (has_attr(attr_names, "a_influence") && bone_skinning_info) { var influences = new Float32Array(base_length * INFLUENCE_NUM_COMP); var groups_num = groups.length/base_length; // bones corresponding to vertex group var deform_bone_indices = get_deform_bone_indices(bone_skinning_info, groups_num); // NOTE: create buffers outside vertices cycle var buf_length = groups_num > 3 ? groups_num: 4; var weights_buf = new Float32Array(buf_length); var bones_buf = new Uint32Array(buf_length); var res_buf = new Float32Array(INFLUENCE_NUM_COMP); var zero_weights = new Float32Array(buf_length); var zero_bones = new Uint32Array(buf_length); var zero_res = new Float32Array(INFLUENCE_NUM_COMP); for (var i = 0; i < base_length; i++) { weights_buf.set(zero_weights); bones_buf.set(zero_bones); res_buf.set(zero_res); influences.set(get_vertex_influences(groups, groups_num, i, base_length, deform_bone_indices, weights_buf, bones_buf, res_buf), i * INFLUENCE_NUM_COMP); } } else var influences = new Float32Array(0); return influences; } function get_deform_bone_indices(bone_skinning_info, groups_num) { var deform_bone_indices = new Float32Array(groups_num); for (var i = 0; i < groups_num; i++) { deform_bone_indices[i] = -1; for (var j in bone_skinning_info) { var bone_sk_info = bone_skinning_info[j]; if (bone_sk_info.vgroup_index === i) { deform_bone_indices[i] = bone_sk_info.deform_bone_index; break; } } } return deform_bone_indices; } function get_vertex_influences(vertex_groups, groups_num, vert_index, base_length, deform_bone_indices, weights_buf, bones_buf, res_buf) { var precision = 0.01; var no_weights = true; for (var i = 0; i < groups_num; i++) { var weight = vertex_groups[i * base_length + vert_index]; if (weight !== -1) { var bone_index = deform_bone_indices[i]; // vertex can be assigned to non-bone group if (bone_index !== -1) { weights_buf[i] = weight; bones_buf[i] = bone_index; no_weights = false; } } } if (no_weights) return res_buf; // sort in descending order by weights sort_two_arrays(weights_buf, bones_buf, exports.SORT_NUMERIC, false); // normalize weights (in case they were not normalized by author) var sum_weights = 0; for (var i = 0; i < INFLUENCE_NUM_COMP; i++) sum_weights += weights_buf[i]; if (sum_weights < precision) return res_buf; for (var i = 0; i < INFLUENCE_NUM_COMP; i++) weights_buf[i] /= sum_weights; // pack to one vector; use a group index in integer part and // a bone weight in fractional part of a number if (Math.abs(weights_buf[0] - 1.0) < precision) // single group case res_buf[0] = bones_buf[0] + 1.0; else // multi group case for (var i = 0; i < INFLUENCE_NUM_COMP; i++) res_buf[i] = bones_buf[i] + weights_buf[i]; return res_buf; } /** * This function works only for non-animated arrays */ function num_comp(array, base_length) { if (base_length == 0) return 0; var array_length = array.length; var factor = array_length / base_length; if (factor != Math.floor(factor)) m_util.panic("Array size mismatch during geometry calculation: array length=" + array_length + ", base length=" + base_length); return factor; } /** * Sort triangles and update index buffers when camera moves. */ exports.update_buffers_movable = function(bufs_data, z_sort_info, world_tsr, eye) { // retrieve data required for update var indices = bufs_data.ibo_array; var positions = extract_array(bufs_data, "a_position"); var median_cache = z_sort_info.median_cache; var median_world_cache = z_sort_info.median_world_cache; var dist_cache = z_sort_info.dist_cache; var sort_back_to_front = z_sort_info.sort_back_to_front; // get positions to world space and calc medians // note: skinning ignored compute_triangle_medians(indices, positions, median_cache); m_tsr.transform_vectors(median_cache, world_tsr, median_world_cache); compute_triangle_dists(median_world_cache, eye, dist_cache); var indices = sort_triangles(dist_cache, indices); // bind and update IBO _gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER, bufs_data.ibo); _gl.bufferData(_gl.ELEMENT_ARRAY_BUFFER, indices, _gl.DYNAMIC_DRAW); } /** * Store medians to preallocated Float32Array */ function compute_triangle_medians(indices, positions, medians) { var num_faces = indices.length / 3; for (var i = 0; i < num_faces; i++) { var index0 = indices[3 * i]; var index1 = indices[3 * i + 1]; var index2 = indices[3 * i + 2]; // vertex coordinate var pos00 = positions[3 * index0]; var pos01 = positions[3 * index0 + 1]; var pos02 = positions[3 * index0 + 2]; var pos10 = positions[3 * index1]; var pos11 = positions[3 * index1 + 1]; var pos12 = positions[3 * index1 + 2]; var pos20 = positions[3 * index2]; var pos21 = positions[3 * index2 + 1]; var pos22 = positions[3 * index2 + 2]; medians[3*i] = (pos00 + pos10 + pos20) / 3; medians[3*i + 1] = (pos01 + pos11 + pos21) / 3; medians[3*i + 2] = (pos02 + pos12 + pos22) / 3; } return medians; } /** * Store square dists to preallocated Float32Array */ function compute_triangle_dists(medians, eye, dists) { var len = medians.length/3; for (var i = 0; i < len; i++) { var dx = medians[3*i] - eye[0]; var dy = medians[3*i + 1] - eye[1]; var dz = medians[3*i + 2] - eye[2]; dists[i] = dx*dx + dy*dy + dz*dz; } return dists; } /** * Using comb sort * currently supported BACK_TO_FRONT sort (discending order) */ function sort_triangles(dists, indices) { var dlen = dists.length; if (dlen < 2) return indices; var gap = dlen; var swapped = false; var t; while ((gap > 1) || swapped) { if (gap > 1) { gap = Math.floor(gap / COMB_SORT_JUMP_COEFF); } swapped = false; for (var i = 0; gap + i < dlen; i++) { if (dists[i] - dists[i + gap] < 0) { t = dists[i]; dists[i] = dists[i+gap]; dists[i+gap] = t; swap_indices(indices, i, i+gap); swapped = true; } } } return indices; } exports.sort_two_arrays = sort_two_arrays; function sort_two_arrays(main_arr, extra_arr, type, ascending) { var arr_length = main_arr.length; var gap = arr_length; var swapped = false; var tmp; var order_factor = ascending ? -1 : 1; while (gap > 1 || swapped) { if (gap > 1) gap = Math.floor(gap / COMB_SORT_JUMP_COEFF); swapped = false; for (var i = 0; gap + i < arr_length; i++) { if (type == exports.SORT_NUMERIC) var condition = order_factor * (main_arr[i] - main_arr[i + gap]) < 0; else var condition = main_arr[i] < main_arr[i + gap] && ascending; if (condition) { tmp = main_arr[i]; main_arr[i] = main_arr[i + gap]; main_arr[i + gap] = tmp; tmp = extra_arr[i]; extra_arr[i] = extra_arr[i + gap]; extra_arr[i + gap] = tmp; swapped = true; } } } } /** * pos1 <-> pos2 */ function swap_indices(indices, pos1, pos2) { var t0 = indices[3*pos1]; var t1 = indices[3*pos1 + 1]; var t2 = indices[3*pos1 + 2]; indices[3*pos1] = indices[3*pos2]; indices[3*pos1 + 1] = indices[3*pos2 + 1]; indices[3*pos1 + 2] = indices[3*pos2 + 2]; indices[3*pos2] = t0; indices[3*pos2 + 1] = t1; indices[3*pos2 + 2] = t2; } /** * Perform normals calculation * * shared indices required in case of normals smoothing * @methodOf geometry */ exports.calc_normals = function(indices, positions, shared_indices) { // TODO: rewrite to match new submesh architecture (drop explicit binormals) var num_vertices = positions.length / POS_NUM_COMP; var num_faces = indices.length / 3; // FACE === TRIANGLE // init storage (destination) var normals = []; var pos0 = new Array(3); var pos1 = new Array(3); var pos2 = new Array(3); // for each face perform normals calculation for (var i = 0; i < num_faces; i++) { var index0 = indices[3 * i]; var index1 = indices[3 * i + 1]; var index2 = indices[3 * i + 2]; for (var j = 0; j < POS_NUM_COMP; j++) { pos0[j] = positions[POS_NUM_COMP * index0 + j]; pos1[j] = positions[POS_NUM_COMP * index1 + j]; pos2[j] = positions[POS_NUM_COMP * index2 + j]; } if (shared_indices) { // calculate angles to use as weights for averaging var angle0 = angle(pos0, pos1, pos2); var angle1 = angle(pos1, pos2, pos0); var angle2 = Math.PI - angle0 - angle1; } else { var angle0 = 1; var angle1 = 1; var angle2 = 1; } calc_normal_for_face(index0, index1, index2, pos0, pos1, pos2, angle0, angle1, angle2, normals); } // perform normals smoothing if (shared_indices) smooth_normals(shared_indices, normals); // normalize normals for (var i = 0; i < num_vertices; i++) normalize_normal(i, normals) return normals; } function angle(pos, pos1, pos2) { var vec1 = []; var vec2 = []; m_vec3.subtract(pos1, pos, vec1); m_vec3.subtract(pos2, pos, vec2); m_vec3.normalize(vec1, vec1); m_vec3.normalize(vec2, vec2); var dot = m_util.clamp(m_vec3.dot(vec1, vec2), -1, 1); return Math.acos(dot); } function calc_normal_for_face(index0, index1, index2, pos0, pos1, pos2, angle0, angle1, angle2, dest) { // calculate a face normal (same for all 3 vertices in a triangle) var normal = calc_normal_by_pos(pos0, pos1, pos2); // sum normals for vertices with the same coords for (var i = 0; i < NOR_NUM_COMP; i++) { var i0 = NOR_NUM_COMP * index0 + i; var i1 = NOR_NUM_COMP * index1 + i; var i2 = NOR_NUM_COMP * index2 + i; dest[i0] = angle0 * normal[i]; dest[i1] = angle1 * normal[i]; dest[i2] = angle2 * normal[i]; } } function calc_normal_by_pos(pos0, pos1, pos2) { var vec1 = [], vec2 = [], normal = []; m_vec3.subtract(pos1, pos0, vec1); m_vec3.subtract(pos2, pos0, vec2); m_vec3.cross(vec1, vec2, normal); m_vec3.normalize(normal, normal); // required return normal; } function smooth_normals(shared_indices, normals) { for (var i in shared_indices) { var indices = shared_indices[i]; for (var j = 0; j < NOR_NUM_COMP; j++) { var offset0 = NOR_NUM_COMP * indices[0] + j; // smooth for (var k = 1; k < indices.length; k++) { var offset = NOR_NUM_COMP * indices[k] + j; normals[offset0] += normals[offset]; } // copy for (var k = 1; k < indices.length; k++) { var offset = NOR_NUM_COMP * indices[k] + j; normals[offset] = normals[offset0]; } } } } function normalize_normal(i, normals) { var normal = new Float32Array(NOR_NUM_COMP); for (var j = 0; j < NOR_NUM_COMP; j++) { var index = NOR_NUM_COMP * i + j; normal[j] = normals[index]; } m_vec3.normalize(normal, normal); for (var j = 0; j < NOR_NUM_COMP; j++) { var index = NOR_NUM_COMP * i + j; normals[index] = normal[j]; } } exports.calc_shared_indices = calc_shared_indices; /** * Calculate shared indices - indices of vertices having same locations * @methodOf geometry */ function calc_shared_indices(indices, shared_locations, locations) { var sh_loc_set = {}; var len = shared_locations.length / 3; for (var i = 0; i < len; i++) { var key = String(shared_locations[3*i]) + String(shared_locations[3*i + 1]) + String(shared_locations[3*i + 2]); sh_loc_set[key] = []; } var len = indices.length; for (var i = 0; i < len; i++) { var index = indices[i]; var key = String(locations[3 * index]) + String(locations[3 * index + 1]) + String(locations[3 * index + 2]); if (key in sh_loc_set) sh_loc_set[key].push(index); } return sh_loc_set; } /** * Return n points uniformly distributed on geometry * point is Array of Float32Arrays of coords */ exports.geometry_random_points = function(submesh, n, process_tbn_quats, seed) { var triangles = extract_triangles_position(submesh, null); if (process_tbn_quats) var triangles_tbn = extract_triangles_tbn_quat(submesh, null); var tnum = triangles.length; var areas = new Float32Array(tnum); var A = _vec3_tmp; var B = _vec3_tmp2; var C = _vec3_tmp3; for (var i = 0; i < tnum; i++) { var tri = triangles[i]; A.set(tri.subarray(0, 3)); B.set(tri.subarray(3, 6)); C.set(tri.subarray(6)); areas[i] = triangle_area(A, B, C); } var cumulative_areas = new Float32Array(tnum); cumulative_areas[0] = areas[0]; for (var i = 1; i < tnum; i++) cumulative_areas[i] = cumulative_areas[i-1] + areas[i]; var geom_area = cumulative_areas[areas.length - 1]; var points = []; // distribute points for (var i = 0; i < n; i++) { var area = geom_area * m_util.rand_r(seed); var tri_index = m_util.binary_search_max(cumulative_areas, area, 0, cumulative_areas.length - 1); if (process_tbn_quats) var tri = triangles_tbn[tri_index]; else var tri = triangles[tri_index]; var ps = triangle_random_point(tri, seed, _vec3_tmp); if (process_tbn_quats) { points[i] = new Float32Array(4); m_vec3.normalize(ps, ps); var quat = m_quat.rotationTo(m_util.AXIS_Y, ps, _quat_tmp); points[i][0] = quat[0]; points[i][1] = quat[1]; points[i][2] = quat[2]; points[i][3] = quat[3]; } else { points[i] = new Float32Array(3); points[i][0] = ps[0]; points[i][1] = ps[1]; points[i][2] = ps[2]; } } return points; } /** *

Return Array of triangles. *

triangle is a Float32Array of 9 cooords *

NOTE: Uses only first frame for vertex-animated meshes * @methodOf geometry */ function extract_triangles_position(submesh, dest) { if (!dest) var dest = []; var positions = submesh.va_frames[0]["a_position"]; return ext_triangles(positions, submesh, dest);; } function extract_triangles_tbn_quat(submesh, dest) { if (!dest) var dest = []; var tbn_quats = submesh.va_frames[0]["a_tbn_quat"]; var count = tbn_quats.length / 4; var positions = new Float32Array(3 * count); for (var i = 0; i < count; i++) { var quat = _quat_tmp; quat[0] = tbn_quats[4*i]; quat[1] = tbn_quats[4*i + 1]; quat[2] = tbn_quats[4*i + 2]; quat[3] = tbn_quats[4*i + 3]; var norm = m_vec3.transformQuat(m_util.AXIS_Y, quat, _vec3_tmp); positions[3*i] = norm[0]; positions[3*i + 1] = norm[1]; positions[3*i + 2] = norm[2]; } return ext_triangles(positions, submesh, dest); } function ext_triangles(positions, submesh, dest) { if (is_indexed(submesh)) { var indices = submesh.indices; var tnum = indices.length / 3; for (var i = 0; i < tnum; i++) { var tri = new Float32Array(9); var i0 = indices[3*i]; tri[0] = positions[3*i0]; tri[1] = positions[3*i0 + 1]; tri[2] = positions[3*i0 + 2]; var i1 = indices[3*i + 1]; tri[3] = positions[3*i1]; tri[4] = positions[3*i1 + 1]; tri[5] = positions[3*i1 + 2]; var i2 = indices[3*i + 2]; tri[6] = positions[3*i2]; tri[7] = positions[3*i2 + 1]; tri[8] = positions[3*i2 + 2]; dest[i] = tri; } } else { var tnum = positions.length / 9; for (var i = 0; i < positions.length; i++) { var tri = new Float32Array(9); tri[0] = positions[9*i]; tri[1] = positions[9*i + 1]; tri[2] = positions[9*i + 2]; tri[3] = positions[9*i + 3]; tri[4] = positions[9*i + 4]; tri[5] = positions[9*i + 5]; tri[6] = positions[9*i + 6]; tri[7] = positions[9*i + 7]; tri[8] = positions[9*i + 8]; dest[i] = tri; } } return dest; } exports.extract_polyindices = extract_polyindices; /** *

Return Array of vertex polygone index. * @methodOf geometry */ function extract_polyindices(submesh) { var polyindices = new Float32Array(submesh.base_length); for (var i = 0; i < submesh.base_length; i++) polyindices[i] = (i % 3) / 2; // (0, 0.5, 1) return polyindices; } /** * Calculate triangle area using Heron's formula * @methodOf geometry */ function triangle_area(A, B, C) { return Math.sqrt(triangle_area_squared(A, B, C)); } /** * Calculate squared triangle area using Heron's formula * @methodOf geometry */ exports.triangle_area_squared = triangle_area_squared; function triangle_area_squared(A, B, C) { var a = m_vec3.dist(A, B); var b = m_vec3.dist(A, C); var c = m_vec3.dist(B, C); var p = (a + b + c) / 2; return p * (p - a) * (p - b) * (p - c); } /** * Get random point within triangle * @methodOf geometry */ function triangle_random_point(triangle, seed, dest) { if (!dest) var dest = new Float32Array(3); var x0 = triangle[0]; var y0 = triangle[1]; var z0 = triangle[2]; var x1 = triangle[3]; var y1 = triangle[4]; var z1 = triangle[5]; var x2 = triangle[6]; var y2 = triangle[7]; var z2 = triangle[8]; // barycentric coords var w1 = m_util.rand_r(seed); var w2 = m_util.rand_r(seed); if ((w1 + w2) > 1) { w1 = 1 - w1; w2 = 1 - w2; } var w0 = 1 - w1 - w2; var x = w0 * x0 + w1 * x1 + w2 * x2; var y = w0 * y0 + w1 * y1 + w2 * y2; var z = w0 * z0 + w1 * z1 + w2 * z2; dest[0] = x; dest[1] = y; dest[2] = z; return dest; } /** * Generate billboard vertices */ exports.gen_bb_vertices = gen_bb_vertices; function gen_bb_vertices(count) { var quad = new Float32Array([0,0,0,1,1,1,1,0]); var bb_vertices = new Float32Array(count * 8); for (var i = 0; i < count; i++) bb_vertices.set(quad, i * 8); return bb_vertices; } exports.scale_submesh_xyz = function(submesh, scale, center) { var positions = submesh.va_frames[0]["a_position"]; for (var i = 0; i < positions.length; i += 3) { positions[i] = (positions[i] - center[0]) * scale[0] + center[0]; positions[i + 1] = (positions[i + 1] - center[1]) * scale[1] + center[1]; positions[i + 2] = (positions[i + 2] - center[2]) * scale[2] + center[2]; } } exports.apply_shape_key = function(obj, key_name, new_value) { // NOTE: only applies shape key for the first scene var batches = obj.scenes_data[0].batches; var sk_data = obj.render.shape_keys_values; for (var i = 1; i < sk_data.length; i++) if (sk_data[i]["name"] == key_name) sk_data[i]["value"] = new_value; for (var i = 0; i < batches.length; i++) { if (batches[i].forked_batch || !batches[i].use_shape_keys || batches[i].debug_sphere) continue; var bd = batches[i].bufs_data; // NOTE: split function into smaller ones (optimization issue in Chrome) var type = get_vbo_type_by_attr_name("a_position"); var vbo = get_vbo_by_type(bd.vbo_data, type); var vbo_source = get_vbo_source_by_type(bd.vbo_source_data, type); _gl.bindBuffer(_gl.ARRAY_BUFFER, vbo); apply_shape_key_pos(bd.pointers["a_position"], batches[i], vbo_source, sk_data); var type = get_vbo_type_by_attr_name("a_tbn_quat"); var vbo = get_vbo_by_type(bd.vbo_data, type); var vbo_source = get_vbo_source_by_type(bd.vbo_source_data, type); _gl.bindBuffer(_gl.ARRAY_BUFFER, vbo); apply_shape_key_tbn_quat(bd.pointers["a_tbn_quat"], batches[i], vbo_source, sk_data); } } function apply_shape_key_pos(pos_pointer, batch, vbo_source, sk_data) { if (pos_pointer) { var pos_offset = pos_pointer.offset; var pos_length = pos_pointer.length + pos_offset; var pos = batch.bufs_data.shape_keys[0].geometry["a_position"]; for (var i = pos_offset; i < pos_length; i++) vbo_source[i] = pos[i - pos_offset]; for (var i = 1; i < batch.bufs_data.shape_keys.length; i++) { var positions = batch.bufs_data.shape_keys[i].geometry["a_position"]; var value = sk_data[i]["value"]; if (!value) continue; for (var j = pos_offset; j < pos_length; j++) vbo_source[j] += value * positions[j - pos_offset]; } _gl.bufferSubData(_gl.ARRAY_BUFFER, m_util.FLOAT_SIZE * pos_offset, vbo_source.subarray(pos_offset)); } } function apply_shape_key_tbn_quat(tbn_quat_pointer, batch, vbo_source, sk_data) { if (tbn_quat_pointer) { var tbn_quat_offset = tbn_quat_pointer.offset; var tbn_quat_length = tbn_quat_pointer.length + tbn_quat_offset; var tbn_quat_first = batch.bufs_data.shape_keys[0].geometry["a_tbn_quat"]; var sum_tbn_quat = _quat_tmp; for (var i = tbn_quat_offset; i < tbn_quat_length; i+=TBN_QUAT_NUM_COMP) { sum_tbn_quat[0] = 0; sum_tbn_quat[1] = 0; sum_tbn_quat[2] = 0; sum_tbn_quat[3] = 1; for (var j = 1; j < batch.bufs_data.shape_keys.length; j++) { var tbn_quat = batch.bufs_data.shape_keys[j].geometry["a_tbn_quat"]; var value = sk_data[j]["value"]; if (!value) continue; var f_quat = _quat_tmp2; f_quat[0] = tbn_quat[i - tbn_quat_offset]; f_quat[1] = tbn_quat[i + 1 - tbn_quat_offset]; f_quat[2] = tbn_quat[i + 2 - tbn_quat_offset]; f_quat[3] = tbn_quat[i + 3 - tbn_quat_offset]; var p_f_quat = m_quat.slerp(m_util.QUAT4_IDENT, f_quat, value, _quat_tmp2); m_quat.multiply(p_f_quat, sum_tbn_quat, sum_tbn_quat); } var r_quat = _quat_tmp2; r_quat[0] = tbn_quat_first[i - tbn_quat_offset]; r_quat[1] = tbn_quat_first[i - tbn_quat_offset + 1]; r_quat[2] = tbn_quat_first[i - tbn_quat_offset + 2]; r_quat[3] = tbn_quat_first[i - tbn_quat_offset + 3]; var is_righthand = r_quat[3] > 0; m_quat.multiply(sum_tbn_quat, r_quat, r_quat); if (r_quat[3] > 0 && !is_righthand || r_quat[3] < 0 && is_righthand) m_quat.scale(r_quat, -1, r_quat); vbo_source[i] = m_util.float_to_short(r_quat[0]); vbo_source[i + 1] = m_util.float_to_short(r_quat[1]); vbo_source[i + 2] = m_util.float_to_short(r_quat[2]); vbo_source[i + 3] = m_util.float_to_short(r_quat[3]); } _gl.bufferSubData(_gl.ARRAY_BUFFER, m_util.FLOAT_SIZE * tbn_quat_offset, vbo_source.subarray(tbn_quat_offset)); } } exports.check_shape_keys = function(obj) { return obj.render.use_shape_keys; } exports.get_shape_keys_names = function(obj) { var shape_keys_names = []; if (obj.render) for (var i = 1; i < obj.render.shape_keys_values.length; i++) { shape_keys_names.push(obj.render.shape_keys_values[i]["name"]); } return shape_keys_names; } exports.get_shape_key_value = function(obj, key_name) { if (obj.render) for (var i = 1; i < obj.render.shape_keys_values.length; i++) if (key_name == obj.render.shape_keys_values[i]["name"]) return obj.render.shape_keys_values[i]["value"]; return 0; } exports.has_shape_key = function(obj, key_name) { var shape_keys = obj.render.shape_keys_values; if (shape_keys) for (var i = 1; i < shape_keys.length; i++) if (shape_keys[i]["name"] == key_name) return true; return false; } exports.has_dyn_geom = function(obj) { if (obj && obj.render && obj.render.dynamic_geometry) return true; else return false; } exports.draw_line = function(batch, positions, is_split) { var bufs_data = batch.bufs_data; if (bufs_data) { if (is_split) var num_line_segm = positions.length / 3 / 2; else var num_line_segm = positions.length / 3 - 1; // two triangles, 4 vertices, 6 indices var tri_pos = new Float32Array(num_line_segm * 4 * 3); var tri_dir = new Float32Array(num_line_segm * 4 * 3); var tri_ind = new Uint16Array(num_line_segm * 6); for (var i = 0; i < num_line_segm; i++) { if (is_split) { var pos_offset = 2 * 3 * i; var pos_offset_next = 2 * 3 * i + 3; } else { var pos_offset = 3 * i; var pos_offset_next = 3 * (i + 1); } var pos_x = positions[pos_offset]; var pos_y = positions[pos_offset + 1]; var pos_z = positions[pos_offset + 2]; var pos_x_next = positions[pos_offset_next]; var pos_y_next = positions[pos_offset_next + 1]; var pos_z_next = positions[pos_offset_next + 2]; var dir_x = pos_x_next - pos_x; var dir_y = pos_y_next - pos_y; var dir_z = pos_z_next - pos_z; // 0, right tri_pos[i * 4 * 3 ] = pos_x; tri_pos[i * 4 * 3 + 1] = pos_y; tri_pos[i * 4 * 3 + 2] = pos_z; tri_dir[i * 4 * 3 ] = dir_x; tri_dir[i * 4 * 3 + 1] = dir_y; tri_dir[i * 4 * 3 + 2] = dir_z; // 1, left, next tri_pos[i * 4 * 3 + 3] = pos_x_next; tri_pos[i * 4 * 3 + 4] = pos_y_next; tri_pos[i * 4 * 3 + 5] = pos_z_next; tri_dir[i * 4 * 3 + 3] = -dir_x; tri_dir[i * 4 * 3 + 4] = -dir_y; tri_dir[i * 4 * 3 + 5] = -dir_z; // 2, left tri_pos[i * 4 * 3 + 6] = pos_x; tri_pos[i * 4 * 3 + 7] = pos_y; tri_pos[i * 4 * 3 + 8] = pos_z; tri_dir[i * 4 * 3 + 6] = -dir_x; tri_dir[i * 4 * 3 + 7] = -dir_y; tri_dir[i * 4 * 3 + 8] = -dir_z; // 3, right, next tri_pos[i * 4 * 3 + 9] = pos_x_next; tri_pos[i * 4 * 3 + 10] = pos_y_next; tri_pos[i * 4 * 3 + 11] = pos_z_next; tri_dir[i * 4 * 3 + 9] = dir_x; tri_dir[i * 4 * 3 + 10] = dir_y; tri_dir[i * 4 * 3 + 11] = dir_z; // 0 1 2 tri_ind[i * 6] = 4 * i; tri_ind[i * 6 + 1] = 4 * i + 1; tri_ind[i * 6 + 2] = 4 * i + 2; // 0 3 1 tri_ind[i * 6 + 3] = 4 * i; tri_ind[i * 6 + 4] = 4 * i + 3; tri_ind[i * 6 + 5] = 4 * i + 1; } var new_vbo_size = 0; for (var attr in bufs_data.pointers) { var pointer = bufs_data.pointers[attr]; new_vbo_size += tri_pos.length / 3 * pointer.num_comp; } var lengths = {} lengths[VBO_FLOAT] = new_vbo_size; var vsd = bufs_data.vbo_source_data = init_vbo_source_data(lengths); exports.update_bufs_data_index_array(bufs_data, batch.draw_mode, tri_ind); var offset = 0; for (var attr in bufs_data.pointers) { var pointer = bufs_data.pointers[attr]; switch (attr) { case "a_position": vbo_source_data_set_attr(vsd, attr, tri_pos, offset); pointer.offset = offset; pointer.length = tri_pos.length; offset += pointer.length; break; case "a_direction": vbo_source_data_set_attr(vsd, attr, tri_dir, offset); pointer.offset = offset; pointer.length = tri_dir.length; offset += pointer.length; break; default: pointer.offset = offset; pointer.length = tri_pos.length / 3 * pointer.num_comp; var new_array = new Float32Array(pointer.length); vbo_source_data_set_attr(vsd, attr, new_array, offset); offset += pointer.length; break; } } update_gl_buffers(bufs_data); } } // NOTE: cloning without vbo_data - for deferred updating exports.clone_bufs_data = function(bufs_data) { var out = init_bufs_data(); if (bufs_data.ibo_array) switch (bufs_data.ibo_type) { case _gl.UNSIGNED_SHORT: out.ibo_array = new Uint16Array(bufs_data.ibo_array); break; case _gl.UNSIGNED_INT: out.ibo_array = new Uint32Array(bufs_data.ibo_array); break; } else out.ibo_array = null; out.vbo_source_data = clone_vbo_source_data(bufs_data.vbo_source_data); out.ibo_type = bufs_data.ibo_type; out.count = bufs_data.count; out.pointers = m_util.clone_object_r(bufs_data.pointers); out.usage = bufs_data.usage; out.debug_ibo_bytes = bufs_data.debug_ibo_bytes; out.debug_vbo_bytes = bufs_data.debug_vbo_bytes; out.ibo = null; out.info_for_z_sort_updates = m_util.clone_object_r(bufs_data.info_for_z_sort_updates); out.shape_keys = m_util.clone_object_r(bufs_data.shape_keys); out.instance_count = bufs_data.instance_count; out.cleanup_gl_data_on_unload = bufs_data.cleanup_gl_data_on_unload; return out; } exports.init_submesh = init_submesh; function init_submesh(name) { var va_common = { "a_influence": new Float32Array(0), "a_color": new Float32Array(0), "a_texcoord": new Float32Array(0) }; return { name: name, // number of vertices per frame base_length: 0, indices: null, va_frames: [], va_common: va_common, shape_keys: [], submesh_bd: { bb_local : m_bounds.create_bb(), be_local : m_bounds.create_be(), bs_local : m_bounds.create_bs(), bbr_local : m_bounds.create_rot_bb() }, instanced_array_data: null }; } exports.reset = function() { _gl = null; } exports.init_vbo_source_data = init_vbo_source_data; function init_vbo_source_data(lengths) { var vbo_source_data = []; for (var type in lengths) vbo_source_data.push(create_vbo_source_obj(type, lengths[type])); return vbo_source_data; } function create_vbo_source_obj(type, len) { var constructor = get_constructor_by_type(type); return { vbo_source: new constructor(len), type: type }; } function clone_vbo_source_data(vbo_source_data) { var new_vbo_source_data = []; for (var i = 0; i < vbo_source_data.length; i++) { var vbo_source = vbo_source_data[i].vbo_source; var type = vbo_source_data[i].type; new_vbo_source_data.push({ vbo_source: new vbo_source.constructor(vbo_source), type: type }); } return new_vbo_source_data; } exports.search_vbo_index_by_type = search_vbo_index_by_type; function search_vbo_index_by_type(array, type) { for (var i = 0; i < array.length; i++) if (array[i].type == type) return i; return -1; } exports.get_vbo_by_type = get_vbo_by_type; function get_vbo_by_type(vbo_data, type) { var index = search_vbo_index_by_type(vbo_data, type); return index == -1 ? null: vbo_data[index].vbo; } exports.get_vbo_source_by_type = get_vbo_source_by_type; function get_vbo_source_by_type(vbo_source_data, type) { var index = search_vbo_index_by_type(vbo_source_data, type); return index == -1 ? null: vbo_source_data[index].vbo_source; } exports.get_constructor_by_type = get_constructor_by_type; function get_constructor_by_type(type) { switch (type) { case VBO_FLOAT: return Float32Array; case VBO_SHORT: return Int16Array; case VBO_UBYTE: return Uint8Array; default: return Float32Array; } } exports.get_vbo_type_by_attr_name = get_vbo_type_by_attr_name; function get_vbo_type_by_attr_name(name) { name = name.replace(/_next$/, ""); name = name.replace(/param_GEOMETRY_VC_a_\w+/, "param_GEOMETRY_VC_a"); switch (name) { case "a_tbn_quat": case "a_shade_tangs": return VBO_SHORT; case "a_color": case "a_bending_col_main": case "a_bending_col_detail": case "a_grass_size": case "a_grass_color": case "param_GEOMETRY_VC_a": case "a_polyindex": case "a_p_bb_vertex": case "a_halo_bb_vertex": case "a_bb_vertex": return VBO_UBYTE; default: return VBO_FLOAT; } } exports.get_type_size_by_attr_name = get_type_size_by_attr_name; function get_type_size_by_attr_name(name) { var type = get_vbo_type_by_attr_name(name); switch (type) { case VBO_FLOAT: return m_util.FLOAT_SIZE; case VBO_SHORT: return m_util.SHORT_SIZE; case VBO_UBYTE: return m_util.BYTE_SIZE; default: return m_util.FLOAT_SIZE; } } exports.get_gl_type_by_attr_name = get_gl_type_by_attr_name; function get_gl_type_by_attr_name(name) { var type = get_vbo_type_by_attr_name(name); switch(type) { case VBO_FLOAT: return _gl.FLOAT; case VBO_SHORT: return _gl.SHORT; case VBO_UBYTE: return _gl.UNSIGNED_BYTE; default: return _gl.FLOAT; } } exports.vbo_source_data_set_attr = vbo_source_data_set_attr; function vbo_source_data_set_attr(vbo_source_data, attr_name, array, offset) { var type = get_vbo_type_by_attr_name(attr_name); var index = search_vbo_index_by_type(vbo_source_data, type); switch (type) { case VBO_FLOAT: vbo_source_data[index].vbo_source.set(array, offset); break; case VBO_SHORT: for (var i = 0; i < array.length; i++) vbo_source_data[index].vbo_source[offset + i] = m_util.float_to_short(array[i]); break; case VBO_UBYTE: for (var i = 0; i < array.length; i++) vbo_source_data[index].vbo_source[offset + i] = m_util.ufloat_to_ubyte(array[i]); break; } } }