Source: Engine/Entities/Material/AxMaterial.js

/**
 * Creates a new material
 * @param {Axis} context The context to use for the material
 * @constructor
 */
function AxMaterial(context)
{
    AxResource.call(this);
    
    this.context = context;

    this.typeId = AxMaterial.classId;
    this.resourceType = AxResourceType.Material;

    this.properties.Add(new AxProperty(new AxString('Override Materials'), false));

    this.deviceShader = this.context.graphicsDevice.CreateShader();

    this.shadingLayers = new AxList();
    this.renderStates = new AxList();

    this.lightSourcesCount = 0;
    this.isAnimated = false;
    this.successfullyCompiled = false;
    this.compilationMessage = new AxString();

    this.paramAddressWorldMatrix = -1;
    this.paramAddressViewMatrix = -1;
    this.paramAddressProjectionMatrix = -1;
    this.paramAddressViewPosition = -1;
    this.paramAddressLightsPos = -1;
    this.paramAddressLightsColor = -1;
    this.paramAddressLightsIntensity = -1;
    this.paramAddressActiveLightsCount = -1;
    this.paramAddressAmbientLight = -1;
    
    this.shadingPropertiesAddresses = new AxList();
    
    this.noCullingPropertyIndex = -1;

    this.updateState = false;

    this.CommitRenderStates();
}

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


AxMaterial.SerializationId_ShadingLayers		= 0x21111003;
AxMaterial.SerializationId_ShadingLayer		= 0x21111103;
AxMaterial.SerializationId_RenderStates		= 0x21112003;
AxMaterial.SerializationId_RenderState		= 0x21112103;

AxMaterial.classId = (AxResourceType.Material << 16) | 0;

AxMaterial.propertyIndex_OverrideMaterials = AxResource.propertyIndex_ChildPropertiesIndex + 0;


AxMaterial.prototype.Dispose = function()
{
};


/*
 * Creates a property name by combining the given desiredName and propertyNumber.
 * Using this method is recommended when it's needed to create multiple properties of the same name.
 * The desiredName and the propertyNumber are combined by concatenating them with a space in between. If propertyNumber is zero, the result is the unchanged desiredName.
 * @param {AxString} desiredName The desirable property name
 * @param {Integer} propertyNumber The number to combine with the given desiredName. If zero, the return value would be the unchanged desiredName
 * @returns {AxString} A name, created by combining the given desiredName and propertyNumber
 */
AxMaterial.CreatePropertyName = function(desiredName, propertyNumber)
{
    desiredName = AxString.GetAxString(desiredName);

    if (propertyNumber === 0)
        return new AxString(desiredName.contents);

    var result = new AxString(desiredName.contents + " " + propertyNumber);
    return new AxString(result);
};

/*
 * Creates a unique property name, based on the given desiredName.
 * If a property of the given desiredName doesn't already exist, returns desiredName unchanged. Otherwise, returns the given desiredName, combined with the smallest number which will produce a unique name.
 * @param {AxString} desiredName The desirable property name
 * @returns {AxString} A unique name for a property, based on the given desiredName
 */
AxMaterial.prototype.CreateUniquePropertyName = function(desiredName)
{
    desiredName = AxString.GetAxString(desiredName);
    
    var result = new AxString(desiredName.contents);
    var propertyNumber = 0;

    while (this.GetProperty(result) !== null)
    {
        propertyNumber++;
        result = AxMaterial.CreatePropertyName(desiredName, propertyNumber);
    }

    return new AxString(result);
};

/*
 * Sets a property by taking the value of the given one.
 * If a property with a name as the given one exists, its value will be updated, otherwise a new property will be created
 * @param {Integer} index The index at whitch to insert a new property in case one is to be created
 * @param {AxProperty} prop The property which is to be set
 * @returns {Boolean} True if a new property was created, false if an existing property with the same name was updated
 */
AxMaterial.prototype.SetProperty = function(index, prop)
{
    var existingProp = this.GetProperty(prop.name);
    if (existingProp === null)
    {
        existingProp = new AxProperty(prop.name, prop.value, prop.type);
        this.properties.Insert(index, existingProp);
        return true;
    }
    else
    {
        existingProp.SetValue(prop.value, prop.type);
        return false;
    }
};

/*
 * Inserts the proper property for the value of a given color operation
 * @param {Integer} index The index at which to insert the property
 * @param {AxString} name The name of the property to insert
 * @param {AxColorOperation} colorOp The color operation for which to insert the property
 */
AxMaterial.prototype.InsertColorOperationProperty = function(index, name, colorOp)
{
    switch (colorOp)
    {
        case AxColorOperation.None:
        {
            break;
        }

        case AxColorOperation.Set:
        case AxColorOperation.Blend:
        case AxColorOperation.Add:
        case AxColorOperation.Subtract:
        case AxColorOperation.Modulate:
        case AxColorOperation.Lighter:
        case AxColorOperation.Darker:
        case AxColorOperation.AlphaBlend:
        case AxColorOperation.AlphaAdd:
        case AxColorOperation.AlphaSubtract:
        case AxColorOperation.AlphaModulate:
        {
            this.properties.Insert(index, new AxProperty(name, 1.0));
            break;
        }
    }
};

/**
 * Applies the material's render states
 */
AxMaterial.prototype.CommitRenderStates = function()
{
    var numRenderStates = this.renderStates.count;
    for (var i = 0; i < numRenderStates; i++)
        this.context.graphicsDevice.SetRenderState(this.renderStates.Get(i));
};

/**
 * Inserts a shading layer at a given position
 * @param {AxShadingOperation} shadingOp The shading operation to insert
 * @param {AxColorOperation} colorOp The color operation to use with the shading operation
 * @param {Integer} index The index at which to insert the shading operation
 */
