1
0
Fork 0

Added background shader in map view

This commit is contained in:
Michaël Lemaire 2017-10-15 19:54:37 +02:00
parent 4e498cbdbd
commit 939313889c
8 changed files with 176 additions and 25 deletions

View file

@ -16,7 +16,9 @@
## How to develop
The only hard dependency of the toolchain is Python3.
The only hard dependency of the toolchain is [yarn](https://yarnpkg.com).
If yarn is not installed on your system but Python3 is, yarn will be automatically installed in a local virtual environment.
If you want to build on your computer, clone the repository, then run:

View file

@ -105,7 +105,7 @@ Technical
* Pack all images in atlases, and split them by stage
* Pack sounds
* Use shaders for backgrounds, with fallback images
* Add toggles for shaders, automatically disable them if too slow, and initially disable them on mobile
* Replace jasmine with mocha+chai
Network

View file

@ -0,0 +1,56 @@
// Star Nest by Pablo Román Andrioli
// This content is under the MIT License.
precision mediump float;
#define iterations 15
#define formuparam 0.53
#define volsteps 12
#define stepsize 0.11
#define zoom 0.800
#define tile 0.850
#define brightness 0.0015
#define darkmatter 0.300
#define distfading 0.770
#define saturation 0.900
uniform vec2 resolution;
uniform vec2 offset;
uniform float scale;
void main()
{
//get coords and direction
vec2 uv=gl_FragCoord.xy/resolution.xy-.5;
uv.y*=resolution.y/resolution.x;
vec3 dir=vec3(uv*zoom*20./pow(scale,0.5),1.);
vec3 from=vec3(4.+offset.x*0.05,-2.+offset.y*0.05,-2.5-1.0/scale);
//volumetric rendering
float s=0.1,fade=1.;
vec3 v=vec3(0.);
for (int r=0; r<volsteps; r++) {
vec3 p=from+s*dir*.5;
p = abs(vec3(tile)-mod(p,vec3(tile*2.))); // tiling fold
float pa,a=pa=0.;
for (int i=0; i<iterations; i++) {
p=abs(p)/dot(p,p)-formuparam; // the magic formula
a+=abs(length(p)-pa); // absolute sum of average change
pa=length(p);
}
float dm=max(0.,darkmatter-a*a*.001); //dark matter
a*=a*a; // add contrast
if (r>6) fade*=1.-dm; // dark matter, don't render near
//v+=vec3(dm,dm*.5,0.);
v+=fade;
v+=vec3(s,s*s,s*s*s*s)*a*brightness*fade; // coloring based on distance
fade*=distfading; // distance fading
s+=stepsize;
}
v=mix(vec3(length(v)),v,saturation); //color adjust
gl_FragColor = vec4(v*.01,1.);
}

View file

@ -1,15 +1,19 @@
#!/bin/bash
# Main build/run tool for SpaceTac
# Uses yarn installed in local node.js environment
# REQUIRES python3, else falls back to system-wide npm
# REQUIRES yarn
# If yarn is not found, python3 may be used to create a local node.js environment with yarn in it, and use it
yarn=$(which yarn 2>/dev/null)
set -e
which python3 > /dev/null 2>&1 || ( npm "$@" && exit 0 )
if [ "x${yarn}" != "x" ]
then
"${yarn}" "$@"
else
dir=$(dirname $0)
dir=$(dirname $0)
test -x "${dir}/.env/bin/nodeenv" || ( virtualenv -p python3 "${dir}/.env" && "${dir}/.env/bin/pip" install --upgrade nodeenv )
test -e "${dir}/.env/node/bin/activate" || "${dir}/.env/bin/nodeenv" --node=6.11.1 --force "${dir}/.env/node"
test -e "${dir}/.env/node/bin/yarn" || "${dir}/.env/node/bin/shim" "${dir}/.env/node/bin/npm" install -g yarn@1.1.0
PATH="${dir}/.env/node/bin:${PATH}" yarn "$@"
test -x "${dir}/.env/bin/nodeenv" || ( virtualenv -p python3 "${dir}/.env" && "${dir}/.env/bin/pip" install --upgrade nodeenv )
test -e "${dir}/.env/node/bin/activate" || "${dir}/.env/bin/nodeenv" --node=6.11.1 --force "${dir}/.env/node"
test -e "${dir}/.env/node/bin/yarn" || "${dir}/.env/node/bin/shim" "${dir}/.env/node/bin/npm" install -g yarn@1.1.0
PATH="${dir}/.env/node/bin:${PATH}" yarn "$@"
fi

View file

@ -123,6 +123,8 @@ module TK.SpaceTac.UI {
this.loadSound("music/division.mp3");
this.loadSound("music/spring-thaw.mp3");
this.loadShader("map-background.glsl");
}
this.load.start();
@ -158,5 +160,9 @@ module TK.SpaceTac.UI {
loadSound(path: string) {
this.load.audio(AssetLoading.getKey(path), "assets/sounds/" + path);
}
loadShader(path: string) {
this.load.shader(AssetLoading.getKey(path), "assets/shaders/" + path);
}
}
}

