phoboslab / TwoPointFive

3D View Plugin for the Impact HTML5 Game Engine
MIT License
143 stars 20 forks source link

adding a Skybox #7

Open rontrek opened 5 years ago

rontrek commented 5 years ago

Hi, I see that the forum is now read only and noted to post here on GitHub for questions, so in particular I'm trying to create a background skybox for a twopointfive level. I did search for previous related post here:

https://impactjs.com/forums/impact-engine/impact-1-24-pseudo-3d-web-audio

but with no luck and somehow can't get it to work. What I have done so far is cleared a part of the ceiling assuming it will be drawn there.

main.js

init: function() {
...
this.skyboxImage = new ig.Image( 'media/skybox.png' );
this.skyboxImage.width = screen.width;
this.skyboxImage.height = screen.height;
this.skyboxQuad = new tpf.HudTile( this.skyboxImage, 0, screen.width, screen.height);
this.skyboxQuad.setPosition(0, 0, 0);       

...
...
...

drawWorld: function() {
this.skyboxQuad.draw();
this.parent();
}
Igecode commented 5 years ago

I think your skybox need to be un cube. I've wrote this for a game prototype few years ago. I think it's a "good" base to solve your problem (I think it's not usable without somme work for you). It use an entity to work with weltmeister :

PLUGIN:

ig.module(
    'plugins.igecode.world.skybox'
)
.requires(
    'impact.image',
    'plugins.twopointfive.renderer.quad'
)
.defines(function(){

ig.Skybox = ig.Class.extend({

    boxSize: 4096,
    boxPos: {x: 0, y: 0, z:0},

    tileSize: 32,
    tileScale: 1,

    textures: {
        /* sky list or tweak it for use image with init(settings)*/
        black: new ig.Image('media/skydomes/black.png'),
        bluesky: new ig.Image('media/skydomes/bluesky.jpg'),
        test: new ig.Image('media/skydomes/test.png')
    },

    boxFaces: null,
    image: null,

    init:function(settings){
        if(settings != null) {
            if(settings.image && this.textures[settings.image]){
                this.image = this.textures[settings.image];
            }

            this.boxPos = {x:settings.x || 0, y:settings.z || 0, z:settings.y || 0};
            this.boxSize = settings.boxSize || 2048
            this.tileSize = settings.tileSize || 512
        }

        this.tileScale = this.boxSize / this.tileSize;

        if(this.image == null) {
            this.image = this.textures.black;
        }

        // Create faces
        this.createGeometry();
    },

    createGeometry: function(){
        // creates tiles
        this.boxFaces = [
            new tpf.Tile( this.image, 0, this.tileSize, this.tileSize, this.tileScale ),
            new tpf.Tile( this.image, 5, this.tileSize, this.tileSize, this.tileScale ),
            new tpf.Tile( this.image, 2, this.tileSize, this.tileSize, this.tileScale ),
            new tpf.Tile( this.image, 1, this.tileSize, this.tileSize, this.tileScale ),
            new tpf.Tile( this.image, 4, this.tileSize, this.tileSize, this.tileScale ),
            new tpf.Tile( this.image, 3, this.tileSize, this.tileSize, this.tileScale ),
        ];

        // face 1 (front)
        this.boxFaces[0].quad.setPosition(
            this.boxPos.x,
            this.boxPos.y,
            this.boxPos.z + this.boxSize/2
        );
        this.boxFaces[0].quad.setRotation(0, Math.PI, 0);

        // face 2 (left)
        this.boxFaces[1].quad.setPosition(
            this.boxPos.x - this.boxSize/2,
            this.boxPos.y,
            this.boxPos.z 
        );
        this.boxFaces[1].quad.setRotation(0, Math.PI/2, 0);

        // face 3 (back)
        this.boxFaces[2].quad.setPosition(
            this.boxPos.x,
            this.boxPos.y,
            this.boxPos.z - this.boxSize/2
        );

        // face 4 (right)
        this.boxFaces[3].quad.setPosition(
            this.boxPos.x + this.boxSize/2,
            this.boxPos.y,
            this.boxPos.z 
        );
        this.boxFaces[3].quad.setRotation(0, -Math.PI/2, 0);

        // face 5 (top)
        this.boxFaces[4].quad.setPosition(
            this.boxPos.x,
            this.boxPos.y + this.boxSize/2,
            this.boxPos.z 
        );
        this.boxFaces[4].quad.setRotation(-Math.PI/2, Math.PI, Math.PI/2);

        // face 6 (bottom)
        this.boxFaces[5].quad.setPosition(
            this.boxPos.x,
            this.boxPos.y - this.boxSize/2,
            this.boxPos.z 
        );
        this.boxFaces[5].quad.setRotation(Math.PI/2, -Math.PI, 0);
    },

    draw: function(){
        if(!this.boxFaces) {
            this.createGeometry();
        }

        var mesh = new tpf.TileMesh(this.boxFaces),
            renderer = ig.system.renderer;

        renderer.pushMesh(mesh);

        renderer.setProgram(renderer.programFog);

    }
});
});

ENTITY

ig.module(
    'game.entities.skybox-params'
)
.requires(
    'impact.entity'
)
.defines(function(){

EntitySkyboxParams = ig.Entity.extend({
    type: ig.Entity.TYPE.NONE,
    checkAgainst: ig.Entity.TYPE.NONE,
    collides: ig.Entity.COLLIDES.NEVER,

    size: {x: 8, y: 8},
    boxSize: 2048,

    _wmDrawBox: true,
    _wmBoxColor: '#88f',

    init: function(x, y, settings){
        this.parent(x, y, settings);
    },

    update: function(){},

    draw: function(){
        if(!ig.global.wm){
            return;
        }

        var x = (this.pos.x-this.boxSize/2+this.size.x/2-ig.game.screen.x)*ig.system.scale,
            y = (this.pos.y-this.boxSize/2+this.size.y/2-ig.game.screen.y)*ig.system.scale,
            boxSize = this.boxSize*ig.system.scale,
            alpha = (this == ig.game.entities.selectedEntity)?1: 0.2;

        var ctx = ig.system.context;
        ctx.globalAlpha = alpha;

        ctx.lineWidth = 1;
        ctx.strokeStyle = this._wmBoxColor;
        ctx.stroke();
        ctx.strokeRect( x, y, this.boxSize * ig.system.scale, this.boxSize * ig.system.scale );
        ctx.globalAlpha = 1;
    }
});
});

GAME EXTEND :

ig.module(
    'plugins.igecode.game'
)
.requires(
    'plugins.twopointfive.game',
    'plugins.igecode.world.skybox'
)
.defines(function(){ "use strict";

ig.GameExtend = tpf.Game.extend({

    skybox: null,

    clearLevel: function() {
        this.parent();

        this.skybox = null;
    },

    loadLevel: function( data ) {
        this.parent();

        // creation du skybox
        // Find the info entity
        var skyboxParams = null;
        for( var i=0; i<data.entities.length; i++ ) {
            if( data.entities[i].settings && data.entities[i].type == 'EntitySkyboxParams' ) {
                skyboxParams = data.entities[i].settings;
                skyboxParams.x = data.entities[i].x;
                skyboxParams.y = data.entities[i].y;
            }
        }

        if(skyboxParams != null) {
            this.skybox = new ige.Skybox(skyboxParams);
        }
    },

    drawWorld: function() {
        this.parent();

        if(this.skybox != null) {
            this.skybox.draw();
        }

    }
});
});
rontrek commented 5 years ago

Awesome, Thanks a lot! @SilvRO :smiley:

one small issue I noticed is it is affected by fog, so I have to turn it off. It would be nice though to get this to work with fog.

Anyways, really appreciate this and got it working. thanks again :+1: :+1: :+1:

Igecode commented 5 years ago

The quads are very far away from the camera. You need some tweak to disable fog only on skybox.

I'm glad to have helped you 😃

rontrek commented 5 years ago

I see and I would assume that would involve tweaking the fog shader. @phoboslab is there a current workaround to exclude an entity/quad set from fog?

Another minor thing would be attaching or parenting the skybox to the player/camera to get that endless horizon effect. I'm fairly new to impact/tpf and tried to copy the x and y player position to the box position and it is not moving at all.. looks like that's not how it works. any tips for that?

Igecode commented 5 years ago

My code was really for testing. Normally a skybox is not a "physical" entity that the player can touch. I think we need to review the integration of the skybox to do this. A 3D pass for the skybox with fixed origin and camera angle, then a pass for the level. For fog, perhaps pass a parameter to the shader fragment to disable it (impact\lib\plugins\twopointfive\renderer\render.js: tpf.Renderer.Shaders.vertex.FragmentWithFog). And go up to the Quad object to pass the parameter.

rontrek commented 5 years ago

Ok got it. Yes, agreed and what I have proposed there is not having to use shaders as much as possible as this is the go-to method which was used before shaders became available, and that is setting up a large skybox following camera or using pivot attached to player camera. I think your solution here is more practical and simple, but I don't mind going for a shader version either.

As for the fog, I finally figured out a workaround which just involves depth. With the fog near and far default setting to 128 and 512 respectively, I just set the boxSize a little bit bigger around 8000 (so the sky looks bigger/farther with a huge margin just to make sure) and added this on the fog shader.

if(depth > 2000.0) fogFactor = 0.0;

or just 1000, might need some adjustment depending on the map setup, but it works. I still have no clue with interacting with custom quads and passing parameters to shader on Impact, but would be curious to know how this can be done, particularly with entities that are not far but is not affected with fog (items, entities, powerups, etc.).