AxMaterial.prototype.InsertShadingLayer = function(shadingOp, colorOp, index)
{
    var numProperties = this.properties.count;

    var propertyIndex = index < this.shadingLayers.count ? this.shadingLayers.Get(index).propertyFirst : numProperties;
    var propertyFirst = propertyIndex;

    var defaultTexture = null;//this.context.defaultTexture;

    var countRecurringOps = this.GetShadingLayersCount(shadingOp);

    switch (shadingOp)
    {
        case AxShadingOperation.BasicShading:
        {
            break;
        }

        case AxShadingOperation.FlatColor:
        {
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Flat color", countRecurringOps), new AxVector4()));

            this.InsertColorOperationProperty(propertyIndex++, AxMaterial.CreatePropertyName("Flat color amount", countRecurringOps), colorOp);
            break;
        }

        case AxShadingOperation.Tint:
        {
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Tint color", countRecurringOps), new AxVector4()));

            this.InsertColorOperationProperty(propertyIndex++, AxMaterial.CreatePropertyName("Tint amount", countRecurringOps), colorOp);
            break;
        }

        case AxShadingOperation.AmbientLighting:
        {
            this.InsertColorOperationProperty(propertyIndex++, AxMaterial.CreatePropertyName("Ambient amount", countRecurringOps), colorOp);
            break;
        }

        case AxShadingOperation.LightSource:
        {
            break;
        }

        case AxShadingOperation.VertexLighting:
        {
            if (this.SetProperty(propertyIndex, new AxProperty(AxMaterial.CreatePropertyName("Diffuse", countRecurringOps), new AxVector4(0.5, 0.5, 0.5, 1.0))))
                propertyIndex++;
            if (this.SetProperty(propertyIndex, new AxProperty(AxMaterial.CreatePropertyName("Specular", countRecurringOps), new AxVector4(1.0, 1.0, 1.0, 1.0))))
                propertyIndex++;
            if (this.SetProperty(propertyIndex, new AxProperty(AxMaterial.CreatePropertyName("Gloss", countRecurringOps), 10.0)))
                propertyIndex++;

            this.InsertColorOperationProperty(propertyIndex++, AxMaterial.CreatePropertyName("Vertex lighting amount", countRecurringOps), colorOp);
            break;
        }

        case AxShadingOperation.PixelLighting:
        {
            if (this.SetProperty(propertyIndex, new AxProperty(AxMaterial.CreatePropertyName("Diffuse", countRecurringOps), new AxVector4(0.5, 0.5, 0.5, 1.0))))
                propertyIndex++;
            if (this.SetProperty(propertyIndex, new AxProperty(AxMaterial.CreatePropertyName("Specular", countRecurringOps), new AxVector4(1.0, 1.0, 1.0, 1.0))))
                propertyIndex++;
            if (this.SetProperty(propertyIndex, new AxProperty(AxMaterial.CreatePropertyName("Gloss", countRecurringOps), 10.0)))
                propertyIndex++;

            this.InsertColorOperationProperty(propertyIndex++, AxMaterial.CreatePropertyName("Pixel lighting amount", countRecurringOps), colorOp);
            break;
        }

        case AxShadingOperation.ColorMap:
        {
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Color map", countRecurringOps), defaultTexture, AxParameterType.ReferenceTexture));

            this.InsertColorOperationProperty(propertyIndex++, AxMaterial.CreatePropertyName("Color map amount", countRecurringOps), colorOp);
            break;
        }

        case AxShadingOperation.ReflectionMap:
        {
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Reflection map", countRecurringOps), defaultTexture, AxParameterType.ReferenceTexture));
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Reflection map factor", countRecurringOps), 1.0));

            this.InsertColorOperationProperty(propertyIndex++, AxMaterial.CreatePropertyName("Reflection map amount", countRecurringOps), colorOp);
            break;
        }


        case AxShadingOperation.RefractionMap:
        {
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Refraction map", countRecurringOps), defaultTexture, AxParameterType.ReferenceTexture));
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Refraction factor", countRecurringOps), 0.0));

            this.InsertColorOperationProperty(propertyIndex++, AxMaterial.CreatePropertyName("Refraction map amount", countRecurringOps), colorOp);
            break;
        }

        case AxShadingOperation.NormalMap:
        {
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Normal map", countRecurringOps), defaultTexture, AxParameterType.ReferenceTexture));
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Normal map factor", countRecurringOps), 1.0));
            break;
        }

        case AxShadingOperation.BlurMap:
        {
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Blur map", countRecurringOps), defaultTexture, AxParameterType.ReferenceTexture));
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Blur map factor", countRecurringOps), 0.0));

            this.InsertColorOperationProperty(propertyIndex++, AxMaterial.CreatePropertyName("Blur map amount", countRecurringOps), colorOp);
            break;
        }

        case AxShadingOperation.HBlur:
        {
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("HBlur map", countRecurringOps), defaultTexture, AxParameterType.ReferenceTexture));
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("HBlur map factor", countRecurringOps), 0.0));

            this.InsertColorOperationProperty(propertyIndex++, AxMaterial.CreatePropertyName("HBlur map amount", countRecurringOps), colorOp);
            break;
        }

        case AxShadingOperation.VBlur:
        {
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("VBlur map", countRecurringOps), defaultTexture, AxParameterType.ReferenceTexture));
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("VBlur map factor", countRecurringOps), 0.0));

            this.InsertColorOperationProperty(propertyIndex++, AxMaterial.CreatePropertyName("VBlur map amount", countRecurringOps), colorOp);
            break;
        }

        case AxShadingOperation.AlignScreenQuad:
        {
            break;
        }

        case AxShadingOperation.ProjectTexture:
        {
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Projection Map", countRecurringOps), defaultTexture, AxParameterType.ReferenceTexture));
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Projection Cam", countRecurringOps), 0, AxParameterType.ReferenceCamera));

            this.InsertColorOperationProperty(propertyIndex++, AxMaterial.CreatePropertyName("Project texture amount", countRecurringOps), colorOp);
            break;
        }

        case AxShadingOperation.ProjectPicture:
        {
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Picture Map", countRecurringOps), defaultTexture, AxParameterType.ReferenceTexture));
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Picture Cam", countRecurringOps), 0, AxParameterType.ReferenceCamera));

            this.InsertColorOperationProperty(propertyIndex++, AxMaterial.CreatePropertyName("Project picture amount", countRecurringOps), colorOp);
            break;
        }

        case AxShadingOperation.ShadowMapWriter:
        {
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Shadow map depth", countRecurringOps), 0.0));
            break;
        }

        case AxShadingOperation.ShadowMap:
        {
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("ShadowMap Cam", countRecurringOps), 0, AxParameterType.ReferenceCamera));
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Shadow Map", countRecurringOps), defaultTexture, AxParameterType.ReferenceTexture));
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Light Map", countRecurringOps), defaultTexture, AxParameterType.ReferenceTexture));
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Bias", countRecurringOps), 0.0));
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Shadow map depth", countRecurringOps), 0.0));
            break;
        }

        case AxShadingOperation.TextureTransform:
        {
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Texture Transform", countRecurringOps), 0, AxParameterType.ReferenceTransform));
            break;
        }

        // TODO: Consider removing this operation. Currently, it only provides the properties without actually doing anything
        case AxShadingOperation.Material:
        {
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Ambient", countRecurringOps), new AxVector4(0.2, 0.2, 0.2, 1.0)));
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Diffuse", countRecurringOps), new AxVector4(0.5, 0.5, 0.5, 1.0)));
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Specular", countRecurringOps), new AxVector4(1.0, 1.0, 1.0, 1.0)));
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Gloss", countRecurringOps), 10.0));
            
            break;
        }

        case AxShadingOperation.Fog:
        {
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Fog color", countRecurringOps), new AxVector4(0.8, 0.8, 0.8, 1.0)));
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Fog start", countRecurringOps), 100.0));
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("Fog density", countRecurringOps), 100.0));

            if (this.InsertColorOperationProperty(propertyIndex, AxMaterial.CreatePropertyName("Fog amount", countRecurringOps), colorOp))
                propertyIndex++;

            break;
        }
        // TODO: consider moving this to a "Parameters" resource entity, dedicated to changing various parameters of the engine alont traversing the scene
        case AxShadingOperation.ChangeCulling:
        {
            this.properties.Insert(propertyIndex++, new AxProperty(AxMaterial.CreatePropertyName("No culling", countRecurringOps), false));

            break;
        }
    }

    var numNewProperties = this.properties.count - numProperties;
    this.shadingLayers.Insert(index, new AxMaterialShadingLayer(shadingOp, colorOp, propertyFirst, numNewProperties));

    var count = this.shadingLayers.count;
    for (var i = index + 1; i < count; i++)
        this.shadingLayers.Get(i).propertyFirst += numNewProperties;
};

