A dive into the Python code.

dsp2017-1.png

As I’ve written before: loading and displaying model on a isometric game level is a first goal. So let’s have a look into Python scripts loading Blender cubic models and into resultant JSON files structure. The code is here https://github.com/adamskopl/cubicforest/tree/master/cubicLoader.

The code’s structure reflects the structure of the model:

  • cubicLoader.py
  • cubicModel.py
  • cubicCubesGroup.py
  • cubucCube.py

Loader makes an instance of a Model and invokes loadGroups.

"""
cubicModel.py
"""
    def loadGroups(self):
        """
        Fill groups list with CubesGroup objects.
        """
        scene = bpy.data.scenes[0]
        sceneGroups = bpy.data.groups
        for sGroup in sceneGroups:
            groupName = sGroup.name
            groupCubes = sGroup.objects
            cubicGroup = cubicCubesGroup.CubesGroup(groupName)
            cubicGroup.loadCubes(groupCubes)
            self.groups.append(cubicGroup)

Having in mind that maybe I’ll want to divide model into groups (e.g. for simple animation purposed), loading starts with reading them:

sceneGroups = bpy.data.groups

The bpy object is generated by Blender. Next the group loads its cubes and the result is appended to the groups collection.

"""
cubicCubesGroup.py
"""
    def loadCubes(self, groupCubes):
        """
        Fill cubes list with objects from groupCubes list.
        """
        for groupCube in groupCubes:
            cube = cubicCube.Cube(groupCube.name)
            cube.setPos(groupCube.location)
            cube.setRot(groupCube.rotation_euler)
            cube.setCol(groupCube.data.materials[0].name)
            self.cubes.append(cube)

The Cube class is filled with data:

  • position
  • rotation
  • color

The location, rotation_euler and data.materials[0].name are provided by Blender. I’m saving material’s name, which will be later used to indicate the texture needed to render cube.

"""
cubicCube.py
"""
class Cube:
    """
    Class defining model's cube.

    Blender's equivalent: Cube Object.
    """
    def __init__(self, name):
        """ x """
        self.name = name
        self.pos = Vector()
        self.rot = Euler()

    def setPos(self, pos):
        self.pos = pos

    def setRot(self, rot):
        self.rot = rot

    def setCol(self, colName):
        self.colName = colName

Looks simple but it took some time to figure out how to extract needed data from the Blender’s scene.

Next step is to save the model as a JSON file:

"""
cubicLoader.py
"""
    def saveModel(self, fStart, fEnd):
        currentDir=os.path.dirname(os.path.abspath(__file__))
        config = configparser.ConfigParser()
        config.read(currentDir + "/config.ini")
        modelJson = self.model.toJson(fStart, fEnd)
        jsonData = simplejson.dumps(modelJson)
        fd = open(config['Paths']['writePath'] + '/' + self.modelName + ".json", "w")
        fd.write(jsonData)
        fd.close
"""
cubicModel.py
"""
    def toJson(self, fStart, fEnd):
        scene = bpy.data.scenes[0]
        modelDict = dict()
        framesList = list()
        for frameNum in range(fStart, fEnd+1):
            scene.frame_set(frameNum)
            frameDict = dict()
            groupsList = list()
            for frameGroup in self.groups:
                groupDict = frameGroup.toJson()
                groupsList.append(groupDict)
            frameDict["number"] = frameNum
            frameDict["groups"] = groupsList
            framesList.append(frameDict)
        modelDict["name"] = self.name
        modelDict["frames"] = framesList
        return modelDict
"""
cubicCubesGroup.python
"""
    def toJson(self):
        groupDict = dict()
        cubesList = list()
        for c in self.cubes:
            cubeDict = c.toJson()
            cubesList.append(cubeDict)
        groupDict["name"] = self.name
        groupDict["cubes"] = cubesList
        return groupDict
