Compare commits

..

10 commits

Author SHA1 Message Date
NGnius (Graham)
21ddbf698b Add info for common unsupported audio types 2020-09-28 09:42:18 -04:00
NGnius (Graham)
5aa13c2e82 Add text block support to RC importer & general tweaks to make that work 2020-09-27 20:29:02 -04:00
NGnius (Graham)
03c429fa72 Multi-thread optimisation algorithm 2020-09-26 15:55:36 -04:00
NGnius (Graham)
5e0d2a514c Optimise midi import block placement 2020-09-25 11:39:30 -04:00
NGnius (Graham)
7f9ef818ac Create prototype MIDI importer 2020-09-23 17:39:38 -04:00
NGnius (Graham)
d8064db54d Merge branch 'master' into preview 2020-09-23 10:09:15 -04:00
NGnius (Graham)
95a07a178e Convert to new GCIPA 2020-08-23 10:31:04 -04:00
NGnius (Graham)
ab1cd96e93 Merge branch 'master' into preview 2020-08-16 11:28:58 -04:00
NGnius (Graham)
1afff6c583 Use geometric (pythagorean) distance for nearest colour calculations 2020-07-20 09:07:41 -04:00
NGnius (Graham)
28d362cba4 Update refs for preview version 2020-07-20 08:47:58 -04:00
12 changed files with 559 additions and 1006 deletions

View file

@ -31,8 +31,6 @@ namespace Pixi.Audio
public static byte Key = 0; public static byte Key = 0;
public static float VolumeMultiplier = 1f;
public MidiImporter() public MidiImporter()
{ {
AudioTools.GenerateProgramMap(); AudioTools.GenerateProgramMap();
@ -51,6 +49,10 @@ namespace Pixi.Audio
Logging.MetaLog($"Found {midi.GetNotes().Count()} notes over {midi.GetDuration<MidiTimeSpan>().TimeSpan} time units"); Logging.MetaLog($"Found {midi.GetNotes().Count()} notes over {midi.GetDuration<MidiTimeSpan>().TimeSpan} time units");
BlockJsonInfo[] blocks = new BlockJsonInfo[(midi.GetNotes().Count() * 2) + 3]; BlockJsonInfo[] blocks = new BlockJsonInfo[(midi.GetNotes().Count() * 2) + 3];
List<BlockJsonInfo> blocksToBuild = new List<BlockJsonInfo>(); List<BlockJsonInfo> blocksToBuild = new List<BlockJsonInfo>();
#if DEBUG
// test (for faster, but incomplete, imports)
if (blocks.Length > 103) blocks = new BlockJsonInfo[103];
#endif
// convert Midi notes to sfx blocks // convert Midi notes to sfx blocks
Dictionary<long, uint> breadthCache = new Dictionary<long, uint>(); Dictionary<long, uint> breadthCache = new Dictionary<long, uint>();
Dictionary<long, uint> depthCache = new Dictionary<long, uint>(); Dictionary<long, uint> depthCache = new Dictionary<long, uint>();
@ -192,7 +194,7 @@ namespace Pixi.Audio
sfx.Pitch = n.NoteNumber - 60 + Key; // In MIDI, 60 is middle C, but GC uses 0 for middle C sfx.Pitch = n.NoteNumber - 60 + Key; // In MIDI, 60 is middle C, but GC uses 0 for middle C
sfx.TrackIndex = channelPrograms[n.Channel]; sfx.TrackIndex = channelPrograms[n.Channel];
sfx.Is3D = ThreeDee; sfx.Is3D = ThreeDee;
sfx.Volume = AudioTools.VelocityToVolume(n.Velocity) * VolumeMultiplier; sfx.Volume = AudioTools.VelocityToVolume(n.Velocity);
count++; count++;
// connect wires // connect wires
if (t == null) continue; // this should never happen if (t == null) continue; // this should never happen

View file

@ -17,15 +17,8 @@ namespace Pixi.Common
public static Dictionary<string, BlockJsonInfo[]> ParseBlueprintResource(string name) public static Dictionary<string, BlockJsonInfo[]> ParseBlueprintResource(string name)
{ {
StreamReader bluemap; StreamReader bluemap = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream(name));
#if DEBUG return JsonConvert.DeserializeObject<Dictionary<string, BlockJsonInfo[]>>(bluemap.ReadToEnd());
if (File.Exists(name))
bluemap = File.OpenText(name);
else
#endif
bluemap = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream(name));
using (bluemap)
return JsonConvert.DeserializeObject<Dictionary<string, BlockJsonInfo[]>>(bluemap.ReadToEnd());
} }
public static ProcessedVoxelObjectNotation[][] ProcessAndExpandBlocks(string name, BlockJsonInfo[] blocks, BlueprintProvider blueprints) public static ProcessedVoxelObjectNotation[][] ProcessAndExpandBlocks(string name, BlockJsonInfo[] blocks, BlueprintProvider blueprints)

