Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
3e9c08e
Set up for separate run.
ikeough Aug 1, 2022
8253269
A little more cleanup.
ikeough Aug 1, 2022
3a1910e
Rudimentary inputs working.
ikeough Aug 1, 2022
5ec7123
Styling tweaks.
ikeough Aug 1, 2022
b5285cf
Merge branch 'buffer-perf' into playground
ikeough Aug 1, 2022
63583dc
Cleanup.
ikeough Aug 1, 2022
85c654f
Run at start.
ikeough Aug 1, 2022
1db3e98
Shadows and visual tweaks.
ikeough Aug 1, 2022
01be8da
Typed inputs working.
ikeough Aug 2, 2022
e030fa8
Color inputs working.
ikeough Aug 2, 2022
cb9b026
Point inputs working.
ikeough Aug 2, 2022
243a9b7
Profile inputs working.
ikeough Aug 2, 2022
6f51451
Fix null ref exception on profle.
ikeough Aug 2, 2022
edccc7f
Editable profile names.
ikeough Aug 2, 2022
2e794fa
Scroll the inputs field.
ikeough Aug 2, 2022
44ef01a
Use HDR lighting.
ikeough Aug 2, 2022
7e6546b
Cleaning up the model.
ikeough Aug 2, 2022
dd9fc88
Move inputs to middle.
ikeough Aug 2, 2022
fdd0751
Style tweaks
ikeough Aug 2, 2022
d12d372
Get Ace editor from another CDN.
ikeough Aug 3, 2022
26d1edc
Transformable point working.
ikeough Aug 3, 2022
3128411
Update state when moving point.
ikeough Aug 3, 2022
c29bfc6
Fix binding syntax to avoid lag.
ikeough Aug 4, 2022
b1e43c9
Save the code between page loads.
ikeough Aug 4, 2022
4514660
Don't save the code.
ikeough Aug 4, 2022
ab558cb
Only set on construction.
ikeough Aug 4, 2022
89c5df5
Support multiple vector inputs.
ikeough Aug 4, 2022
821d13f
More static inputs.
ikeough Aug 4, 2022
7a6cf5c
Merge branch 'master' into playground
ikeough Aug 6, 2022
8b4125e
In memory images for textures.
ikeough Aug 7, 2022
b5c89ea
Merge branch 'master' into playground
ikeough Aug 22, 2022
b2dbf4c
Merge branch 'master' into playground
ikeough Aug 23, 2022
29324ac
Revert "In memory images for textures."
ikeough Aug 23, 2022
7c69270
Merge branch 'playground' into wasm
ikeough Aug 23, 2022
6e6b925
Fold elements.wasm into playground.
ikeough Aug 26, 2022
5409d45
Better documentation.
ikeough Aug 26, 2022
a7b41fd
Add some auto-generated VS stuff.
ikeough Aug 27, 2022
3960c99
A tiny demo app with documentation.
ikeough Aug 27, 2022
b9b2811
fixes for explore-based playground
andrewheumann Aug 29, 2022
7c2a0fe
add serve script
andrewheumann Aug 29, 2022
02ebfce
PR comments
andrewheumann Aug 29, 2022
1a4e7e1
Merge branch 'master' into wasm
ikeough Sep 1, 2022
e74a89c
Merge pull request #875 from hypar-io/wasm-explore-fixes
andrewheumann Sep 1, 2022
c850fff
Merge remote-tracking branch 'origin/master' into wasm
andrewheumann Oct 30, 2022
2d9e54f
add spatial to default imports
andrewheumann Oct 30, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/Elements.Playground/Elements.Playground.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/Elements.Playground/Elements.Playground.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/Elements.Playground/Elements.Playground.csproj"
],
"problemMatcher": "$msCompile"
}
]
}
11 changes: 1 addition & 10 deletions Elements.Playground/App.razor
Original file line number Diff line number Diff line change
@@ -1,10 +1 @@
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
<Elements.Playground.ElementsAPI />
60 changes: 33 additions & 27 deletions Elements.Playground/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@

namespace Elements.Playground
{
public class Globals
{
public string InputJson { get; set; }
}

public static class Compiler
{
class BlazorBoot
Expand All @@ -30,42 +35,39 @@ class Resources
public Dictionary<string, string> pdb { get; set; }
public Dictionary<string, string> runtime { get; set; }
}

private static Task InitializationTask;
private static List<MetadataReference> References;

public static void InitializeMetadataReferences(HttpClient client)
{
async Task InitializeInternal()
{
var model = new Model();
Console.WriteLine("Initializing the code editor...");
private static bool _isInitialized = false;

// TODO: This loads every assembly that is available. We should
// see if we can limit this to just the ones that we need.
var response = await client.GetFromJsonAsync<BlazorBoot>("_framework/blazor.boot.json");
var assemblies = await Task.WhenAll(response.resources.assembly.Keys.Select(x => client.GetAsync("_framework/" + x)));
var references = new List<MetadataReference>(assemblies.Length);
foreach (var asm in assemblies)
{
using var task = await asm.Content.ReadAsStreamAsync();
references.Add(MetadataReference.CreateFromStream(task));
}
References = references;
}
InitializationTask = InitializeInternal();
public static bool IsReady()
{
return _isInitialized;
}

public static Task WhenReady(Func<Task> action)
public static async Task InitializeMetadataReferences(HttpClient client)
{
if (InitializationTask.Status != TaskStatus.RanToCompletion)
if (_isInitialized)
{
return InitializationTask.ContinueWith(x => action());
return;
}
else
var model = new Model();
Console.WriteLine("Loading metadata references...");
// TODO: Make this conditional on some build flag, so we can easily
// deploy to prod or dev, or allow others cloning the project to
// spec the URL where they'll be hosting it.
var rootUrl = "https://elements.hypar.io/";
// TODO: This loads every assembly that is available. We should
// see if we can limit this to just the ones that we need.
var response = await client.GetFromJsonAsync<BlazorBoot>($"{rootUrl}blazor.boot.json");
var assemblies = await Task.WhenAll(response.resources.assembly.Keys.Select(x => client.GetAsync($"{rootUrl}{x}")));
var references = new List<MetadataReference>(assemblies.Length);
foreach (var asm in assemblies)
{
return action();
using var task = await asm.Content.ReadAsStreamAsync();
references.Add(MetadataReference.CreateFromStream(task));
}
References = references;
_isInitialized = true;
}

public static (bool success, Assembly asm, Compilation compilation) LoadSource(string source)
Expand All @@ -83,11 +85,15 @@ public static (bool success, Assembly asm, Compilation compilation) LoadSource(s
"System.Collections.Generic",
"System.Console",
"System.Linq",
"System.Text.Json",
"System.Text.Json.Serialization",
"Elements",
"Elements.Geometry",
"Elements.Geometry.Solids",
"Elements.Spatial",
"Elements.Geometry.Profiles",
"Elements.Validators"
}, concurrentBuild: false));
}, concurrentBuild: false), globalsType: typeof(Globals));

