Fire Special Effect in Python

Thursday, 12. November 2009

I have interested in special effects these days, and bought a book “Special Effects Game Programming with DirectX” yesterday. The algorithm of fire effect looks simple. I want to compile and execute bundled source code that is implemented in C++, but it uses DirectX8 whereas my computer has only DirectX10 SDK installed.

Implementing the algorithm in Python was easy except creating a palette. I hadn’t understood how to “make” a palette, rather I thought palettes are provided by OS or libraries. The code from Pygame repository that creates red-palette is good for me to understand how to make a palette.

Anyway my code is as below


'''
Created on Nov 12, 2009

Class for fire special effect 

Dependency:
 numpy: http://numpy.scipy.org/
 pygame: http://pygame.org/

@author: grayger (http://www.grayger.com/)
'''

import random
import sys
import numpy
import pygame
from pygame.locals import *

class FireEffect:
    def __init__(self, size=(40, 40), coolingFactor=5, fuelRange=(-31, 32)):
        self.__width, self.__height = size
        self.coolingFactor = coolingFactor
        self.fuelRange = fuelRange

        self.__array = numpy.zeros((self.__width, self.__height))
        self.__fireSurface = pygame.Surface((self.__width, self.__height), 0, 8 )
        self.__fireSurface.set_palette(self.__getPalette())

        random.seed()

    def __getPalette(self):
        gstep, bstep = 75, 150
        cmap = numpy.zeros((256, 3))
        cmap[:, 0] = numpy.minimum(numpy.arange(256) * 3, 255)
        cmap[gstep:, 1] = cmap[:-gstep, 0]
        cmap[bstep:, 2] = cmap[:-bstep, 0]
        return cmap  

    def getFireSurface(self):
        tempArray = numpy.zeros(self.__array.shape)
        for r in range(0, self.__width):
            for c in range(0, self.__height):
                tempArray[r, max(0, c - 1)] = min(255, max(0, (int(self.__array[max(0, r - 1), c]) + int(self.__array[min(self.__width - 1, r + 1), c]) + int(self.__array[r, max(0, c - 1)]) + int(self.__array[r, min(self.__height - 1, c + 1)])) / 4 - self.coolingFactor))

        for r in range (0, self.__width, 2):
            fuel = min(255, max(0, self.__array[r, self.__height - 1] + random.randint(*self.fuelRange)))

            tempArray[r, self.__height - 1] = fuel
            tempArray[r + 1, self.__height - 1] = fuel

        self.__array = tempArray
        pygame.surfarray.blit_array(self.__fireSurface, self.__array.astype('int'))
        return self.__fireSurface

#######################################

pygame.init()
screen = pygame.display.set_mode((320, 120), 0, 8 )
fireEffect = FireEffect()

clock = pygame.time.Clock()

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == KEYDOWN:
            if event.key == K_a and fireEffect.coolingFactor < 10:
                fireEffect.coolingFactor += 1
            elif event.key == K_z and fireEffect.coolingFactor > 1:
                fireEffect.coolingFactor -= 1

    fireSurface=fireEffect.getFireSurface()
    # draw the original fire image
    screen.blit(fireSurface, (0,0))    

    # draw the scaled and mirrored fire image
    pos = (0, 40)
    fireSurface = pygame.transform.scale(fireSurface, (80, 80))
    fireSurface2 = pygame.transform.flip(fireSurface, True, False)
    for i in range(0, 4, 2):
        screen.blit(fireSurface, (pos[0] + fireSurface.get_width()*i, pos[1]))
        screen.blit(fireSurface2, (pos[0] + fireSurface.get_width()*(i + 1), pos[1]))

    clock.tick(30)
    pygame.display.flip()

#######################################    

When creating a FireEffect instance, you can set the size of surface array, cooling factor, and the range of fuel factor. The bigger size requires more CPU resource but produces better quality. The bigger cooling factor, the shorter the fire height. The bigger fuel factor, the brighter the fire.
fire2
The upper image is original-sized one and the lower image is scaled/mirrored one.

Bit Hacks

Wednesday, 4. November 2009

I found a short code from pyglet source code.


def _is_pow2(v):
    # http://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2
    return (v & (v - 1)) == 0

At a glance, I can understand how it works, but I have never devised it!
It is from “Bit Hacks” by Sean Eron Anderson. They are not only beautiful but very useful in graphics libraries.

The culture statement of Netflix

Saturday, 12. September 2009

As a company gets bigger, heavier process and controls are followed by the growth. Is the phenomenon reasonable? The culture statement of Netflix may break the stereotype.

Rethinking Goals Of Software Projects

Tuesday, 21. July 2009

Tom DeMarco, from his recent article in IEEE Software, says we should rethink the goal of a software project.

For the past 40 years, for example, we’ve tortured ourselves over our inability to finish a software project on time and on budget. But as I hinted earlier, this never should have been the supreme goal. The more important goal is transformation, creating software that changes the world or that transforms a company or how it does business.

Is software engineering, which weights controls over software projects, outdated? Really? The article is too short to strenghten his sensational arguments. However, software developement may become boring and dry as we focus more on a defined process or metrics.