Source: Engine/Entities/Mesh/AxMesh.js

/**
 * Creates a new mesh
 * @param {Axis} context The context to use for the mesh
 * @constructor
 */
function AxMesh(context)
{
    AxResource.call(this);
    
    this.typeId = AxMesh.classId;
    this.resourceType = AxResourceType.Mesh;
    this.active = true;

    this.bounds = new AxBoundingVolume();
        
    this.properties.Add(new AxProperty(new AxString('Cull'), true));
    
    this.deviceMesh = context.graphicsDevice.CreateMesh();

    this.serializeVertices = true;
    this.serializeTexCoords = true;
    this.serializeBones = true;
    this.serializeNormals = false;
    this.serializeTangents = false;
    
	this.blendChannelsCount = 0;
	this.blendChannels = null;
    this.blendOrigin = null;
    this.blendBuffer = null;
}

AxMesh.prototype = Object.create(AxResource.prototype);



AxMesh.classId = (AxResourceType.Mesh << 16) | 0;

AxMesh.propertyIndex_Cull = AxResource.propertyIndex_ChildPropertiesIndex + 0;

AxMesh.SerializationId_Vertices         = 0x21111004;
AxMesh.SerializationId_Normals          = 0x21112004;
AxMesh.SerializationId_TexCoords        = 0x21113004;
AxMesh.SerializationId_Triangles        = 0x21114004;
AxMesh.SerializationId_Bones            = 0x21115004;
AxMesh.SerializationId_BlendVertices    = 0x21111104;



AxMesh.prototype.Dispose = function()
{
	this.SetBlendChannelsCount(0);

    //this.deviceMesh.Dispose();
};

/**
 * Computes normals for the mesh based on averaging the orientation of shared faces
 */
AxMesh.prototype.ComputeNormals = function()
{
    new AxNormalsGenerator(this.deviceMesh);	

    this.deviceMesh.UpdateVertices(0, -1);
};

/**
 * Computes tangent vectors for the mesh, based on the orientation of the mesh's texture mapping base vectors, by averaging the tangents of a vertex's shared faces
 */
AxMesh.prototype.ComputeTangents = function()
{
    new AxTangentsGenerator(this.deviceMesh);	

    this.deviceMesh.UpdateVertices(0, -1);
};

/**
 * Computes the bounding volume of the mesh
 */
AxMesh.prototype.ComputeBounds = function()
{
    v = new AxVector3();
    min = new AxVector3();
    max = new AxVector3();

    min.Set_1(AxMath.FloatMax);
    max.Set_1(AxMath.FloatMin);
    var numVertices = this.deviceMesh.GetVertexCount();
    for (var i = 0; i < numVertices; i++)
    {
        this.deviceMesh.GetVertexPosition(i, v);
        AxVector3.Min(min, min, v);
        AxVector3.Max(max, max, v);
    }

    AxVector3.Add(this.bounds.center, min, max);
    AxVector3.Scale(this.bounds.center, this.bounds.center, 1.0 / 2.0);

    AxVector3.Subtract(this.bounds.box, max, min);
    AxVector3.Scale(this.bounds.box, this.bounds.box, 1.0 / 2.0);

    this.bounds.sphereRadius = 0.0;
    for (var i = 0; i < numVertices; i++)
    {
        this.deviceMesh.GetVertexPosition(i, v);
        this.bounds.sphereRadius = AxMath.Max(this.bounds.sphereRadius, AxVector3.Distance(this.bounds.center, v));
    }
};

    
/**
 * Tests whether the mesh gets intersected by a given ray and gives details on the intersectoin
 * @param {AxVector3} rayPoint Starting point of the ray
 * @param {AxVector3} rayDir Orientation of the ray
 * @param {AxIntersectionInfo} intersectionInfo Outputs details on the intersection
 * @return {Boolean} True if the mesh gets intersected by the given ray
 */
AxMesh.prototype.Intersection = function(rayPoint, rayDir, intersectionInfo)
{
    intersectionInfo.hasIntersected = false;

    if (!AxMaths.RayToSphereIntersection(rayPoint, rayDir, this.bounds.center, this.bounds.sphereRadius))
        return false;

    var v0 = new AxVector3();
    var v1 = new AxVector3();
    var v2 = new AxVector3();
    var intersection = new AxVector3();

    intersectionInfo.distance = AxMath.FloatMax;
    var numIndices = this.deviceMesh.GetIndexCount();
    for (var i = 0; i < numIndices; i += 3)
    {
        var index0 = this.deviceMesh.GetIndex(i + 2);
        var index1 = this.deviceMesh.GetIndex(i + 1);
        var index2 = this.deviceMesh.GetIndex(i);

        this.deviceMesh.GetVertexPosition(index0, v0);
        this.deviceMesh.GetVertexPosition(index1, v1);
        this.deviceMesh.GetVertexPosition(index2, v2);
            
        if (AxMaths.RayToTriangleIntersection(rayPoint, rayDir, v0, v1, v2, intersection))
        {
            if ((intersection.z > 0) && (intersection.z < intersectionInfo.distance))
            {
                var delta1 = new AxVertex(), delta2 = new AxVertex();
                var n0 = new AxVector3(), n1 = new AxVector3(), n2 = new AxVector3();
                var texCoord0 = new AxVector2(), texCoord1 = new AxVector2(), texCoord2 = new AxVector2();

                this.deviceMesh.GetVertexNormal(index0, n0);
                this.deviceMesh.GetVertexNormal(index1, n1);
                this.deviceMesh.GetVertexNormal(index2, n2);

                this.deviceMesh.GetVertexTexCoords(index0, texCoord0);
                this.deviceMesh.GetVertexTexCoords(index1, texCoord1);
                this.deviceMesh.GetVertexTexCoords(index2, texCoord2);

                AxVector3.Subtract(delta1.position, v1, v0);
                AxVector3.Subtract(delta1.normal, n1, n0);
                AxVector2.Subtract(delta1.texCoords, texCoord1, texCoord0);

                AxVector3.Subtract(delta2.position, v2, v0);
                AxVector3.Subtract(delta2.normal, n2, n0);
                AxVector2.Subtract(delta2.texCoords, texCoord2, texCoord0);


                intersectionInfo.point.position.x = v0.x + delta1.position.x * intersection.x + delta2.position.x * intersection.y;
                intersectionInfo.point.position.y = v0.y + delta1.position.y * intersection.x + delta2.position.y * intersection.y;
                intersectionInfo.point.position.z = v0.z + delta1.position.z * intersection.x + delta2.position.z * intersection.y;
                intersectionInfo.point.normal.x = n0.x + delta1.normal.x * intersection.x + delta2.normal.x * intersection.y;
                intersectionInfo.point.normal.y = n0.y + delta1.normal.y * intersection.x + delta2.normal.y * intersection.y;
                intersectionInfo.point.normal.z = n0.z + delta1.normal.z * intersection.x + delta2.normal.x * intersection.y;
                intersectionInfo.point.texCoords.x = texCoord0.x + delta1.texCoords.x * intersection.x + delta2.texCoords.x * intersection.y;
                intersectionInfo.point.texCoords.y = texCoord0.y + delta1.texCoords.y * intersection.x + delta2.texCoords.y * intersection.y;

                intersectionInfo.distance = intersection.z;
                intersectionInfo.primitiveIndex = i;
            }
            intersectionInfo.hasIntersected = true;
        }
    }
    return intersectionInfo.hasIntersected;
};

/**
 * Copies another mesh
 * @param {AxMesh} source The mesh to be copied
 */
AxMesh.prototype.CopyFrom = function(source)
{
    this.deviceMesh.CopyFrom(source.deviceMesh);
};