View file

@ -564,58 +564,6 @@ namespace Pixi.Common
} }
} }
break; break;
case BlockIDs.DampedSpring:
string[] springSplit = pVONs[i].metadata.Split('\t');
if (springSplit.Length > 1 && float.TryParse(springSplit[1], out float stiffness))
{
DampedSpring d = blocks[i].Specialise<DampedSpring>();
d.Stiffness = stiffness;
if (springSplit.Length > 2 && float.TryParse(springSplit[2], out float damping))
{
d.Damping = damping;
}
}
break;
case BlockIDs.ServoAxle:
case BlockIDs.ServoHinge:
case BlockIDs.PneumaticAxle:
case BlockIDs.PneumaticHinge:
string[] servoSplit = pVONs[i].metadata.Split('\t');
if (servoSplit.Length > 1 && float.TryParse(servoSplit[1], out float minAngle))
{
Servo s = blocks[i].Specialise<Servo>();
s.MinimumAngle = minAngle;
if (servoSplit.Length > 2 && float.TryParse(servoSplit[2], out float maxAngle))
{
s.MaximumAngle = maxAngle;
if (servoSplit.Length > 3 && float.TryParse(servoSplit[3], out float maxForce))
{
s.MaximumForce = maxForce;
if (servoSplit.Length > 4 && bool.TryParse(servoSplit[4], out bool reverse))
{
s.Reverse = reverse;
}
}
}
}
break;
case BlockIDs.MotorM:
case BlockIDs.MotorS:
string[] motorSplit = pVONs[i].metadata.Split('\t');
if (motorSplit.Length > 1 && float.TryParse(motorSplit[1], out float topSpeed))
{
Motor m = blocks[i].Specialise<Motor>();
m.TopSpeed = topSpeed;
if (motorSplit.Length > 2 && float.TryParse(motorSplit[2], out float torque))
{
m.Torque = torque;
if (motorSplit.Length > 3 && bool.TryParse(motorSplit[3], out bool reverse))
{
m.Reverse = reverse;
}
}
}
break;
default: break; // do nothing default: break; // do nothing
} }
} }

View file

@ -42,7 +42,7 @@ namespace Pixi.Common
public static BlockJsonInfo JsonObject(Block block, float[] origin = null) public static BlockJsonInfo JsonObject(Block block, float[] origin = null)
{ {
if (origin == null) origin = origin_base; if (origin == null) origin = origin_base;
BlockJsonInfo jsonInfo = new BlockJsonInfo return new BlockJsonInfo
{ {
name = block.Type.ToString(), name = block.Type.ToString(),
position = new float[3] { block.Position.x - origin[0], block.Position.y - origin[1], block.Position.z - origin[2]}, position = new float[3] { block.Position.x - origin[0], block.Position.y - origin[1], block.Position.z - origin[2]},
@ -50,37 +50,6 @@ namespace Pixi.Common
color = ColorSpaceUtility.UnquantizeToArray(block.Color), color = ColorSpaceUtility.UnquantizeToArray(block.Color),
scale = new float[3] {block.Scale.x, block.Scale.y, block.Scale.z}, scale = new float[3] {block.Scale.x, block.Scale.y, block.Scale.z},
}; };
// custom stats for special blocks
switch (block.Type)
{
case BlockIDs.TextBlock:
TextBlock t = block.Specialise<TextBlock>();
jsonInfo.name += "\t" + t.Text + "\t" + t.TextBlockId;
break;
case BlockIDs.ConsoleBlock:
ConsoleBlock c = block.Specialise<ConsoleBlock>();
jsonInfo.name += "\t" + c.Command + "\t" + c.Arg1 + "\t" + c.Arg2 + "\t" + c.Arg3;
break;
case BlockIDs.DampedSpring:
DampedSpring d = block.Specialise<DampedSpring>();
jsonInfo.name += "\t" + d.Stiffness + "\t" + d.Damping;
break;
case BlockIDs.ServoAxle:
case BlockIDs.ServoHinge:
case BlockIDs.PneumaticAxle:
case BlockIDs.PneumaticHinge:
Servo s = block.Specialise<Servo>();
jsonInfo.name += "\t" + s.MinimumAngle + "\t" + s.MaximumAngle + "\t" + s.MaximumForce + "\t" +
s.Reverse;
break;
case BlockIDs.MotorM:
case BlockIDs.MotorS:
Motor m = block.Specialise<Motor>();
jsonInfo.name += "\t" + m.TopSpeed + "\t" + m.Torque + "\t" + m.Reverse;
break;
default: break;
}
return jsonInfo;
} }
public static BlockIDs NameToEnum(BlockJsonInfo block) public static BlockIDs NameToEnum(BlockJsonInfo block)

