299 lines
10 KiB
JavaScript
299 lines
10 KiB
JavaScript
/*
|
|
* Copyright (C) 2008 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
/**
|
|
* @fileoverview This implements a Photoshop script that can be used to generate
|
|
* collision information for the AndouKun game engine. This tool walks over
|
|
* each path in the current document and generates a list of edges and normals
|
|
* in a new document. It is intended to be used on a file containing
|
|
* graphical representations of the collision tiles used by the engine. Each
|
|
* path in the file must be closed and may not contain any curved points
|
|
* (the tool assumes that the line between any two points in a given path is
|
|
* straight). Only one shape may be contained per path layer (each path must go
|
|
* in its own path layer). This tool can also output a graphical version of its
|
|
* edge calculation for debugging purposes.
|
|
*/
|
|
|
|
/* If set to true, the computation will be rendered graphically to the output
|
|
file */
|
|
var drawOutput = false;
|
|
/* If true, the computation will be printed in a text layer in the
|
|
output file.*/
|
|
var printOutput = true;
|
|
|
|
// Back up the ruler units that this file uses before switching to pixel units.
|
|
var defaultRulerUnits = app.preferences.rulerUnits;
|
|
app.preferences.rulerUnits = Units.PIXELS;
|
|
|
|
var tileSizeX = prompt("Tile pixel width:");
|
|
var tileSizeY = prompt("Tile pixel height:");
|
|
|
|
var documentWidth = app.activeDocument.width;
|
|
var documentHeight = app.activeDocument.height;
|
|
|
|
var tilesPerRow = documentWidth / tileSizeX;
|
|
var tilesPerColumn = documentHeight / tileSizeY;
|
|
|
|
var tiles = new Array();
|
|
tiles.length = tilesPerRow * tilesPerColumn;
|
|
|
|
// Walk the list of paths and extract edges and normals. Store these in
|
|
// an array by tile.
|
|
var pathList = app.activeDocument.pathItems;
|
|
for (pathIndex = 0; pathIndex < pathList.length; pathIndex++) {
|
|
var main_path = pathList[pathIndex];
|
|
if (main_path) {
|
|
var itemList = main_path.subPathItems;
|
|
if (!itemList) {
|
|
alert("Path has no sub items!");
|
|
} else {
|
|
for (var x = 0; x < itemList.length; x++) {
|
|
var item = itemList[x];
|
|
var points = item.pathPoints;
|
|
var tile = new Object;
|
|
tile.edges = new Array();
|
|
|
|
var totalX = 0;
|
|
var totalY = 0;
|
|
for (var y = 0; y < points.length; y++) {
|
|
var firstPoint = points[y];
|
|
var lastPoint = points[(y + 1) % points.length];
|
|
|
|
var edge = new Object;
|
|
|
|
edge.startX = firstPoint.anchor[0];
|
|
edge.startY = firstPoint.anchor[1];
|
|
|
|
edge.endX = lastPoint.anchor[0];
|
|
edge.endY = lastPoint.anchor[1];
|
|
|
|
var normalX = -(edge.endY - edge.startY);
|
|
var normalY = edge.endX - edge.startX;
|
|
|
|
var normalLength = Math.sqrt((normalX * normalX) + (normalY * normalY));
|
|
normalX /= normalLength;
|
|
normalY /= normalLength;
|
|
|
|
edge.normalX = normalX;
|
|
edge.normalY = normalY;
|
|
|
|
if (normalX == 0 && normalY == 0) {
|
|
alert("Zero length normal calculated at path " + pathIndex);
|
|
}
|
|
|
|
var normalLength2 = Math.sqrt((normalX * normalX) + (normalY * normalY));
|
|
if (normalLength2 > 1 || normalLength2 < 0.9) {
|
|
alert("Normal of invalid length (" + normalLength2 + ") found at path " + pathIndex);
|
|
}
|
|
|
|
totalX += edge.endX;
|
|
totalY += edge.endY;
|
|
|
|
var width = edge.endX - edge.startX;
|
|
var height = edge.endY - edge.startY;
|
|
|
|
edge.centerX = edge.endX - (width / 2);
|
|
edge.centerY = edge.endY - (height / 2);
|
|
|
|
tile.edges.push(edge);
|
|
}
|
|
|
|
totalX /= points.length;
|
|
totalY /= points.length;
|
|
tile.centerX = totalX;
|
|
tile.centerY = totalY;
|
|
|
|
var column = Math.floor(tile.centerX / tileSizeX);
|
|
var row = Math.floor(tile.centerY / tileSizeY);
|
|
|
|
tile.xOffset = column * tileSizeX;
|
|
tile.yOffset = row * tileSizeY;
|
|
|
|
tile.centerX -= tile.xOffset;
|
|
tile.centerY -= tile.yOffset;
|
|
|
|
var tileIndex = Math.floor(row * tilesPerRow + column);
|
|
tiles[tileIndex] = tile;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var outputString = "";
|
|
|
|
// For each tile print the edges to a string.
|
|
for (var x = 0; x < tiles.length; x++) {
|
|
if (tiles[x]) {
|
|
var tile = tiles[x];
|
|
for (var y = 0; y < tile.edges.length; y++) {
|
|
var edge = tile.edges[y];
|
|
|
|
// convert to tile space
|
|
edge.startX -= tile.xOffset;
|
|
edge.startY -= tile.yOffset;
|
|
edge.endX -= tile.xOffset;
|
|
edge.endY -= tile.yOffset;
|
|
edge.centerX -= tile.xOffset;
|
|
edge.centerY -= tile.yOffset;
|
|
|
|
// The normals that we calculated previously might be facing the wrong
|
|
// direction. Detect this case and correct it by checking to see if
|
|
// adding the normal to a point on the edge moves the point closer or
|
|
// further from the center of the shape.
|
|
if (Math.abs(edge.centerX - tile.centerX) >
|
|
Math.abs((edge.centerX + edge.normalX) - tile.centerX)) {
|
|
edge.normalX *= -1;
|
|
edge.normalY *= -1;
|
|
}
|
|
|
|
if (Math.abs(edge.centerY - tile.centerY) >
|
|
Math.abs((edge.centerY + edge.normalY) - tile.centerY)) {
|
|
edge.normalX *= -1;
|
|
edge.normalY *= -1;
|
|
}
|
|
|
|
|
|
// Convert to left-handed GL space (the origin is at the bottom-left).
|
|
edge.normalY *= -1;
|
|
edge.startY = tileSizeY - edge.startY;
|
|
edge.endY = tileSizeY - edge.endY;
|
|
edge.centerY = tileSizeY - edge.centerY;
|
|
|
|
outputString += x + ":" + Math.floor(edge.startX) + "," +
|
|
Math.floor(edge.startY) + ":" + Math.floor(edge.endX) + "," +
|
|
Math.floor(edge.endY) + ":" + edge.normalX + "," + edge.normalY +
|
|
"\r";
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (outputString.length > 0) {
|
|
|
|
var newDoc = app.documents.add(600, 700, 72.0, "Edge Output",
|
|
NewDocumentMode.RGB);
|
|
|
|
if (drawOutput) {
|
|
// Render the edges and normals to the new document.
|
|
var pathLayer = newDoc.artLayers.add();
|
|
newDoc.activeLayer = pathLayer;
|
|
|
|
// draw the edges to make sure everything works
|
|
var black = new SolidColor;
|
|
black.rgb.red = 0;
|
|
black.rgb.blue = 0;
|
|
black.rgb.green = 0;
|
|
|
|
var redColor = new SolidColor;
|
|
redColor.rgb.red = 255;
|
|
redColor.rgb.blue = 0;
|
|
redColor.rgb.green = 0;
|
|
|
|
var greenColor = new SolidColor;
|
|
greenColor.rgb.red = 0;
|
|
greenColor.rgb.blue = 0;
|
|
greenColor.rgb.green = 255;
|
|
|
|
var blueColor = new SolidColor;
|
|
blueColor.rgb.red = 0;
|
|
blueColor.rgb.blue = 255;
|
|
blueColor.rgb.green = 0;
|
|
|
|
var lineIndex = 0;
|
|
for (var x = 0; x < tiles.length; x++) {
|
|
if (tiles[x]) {
|
|
var tile = tiles[x];
|
|
var lineArray = new Array();
|
|
var offsetX = Math.floor(x % tilesPerRow) * tileSizeX;
|
|
var offsetY = Math.floor(x / tilesPerRow) * tileSizeY;
|
|
|
|
for (var y = 0; y < tile.edges.length; y++) {
|
|
var edge = tile.edges[y];
|
|
|
|
lineArray[y] = Array(offsetX + edge.startX, offsetY + edge.startY);
|
|
}
|
|
|
|
// I tried to do this by stroking paths, but the documentation
|
|
// provided by Adobe is faulty (their sample code doesn't run). The
|
|
// same thing can be accomplished with selections instead.
|
|
newDoc.selection.select(lineArray);
|
|
newDoc.selection.stroke(black, 2);
|
|
|
|
for (var y = 0; y < tile.edges.length; y++) {
|
|
var edge = tile.edges[y];
|
|
|
|
var normalX = Math.round(tile.centerX +
|
|
(edge.normalX * (tileSizeX / 2)));
|
|
var normalY = Math.round(tile.centerY +
|
|
(edge.normalY * (tileSizeY / 2)));
|
|
|
|
var tileCenterArray = new Array();
|
|
tileCenterArray[0] = new Array(offsetX + tile.centerX - 1,
|
|
offsetY + tile.centerY - 1);
|
|
tileCenterArray[1] = new Array(offsetX + tile.centerX - 1,
|
|
offsetY + tile.centerY + 1);
|
|
tileCenterArray[2] = new Array(offsetX + tile.centerX + 1,
|
|
offsetY + tile.centerY + 1);
|
|
tileCenterArray[3] = new Array(offsetX + tile.centerX + 1,
|
|
offsetY + tile.centerY - 1);
|
|
tileCenterArray[4] = new Array(offsetX + normalX - 1,
|
|
offsetY + normalY - 1);
|
|
tileCenterArray[5] = new Array(offsetX + normalX + 1,
|
|
offsetY + normalY + 1);
|
|
tileCenterArray[6] = new Array(offsetX + tile.centerX,
|
|
offsetY + tile.centerY);
|
|
|
|
newDoc.selection.select(tileCenterArray);
|
|
newDoc.selection.fill(redColor);
|
|
|
|
var centerArray = new Array();
|
|
centerArray[0] = new Array(offsetX + edge.centerX - 1,
|
|
offsetY + edge.centerY - 1);
|
|
centerArray[1] = new Array(offsetX + edge.centerX - 1,
|
|
offsetY + edge.centerY + 1);
|
|
centerArray[2] = new Array(offsetX + edge.centerX + 1,
|
|
offsetY + edge.centerY + 1);
|
|
centerArray[3] = new Array(offsetX + edge.centerX + 1,
|
|
offsetY + edge.centerY - 1);
|
|
|
|
newDoc.selection.select(centerArray);
|
|
newDoc.selection.fill(greenColor);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (printOutput) {
|
|
var textLayer = newDoc.artLayers.add();
|
|
textLayer.kind = LayerKind.TEXT;
|
|
textLayer.textItem.contents = outputString;
|
|
}
|
|
}
|
|
|
|
preferences.rulerUnits = defaultRulerUnits;
|
|
|
|
// Convenience function for clamping negative values to zero. Trying to select
|
|
// areas outside the canvas causes Bad Things.
|
|
function clamp(input) {
|
|
if (input < 0) {
|
|
return 0;
|
|
}
|
|
return input;
|
|
}
|