/**
 * Sets the number of blend channels, preparing the mesh for blending
 * Blend channels are versions of the mesh, but with different deformations. These versions are used to blend in different amount witht he original to morph it and to create animations.
 * Blend channels correspond to the "Morph" modificator in 3DS Max the and "Blending" in Maya
 * @param {type} numberOfChannels The number of blend channels to set for use
 */
AxMesh.prototype.SetBlendChannelsCount = function(numberOfChannels)
{
	if (this.blendChannels !== null)
	{
		this.blendChannelsCount = 0;
		this.blendChannels = null;
		this.blendOrigin = null;
		this.blendBuffer = null;
	}

	if (numberOfChannels > 0)
	{
		for (var i = 0; i < numberOfChannels; i++)
		{
			var propName = new AxString("Blend " + i.toString());
			var prop = this.GetProperty(propName);
			if (prop === null)
				prop = this.properties.Add(new AxProperty(propName, 0.0));
		}
		this.propertyIndex_Blend = this.properties.IndexOf(this.GetProperty("Blend 0"));

		var vertexCount = this.deviceMesh.GetVertexCount();

		this.blendChannelsCount = numberOfChannels;
		this.blendChannels = [];
        this.blendOrigin = [];
		this.blendBuffer = [];
        
		for (var i = 0; i < numberOfChannels * vertexCount; i++)
            this.blendChannels.push(new AxVector3());

		for (var i = 0; i < vertexCount; i++)
        {
            this.blendOrigin.push(new AxVector3());
        	this.blendBuffer.push(new AxVector3());
			this.deviceMesh.GetVertexPosition(i, this.blendOrigin[i]);
        }
	}
};

/**
 * Applies blending channels and changes the mesh geometry
 */
AxMesh.prototype.ApplyBlendChannels = function()
{
    if (this.blendChannelsCount === 0)
		return;

	var vertexCount = this.deviceMesh.GetVertexCount();

	//this.blendBuffer = this.blendOrigin.slice(0);
    for (var i = 0; i < vertexCount; i++)
    {
        this.blendBuffer[i].x = this.blendOrigin[i].x;
        this.blendBuffer[i].y = this.blendOrigin[i].y;
        this.blendBuffer[i].z = this.blendOrigin[i].z;
    }

    for (var channelIndex = 0; channelIndex < this.blendChannelsCount; channelIndex++)
	{
		var blendFactor = this.properties.Get(this.propertyIndex_Blend + channelIndex).GetFloat();

		if (AxMath.Abs(blendFactor) < 0.01)
			continue;

		for (var vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++)
		{
			var blendVector = this.blendChannels[channelIndex * vertexCount + vertexIndex];
			var blendSum = this.blendBuffer[vertexIndex];

			blendSum.x += blendVector.x * blendFactor;
			blendSum.y += blendVector.y * blendFactor;
			blendSum.z += blendVector.z * blendFactor;
		}
	}

	for (var vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++)
    {
		this.deviceMesh.SetVertexPosition(vertexIndex, this.blendBuffer[vertexIndex]);
    }

    this.deviceMesh.context.gl.bindBuffer(this.deviceMesh.context.gl.ARRAY_BUFFER, this.deviceMesh.verticesPositionsBuffer);
    this.deviceMesh.context.gl.bufferData(this.deviceMesh.context.gl.ARRAY_BUFFER, this.deviceMesh.verticesPositions, this.deviceMesh.context.gl.STATIC_DRAW);
	//this.deviceMesh.UpdateVertices(0, vertexCount);
};

/**
 * Deserializes the mesh from a given stream
 * @param {AxStream} source The stream holding the serialized data
 * @return {Boolean} True if the deserialization was successfull
 */
AxMesh.prototype.Deserialize = function(source)
{
    this.serializeVertices = false;
    this.serializeNormals = false;
    this.serializeTexCoords = false;
    this.serializeTangents = false;
    this.serializeBones = false;
    
    if (!AxResource.prototype.Deserialize.call(this, source))
        return false;
    
    if (!this.serializeNormals)
        this.ComputeNormals();

    if (!this.serializeTangents)
        this.ComputeTangents();
    

    this.deviceMesh.UpdateIndices(0, -1);
    this.deviceMesh.UpdateVertices(0, -1);
    this.ComputeBounds();

    return true;
};

/**
 * Writes chunks for all the data which is needed to serialize the mesh. 
 * @param {AxHierarchyStreamWriter} writer Writer to use for writing the serialization data
 */
AxMesh.prototype.SerializeChunks = function(writer)
{
    AxResource.prototype.SerializeChunks.call(this, writer);

    var embedMeshData = this.context.settings.properties.Get(AxSettings.propertyIndex_EmbedGeneratedMeshes).GetBool();

    var numVertices = this.deviceMesh.GetVertexCount();
    var numIndices = this.deviceMesh.GetIndexCount();
    var v3 = new AxVector3();
    var v2 = new AxVector2();
    var v41 = new AxVector4();
    var v42 = new AxVector4();

    if (this.serializeVertices || embedMeshData)
    {
        writer.BeginChunk(AxMesh.SerializationId_Vertices);
        writer.stream.WriteInt32(numVertices);
		if (this.blendOrigin === null)
		{
			for (var i = 0; i < numVertices; i++)
			{
				this.deviceMesh.GetVertexPosition(i, v3);
				AxSerializationUtils.SerializeVector3(writer.stream, v3);
			}
		}
		else
		{
			for (var i = 0; i < numVertices; i++)
				AxSerializationUtils.SerializeVector3(writer.stream, this.blendOrigin[i]);
		}
        writer.EndChunk();
    }

    if (this.serializeNormals)
    {
        writer.BeginChunk(AxMesh.SerializationId_Normals);
        writer.stream.WriteInt32(numVertices);
        for (var i = 0; i < numVertices; i++)
        {
            this.deviceMesh.GetVertexNormal(i, v3);
            AxSerializationUtils.SerializeVector3(writer.stream, v3);
        }
        writer.EndChunk();
    }

    if (this.serializeTexCoords || embedMeshData)
    {
        writer.BeginChunk(AxMesh.SerializationId_TexCoords);
        writer.stream.WriteInt32(numVertices);
        for (var i = 0; i < numVertices; i++)
        {
            this.deviceMesh.GetVertexTexCoords(i, v2);
            AxSerializationUtils.SerializeVector2(writer.stream, v2);
        }
        writer.EndChunk();
    }

    if (this.serializeVertices || embedMeshData)
    {
        writer.BeginChunk(AxMesh.SerializationId_Triangles);
        writer.stream.WriteInt32(numIndices);
        for (var i = 0; i < numIndices; i++)
        {
            writer.stream.WriteUInt32(this.deviceMesh.GetIndex(i));
        }
        writer.EndChunk();
    }

    if (this.serializeBones)
    {
        writer.BeginChunk(AxMesh.SerializationId_Bones);
        writer.stream.WriteInt32(numVertices);
        for (var i = 0; i < numVertices; i++)
        {
            this.deviceMesh.GetVertexBones(i, v41, v42);
            AxSerializationUtils.SerializeVector4(writer.stream, v41);
            AxSerializationUtils.SerializeVector4(writer.stream, v42);
        }
        writer.EndChunk();
    }
    
	if (this.blendChannelsCount > 0)
	{
		writer.BeginChunk(AxMesh.SerializationId_BlendVertices);
		writer.stream.WriteInt32(this.blendChannelsCount);
		writer.stream.WriteInt32(numVertices);
		for (var blendChannelIndex = 0; blendChannelIndex < this.blendChannelsCount; blendChannelIndex++)
		{
			for (var i = 0; i < numVertices; i++)
			{
				AxSerializationUtils.SerializeVector3(writer.stream, this.blendChannels[blendChannelIndex * numVertices + i]);
			}
		}
		writer.EndChunk();
	}
    
};