File diff suppressed because it is too large Load diff

View file

@ -47,14 +47,12 @@ namespace Pixi
root.Inject(new ImageTextBlockImporter()); root.Inject(new ImageTextBlockImporter());
root.Inject(new ImageCommandImporter()); root.Inject(new ImageCommandImporter());
// Robot functionality // Robot functionality
var robot = new RobotInternetImporter(); root.Inject(new RobotInternetImporter());
root.Inject(robot);
//RobotCommands.CreateRobotCRFCommand(); //RobotCommands.CreateRobotCRFCommand();
//RobotCommands.CreateRobotFileCommand(); //RobotCommands.CreateRobotFileCommand();
#if DEBUG #if DEBUG
// Development functionality // Development functionality
RobotCommands.CreatePartDumpCommand(); RobotCommands.CreatePartDumpCommand();
((RobotBlueprintProvider) robot.BlueprintProvider).AddDebugCommands();
root.Inject(new TestImporter()); root.Inject(new TestImporter());
#endif #endif
// Audio functionality // Audio functionality

View file

@ -215,10 +215,10 @@ namespace Pixi.Robots
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void TranslateBlockId(uint cubeId, ref CubeInfo result) private static void TranslateBlockId(uint cubeId, ref CubeInfo result)
{ {
if (map == null) if (map == null)
{ {
StreamReader cubemap = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("Pixi.cubes-id.json")); StreamReader cubemap = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("Pixi.cubes-id.json"));
map = JsonConvert.DeserializeObject<Dictionary<uint, string>>(cubemap.ReadToEnd()); map = JsonConvert.DeserializeObject<Dictionary<uint, string>>(cubemap.ReadToEnd());
} }
if (!map.ContainsKey(cubeId)) if (!map.ContainsKey(cubeId))
@ -231,34 +231,81 @@ namespace Pixi.Robots
#endif #endif
} }
string cubeName = map[cubeId]; string cubeName = map[cubeId];
string gcName = cubeName.Contains("glass") || cubeName.Contains("windshield")
? "Glass"
: "Aluminium";
if (cubeName.Contains("round"))
gcName += "Rounded";
if (cubeName.Contains("cube")) if (cubeName.Contains("cube"))
gcName += "Cube"; {
result.block = BlockIDs.AluminiumCube;
result.rotation = float3.zero;
}
else if (cubeName.Contains("prism") || cubeName.Contains("edge")) else if (cubeName.Contains("prism") || cubeName.Contains("edge"))
gcName += "Slope"; {
if (cubeName.Contains("round"))
{
if (cubeName.Contains("glass") || cubeName.Contains("windshield"))
{
result.block = BlockIDs.GlassRoundedSlope;
} else
result.block = BlockIDs.AluminiumRoundedSlope;
}
else
{
if (cubeName.Contains("glass") || cubeName.Contains("windshield"))
{
result.block = BlockIDs.GlassSlope;
} else
result.block = BlockIDs.AluminiumSlope;
}
}
else if (cubeName.Contains("inner")) else if (cubeName.Contains("inner"))
gcName += "SlicedCube"; {
if (cubeName.Contains("round"))
{
if (cubeName.Contains("glass") || cubeName.Contains("windshield"))
{
result.block = BlockIDs.GlassRoundedSlicedCube;
} else
result.block = BlockIDs.AluminiumRoundedSlicedCube;
}
else
{
if (cubeName.Contains("glass") || cubeName.Contains("windshield"))
{
result.block = BlockIDs.GlassSlicedCube;
} else
result.block = BlockIDs.AluminiumSlicedCube;
}
}
else if (cubeName.Contains("tetra") || cubeName.Contains("corner")) else if (cubeName.Contains("tetra") || cubeName.Contains("corner"))
gcName += "Corner"; {
if (cubeName.Contains("round"))
{
if (cubeName.Contains("glass") || cubeName.Contains("windshield"))
{
result.block = BlockIDs.GlassRoundedCorner;
} else
result.block = BlockIDs.AluminiumRoundedCorner;
}
else
{
if (cubeName.Contains("glass") || cubeName.Contains("windshield"))
{
result.block = BlockIDs.GlassCorner;
} else
result.block = BlockIDs.AluminiumCorner;
}
}
else if (cubeName.Contains("pyramid")) else if (cubeName.Contains("pyramid"))
gcName += "PyramidSegment"; {
result.block = BlockIDs.AluminiumPyramidSegment;
}
else if (cubeName.Contains("cone")) else if (cubeName.Contains("cone"))
gcName += "ConeSegment"; {
result.block = BlockIDs.AluminiumConeSegment;
}
else else
{ {
result.block = BlockIDs.TextBlock; result.block = BlockIDs.TextBlock;
result.name = cubeName; result.name = cubeName;
return;
} }
BlockIDs id = VoxelObjectNotationUtility.NameToEnum(gcName);
result.block = id;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]

