Code Monkey home page Code Monkey logo

ccpwgl's Introduction

CCP WebGL Library

An implementation of CCP Games graphics engine in webgl

Core files

  • dist/ccpwgl_int.js - ccp webgl core library
  • dist/ccpwgl_int.min.js - minified ccp webgl core library
  • dist/ccpwgl.js - An example implementation of the ccp webgl library (required for demos)

Demos

  • demo/index.html - A collection of ccpwgl demonstrations
  • demo/sof.html - Shows how to load ships using Space Object Factory and how to query its data
  • demo/planets.html - Shows how to load planets
  • demo/sun.html - Shows how to load suns (lens flares)
  • demo/tech3.html - Shows how to load Tech III composite ships
  • demo/cubeofdeath.html - Performance test (multiple ships)
  • demo/fitting.html - Shows how to fit turrets on the ship
  • demo/firing.html - Shows how to fire turrets
  • demo/explosions.html - Shows how to construct, play and remove explosions
  • demo/typeids.html - Shows how to query type ID data

Updates

  • This project now uses webpack instead of grunt
  • The glMatrix library is now included in the library to simplify dependencies

Installation

  1. Install Node.js along with the node package manager
  2. Clone git clone https://github.com/ccpgames/ccpwgl.git
  3. Run npm install once from your ccpwgl folder

Build

  • Run webpack to lint, format and build dist/ccpwgl_int.js and dist/ccpwgl_int.min.js
  • Run npm run watcher to automatically rebuild dist/ccpwgl_int.js and dist/ccpwgl_int.min.js while developing

ccpwgl's People

Contributors

antihax avatar ccpbracari avatar cppctamber avatar davidrautert avatar dertseha avatar filipppavlov avatar hakshak avatar regner avatar zemanel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ccpwgl's Issues

Tw2MayaAnimationEngine missing prototype `_EvaluateInfinities` and prototype `_EvaluateBezier` errors

  • The prototype _EvaluateInfinities is missing from Tw2MayaAnimationEngine
  • The _EvaluateBezier prototype seems to have multiple errors which are outlined below:
/**
 * _EvaluateBezier
 * TODO: This function possibly has multiple errors
 * @param segment
 * @param time
 * @param nextKeyTime
 * @returns {?}
 * @private
 */
Tw2MayaAnimationEngine.prototype._EvaluateBezier = function (segment, time, nextKeyTime)
{
    var t, s;

    s = (time - segment[Tw2MayaAnimationEngine.BezierSegment.TIME]) / (nextSegmentTime - segment[Tw2MayaAnimationEngine.BezierSegment.TIME]);

    if (segment[Tw2MayaAnimationEngine.BezierSegment.IS_LINEAR]) {
        t = s;
    }
    else {
        var poly3 = segment[Tw2MayaAnimationEngine.BezierSegment.COEFF][3];
        var poly2 = segment[Tw2MayaAnimationEngine.BezierSegment.COEFF][2];
        var poly1 = segment[Tw2MayaAnimationEngine.BezierSegment.COEFF][1];
        var poly0 = segment[Tw2MayaAnimationEngine.BezierSegment.COEFF][0] - s;
        var roots = [];
        if (polyZeroes (poly, 3, 0.0, 1, 1.0, 1, roots) == 1) {
            t = roots[0];
        }
        else {
            t = 0.0;
        }
    }
    var poly = segment[Tw2MayaAnimationEngine.BezierSegment.POLYY];
    return (t * (t * (t * poly[3] + poly[2]) + poly[1]) + poly[0]);
};
  • nextSegmentTime (never defined) looks like it should actually be called nextKeyTime (this @param never used)
  • poly0, poly1, poly2 and poly3 look like they should be array elements of var poly =[] and thus var poly = quat4.create() should be defined at the top of the prototype

I have not made any changes as I am unsure what is supposed to happen on the 3rd to last line:

    var poly = segment[Tw2MayaAnimationEngine.BezierSegment.POLYY];

Designer skins support + graphicMaterialSets.yaml

There is no way to load designer skins such as Raata Sunset or Blue Tiger currently, because they cannot be described by ship DNA.

Right now to load the default skin I can use data from graphicIDs.yaml for a specific typeID:

sofHullName, sofRaceName, sofFactionName

Available shaders for a specific sofRaceName can be found by SELECT DISTINCT sofFactionName FROM graphicIDs WHERE sofRaceName = [ships sofRaceName]

Available in-game SKINs for specific typeID can be found via skins.yaml, skinMaterials.yaml and graphicMaterialSets.yaml (the last one is not part of the SDE yet, it contains sofFactionName).

Once all three variables are found, ship can be loaded in CCPWebGL:

ship = scene.loadShip(sofHullName+':'+sofFactionName+':'+sofRaceName, undefined);

PS. Including graphicMaterialSets.yaml in SDE would be nice too :-)

Assignment as condition: Tw2AnimationController.FindModelForMesh() does not search properly

In core/Tw2AnimationController.js:
652,62: Expected a conditional expression and instead saw an assignment.

    ...
    var mesh = geometryResource.meshes[meshIndex];
    for (var i = 0; i < this.models.length; ++i)
    {
        for (var j = 0; j < this.models[i].modelRes.meshBindings.length; ++i)
        {
            if (this.models[i].modelRes.meshBindings[j].mesh = mesh)
            {
                return this.models[i];
            }
        }
    }

The IF has an assignment as condition. Set up this way, always the first model is returned (assuming mesh is an object). I don't think this is the intent.
Found using JSHint.

Decal Planes on Space Object Animation

When changing the animation state of a space object (eg. Blackbird/ cc4_t1), decal plane geometry on the animated parts of the ship stays static during the animation.

example_decal_unanimated

Respect JavaScript function scope

