/* This file is part of jpcsp. Jpcsp is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Jpcsp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Jpcsp. If not, see <http://www.gnu.org/licenses/>. */ package jpcsp.format.rco.object; import static java.lang.Math.round; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.awt.image.IndexColorModel; import java.awt.image.RescaleOp; import jpcsp.format.RCO.RCOEntry; import jpcsp.format.rco.IDisplay; import jpcsp.format.rco.ObjectField; import jpcsp.format.rco.anim.AbstractAnimAction; import jpcsp.format.rco.type.EventType; import jpcsp.format.rco.type.FloatType; import jpcsp.format.rco.type.IntType; import jpcsp.format.rco.vsmx.interpreter.VSMXArray; import jpcsp.format.rco.vsmx.interpreter.VSMXBaseObject; import jpcsp.format.rco.vsmx.interpreter.VSMXInterpreter; import jpcsp.format.rco.vsmx.interpreter.VSMXNativeObject; import jpcsp.format.rco.vsmx.interpreter.VSMXNumber; import jpcsp.format.rco.vsmx.objects.BaseNativeObject; import jpcsp.format.rco.vsmx.objects.Resource; public class BasePositionObject extends BaseObject implements IDisplay { @ObjectField(order = 101) public FloatType posX; @ObjectField(order = 102) public FloatType posY; @ObjectField(order = 103) public FloatType posZ; @ObjectField(order = 104) public FloatType redScale; @ObjectField(order = 105) public FloatType greenScale; @ObjectField(order = 106) public FloatType blueScale; @ObjectField(order = 107) public FloatType alphaScale; @ObjectField(order = 108) public FloatType width; @ObjectField(order = 109) public FloatType height; @ObjectField(order = 110) public FloatType depth; @ObjectField(order = 111) public FloatType scaleWidth; @ObjectField(order = 112) public FloatType scaleHeight; @ObjectField(order = 113) public FloatType scaleDepth; @ObjectField(order = 114) public IntType iconOffset; @ObjectField(order = 115) public EventType onInit; public float rotateX; public float rotateY; public float rotateAngle; public float animX; public float animY; public float animZ; private class AnimRotateAction extends AbstractAnimAction { private float angle; public AnimRotateAction(float x, float y, float angle, int duration) { super(duration); rotateX = x; rotateY = y; this.angle = angle; } @Override protected void anim(float step) { rotateAngle = angle * step; if (log.isDebugEnabled()) { log.debug(String.format("AnimRotateAction to angle=%f", rotateAngle)); } onDisplayUpdated(); } } private class AnimPosAction extends AbstractAnimAction { private float x; private float y; private float z; private float startX; private float startY; private float startZ; public AnimPosAction(float x, float y, float z, int duration) { super(duration); this.x = x; this.y = y; this.z = z; startX = posX.getFloatValue(); startY = posY.getFloatValue(); startZ = posZ.getFloatValue(); } @Override protected void anim(float step) { posX.setFloatValue(interpolate(startX, x, step)); posY.setFloatValue(interpolate(startY, y, step)); posZ.setFloatValue(interpolate(startZ, z, step)); if (log.isDebugEnabled()) { log.debug(String.format("AnimPosAction from (%f,%f,%f) to (%f,%f,%f)", startX, startY, startZ, posX.getFloatValue(), posY.getFloatValue(), posZ.getFloatValue())); } onDisplayUpdated(); } } private class AnimScaleAction extends AbstractAnimAction { private float width; private float height; private float depth; private float startWidth; private float startHeight; private float startDepth; public AnimScaleAction(float width, float height, float depth, int duration) { super(duration); this.width = width; this.height = height; this.depth = depth; startWidth = scaleWidth.getFloatValue(); startHeight = scaleHeight.getFloatValue(); startDepth = scaleDepth.getFloatValue(); } @Override protected void anim(float step) { scaleWidth.setFloatValue(interpolate(startWidth, width, step)); scaleHeight.setFloatValue(interpolate(startHeight, height, step)); scaleDepth.setFloatValue(interpolate(startDepth, depth, step)); if (log.isDebugEnabled()) { log.debug(String.format("AnimScaleAction scaling from (%f,%f,%f) to (%f,%f,%f)", startWidth, startHeight, startDepth, scaleWidth.getFloatValue(), scaleHeight.getFloatValue(), scaleDepth.getFloatValue())); } onDisplayUpdated(); } } private class AnimColorAction extends AbstractAnimAction { private float red; private float green; private float blue; private float alpha; private float startRed; private float startGreen; private float startBlue; private float startAlpha; public AnimColorAction(float red, float green, float blue, float alpha, int duration) { super(duration); this.red = red; this.green = green; this.blue = blue; this.alpha = alpha; startRed = redScale.getFloatValue(); startGreen = greenScale.getFloatValue(); startBlue = blueScale.getFloatValue(); startAlpha = alphaScale.getFloatValue(); } @Override protected void anim(float step) { redScale.setFloatValue(interpolate(startRed , red , step)); greenScale.setFloatValue(interpolate(startGreen, green, step)); blueScale.setFloatValue(interpolate(startBlue , blue , step)); alphaScale.setFloatValue(interpolate(startAlpha, alpha, step)); if (log.isDebugEnabled()) { log.debug(String.format("AnimColorAction scaling from (%f,%f,%f,%f) to (%f,%f,%f,%f)", startRed, startGreen, startBlue, startAlpha, red, green, blue, alpha)); } onDisplayUpdated(); } } @Override public int getWidth() { float w = width.getFloatValue(); if (w == 0f) { BufferedImage image = getImage(); if (image != null) { w = (float) image.getWidth(); } } return Math.round(w * scaleWidth.getFloatValue()); } @Override public int getHeight() { float h = height.getFloatValue(); if (h == 0f) { BufferedImage image = getImage(); if (image != null) { h = (float) image.getHeight(); } } return Math.round(h * scaleHeight.getFloatValue()); } @Override public BufferedImage getImage() { BufferedImage image = null; if (getObject().hasPropertyValue(Resource.textureName)) { VSMXBaseObject textureObject = getObject().getPropertyValue(Resource.textureName); if (textureObject instanceof VSMXNativeObject) { BaseNativeObject texture = ((VSMXNativeObject) textureObject).getObject(); if (texture instanceof ImageObject) { image = ((ImageObject) texture).getImage(); } } } return image; } @Override public int getX() { int parentX = 0; if (getParent() instanceof BasePositionObject) { parentX = ((BasePositionObject) getParent()).getX(); } return parentX + posX.getIntValue() + round(animX); } @Override public int getY() { int parentY = 0; if (getParent() instanceof BasePositionObject) { parentY = ((BasePositionObject) getParent()).getY(); } return parentY + posY.getIntValue() + round(animY); } @Override public float getAlpha() { return alphaScale.getFloatValue(); } @Override public BufferedImage getAnimImage() { BufferedImage image = getImage(); if (image == null) { return image; } if (image.getColorModel() instanceof IndexColorModel) { // Cannot rescale colors on an indexed image } else { float[] scales = { redScale.getFloatValue(), greenScale.getFloatValue(), blueScale.getFloatValue(), alphaScale.getFloatValue() }; float[] offsets = { 0f, 0f, 0f, 0f }; RescaleOp colorRescale = new RescaleOp(scales, offsets, null); image = colorRescale.filter(image, null); } if (rotateAngle != 0f) { if (log.isDebugEnabled()) { log.debug(String.format("Rotating image at (%f,%f) by %f", rotateX, rotateY, rotateAngle)); } AffineTransform rotation = new AffineTransform(); rotation.rotate(-rotateAngle, rotateX + image.getWidth() / 2, rotateY + image.getHeight() / 2); AffineTransformOp op = new AffineTransformOp(rotation, AffineTransformOp.TYPE_BILINEAR); image = op.filter(image, null); } return image; } public void setPos(VSMXBaseObject object, VSMXBaseObject posX, VSMXBaseObject posY) { if (log.isDebugEnabled()) { log.debug(String.format("setPos(%s, %s)", posX, posY)); } this.posX.setFloatValue(posX.getFloatValue()); this.posY.setFloatValue(posY.getFloatValue()); } public void setPos(VSMXBaseObject object, VSMXBaseObject posX, VSMXBaseObject posY, VSMXBaseObject posZ) { if (log.isDebugEnabled()) { log.debug(String.format("setPos(%s, %s, %s)", posX, posY, posZ)); } this.posX.setFloatValue(posX.getFloatValue()); this.posY.setFloatValue(posY.getFloatValue()); this.posZ.setFloatValue(posZ.getFloatValue()); } public VSMXBaseObject getPos(VSMXBaseObject object) { VSMXInterpreter interpreter = object.getInterpreter(); VSMXArray pos = new VSMXArray(interpreter, 3); pos.setPropertyValue(0, new VSMXNumber(interpreter, posX.getFloatValue())); pos.setPropertyValue(1, new VSMXNumber(interpreter, posY.getFloatValue())); pos.setPropertyValue(2, new VSMXNumber(interpreter, posZ.getFloatValue())); if (log.isDebugEnabled()) { log.debug(String.format("getPos() returning %s", pos)); } return pos; } public void setRotate(VSMXBaseObject object, VSMXBaseObject x, VSMXBaseObject y, VSMXBaseObject rotationRads) { if (log.isDebugEnabled()) { log.debug(String.format("setRotate(%s, %s, %s)", x, y, rotationRads)); } rotateX = x.getFloatValue(); rotateY = y.getFloatValue(); rotateAngle = rotationRads.getFloatValue(); } public VSMXBaseObject getColor(VSMXBaseObject object) { VSMXInterpreter interpreter = object.getInterpreter(); VSMXArray color = new VSMXArray(interpreter, 4); color.setPropertyValue(0, new VSMXNumber(interpreter, redScale.getFloatValue())); color.setPropertyValue(1, new VSMXNumber(interpreter, greenScale.getFloatValue())); color.setPropertyValue(2, new VSMXNumber(interpreter, blueScale.getFloatValue())); color.setPropertyValue(3, new VSMXNumber(interpreter, alphaScale.getFloatValue())); if (log.isDebugEnabled()) { log.debug(String.format("getColor() returning %s", color)); } return color; } public void setColor(VSMXBaseObject object, VSMXBaseObject red, VSMXBaseObject green, VSMXBaseObject blue, VSMXBaseObject alpha) { if (log.isDebugEnabled()) { log.debug(String.format("setColor(%s, %s, %s, %s)", red, green, blue, alpha)); } redScale.setFloatValue(red.getFloatValue()); greenScale.setFloatValue(green.getFloatValue()); blueScale.setFloatValue(blue.getFloatValue()); alphaScale.setFloatValue(alpha.getFloatValue()); onDisplayUpdated(); } public void animColor(VSMXBaseObject object, VSMXBaseObject red, VSMXBaseObject green, VSMXBaseObject blue, VSMXBaseObject alpha, VSMXBaseObject duration) { if (log.isDebugEnabled()) { log.debug(String.format("animColor(%s, %s, %s, %s, %s)", red, green, blue, alpha, duration)); } AnimColorAction action = new AnimColorAction(red.getFloatValue(), green.getFloatValue(), blue.getFloatValue(), alpha.getFloatValue(), duration.getIntValue()); getScheduler().addAction(action); } public void setScale(VSMXBaseObject object, VSMXBaseObject width, VSMXBaseObject height, VSMXBaseObject depth) { if (log.isDebugEnabled()) { log.debug(String.format("setScale(%s, %s, %s)", width, height, depth)); } // TODO To be implemented onDisplayUpdated(); } public void animScale(VSMXBaseObject object, VSMXBaseObject width, VSMXBaseObject height, VSMXBaseObject depth, VSMXBaseObject duration) { if (log.isDebugEnabled()) { log.debug(String.format("animScale(%s, %s, %s, %s)", width, height, depth, duration)); } AnimScaleAction action = new AnimScaleAction(width.getFloatValue(), height.getFloatValue(), depth.getFloatValue(), duration.getIntValue()); getScheduler().addAction(action); } public void animPos(VSMXBaseObject object, VSMXBaseObject x, VSMXBaseObject y, VSMXBaseObject z, VSMXBaseObject duration) { if (log.isDebugEnabled()) { log.debug(String.format("animPos from (%s,%s,%s) to (%s, %s, %s), duration=%s", posX, posY, posZ, x, y, z, duration)); } AnimPosAction action = new AnimPosAction(x.getFloatValue(), y.getFloatValue(), z.getFloatValue(), duration.getIntValue()); getScheduler().addAction(action); } public void animRotate(VSMXBaseObject object, VSMXBaseObject x, VSMXBaseObject y, VSMXBaseObject rotationRads, VSMXBaseObject duration) { if (log.isDebugEnabled()) { log.debug(String.format("animRotate(%s, %s, %s, %s)", x, y, rotationRads, duration)); } AnimRotateAction action = new AnimRotateAction(x.getFloatValue(), y.getFloatValue(), rotationRads.getFloatValue(), duration.getIntValue()); getScheduler().addAction(action); } public void setFocus() { if (log.isDebugEnabled()) { log.debug(String.format("setFocus()")); } if (display != null) { display.setFocus(getObject()); } if (controller != null) { controller.setFocus(this); } } public void setFocus(VSMXBaseObject object) { setFocus(); } public void focusOut() { } public void setSize(VSMXBaseObject object, VSMXBaseObject width, VSMXBaseObject height, VSMXBaseObject depth) { if (log.isDebugEnabled()) { log.debug(String.format("setSize(%s, %s, %s)", width, height, depth)); } this.width.setFloatValue(width.getFloatValue()); this.height.setFloatValue(height.getFloatValue()); this.depth.setFloatValue(depth.getFloatValue()); onDisplayUpdated(); } public void onUp() { } public void onDown() { } public void onLeft() { } public void onRight() { } public void onPush() { } @Override public VSMXBaseObject createVSMXObject(VSMXInterpreter interpreter, VSMXBaseObject parent, RCOEntry entry) { VSMXBaseObject object = super.createVSMXObject(interpreter, parent, entry); BufferedImage image = getImage(); if (image != null) { object.setPropertyValue(Resource.textureName, new VSMXNativeObject(interpreter, new ImageObject(image))); } return object; } }