View file

@ -1,14 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using Svelto.DataStructures; using Svelto.DataStructures;
using Unity.Mathematics; using Unity.Mathematics;
using UnityEngine; using UnityEngine;
using GamecraftModdingAPI.Blocks; using GamecraftModdingAPI.Blocks;
using GamecraftModdingAPI.Commands;
using GamecraftModdingAPI.Utility; using GamecraftModdingAPI.Utility;
using Newtonsoft.Json;
using Pixi.Common; using Pixi.Common;
namespace Pixi.Robots namespace Pixi.Robots
@ -35,8 +33,14 @@ namespace Pixi.Robots
if (!botprints.ContainsKey(root.name) || RobotInternetImporter.CubeSize != 3) if (!botprints.ContainsKey(root.name) || RobotInternetImporter.CubeSize != 3)
{ {
BlockJsonInfo copy = root; if (!parent.textBlockInfo.ContainsKey(name))
copy.name = $"TextBlock\t{root.name} ({CubeUtility.CubeIdDescription(uint.Parse(root.name))})\tPixi"; {
parent.textBlockInfo[name] = new FasterList<string>();
}
BlockJsonInfo copy = root;
copy.name = "TextBlock";
Logging.MetaLog($"Parsing uint from '{root.name}'");
parent.textBlockInfo[name].Add(root.name + " (" + CubeUtility.CubeIdDescription(uint.Parse(root.name)) + ")");
return new BlockJsonInfo[1] {copy}; return new BlockJsonInfo[1] {copy};
} }
BlockJsonInfo[] blueprint = botprints[root.name]; BlockJsonInfo[] blueprint = botprints[root.name];
@ -92,49 +96,5 @@ namespace Pixi.Robots
} }
return adjustedBlueprint; return adjustedBlueprint;
} }
#if DEBUG
public void AddDebugCommands()
{
CommandBuilder.Builder("PixiReload", "Reloads the robot blueprints")
.Action(() => botprints = null).Build();
CommandBuilder.Builder("RotateBlueprint",
"Rotates a blueprint with a given ID and dumps the result to a file. 1 means 90 degrees.")
.Action<string>(RotateBlueprint).Build();
}
private void RotateBlueprint(string parameters)
{
var p = parameters.Split(' ');
string id = p[0];
var xyz = new int[3];
for (int i = 0; i < xyz.Length; i++)
xyz[i] = int.Parse(p[i + 1]) * 90;
if (botprints == null)
{
botprints = BlueprintUtility.ParseBlueprintResource("Pixi.blueprints.json");
}
if (!botprints.ContainsKey(id))
{
Logging.CommandLogWarning("Blueprint with that ID not found.");
return;
}
var bp = botprints[id];
var rotChange = Quaternion.Euler(xyz[0], xyz[1], xyz[2]);
for (var i = 0; i < bp.Length; i++)
{
ref var info = ref bp[i];
var pos = ConversionUtility.FloatArrayToFloat3(info.position);
info.position = ConversionUtility.Float3ToFloatArray(rotChange * pos);
var rot = Quaternion.Euler(ConversionUtility.FloatArrayToFloat3(info.rotation));
info.rotation = ConversionUtility.Float3ToFloatArray((rotChange * rot).eulerAngles);
}
File.WriteAllText(id, JsonConvert.SerializeObject(bp));
Logging.CommandLog("Blueprint rotated " + rotChange.eulerAngles + " and dumped");
}
#endif
} }
} }