ImmutableArray<Diagnostic> diagnostics = compilation.GetDiagnostics();

Expand Down
8 changes: 3 additions & 5 deletions Elements.Playground/Elements.Playground.csproj
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<BlazorEnableCompression>false</BlazorEnableCompression>
<BlazorWebAssemblyEnableLinking>false</BlazorWebAssemblyEnableLinking>
<PublishTrimmed>false</PublishTrimmed>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.11.0" />
<PackageReference Include="System.Net.Http.Json" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.8" />
</ItemGroup>

<ItemGroup>
Expand Down
152 changes: 152 additions & 0 deletions Elements.Playground/ElementsAPI.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
@page "/"

@inject IJSRuntime JSRuntime

<div></div>

@code {

public class Result
{
public bool Success { get; set; }

public string Output { get; set; }

public string ModelJson { get; set; }
}

// Why do we need a component to call Elements compile and
// run elements code from the browser?
// 1. We need an HttpClient passed in from the framework. We can't
// create it because this code runs sandboxed in wasm. It has to
// come from blazor and the only way to do that is by injecting it
// as a service.
// 2. We need the the IJSRuntime to call methods in javascript. This
// also needs to be injected as a service.

private static Globals globals = new Globals();
private static IJSRuntime runtime;
[Inject] private HttpClient Client { get; set; }
private static Func<object[], Task> executionDelegate;

protected override async Task OnInitializedAsync()
{
runtime = JSRuntime;
await Compiler.InitializeMetadataReferences(Client);
await base.OnInitializedAsync();
await Task.FromResult(0);
}

protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
}

[JSInvokable]
public static async Task<Result> Run()
{
var sw = Stopwatch.StartNew();
var currentOut = Console.Out;
var writer = new StringWriter();
Console.SetOut(writer);
var success = false;

if (executionDelegate == null)
{
Console.WriteLine("There is nothing to execute. Try compiling first.");
Console.SetOut(currentOut);
sw.Stop();
return new Result()
{
Success = success,
Output = writer.ToString()
};
}

var model = await (Task<object>)executionDelegate(new object[] { globals, null }) as Elements.Model;
Console.WriteLine($"Run successful in {sw.ElapsedMilliseconds} ms");
sw.Reset();

await Task.Run(async () =>
{
sw.Start();
var glb = model.ToGlTF();
await runtime.InvokeVoidAsync("elements.loadModel", glb);
Console.WriteLine($"GlTF loaded in {sw.ElapsedMilliseconds} ms");
success = true;
});

Console.SetOut(currentOut);

sw.Stop();

return new Result()
{
Success = success,
Output = writer.ToString(),
ModelJson = model.ToJson()
};
}

[JSInvokable]
public static Result Compile(string code)
{
if (!Compiler.IsReady())
{
return new Result
{
Success = false,
Output = "Compiler not ready"
};
}
var sw = Stopwatch.StartNew();

var currentOut = Console.Out;
var writer = new StringWriter();
Console.SetOut(writer);
var compileSuccess = false;

// Redirect debug to the console.
Trace.Listeners.Clear();
Trace.Listeners.Add(
new TextWriterTraceListener(Console.Out));

executionDelegate = null;

// Compilation warnings will be written to console.out
// which is redirected here to the writer.
var (success, asm, compilation) = Compiler.LoadSource(code);
if (success)
{
var entryPoint = compilation.GetEntryPoint(CancellationToken.None);
var type = asm.GetType($"{entryPoint.ContainingNamespace.MetadataName}.{entryPoint.ContainingType.MetadataName}");
var entryPointMethod = type.GetMethod(entryPoint.MetadataName);

executionDelegate = (Func<object[], Task>)entryPointMethod.CreateDelegate(typeof(Func<object[], Task>));
Console.WriteLine($"Compilation successful in {sw.ElapsedMilliseconds} ms");
compileSuccess = true;
}
else
{
Console.WriteLine("Compilation was not successful. The execution will not run.");
compileSuccess = false;
}

Console.SetOut(currentOut);

sw.Stop();

return new Result()
{
Success = compileSuccess,
Output = writer.ToString()
};
}

[JSInvokable]
public static void UpdateInputs(string json)
{
globals.InputJson = json;
return;
}
}
Loading