import register from "../util/register.js";
import m_batch_fact from "../intern/batch.js";
import m_geom_fact from "../intern/geometry.js";
import m_print_fact from "../intern/print.js";
import m_render_fact from "../intern/renderer.js";
import * as m_tbn from "../intern/tbn.js";
/**
* Low-level geometry API.
* Enable the "Dynamic geometry" checkbox for the objects to allow geometry modification.
* @module geometry
*/
function Geometry(ns, exports) {
var m_batch = m_batch_fact(ns);
var m_geom = m_geom_fact(ns);
var m_print = m_print_fact(ns);
var m_render = m_render_fact(ns);
/**
* Extract the vertex array from the object.
* @method module:geometry.extract_vertex_array
* @param {Object3D} obj Object 3D
* @param {string} mat_name Material name
* @param {string} attrib_name Attribute name (a_position, a_tbn, a_normal)
* @returns {Float32Array} Vertex array
*/
exports.extract_vertex_array = function(obj, mat_name, attrib_name) {
if (!m_geom.has_dyn_geom(obj)) {
m_print.error("Wrong object:", obj.name);
return null;
}
var batch = m_batch.find_batch_material(obj, mat_name, "MAIN");
if (batch) {
var bufs_data = batch.bufs_data;
if (bufs_data && bufs_data.pointers &&
(attrib_name == "a_normal" && bufs_data.pointers["a_tbn"] ||
bufs_data.pointers[attrib_name])) {
return m_geom.extract_array_float(bufs_data, attrib_name);
} else {
m_print.error("Attribute not found:" + attrib_name);
return null;
}
} else {
m_print.error("Wrong material:", mat_name);
return null;
}
}
/**
* Extract the array of triangulated face indices from the given object.
* @method module:geometry.extract_index_array
* @param {Object3D} obj Object 3D
* @param {string} mat_name Material name
* @returns {Uint16Array|Uint32Array} Array of triangle indices
*/
exports.extract_index_array = function(obj, mat_name) {
if (!m_geom.has_dyn_geom(obj)) {
m_print.error("Wrong object:", obj.name);
return null;
}
var batch = m_batch.find_batch_material(obj, mat_name, "MAIN");
if (batch) {
var bufs_data = batch.bufs_data;
if (bufs_data && bufs_data.pointers) {
return bufs_data.ibo_array;
} else {
m_print.error("Buffer data not found");
return null;
}
} else {
m_print.error("Wrong material:", mat_name);
return null;
}
}
/**
* Update the vertex array for the given object.
* @method module:geometry.update_vertex_array
* @param {Object3D} obj Object 3D
* @param {string} mat_name Material name
* @param {string} attrib_name Attribute name (a_position, a_tbn, a_normal)
* @param {Float32Array} array The new array
*/
exports.update_vertex_array = function(obj, mat_name, attrib_name, array) {
if (!m_geom.has_dyn_geom(obj)) {
m_print.error("Wrong object:", obj.name);
return;
}
var types = ["MAIN", "SHADOW", "COLOR_ID"];
for (var i = 0; i < types.length; i++) {
var type = types[i];
var batch = m_batch.find_batch_material(obj, mat_name, type);
if (batch) {
var bufs_data = batch.bufs_data;
if (bufs_data && bufs_data.pointers &&
(attrib_name == "a_normal" && bufs_data.pointers["a_tbn"] ||
bufs_data.pointers[attrib_name]))
m_geom.update_bufs_data_array(bufs_data, attrib_name, 0, array);
}
}
}
/**
* Override geometry for the given object.
* @method module:geometry.override_geometry
* @param {Object3D} obj Object 3D
* @param {string} mat_name Material name
* @param {Uint16Array|Uint32Array} ibo_array Array of triangle indices
* @param {Float32Array} positions_array New vertex positions array
* @param {boolean} smooth_normals Enable normals smoothing
*/
exports.override_geometry = function(obj, mat_name, ibo_array,
positions_array, smooth_normals) {
if (!m_geom.has_dyn_geom(obj)) {
m_print.error("Wrong object:", obj.name);
return;
}
if (!(ibo_array instanceof Uint16Array) &&
!(ibo_array instanceof Uint32Array)) {
m_print.error("Wrong ibo_array type");
return;
}
if (!(positions_array instanceof Float32Array)) {
m_print.error("Wrong positions_array type");
return;
}
var types = ["MAIN", "SHADOW", "COLOR_ID"];
for (var i = 0; i < types.length; i++) {
var batch_type = types[i];
var batch = m_batch.find_batch_material(obj, mat_name, batch_type);
if (batch) {
var bufs_data = batch.bufs_data;
if (bufs_data) {
m_geom.update_bufs_data_index_array(bufs_data, batch.draw_mode,
ibo_array);
var lengths = {};
for (var attr in bufs_data.pointers) {
var pointer = bufs_data.pointers[attr];
var len = positions_array.length / 3 * pointer.num_comp;
var type = m_geom.get_vbo_type_by_attr_name(attr);
if (!lengths[type])
lengths[type] = 0;
lengths[type] += len;
}
var vbo_source_data = m_geom.init_vbo_source_data(lengths);
bufs_data.vbo_source_data = vbo_source_data;
var offsets = {}
for (var attr in bufs_data.pointers) {
var pointer = bufs_data.pointers[attr];
var type = m_geom.get_vbo_type_by_attr_name(attr);
if (!offsets[type])
offsets[type] = 0;
switch (attr) {
case "a_position":
m_geom.vbo_source_data_set_attr(bufs_data.vbo_source_data,
"a_position", positions_array, offsets[type]);
pointer.offset = offsets[type];
pointer.length = positions_array.length;
offsets[type] += pointer.length;
break;
case "a_tbn":
var shared_indices;
if (smooth_normals)
shared_indices = m_geom.calc_shared_indices(
ibo_array, positions_array, positions_array);
var normals = m_geom.calc_normals(ibo_array,
positions_array, shared_indices);
pointer.length = positions_array.length / 3 * pointer.num_comp;
var tangents = new Float32Array(pointer.length);
for (var j = 0; j < tangents.length; j +=4) {
tangents[j] = 1;
tangents[j + 3] = 1;
}
var a_tbn = m_tbn.from_norm_tan(normals, tangents);
m_geom.vbo_source_data_set_attr(bufs_data.vbo_source_data,
"a_tbn", a_tbn, offsets[type]);
pointer.offset = offsets[type];
offsets[type] += pointer.length;
break;
default:
pointer.offset = offsets[type];
pointer.length = positions_array.length / 3 * pointer.num_comp;
var constructor = m_geom.get_constructor_by_type(type);
var new_array = new constructor(pointer.length);
m_geom.vbo_source_data_set_attr(bufs_data.vbo_source_data,
attr, new_array, offsets[type]);
offsets[type] += pointer.length;
break;
}
}
m_geom.update_gl_buffers(bufs_data);
m_render.assign_attribute_setters(batch);
// NOTE: process child batches if bufs_data was copied not by link
var child_batch = m_batch.find_batch_material_forked(obj, batch);
if (child_batch && child_batch.bufs_data)
child_batch.bufs_data = bufs_data;
}
}
}
}
/**
* Apply shape key to the object. If the object is supposed to be available for
* selecting (for example, via the {@link module:scenes.pick_object|pick_object}
* method) call the {@link module:objects.update_boundings|update_boundings} method
* after applying a shape key or override object bounding volumes in Blender
* beforehand so that the boundings can contain the object in its largest shape.
* @method module:geometry.set_shape_key_value
* @param {Object3D} obj Object 3D
* @param {string} key_name Shape key name
* @param {number} value Shape key value
* @example var m_geom = require("geometry");
* var m_scenes = require("scenes");
*
* var cube = m_scenes.get_object_by_name("Cube");
* m_geom.set_shape_key_value(cube, "Key 1", 0.5);
*/
exports.set_shape_key_value = function(obj, key_name, value) {
if (!m_geom.check_shape_keys(obj)) {
m_print.error("Wrong object:", obj.name);
return;
}
if (!m_geom.has_shape_key(obj, key_name)) {
m_print.error("Wrong key name:", key_name);
return;
}
var float_value = parseFloat(value);
m_geom.apply_shape_key(obj, key_name, float_value);
}
/**
* Check if object has got shape keys.
* @method module:geometry.check_shape_keys
* @param {Object3D} obj Object 3D
* @returns {boolean} Checking result.
*/
exports.check_shape_keys = function(obj) {
return m_geom.check_shape_keys(obj);
}
/**
* Return all available shape keys names.
* @method module:geometry.get_shape_keys_names
* @param {Object3D} obj Object 3D
* @returns {string[]} Array of animation names
*/
exports.get_shape_keys_names = function(obj) {
if (!m_geom.check_shape_keys(obj)) {
m_print.error("Wrong object:", obj.name);
return [];
}
return m_geom.get_shape_keys_names(obj);
}
/**
* Return shape key current value.
* @method module:geometry.get_shape_key_value
* @param {Object3D} obj Object 3D
* @param {string} key_name Shape key name
* @returns {number} value Shape key value
*/
exports.get_shape_key_value = function(obj, key_name) {
if (!m_geom.check_shape_keys(obj)) {
m_print.error("Wrong object:", obj.name);
return 0;
}
if (!m_geom.has_shape_key(obj, key_name)) {
m_print.error("Wrong key name:", key_name);
return 0;
}
return m_geom.get_shape_key_value(obj, key_name);
}
/**
* Draw a line.
* @method module:geometry.draw_line
* @param {Object3D} obj Line object
* @param {Float32Array} positions Line points [X0,Y0,Z0,X1,Y1,Z1...] in the
* local space of the given line object.
* @param {boolean} [is_split=false] True - draw a split line
* (points specified in pairs), false - draw continuous line
* @example
* var m_geom = require("geometry");
* var m_scenes = require("scenes");
*
* var empty = m_scenes.get_object_by_name("Empty");
* var points = new Float32Array([0, 0, 0, 5, 0, 0, 0, 0, 5, 0, 0, 0]); // draw a triangle
* m_geom.draw_line(empty, points);
*/
exports.draw_line = function(obj, positions, is_split) {
is_split = is_split || false;
if (!(positions instanceof Float32Array)) {
m_print.error("Wrong positions type");
return;
}
var batch = m_batch.get_first_batch(obj);
if (batch) {
m_geom.draw_line(batch, positions, is_split);
m_render.assign_attribute_setters(batch);
}
}
}
var geometry_factory = register("geometry", Geometry);
export default geometry_factory;