Working with JSHint (#2), I came across a lot of notices about already defined variables, such as

116,20: 'j' is already defined.

This comes from the overall use of "var" statements deep within functions, often reusing names (typically from loop counters).

JavaScript has only function scope, no block scope, so trying to redefine a variable does not work as intended. I did not check all occurrences, but so far there doesn't seem to be a bug present, but continuing this practice can lead to oversights.

I tried my hand to clean this up as part of #2, but it became too much in one go - especially with no tests (or example files to use the library) present. But before I invest time in this, my question is: Which strategy you prefer:

  • Clean this up on your own
  • Accept pull requests with the corresponding modifications
  • Ignore this problem and notices
    ?

res:/graphics/starmap/starmap.wbg & StarMap.js

The StarMap.js demo no longer works.
The starmap geometry file is reporting the following error:
Tw2StaticEmitter: input geometry res ' res:/graphics/starmap/starmap.wbg ' mesh lacks ( 0 , 0 ) element required by particle system

Example of Existing Documentation

Hi Fillipp,

Here is a full example of what documentation I have at the moment.

  • There are TODOs
  • If I don't yet have a description for a constructor/ method I use it's name
  • type and param/ property descriptions are only supplied where known
  • Custom types are used where multiple Tw2 types are valid
  • @params and @Property are always defined
  • @link used where relevant in descriptions (Helps when creating the jsdoc html)
  • Where a @param or @Property expects an Array or Object I try to also define their contents

Have a look and let me know if you want anything done differently before I start pushing them.

/**
 * Tw2Track
 * @property {Tw2GeometryTransformTrack} trackRes
 * @property {Tw2Bone} bone
 * @constructor
 */
function Tw2Track()
{
    this.trackRes = null;
    this.bone = null;
}

/**
 * Tw2TrackGroup
 * @property {Tw2GeometryTrackGroup} trackGroupRes
 * @property {Tw2GeometryModel} model
 * @property {Array} transformTracks - Array of {@Link Tw2GeometryTransformTrack}
 * @constructor
 */
function Tw2TrackGroup()
{
    this.trackGroupRes = null;
    this.model = null;
    this.transformTracks = [];
}

/**
 * Tw2Animation
 * @property {Tw2GeometryAnimation} animationRes
 * @property {number} time
 * @property {number} timeScale
 * @property {boolean} cycle
 * @property {boolean} isPlaying
 * @property {Function} callback - Stores optional callback passed to prototypes
 * @property {Array} trackGroups - Array of {@link Tw2TrackGroup}
 * @constructor
 */
function Tw2Animation()
{
    this.animationRes = null;
    this.time = 0;
    this.timeScale = 1.0;
    this.cycle = false;
    this.isPlaying = false;
    this.callback = null;
    this.trackGroups = [];
}

/**
 * Checks to see if the animation has finished playing
 * @return {boolean}
 * @prototype
 */
Tw2Animation.prototype.IsFinished = function ()
{
    return !this.cycle && this.time >= this.duration;
};

/**
 * Tw2Bone
 * TODO: Identify when/ where the property bindingArrays is defined
 * @property {Tw2GeometryBone} boneRes
 * @property {mat4} localTransform
 * @property {mat4} worldTransform
 * @property {mat4} offsetTransform
 * @constructor
 */
function Tw2Bone()
{
    this.boneRes = null;
    this.localTransform = mat4.create();
    this.worldTransform = mat4.create();
    this.offsetTransform = mat4.create();
}

/**
 * Tw2Model
 * @property {Tw2GeometryModel} modelRes
 * @property {Array} bones - Array of {@Link Tw2Bone}
 * @property {Object.<string, Tw2Bone>} bonesByName - An object containing every Tw2Bone name and it's object
 * @constructor
 */
function Tw2Model()
{
    this.modelRes = null;
    this.bones = [];
    this.bonesByName = {};
}


/**
 * Tw2AnimationController
 * @param {Tw2GeometryRes} geometryResource
 * @property {Array} geometryResources - Array of {@link Tw2GeometryRes}
 * @property {Array} models - Array of {@link Tw2Model}
 * @property {Array} animations - Array of {@link Tw2Animation}
 * @property {Array} meshBindings
 * @property {boolean} loaded
 * @property {boolean} update
 * @property {mat4} _tempMat4
 * @property {mat3} _tempMat3
 * @property {quat4} _tempQuat4
 * @property {vec3} _tempVec3
 * @property _geometryResource
 * @prototype
 */
function Tw2AnimationController(geometryResource)
{
    this.geometryResources = [];
    this.models = [];
    this.animations = [];
    this.meshBindings = [];
    this.loaded = false;
    this.update = true;
    this._tempMat4 = mat4.create();
    this._tempMat3 = mat3.create();
    this._tempQuat4 = quat4.create();
    this._tempVec3 = vec3.create();
    this._geometryResource = null;

    if (typeof(geometryResource) != 'undefined')
    {
        this.SetGeometryResource(geometryResource);
    }
}

/**
 * Clears any existing resources and loads the supplied geometry resource
 * @param {Tw2GeometryRes} geometryResource
 * @prototype
 */
Tw2AnimationController.prototype.SetGeometryResource = function (geometryResource)
{
    this.models = [];
    this.animations = [];
    this.meshBindings = [];

    for (var i = 0; i < this.geometryResources.length; ++i)
    {
        this.geometryResources[i].UnregisterNotification(this);
    }

    this.loaded = false;
    this.geometryResources = [];

    if (geometryResource)
    {
        this.geometryResources.push(geometryResource);
        geometryResource.RegisterNotification(this);
    }
};

/**
 * Adds a Geometry Resource
 * @param {Tw2GeometryRes} geometryResource
 * @prototype
 */
Tw2AnimationController.prototype.AddGeometryResource = function (geometryResource)
{
    for (var i = 0; i < this.geometryResources.length; ++i)
    {
        if (this.geometryResources[i] == geometryResource)
        {
            return;
        }
    }
    this.geometryResources.push(geometryResource);
    geometryResource.RegisterNotification(this);
};

/**
 * AddAnimationsFromRes
 * @param resource
 * @prototype
 */
Tw2AnimationController.prototype.AddAnimationsFromRes = function (resource)
{
    for (var i = 0; i < resource.animations.length; ++i)
    {
        var animation = null;
        for (var j = 0; j < this.animations.length; ++j)
        {
            if (this.animations[j].animationRes == resource.animations[i])
            {
                animation = this.animations[i];

                break;
            }
        }
        if (!animation)
        {
            animation = new Tw2Animation();
            animation.animationRes = resource.animations[i];
            this.animations.push(animation);
        }
        for (var j = 0; j < animation.animationRes.trackGroups.length; ++j)
        {
            var found = false;
            for (var k = 0; k < animation.trackGroups.length; ++k)
            {
                if (animation.trackGroups[k].trackGroupRes == animation.animationRes.trackGroups[j])
                {
                    found = true;
                    break;
                }
            }
            if (found)
            {
                continue;
            }
            var model = null;
            for (var k = 0; k < this.models.length; ++k)
            {
                if (this.models[k].modelRes.name == animation.animationRes.trackGroups[j].name)
                {
                    model = this.models[k];
                    break;
                }
            }
            if (model != null)
            {
                var group = new Tw2TrackGroup();
                group.trackGroupRes = animation.animationRes.trackGroups[j];
                for (var k = 0; k < group.trackGroupRes.transformTracks.length; ++k)
                {
                    for (var m = 0; m < model.bones.length; ++m)
                    {
                        if (model.bones[m].boneRes.name == group.trackGroupRes.transformTracks[k].name)
                        {
                            var track = new Tw2Track();
                            track.trackRes = group.trackGroupRes.transformTracks[k];
                            track.bone = model.bones[m];
                            group.transformTracks.push(track);
                            break;
                        }
                    }
                }
                animation.trackGroups.push(group);
            }
        }
    }
};

/**
 * Adds a model resource
 * @param {Tw2GeometryModel} modelRes
 * @returns {null|Tw2Model} Returns a newly created Tw2Model if the model resource doesn't already exist, and null if it does
 * @private
 */
Tw2AnimationController.prototype._AddModel = function (modelRes)
{
    for (var i = 0; i < this.models.length; ++i)
    {
        if (this.models[i].modelRes.name == modelRes.name)
        {
            return null;
        }
    }
    var model = new Tw2Model();
    model.modelRes = modelRes;
    var skeleton = modelRes.skeleton;
    if (skeleton != null)
    {
        for (var j = 0; j < skeleton.bones.length; ++j)
        {
            var bone = new Tw2Bone();
            bone.boneRes = skeleton.bones[j];
            model.bones.push(bone);
            model.bonesByName[bone.boneRes.name] = bone;
        }
    }
    this.models.push(model);
    return model;
};

/**
 * Finds a mesh binding
 * @param {Object} resource
 * @returns {null|Object} Returns the mesh binding of a resource if it exists, null if it doesn't
 * @private
 */
Tw2AnimationController.prototype._FindMeshBindings = function (resource)
{
    for (var i = 0; i < this.meshBindings.length; ++i)
    {
        if (this.meshBindings[i].resource == resource)
        {
            return this.meshBindings[i];
        }
    }
    return null;
};

/**
 * Rebuilds the cached data for a resource (unless it doesn't exist or is already good)
 * @param {Tw2GeometryRes} resource
 * @prototype
 */
Tw2AnimationController.prototype.RebuildCachedData = function (resource)
{
    var found = false;
    for (var i = 0; i < this.geometryResources.length; ++i)
    {
        if (this.geometryResources[i] == resource)
        {
            found = true;
            break;
        }
    }
    if (!found)
    {
        return;
    }
    for (var i = 0; i < this.geometryResources.length; ++i)
    {
        if (!this.geometryResources[i].IsGood())
        {
            return;
        }
    }
    for (var i = 0; i < this.geometryResources.length; ++i)
    {
        this._DoRebuildCachedData(this.geometryResources[i]);
    }
};

/**
 * _DoRebuildCachedData
 * TODO: Sort out commented out code (line 339)
 * TODO: Too many arguments supplied to this.AddAnimationsFromRes prototype (line 352)
 * @param resource
 * @private
 */
Tw2AnimationController.prototype._DoRebuildCachedData = function (resource)
{
    var newModels = [];
    //if (resource.meshes.length)
    {
        for (var i = 0; i < resource.models.length; ++i)
        {
            var model = this._AddModel(resource.models[i]);
            if (model)
            {
                newModels.push(model);
            }
        }
    }
    for (var i = 0; i < this.geometryResources.length; ++i)
    {
        this.AddAnimationsFromRes(this.geometryResources[i], this.models);
    }

    if (resource.models.length == 0)
    {
        for (var i = 0; i < resource.meshes.length; ++i)
        {
            Tw2GeometryRes.BindMeshToModel(resource.meshes[i], this.geometryResources[0].models[0]);
        }
        resource.models.push(this.geometryResources[0].models[0]);
    }
    for (var i = 0; i < resource.models.length; ++i)
    {
        var model = null;
        for (var j = 0; j < this.models.length; ++j)
        {
            if (this.models[j].modelRes.name == resource.models[i].name)
            {
                model = this.models[j];
                break;
            }
        }
        if (model == null)
        {
            continue;
        }
        for (var j = 0; j < resource.models[i].meshBindings.length; ++j)
        {
            var meshIx = resource.meshes.indexOf(resource.models[i].meshBindings[j].mesh);
            var meshBindings = this._FindMeshBindings(resource);
            if (meshBindings == null)
            {
                meshBindings = [];
                meshBindings.resource = resource;
                this.meshBindings.push(meshBindings);
            }
            meshBindings[meshIx] = new glMatrixArrayType(resource.models[i].meshBindings[j].bones.length * 12);
            for (var k = 0; k < resource.models[i].meshBindings[j].bones.length; ++k)
            {
                for (var n = 0; n < model.bones.length; ++n)
                {
                    if (model.bones[n].boneRes.name == resource.models[i].meshBindings[j].bones[k].name)
                    {
                        if (!model.bones[n].bindingArrays)
                        {
                            model.bones[n].bindingArrays = [];
                        }
                        var arrayInfo = { 'array': meshBindings[meshIx], 'offset': k * 12};
                        model.bones[n].bindingArrays[model.bones[n].bindingArrays.length] = arrayInfo;
                        //meshBindings[meshIx][k] = model.bones[n].offsetTransform;
                        break;
                    }
                }
            }
        }
    }
    if (resource.meshes.length && resource.models.length)
    {
        this.ResetBoneTransforms(resource.models);
    }
    this.loaded = true;
    if (this.animations.length)
    {
        if (this.pendingCommands)
        {
            for (var i = 0; i < this.pendingCommands.length; ++i)
            {
                this.pendingCommands[i].func.apply(this, this.pendingCommands[i].args);
            }
        }
        this.pendingCommands = null;
    }
};

/**
 * Plays a specific animation
 * @param {string} name - Animation Name
 * @param {boolean} cycle
 * @param {Function} callback - Optional callback which is fired once the animation has completed
 * @prototype
 */
Tw2AnimationController.prototype.PlayAnimation = function (name, cycle, callback)
{
    if (this.animations.length == 0)
    {
        if (!this.pendingCommands)
        {
            this.pendingCommands = [];
        }
        this.pendingCommands.push({'func': this.PlayAnimation, 'args': [name, cycle, callback]});
        return;
    }
    for (var i = 0; i < this.animations.length; ++i)
    {
        if (this.animations[i].animationRes.name == name)
        {
            this.animations[i].time = 0;
            this.animations[i].isPlaying = true;
            if (typeof(cycle) != 'undefined')
            {
                this.animations[i].cycle = cycle;
            }
            if (typeof(callback) != 'undefined')
            {
                this.animations[i].callback = callback;
            }
        }
    }
};

/**
 * Stops a specific animation from playing
 * @param {string} name - Animation Name
 * @prototype
 */
Tw2AnimationController.prototype.StopAnimation = function (name)
{
    for (var i = 0; i < this.animations.length; ++i)
    {
        if (this.animations[i].animationRes.name == name)
        {
            this.animations[i].isPlaying = false;
        }
    }
};

/**
 * Stops all animations from playing
 * @prototype
 */
Tw2AnimationController.prototype.StopAllAnimations = function ()
{
    for (var i = 0; i < this.animations.length; ++i)
    {
        this.animations[i].isPlaying = false;
    }
};

/**
 * Resets the bone transforms for the supplied models
 * @param {Array} models - Array of {@Link @Tw2Model}
 * @prototype
 */
Tw2AnimationController.prototype.ResetBoneTransforms = function (models)
{
    for (var i = 0; i < this.models.length; ++i)
    {
        for (var j = 0; j < this.models[i].bones.length; ++j)
        {
            var bone = this.models[i].bones[j];
            var boneRes = bone.boneRes;
            mat4.set(boneRes.localTransform, bone.localTransform);
            if (boneRes.parentIndex != -1)
            {
                mat4.multiply(bone.localTransform, this.models[i].bones[bone.boneRes.parentIndex].worldTransform, bone.worldTransform);
            }
            else
            {
                mat4.set(bone.localTransform, bone.worldTransform);
            }
            mat4.identity(bone.offsetTransform);
        }
    }
    var id = mat4.identity(mat4.create());
    for (var i = 0; i < this.meshBindings.length; ++i)
    {
        for (var j = 0; j < this.meshBindings[i].length; ++j )
        {
            for (var k = 0; k * 16 < this.meshBindings[i][j].length; ++k)
            {
                for (var m = 0; m < 16; ++m)
                {
                    this.meshBindings[i][j][k * 16 + m] = id[m];
                }
            }
        }
    }
};

/**
 * EvaluateCurve
 * @param {Tw2GeometryCurve} curve
 * @param {number} time
 * @param value
 * @param {boolean} cycle
 * @param {number} duration
 */
Tw2AnimationController.EvaluateCurve = function (curve, time, value, cycle, duration)
{
    var count = curve.knots.length;
    var knot = count - 1;
    var t = 0;
    for (var i = 0; i < curve.knots.length; ++i)
    {
        if (curve.knots[i] > time)
        {
            knot = i;
            break;
        }
    }

    if (curve.degree == 0)
    {
        for (var i = 0; i < curve.dimension; ++i)
        {
            value[i] = curve.controls[knot * curve.dimension + i];
        }
    }
    else if (curve.degree == 1)
    {
        var knot0 = cycle ? (knot + count - 1) % count : knot == 0 ? 0 : knot - 1;
        var dt = curve.knots[knot] - curve.knots[knot0];
        if (dt < 0)
        {
            dt += duration;
        }
        if (dt > 0)
        {
            t = (time - curve.knots[i - 1]) / dt;
        }
        for (var i = 0; i < curve.dimension; ++i)
        {
            value[i] = curve.controls[knot0 * curve.dimension + i] * (1 - t) + curve.controls[knot * curve.dimension + i] * t;
        }
    }
    else
    {
        var k_2 = cycle ? (knot + count - 2) % count : knot == 0 ? 0 : knot - 2;
        var k_1 = cycle ? (knot + count - 1) % count : knot == 0 ? 0 : knot - 1;

        var p1 = (k_2) * curve.dimension;
        var p2 = (k_1) * curve.dimension;
        var p3 = knot * curve.dimension;

        var ti_2 = curve.knots[k_2];
        var ti_1 = curve.knots[k_1];
        var ti = curve.knots[knot];
        var ti1 = curve.knots[(knot + 1) % count];
        if (ti_2 > ti)
        {
            ti += duration;
            ti1 += duration;
            time += duration;
        }
        if (ti_1 > ti)
        {
            ti += duration;
            ti1 += duration;
            time += duration;
        }
        if (ti1 < ti)
        {
            ti1 += duration;
        }

        var tmti_1 = (time - ti_1);
        var tmti_2 = (time - ti_2);
        var dL0 = ti - ti_1;
        var dL1_1 = ti - ti_2;
        var dL1_2 = ti1 - ti_1;

        var L0 = tmti_1 / dL0;
        var L1_1 = tmti_2 / dL1_1;
        var L1_2 = tmti_1 / dL1_2;

        var ci_2 = (L1_1 + L0) - L0 * L1_1;
        var ci = L0 * L1_2;
        var ci_1 = ci_2 - ci;
        ci_2 = 1 - ci_2;

        for (var i = 0; i < curve.dimension; ++i)
        {
            value[i] = ci_2 * curve.controls[p1 + i] + ci_1 * curve.controls[p2 + i] + ci * curve.controls[p3 + i];
        }
    }
};

/**
 * Internal render/update function which is called every frame.
 * TODO: Sort out Commented out code (line 718)
 * @param {number} dt - Delta Time
 * @prototype
 */
Tw2AnimationController.prototype.Update = function (dt)
{
    if (this.models == null || !this.update)
    {
        return;
    }

    for (var i = 0; i < this.geometryResources.length; ++i)
    {
        this.geometryResources[i].KeepAlive();
    }


    var tempMat = this._tempMat4;
    var updateBones = false;
    for (var i = 0; i < this.animations.length; ++i)
    {
        var animation = this.animations[i];
        if (animation.isPlaying)
        {
            var res = animation.animationRes;
            animation.time += dt * animation.timeScale;
            if (animation.time > res.duration)
            {
                if (animation.callback != null)
                {
                    animation.callback(this, animation);
                }
                if (animation.cycle)
                {
                    animation.time = animation.time % res.duration;
                }
                else
                {
                    animation.isPlaying = false;
                    animation.time = res.duration;
                }
            }
            var orientation = this._tempQuat4;
            var scale = this._tempMat3;
            var position = this._tempVec3;
            for (var j = 0; j < animation.trackGroups.length; ++j)
            {
                for (var k = 0; k < animation.trackGroups[j].transformTracks.length; ++k)
                {
                    var track = animation.trackGroups[j].transformTracks[k];
                    if (track.trackRes.position)
                    {
                        Tw2AnimationController.EvaluateCurve(track.trackRes.position, animation.time, position, animation.cycle, res.duration);
                    }
                    else
                    {
                        position[0] = position[1] = position[2] = 0;
                    }
                    if (track.trackRes.orientation)
                    {
                        Tw2AnimationController.EvaluateCurve(track.trackRes.orientation, animation.time, orientation, animation.cycle, res.duration);
                        quat4.normalize(orientation);
                    }
                    else
                    {
                        orientation[0] = orientation[1] = orientation[2] = 0;
                        orientation[3] = 1;
                    }
                    if (track.trackRes.scaleShear)
                    {
                        Tw2AnimationController.EvaluateCurve(track.trackRes.scaleShear, animation.time, scale, animation.cycle, res.duration);
                    }
                    else
                    {
                        mat3.identity(scale);
                    }

                    mat3.toMat4(scale, track.bone.localTransform);
                    mat4.multiply(track.bone.localTransform, mat4.transpose(quat4.toMat4(orientation, tempMat)));
                    track.bone.localTransform[12] = position[0];
                    track.bone.localTransform[13] = position[1];
                    track.bone.localTransform[14] = position[2];
                    updateBones = true;
                }
            }
        }
    }
    //if (updateBones)
    {
        for (var i = 0; i < this.models.length; ++i)
        {
            for (var j = 0; j < this.models[i].bones.length; ++j)
            {
                var bone = this.models[i].bones[j];
                if (bone.boneRes.parentIndex != -1)
                {
                    mat4.multiply(this.models[i].bones[bone.boneRes.parentIndex].worldTransform, bone.localTransform, bone.worldTransform);
                }
                else
                {
                    mat4.set(bone.localTransform, bone.worldTransform);
                }
                mat4.multiply(bone.worldTransform, bone.boneRes.worldTransformInv, bone.offsetTransform);
                if (bone.bindingArrays)
                {
                    for (var a = 0; a < bone.bindingArrays.length; ++a)
                    {
                        bone.bindingArrays[a].array[bone.bindingArrays[a].offset + 0] = bone.offsetTransform[0];
                        bone.bindingArrays[a].array[bone.bindingArrays[a].offset + 1] = bone.offsetTransform[4];
                        bone.bindingArrays[a].array[bone.bindingArrays[a].offset + 2] = bone.offsetTransform[8];
                        bone.bindingArrays[a].array[bone.bindingArrays[a].offset + 3] = bone.offsetTransform[12];

                        bone.bindingArrays[a].array[bone.bindingArrays[a].offset + 4] = bone.offsetTransform[1];
                        bone.bindingArrays[a].array[bone.bindingArrays[a].offset + 5] = bone.offsetTransform[5];
                        bone.bindingArrays[a].array[bone.bindingArrays[a].offset + 6] = bone.offsetTransform[9];
                        bone.bindingArrays[a].array[bone.bindingArrays[a].offset + 7] = bone.offsetTransform[13];

                        bone.bindingArrays[a].array[bone.bindingArrays[a].offset + 8] = bone.offsetTransform[2];
                        bone.bindingArrays[a].array[bone.bindingArrays[a].offset + 9] = bone.offsetTransform[6];
                        bone.bindingArrays[a].array[bone.bindingArrays[a].offset + 10] = bone.offsetTransform[10];
                        bone.bindingArrays[a].array[bone.bindingArrays[a].offset + 11] = bone.offsetTransform[14];
                    }
                }
            }
        }
    }
};

/**
 * RenderDebugInfo
 * TODO: Sort out commented out code (lines 767 - 770)
 * @param {function} debugHelper
 * @prototype
 */
Tw2AnimationController.prototype.RenderDebugInfo = function (debugHelper)
{
    /*for (var i = 0; i < this.geometryResources.length; ++i)
    {
        this.geometryResources[i].RenderDebugInfo(debugHelper);
    }*/
    for (var i = 0; i < this.models.length; ++i)
    {
        for (var j = 0; j < this.models[i].bones.length; ++j)
        {
            var b0 = this.models[i].bones[j];
            if (b0.boneRes.parentIndex >= 0)
            {
                var b1 = this.models[i].bones[b0.boneRes.parentIndex];
                debugHelper.AddLine([b0.worldTransform[12], b0.worldTransform[13], b0.worldTransform[14]], [b1.worldTransform[12], b1.worldTransform[13], b1.worldTransform[14]]);
            }
        }
    }
};

/**
 * GetBoneMatrixes
 * TODO: Matrixes is spelt wrong (should be Matrices), multiple refactors required
 * @param {number} meshIndex
 * @param {undefined|Tw2GeometryRes} geometryResource
 * @returns {Float32Array}
 * @prototype
 */
Tw2AnimationController.prototype.GetBoneMatrixes = function (meshIndex, geometryResource)
{
    if (this.geometryResources.length == 0)
    {
        return new Float32Array();
    }

    if (typeof (geometryResource) == 'undefined')
    {
        geometryResource = this.geometryResources[0];
    }
    var meshBindings = this._FindMeshBindings(geometryResource);
    if (meshBindings && meshIndex < meshBindings.length)
    {
        return meshBindings[meshIndex];
    }
    return new Float32Array();
};

/**
 * FindModelForMesh
 * @param {number} meshIndex
 * @param {undefined|Tw2GeometryRes} geometryResource
 * @returns {Tw2Model|null} Returns the Tw2Model for the mesh if found and is good, else returns null
 * @prototype
 */
Tw2AnimationController.prototype.FindModelForMesh = function (meshIndex, geometryResource)
{
    if (this.geometryResources.length == 0)
    {
        return null;
    }
    if (typeof (geometryResource) == 'undefined')
    {
        geometryResource = this.geometryResources[0];
    }
    if (!geometryResource.IsGood())
    {
        return null;
    }
    var mesh = geometryResource.meshes[meshIndex];
    for (var i = 0; i < this.models.length; ++i)
    {
        for (var j = 0; j < this.models[i].modelRes.meshBindings.length; ++i)
        {
            if (this.models[i].modelRes.meshBindings[j].mesh == mesh)
            {
                return this.models[i];
            }
        }
    }
    return null;
};

Updating an object's transform doesn't update it's EveSpriteSets

Updating an object's transform doesn't update it's EveSpriteSets.

  • Tested on Windows 10 64BIT
  • Chrome 50.0.2661.102 m and Firefox 46.0.1
  • Version: currentccpwgl_int.js git file

Example:

var ship = scene.loadShip('ab3_t1:amarrbase:amarr');
var tr = ship.getTransform();
mat4.translate(tr, [1000,1000,1000]);
ship.setTransform(tr);
// Ship is translated
// It's sprite sets are not

Out of curiosity (and because I can't find it myself) where/ how do EveSpriteSets world matrices get updated?

GroupIndex

Hi Filipp,

Any issues with adding this.groupIndex = -1 to the EveSpriteSetItem and EvePlaneSetItem constructors and having them populated during object SOF construction? Having this property available makes it much easier to create SOF data for custom ships and objects.

Cheers.

Resources are loaded using HTTP

The new version of CCP WebGL is using HTTP protocol to retrieve resources: SOF data, ship models, shaders and textures. If the website using CCP WebGL was loaded using HTTPS, it will cause browser security errors in Chrome (resulting in blank screen), and security warnings in Firefox.

JS should either detect if the website was lodaded using HTTPS or HTTP (like google analytics do) and use the correct protocol when calling for resources, OR it could be hard-coded to use HTTPS, whichever is easier to implement.

Planet.isLoaded() always returns undefined

The method Planet.isLoaded() always returns undefined.
It refers to "this.hightDirty", which is an undefined member. But the wrapped object, ccpwgl_int.EvePlanet, contains such a member.

Yet I don't think this is the proper way to check whether the planet is loaded - this member of EvePlanet is reset to false in GetBatches() at the render stage.

Since there is no onLoad parameter for scene.loadPlanet(), for now we have to assume a planet is immediately in "loaded" state after construction.

fragment shader does not compile on IE or Edge

The latest version does not work for Internet Explorer or Edge:

Tw2EffectRes:  error compiling  fragment  shader (effect ' res:/graphics/effect.gles2/managed/space/spaceobject/v5/quad/quadv5.sm_hi '):  Shader compilation errors
(17, 6): syntax error, unexpected IDENT_TOK

Tw2EffectRes:  error compiling  fragment  shader (effect ' res:/graphics/effect.gles2/managed/space/spaceobject/v5/quad/quadheatv5.sm_hi '):  Shader compilation errors
(17, 6): syntax error, unexpected IDENT_TOK

Tw2EffectRes:  error compiling  fragment  shader (effect ' res:/graphics/effect.gles2/managed/space/decals/v3/decalnormalv3.sm_hi '):  Shader compilation errors
(17, 6): syntax error, unexpected IDENT_TOK

this one did work partially: 205ce26

Tw2TransformTrack - Undefined Variable `orientation`

  • There is an undefined variable orientation in Tw2TransformTrack's prototype UpdateValue which is probably supposed to be this.rotation
  • there are two if statements for (!this.positionCurve) directly after one another, not sure if this is a mistake and there should just be one or if one of these variables is incorrect
/**
 * Updates a value at a specific time
 * TODO: Undefined variable orientation
 * @param {number} time
 * @prototype
 */
Tw2TransformTrack.prototype.UpdateValue = function (time)
{
    if (!this.res || !this.res.IsGood())
    {
        return;
    }
    if (!this.positionCurve)
    {
        this.FindTracks();
    }
    if (!this.positionCurve) //<----------------- Duplicate if statement conditions
    {
        return;
    }

    if (this.cycle)
    {
        time = time % this.duration;
    }
    if (time > this.duration || time < 0)
    {
        return;
    }

    this.EvaluateCurve(this.positionCurve, time, this.translation, this.cycle, this.duration);
    this.EvaluateCurve(this.orientationCurve, time, this.rotation, this.cycle, this.duration);
    quat4.normalize(orientation); //<------------------------ Undefined variable
    this.EvaluateCurve(this.scaleCurve, time, this._scaleShear, this.cycle, this.duration);
    this.scale[0] = vec3.length(this.scaleCurve);
    this.scale[1] = vec3.length(this.scaleCurve.subarray(3, 6));
    this.scale[2] = vec3.length(this.scaleCurve.subarray(6, 9));
};

Communication

Hi Fillip, is there a forum or communications channel we can all discuss development of ccpwgl outside of GitHub? - Cheers

Tween.js missing and other issues

Hi there, the tween.js linked in the example files is missing, also when are you guys going to give us access to the other files needed to run things, specifically the .red .cube and .fx files linked in the examples, its all well and good running these things from the CCP CDN but it breaks functionality with chrome if you're using SSL so currently chrome users only get half a page when looking at the starmap i have up and running, any information about this would be appreciated, and if i've missed a download link for the files i've mentioned then please feel free to point me towards it so i can run this locally rather than from the CDN

Effects don't animate anymore (since AT XI update?)

Effects, such as the positioning lights, booster flames or the shield at the front of the Avatar don't animate anymore.

This is the case since about the AT XI update. One on IRC mentioned something in the updated .fx files is broken. (?)

`distortionsEffects` array for EveMeshOverlayEffect

Hi Fillipp,

I've created my own version of what I think would be in the missing EveMeshOverlayEffect constructor. For basic effects like shield and armour harders it works fine, however I've come across two hurdles with the more complex cloaking effects:

  • distortionEffects
  • How to trigger and manage the transitions between the EveMeshOverlayEffect and it's parent's effects (ie. cloak on, cloak off)

I've rerouted the distortion effects from the cloaking red files to an opaqueArea to see what the shaders output, and it looks like normal data. Do distortion effects get rendered to a texture, and then reapplied as a normalMap perhaps?

image

Any help would be appreciated thanks!

Documentation

Hi Filipp,

Do you have a plan for code documentation?

I have basic jsdocs (@params, @properties, function type, custom types) for all the /src files but I'm not sure what state you'd accept them in or even if you want them at all. Completing them all will take some time but I have enough presently to build basic jsdocs html files using a grunt task.

Perhaps you could join us on the tweetfleet #ccpwgl channel to discuss or we could do so on the forums somewhere?

Example:

/**
 * @typedef {(Tw2RenderBatch|Tw2ForwardingRenderBatch|Tw2GeometryBatch|Tw2GeometryLineBatch|Tw2InstancedMeshBatch|EvePlaneSetBatch)} RenderBatch
 * @typedef {(Tw2FloatParameter|Tw2Vector2Parameter|Tw2Vector3Parameter|Tw2Vector4Parameter|Tw2TextureParameter)} Tw2Parameter
 * @typedef {(Tw2Mesh|Tw2InstancedMesh)} Mesh
 * @typedef {(EveCurveLineSet|EveSpaceObjectDecal|EveTurretSet)} GeometryProvider
 * @typedef {(device.RM_ANY|device.RM_OPAQUE|device.RM_DECAL|device.RM_TRANSPARENT|device.RM_ADDITIVE|device.RM_DEPTH|device.RM_FULLSCREEN)} RenderMode
 * @typedef {(Tw2ParticleElementDeclaration.LIFETIME|Tw2ParticleElementDeclaration.POSITION|Tw2ParticleElementDeclaration.VELOCITY|Tw2ParticleElementDeclaration.MASS|Tw2ParticleElementDeclaration.CUSTOM)} ParticleElementType
 */

/**
 * Accumulates render batches
 * @param {function} sorting - A function for sorting render batches
 *
 * @property {Array} batches - An array of render batches
 * @property {number} count - How many batch array elements will be processed
 * @property {function} _sortMethod - the stored sorting function
 * @throws If the '_sortMethod' @property or 'sorting' @param is 'null' in Firefox and Edge (but not Chrome)
 * @constructor
 */
function Tw2BatchAccumulator(sorting)
{
    this.batches = [];
    this.count = 0;
    this._sortMethod = sorting;
}

/**
 *
 * @param {RenderBatch} batch
 * @prototype
 */
Tw2BatchAccumulator.prototype.Commit = function(batch)
{
    this.batches[this.count++] = batch;
};

Overlay effects not applied to animations

When aplying overlayEffect they are ony applied to one animation state (not necessarily the right one) when playing an animation, the overlayeffect does not change

Wrapped Objects Query

Do you know if there is a plan to have any future space objects that use multiple wrapped objects apart from Tech 3 cruisers? I'm considering creating a class just for Tech3 cruisers in my version of ccpwgl and removing the wrapped object arrays. This will simply things some what and I wanted to make sure I wasn't shooting myself in the foot :3

EveCurveLineSet

Solved, was a user error :)

// Create Curve Line Set
var curveSet = new ccpwgl_int.EveCurveLineSet();

// Set shader textures
curveSet.lineEffect.parameters['TexMap'] = new ccpwgl_int.Tw2TextureParameter('TexMap', 'res:/texture/global/white.dds.0.png');
curveSet.lineEffect.parameters['OverlayTexMap'] = new ccpwgl_int.Tw2TextureParameter('OverlayTexMap', 'res:/texture/global/white.dds.0.png');
curveSet.lineEffect.Initialize();

// Create batch accumulator
var batches = new ccpwgl_int.Tw2BatchAccumulator();

// Collect batches to render during the post scene render loop
ccpwgl.onPostSceneRender = function (dt) {
    // Collect batches
    curveSet.GetBatches(ccpwgl_int.device.RM_TRANSPARENT, batches);

    // Render batches
    batches.Render();

    // Clear batches 
    batches.Clear();
};

// Create some lines
curveSet.AddCurvedLineCrt([1000, 1000, 1000], [1, 1, 1, 1], [-5000, -1050, 300], [1, 1, 1, 1], [1200, -4100, 2100], 10);
curveSet.AddStraightLine([-5000, -1050, 300], [0, 1, 0, 1], [1000, 1000, 1000], [0, 1, 0, 1], 10);

// Submit changes (must be done after any new lines are added"
curveSet.SubmitChanges();

EveCurveLineSet Lines not showing correctly when a spaceObject is removed from scene

  • Lines show a black outline
  • Line segment connection points are missing
  • When resource purging occurs the line sprite texture is unloaded from memory resulting in completely black lines

-- With Connection Points (And Ship)
image

-- Without Connection Points (Ship Removed)
image

-- After Resource Clear
image

Default killmark textures

Killmarks don't seem to work with CCP's killmark textures. As far as I can tell, the issue is that when the render state is set up, TEXTURE_WRAP_S/T are only set to REPEAT if there are mipmaps present, which is only the case if the texture is a power of 2 in each direction. CCP's killmark textures are 64x192, so no mipmaps, and the WRAP mode is set to CLAMP. This kills the killmarks.

Provide build system for combined & minified files, module structure

The repository currently only contains the dedicated source files and a minified result. Please provide a description and/or commands to create the combined files as well as the minified version for testing and to be able to reproduce it.

As a side request, it would be great if the whole thing was set up to be used with require.js / browserify in an AMD like structure.
Would you accept a pull request with the whole thing reworked using requirejs?

Planetary Bodies - Issues with Depth

Spaceobjects get clipped/ intersected by planetary bodies when the camera distance gets to a certain point (I am unsure what amount) even though they are positioned in a scene where they shouldn't.

Planets always render behind objects

Planets always render behind objects even when they are in front of them.
While this looks intentional, is this something that could be changed?

planet_example

RenderMode device.RM_DEPTH and DepthAreas

Is there a reason why the RM_DEPTH rendermode is never accumulated, and DepthAreas rendered?
Wondering if it's something missing or it's for something that isn't webgl compatible.

http: access resulting in CORS errors

Presently unable to access CDN files using http as it results in Cross Origin Resource errors.

XMLHttpRequest cannot load http://developers.eveonline.com/ccpwgl/assetpath/1005132/dx9/scene/universe/a12_cube.red. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://www.caldariprimeponyclub.com' is therefore not allowed access.

https works fine.

Tw2RotationCurve - Undefined variable `q0`

  • There is an undefined value q0 in Tw2RotationCurve's prototype GetValueAt See 12th line from the end.
/**
 * Gets a value at a specific time
 * TODO: Identify what the undefined value `q0` should be
 * @param {number} time
 * @param {quat4} value
 * @returns {quat4}
 * @prototype
 */
Tw2RotationCurve.prototype.GetValueAt = function (time, value)
{
    if (this.length == 0)
    {
        quat4.set(this.value, value);
        return value;
    }

    var firstKey = this.keys[0];
    var lastKey = this.keys[this.keys.length - 1];
    if (time >= lastKey.time)
    {
        if (this.extrapolation == 0)
        {
            quat4.set(this.value, value);
            return value;
        }
        else if (this.extrapolation == 1)
        {
            quat4.set(lastKey.value, value);
            return value;
        }
        else
        {
            time = time % lastKey.time;
        }
    }
    else if (time < 0 || time < firstKey.time)
    {
        if (this.extrapolation == 0)
        {
            quat4.set(this.value, value);
            return value;
        }
        else
        {
            quat4.set(firstKey.value, value);
            return value;
        }
    }
    var ck = this.keys[this._currKey];
    var ck_1 = this.keys[this._currKey - 1];
    while ((time >= ck.time) || (time < ck_1.time))
    {
        if (time < ck_1.time)
        {
            this._currKey = 0;
        }
        this._currKey++;
        ck = this.keys[this._currKey];
        ck_1 = this.keys[this._currKey - 1];
    }

    var nt = (time - ck_1.time) / (ck.time - ck_1.time);
    if (ck_1.interpolation == 1)
    {
        quat4.set(ck_1.value, value);
    }
    else if (ck_1.interpolation == 2)
    {
        value[0] = ck_1.value[0] * (1 - nt) + ck.value[0] * nt;
        value[1] = ck_1.value[1] * (1 - nt) + ck.value[1] * nt;
        value[2] = ck_1.value[2] * (1 - nt) + ck.value[2] * nt;
        value[3] = ck_1.value[3] * (1 - nt) + ck.value[3] * nt;
    }
    else if (ck_1.interpolation == 3)
    {
        var collect = quat4.create();
        collect[3] = 1;
        var arr = [ck_1.value, ck_1.right, ck.left, ck.value];
        for (var i = 3; i > 0; i--)
        {
            var power = Tw2RotationCurve.BICumulative(i, nt);
            if (power > 1)
            {
                quat4.multiply(collect, arr[i], value);
            }
            value[0] = -arr[i - 1][0];
            value[1] = -arr[i - 1][1];
            value[2] = -arr[i - 1][2];
            value[3] =  arr[i - 1][3];
            quat4.multiply(value, arr[i], value);
            Tw2RotationCurve.QuaternionPow(value, value, power);
            quat4.multiply(collect, value, collect);
        }
        return quat4.multiply(collect, q0, value); // <------------------------ undefined variable q0
    }
    else if (ck_1.interpolation == 5)
    {
        return quat4.slerp(ck_1.value, ck.value, nt, value);
    }
    else
    {
        return quat4.slerp(quat4.slerp(ck_1.value, ck.value, nt, quat4.create()), quat4.slerp(ck_1.right, ck.left, nt, quat4.create()), 2.0 * time * (1.0 - time), value);
    }
    return value;
};

Tw2QuaternionKey

The Tw2QuaternionKey constructor is defined twice, one of which looks like the older style of tr2 keys.

Tw2RotationCurve.js

/**
 * Tw2QuaternionKey
 * @property {number} time
 * @property {quat4} value
 * @property {quat4} left
 * @property {quat4} right
 * @property {number} interpolation
 * @constructor
 */
function Tw2QuaternionKey()
{
    this.time = 0;
    this.value = quat4.create();
    this.left = quat4.create();
    this.right = quat4.create();
    this.interpolation = 5;
}

Tw2QuaternoinCurve.js

/**
 * Tw2QuaternionKey
 * @property {number} time
 * @property {quat4} value
 * @property {quat4} leftTangent
 * @property {quat4} rightTangent
 * @property {number} interpolation
 * @constructor
 */
function Tw2QuaternionKey()
{
    this.time = 0;
    this.value = quat4.create();
    this.leftTangent = quat4.create();
    this.rightTangent = quat4.create();
    this.interpolation = 1;
}

Tw2FloatParameter.OnValueChanged() uses undefined variable

In core/Tw2FloatParameter.js
43,9: 'constantBuffer' is not defined.

Tw2FloatParameter.prototype.OnValueChanged = function ()
{
    if (this.constantBuffer != null)
    {
        constantBuffer[this.offset] = this.value;
    }
};

constantBuffer is undefined. This should probably refer to the object member.
Found using JSHint.

Starmap has duplicated stars

Some stars appear in two different places. Such as Otosela

I've pulled the mapping between starid and solarsystemid out of the code, and I'm seeing the same solarsystemid appearing against multiple starids, with different coordinates.

This makes the starmap less than useful.

Request for clarification: Intended closure in EveTurretSet.Render()?

In EveTurretSet.Render(), some anonymous functions are created in a loop, which refer to the index variable through closure.

I came across this code since JSHint notified that the function should be created outside the loop. But before I did that as part of my initial cleanup (#2), I wondered about the unclear reference to index. It is after all modified later on, but I don't understand the whole design to know whether the resulting behaviour is intended or not.

Please clarify and/or extract the function from the loop.

Console Logs vs Events Emits

In my version of ccpwgl_int I have an event emitter that replaces the console logging currently being used and I thought perhaps this could be something that could be added to the standard one, or perhaps we could remove all of the non error console logs?

While I still have the ability to output the console logs exactly as they are now, they can be disabled (this increases performance significantly in firefox when the dev console is open ), more data can be supplied for a particular event, and they can be passed directly to my own event handlers.

Anyone have any objections or thoughts on this topic?

Example:

// Global `ccpwgl_int` emitter
var emitter = new Tw2EventEmitter();

/**
 * Replicates existing console.log functionality
 * @param {{}} eventObj - An event object
 * @param {String} eventObj.msg - An event message
 */
function logListener(eventObj)
{
    if (resMan.log)  console.log('CCPWGL: ' + eventObj.msg);
};

// Add the resEventListener
emitter.on('Log', logListener);

/**
  * Example emit
  */
var SomeConstructor()
{
    ...
    emitter.emit('Log',{
        msg: `Tw2LoadingObject: Preparing resource '${this.path}'`,
        type: 'RESOURCE', code: 'PREPARE',  path: this.path
    })
    ...
}

Example usage for dev:

ccpwgl_int.emitter.on('Log', myCustomLogListener);

This event emitter is very similar to the one in nodejs and could be useful elsewhere too.

 * Event Emitter
 * @param {String} [name='']
 * @property {String} name
 * @property {{}} events
 * @property {{}} _public
 * @property {Function} _public.on
 * @property {Function} _public.once
 * @property {Function} _public.off
 * @property {Function} _public.delete
 */
export class Tw2EventEmitter
{
    constructor(name = '')
    {
        this.name = name;
        this.events = {};
        this._public = {};
        this._public.on = this.on.bind(this);
        this._public.once = this.once.bind(this);
        this._public.off = this.off.bind(this);
        this._public.delete = this.delete.bind(this);
    }

    /**
     * Gets public only emitter methods
     * @returns {{}}
     */
    get public()
    {
        return this._public;
    }

    /**
     * Registers an event
     * - Events are registered automatically when a listener is added to an event that doesn't exist yet
     * - Emits `EventRegistered` with the newly registered eventName as an argument
     * @param {String} eventName
     * @returns {EventEmitter}
     */
    register(eventName)
    {
        if (!(eventName in this.events))
        {
            this.events[eventName] = new Set();
            this.emit('EventAdded', eventName);
        }
        return this;
    }

    /**
     * Deregisters an event
     * - Emits `EventDeregistered` with the deregistered eventname as an argument
     * @param {String} eventName
     * @returns {EventEmitter}
     */
    deregister(eventName)
    {
        if (eventName in this.events)
        {
            this.emit('EventDeregistered', eventName);
            delete this.events[eventName];
        }
        return this;
    }

    /**
     * Adds a listener to an event
     * - Listeners can only exist once on an event
     * @param {String} eventName
     * @param {Function} listener
     * @returns {EventEmitter}
     */
    on(eventName, listener)
    {
        this.register(eventName);
        this.events[eventName].add(listener);
        return this;
    }

    /**
     * Adds a listener to an event and removes it after it's first emit
     * @param {String} eventName
     * @param {Function} listener
     */
    once(eventName, listener)
    {
        let once = (...args) =>
        {
            listener(...args);
            this.off(eventName, once)
        };
        this.on(eventName, once);
        return this;
    }

    /**
     * Deletes all instances of a listener
     * @param {Function} listener
     * @returns {EventEmitter}
     */
    delete(listener)
    {
        const self = this;
        for (let eventName in this.events)
        {
            if (this.events.hasOwnProperty(eventName))
            {
                self.off(eventName, listener);
            }
        }
        return this;
    }

    /**
     * Removes a listener from an event
     * @param {String} eventName
     * @param {Function} listener
     * @returns {EventEmitter}
     */
    off(eventName, listener)
    {
        if (eventName in this.events)
        {
            this.events[eventName].delete(listener)
        }
        return this;
    }

    /**
     * Emits an event
     * @param {String} eventName
     * @param args
     * @returns {EventEmitter}
     */
    emit(eventName, ...args)
    {
        if (!(eventName in this.events))
        {
            return this.register(eventName);
        }

        for (let listener of this.events[eventName])
        {
            listener(...args);
        }
        return this;
    }
}

var emitter = new Tw2EventEmitter('ccpwgl_int');

Booster and Killmark constant buffers

It seems that the killmark and booster shaders both reference the same constant buffer value, and do different things with it. Using chrome's shader editor, the cb value in question seems to be cb4[0].x, which the booster uses as an intensity value, and the killmark shader uses as the number of killmarks to display.Assuming that the shaders are correct, perhaps a different constant buffer needs to be bound to each (rather than using the same per-object PS data for both booster and killmark draw calls?)

Where are the resources?

I've found that the resource paths used in the demos are similar to what can be found in some of the SDEs (Static Data Export). But a quick cross check with other paths shows me that apparently no other are available on the CDN.

A dedicated discovery mechanism is not immediately necessary, with the paths available in the SDE, it would be enough if the files were available on the CDN. Can you make them available or is some general overhaul planned, changing all the paths?

Local storage options

I remember at the FanFest round table the dev's mentioned one of the problems with the current library is how to transfer and store the massive amounts of data a scene could potentially have.

Being the smart people they are I'm interested if they have looked into the up and coming indexedDB technique. (https://developer.mozilla.org/en-US/docs/IndexedDB). While it would not solve the transport of data issue, it could be used the store the data on a users local machine (textures being transferred as binary could aslo be saved as binary in the db).

Comments?

Outdated GL-Matrix Library

Hi Filipp, do you have any plans to refactor to the latest GL-Matrix library? The newer versions have quite different argument layouts but a lot more functionality. If not, perhaps we could start extending the TW2 Parameters to start handling some of these functions and remove the reliance on other libraries or perhaps I could start updating current gl library?

This could also help us to get rid of a few of those global functions lying around :)

Request for clarification: Tw2ObjectReader.Construct() and Tw2ObjectReader.ConstructAsync() doing the opposite?

I came across Tw2ObjectReader.ConstructAsync() because of its internal while() loop with a direct semicolon (implicit empty block).

Comparing it to the method Tw2ObjectReader.Construct() it became confusing; the "Async" variant performs a blocking operation (synchronous), while the other one implements a deferring strategy.

I see that the ConstructAsync() method is nowhere used, but is their naming intended?

T3 Cruisers Problem with Boosters and Turrets

When building a T3 cruiser using loadShip and the 5 resPaths, I does show up without boosters. Also simply loading Boosters does not work. The Boosters have no _boosterTransforms. When adding Boosters through EveBoosterSet.Add() providing the locators followed by a Rebuild they have been added once per Subsystem. e.g. a Legion with 6 booster locators in total coming from 2 different subsystems will have 6 _boosterTransforms for each of the 5 Subsystems. I think the same happens when mounting turrets with mountTurret() it looks like also every Turret is actually an overlay of five.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.