/**
 * Adds a shading layer to the material
 * @param {AxShadingOperation} shadingOp The shading operation to insert
 * @param {AxColorOperation} colorOp The color operation to use with the shading operation
 * @return {Integer} The index at which the added shading layer can be found. Practically, this is the last index in the material's shading layers collection
 */
AxMaterial.prototype.AddShadingLayer = function(shadingOp, colorOp)
{
    this.InsertShadingLayer(shadingOp, colorOp, this.shadingLayers.count);
    return this.shadingLayers.count - 1;
};

/**
 * Removes a shading layer at specified location
 * @param {Integer} index The index of the shading layer to remove
 */
AxMaterial.prototype.RemoveShadingLayer = function(index)
{
    var count = this.shadingLayers.count;
    for (var i = index + 1; i < count; i++)
        this.shadingLayers.Get(i).propertyFirst -= this.shadingLayers.Get(index).propertyCount;

    this.properties.RemoveRange(this.shadingLayers.Get(index).propertyFirst, this.shadingLayers.Get(index).propertyCount);
    this.shadingLayers.RemoveAt(index);
};

/**
 * Moves a shading layer from one position to another in the material's shading layers collection
 * @param {Integer} originalIndex The original index of the shading layer which is to be moved
 * @param {Integer} newIndex The index to which the shading layer should be moved to
 */
AxMaterial.prototype.MoveShadingLayer = function(originalIndex, newIndex)
{
    var originalIndexPropertiesStart = this.shadingLayers.Get(originalIndex).propertyFirst;
    var originalIndexPropertiesCount = this.shadingLayers.Get(originalIndex).propertyCount;

    var start = AxMath.Min(originalIndex, newIndex);
    var propertyFirst = this.shadingLayers.Get(start).propertyFirst;

    this.shadingLayers.Move(originalIndex, newIndex, 1);

    var count = this.shadingLayers.count;
    for (var i = start; i < count; i++)
    {
        this.shadingLayers.Get(i).propertyFirst = propertyFirst;
        propertyFirst += this.shadingLayers.Get(i).propertyCount;
    }

    this.properties.Move(originalIndexPropertiesStart, this.shadingLayers.Get(newIndex).propertyFirst, originalIndexPropertiesCount);
};

/**
 * Removes all shading layers from the material
 */
AxMaterial.prototype.ClearShadingLayers = function()
{
    this.shadingLayers.Clear();
    this.properties.Clear();

    this.shadingProperties.Clear();
};

/*
 * Returns the number of shading layers of a specified signature
 * @param {AxShadingOperation} shadingOp The shading operation of the layer
 * @param {!AxColorOperation} colorOp The color operation of the layer. If omitted, the color operaion will be disregarded.
 * @returns {Integer} The number of shading layers with the specified signature
 */
AxMaterial.prototype.GetShadingLayersCount = function(shadingOp, colorOp)
{
    var result = 0;
    var ignoreColorOp = AxUtils.IsUndefinedOrNull(colorOp);
    for (var i = 0; i < this.shadingLayers.count; i++)
    {
        var shadingLayer = this.shadingLayers.Get(i);
        if ((shadingLayer.shadingOp === shadingOp) && (ignoreColorOp || (shadingLayer.colorOp === colorOp)))
            result++;
    }

    return result;
};

/**
 * Updates the material shader according to the material's shading layers
 * @return {Boolean} True if the shader was successfully updated
 */
AxMaterial.prototype.UpdateShader = function()
{
    this.successfullyCompiled = false;

    // Setup material
    this.deviceShader.ClearOperations();
    for (var i = 0; i < this.shadingLayers.count; i++)
        this.deviceShader.AddOperation(this.shadingLayers.Get(i).shadingOp, this.shadingLayers.Get(i).colorOp);

    // Update shader
    if (!this.deviceShader.Update(this.compilationMessage))
        return false;

    // Setup parameters
    this.shadingPropertiesAddresses.SetSize(this.properties.count);
    for (var i = 0; i < this.properties.count; i++)
    {
        this.shadingPropertiesAddresses.Set(i, this.deviceShader.GetParameterAddress(this.properties.Get(i).name));
    }

    this.noCullingPropertyIndex = this.properties.IndexOf(this.GetProperty("No culling"));

    this.paramAddressWorldMatrix = this.deviceShader.GetParameterAddress(new AxString('World matrix'));
    this.paramAddressViewMatrix = this.deviceShader.GetParameterAddress(new AxString('View matrix'));
    this.paramAddressProjectionMatrix = this.deviceShader.GetParameterAddress(new AxString('Projection matrix'));
    this.paramAddressViewPosition = this.deviceShader.GetParameterAddress(new AxString('View position'));
    this.paramAddressLightsPos = this.deviceShader.GetParameterAddress(new AxString('Lights pos'));
    this.paramAddressLightsColor = this.deviceShader.GetParameterAddress(new AxString('Lights color'));
    this.paramAddressLightsIntensity = this.deviceShader.GetParameterAddress(new AxString('Lights intensity'));
    this.paramAddressActiveLightsCount = this.deviceShader.GetParameterAddress(new AxString('Active lights count'));
    this.paramAddressAmbientLight = this.deviceShader.GetParameterAddress(new AxString('Ambient light'));

    // Get some common parameters
    this.isAnimated = false;
    this.lightSourcesCount = 0;
    for (var i = 0; i < this.shadingLayers.count; i++)
    {
        if (this.shadingLayers.Get(i).shadingOp === AxShadingOperation.LightSource)
            this.lightSourcesCount++;
        if (this.shadingLayers.Get(i).shadingOp === AxShadingOperation.Skinning)
            this.isAnimated = true;
    }

    this.successfullyCompiled = true;
    return true;
};

