Source: framework/components/camera/system.js

Object.assign(pc, function () {
    var _schema = [
        'enabled',
        'clearColorBuffer',
        'clearColor',
        'clearDepthBuffer',
        'clearStencilBuffer',
        'frustumCulling',
        'projection',
        'fov',
        'orthoHeight',
        'nearClip',
        'farClip',
        'priority',
        'rect',
        'scissorRect',
        'camera',
        'aspectRatio',
        'aspectRatioMode',
        'horizontalFov',
        'model',
        'renderTarget',
        'calculateTransform',
        'calculateProjection',
        'cullFaces',
        'flipFaces',
        'layers'
    ];

    /**
     * @constructor
     * @name pc.CameraComponentSystem
     * @classdesc Used to add and remove {@link pc.CameraComponent}s from Entities. It also holds an
     * array of all active cameras.
     * @description Create a new CameraComponentSystem
     * @param {pc.Application} app The Application
     *
     * @property {pc.CameraComponent[]} cameras Holds all the active camera components
     * @extends pc.ComponentSystem
     */
    var CameraComponentSystem = function (app) {
        pc.ComponentSystem.call(this, app);

        this.id = 'camera';
        this.description = "Renders the scene from the location of the Entity.";

        this.ComponentType = pc.CameraComponent;
        this.DataType = pc.CameraComponentData;

        this.schema = _schema;

        // holds all the active camera components
        this.cameras = [];

        this.on('beforeremove', this.onBeforeRemove, this);
        this.on('remove', this.onRemove, this);
        this.app.on("prerender", this.onPrerender, this);

        pc.ComponentSystem.bind('update', this.onUpdate, this);
    };
    CameraComponentSystem.prototype = Object.create(pc.ComponentSystem.prototype);
    CameraComponentSystem.prototype.constructor = CameraComponentSystem;

    pc.Component._buildAccessors(pc.CameraComponent.prototype, _schema);

    Object.assign(CameraComponentSystem.prototype, {
        initializeComponentData: function (component, _data, properties) {
            properties = [
                'postEffects',
                'enabled',
                'model',
                'camera',
                'aspectRatio',
                'aspectRatioMode',
                'horizontalFov',
                'renderTarget',
                'clearColor',
                'fov',
                'orthoHeight',
                'nearClip',
                'farClip',
                'projection',
                'priority',
                'clearColorBuffer',
                'clearDepthBuffer',
                'clearStencilBuffer',
                'frustumCulling',
                'rect',
                'scissorRect',
                'calculateTransform',
                'calculateProjection',
                'cullFaces',
                'flipFaces',
                'layers'
            ];

            // duplicate data because we're modifying the data
            var data = {};
            for (var i = 0, len = properties.length; i < len; i++) {
                var property = properties[i];
                data[property] = _data[property];
            }

            if (data.layers && pc.type(data.layers) === 'array') {
                data.layers = data.layers.slice(0);
            }

            if (data.clearColor && pc.type(data.clearColor) === 'array') {
                var c = data.clearColor;
                data.clearColor = new pc.Color(c[0], c[1], c[2], c[3]);
            }

            if (data.rect && pc.type(data.rect) === 'array') {
                var rect = data.rect;
                data.rect = new pc.Vec4(rect[0], rect[1], rect[2], rect[3]);
            }

            if (data.scissorRect && pc.type(data.scissorRect) === 'array') {
                var scissorRect = data.scissorRect;
                data.scissorRect = new pc.Vec4(scissorRect[0], scissorRect[1], scissorRect[2], scissorRect[3]);
            }

            if (data.activate) {
                console.warn("WARNING: activate: Property is deprecated. Set enabled property instead.");
                data.enabled = data.activate;
            }

            data.camera = new pc.Camera();
            data._node = component.entity;
            data.camera._component = component;

            var self = component;
            data.camera.calculateTransform = function (mat, mode) {
                if (!self._calculateTransform)
                    return null;

                return self._calculateTransform(mat, mode);
            };
            data.camera.calculateProjection = function (mat, mode) {
                if (!self._calculateProjection)
                    return null;

                return self._calculateProjection(mat, mode);
            };

            data.postEffects = new pc.PostEffectQueue(this.app, component);

            pc.ComponentSystem.prototype.initializeComponentData.call(this, component, data, properties);
        },

        onBeforeRemove: function (entity, component) {
            this.removeCamera(component);
            component.onRemove();
        },

        onRemove: function (entity, data) {
            data.camera = null;
        },

        onUpdate: function (dt) {
            var components = this.store;
            var component, componentData, cam, vrDisplay;

            if (this.app.vr) {
                for (var id in components) {
                    component = components[id];
                    componentData = component.data;
                    cam = componentData.camera;
                    vrDisplay = cam.vrDisplay;
                    if (componentData.enabled && component.entity.enabled && vrDisplay) {
                        // Change WebVR near/far planes based on the stereo camera
                        vrDisplay.setClipPlanes(cam._nearClip, cam._farClip);

                        // update camera node transform from VrDisplay
                        if (cam._node) {
                            cam._node.localTransform.copy(vrDisplay.combinedViewInv);
                            cam._node._dirtyLocal = false;
                            cam._node._dirtifyWorld();
                        }
                    }
                }
            }
        },

        onPrerender: function () {
            for (var i = 0, len = this.cameras.length; i < len; i++) {
                this.cameras[i].onPrerender();
            }
        },

        addCamera: function (camera) {
            this.cameras.push(camera);
            this.sortCamerasByPriority();
        },

        removeCamera: function (camera) {
            var index = this.cameras.indexOf(camera);
            if (index >= 0) {
                this.cameras.splice(index, 1);
                this.sortCamerasByPriority();
            }
        },

        sortCamerasByPriority: function () {
            this.cameras.sort(function (a, b) {
                return a.priority - b.priority;
            });
        }
    });

    return {
        CameraComponentSystem: CameraComponentSystem
    };
}());