this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix map editor importing

clover 21eb317b fb81dbdc

+525 -285
+21
CHANGELOG.md
··· 2 2 3 3 All notable changes to this project will be documented in this file. 4 4 5 + ## [0.10.0] - 2025-03-13 6 + 7 + ### Added 8 + - (Sprite Editor) Undo/redo for all actions 9 + - (Sprite Editor) Popup window for selecting rasters 10 + - (Sprite Editor) Timing information listed for commands in command list 11 + - (Sprite Editor) Error highlighting for commands/components/etc 12 + - (Map Editor) A wide array of import formats are now supported, including fbx and gltf 13 + 14 + ### Changed 15 + - (Sprite Editor) UI reorganized and improved 16 + - (Sprite Editor) New animations tab for easier animation/component list editing 17 + - (Sprite Editor) More intuitive UI for binding assets to rasters 18 + - (Sprite Editor) Proper keyframe support 19 + - (Sprite Editor) Faster sprite loading 20 + - Version checking will not open windows from command line and can be skipped 21 + - Faster startup 22 + 23 + ### Fixed 24 + - (Map Editor) Importing/exporting 25 + 5 26 ## [0.9.4] - 2025-02-21 6 27 7 28 ### Fixed
+1 -1
app.properties
··· 1 - version=0.9.4 1 + version=0.10.0
+36 -29
src/main/java/app/Environment.java
··· 311 311 connection.setReadTimeout(1000); 312 312 313 313 int responseCode = connection.getResponseCode(); 314 - if (responseCode == HttpURLConnection.HTTP_OK) { 315 - InputStreamReader inputStreamReader = new InputStreamReader(connection.getInputStream()); 316 - JsonObject jsonObject = JsonParser.parseReader(inputStreamReader).getAsJsonObject(); 317 - String latestVersion = jsonObject.get("tag_name").getAsString(); 314 + if (responseCode != HttpURLConnection.HTTP_OK) { 315 + Logger.logError("Update check failed (response code: " + responseCode + ")"); 316 + return; 318 317 319 - if (!latestVersion.equals("v" + versionString)) { 320 - Logger.log("Detected newer remote version: " + latestVersion); 318 + } 319 + InputStreamReader inputStreamReader = new InputStreamReader(connection.getInputStream()); 320 + JsonObject jsonObject = JsonParser.parseReader(inputStreamReader).getAsJsonObject(); 321 + String latestVersion = jsonObject.get("tag_name").getAsString(); 321 322 322 - int result = SwingUtils.getWarningDialog() 323 - .setTitle("Update Available") 324 - .setMessage("A newer version is available!", "Please visit the GitHub repo to download it.") 325 - .setOptions("Yes", "No", "Don't Remind Me") 326 - .choose(); 323 + if (latestVersion.equals("v" + versionString)) { 324 + return; 325 + } 326 + 327 + Logger.log("Detected newer remote version: " + latestVersion); 328 + 329 + if (isCommandLine()) { 330 + Logger.log("Please visit the GitHub repo to download it."); 331 + return; 332 + } 333 + 334 + int result = SwingUtils.getWarningDialog() 335 + .setTitle("Update Available") 336 + .setMessage("A newer version is available!", "Please visit the GitHub repo to download it.") 337 + .setOptions("Yes", "No", "Don't Remind Me") 338 + .choose(); 327 339 328 - if (result == 0) { 329 - try { 330 - Desktop desktop = Desktop.getDesktop(); 331 - URI uri = new URI("https://github.com/z64a/star-rod/releases"); 332 - desktop.browse(uri); 333 - exit(); 334 - } 335 - catch (IOException | URISyntaxException e) { 336 - Toolkit.getDefaultToolkit().beep(); 337 - Logger.printStackTrace(e); 338 - } 339 - } 340 - else if (result == 2) { 341 - mainConfig.setBoolean(Options.CheckForUpdates, false); 342 - } 340 + if (result == 0) { 341 + try { 342 + Desktop desktop = Desktop.getDesktop(); 343 + URI uri = new URI("https://github.com/z64a/star-rod/releases"); 344 + desktop.browse(uri); 345 + exit(); 346 + } 347 + catch (IOException | URISyntaxException e) { 348 + Toolkit.getDefaultToolkit().beep(); 349 + Logger.printStackTrace(e); 343 350 } 344 351 } 345 - else { 346 - Logger.logError("Update check failed (response code: " + responseCode + ")"); 352 + else if (result == 2) { 353 + mainConfig.setBoolean(Options.CheckForUpdates, false); 347 354 } 348 355 } 349 356 catch (Exception e) { ··· 598 605 catch (IOException e) { 599 606 Logger.printStackTrace(e); 600 607 showErrorMessage("Base ROM Read Exception", 601 - "IOException while attempting to read baserom: %n%s %n%s", usBaseRom.getAbsolutePath()); 608 + "IOException while attempting to read baserom: %n%s", usBaseRom.getAbsolutePath()); 602 609 return null; 603 610 } 604 611
+84 -102
src/main/java/game/map/Map.java
··· 4 4 5 5 import java.awt.image.BufferedImage; 6 6 import java.io.File; 7 - import java.io.FileNotFoundException; 8 7 import java.io.IOException; 9 8 import java.io.PrintWriter; 10 9 import java.nio.file.Files; ··· 28 27 import app.SwingUtils; 29 28 import app.input.IOUtils; 30 29 import assets.AssetManager; 31 - import assets.AssetSubdir; 32 30 import common.commands.AbstractCommand; 33 31 import common.commands.CommandBatch; 34 32 import game.map.MapObject.MapObjectType; ··· 69 67 import game.map.tree.ZoneTreeModel; 70 68 import util.IterableListModel; 71 69 import util.Logger; 72 - import util.Priority; 73 70 import util.identity.IdentityHashSet; 74 71 import util.xml.XmlKey; 75 72 import util.xml.XmlWrapper.XmlReader; ··· 940 937 return objectList; 941 938 } 942 939 940 + private <T extends MapObject> void addSelectionFromTree(List<MapObject> selectedObjects, MapObjectTreeModel<T> treeModel) 941 + { 942 + Stack<MapObjectNode<T>> nodes = new Stack<>(); 943 + List<MapObjectNode<T>> selectedNodes = new ArrayList<>(); 944 + 945 + MapObjectNode<T> root = treeModel.getRoot(); 946 + nodes.push(root); 947 + 948 + while (!nodes.isEmpty()) { 949 + MapObjectNode<T> node = nodes.pop(); 950 + T obj = node.getUserObject(); 951 + 952 + if (obj.selected && node != root) 953 + selectedNodes.add(node); 954 + 955 + for (int i = 0; i < node.getChildCount(); i++) 956 + nodes.push(node.getChildAt(i)); 957 + } 958 + 959 + // selection order not important, keep them in tree index order 960 + Collections.sort(selectedNodes); 961 + for (MapObjectNode<T> node : selectedNodes) 962 + selectedObjects.add(node.getUserObject()); 963 + } 964 + 943 965 public void addChildrenToList(List<MapObject> objectList) 944 966 { 945 967 Stack<MapObject> objectStack = new Stack<>(); ··· 964 986 } 965 987 } 966 988 } 989 + } 990 + 991 + public int exportPrefab(File f, boolean quiet) throws IOException 992 + { 993 + List<MapObject> selectedList = getCompleteSelection(); 994 + 995 + if (selectedList.size() > 0) { 996 + Prefab prefab = new Prefab(selectedList, lightSets, texName); 997 + 998 + try (XmlWriter xmw = new XmlWriter(f)) { 999 + prefab.toXML(xmw); 1000 + xmw.save(); 1001 + } 1002 + 1003 + if (!quiet) { 1004 + for (MapObject obj : selectedList) 1005 + Logger.log("Exported " + obj.getName()); 1006 + } 1007 + } 1008 + 1009 + return selectedList.size(); 967 1010 } 968 1011 969 1012 private int exportOBJ(File f) throws IOException ··· 985 1028 if (num == 0) 986 1029 return 0; 987 1030 988 - File textureFile = AssetManager.get(AssetSubdir.MAP_TEX, texName + "/" + texName + ".mtl"); 989 - if ((models.size() > 0) && !textureFile.exists()) { 990 - Logger.log("Could not load materials for area " + texName); 991 - return 0; 992 - } 993 - 994 1031 ObjExporter exporter = new ObjExporter(f); 995 1032 exporter.writeModels(models, texName); 996 1033 exporter.writeColliders(colliders); ··· 1001 1038 return num; 1002 1039 } 1003 1040 1004 - public int exportPrefab(File f, boolean quiet) throws IOException 1041 + public void exportToFile(File f) throws IOException 1005 1042 { 1006 - List<MapObject> selectedList = getCompleteSelection(); 1043 + String ext = FilenameUtils.getExtension(f.getName()); 1007 1044 1008 - if (selectedList.size() > 0) { 1009 - Prefab prefab = new Prefab(selectedList, lightSets, texName); 1010 - 1011 - try (XmlWriter xmw = new XmlWriter(f)) { 1012 - prefab.toXML(xmw); 1013 - xmw.save(); 1014 - } 1015 - catch (FileNotFoundException e) { 1016 - Logger.logError("EXPORT FAILED! File not found: " + f.getAbsolutePath()); 1017 - return 0; 1018 - } 1019 - 1020 - if (!quiet) { 1021 - for (MapObject obj : selectedList) 1022 - Logger.log("Exported " + obj.getName()); 1023 - } 1045 + if (ext.equalsIgnoreCase("obj")) { 1046 + exportOBJ(f); 1024 1047 } 1025 - 1026 - return selectedList.size(); 1027 - } 1028 - 1029 - private <T extends MapObject> void addSelectionFromTree(List<MapObject> selectedObjects, MapObjectTreeModel<T> treeModel) 1030 - { 1031 - Stack<MapObjectNode<T>> nodes = new Stack<>(); 1032 - List<MapObjectNode<T>> selectedNodes = new ArrayList<>(); 1033 - 1034 - MapObjectNode<T> root = treeModel.getRoot(); 1035 - nodes.push(root); 1036 - 1037 - while (!nodes.isEmpty()) { 1038 - MapObjectNode<T> node = nodes.pop(); 1039 - T obj = node.getUserObject(); 1040 - 1041 - if (obj.selected && node != root) 1042 - selectedNodes.add(node); 1043 - 1044 - for (int i = 0; i < node.getChildCount(); i++) 1045 - nodes.push(node.getChildAt(i)); 1048 + else { 1049 + SwingUtils.getWarningDialog() 1050 + .setTitle("Export Failed") 1051 + .setMessage("Unsupported export format: " + ext) 1052 + .show(); 1046 1053 } 1047 - 1048 - // selection order not important, keep them in tree index order 1049 - Collections.sort(selectedNodes); 1050 - for (MapObjectNode<T> node : selectedNodes) 1051 - selectedObjects.add(node.getUserObject()); 1052 1054 } 1053 1055 1054 1056 public static class PrefabImportData ··· 1212 1214 Logger.log("Succesfully loaded " + numObjects + " objects from " + f.getName()); 1213 1215 } 1214 1216 1215 - if (ext.equalsIgnoreCase("obj")) { 1217 + else if (ext.equalsIgnoreCase("obj")) { 1216 1218 int numObjects = 0; 1217 1219 1218 1220 MapObjectType type = (node == null) ? MapObjectType.MODEL : node.getObjectType(); ··· 1247 1249 1248 1250 Logger.log("Succesfully loaded " + numObjects + " objects from " + f.getName()); 1249 1251 } 1252 + } 1250 1253 1251 - if (ext.equalsIgnoreCase("fbx")) { 1252 - int numObjects = 0; 1254 + @SuppressWarnings("unchecked") 1255 + public void importViaAssimp(File f, AssimpImportOptions options, MapObjectNode<? extends MapObject> node) 1256 + { 1257 + int numObjects = 0; 1253 1258 1254 - MapObjectType type = (node == null) ? MapObjectType.MODEL : node.getObjectType(); 1255 - switch (type) { 1256 - case MODEL: 1257 - List<Model> models = AssimpImporter.importModels(f, new AssimpImportOptions()); 1258 - MapEditor.execute(new CreateObjects(models)); 1259 - numObjects = models.size(); 1260 - break; 1259 + MapObjectType importAs; 1260 + if (node == null) 1261 + importAs = options.importAs.getType(); 1262 + else 1263 + importAs = node.getObjectType(); 1261 1264 1262 - case COLLIDER: 1263 - List<Collider> colliders = AssimpImporter.importColliders(f, new AssimpImportOptions()); 1264 - MapEditor.execute(new CreateObjects(colliders)); 1265 - numObjects = colliders.size(); 1266 - break; 1265 + switch (importAs) { 1266 + case MODEL: 1267 + List<Model> models = AssimpImporter.importModels(f, options, (MapObjectNode<Model>) node); 1268 + MapEditor.execute(new CreateObjects(models)); 1269 + numObjects = models.size(); 1270 + break; 1267 1271 1268 - case ZONE: 1269 - List<Zone> zones = AssimpImporter.importZones(f, new AssimpImportOptions()); 1270 - MapEditor.execute(new CreateObjects(zones)); 1271 - numObjects = zones.size(); 1272 - break; 1272 + case COLLIDER: 1273 + List<Collider> colliders = AssimpImporter.importColliders(f, options, (MapObjectNode<Collider>) node); 1274 + MapEditor.execute(new CreateObjects(colliders)); 1275 + numObjects = colliders.size(); 1276 + break; 1273 1277 1274 - default: 1275 - } 1278 + case ZONE: 1279 + List<Zone> zones = AssimpImporter.importZones(f, options, (MapObjectNode<Zone>) node); 1280 + MapEditor.execute(new CreateObjects(zones)); 1281 + numObjects = zones.size(); 1282 + break; 1276 1283 1277 - Logger.log("Succesfully loaded " + numObjects + " objects from " + f.getName()); 1284 + default: 1278 1285 } 1279 - } 1280 1286 1281 - public void exportToFile(File f) 1282 - { 1283 - String ext = FilenameUtils.getExtension(f.getName()); 1284 - 1285 - try { 1286 - if (ext.equalsIgnoreCase("prefab")) { 1287 - exportPrefab(f, false); 1288 - } 1289 - else if (ext.equalsIgnoreCase("obj")) { 1290 - exportOBJ(f); 1291 - } 1292 - else if (ext.equalsIgnoreCase("fbx")) { 1293 - //TODO exportFBX(f); 1294 - } 1295 - else { 1296 - SwingUtils.getWarningDialog() 1297 - .setTitle("Export Failed") 1298 - .setMessage("Unsupported export format: " + ext) 1299 - .show(); 1300 - } 1301 - } 1302 - catch (IOException e) { 1303 - Logger.log("IOException when saving objects to " + f.getName(), Priority.WARNING); 1304 - return; 1305 - } 1287 + Logger.log("Succesfully loaded " + numObjects + " objects from " + f.getName()); 1306 1288 } 1307 1289 1308 1290 /*
-2
src/main/java/game/map/editor/MapEditor.java
··· 688 688 EditableField.setCallbacksEnabled(false); 689 689 TextureManager.clear(); 690 690 Logger.removeListener(gui); 691 - glCanvas.disposeCanvas(); 692 691 gui.destroyGUI(); 693 692 694 693 // update recent/crashed maps and flush the config ··· 763 762 TextureManager.clear(); 764 763 Logger.removeListener(gui); 765 764 766 - glCanvas.disposeCanvas(); 767 765 gui.destroyGUI(); 768 766 769 767 editorLog.close();
+99 -51
src/main/java/game/map/editor/ui/SwingGUI.java
··· 46 46 import javax.swing.WindowConstants; 47 47 48 48 import org.apache.commons.io.FilenameUtils; 49 + import org.lwjgl.assimp.Assimp; 49 50 50 51 import app.Environment; 51 52 import app.StarRodFrame; 53 + import app.StarRodMain; 52 54 import app.SwingUtils; 53 55 import app.SwingUtils.OpenDialogCounter; 54 56 import assets.AssetHandle; ··· 67 69 import game.map.MapObject.MapObjectType; 68 70 import game.map.editor.EditorShortcut; 69 71 import game.map.editor.MapEditor; 70 - import game.map.editor.MapPreferencesPanel; 71 72 import game.map.editor.MapEditor.EditorMode; 72 73 import game.map.editor.MapEditor.IShutdownListener; 74 + import game.map.editor.MapPreferencesPanel; 73 75 import game.map.editor.PaintManager; 74 76 import game.map.editor.commands.ChangeTextureArchive; 75 77 import game.map.editor.commands.CreateObjects; 78 + import game.map.editor.render.PreviewGeneratorPrimitive; 76 79 import game.map.editor.render.TextureManager; 77 80 import game.map.editor.selection.Selection; 78 81 import game.map.editor.selection.SelectionManager; ··· 91 94 import game.map.editor.ui.info.DisplayListPanel.AddTriangles; 92 95 import game.map.hit.Collider; 93 96 import game.map.hit.Zone; 94 - import game.map.impex.ExportDialog; 95 97 import game.map.impex.ImportDialog; 98 + import game.map.impex.ImportDialog.ImportDialogResult; 96 99 import game.map.marker.Marker; 97 100 import game.map.shape.Model; 98 101 import game.map.shape.TexturePanner; ··· 175 178 private GenerateFromTrianglesDialog generateFromTrianglesDialog; 176 179 private GenerateFromPathsDialog generateFromPathsDialog; 177 180 private EditPannerDialog editTexPannerDialog; 178 - private ExportDialog exportDialog; 179 - private ImportDialog importDialog; 180 181 181 182 private TransformSelectionPanel transformSelectionPanel; 182 183 ··· 226 227 // set file chooser behavior 227 228 File mapDir = Environment.getProjectDirectory(); 228 229 229 - importFileChooser = new OpenFileChooser(mapDir, "Import Geometry", "Importables", "obj", "fbx", "prefab"); 230 - exportFileChooser = new SaveFileChooser(mapDir, "Export Geometry", null, "obj", "fbx"); 230 + importFileChooser = new OpenFileChooser(mapDir, "Import Geometry", "Importables", "prefab", "obj", "fbx", "gltf", "glb"); 231 + exportFileChooser = new SaveFileChooser(mapDir, "Export Geometry", null, "prefab", "obj"); 231 232 232 233 commandMap = new HashMap<>(); 233 234 for (GuiCommand cmd : GuiCommand.values()) ··· 378 379 public void destroyGUI() 379 380 { 380 381 setVisible(false); 381 - dispose(); //TODO verify if necessary 382 + dispose(); 382 383 } 383 384 384 385 public boolean isModalDialogOpen() ··· 517 518 menu.add(item); 518 519 519 520 menu.addSeparator(); 520 - 521 - item = new JMenuItem("Export"); 522 - addButtonCommand(item, GuiCommand.PROMPT_EXPORT); 523 - menu.add(item); 524 521 525 522 item = new JMenuItem("Import"); 526 523 addButtonCommand(item, GuiCommand.PROMPT_IMPORT); 527 524 menu.add(item); 528 525 526 + item = new JMenuItem("Export"); 527 + addButtonCommand(item, GuiCommand.PROMPT_EXPORT); 528 + menu.add(item); 529 + 529 530 menu.addSeparator(); 530 531 531 532 item = new JMenuItem("Switch Tools"); ··· 1155 1156 break; 1156 1157 1157 1158 case PROMPT_EXPORT: 1158 - prompt_Import(); 1159 + prompt_Export(); 1159 1160 break; 1160 1161 case PROMPT_IMPORT: 1161 - prompt_Export(); 1162 + prompt_Import(null); 1162 1163 break; 1163 1164 case SAVE_PREFAB: 1164 1165 prompt_Prefab(); ··· 1359 1360 } 1360 1361 } 1361 1362 1362 - //XXX 1363 - public void prompt_ImportObjects(MapObjectNode<? extends MapObject> node) 1363 + public void prompt_Import(MapObjectNode<? extends MapObject> node) 1364 1364 { 1365 1365 openDialogCount.increment(); 1366 - if (importFileChooser.prompt() == ChooseDialogResult.APPROVE) { 1367 - File f = importFileChooser.getSelectedFile(); 1368 - editor.map.importFromFile(f, node); 1366 + ChooseDialogResult result = importFileChooser.prompt(); 1367 + openDialogCount.decrement(); 1368 + 1369 + if (result != ChooseDialogResult.APPROVE) 1370 + return; 1371 + 1372 + File in = importFileChooser.getSelectedFile(); 1373 + String ext = FilenameUtils.getExtension(in.getName()).toLowerCase(); 1374 + 1375 + if ("prefab".equals(ext)) { 1376 + editor.map.importFromFile(in, node); 1369 1377 } 1370 - openDialogCount.decrement(); 1371 - } 1378 + else if ("obj".equals(ext)) { 1379 + editor.map.importFromFile(in, node); 1380 + } 1381 + else if (Assimp.aiIsExtensionSupported(ext)) { 1382 + openDialogCount.increment(); 1383 + 1384 + ImportDialog importDialog = new ImportDialog(this, in, node != null); 1385 + importDialog.pack(); 1386 + importDialog.setVisible(true); 1372 1387 1373 - public void prompt_Import() 1374 - { 1375 - //TODO if needed 1376 - /* 1377 - if (importDialog == null) 1378 - importDialog = new ImportDialog(this); 1388 + openDialogCount.decrement(); 1379 1389 1380 - importDialog.pack(); 1381 - importDialog.setVisible(true); 1382 - */ 1390 + if (importDialog.getResult() == ImportDialogResult.READY) 1391 + editor.map.importViaAssimp(in, importDialog.getOptions(), node); 1392 + } 1393 + else { 1394 + SwingUtils.getErrorDialog() 1395 + .setParent(this) 1396 + .setCounter(openDialogCount) 1397 + .setTitle("Unable to Import") 1398 + .setMessage("Unsupported file extension: " + ext) 1399 + .setOptions("OK") 1400 + .show(); 1401 + } 1402 + 1383 1403 } 1384 1404 1385 1405 public void prompt_Export() 1386 1406 { 1387 - exportFileChooser.setFilters(null, "obj", "fbx"); 1407 + exportFileChooser.setFilters("Export Geometry", "obj"); 1388 1408 1389 1409 openDialogCount.increment(); 1390 - if (exportFileChooser.prompt() == ChooseDialogResult.APPROVE) { 1391 - File f = exportFileChooser.getSelectedFile(); 1392 - editor.map.exportToFile(f); 1393 - } 1410 + ChooseDialogResult result = exportFileChooser.prompt(); 1394 1411 openDialogCount.decrement(); 1395 1412 1396 - //TODO if needed 1397 - /* 1398 - if (exportDialog == null) 1399 - exportDialog = new ExportDialog(this); 1413 + if (result != ChooseDialogResult.APPROVE) 1414 + return; 1415 + 1416 + try { 1417 + File out = exportFileChooser.getSelectedFile(); 1418 + String ext = FilenameUtils.getExtension(out.getName()).toLowerCase(); 1419 + 1420 + if ("obj".equals(ext)) { 1421 + editor.map.exportToFile(out); 1422 + } 1423 + else { 1424 + SwingUtils.getErrorDialog() 1425 + .setParent(this) 1426 + .setCounter(openDialogCount) 1427 + .setTitle("Unable to Export") 1428 + .setMessage("Unsupported file extension: " + ext) 1429 + .setOptions("OK") 1430 + .show(); 1400 1431 1401 - exportDialog.pack(); 1402 - exportDialog.setVisible(true); 1403 - */ 1432 + } 1433 + } 1434 + catch (Exception e) { 1435 + openDialogCount.increment(); 1436 + StarRodMain.displayStackTrace(e); 1437 + openDialogCount.decrement(); 1438 + } 1404 1439 } 1405 1440 1406 1441 public void prompt_Prefab() ··· 1413 1448 exportFileChooser.setFilters("Prefabs", "prefab"); 1414 1449 1415 1450 openDialogCount.increment(); 1416 - if (exportFileChooser.prompt() == ChooseDialogResult.APPROVE) { 1417 - File f = exportFileChooser.getSelectedFile(); 1418 - editor.map.exportToFile(f); 1451 + ChooseDialogResult result = exportFileChooser.prompt(); 1452 + openDialogCount.decrement(); 1453 + 1454 + if (result != ChooseDialogResult.APPROVE) 1455 + return; 1456 + 1457 + try { 1458 + File out = exportFileChooser.getSelectedFile(); 1459 + 1460 + int count = editor.map.exportPrefab(out, false); 1461 + if (count > 0) 1462 + Logger.log("Exported " + count + " objects to prefab."); 1463 + } 1464 + catch (Exception e) { 1465 + openDialogCount.increment(); 1466 + StarRodMain.displayStackTrace(e); 1467 + openDialogCount.decrement(); 1419 1468 } 1420 - openDialogCount.decrement(); 1421 1469 } 1422 1470 1423 1471 public void prompt_EditTexPanner(TexturePanner panner) ··· 1517 1565 return; 1518 1566 if (batch == null) 1519 1567 return; 1520 - if (editor.generatePrimitivePreview.targetBatch != null) 1521 - MapEditor.execute(new AddTriangles(editor.generatePrimitivePreview.targetBatch, 1522 - batch.triangles)); 1568 + 1569 + PreviewGeneratorPrimitive preview = editor.generatePrimitivePreview; 1570 + if (preview.targetBatch != null) 1571 + MapEditor.execute(new AddTriangles(preview.targetBatch, batch.triangles)); 1523 1572 else 1524 - createObjectFromBatch(batch, 1525 - generatePrimitiveDialog.getTypeName(), 1526 - editor.generatePrimitivePreview.parentObj); 1573 + createObjectFromBatch(batch, generatePrimitiveDialog.getTypeName(), preview.parentObj); 1527 1574 }); 1528 1575 1529 1576 generatePrimitiveDialog.setTitle(GeneratePrimitiveOptionsDialog.FRAME_TITLE); ··· 1546 1593 return; 1547 1594 if (batch == null) 1548 1595 return; 1596 + 1549 1597 createObjectFromBatch(batch, 1550 1598 generateFromTrianglesDialog.getTypeName(), 1551 1599 editor.generateFromTrianglesPreview.parentObj);
+23 -10
src/main/java/game/map/editor/ui/dialogs/OpenFileChooser.java
··· 9 9 public class OpenFileChooser 10 10 { 11 11 private final String title; 12 - private final String filterName; 13 - private final String[] filterExts; 12 + private String baseFilterName; 13 + private String filterName; 14 + private String[] filterExts; 14 15 15 16 private File currentDirectory = null; 16 17 private File selected = null; 17 18 18 - public OpenFileChooser(File dir, String title, String filterName, String ... filterExts) 19 + public OpenFileChooser(File dir, String title, String baseFilterName, String ... filterExts) 19 20 { 20 21 this.title = title; 21 - StringBuilder sb = new StringBuilder(filterName); 22 - sb.append(" ("); 23 - for (String s : filterExts) 24 - sb.append("*.").append(s); 25 - sb.append(")"); 26 - this.filterName = sb.toString(); 27 - this.filterExts = filterExts; 28 22 currentDirectory = dir; 23 + 24 + this.baseFilterName = baseFilterName; 25 + setExtenstions(filterExts); 29 26 } 30 27 31 28 public void setDirectoryContaining(File dir) ··· 68 65 public File getSelectedFile() 69 66 { 70 67 return selected; 68 + } 69 + 70 + public String[] getExtensions() 71 + { 72 + return filterExts; 73 + } 74 + 75 + public void setExtenstions(String ... exts) 76 + { 77 + filterExts = exts; 78 + 79 + StringBuilder sb = new StringBuilder(baseFilterName); 80 + sb.append(" ("); 81 + for (String s : filterExts) 82 + sb.append("*.").append(s); 83 + sb.append(")"); 71 84 } 72 85 }
+92 -11
src/main/java/game/map/impex/AssimpImporter.java
··· 3 3 import static org.lwjgl.assimp.Assimp.*; 4 4 5 5 import java.io.File; 6 + import java.nio.ByteBuffer; 6 7 import java.nio.IntBuffer; 7 8 import java.util.ArrayList; 8 9 import java.util.HashMap; ··· 14 15 import org.lwjgl.assimp.AIFace; 15 16 import org.lwjgl.assimp.AIMaterial; 16 17 import org.lwjgl.assimp.AIMesh; 18 + import org.lwjgl.assimp.AIMetaData; 19 + import org.lwjgl.assimp.AIMetaDataEntry; 17 20 import org.lwjgl.assimp.AIScene; 18 21 import org.lwjgl.assimp.AIString; 19 22 import org.lwjgl.assimp.AIVector3D; ··· 22 25 import game.map.MapObject.HitType; 23 26 import game.map.hit.Collider; 24 27 import game.map.hit.Zone; 28 + import game.map.impex.ImportDialog.ImportAsValue; 25 29 import game.map.mesh.AbstractMesh; 26 30 import game.map.mesh.TexturedMesh; 27 31 import game.map.mesh.Triangle; ··· 29 33 import game.map.shape.Model; 30 34 import game.map.shape.TriangleBatch; 31 35 import game.map.shape.UV; 36 + import game.map.tree.MapObjectNode; 32 37 import game.texture.ModelTexture; 33 38 import util.Logger; 34 39 ··· 36 41 { 37 42 public static class AssimpImportOptions 38 43 { 44 + public ImportAsValue importAs = ImportAsValue.Models; 45 + 39 46 boolean triangulate = true; 40 47 boolean joinVertices = true; 41 48 boolean convertZupToYup = true; 42 49 50 + float scale = 100.0f; 43 51 float uvScale = 1024.0f; 44 52 } 45 53 46 - public static List<Model> importModels(File modelFile, AssimpImportOptions options) 54 + private static class AssimpSceneMetadata 55 + { 56 + private static final int AI_AXIS_X = 0; 57 + private static final int AI_AXIS_Y = 1; 58 + private static final int AI_AXIS_Z = 2; 59 + 60 + public final int axisUp; 61 + public final float unitScale; 62 + 63 + public AssimpSceneMetadata(AIScene aiScene) 64 + { 65 + AIMetaData metadata = aiScene.mMetaData(); 66 + 67 + AIString.Buffer aiKeys = metadata.mKeys(); 68 + AIMetaDataEntry.Buffer aiVals = metadata.mValues(); 69 + 70 + int upAxis = 1; 71 + float unitScale = 1.0f; 72 + 73 + for (int i = 0; i < metadata.mNumProperties(); i++) { 74 + String keyName = aiKeys.get(i).dataString(); 75 + 76 + if ("UpAxis".equals(keyName)) { 77 + AIMetaDataEntry entry = aiVals.get(i); 78 + if (entry.mType() == AI_INT32) { 79 + ByteBuffer bb = entry.mData(4); 80 + upAxis = bb.getInt(); 81 + } 82 + } 83 + /* 84 + else if ("UpAxisSign".equals(keyName)) { 85 + AIMetaDataEntry entry = aiVals.get(i); 86 + if (entry.mType() == AI_METADATA_INT) { 87 + ByteBuffer bb = entry.mData(4); 88 + upAxisSign = bb.getInt(); 89 + } 90 + } 91 + */ 92 + else if ("UnitScaleFactor".equals(keyName)) { 93 + AIMetaDataEntry entry = aiVals.get(i); 94 + if (entry.mType() == AI_FLOAT) { 95 + ByteBuffer bb = entry.mData(4); 96 + unitScale = bb.getFloat(); 97 + } 98 + } 99 + } 100 + 101 + this.axisUp = upAxis; 102 + this.unitScale = unitScale; 103 + } 104 + } 105 + 106 + public static List<Model> importModels(File modelFile, AssimpImportOptions options, MapObjectNode<Model> node) 47 107 { 48 108 List<Model> models = new ArrayList<>(); 49 109 50 - AIScene aiScene = aiImportFile(modelFile.getPath(), 0); //aiProcess_JoinIdenticalVertices); 110 + AIScene aiScene = aiImportFile(modelFile.getPath(), 0); 51 111 if (aiScene == null) 52 112 throw new StarRodException("Error loading " + modelFile.getName()); 53 113 114 + AssimpSceneMetadata metadata = new AssimpSceneMetadata(aiScene); 115 + 54 116 int numMaterials = aiScene.mNumMaterials(); 55 117 PointerBuffer aiMaterials = aiScene.mMaterials(); 56 118 ··· 76 138 if (matIndex >= 0 && matIndex < texNames.size()) 77 139 model.getMesh().setTexture(texNames.get(matIndex)); 78 140 79 - TriangleBatch batch = processMesh(aiMesh, model.getMesh(), options); 141 + TriangleBatch batch = processMesh(aiMesh, model.getMesh(), options, metadata); 80 142 model.getMesh().displayListModel.addElement(batch); 81 143 144 + if (node != null) { 145 + model.getNode().parentNode = node; 146 + model.getNode().childIndex = node.getChildCount(); 147 + } 148 + 82 149 model.updateMeshHierarchy(); 83 150 models.add(model); 84 151 } ··· 86 153 return models; 87 154 } 88 155 89 - public static List<Collider> importColliders(File modelFile, AssimpImportOptions options) 156 + public static List<Collider> importColliders(File modelFile, AssimpImportOptions options, MapObjectNode<Collider> node) 90 157 { 91 158 List<Collider> colliders = new ArrayList<>(); 92 159 ··· 94 161 if (aiScene == null) 95 162 throw new StarRodException("Error loading " + modelFile.getName()); 96 163 164 + AssimpSceneMetadata metadata = new AssimpSceneMetadata(aiScene); 165 + 97 166 int numMeshes = aiScene.mNumMeshes(); 98 167 PointerBuffer aiMeshes = aiScene.mMeshes(); 99 168 ··· 102 171 103 172 Collider c = new Collider(HitType.HIT); 104 173 c.setName(aiMesh.mName().dataString()); 105 - c.mesh.batch = processMesh(aiMesh, c.mesh, options); 174 + c.mesh.batch = processMesh(aiMesh, c.mesh, options, metadata); 175 + 176 + if (node != null) { 177 + c.getNode().parentNode = node; 178 + c.getNode().childIndex = node.getChildCount(); 179 + } 106 180 107 181 c.updateMeshHierarchy(); 108 182 colliders.add(c); ··· 111 185 return colliders; 112 186 } 113 187 114 - public static List<Zone> importZones(File modelFile, AssimpImportOptions options) 188 + public static List<Zone> importZones(File modelFile, AssimpImportOptions options, MapObjectNode<Zone> node) 115 189 { 116 190 List<Zone> zones = new ArrayList<>(); 117 191 118 192 AIScene aiScene = aiImportFile(modelFile.getPath(), 0); 119 193 if (aiScene == null) 120 194 throw new StarRodException("Error loading " + modelFile.getName()); 195 + 196 + AssimpSceneMetadata metadata = new AssimpSceneMetadata(aiScene); 121 197 122 198 int numMeshes = aiScene.mNumMeshes(); 123 199 PointerBuffer aiMeshes = aiScene.mMeshes(); ··· 127 203 128 204 Zone z = new Zone(HitType.HIT); 129 205 z.setName(aiMesh.mName().dataString()); 130 - z.mesh.batch = processMesh(aiMesh, z.mesh, options); 206 + z.mesh.batch = processMesh(aiMesh, z.mesh, options, metadata); 207 + 208 + if (node != null) { 209 + z.getNode().parentNode = node; 210 + z.getNode().childIndex = node.getChildCount(); 211 + } 131 212 132 213 z.updateMeshHierarchy(); 133 214 zones.add(z); ··· 136 217 return zones; 137 218 } 138 219 139 - private static TriangleBatch processMesh(AIMesh aiMesh, AbstractMesh srMesh, AssimpImportOptions options) 220 + private static TriangleBatch processMesh(AIMesh aiMesh, AbstractMesh srMesh, AssimpImportOptions options, AssimpSceneMetadata metadata) 140 221 { 141 222 TriangleBatch batch = new TriangleBatch(srMesh); 142 223 ··· 156 237 while (aiVertices.hasRemaining()) { 157 238 AIVector3D aiVertex = aiVertices.get(); 158 239 Vertex vtx; 159 - if (options.convertZupToYup) 160 - vtx = new Vertex(aiVertex.x(), aiVertex.z(), aiVertex.y()); 240 + if (options.convertZupToYup && metadata.axisUp == AssimpSceneMetadata.AI_AXIS_Z) 241 + vtx = new Vertex(options.scale * aiVertex.x(), options.scale * aiVertex.z(), options.scale * aiVertex.y()); 161 242 else 162 - vtx = new Vertex(aiVertex.x(), aiVertex.y(), aiVertex.z()); 243 + vtx = new Vertex(options.scale * aiVertex.x(), options.scale * aiVertex.y(), options.scale * aiVertex.z()); 163 244 vertices.add(vtx); 164 245 vtxMap.put(vtx, vtx); 165 246 }
+28 -12
src/main/java/game/map/impex/ExportDialog.java
··· 1 1 package game.map.impex; 2 2 3 3 import java.awt.event.WindowEvent; 4 - import java.io.File; 5 4 6 5 import javax.swing.JButton; 7 6 import javax.swing.JCheckBox; ··· 19 18 20 19 public class ExportDialog extends JDialog 21 20 { 21 + private static enum ExportFormat 22 + { 23 + PREFAB ("prefab"), 24 + OBJ ("obj"), 25 + FBX ("fbx"), 26 + GLTF ("gltf"), 27 + GLB ("glb"); 28 + 29 + private final String name; 30 + 31 + private ExportFormat(String name) 32 + { 33 + this.name = name; 34 + } 35 + 36 + @Override 37 + public String toString() 38 + { 39 + return name; 40 + } 41 + } 42 + 22 43 public static final String FRAME_TITLE = "Export"; 23 44 24 - private JComboBox<ImpexFormat> fileTypeComboBox; 45 + private JComboBox<ExportFormat> fileTypeComboBox; 25 46 private JCheckBox cbSelectedOnly; 26 47 27 - public ExportDialog(JFrame parent) 48 + public ExportDialog(JFrame parent, SaveFileChooser fileChooser) 28 49 { 29 50 super(parent); 30 51 31 52 JButton selectButton = new JButton("Export"); 32 53 SwingUtils.addBorderPadding(selectButton); 33 54 selectButton.addActionListener((e) -> { 34 - 35 - File mapDir = Environment.getProjectDirectory(); 36 - SaveFileChooser chooser = new SaveFileChooser(mapDir, "Export Geometry", 37 - "Exportables", "obj", "fbx", "prefab"); 38 - 39 - //TODO select file 55 + //TODO select file? 40 56 setVisible(false); 41 57 }); 42 58 ··· 55 71 } 56 72 }); 57 73 58 - fileTypeComboBox = new JComboBox<>(ImpexFormat.values()); 74 + fileTypeComboBox = new JComboBox<>(ExportFormat.values()); 59 75 SwingUtils.addBorderPadding(fileTypeComboBox); 60 76 61 77 cbSelectedOnly = new JCheckBox(" Selected object only"); ··· 68 84 add(cbSelectedOnly, "span, growx, gapbottom 8"); 69 85 70 86 add(new JPanel(), "span, split 3, growx, sg but"); 71 - add(cancelButton, "growx, sg but"); 72 87 add(selectButton, "growx, sg but"); 88 + add(cancelButton, "growx, sg but"); 73 89 74 90 pack(); 75 91 setResizable(false); ··· 77 93 setTitle(FRAME_TITLE); 78 94 setIconImage(Environment.getDefaultIconImage()); 79 95 setLocationRelativeTo(parent); 80 - setModal(false); 96 + setModal(true); 81 97 } 82 98 }
-21
src/main/java/game/map/impex/ImpexFormat.java
··· 1 - package game.map.impex; 2 - 3 - public enum ImpexFormat 4 - { 5 - PREFAB("prefab"), 6 - OBJ("obj"), 7 - FBX("fbx"); 8 - 9 - private final String name; 10 - 11 - private ImpexFormat(String name) 12 - { 13 - this.name = name; 14 - } 15 - 16 - @Override 17 - public String toString() 18 - { 19 - return name; 20 - } 21 - }
+104 -15
src/main/java/game/map/impex/ImportDialog.java
··· 1 1 package game.map.impex; 2 2 3 3 import java.awt.event.WindowEvent; 4 + import java.io.File; 4 5 5 6 import javax.swing.JButton; 6 7 import javax.swing.JCheckBox; ··· 9 10 import javax.swing.JFrame; 10 11 import javax.swing.JLabel; 11 12 import javax.swing.JPanel; 13 + import javax.swing.JTextField; 12 14 import javax.swing.WindowConstants; 13 15 14 16 import app.Environment; 15 17 import app.SwingUtils; 18 + import game.map.MapObject.MapObjectType; 19 + import game.map.impex.AssimpImporter.AssimpImportOptions; 16 20 import net.miginfocom.swing.MigLayout; 21 + import util.ui.FloatTextField; 17 22 18 23 public class ImportDialog extends JDialog 19 24 { 20 25 public static final String FRAME_TITLE = "Import"; 21 26 22 - private JComboBox<ImpexFormat> fileTypeComboBox; 23 - private JCheckBox cbSelectedOnly; 27 + public static enum ImportDialogResult 28 + { 29 + READY, CANCEL 30 + }; 24 31 25 - public ImportDialog(JFrame parent) 32 + public static enum ImportAsValue 33 + { 34 + Models (MapObjectType.MODEL), 35 + Colliders (MapObjectType.COLLIDER), 36 + Zones (MapObjectType.ZONE); 37 + 38 + private final MapObjectType type; 39 + 40 + private ImportAsValue(MapObjectType type) 41 + { 42 + this.type = type; 43 + } 44 + 45 + public MapObjectType getType() 46 + { 47 + return type; 48 + } 49 + }; 50 + 51 + private static AssimpImportOptions options = new AssimpImportOptions(); 52 + 53 + private ImportDialogResult result = ImportDialogResult.CANCEL; 54 + 55 + public ImportDialog(JFrame parent, File f, boolean hasDestNode) 26 56 { 27 57 super(parent); 28 58 59 + JComboBox<ImportAsValue> importAsBox = new JComboBox<>(ImportAsValue.values()); 60 + SwingUtils.addBorderPadding(importAsBox); 61 + importAsBox.addActionListener((e) -> { 62 + options.importAs = (ImportAsValue) importAsBox.getSelectedItem(); 63 + }); 64 + 65 + JCheckBox cbTriangulate = new JCheckBox("Triangulate faces"); 66 + cbTriangulate.setIconTextGap(12); 67 + cbTriangulate.setSelected(options.triangulate); 68 + cbTriangulate.addActionListener((e) -> { 69 + options.triangulate = cbTriangulate.isSelected(); 70 + }); 71 + cbTriangulate.setToolTipText("<html>" 72 + + "Convert quads to triangle pairs and polygonal faces to triangle fans.<br>" 73 + + "</html>"); 74 + 75 + JCheckBox cbJoinVertices = new JCheckBox("Join identical vertices"); 76 + cbJoinVertices.setIconTextGap(12); 77 + cbJoinVertices.setSelected(options.joinVertices); 78 + cbJoinVertices.addActionListener((e) -> { 79 + options.joinVertices = cbJoinVertices.isSelected(); 80 + }); 81 + cbJoinVertices.setToolTipText("<html>" 82 + + "Merge vertices with identical positions and UV coordinates.<br>" 83 + + "</html>"); 84 + 85 + JCheckBox cbConvertUp = new JCheckBox("Convert to Y-up"); 86 + cbConvertUp.setIconTextGap(12); 87 + cbConvertUp.setSelected(options.triangulate); 88 + cbConvertUp.addActionListener((e) -> { 89 + options.convertZupToYup = cbConvertUp.isSelected(); 90 + }); 91 + cbConvertUp.setToolTipText("<html>" 92 + + "Paper Mario uses a coordinate system with Y pointing up.<br>" 93 + + "This option will attempt to convert from other coordinate systems." 94 + + "</html>"); 95 + 96 + FloatTextField scaleField = new FloatTextField((val) -> options.scale = val); 97 + scaleField.setValue(options.scale); 98 + scaleField.setHorizontalAlignment(JTextField.CENTER); 99 + 100 + FloatTextField uvScaleField = new FloatTextField((val) -> options.uvScale = val); 101 + uvScaleField.setValue(options.uvScale); 102 + uvScaleField.setHorizontalAlignment(JTextField.CENTER); 103 + 29 104 JButton selectButton = new JButton("Import"); 30 105 SwingUtils.addBorderPadding(selectButton); 31 106 selectButton.addActionListener((e) -> { 32 - //TODO select file 107 + result = ImportDialogResult.READY; 33 108 setVisible(false); 34 109 }); 35 110 ··· 48 123 } 49 124 }); 50 125 51 - fileTypeComboBox = new JComboBox<>(ImpexFormat.values()); 52 - SwingUtils.addBorderPadding(fileTypeComboBox); 126 + setLayout(new MigLayout("ins 16, fill, wrap 2, hidemode 3", "", "[]8[]")); 53 127 54 - cbSelectedOnly = new JCheckBox(" Selected object only"); 55 - cbSelectedOnly.setSelected(true); 128 + if (!hasDestNode) { 129 + add(new JLabel("Import as")); 130 + add(importAsBox, "growx, gapbottom 12"); 131 + } 132 + add(cbTriangulate, "span"); 133 + add(cbJoinVertices, "span"); 134 + add(cbConvertUp, "span, gapbottom 12"); 56 135 57 - setLayout(new MigLayout("ins 16, fill, hidemode 3, wrap 2")); 136 + add(new JLabel("Unit Scale"), "w 25%"); 137 + add(scaleField, "growx, sg field"); 58 138 59 - add(new JLabel("File Format")); 60 - add(fileTypeComboBox, "growx"); 61 - add(cbSelectedOnly, "span, growx, gapbottom 8"); 139 + add(new JLabel("UV Scale")); 140 + add(uvScaleField, "growx, sg field, gapbottom 12"); 62 141 63 - add(new JPanel(), "span, split 3, growx, sg but"); 142 + add(new JPanel(), "growx, sg but, span, split 3"); 143 + add(selectButton, "growx, sg but"); 64 144 add(cancelButton, "growx, sg but"); 65 - add(selectButton, "growx, sg but"); 66 145 67 146 pack(); 68 147 setResizable(false); ··· 70 149 setTitle(FRAME_TITLE); 71 150 setIconImage(Environment.getDefaultIconImage()); 72 151 setLocationRelativeTo(parent); 73 - setModal(false); 152 + setModal(true); 153 + } 154 + 155 + public ImportDialogResult getResult() 156 + { 157 + return result; 158 + } 159 + 160 + public AssimpImportOptions getOptions() 161 + { 162 + return options; 74 163 } 75 164 }
+6 -3
src/main/java/game/map/impex/ObjExporter.java
··· 7 7 import java.util.IdentityHashMap; 8 8 9 9 import app.input.IOUtils; 10 + import assets.AssetManager; 11 + import assets.AssetSubdir; 10 12 import common.Vector3f; 11 13 import game.map.hit.Collider; 12 14 import game.map.hit.Zone; ··· 30 32 vertexTable = new ArrayList<>(); 31 33 } 32 34 33 - public void writeModels(Iterable<Model> models, String mtlFilename) 35 + public void writeModels(Iterable<Model> models, String texName) 34 36 { 35 - if (!mtlFilename.isEmpty()) { 36 - pw.println("mtllib " + mtlFilename); 37 + File textureFile = AssetManager.get(AssetSubdir.MAP_TEX, texName + "/" + texName + ".mtl"); 38 + if (textureFile.exists()) { 39 + pw.println("mtllib " + texName); 37 40 pw.println(""); 38 41 } 39 42
+3
src/main/java/game/map/impex/ObjImporter.java
··· 192 192 float vScale = ModelTexture.getScaleV(currentTexture); 193 193 currentBatch.triangles.add(readTriangle(tokens, uScale, vScale)); 194 194 } 195 + else if (tokens[0].equals("f") && tokens.length == 5) { 196 + Logger.logWarning("Unsupported OBJ face quads: " + line); 197 + } 195 198 else 196 199 Logger.logWarning("Unsupported OBJ keyword: " + line); 197 200 }
+2 -2
src/main/java/game/map/tree/MapObjectJTree.java
··· 28 28 import javax.swing.tree.TreePath; 29 29 import javax.swing.tree.TreeSelectionModel; 30 30 31 + import common.commands.AbstractCommand; 31 32 import game.map.MapObject; 32 33 import game.map.editor.EditorShortcut; 33 34 import game.map.editor.MapEditor; 34 - import common.commands.AbstractCommand; 35 35 import game.map.editor.ui.GuiCommand; 36 36 import game.map.editor.ui.MapObjectPanel; 37 37 import game.map.editor.ui.MapObjectTreeCellRenderer; ··· 285 285 break; 286 286 287 287 case POPUP_IMPORT_HERE: 288 - editor.gui.prompt_ImportObjects(popupSource); 288 + editor.gui.prompt_Import(popupSource); 289 289 break; 290 290 291 291 case POPUP_PASTE_HERE:
+25 -25
src/main/java/game/message/StringConstants.java
··· 243 243 INPUT_ON (0, 0x08, "InputOn"), 244 244 DELAY_OFF (0, 0x09, "DelayOff"), 245 245 DELAY_ON (0, 0x0A, "DelayOn"), 246 - SPACING (1, 0x0B, 0x5, "Spacing"), //XXX Kerning --> CharWidth // char width override. forces all chars to have width = arg 246 + SPACING (1, 0x0B, 0x5, "Spacing"), // char width override. forces all chars to have width = arg 247 247 SCROLL (1, 0x0C, 0xFA, "Scroll"), // prints just FA; not FF,FA 248 248 SIZE (2, 0x0D, 0x6, "Size"), 249 249 SIZE_RESET (0, 0x0E, 0x7, "SizeReset"), 250 250 SPEED (2, 0x0F, "Speed"), 251 - SET_X (2, 0x10, 0x8, "SetPosX"), //XXX SetPrintPos --> SetPosX // args = (upper posX, lower posX) 252 - SET_Y (1, 0x11, 0x9, "SetPosY"), //XXX SetPrintY --> SetPosY // args = (posY) 253 - RIGHT (1, 0x12, 0xA, "Right"), //XXX ?? Indent --> Right 251 + SET_X (2, 0x10, 0x8, "SetPosX"), // args = (upper posX, lower posX) 252 + SET_Y (1, 0x11, 0x9, "SetPosY"), // args = (posY) 253 + RIGHT (1, 0x12, 0xA, "Right"), 254 254 DOWN (1, 0x13, 0xB, "Down"), 255 255 UP (1, 0x14, 0xC, "Up"), 256 - INLINE_IMAGE(1, 0x15, 0xE, "InlineImage"), //XXX Image1 --> InlineImage // args = (index) for small inline images printed at caret pos 257 - ANIM_SPRITE (3, 0x16, 0xF, "AnimSprite"), //XXX Sprite --> AnimSprite 258 - ITEM_ICON (2, 0x17, 0x10, "ItemIcon"), //XXX Item --> ItemIcon // args = (itemID upper, itemID lower) -- DEV ERROR in getproperties assumes this has only one arg! 259 - IMAGE (7, 0x18, "Image"), //XXX Image7 --> Image // args = (index, posX upper, posX lower, posY, hasBorder (can also be 2?), alphaFinal, alphaStep) 256 + INLINE_IMAGE(1, 0x15, 0xE, "InlineImage"), // args = (index) for small inline images printed at caret pos 257 + ANIM_SPRITE (3, 0x16, 0xF, "AnimSprite"), 258 + ITEM_ICON (2, 0x17, 0x10, "ItemIcon"), // args = (itemID upper, itemID lower) -- DEV ERROR in getproperties assumes this has only one arg! 259 + IMAGE (7, 0x18, "Image"), // args = (index, posX upper, posX lower, posY, hasBorder (can also be 2?), alphaFinal, alphaStep) 260 260 HIDE_IMAGE (1, 0x19, "HideImage"), // args = (fade amount per frame, 0 = instant) 261 - ANIM_DELAY (3, 0x1A, 0x11, "AnimDelay"), //XXX Func_1A --> AnimDelay 262 - ANIM_LOOP (2, 0x1B, 0x12, "AnimLoop"), //XXX Func_1B --> AnimLoop 263 - ANIM_DONE (1, 0x1C, 0x13, "AnimDone"), //XXX Func_1C --> AnimDone 261 + ANIM_DELAY (3, 0x1A, 0x11, "AnimDelay"), 262 + ANIM_LOOP (2, 0x1B, 0x12, "AnimLoop"), 263 + ANIM_DONE (1, 0x1C, 0x13, "AnimDone"), 264 264 SET_CURSOR (3, 0x1D, "SetCursorPos"), // args = (index, cursor[i] x, cursor[i] y) 265 265 CURSOR (1, 0x1E, 0x14, "Cursor"), // position in text where finger cursor for choice N 266 266 END_CHOICE (1, 0x1F, "EndChoice"), // end choices 267 - SET_CANCEL (1, 0x20, "SetCancel"), // sets the return value of the cancel button (b) 267 + SET_CANCEL (1, 0x20, "SetCancel"), // sets the return value of the cancel button (B) 268 268 OPTION (1, 0x21, 0x15, "Option"), // denotes text to highlight for each option 269 - PUSH_POS (0, 0x22, 0x18, "SavePos"), //XXX StartAnim --> SavePos 270 - POP_POS (0, 0x23, 0x19, "RestorePos"), //XXX EndAnim --> RestorePos 271 - PUSH_COLOR (0, 0x24, 0x1A, "SaveColor"), //XXX PushColor --> SaveColor (no stack is involved!) 272 - POP_COLOR (0, 0x25, 0x1B, "RestoreColor"),//XXX PopColor --> RestoreColor (no stack is involved!) 269 + PUSH_POS (0, 0x22, 0x18, "SavePos"), 270 + POP_POS (0, 0x23, 0x19, "RestorePos"), 271 + PUSH_COLOR (0, 0x24, 0x1A, "SaveColor"), 272 + POP_COLOR (0, 0x25, 0x1B, "RestoreColor"), 273 273 START_FX (-1, 0x26, 0x1C, "StartFX"), 274 274 END_FX (-1, 0x27, 0x1D, "EndFX"), 275 275 VAR (1, 0x28, "Var"), 276 276 CENTER_X (1, 0x29, 0x1E, "CenterX"), 277 277 SET_REWIND (1, 0x2A, "SetRewind"), // arg = set or clear print->stateFlags | 0x40000; 278 - ENABLE_CDOWN(0, 0x2B, "EnableCDownNext"), //XXX Func_2B --> AllowCDownNext // allows C-Down to advance to next page, print->stateFlags | 0x80000; 278 + ENABLE_CDOWN(0, 0x2B, "EnableCDownNext"), // allows C-Down to advance to next page 279 279 SETVOICE (8, 0x2C, "CustomVoice"), // args = soundA=(AA BB CC DD) soundB=(EE FF GG HH) 280 280 // 2D 281 281 VOLUME (1, 0x2E, "Volume"), ··· 317 317 public static enum StringEffect 318 318 { 319 319 // @formatter:off 320 - SHAKE (0, 0x00, 0x01, "Shake"), //XXX Jitter --> Shake 321 - WAVE (0, 0x01, 0x02, "Wave"), //XXX Wavy --> Wave 322 - NOISE_OUTLINE (0, 0x02, 0x04, "NoiseOutline"), //XXX Noise --> NoiseOutline 323 - STATIC (1, 0x03,0x10000, "Static"), //XXX FadedNoise --> StaticBlend (amount=d|percent=f) 320 + SHAKE (0, 0x00, 0x01, "Shake"), 321 + WAVE (0, 0x01, 0x02, "Wave"), 322 + NOISE_OUTLINE (0, 0x02, 0x04, "NoiseOutline"), 323 + STATIC (1, 0x03,0x10000, "Static"), // StaticBlend (amount=d|percent=f) 324 324 UNUSED (0, 0x04, 0, "???"), // there is no code for this 325 - BLUR (1, 0x05, 0x20, "Blur"), //XXX FadedJitter --> Blur (direction|dir)=(x|y|both) 325 + BLUR (1, 0x05, 0x20, "Blur"), // Blur (direction|dir)=(x|y|both) 326 326 RAINBOW (0, 0x06, 0x40, "Rainbow"), 327 - DITHER_FADE (1, 0x07, 0x80, "DitherFade"), //XXX Faded --> DitherFade (amount=d|percent=f) 328 - GLOBAL_WAVE (0, 0x08, 0x200, "GlobalWave"), //XXX WavyB --> GlobalWave 329 - GLOBAL_RAINBOW (0, 0x09, 0x400, "GlobalRainbow"), //XXX RainbowB --> GLobalRainbow 327 + DITHER_FADE (1, 0x07, 0x80, "DitherFade"), // DitherFade (amount=d|percent=f) 328 + GLOBAL_WAVE (0, 0x08, 0x200, "GlobalWave"), 329 + GLOBAL_RAINBOW (0, 0x09, 0x400, "GlobalRainbow"), 330 330 RISE_PRINT (0, 0x0A, 0x800, "PrintRising"), 331 331 GROW_PRINT (0, 0x0B, 0x1000, "PrintGrowing"), 332 332 SIZE_JITTER (0, 0x0C, 0x2000, "SizeJitter"),
+1 -1
src/main/java/game/sprite/editor/animators/command/SetUnknown.java
··· 49 49 public void fromXML(XmlReader xmr, Element elem) 50 50 { 51 51 if (xmr.hasAttribute(elem, ATTR_VALUE)) 52 - value = xmr.readInt(elem, ATTR_VALUE); //TODO check if hex... 52 + value = xmr.readInt(elem, ATTR_VALUE); 53 53 } 54 54 55 55 @Override