/**
 * Applies a material property's value to the shader
 * @param {AxProperty} materialProperty The prperty, whose value is to be applied to the shader
 * @param {Integer} shaderAddress The address of the property in terms of the material's shader
 */
AxMaterial.prototype.ApplyProperty = function(materialProperty, shaderAddress)
{
    if (!this.successfullyCompiled)
        return;

    switch (materialProperty.type)
    {
        case AxParameterType.ReferenceTexture:
        {
            var texture = materialProperty.GetEffectiveValue();
            if (texture !== null)
                this.deviceShader.SetParameter(shaderAddress, 0, texture.deviceTexture);
            break;
        }

        case AxParameterType.ReferenceCamera:
        {
            var camera = materialProperty.GetEffectiveValue();
            if (camera !== null)
                this.deviceShader.SetParameter(shaderAddress, 0, camera.viewProjection);
            break;
        }

        case AxParameterType.ReferenceTransform:
        {
            var transform = materialProperty.GetEffectiveValue();
            if (transform !== null)
                this.deviceShader.SetParameter(shaderAddress, 0, transform.localMatrix);
            break;
        }

        case AxParameterType.ReferenceVector3:
        {
            var transform = materialProperty.GetEffectiveValue();
            if (transform !== null)
            {
                this.deviceShader.SetParameter(shaderAddress, 0, transform.localMatrix._41);
            }
            break;
        }

        default:
        {
            this.deviceShader.SetParameter(shaderAddress, 0, materialProperty.GetEffectiveValue());
            break;
        }
    }
};

/**
 * Applies all material property values to the shader
 */
AxMaterial.prototype.ApplyProperties = function()
{
    if (!this.successfullyCompiled)
        return;

    if (this.properties.count !== this.shadingPropertiesAddresses.count)
        return;

    var count = this.properties.count;
    for (var i = 0; i < count; i++)
        this.ApplyProperty(this.properties.Get(i), this.shadingPropertiesAddresses.Get(i));

    if (this.noCullingPropertyIndex !== -1)
        if (this.properties.Get(this.noCullingPropertyIndex).GetBool())
            this.context.graphicsDevice.SetRenderState(AxRenderState.CullNone);
        else
            this.context.graphicsDevice.SetRenderState(AxRenderState.CullFrontFace);
    else
        this.context.graphicsDevice.SetRenderState(AxRenderState.CullBackFace);
};

/**
 * If the material has light sources, sets them, choosing from the lights in the scene, which are closest to the given location
 * The location should generally be the place where the object rendered with this material is located in the scene
 * @param {AxVector3} location Location in relation to which to select the closest lights in the scene
 */
