Skip to content

Latest commit

 

History

History
395 lines (319 loc) · 10.6 KB

README.md

File metadata and controls

395 lines (319 loc) · 10.6 KB

xna.js

This is the first public version of xna.js. WebGL library strongly inspired by XNA/Monogame framework. The library is in the early stages. I'm writing it because I want to learn WebGL and create some demo. Still a lot to do here.

Showcase:

Spritebatch

Normalmap

Mobile game

How to use:

lighting.js:

// Core
var Game = require("xnajs/game");
var Pointer = require("xnajs/input/pointer");
var Keyboard = require("xnajs/input/keyboard");
var Keys = require("xnajs/input/keys");

// Graphics
var Effect = require("xnajs/graphics/effect");
var SpriteBatch = require("xnajs/graphics/sprite-batch");
var VertexBuffer = require("xnajs/graphics/vertex-buffer");
var IndexBuffer = require("xnajs/graphics/index-buffer");
var Texture2d = require("xnajs/graphics/texture2d");
var Blend = require("xnajs/graphics/blend");
var BlendState = require("xnajs/graphics/blend-state");
var RenderTarget2D = require("xnajs/graphics/render-target2d");

// Components
var FpsMeter = require("xnajs-components/fps-meter");
var mat4 = require("xnajs/matrix/mat4");

var Lighting = Game.extend({
  initialize: function() {
    this.base();

    this.width = 1136;
    this.height = 640;

    this.graphicsDevice.backBufferWidth = this.width;
    this.graphicsDevice.backBufferHeight = this.height;

    Keyboard.addKey(Keys.SPACE);
    Keyboard.addKey(Keys.D);
    Keyboard.addKey(Keys.Q);
    Keyboard.addKey(Keys.W);
    Keyboard.addKey(Keys.A);
    Keyboard.addKey(Keys.S);

    this.debug = false;

    var ambient = 0.1;

    this._lights = [];
    this._ambientLight = [ambient, ambient, ambient, 1];
    this._specularStrength = 0.5;

    // add lights
    this._lights.push({
      color: [1.0, 0.0, 1.0, 1.0],
      power: 0.8,
      lightDecay: 400,
      position: [0, 0, 10],
      isEnabled: true
    });

    this._lights.push({
      color: [0.2, 0.4, 1.0, 1.0],
      power: 0.4,
      lightDecay: 300,
      position: [this.width / 2, 100, 10],
      isEnabled: true
    });

    this.newState = null;
    this.oldState = Keyboard.getState();
  },

  loadContent: function() {
    this.spriteBatch = new SpriteBatch(this.graphicsDevice);

    this.texture = this.content.load["Texture2D"](
      "textures/floor_tile_02-1024.jpg"
    );
    this.textureNormal = this.content.load["Texture2D"](
      "textures/floor_tile_02-1024_norm.jpg"
    );
    this.font = this.content.load["SpriteFont"]("fonts/quant.fnt");
    this.debugFont = this.content.load["SpriteFont"]("fonts/calibri16.fnt");

    this.width = this.graphicsDevice.backBufferWidth;
    this.height = this.graphicsDevice.backBufferHeight;

    this.colorMapRenderTarget = new RenderTarget2D(
      this.graphicsDevice,
      this.width,
      this.height
    );
    this.shadowMapRenderTarget = new RenderTarget2D(
      this.graphicsDevice,
      this.width,
      this.height
    );
    this.normalMapRenderTarget = new RenderTarget2D(
      this.graphicsDevice,
      this.width,
      this.height
    );

    this.pointLightEffect = new Effect(
      this.graphicsDevice,
      require("./shaders/deferred-pointlight.fx")
    );
    this.lightCombinedEffect = new Effect(
      this.graphicsDevice,
      require("./shaders/deferred-combined.fx")
    );

    this.fpsMeter = new FpsMeter();
  },

  update: function(gameTime) {
    // Tweak parameters

    this.newState = Keyboard.getState();

    // Is the SPACE key down?
    if (this.newState.isKeyDown(Keys.SPACE)) {
      //key has just been pressed.
      if (!this.oldState.isKeyDown(Keys.SPACE)) {
        this._lights.push({
          color: [Math.random(), Math.random(), Math.random(), 1.0],
          power: (Math.random() * 10 + 1) * 0.1,
          lightDecay: Math.random() * 300 + 100,
          position: [
            Math.random() * this.width,
            Math.random() * this.height,
            80
          ],
          isEnabled: true
        });
      }
    }

    // Is the SPACE key down?
    if (this.newState.isKeyDown(Keys.D)) {
      //key has just been pressed.
      if (!this.oldState.isKeyDown(Keys.D)) {
        this.debug = !this.debug;
      }
    }

    if (this.newState.isKeyDown(Keys.Q)) {
      var ambient = this._ambientLight[0];
      ambient -= 0.05;
      ambient = Math.max(0, ambient);
      this._ambientLight = [ambient, ambient, ambient, 1];
    }

    if (this.newState.isKeyDown(Keys.W)) {
      var ambient = this._ambientLight[0];
      ambient += 0.05;
      ambient = Math.min(1, ambient);
      this._ambientLight = [ambient, ambient, ambient, 1];
    }

    if (this.newState.isKeyDown(Keys.A)) {
      this._specularStrength -= 0.05;
      this._specularStrength = Math.max(0, this._specularStrength);
    }

    if (this.newState.isKeyDown(Keys.S)) {
      this._specularStrength += 0.05;
      this._specularStrength = Math.min(10, this._specularStrength);
    }

    // Update saved state.
    this.oldState = this.newState;

    this._lights[0].position[0] = Pointer.items[0].x;
    this._lights[0].position[1] = Pointer.items[0].y;

    light = this._lights[1];
    light.position[0] =
      Math.sin(gameTime.totalGameTime / 1000) * this.width / 4 + this.width / 2;
    light.position[1] =
      Math.cos(gameTime.totalGameTime / 1000) * this.height / 4 +
      this.height / 2;

    this.fpsMeter.update(gameTime);
  },

  draw: function(gameTime) {
    if (!this.content.isReady) {
      return;
    }

    this.graphicsDevice.clear([100 / 255, 149 / 255, 237 / 255, 1]);

    // Set the render targets
    this.graphicsDevice.setRenderTarget(this.colorMapRenderTarget);

    // Clear all render targets
    this.graphicsDevice.clear([0, 0, 0, 0]);

    this.drawColorMap();

    this.graphicsDevice.setRenderTarget(null);
    this.graphicsDevice.setRenderTarget(this.normalMapRenderTarget);

    // Clear all render targets
    this.graphicsDevice.clear([0, 0, 0, 0]);

    this.drawNormalMap();

    // Deactive the rander targets to resolve them
    this.graphicsDevice.setRenderTarget(null);

    this.generateShadowMap();

    this.graphicsDevice.clear([0, 0, 0, 1]);

    // Finally draw the combined Maps onto the screen
    this.drawCombinedMaps();

    this.drawDebugRenderTargets(this.spriteBatch);
    this.drawDebugInformation();
  },

  drawColorMap: function() {
    this.spriteBatch.begin();
    this.spriteBatch.draw(this.texture, [0, 0]);
    this.spriteBatch.end();
  },

  drawNormalMap: function() {
    this.spriteBatch.begin();
    this.spriteBatch.draw(this.textureNormal, [0, 0]);
    this.spriteBatch.end();
  },

  generateShadowMap: function() {
    this.graphicsDevice.setRenderTarget(this.shadowMapRenderTarget);
    this.graphicsDevice.clear([0, 0, 0, 1]);

    for (var i = 0; i < this._lights.length; i++) {
      var light = this._lights[i];

      if (!light.isEnabled) {
        continue;
      }

      this.pointLightEffect.apply();

      this.pointLightEffect.parameters["lightStrength"].setValue(light.power);
      this.pointLightEffect.parameters["lightPosition"].setValue(
        light.position
      );
      this.pointLightEffect.parameters["lightColor"].setValue(light.color);
      this.pointLightEffect.parameters["lightDecay"].setValue(light.lightDecay);
      this.pointLightEffect.parameters["specularStrength"].setValue(
        this._specularStrength
      );

      this.pointLightEffect.parameters["screenWidth"].setValue(this.width);
      this.pointLightEffect.parameters["screenHeight"].setValue(this.height);
      this.pointLightEffect.parameters["NormalMap"].setValue(
        this.normalMapRenderTarget
      );
      this.pointLightEffect.parameters["ColorMap"].setValue(
        this.colorMapRenderTarget
      );

      this.spriteBatch.begin(null, this.pointLightEffect);

      var gl = this.graphicsDevice.gl;
      gl.enable(gl.BLEND);
      gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.SRC_ALPHA, gl.ONE);
      gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD);

      this.spriteBatch.draw(this.normalMapRenderTarget, [0, 0]);
      this.spriteBatch.end();
    }

    this.graphicsDevice.setRenderTarget(null);
  },

  drawCombinedMaps: function() {
    this.lightCombinedEffect.apply();
    this.lightCombinedEffect.parameters["ambient"].setValue(1);
    this.lightCombinedEffect.parameters["lightAmbient"].setValue(4);
    this.lightCombinedEffect.parameters["ambientColor"].setValue(
      this._ambientLight
    );
    this.lightCombinedEffect.parameters["NormalMap"].setValue(
      this.normalMapRenderTarget
    );
    this.lightCombinedEffect.parameters["ColorMap"].setValue(
      this.colorMapRenderTarget
    );
    this.lightCombinedEffect.parameters["ShadingMap"].setValue(
      this.shadowMapRenderTarget
    );

    this.spriteBatch.begin(Blend.ALPHA_BLEND, this.lightCombinedEffect);
    this.spriteBatch.draw(this.colorMapRenderTarget, [0, 0]);
    this.spriteBatch.end();
  },

  drawDebugRenderTargets: function(spriteBatch) {
    if (!this.debug) {
      return;
    }

    spriteBatch.begin();

    var scale = [1 / 3, 1 / 3];

    spriteBatch.draw(
      this.colorMapRenderTarget,
      [0, this.height - this.height / 3],
      null,
      null,
      0,
      scale,
      null
    );
    spriteBatch.draw(
      this.normalMapRenderTarget,
      [this.width / 3, this.height - this.height / 3],
      null,
      null,
      0,
      scale,
      null
    );
    spriteBatch.draw(
      this.shadowMapRenderTarget,
      [this.width / 3 * 2, this.height - this.height / 3],
      null,
      null,
      0,
      scale,
      null
    );

    spriteBatch.end();
  },

  drawDebugInformation: function() {
    var lines = [
      "xna.js",
      "fps: " + this.fpsMeter.fps,
      "x: " + Pointer.items[0].x + ", y: " + Pointer.items[0].y,
      "lights: " + this._lights.length,
      "Press SPACE to add light",
      "Press D to toggle debug",
      "Q,W: Ambient light: " + this._ambientLight[0].toPrecision(2),
      "A,S: Specular strength: " + this._specularStrength.toPrecision(2)
    ];

    this.spriteBatch.begin();

    for (var i = 0; i < lines.length; i++) {
      this.spriteBatch.drawString(this.debugFont, lines[i], [
        10,
        10 + this.debugFont.lineHeight * i
      ]);
    }

    this.spriteBatch.end();
  }
});

main.js:

var Lighting = require('./lighting');

document.addEventListener('DOMContentLoaded', main, false);
function main() {
    var game = new Lighting('#game');
    game.run();
}

License

Xna.js is available under the MIT license. See the LICENSE file for more info.