diff --git a/data/themes/MegaLight V4/stages/default.png b/data/themes/MegaLight V4/backgrounds/default.png similarity index 100% rename from data/themes/MegaLight V4/stages/default.png rename to data/themes/MegaLight V4/backgrounds/default.png diff --git a/data/themes/MegaLight V4/hitflamesanimation.png.bak b/data/themes/MegaLight V4/hitflamesanimation.png.bak new file mode 100644 index 000000000..78a1e5211 Binary files /dev/null and b/data/themes/MegaLight V4/hitflamesanimation.png.bak differ diff --git a/data/themes/MegaLight V4/stage.ini b/data/themes/MegaLight V4/stage.ini index 8b1378917..35f810d7c 100644 --- a/data/themes/MegaLight V4/stage.ini +++ b/data/themes/MegaLight V4/stage.ini @@ -1 +1,258 @@ +[layer1] +texture = stage_background.png +xres = 512 +yres = 512 +xscale = 1.3 +yscale = 1.3 +xpos = 0.0 +ypos = 0.0 +angle = 0.0 +[layer2] +texture = stage_lights1.png +xres = 256 +yres = 128 +xpos = -0.65 +ypos = -0.8 + +[layer3] +texture = stage_lights2.png +xres = 256 +yres = 128 +xpos = 0.65 +ypos = -0.8 + +[layer4] +texture = stage_drums.png +xres = 256 +yres = 256 +xpos = -0.7 +ypos = 0.0 + +[layer4:fx1] +type = scale +trigger = beat +xmagnitude = 0.01 +ymagnitude = 0.01 + +[layer5] +texture = stage_bassdrum.png +xres = 128 +yres = 128 +xpos = -0.65 +ypos = 0.13 + +[layer5:fx1] +type = scale +trigger = beat +xmagnitude = 0.06 +ymagnitude = 0.06 + +[layer6] +texture = stage_speakers.png +xres = 256 +yres = 256 +xpos = 0.7 +ypos = 0.0 + +[layer6:fx1] +type = scale +trigger = beat +xmagnitude = 0.03 +ymagnitude = 0.03 +delay = 0.5 + +[layer7] +texture = stage_speaker_cones.png +xres = 256 +yres = 256 +xpos = 0.67 +ypos = -0.03 + +[layer7:fx1] +type = scale +trigger = beat +xmagnitude = 0.07 +ymagnitude = 0.07 + +[layer8] +texture = stage_audience1.png +xres = 256 +yres = 256 +xpos = -0.8 +ypos = 0.4 + +[layer8:fx1] +type = wiggle +trigger = beat +xmagnitude = 0.02 +ymagnitude = -0.04 +frequency = 0.5 + +[layer9] +texture = stage_audience2.png +xres = 256 +yres = 256 +xpos = 0.8 +ypos = 0.4 + +[layer9:fx1] +type = wiggle +trigger = beat +xmagnitude = 0.01 +ymagnitude = -0.03 +frequency = 1.0 +delay = .25 + +[layer10] +texture = stage_light.png +xres = 256 +yres = 256 +xpos = -0.87 +ypos = -0.75 +xscale = 3.0 +yscale = 3.0 +src_blending = src_alpha +dst_blending = one +foreground = 1 + +[layer10:fx1] +type = light +trigger = pick +light_number = 0 +intensity = 0.7 + +[layer10:fx2] +type = rotate +trigger = miss +profile = sinstep +angle = 10 +period = 75 + +[layer11] +texture = stage_light.png +xres = 256 +yres = 256 +xpos = -0.69 +ypos = -0.8 +xscale = 3.0 +yscale = 3.0 +angle = -12.0 +src_blending = src_alpha +dst_blending = one +foreground = 1 + +[layer11:fx1] +type = light +trigger = pick +light_number = 1 +intensity = 0.7 + +[layer11:fx2] +type = rotate +trigger = miss +profile = sinstep +angle = -10 +period = 100 + +[layer12] +texture = stage_light.png +xres = 256 +yres = 256 +xpos = -0.48 +ypos = -0.83 +xscale = 3.0 +yscale = 3.0 +angle = -16.0 +src_blending = src_alpha +dst_blending = one +foreground = 1 + +[layer12:fx1] +type = light +trigger = pick +light_number = 2 +intensity = 0.7 + +[layer12:fx2] +type = rotate +trigger = miss +profile = sinstep +angle = 7 +period = 150 + +[layer13] +texture = stage_light.png +xres = 256 +yres = 256 +xpos = 0.83 +ypos = -0.75 +xscale = 3.0 +yscale = 3.0 +src_blending = src_alpha +dst_blending = one +foreground = 1 + +[layer13:fx1] +type = light +trigger = pick +intensity = 0.7 +light_number = 0 + +[layer13:fx2] +type = rotate +trigger = miss +profile = sinstep +angle = 12 +period = 250 + +[layer14] +texture = stage_light.png +xres = 256 +yres = 256 +xpos = 0.64 +ypos = -0.84 +xscale = 3.0 +yscale = 3.0 +angle = 12.0 +src_blending = src_alpha +dst_blending = one +foreground = 1 + +[layer14:fx1] +type = light +trigger = pick +light_number = 1 +intensity = 0.7 + +[layer14:fx2] +type = rotate +trigger = miss +profile = sinstep +angle = -5 +period = 200 + +[layer15] +texture = stage_light.png +xres = 256 +yres = 256 +xpos = 0.46 +ypos = -0.9 +xscale = 3.0 +yscale = 3.0 +angle = 19.0 +src_blending = src_alpha +dst_blending = one +foreground = 1 + +[layer15:fx1] +type = light +trigger = pick +light_number = 2 +intensity = 0.7 + +[layer15:fx2] +type = rotate +trigger = miss +angle = -5 +period = 150 diff --git a/data/themes/MegaLight V4/stage/light.png b/data/themes/MegaLight V4/stage/light.png new file mode 100644 index 000000000..9e562751f Binary files /dev/null and b/data/themes/MegaLight V4/stage/light.png differ diff --git a/data/themes/MegaLight V4/stage/stage_audience1.png b/data/themes/MegaLight V4/stage/stage_audience1.png new file mode 100644 index 000000000..5fc96f60c Binary files /dev/null and b/data/themes/MegaLight V4/stage/stage_audience1.png differ diff --git a/data/themes/MegaLight V4/stage/stage_audience2.png b/data/themes/MegaLight V4/stage/stage_audience2.png new file mode 100644 index 000000000..b7441c017 Binary files /dev/null and b/data/themes/MegaLight V4/stage/stage_audience2.png differ diff --git a/data/themes/MegaLight V4/stage/stage_background.png b/data/themes/MegaLight V4/stage/stage_background.png new file mode 100644 index 000000000..c63c5c6ef Binary files /dev/null and b/data/themes/MegaLight V4/stage/stage_background.png differ diff --git a/data/themes/MegaLight V4/stage/stage_bassdrum.png b/data/themes/MegaLight V4/stage/stage_bassdrum.png new file mode 100644 index 000000000..fbacdbe4a Binary files /dev/null and b/data/themes/MegaLight V4/stage/stage_bassdrum.png differ diff --git a/data/themes/MegaLight V4/stage/stage_drums.png b/data/themes/MegaLight V4/stage/stage_drums.png new file mode 100644 index 000000000..9835e1701 Binary files /dev/null and b/data/themes/MegaLight V4/stage/stage_drums.png differ diff --git a/data/themes/MegaLight V4/stage/stage_light.png b/data/themes/MegaLight V4/stage/stage_light.png new file mode 100644 index 000000000..c87cd427c Binary files /dev/null and b/data/themes/MegaLight V4/stage/stage_light.png differ diff --git a/data/themes/MegaLight V4/stage/stage_lights1.png b/data/themes/MegaLight V4/stage/stage_lights1.png new file mode 100644 index 000000000..6828ff76c Binary files /dev/null and b/data/themes/MegaLight V4/stage/stage_lights1.png differ diff --git a/data/themes/MegaLight V4/stage/stage_lights2.png b/data/themes/MegaLight V4/stage/stage_lights2.png new file mode 100644 index 000000000..f77ee00f6 Binary files /dev/null and b/data/themes/MegaLight V4/stage/stage_lights2.png differ diff --git a/data/themes/MegaLight V4/stage/stage_speaker_cones.png b/data/themes/MegaLight V4/stage/stage_speaker_cones.png new file mode 100644 index 000000000..fed6a419c Binary files /dev/null and b/data/themes/MegaLight V4/stage/stage_speaker_cones.png differ diff --git a/data/themes/MegaLight V4/stage/stage_speakers.png b/data/themes/MegaLight V4/stage/stage_speakers.png new file mode 100644 index 000000000..2704c7a10 Binary files /dev/null and b/data/themes/MegaLight V4/stage/stage_speakers.png differ diff --git a/data/themes/MegaLight/rockmeter.ini b/data/themes/MegaLight/rockmeter.ini index f58951731..503184ba1 100644 --- a/data/themes/MegaLight/rockmeter.ini +++ b/data/themes/MegaLight/rockmeter.ini @@ -1,22 +1,25 @@ [layer0:Image] texture = rock_bottom.png -xpos = 0.07 +xpos = 25 ypos = 0.5 xscale = .25 yscale = .25 +inPixels = xpos [layer1:Image] texture = rock_fill.png -xpos = 0.07 -ypos = (.265*rock) + .235 +xpos = 25 +ypos = 0.5 xscale = .25 -yscale = -.25*rock -rect = (1,0,rock,0) +yscale = .25 +rect = (0.0,1.0,0,rock) +valignment = bottom +inPixels = xpos [layer2:Image] texture = rock_top.png -xpos = 0.07 -ypos = 0.475 +xpos = 25 +ypos = 0.5 xscale = .25 yscale = .25 @@ -38,16 +41,17 @@ yscale = 0.41 texture = overdrive fill.png xpos = 0.5 ypos = 0.032 -xscale = .41*power +xscale = .41 yscale = .35 rect = (0.0, power, 0.25, 1.0) +alignment = left -; [layer6:image} -; texture = overdrive top.png -; xpos = .295 -; ypos = 0.065 -; xscale = 0.3 -; yscale = 0.3 +[layer6:Image] +texture = overdrive top.png +xpos = .5 +ypos = 0.065 +xscale = 0.3 +yscale = 0.3 [layer9:Text] text = streak @@ -95,122 +99,28 @@ ypos = 315 alignment = right inPixels = xpos|ypos -[layer18:Image] -condition = not player.isBassGuitar and player.starPowerActive and streak >= streakMax -texture = mult.png -xpos = 600 -ypos = 0.7 -xscale = .41 -yscale = .41 -rect = (0.0, 1.0, float(multiplier-1)*0.125, float(multiplier)*0.125) -inPixels = xpos - -[layer19:Image] -condition = not player.isBassGuitar and player.starPowerActive and not streak >= streakMax -texture = mult.png -xpos = 600 -ypos = 0.7 -xscale = .41 -yscale = .41 -rect = (0.0, 1.0, float(multiplier-1)*0.125, float(multiplier)*0.125) -inPixels = xpos +[layer17:Image] +texture = mult2.png +xpos = .5 +ypos = 0.0395 +xscale = .4305 +yscale = .4305 +rect = (0.0, 1.0, (streak%10)*.1, ((streak%10)+1)*.1) -[layer20:Image] -condition = not player.isBassGuitar and not player.starPowerActive and streak >= streakMax -texture = mult.png -xpos = 600 -ypos = 0.7 -xscale = .41 -yscale = .41 -rect = (0.0, 1.0, float(multiplier-1)*0.125 + 1.000, float(multiplier)*0.125 + 1.000) -inPixels = xpos +[layer17:fx0:Replace] +rect = (0.0, 1.0, 0.9, 1.0) +condition = streak >= streakMax -[layer21:Image] -condition = not player.isBassGuitar and not player.starPowerActive and not streak >= streakMax +[layer18:Image] texture = mult.png -xpos = 600 -ypos = 0.7 -xscale = .41 -yscale = .41 +xpos = .5 +ypos = 0.034 +xscale = .5 +yscale = .5 rect = (0.0, 1.0, float(multiplier-1)*0.125, float(multiplier)*0.125) -inPixels = xpos -[layer22:Image] -condition = player.isBassGuitar and player.starPowerActive and streak >= streakMax +[layer18:fx0:Replace] texture = bassgroovemult.png -xpos = 600 -ypos = 0.7 -xscale = .41 -yscale = .41 rect = (0.0, 1.0, float(multiplier-1)*0.083333333, float(multiplier)*0.083333333) -inPixels = xpos +condition = player.isBassGuitar and streak > 30 -[layer23:Image] -condition = player.isBassGuitar and player.starPowerActive and not streak >= streakMax -texture = bassgroovemult.png -xpos = 600 -ypos = 0.7 -xscale = .41 -yscale = .41 -rect = (0.0, 1.0, float(multiplier-1)*0.083333333, float(multiplier)*0.083333333) -inPixels = xpos - -[layer24:Image] -condition = player.isBassGuitar and not player.starPowerActive and streak >= streakMax -texture = bassgroovemult.png -xpos = 600 -ypos = 0.7 -xscale = .41 -yscale = .41 -rect = (0.0, 1.0, float(multiplier-1)*0.083333333 + 1.000, float(multiplier)*0.083333333 + 1.000) -inPixels = xpos - -[layer25:Image] -condition = player.isBassGuitar and not player.starPowerActive and not streak >= streakMax -texture = bassgroovemult.png -xpos = 600 -ypos = 0.7 -xscale = .41 -yscale = .41 -rect = (0.0, 1.0, float(multiplier-1)*0.083333333, float(multiplier)*0.083333333) -inPixels = xpos - -[layer26:Image] -texture = mult2.png -condition = streak > 0 and streak <= 10 -xpos = 600 -ypos = 0.7055 -xscale = .4305 -yscale = .4305 -rect = (0.0, .25, float(streak-1)*.1, float(streak)*.1) -inPixels = xpos - -[layer27:Image] -texture = mult2.png -condition = streak > 10 and streak <= 20 -xpos = 600 -ypos = 0.7055 -xscale = .4305 -yscale = .4305 -rect = (.25, .5, float(streak-1)*.1, float(streak)*.1) -inPixels = xpos - -[layer28:Image] -texture = mult2.png -condition = streak > 20 and streak <= 30 -xpos = 600 -ypos = 0.7055 -xscale = .4305 -yscale = .4305 -rect = (.5, .75, float(streak-1)*.1, float(streak)*.1) -inPixels = xpos - -[layer29:Image] -texture = mult2.png -condition = streak > 30 -xpos = 600 -ypos = 0.7055 -xscale = .4305 -yscale = .4305 -rect = (.75, 1.0, 0.0, 0.10) -inPixels = xpos \ No newline at end of file diff --git a/data/themes/MegaLight/rockmeter/mult2.png b/data/themes/MegaLight/rockmeter/mult2.png index 729d37615..44139e9ac 100644 Binary files a/data/themes/MegaLight/rockmeter/mult2.png and b/data/themes/MegaLight/rockmeter/mult2.png differ diff --git a/src/GameEngine.py b/src/GameEngine.py index f2270e728..d276f2c1e 100644 --- a/src/GameEngine.py +++ b/src/GameEngine.py @@ -832,7 +832,7 @@ def drawStarScore(self, screenwidth, screenheight, xpos, ypos, stars, scale = No def drawImage(self, image, scale = (1.0, -1.0), coord = (0, 0), rot = 0, \ color = (1,1,1,1), rect = (0,1,0,1), stretched = 0, fit = 0, \ - alignment = 1): + alignment = 1, valignment = 1): """ Draws the image/surface to screen @@ -855,6 +855,8 @@ def drawImage(self, image, scale = (1.0, -1.0), coord = (0, 0), rot = 0, \ on the top side (1), bottom side (2), or center point (any other value) of the image @param alignment: Adjusts the texture so the coordinate for x-axis placement can either be on the left side (0), center point (1), or right(2) side of the image + @param valignment: Adjusts the texture so the coordinate for y-axis placement can either be + on the bottom side (0), center point (1), or top(2) side of the image """ width, height = scale @@ -882,6 +884,7 @@ def drawImage(self, image, scale = (1.0, -1.0), coord = (0, 0), rot = 0, \ image.setScale(width, height) image.setPosition(x, y) image.setAlignment(alignment) + image.setVAlignment(valignment) image.setAngle(rot) image.setColor(color) image.draw() diff --git a/src/Rockmeter.py b/src/Rockmeter.py index d6afe3343..1104e3f2c 100644 --- a/src/Rockmeter.py +++ b/src/Rockmeter.py @@ -44,6 +44,8 @@ LEFT = 0 CENTER = 1 RIGHT = 2 +TOP = 0 +BOTTOM = 2 #by making these global for the class, all layers that rely on #these numbers will no longer have to have them be independent per @@ -134,6 +136,7 @@ def updateLayer(self, playerNum): self.condition = bool(eval(self.get("condition", str, "True"))) self.alignment = eval(self.get("alignment", str, "center").upper()) + self.valignment = eval(self.get("valignment", str, "center").upper()) rect = self.rect self.scale[0] *= (rect[1] - rect[0]) @@ -166,19 +169,21 @@ def render(self, visibility, playerNum): for effect in self.effects: effect.update() - coord = self.position - scale = self.scale - rot = self.angle - color = self.engine.theme.hexToColor(self.color) - alignment = self.alignment - drawing = self.drawing - rect = self.rect + coord = self.position + scale = self.scale + rot = self.angle + color = self.engine.theme.hexToColor(self.color) + alignment = self.alignment + valignment = self.valignment + drawing = self.drawing + rect = self.rect #frameX = self.frameX #frameY = self.frameY if self.condition: - self.engine.drawImage(drawing, scale, coord, rot, color, rect, alignment = alignment) + self.engine.drawImage(drawing, scale, coord, rot, color, rect, + alignment = alignment, valignment = valignment) #defines layers that are just font instead of images class FontLayer(Layer): @@ -425,7 +430,8 @@ def __init__(self, layer, section): texture = self.get("texture").strip().split("|") for tex in texture: path = os.path.join("themes", layer.stage.themename, "rockmeter", tex) - self.drawings.append(layer.engine.loadImgDrawing(self, "drawing", drawing)) + drawing = layer.engine.loadImgDrawing(self, None, path) + self.drawings.append(drawing) self.drawings.append(layer.drawing) if not self.get("rect") == None: rects = self.get("rect").split("|") @@ -433,7 +439,6 @@ def __init__(self, layer, section): self.rects.append(eval(rect)) self.rects.append(layer.rect) self.type = "image" - print self.rects elif isinstance(layer, FontLayer): self.font = self.engine.data.fontDict[self.get("font")] self.text = self.get("text").split("|") @@ -453,9 +458,9 @@ def fixScale(self): #this allows you to scale images in relation to pixels instead #of percentage of the size of the image. if "xscale" in self.layer.inPixels: - scale[0] /= self.layer.texture.pixelSize[0] + scale[0] /= self.layer.drawing.pixelSize[0] if "yscale" in self.layer.inPixels: - scale[1] /= self.layer.texture.pixelSize[1] + scale[1] /= self.layer.drawing.pixelSize[1] scale[1] = -scale[1] scale[0] *= w/640.0 @@ -478,7 +483,7 @@ def update(self): self.layer.drawing = self.drawings[i] if len(self.rects) > 1: self.layer.rect = self.rects[i] - self.fixScale() + self.fixScale() break class Rockmeter: diff --git a/src/Stage.py b/src/Stage.py index cedbf10b6..7f45768f8 100644 --- a/src/Stage.py +++ b/src/Stage.py @@ -32,6 +32,7 @@ import os import random #MFH - needed for new stage background handling from Language import _ +import math try: from VideoPlayer import VideoPlayer @@ -41,12 +42,212 @@ import Rockmeter #blazingamer - new 4.0 code for rendering rockmeters through stage.ini + +class Layer(object): + """ + A graphical stage layer that can have a number of animation effects associated with it. + """ + def __init__(self, stage, drawing): + """ + Constructor. + + @param stage: Containing Stage + @param drawing: SvgDrawing for this layer. Make sure this drawing is rendered to + a texture for performance reasons. + """ + self.stage = stage + self.drawing = drawing + self.position = (0.0, 0.0) + self.angle = 0.0 + self.scale = (1.0, 1.0) + self.color = (1.0, 1.0, 1.0, 1.0) + self.srcBlending = GL_SRC_ALPHA + self.dstBlending = GL_ONE_MINUS_SRC_ALPHA + self.transforms = [[1,1], [1,1], 1, [1,1,1,1]] #scale, coord, angle, color + self.effects = [] + + def render(self, visibility): + """ + Render the layer. + + @param visibility: Floating point visibility factor (1 = opaque, 0 = invisibile) + """ + w, h = self.stage.engine.view.geometry[2:4] + v = 1.0 - visibility ** 2 + + color = self.color + + #coordinates are positioned with (0,0) being in the middle of the screen + coord = [w/2 + self.position[0] * w/2, h/2 - self.position[1] * h/2] + if v > .01: + color = [self.color[0], self.color[1], self.color[2], visibility] + scale = [self.scale[0], -self.scale[1]] + rot = self.angle + + self.transforms = [scale, coord, rot, color] + # Blend in all the effects + for effect in self.effects: + effect.apply() + + glBlendFunc(self.srcBlending, self.dstBlending) + self.stage.engine.drawImage(self.drawing, self.transforms[0], self.transforms[1], + self.transforms[2], self.transforms[3]) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + +class Effect(object): + """ + An animationn effect that can be attached to a Layer. + """ + def __init__(self, layer, options): + """ + Constructor. + + @param layer: Layer to attach this effect to. + @param options: Effect options (default in parens): + intensity - Floating point effect intensity (1.0) + trigger - Effect trigger, one of "none", "beat", + "quarterbeat", "pick", "miss" ("none") + period - Trigger period in ms (200.0) + delay - Trigger delay in periods (0.0) + profile - Trigger profile, one of "step", "linstep", + "smoothstep" + """ + self.layer = layer + self.stage = layer.stage + self.intensity = float(options.get("intensity", 1.0)) + self.trigger = getattr(self, "trigger" + options.get("trigger", "none").capitalize()) + self.period = float(options.get("period", 500.0)) + self.delay = float(options.get("delay", 0.0)) + self.triggerProf = getattr(self, options.get("profile", "linstep")) + + def apply(self): + pass + + def triggerNone(self): + return 0.0 + + def triggerBeat(self): + if not self.stage.lastBeatPos: + return 0.0 + t = self.stage.pos - self.delay * self.stage.beatPeriod - self.stage.lastBeatPos + return self.intensity * (1.0 - self.triggerProf(0, self.stage.beatPeriod, t)) + + def triggerQuarterbeat(self): + if not self.stage.lastQuarterBeatPos: + return 0.0 + t = self.stage.pos - self.delay * (self.stage.beatPeriod / 4) - self.stage.lastQuarterBeatPos + return self.intensity * (1.0 - self.triggerProf(0, self.stage.beatPeriod / 4, t)) + + def triggerPick(self): + if not self.stage.lastPickPos: + return 0.0 + t = self.stage.pos - self.delay * self.period - self.stage.lastPickPos + return self.intensity * (1.0 - self.triggerProf(0, self.period, t)) + + def triggerMiss(self): + if not self.stage.lastMissPos: + return 0.0 + t = self.stage.pos - self.delay * self.period - self.stage.lastMissPos + return self.intensity * (1.0 - self.triggerProf(0, self.period, t)) + + def step(self, threshold, x): + return (x > threshold) and 1 or 0 + + def linstep(self, min, max, x): + if x < min: + return 0 + if x > max: + return 1 + return (x - min) / (max - min) + + def smoothstep(self, min, max, x): + if x < min: + return 0 + if x > max: + return 1 + def f(x): + return -2 * x**3 + 3*x**2 + return f((x - min) / (max - min)) + + def sinstep(self, min, max, x): + return math.cos(math.pi * (1.0 - self.linstep(min, max, x))) + + def getNoteColor(self, note): + if note >= len(self.stage.engine.theme.noteColors) - 1: + return self.stage.engine.theme.noteColors[-1] + elif note <= 0: + return self.stage.engine.theme.noteColors[0] + f2 = note % 1.0 + f1 = 1.0 - f2 + c1 = self.stage.engine.theme.noteColors[int(note)] + c2 = self.stage.engine.theme.noteColors[int(note) + 1] + return (c1[0] * f1 + c2[0] * f2, \ + c1[1] * f1 + c2[1] * f2, \ + c1[2] * f1 + c2[2] * f2) + +class LightEffect(Effect): + def __init__(self, layer, options): + Effect.__init__(self, layer, options) + self.lightNumber = int(options.get("light_number", 0)) + self.ambient = float(options.get("ambient", 0.5)) + self.contrast = float(options.get("contrast", 0.5)) + + def apply(self): + if len(self.stage.averageNotes) < self.lightNumber + 2: + self.layer.color = (0.0, 0.0, 0.0, 0.0) + return + + t = self.trigger() + t = self.ambient + self.contrast * t + c = self.getNoteColor(self.stage.averageNotes[self.lightNumber]) + self.layer.transforms[3] = (c[0] * t, c[1] * t, c[2] * t, self.intensity) + +class RotateEffect(Effect): + def __init__(self, layer, options): + Effect.__init__(self, layer, options) + self.angle = math.pi / 180.0 * float(options.get("angle", 45)) + + def apply(self): + if not self.stage.lastMissPos: + return + + t = self.trigger() + self.layer.transforms[2] = t*self.angle + +class WiggleEffect(Effect): + def __init__(self, layer, options): + Effect.__init__(self, layer, options) + self.freq = float(options.get("frequency", 6)) + self.xmag = float(options.get("xmagnitude", 0.1)) + self.ymag = float(options.get("ymagnitude", 0.1)) + + def apply(self): + t = self.trigger() + + w, h = self.stage.engine.view.geometry[2:4] + p = t * 2 * math.pi * self.freq + s, c = t * math.sin(p), t * math.cos(p) + self.layer.transforms[1][0] += self.xmag * w * s + self.layer.transforms[1][1] += self.ymag * h * c + +class ScaleEffect(Effect): + def __init__(self, layer, options): + Effect.__init__(self, layer, options) + self.xmag = float(options.get("xmagnitude", .1)) + self.ymag = float(options.get("ymagnitude", .1)) + + def apply(self): + t = self.trigger() + self.layer.transforms[0] = (1.0 + self.xmag * t, -1.0 + self.ymag * t) + class Stage(object): def __init__(self, guitarScene, configFileName): self.scene = guitarScene self.engine = guitarScene.engine self.config = Config.MyConfigParser() - self.layers = [] + self.backgroundLayers = [] + self.foregroundLayers = [] + self.textures = {} self.reset() @@ -85,13 +286,74 @@ def __init__(self, guitarScene, configFileName): # evilynux - Improved stage error handling self.themename = self.engine.data.themeLabel - self.path = os.path.join("themes",self.themename,"stages") + self.path = os.path.join("themes",self.themename,"backgrounds") self.pathfull = self.engine.getPath(self.path) if not os.path.exists(self.pathfull): # evilynux Log.warn("Stage folder does not exist: %s" % self.pathfull) self.mode = 1 # Fallback to song-specific stage suffix = ".jpg" + self.loadLayers(configFileName) + + def loadLayers(self, configFileName): + self.config.read(configFileName) + path = os.path.join("themes", self.themename, "stage") + + # Build the layers + for i in range(32): + section = "layer%d" % i + if self.config.has_section(section): + def get(value, type = str, default = None): + if self.config.has_option(section, value): + return type(self.config.get(section, value)) + return default + + xres = get("xres", int, 256) + yres = get("yres", int, 256) + texture = get("texture") + + try: + drawing = self.textures[texture] + except KeyError: + drawing = self.engine.loadImgDrawing(self, None, os.path.join(path, texture), textureSize = (xres, yres)) + self.textures[texture] = drawing + + layer = Layer(self, drawing) + + layer.position = (get("xpos", float, 0.0), get("ypos", float, 0.0)) + layer.scale = (get("xscale", float, 1.0), get("yscale", float, 1.0)) + layer.angle = math.pi * get("angle", float, 0.0) / 180.0 + layer.srcBlending = globals()["GL_%s" % get("src_blending", str, "src_alpha").upper()] + layer.dstBlending = globals()["GL_%s" % get("dst_blending", str, "one_minus_src_alpha").upper()] + layer.color = (get("color_r", float, 1.0), get("color_g", float, 1.0), get("color_b", float, 1.0), get("color_a", float, 1.0)) + + # Load any effects + fxClasses = { + "light": LightEffect, + "rotate": RotateEffect, + "wiggle": WiggleEffect, + "scale": ScaleEffect, + } + + for j in range(32): + fxSection = "layer%d:fx%d" % (i, j) + if self.config.has_section(fxSection): + type = self.config.get(fxSection, "type") + + if not type in fxClasses: + continue + + options = self.config.options(fxSection) + options = dict([(opt, self.config.get(fxSection, opt)) for opt in options]) + + fx = fxClasses[type](layer, options) + layer.effects.append(fx) + + if get("foreground", int): + self.foregroundLayers.append(layer) + else: + self.backgroundLayers.append(layer) + def loadVideo(self, libraryName, songName, songVideo = None, songVideoStartTime = None, songVideoEndTime = None): if not videoAvailable: @@ -336,11 +598,6 @@ def triggerBeat(self, pos, beat): self.beat = beat self.averageNotes = self.averageNotes[-4:] + self.averageNotes[-1:] - def renderLayers(self, layers, visibility): - with self.engine.view.orthogonalProjection(normalize = True): - for layer in layers: - layer.render(visibility) - def run(self, pos, period): self.pos = pos self.beatPeriod = period @@ -354,10 +611,16 @@ def run(self, pos, period): if beat > self.beat: self.triggerBeat(pos, beat) + def renderLayers(self, layers, visibility): + if self.mode != 3: + with self.engine.view.orthogonalProjection(normalize = True): + for layer in layers: + layer.render(visibility) + def render(self, visibility): if self.mode != 3: self.renderBackground() - self.renderLayers(self.layers, visibility) + self.renderLayers(self.backgroundLayers, visibility) if shaders.enable("stage"): height = 0.0 for i in shaders.var["color"].keys(): @@ -377,6 +640,5 @@ def render(self, visibility): shaders.disable() self.scene.renderGuitar() + self.renderLayers(self.foregroundLayers, visibility) self.rockmeter.render(visibility) - - diff --git a/src/Svg.py b/src/Svg.py index dcac3b4d4..c8ca5c1b0 100644 --- a/src/Svg.py +++ b/src/Svg.py @@ -38,7 +38,6 @@ class SvgContext(object): def __init__(self, geometry): self.geometry = geometry - self.transform = SvgTransform() self.setGeometry(geometry) self.setProjection(geometry) glMatrixMode(GL_MODELVIEW) @@ -60,35 +59,6 @@ def clear(self, r = 0, g = 0, b = 0, a = 0): glClearColor(r, g, b, a) glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - -class SvgTransform(object): - def __init__(self, baseTransform = None): - self.reset() - if baseTransform is not None: - self.ops = baseTransform.ops[:] - - def transform(self, transform): - self.ops.extend(transform.ops) - - def reset(self): - self.ops = [] - - def translate(self, dx, dy): - # The old code did this with a matrix addition, not a multiplication. - # We get the same effect by doing the translations before anything else. - self.ops.insert(0, lambda: glTranslatef(dx, dy, 0)) - - def rotate(self, angle): - self.ops.append(lambda: glRotatef(math.degrees(angle), 0.0, 0.0, 1.0)) - - def scale(self, sx, sy): - self.ops.append(lambda: glScalef(sx, sy, 1.0)) - - def applyGL(self): - for op in self.ops: - op() - - class ImgDrawing(object): def __init__(self, context, ImgData): self.ImgData = None @@ -115,13 +85,14 @@ def __init__(self, context, ImgData): Log.error(e) raise RuntimeError(e) - self.pixelSize = self.texture.pixelSize - self.position = [0.0,0.0] - self.scale = [1.0,1.0] - self.angle = 0 - self.color = (1.0,1.0,1.0) - self.rect = (0,1,0,1) - self.shift = -.5 + self.pixelSize = self.texture.pixelSize #the size of the image in pixels (from texture) + self.position = [0.0,0.0] #position of the image in the viewport + self.scale = [1.0,1.0] #percentage scaling + self.angle = 0 #angle of rotation (degrees) + self.color = (1.0,1.0,1.0,1.0) #glColor rgba + self.rect = (0,1,0,1) #texture mapping coordinates + self.shift = -.5 #horizontal alignment + self.vshift = -.5 #vertical alignment self.createArrays() @@ -196,6 +167,14 @@ def setAlignment(self, alignment): elif alignment == 2:#right self.shift = -1.0 + def setVAlignment(self, alignment): + if alignment == 0: #bottom + self.vshift = 0 + elif alignment == 1:#center + self.vshift = -.5 + elif alignment == 2:#top + self.vshift = -1.0 + def setColor(self, color): if len(color) == 3: color = (color[0], color[1], color[2], 1.0) @@ -214,9 +193,10 @@ def draw(self): glTranslate(self.position[0], self.position[1], 0.0) glScalef(self.scale[0], self.scale[1], 1.0) glRotatef(self.angle, 0, 0, 1) - + glScalef(self.pixelSize[0], self.pixelSize[1], 1) - glTranslatef(self.shift, -.5, 0) + glTranslatef(self.shift, self.vshift, 0) + glColor4f(*self.color) glEnable(GL_TEXTURE_2D)