Whole load of stuff. Renders, admin management bar, CDN, you name it!

This commit is contained in:
Graphictoria 2022-08-15 17:06:17 -04:00
parent e4ef85a1ce
commit 70f2f88394
36 changed files with 898 additions and 118 deletions

BIN
etc/art/assetbusy.pdn Normal file

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,5 @@
REM : XlXi 2022
REM : Put this in the directory of your PNG export.
REM : ImageMagick is required.
magick convert -delay 333,10000 -loop 0 -alpha set -dispose previous *.png ani.gif

View File

@ -29,6 +29,9 @@
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI}
DocumentRoot "D:/wamp320/graphictoria/sitetest3/web/public"
</VirtualHost>

View File

@ -0,0 +1,51 @@
<?php
/*
Graphictoria 2022
CDN helper.
*/
namespace App\Helpers;
use Illuminate\Support\Facades\Storage;
use Illuminate\Http\Request;
use App\Models\CdnHash;
class CdnHelper
{
public static function GetDisk()
{
return Storage::build([
'driver' => 'local',
'root' => storage_path('app/content'),
]);
}
public static function Hash($content)
{
return hash('sha256', $content);
}
public static function SaveContent($content, $mime)
{
$disk = self::GetDisk();
$hash = self::Hash($content);
if(!$disk->exists($hash) || !CdnHash::where('hash', $hash)->exists()) {
$disk->put($hash, $content);
$cdnItem = new CdnHash();
$cdnItem->hash = $hash;
$cdnItem->mime_type = $mime;
$cdnItem->save();
}
return $hash;
}
public static function SaveContentB64($contentB64, $mime)
{
return self::SaveContent(base64_decode($contentB64), $mime);
}
}

View File

