• Welcome to Final Fantasy Hacktics. Please login or sign up.
 
March 28, 2024, 07:46:12 pm

News:

Don't be hasty to start your own mod; all our FFT modding projects are greatly understaffed! Find out how you can help in the Recruitment section or our Discord!


[UTILITY] Win32 Sprite Animator (1.2.2.4)

Started by lirmont, July 03, 2011, 02:59:36 am

lirmont

Another feature in the works (but not released yet):



It's ugly in comparison to the rest of the program. I've still got a lot of stuff to add to it, too. The obnoxious green thing is the optionally drawn path (motion tween) of the selected image frame (outlined in yellow). The white outline matches the composite frame's bounds in the other window. A selected image frame can be dragged around the screen and placed (presumably more quickly than editing an XML file by hand). What you're seeing in the view at the moment is just plain overwriting/overlapping with no blending. Since this is using hardware, the blending modes aren't available yet (the rest of the program software renders everything before displaying it). Anyway, you can kind of see already the need for such a tool considering how far off my hand-written path was from the line the particles in the effect actually took. If you're wondering, the reason the image frames don't match up with the line is because, after the image frames are placed along the line, I've offset them manually even more (that is, the motion tween acts as a guide to land the image frame, enabling you to adjust the frame's position afterward with the landing position as a more accurate starting point).

lirmont

I opened it in Kubuntu with Wine after many hours of installation:



Sadly, the core functionality (the in-program preview part) isn't usable, and the drawing area loses focus the moment you press a key. Form item docking doesn't appear to work, but I can work through that with anchoring. I'll get actual directions later, but you can use "winetricks" GUI (cd /usr/bin && sh winetricks) to select the default Wine prefix and "Install a Windows DLL or component". My list looks like the following:


  • dotnet20

  • gdiplus

  • gecko110 (was already there)

  • glut

  • richtx32



I don't see this program running like I'd like it to in the near future, but it's been informative figuring out how to get it to run at all. :P

Kivutar

Anyway, this is great. I will try to install it your way.
Tethical, an online FFT clone

lirmont

September 07, 2011, 06:21:58 am #83 Last Edit: September 08, 2011, 11:15:40 am by lirmont
Cure (E001):




























As I mentioned earlier, editing frames with text will be replaced/have an alternative in the form of an interface you can interact with. Here is what it looks like at the moment: Edit Selected Frame Window

However, it's very incomplete. There are many functionalities you can't access with it at the moment, but I used it for every composite frame of the format document at the top of this post. So much faster already. Anyway, click and drag is how it's set up. Right-click is context menu (must be inside the drawing area). Middle-click is panning. The letter "B" will let you select a group of items to move, but I only added it as a convenience so it's not done (you have to start up and to the left of what you want to select and make sure it encompasses the whole object's frame to select it via box select).

Also, and very importantly, what you see is what you will get for OpenGL-related renders later on. The images you see in the EditSelectedFrame window are rendered each to their own square on the screen and blended both at a hardware level (in other words, it's not slow like reliable bitmap/raster images I have to create for exporting .gif files in the main window). Usable sprite-based special effects are close at hand!

lirmont

September 08, 2011, 10:48:17 am #84 Last Edit: September 11, 2011, 12:09:14 am by lirmont
Death (E030):



Program View






Eternal

This is great stuff, guys. Thanks, and keep up the amazing work! :D
  • Modding version: PSX & WotL
"You, no less human than we? Ha! Now there's a beastly thought. You've been less than we from the moment your baseborn father fell upon your mother in whatever gutter saw you sired! You've been chattel since you came into the world drenched in common blood!"
  • Discord username: eternal248#1817

lirmont

@Eternal: Thanks!

This is the addition to the editing interface I'm working on in preparation for trying to re-create Bizen Boat's animation:



Look to the previous post to see color tweening/animation in action.

lirmont


Kagebunji

Soo shiny. Almost like a rainbow, heh.
  • Modding version: Other/Unknown

GeneralStrife

holy hell this is awesome, no this is mindboggling

lirmont

@Kagebunji: Yes, they are. :P I set up the formatting document so that you can just change the colors in the motion tween (colors A through E in dark blade's case) and the whole thing changes. I just picked 5 colors at random and threw them in. That's what happened. :P

@GeneralStrife: Thanks! I expect Bizen Boat to make even more jaws drop.

--

Anyway, I don't have anything terribly exciting to show at the moment, but I needed to make changes to the program's process so the bitmap renderer could handle much larger output. That's all done now and uploaded; get it from the first post if you want it. Several major things were addressed, including a bug I was made aware of elsewhere.

As a practical example of the program's new capabilities, allow me to explain Bizen Boat's setup. This is effect number 135. It's a draw out ability, and it's also a very long effect in comparison to the other ones I've done. Basically, effects are 1-2 seconds. This one is closer to 5-6s range. At any rate, more seconds means more frames. Bizen Boat has 140 frames at 480x300 pixels per frame. My setup breaks it down into 10 rows of 14 columns. What does that mean? That means the resulting image the program processes is a whopping 6,720x3,000 pixels. That's 20,160,000 pixels at 32 bits a pixel. That means it's roughly 77MB (645,120,000 bits = 80,640,000 bytes = 78,750 kilobytes = 76.91 megabytes)! Now that the program handles the images better, there's no lag on my machine even with that amount of data loaded. Of course, this is software rendering and only applies to creating a bitmap (what you've seen in the main window). All of this doesn't apply to or affect the hardware renderer (what you see in the "EditSelectedFrame" window), which only ever loads the original file (ex: 128x256 pixels = 32,768 pixels = 1,048,576 bits = 128 kb). 77MBs for editing, 128kb for in-game display! One of these days, anyway.


lirmont

September 19, 2011, 10:16:32 am #91 Last Edit: September 20, 2011, 02:22:22 pm by lirmont


First 63 of 136 frames of Bizen Boat. Slowly working in enough features to make format editing interface-only (i.e. no need to type anything in a file).

Also, E024 (ICE):


Lijj

Lirmont, this is really neat to see all your progress after a short time away. I wonder if you could code it so pitch black is transparent. If you could do that these would be perfect!
  • Modding version: PSX

lirmont

September 21, 2011, 01:54:01 am #93 Last Edit: September 21, 2011, 02:01:47 am by lirmont
Thanks! Everytime I open the program up to work on this stuff (in order to stumble across new features I need/want), I'm always amazed at how ridiculously well-done FFT's original materials are, considering how old the game is at this point. I may add a range to the transparency key, since blending causes the key to bleed, but I had trouble with it last time I tried to do it (because I want the programming to apply to any color that's used as a transparency key). However, it's only something you'll ever see in the bitmap/gif renderer. So long as the individual frames (ex: the butterflies in Bizen Boat or the crystals in Ice) have a blending mode, they will blend on top of the actual scene. So, where you see black in the .GIF animations, the hardware renderer (ex: OpenGL) will see as a calculation that compares the new color (what you see in the .GIF animation) to the existing color (what's already been rendered; i.e. the map, characters, etc.) For example, in the Ice effect, the blending mode is "lighten". This takes the highest color component from each component of R, G, B, and A between the new color and the existing color and makes a new color out of it, which, unless they're both pitch-black, will not be pitch-black (and therefore you won't be able to see the kind of matting around the effect).

That said, I'm to the point where I may need help figuring out if there's anything left that I don't have the ability to recreate with respect to original FFT effects. For example, I know of one feature that I could add, but I wouldn't be able to easily make a bitmap renderer for it (meaning, it wouldn't show up in the preview, only the "edit selected frame" window). That feature is 3d objects (see effect 004, which is Cure 4: http://www.youtube.com/watch?v=95LXqAy2kYY). I also have to finish giving functionality to some of the blending modes.

Anyway, following that, I'm going to write up an integration library in Python for the Panda3D engine (Tethical uses this), but I'm not familiar with either of them programming-wise. So, it may take lots of trial and error, depending on how much access Panda3D gives to OpenGL's blending modes and some extensions related to them. I already have the code written for OpenGL in C#, so it's really just a translation at this point.

Kivutar

lirmont your work is realy usefull!

I think that you can write your integration library in C++ if it is easier for you, more people could use it as Panda3D is C++ native with Python bindings.

If integrating it to the 3D engine is too hard, we can still write an animation exporter to AVI, and display them on a billboard in game. (Am I right? I still don't understand what are blending modes)
Tethical, an online FFT clone

lirmont

September 21, 2011, 05:18:06 am #95 Last Edit: September 21, 2011, 05:32:51 am by lirmont
Since I will have to research this anyway, here's the link that talks about it on Panda3d's help: http://www.panda3d.org/manual/index.php/Texture_Combine_Modes

Basically, how are two colors blended together? That's all this is about. The default behavior (in OpenGL) is not to blend colors. That means, whatever object/color/graphics get drawn last are shown. For example, if I draw a sprite first followed by a menu, the menu shows over the sprite; there is no blending. However, what if the menu's background is transparent? You'd want to see the sprite through it. That's one example of blending.


Blending In OpenGL

Now, I have code in OpenGL that looks like the following:

   // ...
   switch(blendMode) {
       //...
       default:
            Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA);
            break;
   }
   // ...


The default color function OpenGL uses is addition, where two colors are added together:

// "Source Color" means the color we have in our texture, our source for the color.
// "Destination Color" means the color that already exists in the drawing buffer, what we've already drawn, like sprites, maps, etc.
Resulting Color = [
   (Red Component of Source Color * Source Color Blending Percent) + (Red Component of Destination Color * Destination Color Blending Percent),
   (Green Component of Source Color * Source Color Blending Percent) + (Green Component of Destination Color * Destination Color Blending Percent),
   (Blue Component of Source Color * Source Color Blending Percent) + (Blue Component of Destination Color * Destination Color Blending Percent),
   (Alpha Component of Source Color * Source Color Blending Percent) + (Alpha Component of Destination Color * Destination Color Blending Percent)
]


So, let's look at the default method I set up.

The "glBlendFunc" function is what sets "Source Color Blending Percent" and "Destination Color Blending Percent". The first term is "Source Color Blending Percent" is equal to "GL_SRC_ALPHA"; that means, whatever our texture's alpha component is (a value between 0 and 1), use that as the source blending percentage. The second term is "Destination Color Blending Percent" is equal to "GL_ONE_MINUS_SRC_ALPHA"; that means, 1 minus whatever our original texture's alpha component is. These are the amounts each color will be blended in the function.

Now that we have the amounts, let's go back to my example of drawing a sprite followed by drawing a transparent menu. Understand, these calculations happen over the whole screen, but we'll just look at one pixel as an example. Let's say that one of the pixels beneath our transparent menu is black. Let's say that another one of the pixels beneath our transparent menu is red. Let's say that our transparent menu is blue.

First, we draw the background of our scene. Let's say it's the color black; this is the destination color, since the whole screen is now black.
Second, we draw our sprite in the middle of the screen with no blending. Anywhere our sprite's texture had color, this is the new destination color. So, there is black from the background plus our sprite's colors.
Third, we change the blending mode for new drawing. We want the new drawing to be multiplied with "Source Color's Alpha Component" ("GL_SRC_ALPHA") and what's already drawn to be multiplied with "1 - Source Color's Alpha Component" ("GL_ONE_MINUS_SRC_ALPHA").
Fourth, we draw our blue menu. Part of it is over the sprite. Part of it is over the black background. Let's say the blue we used for the menu is: 0, 0, 1, 0.25

Now, OpenGL performs the following calculation when drawing to the color buffer for the black background:


Resulting Color = [
   (0 * 0.25) + (0 * (1-0.25)) = 0,
   (0 * 0.25) + (0 * (1-0.25)) = 0,
   (1 * 0.25) + (0 * (1-0.25)) = .25,
   (0.25 * 0.25) + (1 * (1-0.25)) = 0.8125
]


So, when we draw a partially transparent blue (0, 0, 1, .25) on top of black (0,0,0,1), we get (0,0,.25,0.8125), which is a partially transparent dark blue. This value is written to the color buffer. This is color blending for one pixel.

If we were to do the same calculation over part of the sprite that was red (for instance), it would look like:


Resulting Color = [
   (0 * 0.25) + (1 * (1-0.25)) = .75,
   (0 * 0.25) + (0 * (1-0.25)) = 0,
   (1 * 0.25) + (0 * (1-0.25)) = .25,
   (0.25 * 0.25) + (1 * (1-0.25)) = 0.8125
]


You can see that 75% of the red from the sprite came through at that pixel, and you can see that 25% of the blue from the menu came through at that pixel, resulting in (0.75, 0, 0.25, 0.8125) getting written to the color buffer.


Blending in Panda3d?

Now, going by the help file for Panda3D, I expect I need to do something like:


tex = loader.loadTexture('effects/effect-bitmap.bmp')
ts = TextureStage('ts')
# Enter loop to draw squares with textures mapped to
for i in range(0, numberOfFramesInCompositeFrame, 1)
    # TODO: Replace following lines with a call to a function that sets appriopriate blending modes based on framesInCompositeFrame[i].blendMode
    # Alpha component
    ts.setCombineAlpha(TextureStage.CMAdd, TextureStage.CSTexture, TextureStage.COSrcAlpha, TextureStage.CSPrevious, TextureStage.COOneMinusSrcAlpha)
    # R, G, B components
    ts.setCombineRgb(TextureStage.CMAdd, TextureStage.CSTexture, TextureStage.COSrcAlpha, TextureStage.CSPrevious, TextureStage.COOneMinusSrcAlpha)
     
    # Set up place to store primitive 3d object; note: requires vertex data made by GeomVertexData
    squareMadeByTriangleStrips = GeomTristrips(Geom.UHDynamic)
     
    # Set up place to hold 3d data and for the following coordinates:
    #   square's points (V3: x, y, z),
    #   the colors at each point of the square (c4: r, g, b, a), and
    #   for the UV texture coordinates at each point of the square     (t2: S, T).
    vertexData = GeomVertexData('square-'+i, GeomVertexFormat.getV3c4t2(), Geom.UHDynamic)
    vertex = GeomVertexWriter(vertexData, 'vertex')    
    color = GeomVertexWriter(vertexData, 'color')
    texcoord = GeomVertexWriter(vertexData, 'texcoord')
     
    # Add the square's data
    # Upper-Left corner of square
    vertex.addData3f(-framesInCompositeFrame[i].bound.Width / 2.0, -framesInCompositeFrame[i].bound.Height / 2.0, framesInCompositeFrame[i].z)    
    color.addData4f(framesInCompositeFrame[i].color.R/255.0, framesInCompositeFrame[i].color.G/255.0, framesInCompositeFrame[i].color.B/255.0, framesInCompositeFrame[i].color.A/255.0)
    texcoord.addData2f(framesInCompositeFrame[i].s, framesInCompositeFrame[i].t)

    # Upper-Right corner of square
    vertex.addData3f(framesInCompositeFrame[i].bound.Width / 2.0, -framesInCompositeFrame[i].bound.Height / 2.0, framesInCompositeFrame[i].z)
    color.addData4f(framesInCompositeFrame[i].color.R/255.0, framesInCompositeFrame[i].color.G/255.0, framesInCompositeFrame[i].color.B/255.0, framesInCompositeFrame[i].color.A/255.0)
    texcoord.addData2f(framesInCompositeFrame[i].S, framesInCompositeFrame[i].t)

    # Lower-Right corner of square
    vertex.addData3f(framesInCompositeFrame[i].bound.Width / 2.0, framesInCompositeFrame[i].bound.Height / 2.0, framesInCompositeFrame[i].z)
    color.addData4f(framesInCompositeFrame[i].color.R/255.0, framesInCompositeFrame[i].color.G/255.0, framesInCompositeFrame[i].color.B/255.0, framesInCompositeFrame[i].color.A/255.0)
    texcoord.addData2f(framesInCompositeFrame[i].S, framesInCompositeFrame[i].T)
     
    # Lower-Left corner of square
    vertex.addData3f(-framesInCompositeFrame[i].bound.Width / 2.0, framesInCompositeFrame[i].bound.Height / 2.0, framesInCompositeFrame[i].z)
    color.addData4f(framesInCompositeFrame[i].color.R/255.0, framesInCompositeFrame[i].color.G/255.0, framesInCompositeFrame[i].color.B/255.0, framesInCompositeFrame[i].color.A/255.0)
    texcoord.addData2f(framesInCompositeFrame[i].s, framesInCompositeFrame[i].T)
     
    # Pass data to primitive
    squareMadeByTriangleStrips.addNextVertices(4)
    squareMadeByTriangleStrips.closePrimitive()
    square.addPrimitive(squareMadeByTriangleStrips)
    # Pass primtive to drawing node
    drawPrimitiveNode=GeomNode('square-'+i)
     # TODO: Apply translation, then rotation, then scaling to node.
    drawPrimitiveNode.addGeom(square)
    # Pass node to scene
    render.attachNewNode(drawingPrimitiveNode)
# Loop continues on through each frame called in the composite frame.


But, remember, I know very little about Python and less about Panda3d, so this is a learning experience for me.

lirmont

September 21, 2011, 04:38:58 pm #96 Last Edit: September 21, 2011, 05:07:12 pm by lirmont
The beginning of the end is near!

Linear Dodge in action:




Overwrite in action:


Code for Panda3d (only a proof of concept at this point):

import direct.directbase.DirectStart
from direct.gui.OnscreenText import OnscreenText
from pandac.PandaModules import PandaNode,NodePath,Camera,TextNode,GeomTristrips,Geom,GeomVertexFormat,GeomVertexData,GeomVertexWriter,GeomNode,TransformState,OrthographicLens,TextureStage,TexGenAttrib,PNMImage,Texture,ColorBlendAttrib
from direct.gui.DirectGui import *
import xml.etree.cElementTree as etree

def transparencyKey(filename):
    image = PNMImage(filename)
    image.addAlpha()
    backgroundColor = None
    for y in range(image.getYSize()):
        for x in range(image.getXSize()):
            if backgroundColor == None:
                backgroundColor = Color(image.getRedVal(x, y), image.getGreenVal(x, y), image.getGreenVal(x, y), 0)
            if image.getRedVal(x, y) == backgroundColor.R and \
               image.getGreenVal(x, y) == backgroundColor.G and \
               image.getGreenVal(x, y) == backgroundColor.B:
                # Transparent
                image.setAlpha(x, y, 0.0)
            else:
                # Opaque
                image.setAlpha(x, y, 1.0)
    return image

class Bound:
    X = 0
    Y = 0
    Width = 0
    Height = 0
    def __init__(self, x, y, width, height):
        self.X = x
        self.Y = y
        self.Width = width
        self.Height = height

class Color:
    R = 0
    G = 0
    B = 0
    A = 1
    def __init__(self, R, G, B, A):
        self.R = R
        self.G = G
        self.B = B
        self.A = A

class Frame:
    bound = Bound(0, 0, 0, 0)
    s = 0
    t = 0
    S = 1
    T = 1
    blendMode = "overwrite"
    scaleX = 1
    scaleY = 1
    color = Color(1,1,1,1)
    rotationZ = 0
    def __init__(self, bound, s, t, S, T, blendMode, scaleX, scaleY, color, rotationZ):
        self.bound = bound
        self.s = s
        self.t = t
        self.S = S
        self.T = T
        self.blendMode = blendMode
        self.scaleX = scaleX
        self.scaleY = scaleY
        self.color = color
        self.rotationZ = rotationZ
    def printAsString(self):
        print "["+str(self)+"]::"
        print "   Bound: ["+str(self.bound.X)+", "+str(self.bound.Y)+"; "+str(self.bound.Width)+"x"+str(self.bound.Height)+"],"
        print "   (s, t; S, T): ["+str(self.s)+", "+str(self.t)+"; "+str(self.S)+", "+str(self.T)+"],"
        print "   Blend: '"+self.blendMode+"',"
        print "   Scale(x, y): ["+str(self.scaleX)+", "+str(self.scaleY)+"],"
        print "   Color(R, G, B, A): ["+str(self.color.R)+", "+str(self.color.G)+", "+str(self.color.B)+", "+str(self.color.A)+"],"
        print "   Rotation-Z: "+str(self.rotationZ)

tree = etree.parse("./sprite.xml")
root = tree.getroot()
baseWidth = 0 if root.attrib.get("base-width") == None else float(root.attrib.get("base-width"))
baseHeight = 0 if root.attrib.get("base-height") == None else float(root.attrib.get("base-height"))
effectWidth = 1 if root.attrib.get("frame-width") == None else float(root.attrib.get("frame-width"))
effectHeight = 1 if root.attrib.get("frame-height") == None else float(root.attrib.get("frame-height"))
frames = root.find('.//frames')
colors = root.find('.//colors')
compositeFrames = root.find('.//composite-frames')
# Load only the following frame.
loadFrame = 11
frameList = []
for node in compositeFrames.getiterator('composite-frame'):
    if node.tag == "composite-frame" and node.attrib.get("id") == str(loadFrame):
        for frameCallNode in node:
            for frameNode in frames.getiterator('frame'):
                if frameNode.tag == "frame" and frameNode.attrib.get("id") == frameCallNode.attrib.get("id"):
                    offsetX = 0 if frameCallNode.attrib.get("offset-x") == None else float(frameCallNode.attrib.get("offset-x"))
                    offsetY = 0 if frameCallNode.attrib.get("offset-y") == None else float(frameCallNode.attrib.get("offset-y"))
                    tweenId = frameCallNode.attrib.get("tween")
                    frameInTween = 0 if frameCallNode.attrib.get("frame-in-tween") == None else int(frameCallNode.attrib.get("frame-in-tween"))
                    addWidth = 0 if frameNode.attrib.get("w") == None else float(frameNode.attrib.get("w"))
                    addHeight = 0 if frameNode.attrib.get("h") == None else float(frameNode.attrib.get("h"))
                    sInPixels = 0 if frameNode.attrib.get("s") == None else float(frameNode.attrib.get("s"))
                    tInPixels = 0 if frameNode.attrib.get("t") == None else float(frameNode.attrib.get("t"))
                    swInPixels = sInPixels + addWidth
                    thInPixels = tInPixels + addHeight
                    s = (sInPixels / baseWidth)
                    t = 1 - (tInPixels / baseHeight) # Complemented to deal with loading image upside down.
                    S = (swInPixels / baseWidth)
                    T = 1 - (thInPixels / baseHeight) # Complemented to deal with loading image upside down.
                    blend = "overwrite" if frameCallNode.attrib.get("blend") == None else frameCallNode.attrib.get("blend")
                    scaleX = 1 if frameCallNode.attrib.get("scale-x") == None else float(frameCallNode.attrib.get("scale-x"))
                    scaleY = 1 if frameCallNode.attrib.get("scale-y") == None else float(frameCallNode.attrib.get("scale-y"))
                    color = Color(1,1,1,1)
                    tweenHasColor = False
                    frameCallHasColor = False
                    frameCallColorName = frameCallNode.attrib.get("color-name")
                    if frameCallColorName != None:
                        # Get color at frame call as first resort.
                        frameCallHasColor = True
                        for colorNode in colors.getiterator('color'):
                            if colorNode.tag == 'color' and colorNode.attrib.get("name") == frameCallColorName:
                                R = 1 if colorNode.attrib.get("r") == None else float(colorNode.attrib.get("r"))
                                G = 1 if colorNode.attrib.get("g") == None else float(colorNode.attrib.get("g"))
                                B = 1 if colorNode.attrib.get("b") == None else float(colorNode.attrib.get("b"))
                                A = 1 if colorNode.attrib.get("a") == None else float(colorNode.attrib.get("a"))
                                color = Color(R, G, B, A)
                                break # leave for loop when we find the correct color
                        pass
                    elif tweenId != None and False:
                        # Get color at tween frame as second resort.
                        # TODO: write tween code for color
                        pass
                    elif frameNode.attrib.get("color-name") != None:
                        # Get color at frame definition as last resort.
                        for colorNode in colors.getiterator('color'):
                            if colorNode.tag == 'color' and colorNode.attrib.get("name") == frameNode.attrib.get("color-name"):
                                R = 1 if colorNode.attrib.get("r") == None else float(colorNode.attrib.get("r"))
                                G = 1 if colorNode.attrib.get("g") == None else float(colorNode.attrib.get("g"))
                                B = 1 if colorNode.attrib.get("b") == None else float(colorNode.attrib.get("b"))
                                A = 1 if colorNode.attrib.get("a") == None else float(colorNode.attrib.get("a"))
                                color = Color(R, G, B, A)
                                break # leave for loop when we find the correct color
                        pass
                    rotationZ = 0 if frameCallNode.attrib.get("rotation-z") == None else float(frameCallNode.attrib.get("rotation-z"))
                    frameList.append(Frame(Bound(offsetX, offsetY, addWidth, addHeight), s, t, S, T, blend, scaleX, scaleY, color, rotationZ))
            pass
        break # Leave once we've found the appropriate frame

# Load texture; supply alpha channel if it doesn't exist.
p = transparencyKey('night-sword.fft-E173.BMP')
tex = Texture()
tex.setup2dTexture(p.getXSize(), p.getYSize(), Texture.TUnsignedByte, Texture.FRgba)
tex.load(p)

# Make an identifier for to tack onto square names in Panda3d's scene graph.
frameIndexForName = 1
# Loop through loaded frames that make up composite frame.
for loadedFrame in frameList:
    # For debugging purposes, print the object.
    if True:
        loadedFrame.printAsString()

    # Set up place to store primitive 3d object; note: requires vertex data made by GeomVertexData
    squareMadeByTriangleStrips = GeomTristrips(Geom.UHDynamic)
     
    # Set up place to hold 3d data and for the following coordinates:
    #   square's points (V3: x, y, z),
    #   the colors at each point of the square (c4: r, g, b, a), and
    #   for the UV texture coordinates at each point of the square     (t2: S, T).
    vertexData = GeomVertexData('square-'+str(frameIndexForName), GeomVertexFormat.getV3c4t2(), Geom.UHDynamic)
    vertex = GeomVertexWriter(vertexData, 'vertex')     
    color = GeomVertexWriter(vertexData, 'color')
    texcoord = GeomVertexWriter(vertexData, 'texcoord')
     
    # Add the square's data
    # Upper-Left corner of square
    vertex.addData3f(-loadedFrame.bound.Width / 2.0, 0, -loadedFrame.bound.Height / 2.0)
    color.addData4f(loadedFrame.color.R,loadedFrame.color.G,loadedFrame.color.B,loadedFrame.color.A)
    texcoord.addData2f(loadedFrame.s, loadedFrame.T)

    # Upper-Right corner of square
    vertex.addData3f(loadedFrame.bound.Width / 2.0, 0, -loadedFrame.bound.Height / 2.0)
    color.addData4f(loadedFrame.color.R,loadedFrame.color.G,loadedFrame.color.B,loadedFrame.color.A)
    texcoord.addData2f(loadedFrame.S, loadedFrame.T)
   
# Lower-Left corner of square
    vertex.addData3f(-loadedFrame.bound.Width / 2.0, 0, loadedFrame.bound.Height / 2.0)
    color.addData4f(loadedFrame.color.R,loadedFrame.color.G,loadedFrame.color.B,loadedFrame.color.A)
    texcoord.addData2f(loadedFrame.s, loadedFrame.t)

    # Lower-Right corner of square
    vertex.addData3f(loadedFrame.bound.Width / 2.0, 0, loadedFrame.bound.Height / 2.0)
    color.addData4f(loadedFrame.color.R,loadedFrame.color.G,loadedFrame.color.B,loadedFrame.color.A)
    texcoord.addData2f(loadedFrame.S, loadedFrame.t)

    # Pass data to primitive
    squareMadeByTriangleStrips.addNextVertices(4)
    squareMadeByTriangleStrips.closePrimitive()
    square = Geom(vertexData)
    square.addPrimitive(squareMadeByTriangleStrips)
    # Pass primtive to drawing node
    drawPrimitiveNode=GeomNode('square-'+str(frameIndexForName))   
    drawPrimitiveNode.addGeom(square)
    # Pass node to scene
    nodePath = base.camera.attachNewNode(drawPrimitiveNode)
    # Linear dodge:
    if True:
        nodePath.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OOne, ColorBlendAttrib.OOneMinusIncomingColor))
    else: # Overwrite:
        nodePath.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOneMinusIncomingAlpha))
    nodePath.setDepthTest(False)
    # Apply texture
    nodePath.setTexture(tex)
    # Translate square to be directly in front of camera.   
    nodePath.setPos((loadedFrame.bound.X + loadedFrame.bound.Width / 2.0, 1, -loadedFrame.bound.Y - loadedFrame.bound.Height / 2.0))
    # TODO: Apply translation, then rotation, then scaling to node.
    nodePath.setR(loadedFrame.rotationZ)
    nodePath.setScale(loadedFrame.scaleX, 1, loadedFrame.scaleY)
    #nodePath.place()
    frameIndexForName = frameIndexForName + 1
# Loop continues on through each frame called in the composite frame.

OnscreenText(text = '(0 , 0)', pos = (.1, .05), scale = 0, fg=(1,1,1,1))
for i in range(-20,20):
    OnscreenText(text = '.', pos = (0, i/float(10)), scale = 0, fg=(1,1,1,1))
for i in range(-20,20):
    OnscreenText(text = '.', pos = (i/float(10), 0), scale = 0, fg=(1,1,1,1))
   
OnscreenText(text = 'X', pos = (1.3, .1), scale = 0, fg=(1,1,1,1))
OnscreenText(text = 'Y', pos = (.1, .9), scale = 0, fg=(1,1,1,1))
     
lens = OrthographicLens()
screenWidth = effectWidth
screenHeight = effectHeight
lens.setFilmSize(screenWidth, screenHeight)  # Or whatever is appropriate for your scene
lens.setFilmOffset(screenWidth/2, -screenHeight/2)
#lens.setFilmOffset(screenWidth/2, 0)
base.cam.node().setLens(lens)
base.camera.setPos(0,0,0)
run()

alanai

Lol something funny about that panda grid XD the Origin makes a faec :3 (o,o)

Kivutar

This is awesome!

I can't wait to add the first effect in the game.
Tethical, an online FFT clone

lirmont

September 22, 2011, 10:58:03 am #99 Last Edit: September 22, 2011, 11:18:32 am by lirmont
@alanai: It came from an example on the web. The face owl saved me hours of trying to figure out where I was in Panda3d's camera space.

@Kivutar: For the Panda3d effect renderer, I've got everything looking the way it's supposed to look for the few effects I've done. For now, I'll just turn it over to you. I'll make changes as you decide what doesn't work. Get it here: Panda3d Effect Wrapper (Python)

To use it, you basically make an "Effect" object, giving it the filename (note: you must include the effect folder name in the filename; see line ~460 for examples), then you wrap it in a Panda taskMgr task. I didn't include the image source material, so you'll have to get it from the effects page. I've got links in the first post.

Anyway, here's what it looks like at the moment: