import { EventDispatcher } from '../EventDispatcher.js';
import { generateUUID } from '../base.js';
import { Box3 } from '../math/Box3.js';
import { Sphere } from '../math/Sphere.js';
import { BufferAttribute } from './BufferAttribute.js';
var geometryId = 1;
/**
* An efficient representation of mesh, line, or point geometry.
* Includes vertex positions, face indices, normals, colors, UVs, and custom attributes within buffers, reducing the cost of passing all this data to the GPU.
* To read and edit data in {@link zen3d.Geometry#attributes}.
* @constructor
* @memberof zen3d
* @extends zen3d.EventDispatcher
*/
function Geometry() {
EventDispatcher.call(this);
Object.defineProperty(this, 'id', { value: geometryId++ });
/**
* UUID of this geometry instance.
* This gets automatically assigned, so this shouldn't be edited.
* @type {string}
*/
this.uuid = generateUUID();
/**
* This hashmap has as id the name of the attribute to be set and as value the buffer to set it to.
* Rather than accessing this property directly, use {@link zen3d.Geometry#addAttribute} and {@link zen3d.Geometry#getAttribute} to access attributes of this geometry.
* @type {Object}
*/
this.attributes = {};
/**
* Hashmap of Attributes Array for morph targets.
* @type {Object}
*/
this.morphAttributes = {};
/**
* Allows for vertices to be re-used across multiple triangles; this is called using "indexed triangles" and each triangle is associated with the indices of three vertices.
* This attribute therefore stores the index of each vertex for each triangular face.
* If this attribute is not set, the renderer assumes that each three contiguous positions represent a single triangle.
* @type {zen3d.BufferAttribute|null}
*/
this.index = null;
/**
* Bounding box for the bufferGeometry, which can be calculated with {@link zen3d.Geometry#computeBoundingBox}.
* @type {zen3d.Box3}
* @default zen3d.Box3()
*/
this.boundingBox = new Box3();
/**
* Bounding sphere for the bufferGeometry, which can be calculated with {@link zen3d.Geometry#computeBoundingSphere}.
* @type {zen3d.Sphere}
* @default zen3d.Sphere()
*/
this.boundingSphere = new Sphere();
/**
* Split the geometry into groups, each of which will be rendered in a separate WebGL draw call. This allows an array of materials to be used with the geometry.
* Each group is an object of the form:
* { start: Integer, count: Integer, materialIndex: Integer }
* @type {Array}
* @default []
*/
this.groups = [];
/**
* A version number, incremented every time the attribute object or index object changes to mark VAO drity.
* @type {Integer}
* @default 0
*/
this.version = 0;
}
Geometry.prototype = Object.assign(Object.create(EventDispatcher.prototype), /** @lends zen3d.Geometry.prototype */{
constructor: Geometry,
/**
* Adds an attribute to this geometry.
* Use this rather than the attributes property.
* @param {string} name
* @param {zen3d.BufferAttribute|zen3d.InterleavedBufferAttribute} attribute
*/
addAttribute: function(name, attribute) {
this.attributes[name] = attribute;
},
/**
* Returns the attribute with the specified name.
* @return {zen3d.BufferAttribute|zen3d.InterleavedBufferAttribute}
*/
getAttribute: function(name) {
return this.attributes[name];
},
/**
* Removes the attribute with the specified name.
*/
removeAttribute: function(name) {
delete this.attributes[name];
},
/**
* Set the {@link zen3d.Geometry#index} buffer.
* @param {Array|zen3d.BufferAttribute} index
*/
setIndex: function(index) {
if (Array.isArray(index)) {
this.index = new BufferAttribute(new Uint16Array(index), 1);
} else {
this.index = index;
}
},
/**
* Adds a group to this geometry; see the {@link zen3d.Geometry#groups} for details.
* @param {Integer} start
* @param {Integer} count
* @param {Integer} materialIndex
*/
addGroup: function(start, count, materialIndex) {
this.groups.push({
start: start,
count: count,
materialIndex: materialIndex !== undefined ? materialIndex : 0
});
},
/**
* Clears all groups.
*/
clearGroups: function() {
this.groups = [];
},
/**
* Computes bounding box of the geometry, updating {@link zen3d.Geometry#boundingBox}.
* Bounding boxes aren't computed by default. They need to be explicitly computed.
*/
computeBoundingBox: function() {
var position = this.attributes["a_Position"] || this.attributes["position"];
if (position.isInterleavedBufferAttribute) {
var data = position.data;
this.boundingBox.setFromArray(data.array, data.stride);
} else {
this.boundingBox.setFromArray(position.array, position.size);
}
},
/**
* Computes bounding sphere of the geometry, updating {@link zen3d.Geometry#boundingSphere}.
* Bounding spheres aren't computed by default. They need to be explicitly computed.
*/
computeBoundingSphere: function() {
var position = this.attributes["a_Position"] || this.attributes["position"];
if (position.isInterleavedBufferAttribute) {
var data = position.data;
this.boundingSphere.setFromArray(data.array, data.stride);
} else {
this.boundingSphere.setFromArray(position.array, position.size);
}
},
/**
* Disposes the object from memory.
* You need to call this when you want the BufferGeometry removed while the application is running.
*/
dispose: function() {
this.dispatchEvent({ type: 'dispose' });
},
/**
* Copies another Geometry to this Geometry.
* @param {zen3d.Geometry} source - The geometry to be copied.
* @return {zen3d.Geometry}
*/
copy: function(source) {
var name, i, l;
// reset
this.index = null;
this.attributes = {};
this.morphAttributes = {};
this.groups = [];
this.boundingBox = null;
this.boundingSphere = null;
// index
var index = source.index;
if (index !== null) {
this.setIndex(index.clone());
}
// attributes
var attributes = source.attributes;
for (name in attributes) {
var attribute = attributes[name];
this.addAttribute(name, attribute.clone());
}
// morph attributes
var morphAttributes = source.morphAttributes;
for (name in morphAttributes) {
var array = [];
var morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes
for (i = 0, l = morphAttribute.length; i < l; i++) {
array.push(morphAttribute[i].clone());
}
this.morphAttributes[name] = array;
}
// groups
var groups = source.groups;
for (i = 0, l = groups.length; i < l; i++) {
var group = groups[i];
this.addGroup(group.start, group.count, group.materialIndex);
}
var boundingBox = source.boundingBox;
if (boundingBox !== null) {
this.boundingBox = new Box3().copy(boundingBox);
}
// bounding sphere
var boundingSphere = source.boundingSphere;
if (boundingSphere !== null) {
this.boundingSphere = new Sphere().copy(boundingSphere);
}
return this;
},
/**
* Creates a clone of this Geometry.
* @return {zen3d.Geometry}
*/
clone() {
return new Geometry().copy(this);
}
});
export { Geometry };