/**
 * Reads the data of a chunk. The chunk header is already read by the reader and this method deserializes the contents of the chunk. Called continuously for each of the mesh's chunks
 * @param {AxHierarchyStreamReader} reader Reader to use for reading the serialized data.
 * @return {Boolean} True if a chunk was successfully deserialized
 */
AxMesh.prototype.DeserializeChunk = function(reader)
{
    if (AxResource.prototype.DeserializeChunk.call(this, reader))
        return true;

    switch (reader.chunkId)
    {
        case AxMesh.SerializationId_Vertices:
        {
            var numVertices = reader.stream.ReadInt32();
            if (numVertices !== this.deviceMesh.GetVertexCount())
                this.deviceMesh.CreateVertexBuffer(numVertices);

            for (var i = 0; i < numVertices; i++)
                this.deviceMesh.SetVertexPosition(i, AxSerializationUtils.DeserializeVector3(reader.stream));
            
            this.serializeVertices = true;

            break;
        }

        case AxMesh.SerializationId_Normals:
        {
            var numVertices = reader.stream.ReadInt32();
            if (numVertices !== this.deviceMesh.GetVertexCount())
                this.deviceMesh.CreateVertexBuffer(numVertices);

            for (var i = 0; i < numVertices; i++)
                this.deviceMesh.SetVertexNormal(i, AxSerializationUtils.DeserializeVector3(reader.stream));
            
            this.deserializedNormals = true;
            
            this.serializeNormals = true;

            break;
        }

        case AxMesh.SerializationId_TexCoords:
        {
            var numVertices = reader.stream.ReadInt32();
            if (numVertices !== this.deviceMesh.GetVertexCount())
                this.deviceMesh.CreateVertexBuffer(numVertices);

            for (var i = 0; i < numVertices; i++)
                this.deviceMesh.SetVertexTexCoords(i, AxSerializationUtils.DeserializeVector2(reader.stream));

            this.serializeTexCoords = true;
            
            break;
        }

        case AxMesh.SerializationId_Triangles:
        {
            var numIndices = reader.stream.ReadInt32();
            this.deviceMesh.CreateIndexBuffer(numIndices);

            for (var i = 0; i < numIndices; i++)
                this.deviceMesh.SetIndex(i, reader.stream.ReadUInt32());
            
            break;
        }

        case AxMesh.SerializationId_Bones:
        {
            var numVertices = reader.stream.ReadInt32();
            if (numVertices !== this.deviceMesh.GetVertexCount())
                this.deviceMesh.CreateVertexBuffer(numVertices);

            var boneIndices = new AxVector4();
            var boneWeights = new AxVector4();
            for (var i = 0; i < numVertices; i++)
            {
                boneIndices = AxSerializationUtils.DeserializeVector4(reader.stream);
                boneWeights = AxSerializationUtils.DeserializeVector4(reader.stream);
                this.deviceMesh.SetVertexBones(i, boneIndices, boneWeights);
            }
            
            this.serializeBones = true;

            break;
        }

		case AxMesh.SerializationId_BlendVertices:
		{
			var numBlendChannels = reader.stream.ReadInt32();
			var numVertices = reader.stream.ReadInt32();

			this.SetBlendChannelsCount(numBlendChannels);

			for (var blendChannelIndex = 0; blendChannelIndex < numBlendChannels; blendChannelIndex++)
			{
				for (var i = 0; i < numVertices; i++)
				{
					this.blendChannels[blendChannelIndex * numVertices + i] = AxSerializationUtils.DeserializeVector3(reader.stream);
				}
			}

			break;
		}

        default:
        {
            return false;
        }
    }

    return true;
};

Documentation generated by JSDoc 3.5.3 on Mon Feb 19 2018 20:39:26 GMT+0200 (FLE Standard Time)