@ -115,11 +115,13 @@ class QAaMBHelper
{
$memoryInfo = self::getSystemMemoryInfo();
// XlXi: the -2 is required so it fits inside of the bar thing
// XlXi: change this if theres ever a separate graph
return sprintf(
'%s of %s (%s%%) <br/> %s Free',
self::memoryString($memoryInfo['MemTotal'] - $memoryInfo['MemFree']),
self::memoryString($memoryInfo['MemTotal']),
round(self::getMemoryPercentage() * 100),
round(self::getMemoryPercentage() * (100-2)),
self::memoryString($memoryInfo['MemFree'])
);
}
@ -133,9 +135,11 @@ class QAaMBHelper
public static function getCpuUsage()
{
// XlXi: the -2 is required so it fits inside of the bar thing
// XlXi: change this if theres ever a separate graph
return sprintf(
'%s%% CPU Usage',
round(self::getSystemCpuInfo() * 100)
round(self::getSystemCpuInfo() * (100-2))
);
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use App\Helpers\ValidationHelper;
use App\Http\Controllers\Controller;
use App\Jobs\ArbiterRender;
use App\Models\Asset;
use App\Models\RenderTracker;
class ThumbnailController extends Controller
{
public function renderAsset(Request $request)
{
$validator = Validator::make($request->all(), [
'id' => [
'required',
Rule::exists('App\Models\Asset', 'id')->where(function($query) {
return $query->where('moderated', false);
})
],
'type' => 'regex:/(3D|2D)/i'
]);
if($validator->fails())
return ValidationHelper::generateValidatorError($validator);
$valid = $validator->valid();
$asset = Asset::where('id', $valid['id'])->first();
$valid['type'] = strtolower($valid['type']);
if($asset->thumbnail2DHash && $valid['type'] == '2d')
return response(['status' => 'success', 'data' => route('content', $asset->thumbnail2DHash)]);
if($asset->thumbnail3DHash && $valid['type'] == '3d')
return response(['status' => 'success', 'data' => route('content', $asset->thumbnail3DHash)]);
$tracker = RenderTracker::where('type', sprintf('asset%s', $valid['type']))
->where('target', $valid['id']);
if(!$tracker->exists()) {
$tracker = new RenderTracker;
$tracker->type = sprintf('asset%s', $valid['type']);
$tracker->target = $valid['id'];
$tracker->save();
ArbiterRender::dispatch($tracker, $valid['type'] == '3d');
}
return response(['status' => 'loading']);
}
public function renderUser()
{
//
}
public function tryAsset()
{
//
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace App\Http\Controllers\Cdn;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Http\Request;
use App\Models\CdnHash;
use App\Helpers\CdnHelper;
use App\Helpers\ValidationHelper;
use App\Http\Controllers\Controller;
class CdnController extends Controller
{
public function getContent(Request $request, $hash)
{
$disk = CdnHelper::GetDisk();
if(preg_match('/^[a-f0-9]{64}$/i', $hash) && $disk->exists($hash)) {
$content = CdnHash::where('hash', $hash)->first();
if(!$content || $content->deleted)
return response('This item is currently unavailable.')
->header('content-type', 'text/plain');
return response($disk->get($hash))
->header('content-type', $content->mime_type);
} else {
return response('Invalid hash.')
->header('content-type', 'text/plain');
}
}
}

View File

@ -3,6 +3,7 @@
namespace App\Http\Controllers\Web;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use App\Http\Controllers\Controller;
use App\Grid\SoapService;

View File

@ -0,0 +1,149 @@
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Str;
use App\Grid\SoapService;
use App\Helpers\CdnHelper;
use App\Models\RenderTracker;
class ArbiterRender implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* The number of seconds the job can run before timing out.
*
* @var int
*/
public $timeout = 120;
/**
* The number of times the job may be attempted.
*
* @var int
*/
public $tries = 3;
/**
* The tracker instance.
*
* @var \App\Models\RenderTracker
*/
public $tracker;
/**
* Is the render 3d?
*
* @var bool
*/
public $is3D;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(RenderTracker $tracker, bool $is3D)
{
$this->tracker = $tracker;
$this->is3D = $is3D;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$testScript = <<<TestScript
settings()["Task Scheduler"].ThreadPoolConfig = Enum.ThreadPoolConfig.PerCore4;
game:GetService("ContentProvider"):SetThreadPool(16)
game:GetService("Stats"):SetReportUrl("http://api.gtoria.net/reportstat?cock=1")
local Lighting = game:GetService("Lighting")
Lighting.ClockTime = 13
Lighting.GeographicLatitude = -5
game:Load("http://gtoria.net/asset/?id=3529");
for _, Object in pairs(game:GetChildren())do
if Object:IsA("Tool") then
Object.Parent = workspace
end
end
-- format, width, height, sky, crop
return game:GetService("ThumbnailGenerator"):Click("OBJ", 840, 840, true, true)
TestScript;
$test = new SoapService('http://192.168.0.3:64989');
$result = $test->OpenJob(SoapService::MakeJobJSON(Str::uuid()->toString(), 120, 0, 0, sprintf('Render %s %d', $this->tracker->type, $this->tracker->target), $testScript));
if(is_soap_fault($result))
$this->fail(sprintf('SOAP Fault: (faultcode: %s, faultstring: %s)', $result->faultcode, $result->faultstring));
$result = $result->OpenJobExResult->LuaValue[0]->value;
if($this->is3D) {
$content = json_decode($result);
$result = [
'camera' => $content->camera,
'AABB' => $content->AABB,
'obj' => '',
'mtl' => '',
'textures' => []
];
$mtlTmp;
foreach($content->files as $file => $fileB64) {
$extension = strtolower(substr(strrchr($file, '.'), 1));
if($extension == 'mtl')
$mtlTmp = base64_decode($fileB64->content);
}
// RCC adds map_d for whatever reason. (alpha map)
$mtlTmp = preg_replace('/^map_d.+\n/im', '', $mtlTmp);
// Fix the shine
$mtlTmp = preg_replace('/^Ns \d+/im', 'Ns 0', $mtlTmp);
$mtlTmp = preg_replace('/^Ks.+/im', 'Ks 0.0627451 0.0627451 0.0627451', $mtlTmp);
foreach($content->files as $file => $fileB64) {
$extension = strtolower(substr(strrchr($file, '.'), 1));
if($extension != 'obj' && $extension != 'mtl')
$extension = 'textures';
if($extension == 'mtl')
continue;
$cdnHash = CdnHelper::SaveContentB64($fileB64->content, ($extension == 'png' ? 'image/png' : 'text/plain'));
$mtlTmp = str_replace($file, $cdnHash, $mtlTmp);
if(array_key_exists($extension, $result)) {
if(gettype($result[$extension]) == 'array')
array_push($result[$extension], $cdnHash);
else
$result[$extension] = $cdnHash;
} else {
$result[$extension] = $cdnHash;
}
}
$result['mtl'] = CdnHelper::SaveContent($mtlTmp, 'text/plain');
$this->tracker->targetObj->set3DHash(CdnHelper::SaveContent(json_encode($result), 'text/plain'));
} else {
$this->tracker->targetObj->set2DHash(CdnHelper::SaveContentB64($result, 'image/png'));
}
$this->tracker->delete();
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class CdnHash extends Model
{
use HasFactory;
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class RenderTracker extends Model
{
use HasFactory;
public function targetObj()
{
if($this->type == 'user2d' || $this->type == 'user3d')
return $this->belongsTo(User::class, 'target');
elseif($this->type == 'asset2d' || $this->type == 'asset3d')
return $this->belongsTo(Asset::class, 'target');
}
}

View File

@ -6,9 +6,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;
use App\Models\AssetVersion;
use App\Models\User;
class Asset extends Model
{
use HasFactory;
@ -153,6 +150,18 @@ class Asset extends Model
return 'https://gtoria.local/images/testing/hat.png';
}
public function set2DHash($hash)
{
$this->thumbnail2DHash = $hash;
$this->save();
}
public function set3DHash($hash)
{
$this->thumbnail3DHash = $hash;
$this->save();
}
public function getCreated()
{
$date = $this['created_at'];

View File

@ -3,6 +3,7 @@
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
@ -24,6 +25,8 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot()
{
URL::forceScheme('https');
Blade::directive('owner', function() {
return '<?php if(Auth::check() && Auth::user()->hasRoleset(\'Owner\')): ?>';
});

View File

@ -54,6 +54,14 @@ class RouteServiceProvider extends ServiceProvider
->middleware('api')
->namespace('App\Http\Controllers\Api')
->group(base_path('routes/api.php'));
//
// Domain: cdn.gtoria.net
//
Route::domain('cdn.' . DomainHelper::TopLevelDomain())
->middleware('api')
->namespace('App\Http\Controllers\Cdn')
->group(base_path('routes/cdn.php'));
});
}

View File

@ -13,7 +13,7 @@ return [
|
*/
'default' => env('QUEUE_CONNECTION', 'sync'),
'default' => env('QUEUE_CONNECTION', 'database'),
/*
|--------------------------------------------------------------------------

View File

@ -31,10 +31,8 @@ return new class extends Migration
$table->unsignedSmallInteger('assetAttributeId')->nullable();
$table->unsignedBigInteger('assetVersionId')->comment('The most recent version id for the asset. This is used internally as asset version 0 when using the /asset api.');
// Calculating the subdomain on runtime is too expensive.
// So full URLs are used instead of just the hashes.
$table->string('thumbnailURL')->nullable();
$table->string('3dThumbnailURL')->nullable();
$table->string('thumbnail2DHash')->nullable();
$table->string('thumbnail3DHash')->nullable();
$table->timestamps();
});

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('cdn_hashes', function (Blueprint $table) {
$table->id();
$table->string('hash');
$table->string('mime_type');
$table->unsignedBigInteger('user_id')->nullable();
$table->unsignedBigInteger('asset_version_id')->nullable();
$table->boolean('deleted')->default(false); // XlXi: in the case of copyright or whatever
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('cdn_hashes');
}
};

View File

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('render_trackers', function (Blueprint $table) {
$table->id();
$table->string('type');
$table->unsignedBigInteger('target');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('render_trackers');
}
};

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('jobs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('jobs');
}
};

180
web/package-lock.json generated
View File

@ -5,6 +5,7 @@
"packages": {
"": {
"dependencies": {
"@react-three/postprocessing": "^2.6.1",
"@restart/ui": "^1.3.1",
"classnames": "^2.3.1",
"install": "^0.13.0",
@ -1613,7 +1614,6 @@
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.1.2.tgz",
"integrity": "sha512-E/XrL0QlzExycPzwhOEZGVOheJ/Clr5uNv3oCds88MiNqEmg3UU1iauZk7DhjsUo3jgEW4lf0I5HRl7/HC5ZkQ==",
"dev": true,
"dependencies": {
"@chevrotain/gast": "^10.1.2",
"@chevrotain/types": "^10.1.2",
@ -1624,7 +1624,6 @@
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.1.2.tgz",
"integrity": "sha512-er+TcxUOMuGOPoiOq8CJsRm92zGE4YPIYtyxJfxoVwVgtj4AMrPNCmrHvYaK/bsbt2DaDuFdcbbAfM9bcBXW6Q==",
"dev": true,
"dependencies": {
"@chevrotain/types": "^10.1.2",
"lodash": "4.17.21"
@ -1633,14 +1632,12 @@
"node_modules/@chevrotain/types": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.1.2.tgz",
"integrity": "sha512-4qF9SmmWKv8AIG/3d+71VFuqLumNCQTP5GoL0CW6x7Ay2OdXm6FUgWFLTMneGUjYUk2C+MSCf7etQfdq3LEr1A==",
"dev": true
"integrity": "sha512-4qF9SmmWKv8AIG/3d+71VFuqLumNCQTP5GoL0CW6x7Ay2OdXm6FUgWFLTMneGUjYUk2C+MSCf7etQfdq3LEr1A=="
},
"node_modules/@chevrotain/utils": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.1.2.tgz",
"integrity": "sha512-bbZIpW6fdyf7FMaeDmw3cBbkTqsecxEkwlVKgVfqqXWBPLH6azxhPA2V9F7OhoZSVrsnMYw7QuyK6qutXPjEew==",
"dev": true
"integrity": "sha512-bbZIpW6fdyf7FMaeDmw3cBbkTqsecxEkwlVKgVfqqXWBPLH6azxhPA2V9F7OhoZSVrsnMYw7QuyK6qutXPjEew=="
},
"node_modules/@colors/colors": {
"version": "1.5.0",
@ -1874,7 +1871,6 @@
"version": "8.0.13",
"resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.0.13.tgz",
"integrity": "sha512-hJdVS2F8LJFenR07l80JnkLAMeJ7gf3wiB8OzQ49GjSwjPgPG8bjYumMmchY/eouU+myYtNej0tK6mV6wctmdQ==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.17.8",
"@types/react-reconciler": "^0.26.6",
@ -1912,6 +1908,22 @@
}
}
},
"node_modules/@react-three/postprocessing": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/@react-three/postprocessing/-/postprocessing-2.6.1.tgz",
"integrity": "sha512-IX6i8JI5iY9zYSjOh9ZKr3URQ+V8NSnFEOL0xBPDP7BdyLjwXKs5fPR/ClnW4bGt/4T1TMsGmLCXJ97gtZVRZA==",
"dependencies": {
"postprocessing": "^6.28.5",
"react-merge-refs": "^1.1.0",
"screen-space-reflections": "2.1.1",
"three-stdlib": "^2.8.11"
},
"peerDependencies": {
"@react-three/fiber": ">=7.0",
"react": ">=17.0",
"three": ">=0.136.0"
}
},
"node_modules/@restart/hooks": {
"version": "0.4.7",
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.7.tgz",
@ -2209,7 +2221,6 @@
"version": "0.26.6",
"resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.6.tgz",
"integrity": "sha512-N8MpyC6PJksD+CbMaZ1GW1t940+L4K+f7soiNKcKfgOgof3xWLvs5nPRQ/Q0P6QwDX0GH1PT1MsiQh2FtUY6aw==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
@ -2434,8 +2445,7 @@
"node_modules/@webgpu/glslang": {
"version": "0.0.15",
"resolved": "https://registry.npmjs.org/@webgpu/glslang/-/glslang-0.0.15.tgz",
"integrity": "sha512-niT+Prh3Aff8Uf1MVBVUsaNjFj9rJAKDXuoHIKiQbB+6IUP/3J3JIhBNyZ7lDhytvXxw6ppgnwKZdDJ08UMj4Q==",
"dev": true
"integrity": "sha512-niT+Prh3Aff8Uf1MVBVUsaNjFj9rJAKDXuoHIKiQbB+6IUP/3J3JIhBNyZ7lDhytvXxw6ppgnwKZdDJ08UMj4Q=="
},
"node_modules/@webpack-cli/configtest": {
"version": "1.1.1",
@ -3213,7 +3223,6 @@
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.1.2.tgz",
"integrity": "sha512-hvRiQuhhTZxkPMGD/dke+s1EGo8AkKDBU05CcufBO278qgAQSwIC4QyLdHz0CFHVtqVYWjlAS5D1KwvBbaHT+w==",
"dev": true,
"dependencies": {
"@chevrotain/cst-dts-gen": "^10.1.2",
"@chevrotain/gast": "^10.1.2",
@ -3853,8 +3862,7 @@
"node_modules/debounce": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==",
"dev": true
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
},
"node_modules/debug": {
"version": "4.3.4",
@ -4153,8 +4161,7 @@
"node_modules/draco3d": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.2.tgz",
"integrity": "sha512-AeRQ25Fb29c14vpjnh167UGW0nGY0ZpEM3ld+zEXoEySlmEXcXfsCHZeTgo5qXH925V1JsdjrzasdaQ22/vXog==",
"dev": true
"integrity": "sha512-AeRQ25Fb29c14vpjnh167UGW0nGY0ZpEM3ld+zEXoEySlmEXcXfsCHZeTgo5qXH925V1JsdjrzasdaQ22/vXog=="
},
"node_modules/ee-first": {
"version": "1.1.1",
@ -4500,8 +4507,7 @@
"node_modules/fflate": {
"version": "0.6.10",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz",
"integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==",
"dev": true
"integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg=="
},
"node_modules/file-loader": {
"version": "6.2.0",
@ -5604,8 +5610,7 @@
"node_modules/ktx-parse": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-0.2.2.tgz",
"integrity": "sha512-cFBc1jnGG2WlUf52NbDUXK2obJ+Mo9WUkBRvr6tP6CKxRMvZwDDFNV3JAS4cewETp5KyexByfWm9sm+O8AffiQ==",
"dev": true
"integrity": "sha512-cFBc1jnGG2WlUf52NbDUXK2obJ+Mo9WUkBRvr6tP6CKxRMvZwDDFNV3JAS4cewETp5KyexByfWm9sm+O8AffiQ=="
},
"node_modules/laravel-mix": {
"version": "6.0.43",
@ -6119,8 +6124,7 @@
"node_modules/mmd-parser": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mmd-parser/-/mmd-parser-1.0.4.tgz",
"integrity": "sha512-Qi0VCU46t2IwfGv5KF0+D/t9cizcDug7qnNoy9Ggk7aucp0tssV8IwTMkBlDbm+VqAf3cdQHTCARKSsuS2MYFg==",
"dev": true
"integrity": "sha512-Qi0VCU46t2IwfGv5KF0+D/t9cizcDug7qnNoy9Ggk7aucp0tssV8IwTMkBlDbm+VqAf3cdQHTCARKSsuS2MYFg=="
},
"node_modules/ms": {
"version": "2.1.2",
@ -8643,7 +8647,6 @@
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-1.3.4.tgz",
"integrity": "sha512-d2JE9RP/6uagpQAVtJoF0pJJA/fgai89Cc50Yp0EJHk+eLp6QQ7gBoblsnubRULNY132I0J1QKMJ+JTbMqz4sw==",
"dev": true,
"dependencies": {
"string.prototype.codepointat": "^0.2.1",
"tiny-inflate": "^1.0.3"
@ -9475,11 +9478,21 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"node_modules/postprocessing": {
"version": "6.28.5",
"resolved": "https://registry.npmjs.org/postprocessing/-/postprocessing-6.28.5.tgz",
"integrity": "sha512-e9aH6T720AiQpStdgqrQSsHzpddFQ7Q8v204sZx4iSTnUuYvkq7mFPOyzhVDlx5NrmXXbwz2PEK2XTq+qEYSMg==",
"engines": {
"node": ">= 0.13.2"
},
"peerDependencies": {
"three": ">= 0.107.0 < 0.144.0"
}
},
"node_modules/potpack": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
"integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==",
"dev": true
"integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ=="
},
"node_modules/pretty-time": {
"version": "1.1.0",
@ -9723,7 +9736,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-1.1.0.tgz",
"integrity": "sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==",
"dev": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
@ -9733,7 +9745,6 @@
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.27.0.tgz",
"integrity": "sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==",
"dev": true,
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.21.0"
@ -9789,7 +9800,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz",
"integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==",
"dev": true,
"dependencies": {
"debounce": "^1.2.1"
},
@ -9889,8 +9899,7 @@
"node_modules/regexp-to-ast": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz",
"integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==",
"dev": true
"integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw=="
},
"node_modules/regexpu-core": {
"version": "5.0.1",
@ -10159,7 +10168,6 @@
"version": "0.21.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz",
"integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==",
"dev": true,
"dependencies": {
"loose-envify": "^1.1.0"
}
@ -10181,6 +10189,15 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/screen-space-reflections": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/screen-space-reflections/-/screen-space-reflections-2.1.1.tgz",
"integrity": "sha512-UwBaTb6Ss/8EfhDHEdybPhMbHXmtchOcpHHTa5guFFzjj1f0998twZRr/svOJaA/Nyi44VL2Cqf3xGhdu3hC2w==",
"peerDependencies": {
"postprocessing": ">=6.28.0",
"three": ">=0.141.0"
}
},
"node_modules/select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@ -10590,8 +10607,7 @@
"node_modules/string.prototype.codepointat": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz",
"integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==",
"dev": true
"integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg=="
},
"node_modules/strip-ansi": {
"version": "6.0.1",
@ -10689,7 +10705,6 @@
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.0.8.tgz",
"integrity": "sha512-ZC3r8Hu1y0dIThzsGw0RLZplnX9yXwfItcvaIzJc2VQVi8TGyGDlu92syMB5ulybfvGLHAI5Ghzlk23UBPF8xg==",
"dev": true,
"peerDependencies": {
"react": ">=17.0"
}
@ -10874,10 +10889,9 @@
}
},
"node_modules/three": {
"version": "0.140.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.140.0.tgz",
"integrity": "sha512-jcHjbnYspPLDdsDQChmzyAoZ5KhJbgFk6pNGlAIc9fQMvsfPGjF5H9glrngqvb2CR/qXcClMyp5PYdF996lldA==",
"dev": true,
"version": "0.143.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.143.0.tgz",
"integrity": "sha512-oKcAGYHhJ46TGEuHjodo2n6TY2R6lbvrkp+feKZxqsUL/WkH7GKKaeu6RHeyb2Xjfk2dPLRKLsOP0KM2VgT8Zg==",
"peer": true
},
"node_modules/three-mesh-bvh": {
@ -10893,7 +10907,6 @@
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.10.1.tgz",
"integrity": "sha512-qKJLFB5KMUhPe4JgtJ6bnY7+nEuNPVbxfKyAeRIRbTTBF1oP/rKogZAio+NI5SXSo4iuIu59e02AzOzh8AR9cQ==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.16.7",
"@webgpu/glslang": "^0.0.15",
@ -10929,8 +10942,7 @@
"node_modules/tiny-inflate": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
"dev": true
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="
},
"node_modules/to-arraybuffer": {
"version": "1.0.1",
@ -11916,14 +11928,12 @@
"node_modules/zstddec": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.0.2.tgz",
"integrity": "sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA==",
"dev": true
"integrity": "sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA=="
},
"node_modules/zustand": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
"dev": true,
"engines": {
"node": ">=12.7.0"
},
@ -13000,7 +13010,6 @@
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.1.2.tgz",
"integrity": "sha512-E/XrL0QlzExycPzwhOEZGVOheJ/Clr5uNv3oCds88MiNqEmg3UU1iauZk7DhjsUo3jgEW4lf0I5HRl7/HC5ZkQ==",
"dev": true,
"requires": {
"@chevrotain/gast": "^10.1.2",
"@chevrotain/types": "^10.1.2",
@ -13011,7 +13020,6 @@
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.1.2.tgz",
"integrity": "sha512-er+TcxUOMuGOPoiOq8CJsRm92zGE4YPIYtyxJfxoVwVgtj4AMrPNCmrHvYaK/bsbt2DaDuFdcbbAfM9bcBXW6Q==",
"dev": true,
"requires": {
"@chevrotain/types": "^10.1.2",
"lodash": "4.17.21"
@ -13020,14 +13028,12 @@
"@chevrotain/types": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.1.2.tgz",
"integrity": "sha512-4qF9SmmWKv8AIG/3d+71VFuqLumNCQTP5GoL0CW6x7Ay2OdXm6FUgWFLTMneGUjYUk2C+MSCf7etQfdq3LEr1A==",
"dev": true
"integrity": "sha512-4qF9SmmWKv8AIG/3d+71VFuqLumNCQTP5GoL0CW6x7Ay2OdXm6FUgWFLTMneGUjYUk2C+MSCf7etQfdq3LEr1A=="
},
"@chevrotain/utils": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.1.2.tgz",
"integrity": "sha512-bbZIpW6fdyf7FMaeDmw3cBbkTqsecxEkwlVKgVfqqXWBPLH6azxhPA2V9F7OhoZSVrsnMYw7QuyK6qutXPjEew==",
"dev": true
"integrity": "sha512-bbZIpW6fdyf7FMaeDmw3cBbkTqsecxEkwlVKgVfqqXWBPLH6azxhPA2V9F7OhoZSVrsnMYw7QuyK6qutXPjEew=="
},
"@colors/colors": {
"version": "1.5.0",
@ -13201,7 +13207,6 @@
"version": "8.0.13",
"resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.0.13.tgz",
"integrity": "sha512-hJdVS2F8LJFenR07l80JnkLAMeJ7gf3wiB8OzQ49GjSwjPgPG8bjYumMmchY/eouU+myYtNej0tK6mV6wctmdQ==",
"dev": true,
"requires": {
"@babel/runtime": "^7.17.8",
"@types/react-reconciler": "^0.26.6",
@ -13213,6 +13218,17 @@
"zustand": "^3.7.1"
}
},
"@react-three/postprocessing": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/@react-three/postprocessing/-/postprocessing-2.6.1.tgz",
"integrity": "sha512-IX6i8JI5iY9zYSjOh9ZKr3URQ+V8NSnFEOL0xBPDP7BdyLjwXKs5fPR/ClnW4bGt/4T1TMsGmLCXJ97gtZVRZA==",
"requires": {
"postprocessing": "^6.28.5",
"react-merge-refs": "^1.1.0",
"screen-space-reflections": "2.1.1",
"three-stdlib": "^2.8.11"
}
},
"@restart/hooks": {
"version": "0.4.7",
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.7.tgz",
@ -13496,7 +13512,6 @@
"version": "0.26.6",
"resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.6.tgz",
"integrity": "sha512-N8MpyC6PJksD+CbMaZ1GW1t940+L4K+f7soiNKcKfgOgof3xWLvs5nPRQ/Q0P6QwDX0GH1PT1MsiQh2FtUY6aw==",
"dev": true,
"requires": {
"@types/react": "*"
}
@ -13718,8 +13733,7 @@
"@webgpu/glslang": {
"version": "0.0.15",
"resolved": "https://registry.npmjs.org/@webgpu/glslang/-/glslang-0.0.15.tgz",
"integrity": "sha512-niT+Prh3Aff8Uf1MVBVUsaNjFj9rJAKDXuoHIKiQbB+6IUP/3J3JIhBNyZ7lDhytvXxw6ppgnwKZdDJ08UMj4Q==",
"dev": true
"integrity": "sha512-niT+Prh3Aff8Uf1MVBVUsaNjFj9rJAKDXuoHIKiQbB+6IUP/3J3JIhBNyZ7lDhytvXxw6ppgnwKZdDJ08UMj4Q=="
},
"@webpack-cli/configtest": {
"version": "1.1.1",
@ -14311,7 +14325,6 @@
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.1.2.tgz",
"integrity": "sha512-hvRiQuhhTZxkPMGD/dke+s1EGo8AkKDBU05CcufBO278qgAQSwIC4QyLdHz0CFHVtqVYWjlAS5D1KwvBbaHT+w==",
"dev": true,
"requires": {
"@chevrotain/cst-dts-gen": "^10.1.2",
"@chevrotain/gast": "^10.1.2",
@ -14804,8 +14817,7 @@
"debounce": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==",
"dev": true
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
},
"debug": {
"version": "4.3.4",
@ -15031,8 +15043,7 @@
"draco3d": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.2.tgz",
"integrity": "sha512-AeRQ25Fb29c14vpjnh167UGW0nGY0ZpEM3ld+zEXoEySlmEXcXfsCHZeTgo5qXH925V1JsdjrzasdaQ22/vXog==",
"dev": true
"integrity": "sha512-AeRQ25Fb29c14vpjnh167UGW0nGY0ZpEM3ld+zEXoEySlmEXcXfsCHZeTgo5qXH925V1JsdjrzasdaQ22/vXog=="
},
"ee-first": {
"version": "1.1.1",
@ -15310,8 +15321,7 @@
"fflate": {
"version": "0.6.10",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz",
"integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==",
"dev": true
"integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg=="
},
"file-loader": {
"version": "6.2.0",
@ -16088,8 +16098,7 @@
"ktx-parse": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-0.2.2.tgz",
"integrity": "sha512-cFBc1jnGG2WlUf52NbDUXK2obJ+Mo9WUkBRvr6tP6CKxRMvZwDDFNV3JAS4cewETp5KyexByfWm9sm+O8AffiQ==",
"dev": true
"integrity": "sha512-cFBc1jnGG2WlUf52NbDUXK2obJ+Mo9WUkBRvr6tP6CKxRMvZwDDFNV3JAS4cewETp5KyexByfWm9sm+O8AffiQ=="
},
"laravel-mix": {
"version": "6.0.43",
@ -16481,8 +16490,7 @@
"mmd-parser": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mmd-parser/-/mmd-parser-1.0.4.tgz",
"integrity": "sha512-Qi0VCU46t2IwfGv5KF0+D/t9cizcDug7qnNoy9Ggk7aucp0tssV8IwTMkBlDbm+VqAf3cdQHTCARKSsuS2MYFg==",
"dev": true
"integrity": "sha512-Qi0VCU46t2IwfGv5KF0+D/t9cizcDug7qnNoy9Ggk7aucp0tssV8IwTMkBlDbm+VqAf3cdQHTCARKSsuS2MYFg=="
},
"ms": {
"version": "2.1.2",
@ -18198,7 +18206,6 @@
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-1.3.4.tgz",
"integrity": "sha512-d2JE9RP/6uagpQAVtJoF0pJJA/fgai89Cc50Yp0EJHk+eLp6QQ7gBoblsnubRULNY132I0J1QKMJ+JTbMqz4sw==",
"dev": true,
"requires": {
"string.prototype.codepointat": "^0.2.1",
"tiny-inflate": "^1.0.3"
@ -18711,11 +18718,16 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"postprocessing": {
"version": "6.28.5",
"resolved": "https://registry.npmjs.org/postprocessing/-/postprocessing-6.28.5.tgz",
"integrity": "sha512-e9aH6T720AiQpStdgqrQSsHzpddFQ7Q8v204sZx4iSTnUuYvkq7mFPOyzhVDlx5NrmXXbwz2PEK2XTq+qEYSMg==",
"requires": {}
},
"potpack": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
"integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==",
"dev": true
"integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ=="
},
"pretty-time": {
"version": "1.1.0",
@ -18903,14 +18915,12 @@
"react-merge-refs": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-1.1.0.tgz",
"integrity": "sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==",
"dev": true
"integrity": "sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ=="
},
"react-reconciler": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.27.0.tgz",
"integrity": "sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"scheduler": "^0.21.0"
@ -18947,7 +18957,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz",
"integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==",
"dev": true,
"requires": {
"debounce": "^1.2.1"
}
@ -19036,8 +19045,7 @@
"regexp-to-ast": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz",
"integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==",
"dev": true
"integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw=="
},
"regexpu-core": {
"version": "5.0.1",
@ -19210,7 +19218,6 @@
"version": "0.21.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz",
"integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0"
}
@ -19225,6 +19232,12 @@
"ajv-keywords": "^3.5.2"
}
},
"screen-space-reflections": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/screen-space-reflections/-/screen-space-reflections-2.1.1.tgz",
"integrity": "sha512-UwBaTb6Ss/8EfhDHEdybPhMbHXmtchOcpHHTa5guFFzjj1f0998twZRr/svOJaA/Nyi44VL2Cqf3xGhdu3hC2w==",
"requires": {}
},
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@ -19570,8 +19583,7 @@
"string.prototype.codepointat": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz",
"integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==",
"dev": true
"integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg=="
},
"strip-ansi": {
"version": "6.0.1",
@ -19633,7 +19645,6 @@
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.0.8.tgz",
"integrity": "sha512-ZC3r8Hu1y0dIThzsGw0RLZplnX9yXwfItcvaIzJc2VQVi8TGyGDlu92syMB5ulybfvGLHAI5Ghzlk23UBPF8xg==",
"dev": true,
"requires": {}
},
"svgo": {
@ -19757,10 +19768,9 @@
}
},
"three": {
"version": "0.140.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.140.0.tgz",
"integrity": "sha512-jcHjbnYspPLDdsDQChmzyAoZ5KhJbgFk6pNGlAIc9fQMvsfPGjF5H9glrngqvb2CR/qXcClMyp5PYdF996lldA==",
"dev": true,
"version": "0.143.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.143.0.tgz",
"integrity": "sha512-oKcAGYHhJ46TGEuHjodo2n6TY2R6lbvrkp+feKZxqsUL/WkH7GKKaeu6RHeyb2Xjfk2dPLRKLsOP0KM2VgT8Zg==",
"peer": true
},
"three-mesh-bvh": {
@ -19774,7 +19784,6 @@
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.10.1.tgz",
"integrity": "sha512-qKJLFB5KMUhPe4JgtJ6bnY7+nEuNPVbxfKyAeRIRbTTBF1oP/rKogZAio+NI5SXSo4iuIu59e02AzOzh8AR9cQ==",
"dev": true,
"requires": {
"@babel/runtime": "^7.16.7",
"@webgpu/glslang": "^0.0.15",
@ -19804,8 +19813,7 @@
"tiny-inflate": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
"dev": true
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="
},
"to-arraybuffer": {
"version": "1.0.1",
@ -20540,14 +20548,12 @@
"zstddec": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.0.2.tgz",
"integrity": "sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA==",
"dev": true
"integrity": "sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA=="
},
"zustand": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
"dev": true,
"requires": {}
}
}

View File

@ -30,6 +30,7 @@
"tailwindcss": "^3.0.24"
},
"dependencies": {
"@react-three/postprocessing": "^2.6.1",
"@restart/ui": "^1.3.1",
"classnames": "^2.3.1",
"install": "^0.13.0",

Binary file not shown.

After

Width:  |  Height:  |  Size: 891 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -42,7 +42,7 @@ class Comments extends Component {
}
componentDidMount() {
let commentsElement = document.getElementById(commentsId);
let commentsElement = this.props.element;
if (commentsElement) {
this.assetId = commentsElement.getAttribute('data-asset-id')
this.setState({

View File

@ -0,0 +1,22 @@
// https://levelup.gitconnected.com/react-lazy-load-image-e6a5ca944f32
// XlXi: i was too lazy to write this myself
import React, { useCallback, useEffect, useState } from "react";
export default ({ src, placeholderImg, ...props }) => {
const [imgSrc, setSrc] = useState(placeholderImg || src);
const onLoad = useCallback(() => {
setSrc(src);
}, [src]);
useEffect(() => {
const img = new Image();
img.src = src;
img.addEventListener("load", onLoad);
return () => {
img.removeEventListener("load", onLoad);
};
}, [src, onLoad]);
return <img {...props} src={imgSrc} />;
};

View File

@ -4,7 +4,6 @@
*/
import { createRef, Component } from 'react';
import ReactDOM from 'react-dom';
import classNames from 'classnames/bind';

View File

@ -0,0 +1,221 @@
/*
Graphictoria 5 (https://gtoria.net)
Copyright © XlXi 2022
*/
import { Component, Suspense, useEffect, useRef } from 'react';
import * as THREE from 'three';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader';
import { OrbitControls, PerspectiveCamera } from "@react-three/drei";
import { Canvas, useLoader, useThree } from '@react-three/fiber';
import { EffectComposer, SSAO } from '@react-three/postprocessing';
import { BlendFunction } from 'postprocessing';
import axios from 'axios';
import { buildGenericApiUrl } from '../util/HTTP.js';
import ProgressiveImage from './ProgressiveImage';
import Loader from './Loader';
axios.defaults.withCredentials = true;
const Scene = ({json}) => {
const mtl = useLoader(MTLLoader, json.mtl);
const obj = useLoader(OBJLoader, json.obj, (loader) => {
mtl.preload();
loader.setMaterials(mtl);
});
let controls = useRef();
let midPoint;
useThree(({camera, scene}) => {
let aabbMax = json.AABB.max;
let aabbMin = json.AABB.min;
aabbMax = new THREE.Vector3(aabbMax.x, aabbMax.y, aabbMax.z);
aabbMin = new THREE.Vector3(aabbMin.x, aabbMin.y, aabbMin.z);
midPoint = new THREE.Vector3();
midPoint.copy(aabbMax).add(aabbMin).multiplyScalar(0.5);
let initialPosition = json.camera.position;
let initialDirection = json.camera.direction;
let thumbnailCameraPosition = new THREE.Vector3(initialPosition.x, initialPosition.y, initialPosition.z);
let thumbnailCameraDirection = new THREE.Vector3(initialDirection.x, initialDirection.y, initialDirection.z);
let pointToLookat = new THREE.Vector3();
pointToLookat.copy(thumbnailCameraPosition);
pointToLookat.sub(thumbnailCameraDirection);
camera.position.set(thumbnailCameraPosition.x, thumbnailCameraPosition.y, thumbnailCameraPosition.z);
camera.lookAt(pointToLookat);
// lighting
// FIXME: XlXi: if you toggle 3d on and off it'll create these twice
let ambient = new THREE.AmbientLight(0x878780);
scene.add(ambient);
let sunLight = new THREE.DirectionalLight(0xacacac);
sunLight.position.set(0.671597898, 0.671597898, -0.312909544).normalize();
scene.add(sunLight);
let backLight = new THREE.DirectionalLight(0x444444);
let backLightPos = new THREE.Vector3()
.copy(sunLight.position)
.negate()
.normalize(); // inverse of sun direction
backLight.position.set(backLightPos);
scene.add(backLight);
});
useEffect(() => {
controls.current.target = midPoint;
controls.current.update();
});
return (
<>
<primitive object={obj} />
{/*
<EffectComposer>
<SSAO
blendFunction={BlendFunction.MULTIPLY}
samples={128}
radius={2}
intensity={60}
/>
</EffectComposer>
*/}
<OrbitControls makeDefault ref={controls} enableDamping={false} enablePan={false} />
</>
)
}
class ThumbnailTool extends Component {
constructor(props) {
super(props);
this.state = {
initialLoading: true,
loading: false,
is3d: false,
seed3d: 0
};
this.tryAsset = this.tryAsset.bind(this);
this.loadThumbnail = this.loadThumbnail.bind(this);
this.toggle3D = this.toggle3D.bind(this);
}
componentDidMount() {
let thumbnailElement = this.props.element;
if (thumbnailElement) {
this.thumbnail2d = thumbnailElement.getAttribute('data-asset-thumbnail-2d');
this.assetId = thumbnailElement.getAttribute('data-asset-id');
this.assetName = thumbnailElement.getAttribute('data-asset-name');
this.setState({ initialLoading: false });
}
}
loadThumbnail(url, is3d) {
axios.get(buildGenericApiUrl('api', url))
.then(res => {
let data = res.data;
if(data.status === 'success') {
if(is3d) {
axios.get(data.data)
.then(res => {
let newJson = res.data;
newJson.mtl = buildGenericApiUrl('cdn', newJson.mtl);
newJson.obj = buildGenericApiUrl('cdn', newJson.obj);
if(Array.isArray(newJson.textures)) {
let newTextures = [];
newJson.textures.map((hash) => {
newTextures.push(buildGenericApiUrl('cdn', hash));
});
newJson.textures = newTextures;
} else {
newJson.textures = buildGenericApiUrl('cdn', newJson.textures);
}
this.setState({ loading: false, json3d: res.data });
});
} else {
this.setState({ loading: false });
}
} else {
let lt = this.loadThumbnail;
setTimeout(function(){lt(url,is3d)}, 1000);
}
});
}
tryAsset() {
let is3d = !this.state.is3d;
this.setState({ loading: true, is3d: is3d });
this.loadThumbnail(`thumbnails/v1/try-asset?id=${this.assetId}&type=${this.state.is3d ? '3D' : '2D'}`, is3d);
}
toggle3D() {
let is3d = !this.state.is3d;
this.setState({ loading: true, is3d: is3d, seed3d: Math.random() });
if(is3d) {
this.loadThumbnail(`thumbnails/v1/asset?id=${this.assetId}&type=3D`, true);
} else {
this.setState({ loading: false });
}
}
render() {
return (
<>
{
this.state.initialLoading
?
<div className='position-absolute top-50 start-50 translate-middle'>
<Loader />
</div>
:
<>
{
this.state.loading
?
<div className='position-absolute top-50 start-50 translate-middle'>
<Loader />
</div>
:
(
this.state.is3d
?
<Canvas key={ this.state.seed3d }>
<Suspense fallback={null}>
<Scene json={ this.state.json3d } />
</Suspense>
</Canvas>
:
<ProgressiveImage
src={ this.thumbnail2d }
placeholderImg={ buildGenericApiUrl('www', 'images/busy/asset.png') }
alt={ this.assetName }
className='img-fluid'
/>
)
}
<div className='d-flex position-absolute bottom-0 end-0 pb-2 pe-2'>
<button className='btn btn-secondary me-2' onClick={ this.tryAsset } disabled={ this.state.loading }>Try On</button>
<button className='btn btn-secondary' onClick={ this.toggle3D } disabled={ this.state.loading }>{ this.state.is3d ? '2D' : '3D' }</button>
</div>
</>
}
</>
);
}
}
export default ThumbnailTool;

View File

@ -10,15 +10,22 @@ import { render } from 'react-dom';
import Comments from '../components/Comments';
import PurchaseButton from '../components/PurchaseButton';
import ThumbnailTool from '../components/ThumbnailTool';
const purchaseId = 'gt-purchase-button';
const commentsId = 'gt-comments'; // XlXi: Keep this in sync with the Comments component.
const commentsId = 'gt-comments';
const thumbnailId = 'gt-thumbnail';
$(document).ready(function() {
if (document.getElementById(commentsId)) {
render(<Comments />, document.getElementById(commentsId));
let cElem = document.getElementById(commentsId);
render(<Comments element={ cElem } />, cElem);
}
if (document.getElementById(purchaseId)) {
render(<PurchaseButton />, document.getElementById(purchaseId));
}
if (document.getElementById(thumbnailId)) {
let tElem = document.getElementById(thumbnailId);
render(<ThumbnailTool element={ tElem } />, tElem);
}
});

View File

@ -36,6 +36,11 @@ img.twemoji {
// Shop
.graphictoria-asset-thumbnail {
width: 420px;
height: 420px;
}
.graphictoria-item-page {
max-width: 1096px;
margin: 0 auto 0 auto;

View File

@ -62,6 +62,12 @@
<div class="collapse navbar-collapse" id="graphictoria-admin-nav">
<ul class="navbar-nav graphictoria-admin-nav ms-auto">
@yield('quick-admin')
@admin
<li class="nav-item">
{{-- TODO: XlXi: Make this use route() --}}
<a href="{{ url('/admin/arbiter-diag') }}" class="nav-link py-0">Arbiter Diag</a>
</li>
@endadmin
<li class="nav-item">
{{-- TODO: XlXi: Make this use route() --}}
<a href="{{ url('/admin') }}" class="nav-link py-0"><i class="fa-solid fa-gavel"></i></a>
@ -83,7 +89,7 @@
@endphp
<div
class="{{ $admin_memorybar_color }} rounded-1 position-absolute graphictoria-admin-memorybar"
style="width:{{ $admin_memorybar_usage }}px!important;height:8px!important;"
style="width:{{ $admin_memorybar_usage }}%!important;height:8px!important;"
></div>
</div>
<i class="my-auto fa-solid fa-gear"></i>
@ -105,7 +111,7 @@
@endphp
<div
class="{{ $admin_cpubar_color }} rounded-1 position-absolute graphictoria-admin-memorybar"
style="width:{{ $admin_cpubar_usage }}px!important;height:8px!important;"
style="width:{{ $admin_cpubar_usage }}%!important;height:8px!important;"
></div>
</div>
<i class="my-auto fa-solid fa-microchip"></i>
@ -173,7 +179,6 @@
<div id="graphictoria-nav-searchbar" class="graphictoria-search"></div>
<ul class="navbar-nav ms-auto me-2">
<li class="nav-item">
{{-- TODO: XlXi: messages and notifications --}}
<a @class(['nav-link', 'active'=>str_starts_with(Request::path(), 'my/friends')]) href="{{ url('/my/friends') }}" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Friends">
@php
$friendRequestCount = Auth::user()->getFriendRequests()->count();
@ -188,6 +193,16 @@
<i class="fa-solid fa-user-group"></i>
</a>
</li>
<li class="nav-item">
<a @class(['nav-link', 'active'=>str_starts_with(Request::path(), 'my/messages')]) href="{{ url('/my/messages') }}" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Messages">
<i class="fa-solid fa-inbox"></i>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Notifications">
<i class="fa-solid fa-bell"></i>
</a>
</li>
</ul>
<div class="d-md-flex">
<p class="my-auto me-2 text-muted" style="color:#e59800!important;font-weight:bold">

View File

@ -7,6 +7,26 @@
@endsection
@section('quick-admin')
@owner
<li class="nav-item">
{{-- TODO: XlXi: Make this use route() --}}
<a href="{{ url('/admin/grant-asset?id=' . $asset->id) }}" class="nav-link py-0">Grant Asset</a>
</li>
@endowner
@admin
<li class="nav-item">
{{-- TODO: XlXi: Make this use route() --}}
<a href="{{ url('/admin/rerender-asset?id=' . $asset->id) }}" class="nav-link py-0">Rerender Asset</a>
</li>
<li class="nav-item">
{{-- TODO: XlXi: Make this use route() --}}
<a href="{{ url('/admin/asset-dependencies?id=' . $asset->id) }}" class="nav-link py-0">Asset Dependencies</a>
</li>
<li class="nav-item">
{{-- TODO: XlXi: Make this use route() --}}
<a href="{{ url('/admin/endorse-asset?id=' . $asset->id) }}" class="nav-link py-0">Endorse Asset</a>
</li>
@endadmin
<li class="nav-item">
{{-- TODO: XlXi: Make this use route() --}}
<a href="{{ url('/admin/moderate?id=' . $asset->id . '&type=asset') }}" class="nav-link py-0">Moderate Asset</a>
@ -36,13 +56,14 @@
<div class="card-body">
<div class="d-flex">
<div class="pe-4">
<div class="border-1 position-relative">
<img src={{ asset('images/testing/hat.png') }} alt="{{ $asset->name }}" class="border img-fluid" />
<div id="gt-thumbnail-toolbar"></div>
<div class="d-flex position-absolute bottom-0 end-0 pb-2 pe-2">
<button class="btn btn-secondary me-2">Try On</button>
<button class="btn btn-secondary">3D</button>
</div>
<div id="gt-thumbnail"
class="border position-relative graphictoria-asset-thumbnail"
data-asset-thumbnail-2d="{{ asset('images/testing/hat.png') }}"
data-asset-thumbnail-3d="{{ route('thumbnails.v1.asset', ['id' => $asset->id]) }}"
data-asset-name="{{ $asset->name }}"
data-asset-id="{{ $asset->id }}"
>
<img src="{{ asset('images/testing/hat.png') }}" alt="{{ $asset->name }}" class="img-fluid" />
</div>
</div>
<div class="flex-fill">

View File

@ -12,17 +12,25 @@ Route::middleware('auth')->group(function () {
});
Route::group(['as' => 'comments.', 'prefix' => 'comments'], function() {
Route::group(['as' => 'v1.', 'prefix' => 'v1'], function() {
Route::get('/list-json', 'CommentsController@listJson')->name('list');
Route::post('/share', 'CommentsController@share')->name('share')->middleware(['auth', 'throttle:3,2']);
});
Route::group(['as' => 'v1.', 'prefix' => 'v1'], function() {
Route::get('/list-json', 'CommentsController@listJson')->name('list');
Route::post('/share', 'CommentsController@share')->name('share')->middleware(['auth', 'throttle:3,2']);
});
});
Route::group(['as' => 'shop.', 'prefix' => 'shop'], function() {
Route::group(['as' => 'v1.', 'prefix' => 'v1'], function() {
Route::get('/list-json', 'ShopController@listJson')->name('list');
});
Route::group(['as' => 'v1.', 'prefix' => 'v1'], function() {
Route::get('/list-json', 'ShopController@listJson')->name('list');
});
});
Route::group(['as' => 'thumbnails.', 'prefix' => 'thumbnails'], function() {
Route::group(['as' => 'v1.', 'prefix' => 'v1'], function() {
Route::get('/try-asset', 'ThumbnailController@tryAsset')->name('try')->middleware('auth');
Route::get('/asset', 'ThumbnailController@renderAsset')->name('asset');
Route::get('/user', 'ThumbnailController@renderUser')->name('user');
});
});
Route::fallback(function () {
return response('404 not found.', 404)

8
web/routes/cdn.php Normal file
View File

@ -0,0 +1,8 @@
<?php
Route::get('/{hash}', 'CdnController@getContent')->name('content');
Route::fallback(function () {
return response('404 not found.', 404)
->header('Content-Type', 'text/plain');
});