"""
cubicCube.py
"""
    def toJson(self):
        cubeDict = dict()

        posDict=dict()
        posDict["x"] = self.pos.x
        posDict["y"] = self.pos.y
        posDict["z"] = self.pos.z

        rotDict=dict()
        rotDict["x"] = self.rot.x
        rotDict["y"] = self.rot.y
        rotDict["z"] = self.rot.z

        cubeDict["name"] = self.name
        cubeDict["pos"] = posDict
        cubeDict["rot"] = rotDict
        cubeDict["colName"] = self.colName
        return cubeDict

Cubes are converted to JSON, added to the groups, which are also converted to JSON and passed to the model. Every part has its own toJson function.

Here’s the result file with data describing a model:

  • named ‘prize’
  • composed of 3x3x3=27 cubes
  • one frame, one group
  • all cubes are ‘red’
{"name": "prize", "frames": [{"number": 1, "groups": [{"name":
"Group", "cubes": [{"name": "Cube.027", "colName": "red", "pos": {"z":
7.0, "x": -1.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0, "y": 0.0}},
{"name": "Cube.026", "colName": "red", "pos": {"z": 7.0, "x": 1.0,
"y": 0.0}, "rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name":
"Cube.025", "colName": "red", "pos": {"z": 7.0, "x": 0.0, "y": 0.0},
"rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.024",
"colName": "red", "pos": {"z": 6.0, "x": 0.0, "y": 0.0}, "rot": {"z":
0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.023", "colName": "gray",
"pos": {"z": 6.0, "x": 1.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0,
"y": 0.0}}, {"name": "Cube.022", "colName": "red", "pos": {"z": 6.0,
"x": -1.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name":
"Cube.021", "colName": "red", "pos": {"z": 6.0, "x": 2.0, "y": 0.0},
"rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.020",
"colName": "red", "pos": {"z": 6.0, "x": -2.0, "y": 0.0}, "rot": {"z":
0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.019", "colName": "red",
"pos": {"z": 5.0, "x": -2.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0,
"y": 0.0}}, {"name": "Cube.018", "colName": "red", "pos": {"z": 4.0,
"x": -2.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name":
"Cube.016", "colName": "red", "pos": {"z": 5.0, "x": 2.0, "y": 0.0},
"rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.015",
"colName": "red", "pos": {"z": 4.0, "x": 2.0, "y": 0.0}, "rot": {"z":
0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.013", "colName": "red",
"pos": {"z": 3.0, "x": 1.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0,
"y": 0.0}}, {"name": "Cube.012", "colName": "red", "pos": {"z": 3.0,
"x": -1.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name":
"Cube.011", "colName": "red", "pos": {"z": 4.0, "x": -1.0, "y": 0.0},
"rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.010",
"colName": "red", "pos": {"z": 5.0, "x": -1.0, "y": 0.0}, "rot": {"z":
0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.009", "colName": "red",
"pos": {"z": 4.0, "x": 1.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0,
"y": 0.0}}, {"name": "Cube.008", "colName": "red", "pos": {"z": 5.0,
"x": 1.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name":
"Cube.007", "colName": "brown", "pos": {"z": 2.0, "x": 0.0, "y": 0.0},
"rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.006",
"colName": "red", "pos": {"z": 3.0, "x": 0.0, "y": 0.0}, "rot": {"z":
0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.005", "colName": "red",
"pos": {"z": 5.0, "x": 0.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0,
"y": 0.0}}, {"name": "Cube.004", "colName": "red", "pos": {"z": 4.0,
"x": 0.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name":
"Cube.003", "colName": "brown", "pos": {"z": 0.0, "x": 0.0, "y": 0.0},
"rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.002",
"colName": "brown", "pos": {"z": 1.0, "x": 0.0, "y": 0.0}, "rot":
{"z": 0.0, "x": 0.0, "y": 0.0}}]}]}]}

written in Emacs with org2blog mode

Cubicforest JS setup

dsp2017-1.png

I’ve spent some time lately to prepare JS environment for the Cubicforest project:

  • Editor (Webstorm)
  • Eslint (plus Webstorm)
  • Babel (plus Webstorm)

It’s not much, but before starting the development, I’ve had some problems with the setup. Some of them were fixed in the way I can’t remember, so the exact steps of setup will not be shown.

Without much experience with linting tools (which check Javascript code for e.g. syntax errors) I chose ESlint. The next step was the configuration. eslint-config-airbn choice made https://github.com/airbnb/javascript. Setup steps with npm are listed here https://www.npmjs.com/package/eslint-config-airbnb, everything went smooth.

Having some experience with Webstorm, I’m declaring it as the best IDE for JS development. Previously I was working with Brackets and Sublime. I even have spent some time with Emacs configuration, which gave pretty satisfying results, but in the end Webstorm is the clear winner.

Coming back to the Eslint: Webstorm needs a .eslintrc.js configuration file and path to the binary. After that it shows the errors during the coding. If it comes to airbnb, these are some rules which convinced me to that choice (all of the rules listed here):

  • support for ES6: forcing many ES6 improvements like:
    • using const for all references (let eventually)
    • object method shorthand, property value shorthand
    • default parameter syntax rather than mutating function arguments
    • others..
  • single quotes for strings
  • named function expressions instead of function declarations
  • Never reassign parameters
  • aaaaand many others…

Having linter helps to improve code quality.

Digression about shorthands… really cool example of the issue where ES6 really shines:

const hello = 'hello';

// old way
var obj = {
  hello: hello
};

// es6 Airbnb way
const obj = {
  hello,
};

Notice the trailing comma: it’s another example of the detail which intuitively seems to be a better option.

Next: transpiling ES6 with Babel. Haven’t used yet syntax which would not be understood by Chrome except the modules. Another thing in which ES6 rocks and gives shock about how the things can be simple. Few notes about that: Babel has to choose the way in which ES5 modules will be translated. I’ve chosen AMD + RequierJS. Example how the final code looks like (taken from https://www.npmjs.com/package/babel-plugin-transform-es2015-modules-amd):

from:

export default 42;

to:

define(["exports"], function (exports) {
  "use strict";

  Object.defineProperty(exports, "__esModule", {
    value: true
  });

  exports.default = 42;
});

How to watch files for Babel transpile: again Webstorm comes with help. When .babelrc is properly configured like this:

{
  "presets": ["env", "es2015"],
  "plugins": ["babel-plugin-transform-es2015-modules-amd"]
}

Webstorm launches Babel on every file save and exports results by default into the dist directory. I have lost a lot of time because of the lack of the ‘babel-plugin-transform-es2015-modules-amd’ plugin. Without it Requirejs wasn’t able to load the files.

written in Emacs with org2blog mode

Python paths madness.

dsp2017-1.png

I’ve spent whole evening to launch my models loader Python scripts. Since I didn’t remember anything, I was gladly surprised, that I left usage instructions at the beginning of the file.

""" 
Load and run this script in the Blender Console
e.g.:

import sys
sys.path.append("/path_to_cubicLoader_folder")
import cubicLoader
cubicLoader.run("cubicModel", frameStart, frameEnd)

requirements:
https://github.com/simplejson/simplejson
"""

I opened the source code of pythonLoader.py in Emacs. Copied the path through it:

~/git/cubicforest-java/python/jsonLoader/

And according to the instructions wrote in Blender’s Python console:

import sys
sys.path.append("~/git/cubicforest-java/python/jsonLoader/")
import cubicLoader

Result:

ImportError: No module named 'cubicLoader'

Ok, here it goes. Always something. So I’ve thought that maybe I previously had a different version of Python? Checked. No: the Python version is not guilty.

I started digging into the Python’s documentation about writing modules. I started with a simple ‘fibo’ module example. And it worked. I’ve created directory ‘~/fibo’, changed to it, created fibo.py. In console I’ve written like in my previous instructions:

import sys
sys.path.append.path("~/fibo")
import fibo
fibo.fib(10)

And no errors. The ‘fibo’ module is visible. So I’ve returned to Blender, tried again. Same errors. I’ve opened second Python shell and tried writing outside the Blender shell. Maybe it has some different default paths set. That would explain why fibo example works in normal shell and Blender shell gives errors. So again: I’ve tried exactly the same like like in Blender. Same errors.

Started to check many variations of sys.path.append having a fibo example as a guideline. Still nothing. Why fibo works and my loader not? I’ve changed pythonLoader.py file’s content to the same like in fibo.py. Loaded it in the same way. Still import errors.

I started to fiddle with a fibo example again… and noticed that commenting out sys.path.append.path(“~/fibo”) still results in a valid import. Ok, so more confusion. And then ‘aha’: the fibo.py is in the same directory as the launched Python shell. Import works without sys.append, because I’m in the same folder as fibo.py.

Great. So problem almost solved. I just have to do the same with Blender. Don’t minding why the import worked a year ago. Maybe I had a different Xubuntu installations with some paths set… So I’ve tried to solve the problem by making symbolic links in home directory, so the import will behave like in a fibo example: to make import from the current path.

Something moved forward: but other modules used by cubicLoader started to give import errors. Right: sys.path.append worked then, so now I’ve to make more symbolic links so the other modules could be found.

And then I started another try to make things work like they should. Opened the normal console, launched Python shell, split the console window and moved to the ~/git/cubicforest-java/python/jsonLoader path. Typed ‘pwd’ to get a path. Got /home/adamsko/git/cubicforest-java/python/jsonLoader. In Python shell:

import sys
sys.path.append("/home/adamsko/git/cubicforest-java/python/jsonLoader")
import cubicLoader

Silence. No errors. Confusion. Made a longer break, played http://phoboslab.org/xibalba/ (careful, addictive stuff!) for half an hour. Came back. Spot the mistake.

“~/git/cubicforest-java/python/jsonLoader” “/home/adamsko/git/cubicforest-java/python/jsonLoader”

Wind howling through my head.

Goodnight.

written in Emacs with org2blog mode

The figure-rectangle-quad fetish.

dsp2017-1.png

The inheritance pattern was always like an assumption of the best, one and only solution of creating groups of concepts. In the workplace, on the university, in hobby projects, in books. Because if quad is a rectangle, then it’s afolso a figure. So it’s a perfect candidate for an inheritance design.

IS A, IS A, IS A

One of the guidelines taught on the university and in some books about c++:

  • Q: ‘When should you use composition and when inheritance?’
  • A: ‘When the relationship is more like ‘IT IS’, use inheritance. If the ‘IS COMPOSED FROM’ is more accurate, use composition.’

Whatever project I’ve seen, most of them had an impressive inheritance tree. Sometimes it seemed that multiple level of classes was just for the source code to look smart. In the advanced stage of the project, the whole designed structure was turning out to be flawed. In the result it was bent in an ugly way to fit the new requirements. Not to mention about the journey through all the ancestors to figure out how the youngest class is really behaving.

We have a BehaviorA, BehaviorB and BehaviorC. Then we have a concept, which should behave using A and B, and another concept which should behave using B and C. Inheritance says: make base class with A behavior. And then add two classes: one for B and one for C separately. Both of them should inherit from A. Think about it: what’s the point of that? What if another concept is introduced, which should have A,B,C,D behavior? Why not use a factory function which will know A, B, C, D and then just compose those behaviors into a needed concept? Ok, it’s not so sophisticated as inheritance. But isn’t that just simpler?

Inheritance locks groups of behaviors into permanent concepts. Shouldn’t those behaviors be freed and used as the building blocks?

BEHAVES LIKE, BEHAVES LIKE, BEHAVES LIKE

Recently I was relieved, that what intuitively seems to me as a winning solution was backed in this video:

I don’t see the point of using the inheritance at all. I don’t think it’s a good pattern. I don’t think it should be used.

Also the author of the ‘You Don’t Know Js’ series strongly encourages to not to try to simulate the inheritance in Javascript. Even despite the class keyword being introduced in ES6! The prototype mechanism fits the composition perfectly. As the common behavior can be delegated to another object. Not copied and locked into an inelastic structure.

Another article, this time from Eric Elliot: https://medium.com/@_ericelliott/what-you-were-taught-was-not-prototypal-inheritance-f853ce3db00e#.cgs1zt2di

“Favor composition over class inheritance” – the Gang of Four

‘Composition over inheritance’ and yet the second one seems to reign in majority of the projects.

written in Emacs with org2blog mode

Cubicforest models.

dsp2017-1.png

Introduction

How the Cubicforest models will be created? It will be an isometric game with models made from cubes. What tools are involved in the process:

  • Blender software
  • Python scripts
  • Cube’s sprites in different colors.

Blender models

As it’s a sin to write tools, which already exist, I’m using Blender software to create 3d models, which are exported to JSON files. By starting with a position [0.0, 0.0, 0.0], cubes are added in Blender one by one, to create a final effect. Positioning is important: as the cubes have constant dimensions, when added next to each other, the exported file gives positions without fractions. It’s important later to easily indicate the order of the cubes for 2d rendering.

Python script. Json.

Blender allows to interact with the scene with a help of Python scripting. I’ve used it to export model’s data to JSON files. What is exported:

  • cubes’ positions
  • cubes’ colors

Script will be discussed in another post.

Colors

I’ve came up with a simple idea of exporting predefined colors. I didn’t succeed with coloring sprites in LibGDX, so I created ready, colored textures. List of colors includes ‘pink’, ‘dark pink’, ‘green’ etc. To keep everything simple the cubes have assigned named materials according to the color they should represent in the game’s world. Later the material names are associated with the according cube sprites.

2d sprites

atlas.png

First the 3d positions are sorted into 4 groups: for every angle of the future 2d sprite’s model. Depending on the angle, the group starts with the cube which is farthest (from the camera) and ends with the nearest one. 3d Positions are then mapped into isometric ones and painted onto texture one by one.

From the bottom layer of the model to the top. After that the sprite is saved: the final look is ready.

Effects

The person saying ‘A picture is worth a thousand words’ has just been shot.

Blender models:

wolf.jpg

pig.jpg

Rendered sprites:

wolves.png

pigs.png

written in Emacs with org2blog mode

You Don’t Know English

dsp2017-1.png

Post written in polish. But it’s not 100% a technical one.

Po co tłumaczyć książki programistyczne na język polski? Tzn. w sumie znam odpowiedź. W końcu się sprzedają i na pewno jest uwarunkowane to ekonomicznie. Może nie znam jakiegoś kruczka prawnego, który pozwala otrzymać ulgę podatkową, jeśli książka jest polskiego wydania? Może być.

Angielski jest jednym z podstawowych narzędzi w pracy programisty. Nie trzeba w nim płynnie mówić czy pisać. Ale większość dostępnych materiałów jest właśnie w tym języku. Im więcej się czyta, tym czyta się sprawniej, aż w końcu w tym drugim języku zaczyna się myśleć i niezauważalnie staje się tak samo przyjazny jak polski.

Aboslutnie rozumiem dlaczego bloguje się po polsku. W natywnym języku pisze się łatwiej. Ten post piszę 3 razy szybciej. Dalej: treść przekazana w natywnym języku jest atrakcyjniejsza dla polskiego odbiorcy. Maciej Aniserowicz (organizator DajSięPoznać) posiada grupę czytelników godną pozazdroszczenia. Co jest dowodem, że można dużo osiągnąć koncentrując się na mniejszej grupie. Ale jednak, jak sam napisał: zastanawia się, czy technicznych postów nie pisać jednak po angielsku. Ok, ten akapit to taka chwilowa dygresja na tematy językowe. Wracam do meritum.

Bohaterem pogadanki jest polskie wydanie You Don’t Know JS. Na recenzję polskiego wydania natknąłem się na pewnym polskojęzycznym blogu. Autor raczej koncentrował się na treści książki, niż tłumaczeniu, ale napisał, że woli książki w języku polskim, bo po angielsku czytać to tak nie bardzo. I sądząc po jego postach, jest na pewnie dobrym programistą, którego umiejętności zasługują na szacunek. Ale jeśli ktoś ma do wyboru oryginał po angielsku i wybiera polskie tłumaczenie, to robi błąd. Jeśli nie ćwiczy czytania po angielsku: znów błąd.

Książki techniczne to nie beletrystyka. Tej to nie wiem, czy byłbym w stanie czytać po angielsku. Na pewno spróbuję. Ale to inna para kaloszy: mam ogromną przyjemność z czytania polskich tłumaczeń książek Phillpa Dicka, Lovecrafta czy Diuny Herberta. Polskie tłumaczenie dostarcza mi rozrywki, której szukam. Ale w przypadku programowania: tutaj ma cholerne znaczenie każde słowo. Przekręcenie zdania to nie przekręcenie imienia bohatera czy spalenie dowcipu, który brzmi zabójczo w oryginale. To wprowadzanie czytelnika w błąd, który może pociągnąć za sobą lawinę błędnie rozumianych koncepcji do końca lektury.

Nie mogłem uwierzyć:

“(…) Put slightly differently, it’s said that bar() closes over the scope of foo(). Why? Because bar() appears nested inside of foo(). Plain and simple.” “(…) Ujmując rzecz inaczej, można powiedzieć, że funkcja bar() zostaje zamknięta przez zakres foo(). Dlaczego? Dlatego że funkcja bar() wydaje się zagnieżdżona wewnątrz foo(). To powinno być jasne i oczywiste”

Wydaje się, to do dupy wydawnictwo Helion. To powinno być jasne. I oczywiste.

Ten błąd wypala mi oczy. Za każdym razem. I ktoś sobie pomyśli: ‘Aha! Więc w JS niektóre funkcje się wydają, a inne nie! Figlarny ten język, nie ma co.’ Tak w ogóle, jak coś może się jednocześnie ‘wydawać’ a jednocześnie być ‘jasne i oczywiste’?

Do cholery: to tłumacz, czy Helion przepuszcza książki przez translator? A może siedzi w piwnicy licealista, któremu wydawca podrzuca pod drzwi kawałki pizzy, by biedak nie opadł z sił, tłumacząc w bieżącym tygodniu już piątą książkę? Na tym opiera się model biznesowy wydawnictwa? Wydać książkę, byle wydać? Helion i jego tłumacze z premedytacją wypuszczają na rynek szkodliwie przetłumaczone pozycje.

Programuje się po angielsku. Dokumentację czyta się po angielsku. Książki programistyczne wydane po angielsku, najlepiej czyta się po angielsku. Oryginał jest po polsku? Super, cieszmy się, że w tym piękniejszym języku również jest coś porządnie napisane. W tym przypadku to nie ma znaczenia. Liczy się zaufanie do profesjonalizmu treści.

Żałuję, że wydałem na tą przetłumaczoną serię 120 złotych. Tak, wiem że to cholernie mało, polskie wydania mają boskie ceny. Ale wolałbym wydać dwa razy tyle i dostać oryginał, albo po prostu przelać pieniądze autorowi, udostępniającemu książki za friko, bo napracował się przy tej świetnej serii. W przeciwieństwie do pewnych partaczy.

Nie bądźmy programistami, którym “nie bardzo” czyta się po angielsku. Bądźmy lepsi, niż tłumacze Helionu.

Rzekłem.

written in Emacs with org2blog mode