View file

@ -169,6 +169,33 @@ module TK.SpaceTac.UI.Specs {
expect(a).toBe(3);
})
it("can create shaders", function () {
let builder = new UIBuilder(testgame.view);
let shader1 = builder.shader("test-shader-1", "test-image-1");
expect(shader1 instanceof Phaser.Image).toBe(true);
expect(shader1.name).toEqual("test-image-1");
expect(shader1.filters.length).toBe(1, "one filter set on shader1");
let shader2 = builder.shader("test-shader-2", { width: 500, height: 300 });
expect(shader2 instanceof Phaser.Image).toBe(true);
expect(shader2.width).toEqual(500);
expect(shader2.height).toEqual(300);
expect(shader2.filters.length).toBe(1, "one filter set on shader2");
let i = 0;
let shader3 = builder.shader("test-shader-3", "test-image-3", 50, 30, () => { return { a: i++, b: { x: 1, y: 2 } } });
expect(shader3.x).toEqual(50);
expect(shader3.y).toEqual(30);
expect(shader3.filters[0].uniforms["a"]).toEqual({ type: '1f', value: 0 }, "uniform a initial");
expect(shader3.filters[0].uniforms["b"]).toEqual({ type: '2f', value: Object({ x: 1, y: 2 }) }, "uniform b initial");
shader3.update();
expect(shader3.filters[0].uniforms["a"]).toEqual({ type: '1f', value: 1 }, "uniform a updated");
expect(shader3.filters[0].uniforms["b"]).toEqual({ type: '2f', value: Object({ x: 1, y: 2 }) }, "uniform b updated");
expect(testgame.view.getLayer("base").children.length).toBe(3, "view layer should have three children");
})
it("creates sub-builders, preserving text style", function () {
let base_style = new UITextStyle();
base_style.width = 123;

View file

@ -8,6 +8,8 @@ module TK.SpaceTac.UI {
export type UIGroup = Phaser.Group
export type UIContainer = Phaser.Group | Phaser.Image
export type ShaderValue = number | { x: number, y: number }
/**
* Text style interface
*/
@ -185,6 +187,38 @@ module TK.SpaceTac.UI {
return result;
}
/**
* Add a fragment shader area, with optional fallback image
*/
shader(name: string, base: string | { width: number, height: number }, x = 0, y = 0, updater?: () => { [name: string]: ShaderValue }): UIImage {
let source = this.game.cache.getShader(name);
source = "" + source;
let uniforms: any = {};
if (updater) {
iteritems(updater(), (key, value) => {
uniforms[key] = { type: (typeof value == "number") ? "1f" : "2f", value: value };
});
}
let filter = new Phaser.Filter(this.game, uniforms, source);
let result: Phaser.Image;
if (typeof base == "string") {
result = this.image(base, x, y);
result.filters = [filter];
filter.setResolution(result.width, result.height);
} else {
result = filter.addToWorld(x, y, base.width, base.height);
this.add(result);
}
if (updater) {
result.update = () => {
iteritems(updater(), (key, value) => filter.uniforms[key].value = value);
filter.update();
}
}
filter.update();
return result;
}
/**
* Change the content of an component
*

View file

@ -66,6 +66,8 @@ module TK.SpaceTac.UI {
create() {
super.create();
let builder = new UIBuilder(this);
this.layer_universe = this.getLayer("universe");
this.layer_overlay = this.getLayer("overlay");
@ -143,7 +145,16 @@ module TK.SpaceTac.UI {
}
});
this.setZoom(2);
this.setZoom(2, 0);
// Add a shader background
builder.shader("map-background", { width: this.getWidth(), height: this.getHeight() }, 0, 0, () => {
let scale = this.layer_universe.scale.x;
return {
offset: { x: (920 - this.layer_universe.x) / scale, y: -(540 - this.layer_universe.y) / scale },
scale: scale
}
});
// Trigger an auto-save any time we go back to the map
this.autoSave();
@ -226,46 +237,57 @@ module TK.SpaceTac.UI {
*/
setCamera(x: number, y: number, span: number, duration = 500, easing = Phaser.Easing.Cubic.InOut) {
let scale = 1000 / span;
this.tweens.create(this.layer_universe.position).to({ x: 920 - x * scale, y: 540 - y * scale }, duration, easing).start();
this.tweens.create(this.layer_universe.scale).to({ x: scale, y: scale }, duration, easing).start();
let dest_x = 920 - x * scale;
let dest_y = 540 - y * scale;
if (duration) {
this.tweens.create(this.layer_universe.position).to({ x: dest_x, y: dest_y }, duration, easing).start();
this.tweens.create(this.layer_universe.scale).to({ x: scale, y: scale }, duration, easing).start();
} else {
this.layer_universe.position.set(dest_x, dest_y);
this.layer_universe.scale.set(scale);
}
}
/**
* Set the camera to include all direct-jump accessible stars
*/
setCameraOnAccessible(star: Star) {
setCameraOnAccessible(star: Star, duration: number) {
let accessible = star.getNeighbors().concat([star]);
let xmin = min(accessible.map(star => star.x));
let xmax = max(accessible.map(star => star.x));
let ymin = min(accessible.map(star => star.y));
let ymax = max(accessible.map(star => star.y));
let dmax = Math.max(xmax - xmin, ymax - ymin);
this.setCamera(xmin + (xmax - xmin) * 0.5, ymin + (ymax - ymin) * 0.5, dmax * 1.2);
this.setCamera(xmin + (xmax - xmin) * 0.5, ymin + (ymax - ymin) * 0.5, dmax * 1.2, duration);
}
/**
* Set the alpha value for all links
*/
setLinksAlpha(alpha: number) {
this.game.add.tween(this.starlinks_group).to({ alpha: alpha }, 500 * Math.abs(this.starlinks_group.alpha - alpha)).start();
setLinksAlpha(alpha: number, duration = 500) {
if (duration) {
this.game.add.tween(this.starlinks_group).to({ alpha: alpha }, duration * Math.abs(this.starlinks_group.alpha - alpha)).start();
} else {
this.starlinks_group.alpha = alpha;
}
}
/**
* Set the current zoom level (0, 1 or 2)
*/
setZoom(level: number) {
setZoom(level: number, duration = 500) {
let current_star = this.player.fleet.location ? this.player.fleet.location.star : null;
if (!current_star || level <= 0) {
this.setCamera(0, 0, this.universe.radius * 2);
this.setLinksAlpha(1);
this.setCamera(0, 0, this.universe.radius * 2, duration);
this.setLinksAlpha(1, duration);
this.zoom = 0;
} else if (level == 1) {
this.setCameraOnAccessible(current_star);
this.setLinksAlpha(0.6);
this.setCameraOnAccessible(current_star, duration);
this.setLinksAlpha(0.6, duration);
this.zoom = 1;
} else {
this.setCamera(current_star.x, current_star.y, current_star.radius * 2);
this.setLinksAlpha(0.2);
this.setCamera(current_star.x, current_star.y, current_star.radius * 2, duration);
this.setLinksAlpha(0.2, duration);
this.zoom = 2;
}