Added background shader in map view
This commit is contained in:
parent
4e498cbdbd
commit
939313889c
|
@ -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:
|
||||
|
||||
|
|
2
TODO.md
2
TODO.md
|
@ -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
|
||||
|
|
56
out/assets/shaders/map-background.glsl
Normal file
56
out/assets/shaders/map-background.glsl
Normal 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.);
|
||||
}
|
22
spacetac
22
spacetac
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue