fenomas / noa

Experimental voxel game engine.
MIT License
616 stars 91 forks source link

Material name #4

Closed andrewtheone closed 7 years ago

andrewtheone commented 8 years ago

Hey!

What are the opportunities to a, store addition block data and retrieve by block id b, get the name of block material ?

fenomas commented 8 years ago

Hey!

a. This is planned eventually, but not in yet.

b. The engine currently keeps a name->id hash, but not the other way around, so there's no way to query a block name. This would be a straightforward feature if you want to try adding it though - all relevant code is inside /lib/registry. Or the alternative is to just keep an id->name hash in your client code.

andrewtheone commented 8 years ago

Thanks for all your help, here is my little contribution to the project, its a little library to manage blocks and block data. It can set block at world from 3 different type of argument.

{
name: 'material_name', // will be accessible via 'mat_material_name' hence: it gets a mat_ prefix
texture: 'texture.jpg', // or null
color: [1,1,1, 0.5], // or null
block_data_a: 'value',
block_data_b: 'value2'  //these are accessible via BlockManager.getData(blockId) if it this object was passed to BlockManager.setBlock(pos, value, true), otherwise you have to call BlockManager.setData(blockID, data_object)
/*etc*/
}

Library code


var BlockManager = function(noa) {

    this.noa = noa;

    this.blockData = {};

    this.textures = {};  //by name
    this.textureIDMap = {};  //by id to name
}

BlockManager.prototype.addTexture = function(name, color, texture)  {

    name = 'mat_'+name;

    console.log("Adding texture: "+name+", color: "+color+", texture: "+texture);

    this.noa.registry.registerMaterial(name, color, texture)

    var id = this.textures[name] = this.noa.registry.registerBlock(name, name);
    this.textureIDMap[id] = name;
    return id;
}

BlockManager.prototype.setData = function(blockId, data) {
    this.blockData[blockId] = data;
}

BlockManager.prototype.hasTexture = function(name_or_id) {
    if(typeof name_or_id == "string") {
        return (this.textures.hasOwnProperty(name_or_id));
    }

    return (this.textureIDMap.hasOwnProperty(name_or_id));
}

BlockManager.prototype.getTextureID = function(name) {
    return this.textures[name];
}

BlockManager.prototype.getData = function(blockId) {
    return (this.blockData.hasOwnProperty(blockId)?this.blockData[blockId]:{});
}

BlockManager.prototype.setBlock = function(position, value, doSetData) {
    var original_value = null;
    if(typeof value == "object") {

        original_value = value;

        var possibleTextureName = "mat_"+value.name;
        if(!this.hasTexture(possibleTextureName)) {
            value = this.addTexture(possibleTextureName, (value.color?value.color:[1,1,1]), value.texture);
        } else {
            value = this.getTextureID(possibleTextureName);
        }
        // if texture name exists, then we modify value to possibleTextureName and let the algorithm go forward
    }

    if(typeof value == "string") {

        value = this.getTextureID(value);
    }

    this.noa.setBlock(value, position);

    var blockId = this.noa.world.getBlockID(position[0], position[1], position[2]);

    if(doSetData == true) {
        this.setData(blockId, original_value);
    }

    return blockId;
}

module.exports = BlockManager;
andrewtheone commented 8 years ago

I have extended the above code with texture caching:

It uses: https://github.com/psayre23/WebSQL

TextureDB.js


var TextureDB = function(noa) {
    this.noa = noa;

    this.db = WebSQL('site');

    this.db.query('CREATE TABLE textures (url, data)');
}

TextureDB.prototype.getBase64 = function(url, callback) {
    var img = new Image();
    img.crossOrigin = 'Anonymous';
    img.onload = function(){
        if(typeof window.base64Canvas == "undefined")
            window.base64Canvas = document.createElement('CANVAS');
        var ctx = window.base64Canvas.getContext('2d');
        var dataURL;
        window.base64Canvas.height = this.height;
        window.base64Canvas.width = this.width;
        ctx.drawImage(this, 0, 0);
        dataURL = window.base64Canvas.toDataURL('image/png');
        callback(dataURL);
    };
    img.src = url;
}

TextureDB.prototype.getTexture = function(texture, cb) {

    if(texture == null) {
        cb(null);
        return;
    }

    this.db.query('SELECT data from textures where url = ?', [texture]).done(function(rows) {
        if(rows.length == 0) {
            this.getBase64(texture, function(data) {
                this.db.query('INSERT INTO textures (url, data) VALUES (?,?)', [texture, data]);
                cb(data);
            }.bind(this))
            return;
        }

        cb(rows[0].data);
    }.bind(this))
}

module.exports = TextureDB;

BlockManager.js

var db = require('./texturedb.js');

var BlockManager = function(noa) {

    this.noa = noa;
    this.db = new db(this.noa);

    this.blockData = {};

    this.textures = {};  //by name
    this.textureIDMap = {};  //by id to name
}

BlockManager.prototype.addTexture = function(name, color, texture, cb)  {
    var self = this;

    this.db.getTexture(texture, function(data) {
        self.noa.registry.registerMaterial(name, color, data)

        var id = self.textures[name] = self.noa.registry.registerBlock(name, name);
        self.textureIDMap[id] = name;

        if(cb) cb(id);
    })
}

BlockManager.prototype.setData = function(blockId, data) {
    this.blockData[blockId] = data;
}

BlockManager.prototype.hasTexture = function(name_or_id) {
    if(typeof name_or_id == "string") {
        return (this.textures.hasOwnProperty(name_or_id));
    }

    return (this.textureIDMap.hasOwnProperty(name_or_id));
}

BlockManager.prototype.getTextureID = function(name) {
    return this.textures[name];
}

BlockManager.prototype.getData = function(blockId) {
    return (this.blockData.hasOwnProperty(blockId)?this.blockData[blockId]:{});
}

BlockManager.prototype.setBlock = function(position, value, cb) {

    if(typeof value == "object") {

        var possibleTextureName = value.name;
        if(!this.hasTexture(possibleTextureName)) {
            var self = this;
            this.addTexture(possibleTextureName, (value.color?value.color:[1,1,1]), value.texture, function(mat_id) {
                self.setBlock(position, mat_id, cb);
            });
            return;
        } else {
            value = this.getTextureID(possibleTextureName);
        }
        // if texture name exists, then we modify value to possibleTextureName and let the algorithm go forward
    }

    if(typeof value == "string") {

        value = this.getTextureID(value);
    }

    this.noa.setBlock(value, position);

    if(cb) cb(this.noa.world.getBlockID(position[0], position[1], position[2]))
}

module.exports = BlockManager;

And you have to edit rendering.js:515 to:

    var tex = new BABYLON.Texture(url, self._scene, true,false, BABYLON.Texture.NEAREST_SAMPLINGMODE, null, null, url)

because of WebSQL I had to use asyncronous calls

fenomas commented 7 years ago

Hey, sorry for leaving this in limbo so long; it dropped off my radar before I had time to review it. It's certainly interesting but I'd like to avoid adding a dependency on webSQL, and also Babylon caches textures internally as I understand it. If game clients want to have a robust DB-like way to manage materials, I think that code probably should live in the game client rather than inside the engine. Thanks for posting the code though!