View file

@ -31,9 +31,7 @@ namespace Pixi.Robots
{ {
Player local = new Player(PlayerType.Local); Player local = new Player(PlayerType.Local);
Block baseBlock = local.GetBlockLookedAt(); Block baseBlock = local.GetBlockLookedAt();
Block[] blocks = local.GetSelectedBlocks(); Block[] blocks = baseBlock.GetConnectedCubes();
if (blocks.Length == 0)
blocks = baseBlock.GetConnectedCubes();
bool isBaseScaled = !(baseBlock.Scale.x > 0 && baseBlock.Scale.x < 2 && baseBlock.Scale.y > 0 && baseBlock.Scale.y < 2 && baseBlock.Scale.z > 0 && baseBlock.Scale.z < 2); bool isBaseScaled = !(baseBlock.Scale.x > 0 && baseBlock.Scale.x < 2 && baseBlock.Scale.y > 0 && baseBlock.Scale.y < 2 && baseBlock.Scale.z > 0 && baseBlock.Scale.z < 2);
if (isBaseScaled) if (isBaseScaled)
{ {

View file

@ -28,6 +28,8 @@ namespace Pixi.Robots
public static int CubeSize = 3; public static int CubeSize = 3;
internal readonly Dictionary<string, FasterList<string>> textBlockInfo = new Dictionary<string, FasterList<string>>();
public RobotInternetImporter() public RobotInternetImporter()
{ {
BlueprintProvider = new RobotBlueprintProvider(this); BlueprintProvider = new RobotBlueprintProvider(this);
@ -124,26 +126,34 @@ namespace Pixi.Robots
blocks[i].position += pos; blocks[i].position += pos;
} }
// set textblock colors (replace <color="white"> with <color=#HEX> in textblocks) // set textblock colors (replace <color="white"> with <color=#HEX> in textblocks)
Regex pattern = new Regex("<color=((?:\"white\")|(?:white))>", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); Regex pattern = new Regex("<color=(\"white\")|(white)>", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
for (int i = 0; i < blocks.Length; i++) for (int i = 0; i < blocks.Length; i++)
{ {
if (blocks[i].block == BlockIDs.TextBlock) if (blocks[i].block == BlockIDs.TextBlock)
{ {
// TODO this blindly replaces color tags anywhere in metadata, not just ones that will go in the TextBlock's text field // TODO this blindly replaces color tags anywhere in metadata, not just ones that will go in the TextBlock's text field
#if DEBUG
Logging.MetaLog($"Replacing text field in block with colour {blocks[i].color} with #{ColorUtility.ToHtmlStringRGBA(ColorSpaceUtility.UnquantizeToColor(blocks[i].color))}");
#endif
blocks[i].metadata = pattern.Replace( blocks[i].metadata = pattern.Replace(
blocks[i].metadata, blocks[i].metadata,
$"<color=#{ColorUtility.ToHtmlStringRGBA(ColorSpaceUtility.UnquantizeToColor(blocks[i].color))}>"); $"<color=#{ColorUtility.ToHtmlStringRGBA(ColorSpaceUtility.UnquantizeToColor(blocks[i].color))}>");
// NOTE: Regex.Replace replaces the whole match string only when there's a capture group (it's dumb, idk why).
// The non-capturing groups may be messing with .NET or something
} }
} }
} }
public void PostProcess(string name, ref Block[] blocks) public void PostProcess(string name, ref Block[] blocks)
{ {
int textBlockInfoIndex = 0;
for (int c = 0; c < blocks.Length; c++)
{
Block block = blocks[c];
// the goal is for this to never evaluate to true (ie all cubes are translated correctly)
if (block.Type == BlockIDs.TextBlock)
{
textBlockInfoIndex++;
block.Specialise<TextBlock>().Text = textBlockInfo[name][textBlockInfoIndex];
}
}
textBlockInfo.Remove(name);
} }
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long