AxMaterial.prototype.ApplyLights = function(location)
{
    //*
    if (this.lightSourcesCount > 0)
    {
        this.context.CalculateNearestActiveLights(this.lightSourcesCount, location);
        var lightsCount = this.context.nearestActiveLights.count;
        this.deviceShader.SetParameter(this.paramAddressActiveLightsCount, 0, lightsCount);
        if (lightsCount > 0)
        {
            if ((this.paramAddressLightsPos !== -1) && (this.paramAddressLightsColor !== -1) && (this.paramAddressLightsIntensity !== -1))
            {
                for (var i = 0; i < lightsCount; i++)
                {
                    this.deviceShader.SetParameter(this.paramAddressLightsPos, i, this.context.nearestActiveLights.Get(i).lightPos);
                    this.deviceShader.SetParameter(this.paramAddressLightsColor, i , this.context.nearestActiveLights.Get(i).lightRef.properties.Get(AxLight.propertyIndex_Color).GetEffectiveValue());
                    this.deviceShader.SetParameter(this.paramAddressLightsIntensity, i , this.context.nearestActiveLights.Get(i).lightRef.properties.Get(AxLight.propertyIndex_Intensity).GetEffectiveValue());
                }
            }
        }
    }
    else
        this.deviceShader.SetParameter(this.paramAddressActiveLightsCount, 0, this.lightSourcesCount);
    //*/
};

/**
 * Sets the context's current bone transformations to the material's shader
 */
AxMaterial.prototype.ApplyBones = function()
{
    // TODO: addresses of parameters should be precalculated, or GetParameterAddress should accept not a parameter name, but an id, so it can map fast and directly to the address
    if (this.isAnimated)
    {
        if (this.paramAddressWorldMatrix !== -1)
        {
            for (var i = 0; i < this.context.bones.count; i++)
            {
                this.deviceShader.SetParameter(this.paramAddressWorldMatrix ,i, this.context.bones.Get(i));
            }
        }
    }
};


/**
 * Deserializes the material from a given stream
 * @param {AxStream} source The stream holding the serialized data
 * @return {Boolean} True if the deserialization was successfull
 */
AxMaterial.prototype.Deserialize = function(source)
{
    if (!AxResource.prototype.Deserialize.call(this, source))
        return false;
    
    return this.UpdateShader();
};

/**
 * Writes chunks for all the data which is needed to serialize the material. 
 * @param {AxHierarchyStreamWriter} writer Writer to use for writing the serialization data
 */
AxMaterial.prototype.SerializeChunks = function(writer)
{
    writer.BeginChunk(AxMaterial.SerializationId_ShadingLayers);

    for (var i = 0; i < this.shadingLayers.count; i++)
    {
        writer.BeginChunk(AxMaterial.SerializationId_ShadingLayer);
        writer.stream.WriteInt16(this.shadingLayers.Get(i).shadingOp);
        writer.stream.WriteInt16(this.shadingLayers.Get(i).colorOp);

        writer.EndChunk();
    }

    writer.EndChunk();

    writer.BeginChunk(AxMaterial.SerializationId_RenderStates);

    for (var i = 0; i < this.shadingLayers.count; i++)
    {
        writer.BeginChunk(AxMaterial.SerializationId_RenderState);
        writer.stream.WriteInt16(this.renderStates.Get(i));
        writer.EndChunk();
    }

    writer.EndChunk();

    // Make base serialization at end, because shader data creates properties, which need to be filled afterwards
    AxResource.prototype.SerializeChunks.call(this, writer);
};

/**
 * 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
 */
AxMaterial.prototype.DeserializeChunk = function(reader)
{
    if (AxResource.prototype.DeserializeChunk.call(this, reader))
        return true;

    switch (reader.chunkId)
    {
        case AxMaterial.SerializationId_ShadingLayers:
        {
            break;
        }

        case AxMaterial.SerializationId_ShadingLayer:
        {
            var shadingOp = reader.stream.ReadInt16();
            var colorOp = reader.stream.ReadInt16();
            this.AddShadingLayer(shadingOp, colorOp);
            break;
        }

        case AxMaterial.SerializationId_RenderStates:
        {
            break;
        }

        case AxMaterial.SerializationId_RenderState:
        {
            this.renderStates.Add(reader.stream.ReadInt16());
            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)