summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgrothedev <grothedev@gmail.com>2024-12-20 22:43:07 -0600
committergrothedev <grothedev@gmail.com>2024-12-20 22:43:07 -0600
commitc24efb154121f2f98c1d9d2473e96e9e1ec7379d (patch)
tree4e621965b508f744d12bc4d720e4628bf7544896
parentb554f0614dd02924a536ce34f09fb1fc3d17bc3e (diff)
working on stuff
-rw-r--r--README.md4
-rw-r--r--app/Http/Controllers/FileController.php65
-rw-r--r--app/Http/Controllers/LinkController.php66
-rw-r--r--app/Http/Controllers/SiteController.php2
-rw-r--r--app/Http/Controllers/TagController.php65
-rw-r--r--app/Http/Requests/StoreLinkRequest.php28
-rw-r--r--app/Http/Requests/UpdateLinkRequest.php28
-rw-r--r--app/Models/File.php11
-rw-r--r--app/Models/Link.php12
-rw-r--r--app/Models/Tag.php11
-rw-r--r--app/Models/Traits/AutoFillable.php25
-rwxr-xr-xdatabase/migrations/0001_01_01_000000_create_users_table.php5
-rw-r--r--database/migrations/2024_12_21_021128_files.php32
-rw-r--r--database/migrations/2024_12_21_021157_tags.php29
-rw-r--r--database/migrations/2024_12_21_021206_quests.php27
-rw-r--r--database/migrations/2024_12_21_021215_links.php27
-rw-r--r--database/migrations/2024_12_21_032114_pivots.php44
-rw-r--r--resources/css/reset.css40
-rw-r--r--resources/css/style.css104
-rw-r--r--resources/js/main.js302
-rw-r--r--resources/js/marked.js38
-rw-r--r--resources/js/webgpu.js253
-rw-r--r--resources/js/webgpu_gltf.js514
-rwxr-xr-xresources/views/home.blade.php1
24 files changed, 1728 insertions, 5 deletions
diff --git a/README.md b/README.md
index b02d4e1..7356fde 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
+
+
+
https://laravel.com/api/11.x/
https://laravel.com/docs/11.x/blade
-
diff --git a/app/Http/Controllers/FileController.php b/app/Http/Controllers/FileController.php
new file mode 100644
index 0000000..c56b6f2
--- /dev/null
+++ b/app/Http/Controllers/FileController.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Models\File;
+use Illuminate\Http\Request;
+
+class FileController extends Controller
+{
+ /**
+ * Display a listing of the resource.
+ */
+ public function index()
+ {
+ //
+ }
+
+ /**
+ * Show the form for creating a new resource.
+ */
+ public function create()
+ {
+ //
+ }
+
+ /**
+ * Store a newly created resource in storage.
+ */
+ public function store(Request $request)
+ {
+ //
+ }
+
+ /**
+ * Display the specified resource.
+ */
+ public function show(File $file)
+ {
+ //
+ }
+
+ /**
+ * Show the form for editing the specified resource.
+ */
+ public function edit(File $file)
+ {
+ //
+ }
+
+ /**
+ * Update the specified resource in storage.
+ */
+ public function update(Request $request, File $file)
+ {
+ //
+ }
+
+ /**
+ * Remove the specified resource from storage.
+ */
+ public function destroy(File $file)
+ {
+ //
+ }
+}
diff --git a/app/Http/Controllers/LinkController.php b/app/Http/Controllers/LinkController.php
new file mode 100644
index 0000000..a175d2f
--- /dev/null
+++ b/app/Http/Controllers/LinkController.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Http\Requests\StoreLinkRequest;
+use App\Http\Requests\UpdateLinkRequest;
+use App\Models\Link;
+
+class LinkController extends Controller
+{
+ /**
+ * Display a listing of the resource.
+ */
+ public function index()
+ {
+ //
+ }
+
+ /**
+ * Show the form for creating a new resource.
+ */
+ public function create()
+ {
+ //
+ }
+
+ /**
+ * Store a newly created resource in storage.
+ */
+ public function store(StoreLinkRequest $request)
+ {
+ //
+ }
+
+ /**
+ * Display the specified resource.
+ */
+ public function show(Link $link)
+ {
+ //
+ }
+
+ /**
+ * Show the form for editing the specified resource.
+ */
+ public function edit(Link $link)
+ {
+ //
+ }
+
+ /**
+ * Update the specified resource in storage.
+ */
+ public function update(UpdateLinkRequest $request, Link $link)
+ {
+ //
+ }
+
+ /**
+ * Remove the specified resource from storage.
+ */
+ public function destroy(Link $link)
+ {
+ //
+ }
+}
diff --git a/app/Http/Controllers/SiteController.php b/app/Http/Controllers/SiteController.php
index 9607aea..a3f3fec 100644
--- a/app/Http/Controllers/SiteController.php
+++ b/app/Http/Controllers/SiteController.php
@@ -72,7 +72,7 @@ class SiteController extends Controller
return $res;
} else {
//return "File uploaded: ${filename} ";
- return redirect("f/${filename}");
+ return redirect("f/${filename}"); //TODO homepage with data flashing (->with())
}
}
diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php
new file mode 100644
index 0000000..754c75a
--- /dev/null
+++ b/app/Http/Controllers/TagController.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Models\Tag;
+use Illuminate\Http\Request;
+
+class TagController extends Controller
+{
+ /**
+ * Display a listing of the resource.
+ */
+ public function index()
+ {
+ //
+ }
+
+ /**
+ * Show the form for creating a new resource.
+ */
+ public function create()
+ {
+ //
+ }
+
+ /**
+ * Store a newly created resource in storage.
+ */
+ public function store(Request $request)
+ {
+ //
+ }
+
+ /**
+ * Display the specified resource.
+ */
+ public function show(Tag $tag)
+ {
+ //
+ }
+
+ /**
+ * Show the form for editing the specified resource.
+ */
+ public function edit(Tag $tag)
+ {
+ //
+ }
+
+ /**
+ * Update the specified resource in storage.
+ */
+ public function update(Request $request, Tag $tag)
+ {
+ //
+ }
+
+ /**
+ * Remove the specified resource from storage.
+ */
+ public function destroy(Tag $tag)
+ {
+ //
+ }
+}
diff --git a/app/Http/Requests/StoreLinkRequest.php b/app/Http/Requests/StoreLinkRequest.php
new file mode 100644
index 0000000..b3e52e1
--- /dev/null
+++ b/app/Http/Requests/StoreLinkRequest.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace App\Http\Requests;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class StoreLinkRequest extends FormRequest
+{
+ /**
+ * Determine if the user is authorized to make this request.
+ */
+ public function authorize(): bool
+ {
+ return false;
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
+ */
+ public function rules(): array
+ {
+ return [
+ //
+ ];
+ }
+}
diff --git a/app/Http/Requests/UpdateLinkRequest.php b/app/Http/Requests/UpdateLinkRequest.php
new file mode 100644
index 0000000..1d70c9a
--- /dev/null
+++ b/app/Http/Requests/UpdateLinkRequest.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace App\Http\Requests;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class UpdateLinkRequest extends FormRequest
+{
+ /**
+ * Determine if the user is authorized to make this request.
+ */
+ public function authorize(): bool
+ {
+ return false;
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
+ */
+ public function rules(): array
+ {
+ return [
+ //
+ ];
+ }
+}
diff --git a/app/Models/File.php b/app/Models/File.php
new file mode 100644
index 0000000..f66e6f9
--- /dev/null
+++ b/app/Models/File.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use App\Models\Traits\AutoFillable;
+
+class File extends Model
+{
+ use AutoFillable;
+}
diff --git a/app/Models/Link.php b/app/Models/Link.php
new file mode 100644
index 0000000..285c611
--- /dev/null
+++ b/app/Models/Link.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use App\Models\Traits\AutoFillable;
+
+class Link extends Model
+{
+ use AutoFillable;
+ //protected $fillable = [];
+}
diff --git a/app/Models/Tag.php b/app/Models/Tag.php
new file mode 100644
index 0000000..5a3da52
--- /dev/null
+++ b/app/Models/Tag.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use App\Models\Traits\AutoFillable;
+
+class Tag extends Model
+{
+ use Fillable;
+}
diff --git a/app/Models/Traits/AutoFillable.php b/app/Models/Traits/AutoFillable.php
new file mode 100644
index 0000000..044d5b8
--- /dev/null
+++ b/app/Models/Traits/AutoFillable.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace App\Models\Traits;
+
+use Illuminate\Support\Facades\Schema;
+
+trait AutoFillable
+{
+ public function initializeAutoFillable()
+ {
+ $table = $this->getTable();
+ $columns = Schema::getColumnListing($table);
+
+ $protected = ['id', 'created_at', 'updated_at', 'deleted_at'];
+ $this->fillable = array_diff($columns, $protected);
+ }
+}
+
+// Then in your model:
+use App\Models\Traits\AutoFillable;
+
+class User extends Model
+{
+ use AutoFillable;
+} \ No newline at end of file
diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php
index 05fb5d9..36a83ff 100755
--- a/database/migrations/0001_01_01_000000_create_users_table.php
+++ b/database/migrations/0001_01_01_000000_create_users_table.php
@@ -13,10 +13,11 @@ return new class extends Migration
{
Schema::create('users', function (Blueprint $table) {
$table->id();
- $table->string('name');
- $table->string('email')->unique();
+ $table->string('name')->required();
+ $table->string('email')->nullable()->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
+ $table->string('description');
$table->rememberToken();
$table->timestamps();
});
diff --git a/database/migrations/2024_12_21_021128_files.php b/database/migrations/2024_12_21_021128_files.php
new file mode 100644
index 0000000..62d1b2a
--- /dev/null
+++ b/database/migrations/2024_12_21_021128_files.php
@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+ /**
+ * Run the migrations.
+ */
+ public function up(): void
+ {
+ Schema::create('files', function (Blueprint $table) {
+ $table->id();
+ $table->timestamps();
+ $table->string('filename')->required();
+ $table->string('path')->unique();
+ $table->string('source');
+ $table->string('description');
+ $table->string('md5')->required();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('files');
+ }
+};
diff --git a/database/migrations/2024_12_21_021157_tags.php b/database/migrations/2024_12_21_021157_tags.php
new file mode 100644
index 0000000..ac79a2c
--- /dev/null
+++ b/database/migrations/2024_12_21_021157_tags.php
@@ -0,0 +1,29 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+ /**
+ * Run the migrations.
+ */
+ public function up(): void
+ {
+ Schema::create('tags', function (Blueprint $table) {
+ $table->id();
+ $table->timestamps();
+ $table->string('label')->required()->unique();
+ $table->integer('refs')->default(0);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('tags');
+ }
+};
diff --git a/database/migrations/2024_12_21_021206_quests.php b/database/migrations/2024_12_21_021206_quests.php
new file mode 100644
index 0000000..6cfb763
--- /dev/null
+++ b/database/migrations/2024_12_21_021206_quests.php
@@ -0,0 +1,27 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+ /**
+ * Run the migrations.
+ */
+ public function up(): void
+ {
+ Schema::create('quests', function (Blueprint $table) {
+ $table->id();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('quests');
+ }
+};
diff --git a/database/migrations/2024_12_21_021215_links.php b/database/migrations/2024_12_21_021215_links.php
new file mode 100644
index 0000000..51b2cbe
--- /dev/null
+++ b/database/migrations/2024_12_21_021215_links.php
@@ -0,0 +1,27 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+ /**
+ * Run the migrations.
+ */
+ public function up(): void
+ {
+ Schema::create('links', function (Blueprint $table) {
+ $table->id();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('links');
+ }
+};
diff --git a/database/migrations/2024_12_21_032114_pivots.php b/database/migrations/2024_12_21_032114_pivots.php
new file mode 100644
index 0000000..df65191
--- /dev/null
+++ b/database/migrations/2024_12_21_032114_pivots.php
@@ -0,0 +1,44 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+//the pivot tables for many-to-many
+return new class extends Migration
+{
+ /**
+ * Run the migrations.
+ */
+ public function up(): void
+ {
+ Schema::create('link_tag', function (Blueprint $table) {
+ $table->integer('link_id')->unsigned()->index();
+ $table->foreign('link_id')->references('id')->on('links')->onDelete('cascade');
+ $table->integer('tag_id')->unsigned()->index();
+ $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
+ });
+ Schema::create('file_tag', function (Blueprint $table) {
+ $table->integer('file_id')->unsigned()->index();
+ $table->foreign('file_id')->references('id')->on('files')->onDelete('cascade');
+ $table->integer('tag_id')->unsigned()->index();
+ $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
+ });
+ Schema::create('quest_tag', function (Blueprint $table) {
+ $table->integer('quest_id')->unsigned()->index();
+ $table->foreign('quest_id')->references('id')->on('quests')->onDelete('cascade');
+ $table->integer('tag_id')->unsigned()->index();
+ $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('link_tag');
+ Schema::dropIfExists('file_tag');
+ Schema::dropIfExists('quest_tag');
+ }
+};
diff --git a/resources/css/reset.css b/resources/css/reset.css
new file mode 100644
index 0000000..905b080
--- /dev/null
+++ b/resources/css/reset.css
@@ -0,0 +1,40 @@
+/* Reset CSS */
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display: block;
+}
+body {
+ line-height: 1;
+}
+ol, ul {
+ list-style: none;
+}
+blockquote, q {
+ quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+} \ No newline at end of file
diff --git a/resources/css/style.css b/resources/css/style.css
new file mode 100644
index 0000000..f92d792
--- /dev/null
+++ b/resources/css/style.css
@@ -0,0 +1,104 @@
+#bg {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: -1;
+}
+
+body * {
+ font-family: 'Roboto', sans-serif;
+ color: #1b1b1b;
+}
+
+main {
+ position: relative;
+ z-index: 1;
+ width: 100%;
+ height: 100%;
+ overflow-y: auto;
+ margin: 0px auto;
+ padding: 80px 12px;
+ display: block;
+ max-width: 800px;
+ justify-content: left;
+}
+
+section {
+ padding: 1.5rem;
+ justify-content: left;
+ margin: 36px 24px;
+ box-shadow: 0 0 10px rgba(0,0,0,0.1);
+ min-width: 200px;
+}
+
+section * {
+ padding: 2px;
+ margin: 2px;
+}
+
+h4 {
+ padding: 2px;
+}
+
+header {
+ padding: 8px 12px 12px;
+ background-color: #cdcdcd33;
+
+ box-shadow: 0 0 10px rgba(0,0,0,0.1);
+ min-width: 200px;
+}
+
+li a {
+ color: #2e3f25;
+ text-decoration: none;
+ word-break: break-all;
+}
+
+#links .section {
+ margin: 1rem 0;
+ padding: 1rem;
+}
+
+ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+#links li {
+ margin-bottom: .5rem;
+ padding-bottom: .5rem;
+ border-bottom: 1px solid #3e3e3e;
+}
+
+/* Remove border from last item */
+#links li:last-child {
+ border-bottom: none;
+ margin-bottom: 0;
+ padding-bottom: 0;
+}
+
+/* Link titles */
+#links li strong {
+ display: block;
+ margin-bottom: 0.5rem;
+ color: #2f2f2f;
+}
+
+/* Actual links */
+#links li a {
+ color: #2e3f25;
+ text-decoration: none;
+ word-break: break-all;
+}
+
+#links li a:hover {
+ text-decoration: underline;
+}
+
+/* Description text */
+#links li p {
+ margin: 0.5rem 0 0 .1f;
+ color: #444;
+ font-size: 0.9em;
+} \ No newline at end of file
diff --git a/resources/js/main.js b/resources/js/main.js
new file mode 100644
index 0000000..14a64dc
--- /dev/null
+++ b/resources/js/main.js
@@ -0,0 +1,302 @@
+import { io } from './socket.io.esm.min.js';
+import { drawScene } from './drawer.js';
+
+const CHUNK_SIZE = 4 * 1024 * 1024; // 4MB chunks
+const FILESIZE_THRESHOLD = 16 * 1024 * 1024; // 16MB // how big before use chunked upload
+const MAXFILESIZE = 2048 * 1024 * 1024; // 2GB
+const API_URL = 'http://192.168.4.32:9002';
+//const WS_URL = 'ws://localhost:80'; //10.50.231.35:80'; // belthelziquor.com:443'; //TODO get from cfg or env var
+const WS_URL = 'wss://belthelziquor.com:443';
+//const API_URL = 'http://localhost:9002';
+const color_bg = "#dedede";
+//TODO change color based on time
+
+var ctx = null;
+var cnv = null;
+var W = 800;
+var H = 600;
+var socket = null;
+var myID;
+
+var elemNumConnected;
+
+//each connected client. same format as server's model
+var friends = {
+
+};
+
+var me = [0,0]; //my cursor position
+var myNickname = '';
+
+var domElems = {};
+
+function prepareFileInput(){
+ var file_input = domElems.inputFile;
+ var files = file_input.files;
+ for (f in files) {
+ console.log(f);
+ }
+ //TODO validation
+ domElems.buttonFileUpload.enabled = true;
+}
+
+//APP START HERE
+$(document).ready(function() {
+ cnv = $('#bg')[0];
+ if (cnv != null) {
+ getServerEnvVars();
+ initDOM();
+
+ ctx = cnv.getContext("2d");
+ W = window.outerWidth;
+ H = window.outerHeight;
+ cnv.width = W;
+ cnv.height = H;
+ ctx.fillStyle = color_bg;
+ ctx.fillRect(0,0,W,H);
+
+ if (connectWebSocket()){
+ setInterval(() => {
+ socket.emit('update_pos', {pos: me, nick: myNickname});
+ }, 100);
+ } else {
+ console.error('Failed to connect web sockets');
+ domElems.debugInfo.innerHTML = 'Unable to connect to web socket server';
+ }
+ document.onmousemove = (e) => handleMouseMove(e);
+ setInterval(() => {
+ draw();
+ }, 20);
+ window.addEventListener('resize', resizeCanvas);
+ } else {
+ console.error('Canvas element not found');
+ }
+
+});
+/*
+var canvas = document.getElementById('canvas');// $('#canvas')[0];
+console.log(canvas);
+var context = canvas.getContext("2d");
+*/
+
+
+
+function getServerEnvVars(){
+ axios.get('/env').then((res)=>{
+ console.log(res);//TODO set api url from this
+ });
+}
+
+//initial setup such as hiding/showing certain things
+function initDOM(){
+ $('#fileupload')[0].hidden = false;
+ domElems.numConnected = $('#numFriendsConnected')[0];
+ domElems.inputFile = $('#f')[0];
+ domElems.debugInfo = $('#debugInfo')[0];
+ domElems.fileUploadResult = $('#fileupload_result')[0];
+ domElems.inputFile.onchange = prepareFileInput;
+ domElems.buttonFileUpload = $('#button_fileupload')[0];
+ //domElems.buttonFileUpload.enabled = false;
+ domElems.buttonFileUpload.onclick = uploadFile;
+ domElems.inputWho = $('#input_who')[0];
+ domElems.inputWho.onchange = ()=> { myNickname = domElems.inputWho.value; };
+}
+
+function resizeCanvas() {
+ W = window.outerWidth;
+ H = window.outerHeight;
+ cnv.width = W;
+ cnv.height = H;
+}
+
+//todo move the old code from file stuff etc to other file so that this file can be just for the canvas+graphics+networkedgame part of the client app
+async function uploadFile() {
+ var files = domElems.inputFile.files;
+ domElems.buttonFileUpload.enabled = false;
+ let formData = new FormData();
+ console.log(files);
+ for (let i = 0; i < files.length; i++) {
+ formData.append('f[]', files[i], files[i].name);
+ }
+
+ axios.post('/f', formData, {headers: {'Content-Type': 'multipart/form-data'}})
+ .then((res)=>{
+ console.log(res);
+ domElems.fileUploadResult.innerHTML = 'File uploaded successfully<br>';
+ res.data.files.forEach((f)=>{
+ domElems.fileUploadResult.innerHTML += `<a href = "f/${f.filename}">${f.filename}</a><br>`;
+ });
+ domElems.inputFile.value = null;
+ domElems.buttonFileUpload.enabled = true;
+ })
+ .catch((err)=>{
+ console.log(err);
+ domElems.fileUploadResult.innerHTML = 'Error uploading file';
+ domElems.buttonFileUpload.enabled = true;
+ });
+}
+
+async function uploadFileChunked(f){
+ const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
+ const filename = file.name;
+
+ for (let chunkNumber = 0; chunkNumber < totalChunks; chunkNumber++) {
+ const chunk = file.slice(chunkNumber * CHUNK_SIZE, (chunkNumber + 1) * CHUNK_SIZE);
+ const formData = new FormData();
+ formData.append('file', chunk);
+ formData.append('filename', filename);
+ formData.append('chunkNumber', chunkNumber);
+ formData.append('totalChunks', totalChunks);
+
+ try {
+ const response = await fetch(`${API_URL}/uploadfiles`, {
+ method: 'POST',
+ body: formData
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ updateProgress(chunkNumber + 1, totalChunks);
+
+ } catch (error) {
+ console.error('Error uploading chunk:', error);
+ alert('Error uploading file. Please try again.');
+ return;
+ }
+ }
+}
+
+async function uploadFileSingle(f){
+ const formData = new FormData();
+ formData.append('f', f);
+ const res = await fetch(`${API_URL}/uploadfiles`, {
+ method: 'POST',
+ body: formData,
+ credentials: 'include'
+ });
+ console.log(res);
+}
+
+function updateProgress(currentChunk, totalChunks) {
+ const progressPercentage = Math.round((currentChunk / totalChunks) * 100);
+ document.getElementById('progress').textContent = `${progressPercentage}% uploaded`;
+}
+
+
+function connectWebSocket(){
+ try {
+ socket = io(WS_URL, {
+ secure: true,
+ rejectUnauthorized: false
+ });
+ socket.on('connect_error', (err) => {
+ console.log('connection error');
+ console.log(err);
+ //TODO $('').textContent = err;
+ });
+ socket.on('connect_failed', (err) => {
+ console.log('conn failed');
+ console.log(err);
+ //TODO $('').textContent = err;
+ });
+ socket.on('disconnect', (err) => {
+ console.log('disconnected');
+ console.log(err);
+ //TODO $('').textContent = err;
+ });
+ socket.on('connect', (err) => {
+ console.log('connected');
+ if (err) console.log(err);
+ //TODO $('').textContent = 'Connected';
+ socket.pingTimeout = 1000;
+ socket.pingInterval = 500;
+ });
+ socket.on('syncCanvas', (data) => {
+ //TODO appShapes = data.shapes;
+ //TODO ClearBake();
+ //TODO DrawCanvas();
+ //TODO $('').textContent = 'Synced';
+ });
+ socket.on('init', (id) => {
+ myID = id;
+ });
+
+ //sync data from server. this is the data from server that should be replicated on each client
+ socket.on('sync_data', (data) => {
+ //data is a map of client id to client payload data (currently just the screen position)
+ friends = {};
+ for (const cid in data){
+ if (cid == myID){
+ continue;
+ }
+ friends[cid] = data[cid];
+ }
+ domElems.numConnected.innerHTML = Object.keys(friends).length + 1;
+ });
+ return true;
+ } catch (err) {
+ console.error(err);
+ return false;
+ }
+}
+
+
+function draw() {
+ //draw background
+ ctx.fillStyle = color_bg;
+ ctx.fillRect(0,0,W,H);
+
+ //draw me
+ ctx.fillStyle = "#000000";
+ ctx.fillRect(me[0], me[1], 4, 4);
+ if (myNickname != ''){
+ ctx.fillText(myNickname, me[0], me[1]);
+ }
+
+ //draw friends
+ Object.keys(friends).forEach((fid)=>{
+ ctx.fillStyle = "#050505";
+ let x = friends[fid].pos[0];
+ let y =friends[fid].pos[1];
+ ctx.fillRect(x, y, 6, 6);
+ if (friends[fid].nick != ''){
+ ctx.fillText(friends[fid].nick, x, y);
+ }
+ });
+
+ drawScene(ctx);
+
+}
+
+function getMousePos(canvas, evt) {
+ var rect = canvas.getBoundingClientRect();
+ /*return {
+ x: evt.clientX - rect.left,
+ y: evt.clientY - rect.top
+ };*/
+ return [evt.clientX - rect.left, evt.clientY - rect.top];
+}
+
+
+function handleMouseMove(e) {
+ var eDoc, doc, body;
+ e = e || window.event; // IE-ism
+ // If pageX/Y aren't available and clientX/Y are,
+ // calculate pageX/Y - logic taken from jQuery.
+ // (This is to support old IE)
+ if (e.pageX == null && e.clientX != null) {
+ eventDoc = (e.target && e.target.ownerDocument) || document;
+ doc = eventDoc.documentElement;
+ body = eventDoc.body;
+
+ e.pageX = e.clientX +
+ (doc && doc.scrollLeft || body && body.scrollLeft || 0) -
+ (doc && doc.clientLeft || body && body.clientLeft || 0);
+ e.pageY = e.clientY +
+ (doc && doc.scrollTop || body && body.scrollTop || 0) -
+ (doc && doc.clientTop || body && body.clientTop || 0 );
+ }
+ me = getMousePos(cnv, e);
+} \ No newline at end of file
diff --git a/resources/js/marked.js b/resources/js/marked.js
new file mode 100644
index 0000000..f193c5a
--- /dev/null
+++ b/resources/js/marked.js
@@ -0,0 +1,38 @@
+let mainContent;
+let inputText;
+let htmlResult; //the resulting html to display the inputted markup text
+let buttonSave;
+
+$(function() {
+ inputText = $('#input_text')[0];
+ mainContent = $('main')[0];
+ buttonSave = $('#button_save')[0];
+
+ inputText.oninput = ()=>{
+ htmlResult = marked.parse(inputText.value);
+ mainContent.innerHTML = htmlResult;
+ };
+
+ buttonSave.onclick = async ()=>{
+ //save to local file
+ console.log(htmlResult);
+
+ downloadFile(htmlResult, 'markdown-doc.html', 'text/plain');
+
+ };
+
+
+});
+
+function downloadFile(content, fileName, mimeType) {
+ const blob = new Blob([content], {type: mimeType});
+
+ const url = window.URL.createObjectURL(blob);
+
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = fileName;
+ a.click();
+
+ window.URL.revokeObjectURL(url);
+} \ No newline at end of file
diff --git a/resources/js/webgpu.js b/resources/js/webgpu.js
new file mode 100644
index 0000000..b66f9ab
--- /dev/null
+++ b/resources/js/webgpu.js
@@ -0,0 +1,253 @@
+//import { mat4, vec3 } from './gl-matrix/dist/gl-matrix.js';
+
+//import { formToJSON } from "axios";
+
+
+var ctx = null;
+var cnv = null;
+var wgc = null;
+var W = 800;
+var H = 600;
+
+const vertexShaderWGSL = `
+struct Uniforms {
+ modelViewProjectionMatrix : mat4x4<f32>,
+}
+@binding(0) @group(0) var<uniform> uniforms : Uniforms;
+
+struct VertexOutput {
+ @builtin(position) Position : vec4<f32>,
+ @location(0) color : vec4<f32>,
+}
+
+@vertex
+fn main(
+ @location(0) position : vec4<f32>,
+ @location(1) color : vec4<f32>
+) -> VertexOutput {
+ var output : VertexOutput;
+ output.Position = uniforms.modelViewProjectionMatrix * position;
+ output.color = color;
+ return output;
+}
+`;
+
+const fragmentShaderWGSL = `
+@fragment
+fn main(@location(0) color : vec4<f32>) -> @location(0) vec4<f32> {
+ return color;
+}
+`;
+
+$(document).ready(function() {
+ cnv = $('#c')[0];
+ if (cnv != null) {
+ //initDOM();
+ //ctx = cnv.getContext("2d");
+ W = window.outerWidth;
+ H = window.outerHeight;
+ cnv.width = W;
+ cnv.height = H;
+ //ctx.fillRect(0,0,W,H);
+
+ window.addEventListener('resize', resizeCanvas);
+ doWebGPUStuff();
+ } else {
+ console.error('Canvas element not found');
+ }
+});
+
+function resizeCanvas() {
+ W = window.outerWidth;
+ H = window.outerHeight;
+ cnv.width = W;
+ cnv.height = H;
+}
+
+async function doWebGPUStuff(){
+ console.log(navigator);
+ if (!navigator.gpu) {
+ console.error("WebGPU not supported on this browser.");
+ return;
+ }
+ const adapter = await navigator.gpu.requestAdapter();
+ if (!adapter) {
+ console.error("No appropriate GPUAdapter found.");
+ return;
+ }
+ console.log(adapter);
+
+ const device = await adapter.requestDevice();
+ console.log(device);
+
+ wgc = cnv.getContext("webgpu");
+ console.log(wgc);
+ const cnvFormat = navigator.gpu.getPreferredCanvasFormat();
+ wgc.configure({
+ device: device,
+ format: cnvFormat
+ });
+
+ const vertices = new Float32Array([
+ // Front face
+ -0.5, -0.5, 0.5, 1.0, 0.0, 0.0, 1.0,
+ 0.5, -0.5, 0.5, 1.0, 0.0, 0.0, 1.0,
+ 0.5, 0.5, 0.5, 1.0, 0.0, 0.0, 1.0,
+ -0.5, 0.5, 0.5, 1.0, 0.0, 0.0, 1.0,
+ // Back face
+ -0.5, -0.5, -0.5, 0.0, 1.0, 0.0, 1.0,
+ 0.5, -0.5, -0.5, 0.0, 1.0, 0.0, 1.0,
+ 0.5, 0.5, -0.5, 0.0, 1.0, 0.0, 1.0,
+ -0.5, 0.5, -0.5, 0.0, 1.0, 0.0, 1.0,
+ ]);
+
+ const indices = new Uint16Array([
+ 0, 1, 2, 0, 2, 3,
+ 1, 5, 6, 1, 6, 2,
+ 5, 4, 7, 5, 7, 6,
+ 4, 0, 3, 4, 3, 7,
+ 3, 2, 6, 3, 6, 7,
+ 4, 5, 1, 4, 1, 0
+ ]);
+
+ const vertexBuffer = device.createBuffer({
+ size: vertices.byteLength,
+ usage: GPUBufferUsage.VERTEX,
+ mappedAtCreation: true,
+ });
+ new Float32Array(vertexBuffer.getMappedRange()).set(vertices);
+ vertexBuffer.unmap();
+
+ const indexBuffer = device.createBuffer({
+ size: indices.byteLength,
+ usage: GPUBufferUsage.INDEX,
+ mappedAtCreation: true,
+ });
+ new Uint16Array(indexBuffer.getMappedRange()).set(indices);
+ indexBuffer.unmap();
+
+ const uniformBuffer = device.createBuffer({
+ size: 16 * 4,
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
+ });
+
+ const pipeline = device.createRenderPipeline({
+ layout: 'auto',
+ vertex: {
+ module: device.createShaderModule({
+ code: vertexShaderWGSL,
+ }),
+ entryPoint: 'main',
+ buffers: [
+ {
+ arrayStride: 7 * 4,
+ attributes: [
+ { shaderLocation: 0, offset: 0, format: 'float32x3' },
+ { shaderLocation: 1, offset: 3 * 4, format: 'float32x4' },
+ ],
+ },
+ ],
+ },
+ fragment: {
+ module: device.createShaderModule({
+ code: fragmentShaderWGSL,
+ }),
+ entryPoint: 'main',
+ targets: [
+ {
+ format: cnvFormat,
+ },
+ ],
+ },
+ primitive: {
+ topology: 'triangle-list',
+ },
+ depthStencil: {
+ depthWriteEnabled: true,
+ depthCompare: 'less',
+ format: 'depth24plus',
+ },
+ });
+
+ const depthTexture = device.createTexture({
+ size: [cnv.width, cnv.height],
+ format: 'depth24plus',
+ usage: GPUTextureUsage.RENDER_ATTACHMENT,
+ });
+
+ const uniformBindGroup = device.createBindGroup({
+ layout: pipeline.getBindGroupLayout(0),
+ entries: [
+ {
+ binding: 0,
+ resource: {
+ buffer: uniformBuffer,
+ },
+ },
+ ],
+ });
+
+ render(device);
+}
+
+function render (device) {
+ const aspect = cnv.width / cnv.height;
+ const projectionMatrix = mat4.create();
+ mat4.perspective(projectionMatrix, (2 * Math.PI) / 5, aspect, 1, 100.0);
+
+ const viewMatrix = mat4.create();
+ const eye = vec3.fromValues(0, 0, 4);
+ const center = vec3.fromValues(0, 0, 0);
+ const up = vec3.fromValues(0, 1, 0);
+ mat4.lookAt(viewMatrix, eye, center, up);
+
+ const modelViewProjectionMatrix = mat4.create();
+ const modelMatrix = mat4.create();
+ mat4.translate(modelMatrix, modelMatrix, [
+ (200 /*cursorPosition.x*/ / cnv.width) * 2 - 1,
+ -(( 200 /*cursorPosition.y*/ / cnv.height) * 2 - 1),
+ 0
+ ]);
+ mat4.rotate(modelMatrix, modelMatrix, Date.now() * 0.001, vec3.fromValues(0, 1, 0));
+
+ mat4.multiply(modelViewProjectionMatrix, viewMatrix, modelMatrix);
+ mat4.multiply(modelViewProjectionMatrix, projectionMatrix, modelViewProjectionMatrix);
+
+ device.queue.writeBuffer(
+ uniformBuffer,
+ 0,
+ modelViewProjectionMatrix.buffer,
+ modelViewProjectionMatrix.byteOffset,
+ modelViewProjectionMatrix.byteLength
+ );
+
+ const commandEncoder = device.createCommandEncoder();
+ const textureView = wgc.getCurrentTexture().createView();
+
+ const renderPassDescriptor = {
+ colorAttachments: [
+ {
+ view: textureView,
+ clearValue: { r: 0.1, g: 0.2, b: 0.3, a: 1.0 },
+ loadOp: 'clear',
+ storeOp: 'store',
+ },
+ ],
+ depthStencilAttachment: {
+ view: depthTexture.createView(),
+ depthClearValue: 1.0,
+ depthLoadOp: 'clear',
+ depthStoreOp: 'store',
+ },
+ };
+ const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+ passEncoder.setPipeline(pipeline);
+ passEncoder.setBindGroup(0, uniformBindGroup);
+ passEncoder.setVertexBuffer(0, vertexBuffer);
+ passEncoder.setIndexBuffer(indexBuffer, 'uint16');
+ passEncoder.drawIndexed(36);
+ passEncoder.end();
+
+ device.queue.submit([commandEncoder.finish()]);
+ requestAnimationFrame(render);
+}
diff --git a/resources/js/webgpu_gltf.js b/resources/js/webgpu_gltf.js
new file mode 100644
index 0000000..28e9742
--- /dev/null
+++ b/resources/js/webgpu_gltf.js
@@ -0,0 +1,514 @@
+//import { mat4, vec3 } from './gl-matrix/dist/gl-matrix.js';
+
+//import { formToJSON } from "axios";
+
+
+var ctx = null;
+var cnv = null;
+var wgc = null;
+var W = 800;
+var H = 600;
+
+const shaderCode = `
+struct Camera {
+ viewProjectionMatrix: mat4x4f,
+ cameraPosition: vec4f,
+}
+
+struct Model {
+ modelMatrix: mat4x4f,
+}
+
+@group(0) @binding(0) var<uniform> camera: Camera;
+@group(0) @binding(1) var<uniform> model: Model;
+
+struct VertexInput {
+ @location(0) position: vec3f,
+ @location(1) normal: vec3f,
+ @location(2) uv: vec2f,
+}
+
+struct VertexOutput {
+ @builtin(position) position: vec4f,
+ @location(0) worldPos: vec3f,
+ @location(1) normal: vec3f,
+ @location(2) uv: vec2f,
+}
+
+@vertex
+fn vertexMain(input: VertexInput) -> VertexOutput {
+ var output: VertexOutput;
+ let worldPosition = model.modelMatrix * vec4f(input.position, 1.0);
+ output.position = camera.viewProjectionMatrix * worldPosition;
+ output.worldPos = worldPosition.xyz;
+ output.normal = normalize((model.modelMatrix * vec4f(input.normal, 0.0)).xyz);
+ output.uv = input.uv;
+ return output;
+}
+
+@fragment
+fn fragmentMain(input: VertexOutput) -> @location(0) vec4f {
+ let lightDir = normalize(vec3f(1.0, 1.0, 1.0));
+ let normal = normalize(input.normal);
+ let diffuse = max(dot(normal, lightDir), 0.0);
+ let ambient = 0.1;
+ let color = vec3f(0.7, 0.7, 0.7);
+ return vec4f(color * (diffuse + ambient), 1.0);
+}`;
+
+class GLTFRenderer {
+ constructor(canvas) {
+ this.canvas = canvas;
+ this.rotation = 0;
+ }
+
+ async initialize() {
+ if (!navigator.gpu) {
+ throw new Error('WebGPU not supported');
+ }
+
+ const adapter = await navigator.gpu.requestAdapter();
+ if (!adapter) {
+ throw new Error('No GPU adapter found');
+ }
+
+ this.device = await adapter.requestDevice();
+ this.context = this.canvas.getContext('webgpu');
+
+ const format = navigator.gpu.getPreferredCanvasFormat();
+ this.context.configure({
+ device: this.device,
+ format: format,
+ alphaMode: 'premultiplied',
+ });
+
+ // Create pipeline
+ this.pipeline = this.device.createRenderPipeline({
+ layout: 'auto',
+ vertex: {
+ module: this.device.createShaderModule({
+ code: shaderCode
+ }),
+ entryPoint: 'vertexMain',
+ buffers: [
+ {
+ // position
+ arrayStride: 12,
+ attributes: [{
+ shaderLocation: 0,
+ offset: 0,
+ format: 'float32x3'
+ }]
+ },
+ {
+ // normal
+ arrayStride: 12,
+ attributes: [{
+ shaderLocation: 1,
+ offset: 0,
+ format: 'float32x3'
+ }]
+ },
+ {
+ // uv
+ arrayStride: 8,
+ attributes: [{
+ shaderLocation: 2,
+ offset: 0,
+ format: 'float32x2'
+ }]
+ }
+ ]
+ },
+ fragment: {
+ module: this.device.createShaderModule({
+ code: shaderCode
+ }),
+ entryPoint: 'fragmentMain',
+ targets: [{
+ format: format
+ }]
+ },
+ primitive: {
+ topology: 'triangle-list',
+ cullMode: 'back'
+ },
+ depthStencil: {
+ depthWriteEnabled: true,
+ depthCompare: 'less',
+ format: 'depth24plus'
+ }
+ });
+
+ // Create depth texture
+ const depthTexture = this.device.createTexture({
+ size: [this.canvas.width, this.canvas.height],
+ format: 'depth24plus',
+ usage: GPUTextureUsage.RENDER_ATTACHMENT
+ });
+
+ this.depthView = depthTexture.createView();
+ }
+
+ async loadGLTF(url) {
+ const response = await fetch(url);
+ const gltfData = await response.json();
+
+ // For this example, we'll assume the first mesh, first primitive
+ const primitive = gltfData.meshes[0].primitives[0];
+
+ // Load buffers
+ const bufferResponse = await fetch(url.replace('gltf', 'bin'));
+ const bufferData = await bufferResponse.arrayBuffer();
+
+ // Process vertices
+ const positionAccessor = gltfData.accessors[primitive.attributes.POSITION];
+ const positionView = gltfData.bufferViews[positionAccessor.bufferView];
+ const positionData = new Float32Array(bufferData, positionView.byteOffset, positionAccessor.count * 3);
+
+ // Create vertex buffer
+ this.vertexBuffer = this.device.createBuffer({
+ size: positionData.byteLength,
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
+ });
+ this.device.queue.writeBuffer(this.vertexBuffer, 0, positionData);
+
+ // Process indices
+ const indexAccessor = gltfData.accessors[primitive.indices];
+ const indexView = gltfData.bufferViews[indexAccessor.bufferView];
+ const indexData = new Uint16Array(bufferData, indexView.byteOffset, indexAccessor.count);
+
+ // Create index buffer
+ this.indexBuffer = this.device.createBuffer({
+ size: indexData.byteLength,
+ usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
+ });
+ this.device.queue.writeBuffer(this.indexBuffer, 0, indexData);
+
+ this.indexCount = indexAccessor.count;
+ }
+
+ createMatrix4(values) {
+ return new Float32Array(values);
+ }
+
+ async render() {
+ // Update rotation
+ this.rotation += 0.01;
+
+ // Create uniform buffers for camera and model
+ const cameraUniformBuffer = this.device.createBuffer({
+ size: 64 + 16, // mat4 + vec4
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
+ });
+
+ const modelUniformBuffer = this.device.createBuffer({
+ size: 64, // mat4
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
+ });
+
+ // Create view-projection matrix
+ const aspect = this.canvas.width / this.canvas.height;
+ const projectionMatrix = this.createMatrix4([
+ 1 / (aspect * Math.tan(Math.PI / 4)), 0, 0, 0,
+ 0, 1 / Math.tan(Math.PI / 4), 0, 0,
+ 0, 0, -1, -1,
+ 0, 0, -0.1, 0
+ ]);
+
+ const viewMatrix = this.createMatrix4([
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, -5, 1
+ ]);
+
+ // Create model matrix with rotation
+ const modelMatrix = this.createMatrix4([
+ Math.cos(this.rotation), 0, Math.sin(this.rotation), 0,
+ 0, 1, 0, 0,
+ -Math.sin(this.rotation), 0, Math.cos(this.rotation), 0,
+ 0, 0, 0, 1
+ ]);
+
+ // Write uniform data
+ this.device.queue.writeBuffer(cameraUniformBuffer, 0, projectionMatrix);
+ this.device.queue.writeBuffer(cameraUniformBuffer, 64, new Float32Array([0, 0, -5, 1]));
+ this.device.queue.writeBuffer(modelUniformBuffer, 0, modelMatrix);
+
+ // Create bind group
+ const bindGroup = this.device.createBindGroup({
+ layout: this.pipeline.getBindGroupLayout(0),
+ entries: [
+ {
+ binding: 0,
+ resource: { buffer: cameraUniformBuffer }
+ },
+ {
+ binding: 1,
+ resource: { buffer: modelUniformBuffer }
+ }
+ ]
+ });
+
+ // Create command encoder
+ const commandEncoder = this.device.createCommandEncoder();
+ const renderPass = commandEncoder.beginRenderPass({
+ colorAttachments: [{
+ view: this.context.getCurrentTexture().createView(),
+ clearValue: { r: 0.1, g: 0.1, b: 0.1, a: 1.0 },
+ loadOp: 'clear',
+ storeOp: 'store'
+ }],
+ depthStencilAttachment: {
+ view: this.depthView,
+ depthClearValue: 1.0,
+ depthLoadOp: 'clear',
+ depthStoreOp: 'store',
+ }
+ });
+
+ renderPass.setPipeline(this.pipeline);
+ renderPass.setBindGroup(0, bindGroup);
+ renderPass.setVertexBuffer(0, this.vertexBuffer);
+ renderPass.setIndexBuffer(this.indexBuffer, 'uint16');
+ renderPass.drawIndexed(this.indexCount);
+ renderPass.end();
+
+ // Submit commands
+ this.device.queue.submit([commandEncoder.finish()]);
+ }
+
+ async start() {
+ const render = () => {
+ this.render();
+ requestAnimationFrame(render);
+ };
+ render();
+ }
+}
+
+
+$(document).ready(async function() {
+ cnv = $('#c')[0];
+ if (cnv != null) {
+ //initDOM();
+ //ctx = cnv.getContext("2d");
+ W = window.outerWidth;
+ H = window.outerHeight;
+ cnv.width = W;
+ cnv.height = H;
+ //ctx.fillRect(0,0,W,H);
+
+ window.addEventListener('resize', resizeCanvas);
+ try {
+ const renderer = new GLTFRenderer(cnv);
+ await renderer.initialize();
+ await renderer.loadGLTF('cube.gltf'); // Make sure to have your GLTF file available
+ renderer.start();
+ } catch (error) {
+ const errorDiv = document.getElementById('error');
+ errorDiv.style.display = 'block';
+ errorDiv.textContent = 'Error: ' + error.message;
+ console.error(error);
+ }
+ doWebGPUStuff();
+ } else {
+ console.error('Canvas element not found');
+ }
+});
+
+function resizeCanvas() {
+ W = window.outerWidth;
+ H = window.outerHeight;
+ cnv.width = W;
+ cnv.height = H;
+}
+
+async function doWebGPUStuff(){
+ console.log(navigator);
+ if (!navigator.gpu) {
+ console.error("WebGPU not supported on this browser.");
+ return;
+ }
+ const adapter = await navigator.gpu.requestAdapter();
+ if (!adapter) {
+ console.error("No appropriate GPUAdapter found.");
+ return;
+ }
+ console.log(adapter);
+
+ const device = await adapter.requestDevice();
+ console.log(device);
+
+ wgc = cnv.getContext("webgpu");
+ console.log(wgc);
+ const cnvFormat = navigator.gpu.getPreferredCanvasFormat();
+ wgc.configure({
+ device: device,
+ format: cnvFormat
+ });
+
+ const vertices = new Float32Array([
+ // Front face
+ -0.5, -0.5, 0.5, 1.0, 0.0, 0.0, 1.0,
+ 0.5, -0.5, 0.5, 1.0, 0.0, 0.0, 1.0,
+ 0.5, 0.5, 0.5, 1.0, 0.0, 0.0, 1.0,
+ -0.5, 0.5, 0.5, 1.0, 0.0, 0.0, 1.0,
+ // Back face
+ -0.5, -0.5, -0.5, 0.0, 1.0, 0.0, 1.0,
+ 0.5, -0.5, -0.5, 0.0, 1.0, 0.0, 1.0,
+ 0.5, 0.5, -0.5, 0.0, 1.0, 0.0, 1.0,
+ -0.5, 0.5, -0.5, 0.0, 1.0, 0.0, 1.0,
+ ]);
+
+ const indices = new Uint16Array([
+ 0, 1, 2, 0, 2, 3,
+ 1, 5, 6, 1, 6, 2,
+ 5, 4, 7, 5, 7, 6,
+ 4, 0, 3, 4, 3, 7,
+ 3, 2, 6, 3, 6, 7,
+ 4, 5, 1, 4, 1, 0
+ ]);
+
+ const vertexBuffer = device.createBuffer({
+ size: vertices.byteLength,
+ usage: GPUBufferUsage.VERTEX,
+ mappedAtCreation: true,
+ });
+ new Float32Array(vertexBuffer.getMappedRange()).set(vertices);
+ vertexBuffer.unmap();
+
+ const indexBuffer = device.createBuffer({
+ size: indices.byteLength,
+ usage: GPUBufferUsage.INDEX,
+ mappedAtCreation: true,
+ });
+ new Uint16Array(indexBuffer.getMappedRange()).set(indices);
+ indexBuffer.unmap();
+
+ const uniformBuffer = device.createBuffer({
+ size: 16 * 4,
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
+ });
+
+ const pipeline = device.createRenderPipeline({
+ layout: 'auto',
+ vertex: {
+ module: device.createShaderModule({
+ code: vertexShaderWGSL,
+ }),
+ entryPoint: 'main',
+ buffers: [
+ {
+ arrayStride: 7 * 4,
+ attributes: [
+ { shaderLocation: 0, offset: 0, format: 'float32x3' },
+ { shaderLocation: 1, offset: 3 * 4, format: 'float32x4' },
+ ],
+ },
+ ],
+ },
+ fragment: {
+ module: device.createShaderModule({
+ code: fragmentShaderWGSL,
+ }),
+ entryPoint: 'main',
+ targets: [
+ {
+ format: cnvFormat,
+ },
+ ],
+ },
+ primitive: {
+ topology: 'triangle-list',
+ },
+ depthStencil: {
+ depthWriteEnabled: true,
+ depthCompare: 'less',
+ format: 'depth24plus',
+ },
+ });
+
+ const depthTexture = device.createTexture({
+ size: [cnv.width, cnv.height],
+ format: 'depth24plus',
+ usage: GPUTextureUsage.RENDER_ATTACHMENT,
+ });
+
+ const uniformBindGroup = device.createBindGroup({
+ layout: pipeline.getBindGroupLayout(0),
+ entries: [
+ {
+ binding: 0,
+ resource: {
+ buffer: uniformBuffer,
+ },
+ },
+ ],
+ });
+
+ render(device);
+}
+
+function render (device) {
+ const aspect = cnv.width / cnv.height;
+ const projectionMatrix = mat4.create();
+ mat4.perspective(projectionMatrix, (2 * Math.PI) / 5, aspect, 1, 100.0);
+
+ const viewMatrix = mat4.create();
+ const eye = vec3.fromValues(0, 0, 4);
+ const center = vec3.fromValues(0, 0, 0);
+ const up = vec3.fromValues(0, 1, 0);
+ mat4.lookAt(viewMatrix, eye, center, up);
+
+ const modelViewProjectionMatrix = mat4.create();
+ const modelMatrix = mat4.create();
+ mat4.translate(modelMatrix, modelMatrix, [
+ (200 /*cursorPosition.x*/ / cnv.width) * 2 - 1,
+ -(( 200 /*cursorPosition.y*/ / cnv.height) * 2 - 1),
+ 0
+ ]);
+ mat4.rotate(modelMatrix, modelMatrix, Date.now() * 0.001, vec3.fromValues(0, 1, 0));
+
+ mat4.multiply(modelViewProjectionMatrix, viewMatrix, modelMatrix);
+ mat4.multiply(modelViewProjectionMatrix, projectionMatrix, modelViewProjectionMatrix);
+
+ device.queue.writeBuffer(
+ uniformBuffer,
+ 0,
+ modelViewProjectionMatrix.buffer,
+ modelViewProjectionMatrix.byteOffset,
+ modelViewProjectionMatrix.byteLength
+ );
+
+ const commandEncoder = device.createCommandEncoder();
+ const textureView = wgc.getCurrentTexture().createView();
+
+ const renderPassDescriptor = {
+ colorAttachments: [
+ {
+ view: textureView,
+ clearValue: { r: 0.1, g: 0.2, b: 0.3, a: 1.0 },
+ loadOp: 'clear',
+ storeOp: 'store',
+ },
+ ],
+ depthStencilAttachment: {
+ view: depthTexture.createView(),
+ depthClearValue: 1.0,
+ depthLoadOp: 'clear',
+ depthStoreOp: 'store',
+ },
+ };
+ const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+ passEncoder.setPipeline(pipeline);
+ passEncoder.setBindGroup(0, uniformBindGroup);
+ passEncoder.setVertexBuffer(0, vertexBuffer);
+ passEncoder.setIndexBuffer(indexBuffer, 'uint16');
+ passEncoder.drawIndexed(36);
+ passEncoder.end();
+
+ device.queue.submit([commandEncoder.finish()]);
+ requestAnimationFrame(render);
+}
diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php
index 0a5d12f..c7d6102 100755
--- a/resources/views/home.blade.php
+++ b/resources/views/home.blade.php
@@ -38,7 +38,6 @@
<input hidden name = "response_format" value = "html" />
<button type = "submit">Upload</button>
</form>
- <p>For now, you will get a json respone upon upload. Just navigate to /f to access. I might update this to return html if useragent is a browser</p>
</section>
<!--</noscript>-->
<section id = "fileupload" hidden="true">