diff --git a/package.json b/package.json index b358b64..805bfa7 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,12 @@ { "name": "toolbox", - "version": "1.1.0", + "version": "1.1.1", "type": "commonjs", "scripts": { "ng": "ng", "start": "ng serve toolbox-main", "start-utilities": "ng serve toolbox-utilities --port 4201", + "prebuild": "node --inspect -r ts-node/register projects/toolbox-main/prebuild.ts", "build": "ng build toolbox-main", "postbuild": "node --inspect -r ts-node/register projects/toolbox-main/postbuild.ts", "watch": "ng build toolbox-main --watch --configuration development", diff --git a/projects/toolbox-main/prebuild.ts b/projects/toolbox-main/prebuild.ts new file mode 100644 index 0000000..7aeb461 --- /dev/null +++ b/projects/toolbox-main/prebuild.ts @@ -0,0 +1,28 @@ +import { readFileSync, writeFileSync } from 'fs'; + +(() => { + const packageJson = require('../../package.json'); + + const changelogFile = "./projects/toolbox-main/src/resources/data/changelog.json"; + const changelog = JSON.parse(readFileSync(changelogFile, { encoding: "utf-8" })); + + const previousEntry = changelog[changelog.length - 2]; + const currentEntry = changelog[changelog.length - 1]; + + if (currentEntry.version || currentEntry.build_date) { + console.error("A unset entry must be present in the changelog."); + process.exit(-1); + } + + if (packageJson.version === previousEntry.version) { + console.error("The version in package.json must be updated before building."); + process.exit(-1); + } + + currentEntry.version = packageJson.version; + currentEntry.build_date = new Date().toISOString().split('T')[0]; + + writeFileSync(changelogFile, JSON.stringify(changelog, undefined, "\t")); + + console.log(`Building Minecraft Toolbox v${packageJson.version}...`); +})(); \ No newline at end of file diff --git a/projects/toolbox-main/src/app/app.component.ts b/projects/toolbox-main/src/app/app.component.ts index 5e81072..bff1a2c 100644 --- a/projects/toolbox-main/src/app/app.component.ts +++ b/projects/toolbox-main/src/app/app.component.ts @@ -1,4 +1,4 @@ -import { Component, ViewChild, ViewContainerRef } from '@angular/core'; +import { AfterViewInit, Component, ViewChild, ViewContainerRef } from '@angular/core'; import packageJson from '../../../../package.json'; import { PanoramaService } from './common/services/panorama-service/panorama.service'; import { TitleService } from './common/services/title-service/title.service'; @@ -10,21 +10,23 @@ import { WindowService } from './common/services/window-service/window.service'; templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], }) -export class AppComponent { - public version = packageJson.version; +export class AppComponent implements AfterViewInit { + protected version = packageJson.version; @ViewChild('windowContainer', { read: ViewContainerRef }) - public windowContainerTarget!: ViewContainerRef; + protected windowContainerTarget!: ViewContainerRef; - constructor( - private _toolboxSettings: ToolboxSettingsService, - private _panorama: PanoramaService, + public constructor( private _window: WindowService, - private _titleService: TitleService + + //This essentially bootstraps the services to the start of the application. + toolboxSettings: ToolboxSettingsService, + panorama: PanoramaService, + titleService: TitleService ) { } - ngAfterViewInit() { + public ngAfterViewInit() { this._window.windowContainer = this.windowContainerTarget; } } \ No newline at end of file diff --git a/projects/toolbox-main/src/app/app.module.ts b/projects/toolbox-main/src/app/app.module.ts index abf9013..0183d87 100644 --- a/projects/toolbox-main/src/app/app.module.ts +++ b/projects/toolbox-main/src/app/app.module.ts @@ -35,11 +35,6 @@ import { LootTableRandomizerFAQComponent } from './loot-table-randomizer/views/f import { LootTableRandomizerInstructionsComponent } from './loot-table-randomizer/views/instructions/instructions.component'; import { LootTableRandomizerViewComponent } from './loot-table-randomizer/views/loot-table-randomizer-view/loot-table-randomizer-view.component'; -//Services -import { PanoramaService } from './common/services/panorama-service/panorama.service'; -import { TitleService } from './common/services/title-service/title.service'; -import { ToolboxSettingsService } from './common/services/toolbox-settings/toolbox-settings.service'; - //Interceptors import { CacheInterceptor } from './common/interceptors/cache/cache.interceptor'; import { LocalInterceptor } from './common/interceptors/local/local.interceptor'; @@ -85,9 +80,6 @@ import { ReversePipe } from './common/pipes/reverse/reverse.pipe'; HttpClientModule ], providers: [ - TitleService, - PanoramaService, - ToolboxSettingsService, { provide: APP_BASE_HREF, useValue: angularJson.projects['toolbox-main'].architect.build.options.baseHref diff --git a/projects/toolbox-main/src/app/common/elements/activity-monitor/activity-monitor.component.ts b/projects/toolbox-main/src/app/common/elements/activity-monitor/activity-monitor.component.ts index 4317391..9c89108 100644 --- a/projects/toolbox-main/src/app/common/elements/activity-monitor/activity-monitor.component.ts +++ b/projects/toolbox-main/src/app/common/elements/activity-monitor/activity-monitor.component.ts @@ -7,12 +7,12 @@ import { ActivityMonitorService } from '../../services/activity-monitor/activity styleUrls: ['./activity-monitor.component.scss'] }) export class ActivityMonitorComponent implements OnInit { - public get activities() { + protected get activities() { return this._activityMonitor.activities; } - constructor( - private _ref: ElementRef, + public constructor( + private _ref: ElementRef, private _activityMonitor: ActivityMonitorService ) { } @@ -21,7 +21,7 @@ export class ActivityMonitorComponent implements OnInit { this._ref.nativeElement.style.display = enabled ? "block" : "none"; } - ngOnInit(): void { + public ngOnInit(): void { this._activityMonitor.setActivityMonitor(this); } -} +} \ No newline at end of file diff --git a/projects/toolbox-main/src/app/common/elements/button/button.component.html b/projects/toolbox-main/src/app/common/elements/button/button.component.html deleted file mode 100644 index 95a0b70..0000000 --- a/projects/toolbox-main/src/app/common/elements/button/button.component.html +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/projects/toolbox-main/src/app/common/elements/button/button.component.ts b/projects/toolbox-main/src/app/common/elements/button/button.component.ts index b8d32fa..66f193b 100644 --- a/projects/toolbox-main/src/app/common/elements/button/button.component.ts +++ b/projects/toolbox-main/src/app/common/elements/button/button.component.ts @@ -1,9 +1,10 @@ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ selector: 'button[standard]', - templateUrl: './button.component.html', - styleUrls: ['./button.component.scss'] + template: '', + styleUrls: ['./button.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush }) export class ButtonComponent { } \ No newline at end of file diff --git a/projects/toolbox-main/src/app/common/elements/error-handler/error-handler.component.ts b/projects/toolbox-main/src/app/common/elements/error-handler/error-handler.component.ts index 198d9fe..fe396c3 100644 --- a/projects/toolbox-main/src/app/common/elements/error-handler/error-handler.component.ts +++ b/projects/toolbox-main/src/app/common/elements/error-handler/error-handler.component.ts @@ -17,15 +17,15 @@ export class ErrorHandlerComponent implements OnInit { ) { } - ngOnInit(): void { + public ngOnInit(): void { this.errorHandler.setErrorHandler(this); } - public reportIssue() { + protected reportIssue() { window.open("https://github.com/Fasguy/MinecraftToolbox/issues", "_blank", "noopener,noreferrer") } - public close() { + protected close() { this.errorHandler.error = null; } } \ No newline at end of file diff --git a/projects/toolbox-main/src/app/common/elements/error-indicator/error-indicator.component.ts b/projects/toolbox-main/src/app/common/elements/error-indicator/error-indicator.component.ts index 3174e05..fcd3500 100644 --- a/projects/toolbox-main/src/app/common/elements/error-indicator/error-indicator.component.ts +++ b/projects/toolbox-main/src/app/common/elements/error-indicator/error-indicator.component.ts @@ -1,9 +1,10 @@ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ selector: 'tbx-error-indicator', templateUrl: './error-indicator.component.html', - styleUrls: ['./error-indicator.component.scss'] + styleUrls: ['./error-indicator.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush }) export class ErrorIndicatorComponent { } \ No newline at end of file diff --git a/projects/toolbox-main/src/app/common/elements/footer/footer.component.ts b/projects/toolbox-main/src/app/common/elements/footer/footer.component.ts index 23811e2..8922baa 100644 --- a/projects/toolbox-main/src/app/common/elements/footer/footer.component.ts +++ b/projects/toolbox-main/src/app/common/elements/footer/footer.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { WindowService } from '../../services/window-service/window.service'; import { ChangelogComponent } from '../../views/windows/changelog/changelog.component'; import { CreditsComponent } from '../../views/windows/credits/credits.component'; @@ -7,10 +7,12 @@ import { SettingsComponent } from '../../views/windows/settings/settings.compone @Component({ selector: 'tbx-footer', templateUrl: './footer.component.html', - styleUrls: ['./footer.component.scss'] + styleUrls: ['./footer.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush }) export class FooterComponent { - public items = [ + protected currentYear = new Date().getFullYear(); + protected items = [ { text: "Credits", click: () => this._window.createWindow(CreditsComponent) @@ -33,11 +35,7 @@ export class FooterComponent { } ]; - public get currentYear(): number { - return new Date().getFullYear(); - } - - constructor( + public constructor( private _window: WindowService ) { } diff --git a/projects/toolbox-main/src/app/common/elements/header/header.component.ts b/projects/toolbox-main/src/app/common/elements/header/header.component.ts index f5590a0..e1273b6 100644 --- a/projects/toolbox-main/src/app/common/elements/header/header.component.ts +++ b/projects/toolbox-main/src/app/common/elements/header/header.component.ts @@ -8,7 +8,7 @@ import { Route, Router, Routes } from '@angular/router'; changeDetection: ChangeDetectionStrategy.OnPush }) export class HeaderComponent implements OnInit { - public routes: Routes = []; + protected routes: Routes = []; @Input() public version!: string; @@ -21,11 +21,11 @@ export class HeaderComponent implements OnInit { this.routes.push(...router.config); } - ngOnInit(): void { + public ngOnInit(): void { this._changeDetector.detectChanges(); } - filterTitledRoutes(t: Route): boolean { + protected filterTitledRoutes(t: Route): boolean { return t.data?.["title"] != null; } } \ No newline at end of file diff --git a/projects/toolbox-main/src/app/common/elements/input/input.component.ts b/projects/toolbox-main/src/app/common/elements/input/input.component.ts index 3f73ae2..0c4b162 100644 --- a/projects/toolbox-main/src/app/common/elements/input/input.component.ts +++ b/projects/toolbox-main/src/app/common/elements/input/input.component.ts @@ -1,9 +1,10 @@ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ selector: 'input[standard][type=text]', - templateUrl: './input.component.html', - styleUrls: ['./input.component.scss'] + template: '', + styleUrls: ['./input.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush }) export class InputComponent { } \ No newline at end of file diff --git a/projects/toolbox-main/src/app/common/elements/loading-indicator/loading-indicator.component.ts b/projects/toolbox-main/src/app/common/elements/loading-indicator/loading-indicator.component.ts index 47275d7..7a472bf 100644 --- a/projects/toolbox-main/src/app/common/elements/loading-indicator/loading-indicator.component.ts +++ b/projects/toolbox-main/src/app/common/elements/loading-indicator/loading-indicator.component.ts @@ -1,18 +1,22 @@ -import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, ViewChild } from '@angular/core'; import { randomRange } from 'src/lib/utils'; @Component({ selector: 'tbx-loading-indicator', templateUrl: './loading-indicator.component.html', - styleUrls: ['./loading-indicator.component.scss'] + styleUrls: ['./loading-indicator.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush }) export class LoadingIndicatorComponent implements AfterViewInit { @ViewChild("item") private item!: ElementRef; - ngAfterViewInit(): void { - this.item.nativeElement.addEventListener("animationiteration", (e) => { + public ngAfterViewInit(): void { + let animationListener = (e: AnimationEvent) => { (e.target).style.backgroundPositionX = `calc(100% * ${randomRange(0, 3)})`; - }); + }; + + this.item.nativeElement.addEventListener("animationstart", animationListener); + this.item.nativeElement.addEventListener("animationiteration", animationListener); } } \ No newline at end of file diff --git a/projects/toolbox-main/src/app/common/elements/panorama/panorama.component.ts b/projects/toolbox-main/src/app/common/elements/panorama/panorama.component.ts index b495aca..779a6c5 100644 --- a/projects/toolbox-main/src/app/common/elements/panorama/panorama.component.ts +++ b/projects/toolbox-main/src/app/common/elements/panorama/panorama.component.ts @@ -1,39 +1,60 @@ -import { AfterViewInit, Component, ElementRef, HostListener, ViewChild } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, HostListener, ViewChild } from '@angular/core'; import { firstValueFrom, skip } from 'rxjs'; import { PanoramaService } from '../../services/panorama-service/panorama.service'; import { ToolboxSettingsService } from '../../services/toolbox-settings/toolbox-settings.service'; -//#4 TODO: Change the cube-texturing method from individual textures to texture coordinates. -// This would allow for more efficient texture rendering, as well as removing the need to -// load the individual parts as separate images, reducing the memory footprint and processing time. - @Component({ selector: 'tbx-panorama', templateUrl: './panorama.component.html', - styleUrls: ['./panorama.component.scss'] + styleUrls: ['./panorama.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush }) export class PanoramaComponent implements AfterViewInit { private _panoramaSrcCache: string | undefined; private _firstPanoramaRendered = false; + private _image = document.createElement("img"); @ViewChild("panorama") private canvas!: ElementRef; private context!: WebGLRenderingContext; private fieldOfViewRadians = this.degToRad(90); private projectionMatrix = [ - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, -3, -1, - 0, 0, -4, 0 + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, -1, -1, + 0, 0, 0, 0 ]; + //projectionMatrix calculation + /* + (() => { + const aspect = this.context.canvas.clientWidth / this.context.canvas.clientHeight; + const zNear = 0.1; + const zFar = 100.0; + var f = Math.tan(Math.PI * 0.5 - 0.5 * this.fieldOfViewRadians); + var rangeInv = 1.0 / (zNear - zFar); + + return [ + f / aspect, 0, 0, 0, + 0, f, 0, 0, + 0, 0, (zNear + zFar) * rangeInv, -1, + 0, 0, zNear * zFar * rangeInv * 2, 0 + ]; + })(); + */ public constructor( private _panorama: PanoramaService, private _toolboxSettings: ToolboxSettingsService, ) { + this._image.addEventListener('load', () => { + this.context.texImage2D(this.context.TEXTURE_2D, 0, this.context.RGBA, this.context.RGBA, this.context.UNSIGNED_BYTE, this._image); + this.canvas.nativeElement.style.removeProperty('display'); + this.resize(); + this._firstPanoramaRendered = true; + }); } - ngAfterViewInit(): void { + public ngAfterViewInit(): void { this.context = this.canvas.nativeElement.getContext("webgl")!; if (!this.context) { this.canvas.nativeElement.remove(); @@ -48,118 +69,125 @@ export class PanoramaComponent implements AfterViewInit { } if (uselessVisualsEnabled && this._panoramaSrcCache) { - this.setCubemap(this._panoramaSrcCache, this.context); + this._image.src = this._panoramaSrcCache; this._panoramaSrcCache = undefined; } this.resize(); }); - this.canvas.nativeElement.style.display = "none"; - this._panorama.Observe.panoramaImage .pipe(skip(1)) .subscribe(async imageUri => { let enabled = await firstValueFrom(this._toolboxSettings.Observe.uselessVisualsEnabled); if (enabled) { - this.setCubemap(imageUri, this.context); + this._image.src = imageUri; } else { this._panoramaSrcCache = imageUri; } }); this.resize(); - this.run(this.context); + this.run(); this._panorama.setIndex("newest"); } - private run = (context: WebGLRenderingContext) => { - let up = [0, 1, 0]; - let program = this.createProgram(context); - let positionLocation = context.getAttribLocation(program, "a_pos"); - let viewDirectionProjectionInverseLocation = context.getUniformLocation(program, "u_ivdp"); - context.enableVertexAttribArray(positionLocation); - context.bindBuffer(context.ARRAY_BUFFER, context.createBuffer()); - context.bufferData(context.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]), context.STATIC_DRAW); - context.bindTexture(context.TEXTURE_CUBE_MAP, context.createTexture()); - context.texParameteri(context.TEXTURE_CUBE_MAP, context.TEXTURE_MIN_FILTER, context.LINEAR); - context.vertexAttribPointer(positionLocation, 2, context.FLOAT, false, 0, 0); + private run = () => { + const program = this.createProgram(); + + this.context.bindTexture(this.context.TEXTURE_2D, this.context.createTexture()); + this.context.texParameteri(this.context.TEXTURE_2D, this.context.TEXTURE_WRAP_S, this.context.CLAMP_TO_EDGE); + this.context.texParameteri(this.context.TEXTURE_2D, this.context.TEXTURE_WRAP_T, this.context.CLAMP_TO_EDGE); + this.context.texParameteri(this.context.TEXTURE_2D, this.context.TEXTURE_MIN_FILTER, this.context.LINEAR); + + //Panorama Cube + this.context.bindBuffer(this.context.ARRAY_BUFFER, this.context.createBuffer()); + this.context.bufferData(this.context.ARRAY_BUFFER, new Float32Array([ + -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, // Front + 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, // Back + -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, // Top + -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, // Bottom + 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, // Right + -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, // Left + ]), this.context.STATIC_DRAW); + + const vertexPosition = this.context.getAttribLocation(program, "a_vpos"); + this.context.vertexAttribPointer(vertexPosition, 3, this.context.FLOAT, false, 0, 0); + this.context.enableVertexAttribArray(vertexPosition); + + //Texture Coordinates + const getTextureCoordinates = (face: number) => { + let x = face % 3; + let y = Math.floor(face / 3); + + const thirdFraction = 1 / 3; + const secondFraction = 1 / 2; + + let result = [ + thirdFraction * x, secondFraction * y, // Top-Left + thirdFraction + (thirdFraction * x), secondFraction * y, // Top-Right + thirdFraction + (thirdFraction * x), secondFraction + (secondFraction * y), // Bottom-Right + thirdFraction * x, secondFraction + (secondFraction * y) // Bottom-Left + ]; + + const margin = 0.0003; + return [ + result[0] + margin, result[1] + margin, + result[2] - margin, result[3] + margin, + result[4] - margin, result[5] - margin, + result[6] + margin, result[7] - margin + ]; + } + + this.context.bindBuffer(this.context.ARRAY_BUFFER, this.context.createBuffer()); + this.context.bufferData(this.context.ARRAY_BUFFER, new Float32Array([ + ...getTextureCoordinates(0), // Front + ...getTextureCoordinates(1), // Back + ...getTextureCoordinates(2), // Top + ...getTextureCoordinates(3), // Bottom + ...getTextureCoordinates(4), // Right + ...getTextureCoordinates(5), // Left + ]), this.context.STATIC_DRAW); + + const textureCoord = this.context.getAttribLocation(program, "a_texcoord"); + this.context.vertexAttribPointer(textureCoord, 2, this.context.FLOAT, false, 0, 0); + this.context.enableVertexAttribArray(textureCoord); + + //Indices + this.context.bindBuffer(this.context.ELEMENT_ARRAY_BUFFER, this.context.createBuffer()); + this.context.bufferData(this.context.ELEMENT_ARRAY_BUFFER, new Uint16Array([ + 0, 1, 2, 0, 2, 3, // Front + 4, 5, 6, 4, 6, 7, // Back + 8, 9, 10, 8, 10, 11, // Top + 12, 13, 14, 12, 14, 15, // Bottom + 16, 17, 18, 16, 18, 19, // Right + 20, 21, 22, 20, 22, 23, // Left + ]), this.context.STATIC_DRAW); + + this.context.uniformMatrix4fv(this.context.getUniformLocation(program, "u_modelmatrix"), false, [ + 0, 0, -1, 0, + 0, 1, 0, 0, + 1, 0, 0, 0, + 0, 0, 0, 1 + ]); + + const up = [0, 1, 0]; let drawScene = (time: number) => { let rotFactor = time / 1000 * .035; - let cameraPosition = [Math.cos(rotFactor), (-Math.cos(rotFactor * 2.1) + 1) * .5, Math.sin(rotFactor)]; + let cameraPosition = [Math.cos(rotFactor), (-Math.cos(rotFactor * 2.1) + 1) * 0.5, Math.sin(rotFactor)]; let cameraMatrix = this.lookAt(cameraPosition, up); let viewMatrix = this.inverse(cameraMatrix); let viewDirectionProjectionMatrix = this.multiply(this.projectionMatrix, viewMatrix); - let viewDirectionProjectionInverseMatrix = this.inverse(viewDirectionProjectionMatrix); - context.uniformMatrix4fv(viewDirectionProjectionInverseLocation, false, viewDirectionProjectionInverseMatrix); - context.drawArrays(context.TRIANGLES, 0, 6); + + this.context.uniformMatrix4fv(this.context.getUniformLocation(program, "u_projectionmatrix"), false, viewDirectionProjectionMatrix); + this.context.drawElements(this.context.TRIANGLES, 36, this.context.UNSIGNED_SHORT, 0); requestAnimationFrame(drawScene); } drawScene(0); } - private createSides(image: HTMLImageElement) { - let cubeDimension = image.height / 2; - - let sideCanvas = document.createElement('canvas'); - let sideContext = sideCanvas.getContext('2d')!; - sideCanvas.width = cubeDimension; - sideCanvas.height = cubeDimension; - - sideContext.translate(cubeDimension, 0); - sideContext.scale(-1, 1); - - let faces: HTMLImageElement[] = []; - const event = new CustomEvent('created', { detail: faces }); - let elem = document.createElement('object'); - for (let cubemapIndex = 0; cubemapIndex < 6; cubemapIndex++) { - let x = cubemapIndex % 3; - let y = Math.floor(cubemapIndex / 3); - sideContext.drawImage(image, x * cubeDimension, y * cubeDimension, cubeDimension, cubeDimension, 0, 0, cubeDimension, cubeDimension); - sideCanvas.toBlob((blob) => { - let cubemapFace = document.createElement('img'); - let url = URL.createObjectURL(blob!); - - cubemapFace.addEventListener('load', () => { - URL.revokeObjectURL(url); - faces[cubemapIndex] = cubemapFace; - if (faces.filter(Boolean).length === 6) { - elem.dispatchEvent(event); - } - }); - - cubemapFace.src = url; - }); - } - return elem; - } - - private setCubemap = (uri: string, gl: WebGLRenderingContext) => { - let faces = [ - gl.TEXTURE_CUBE_MAP_NEGATIVE_X, - gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, - gl.TEXTURE_CUBE_MAP_POSITIVE_X, - gl.TEXTURE_CUBE_MAP_POSITIVE_Z, - gl.TEXTURE_CUBE_MAP_POSITIVE_Y, - gl.TEXTURE_CUBE_MAP_NEGATIVE_Y - ]; - let img = document.createElement('img'); - img.addEventListener('load', () => { - this.createSides(img).addEventListener('created', (e) => { - let i = 0; - (e).detail.forEach((face: TexImageSource) => { - gl.texImage2D(faces[i++], 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, face); - }); - this.canvas.nativeElement.style.removeProperty('display'); - this.resize(); - this._firstPanoramaRendered = true; - }); - }); - img.src = uri; - } - @HostListener('window:resize', ['$event']) public resize = () => { let canvas = this.canvas.nativeElement; @@ -175,21 +203,21 @@ export class PanoramaComponent implements AfterViewInit { this.projectionMatrix[5] = f; } - private createProgram(context: WebGLRenderingContext) { - const program = context.createProgram()!; + private createProgram() { + const program = this.context.createProgram()!; - let vertex = context.createShader(context.VERTEX_SHADER)!; - context.shaderSource(vertex, 'attribute vec4 a_pos;varying vec4 v_pos;void main(){v_pos=a_pos;gl_Position=a_pos;}'); - context.compileShader(vertex); - context.attachShader(program, vertex); + let vertex = this.context.createShader(this.context.VERTEX_SHADER)!; + this.context.shaderSource(vertex, `attribute vec4 a_vpos;attribute vec2 a_texcoord;uniform mat4 u_modelmatrix;uniform mat4 u_projectionmatrix;varying highp vec2 v_texcoord;void main(void){gl_Position=u_projectionmatrix*u_modelmatrix*a_vpos;v_texcoord=a_texcoord;}`); + this.context.compileShader(vertex); + this.context.attachShader(program, vertex); - let fragment = context.createShader(context.FRAGMENT_SHADER)!; - context.shaderSource(fragment, 'precision mediump float;uniform samplerCube u_sky;uniform mat4 u_ivdp;varying vec4 v_pos;void main(){vec4 t=u_ivdp*v_pos;gl_FragColor=textureCube(u_sky,normalize(t.xyz/t.w));}'); - context.compileShader(fragment); - context.attachShader(program, fragment); + let fragment = this.context.createShader(this.context.FRAGMENT_SHADER)!; + this.context.shaderSource(fragment, `varying highp vec2 v_texcoord;uniform sampler2D u_sampler;void main(void){gl_FragColor=texture2D(u_sampler,v_texcoord);}`); + this.context.compileShader(fragment); + this.context.attachShader(program, fragment); - context.linkProgram(program); - context.useProgram(program); + this.context.linkProgram(program); + this.context.useProgram(program); return program; } diff --git a/projects/toolbox-main/src/app/common/elements/selection/selection.component.html b/projects/toolbox-main/src/app/common/elements/selection/selection.component.html index 434a32f..2d8f49d 100644 --- a/projects/toolbox-main/src/app/common/elements/selection/selection.component.html +++ b/projects/toolbox-main/src/app/common/elements/selection/selection.component.html @@ -1,7 +1,6 @@
- >
-
-
- +
+ +
diff --git a/projects/toolbox-main/src/app/common/elements/selection/selection.component.scss b/projects/toolbox-main/src/app/common/elements/selection/selection.component.scss index 5e8413b..a0fe816 100644 --- a/projects/toolbox-main/src/app/common/elements/selection/selection.component.scss +++ b/projects/toolbox-main/src/app/common/elements/selection/selection.component.scss @@ -1,9 +1,15 @@ :host { - max-height: 450px; - overflow-y: auto; display: block; } +.entry-group { + margin-bottom: 1em; + + &:last-child { + margin-bottom: 0; + } +} + .checkbox { display: flex; flex-direction: row; @@ -53,9 +59,52 @@ } } +.display-container { + position: relative; +} + .entries { - display: none; - margin-left: 40px; + overflow-y: auto; + max-height: 455px; + margin-left: 2em; + transition: max-height 1s cubic-bezier(0, 1, 0, 1); + + &+.state-toggler { + cursor: pointer; + display: block; + border: none; + background: transparent; + width: 100%; + + &::before { + content: "⯅ Collapse list"; + text-shadow: + 0px 0px 8px #000, + 0px 0px 8px #000; + } + } + + &.collapsed { + max-height: 50px; + overflow-y: hidden; + -webkit-mask-image: linear-gradient(to bottom, black, transparent); + mask-image: linear-gradient(to bottom, black, transparent); + + //This sadly doesn't limit keyboard interactivity :/ + pointer-events: none; + user-select: none; + + &+.state-toggler { + position: absolute; + width: 100%; + height: 100%; + top: 0; + + &::before { + content: "⯆ Expand list"; + } + } + } } .entry { diff --git a/projects/toolbox-main/src/app/common/elements/selection/selection.component.ts b/projects/toolbox-main/src/app/common/elements/selection/selection.component.ts index 1c73c72..5813eb5 100644 --- a/projects/toolbox-main/src/app/common/elements/selection/selection.component.ts +++ b/projects/toolbox-main/src/app/common/elements/selection/selection.component.ts @@ -1,10 +1,11 @@ -import { ChangeDetectorRef, Component, HostListener, Input, OnChanges } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges } from '@angular/core'; import { AssetManagerService } from 'src/app/common/services/asset-manager/asset-manager.service'; @Component({ selector: 'tbx-selection', templateUrl: './selection.component.html', - styleUrls: ['./selection.component.scss'] + styleUrls: ['./selection.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush }) export class SelectionComponent implements OnChanges { private _entries: EntryGroup[] = []; @@ -26,16 +27,6 @@ export class SelectionComponent implements OnChanges { this._changeDetectorRef.detach(); } - @HostListener("scroll", ["$event"]) - public clipOnScroll(e: Event) { - const target = e.target as HTMLElement; - const entriesList = target.querySelectorAll(".entries"); - - for (const entries of entriesList) { - entries.style.clipPath = `inset(${target.scrollTop + 263 - entries.parentElement!.offsetTop}px 0px 0px)`; - } - } - public changeEntireGroup(group: HTMLElement, groupCheckbox: HTMLInputElement) { let groupCheckboxes = group.querySelectorAll(".entry > label > input[type=checkbox]"); let checked = groupCheckbox.checked; @@ -59,8 +50,7 @@ export class SelectionComponent implements OnChanges { } public toggleEntries(entryContainer: HTMLElement) { - let visible = entryContainer.style.display === "block"; - entryContainer.style.display = visible ? "none" : "block"; + entryContainer.classList.toggle("collapsed"); } /* diff --git a/projects/toolbox-main/src/app/common/elements/sub-section/sub-section.component.scss b/projects/toolbox-main/src/app/common/elements/sub-section/sub-section.component.scss index 57f22cb..70e0fc2 100644 --- a/projects/toolbox-main/src/app/common/elements/sub-section/sub-section.component.scss +++ b/projects/toolbox-main/src/app/common/elements/sub-section/sub-section.component.scss @@ -2,7 +2,7 @@ display: block; margin-bottom: 1em; border-left: 2px solid white; - background-color: #0000002e; + background-color: #00000049; padding: 15px; &:last-child { diff --git a/projects/toolbox-main/src/app/common/elements/sub-section/sub-section.component.ts b/projects/toolbox-main/src/app/common/elements/sub-section/sub-section.component.ts index 8e63ea7..f6c4b5b 100644 --- a/projects/toolbox-main/src/app/common/elements/sub-section/sub-section.component.ts +++ b/projects/toolbox-main/src/app/common/elements/sub-section/sub-section.component.ts @@ -1,9 +1,10 @@ -import { Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; @Component({ selector: 'tbx-sub-section', templateUrl: './sub-section.component.html', - styleUrls: ['./sub-section.component.scss'] + styleUrls: ['./sub-section.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush }) export class SubSectionComponent { @Input() diff --git a/projects/toolbox-main/src/app/common/elements/tooltip/tooltip.component.html b/projects/toolbox-main/src/app/common/elements/tooltip/tooltip.component.html deleted file mode 100644 index 95a0b70..0000000 --- a/projects/toolbox-main/src/app/common/elements/tooltip/tooltip.component.html +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/projects/toolbox-main/src/app/common/elements/tooltip/tooltip.component.ts b/projects/toolbox-main/src/app/common/elements/tooltip/tooltip.component.ts index a450ca4..a76ca13 100644 --- a/projects/toolbox-main/src/app/common/elements/tooltip/tooltip.component.ts +++ b/projects/toolbox-main/src/app/common/elements/tooltip/tooltip.component.ts @@ -1,9 +1,10 @@ -import { Component, HostListener, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostListener, Input } from '@angular/core'; @Component({ selector: '[tooltip]', - templateUrl: './tooltip.component.html', - styleUrls: ['./tooltip.component.scss'] + template: '', + styleUrls: ['./tooltip.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush }) export class TooltipComponent { private static _tooltipDisplay = (() => { @@ -32,4 +33,4 @@ export class TooltipComponent { private mouseenter(e: MouseEvent): void { TooltipComponent._tooltipDisplay.style.display = "block"; } -} +} \ No newline at end of file diff --git a/projects/toolbox-main/src/resources/data/changelog.json b/projects/toolbox-main/src/resources/data/changelog.json index ebfab84..f4cf665 100644 --- a/projects/toolbox-main/src/resources/data/changelog.json +++ b/projects/toolbox-main/src/resources/data/changelog.json @@ -58,5 +58,22 @@ "removals": [] }, "notes": "" + }, + { + "version": "1.1.1", + "build_date": "2022-09-04", + "changes": { + "additions": [], + "modifications": [ + "Selection menus are now more obvious, with a full 'Expand' and 'Collapse' button", + "The background of smaller sub-sections is now a bit darker, to improve readability", + "Slight general performance improvements", + "The panorama now actually starts with the front face", + "The panorama now loads faster and no longer arbitrarily chooses between the newest and current version", + "Panorama images are now less compressed, to prevent the giant dither artifacts" + ], + "removals": [] + }, + "notes": "" } ] \ No newline at end of file diff --git a/projects/toolbox-main/src/resources/other/panorama/1_12.png b/projects/toolbox-main/src/resources/other/panorama/1_12.png index d72d6b6..94f9fc2 100644 Binary files a/projects/toolbox-main/src/resources/other/panorama/1_12.png and b/projects/toolbox-main/src/resources/other/panorama/1_12.png differ diff --git a/projects/toolbox-main/src/resources/other/panorama/1_13.png b/projects/toolbox-main/src/resources/other/panorama/1_13.png index 2f6a323..3c30396 100644 Binary files a/projects/toolbox-main/src/resources/other/panorama/1_13.png and b/projects/toolbox-main/src/resources/other/panorama/1_13.png differ diff --git a/projects/toolbox-main/src/resources/other/panorama/1_14.png b/projects/toolbox-main/src/resources/other/panorama/1_14.png index e11b95e..e8cf3a2 100644 Binary files a/projects/toolbox-main/src/resources/other/panorama/1_14.png and b/projects/toolbox-main/src/resources/other/panorama/1_14.png differ diff --git a/projects/toolbox-main/src/resources/other/panorama/1_15.png b/projects/toolbox-main/src/resources/other/panorama/1_15.png index b957579..7188c9a 100644 Binary files a/projects/toolbox-main/src/resources/other/panorama/1_15.png and b/projects/toolbox-main/src/resources/other/panorama/1_15.png differ diff --git a/projects/toolbox-main/src/resources/other/panorama/1_16.png b/projects/toolbox-main/src/resources/other/panorama/1_16.png index e3961b7..5201717 100644 Binary files a/projects/toolbox-main/src/resources/other/panorama/1_16.png and b/projects/toolbox-main/src/resources/other/panorama/1_16.png differ diff --git a/projects/toolbox-main/src/resources/other/panorama/1_17.png b/projects/toolbox-main/src/resources/other/panorama/1_17.png index f85067e..edfb303 100644 Binary files a/projects/toolbox-main/src/resources/other/panorama/1_17.png and b/projects/toolbox-main/src/resources/other/panorama/1_17.png differ diff --git a/projects/toolbox-main/src/resources/other/panorama/1_18.png b/projects/toolbox-main/src/resources/other/panorama/1_18.png index a4738be..d8c7fb0 100644 Binary files a/projects/toolbox-main/src/resources/other/panorama/1_18.png and b/projects/toolbox-main/src/resources/other/panorama/1_18.png differ diff --git a/projects/toolbox-main/src/resources/other/panorama/1_19.png b/projects/toolbox-main/src/resources/other/panorama/1_19.png index 628f336..137f470 100644 Binary files a/projects/toolbox-main/src/resources/other/panorama/1_19.png and b/projects/toolbox-main/src/resources/other/panorama/1_19.png differ diff --git a/projects/toolbox-main/src/resources/other/panorama/template.png b/projects/toolbox-main/src/resources/other/panorama/template.png deleted file mode 100644 index 0d04073..0000000 Binary files a/projects/toolbox-main/src/resources/other/panorama/template.png and /dev/null differ diff --git a/projects/toolbox-utilities/src/app/app.component.html b/projects/toolbox-utilities/src/app/app.component.html index be9fd70..ddfb939 100644 --- a/projects/toolbox-utilities/src/app/app.component.html +++ b/projects/toolbox-utilities/src/app/app.component.html @@ -1,2 +1,3 @@ - \ No newline at end of file + + \ No newline at end of file diff --git a/projects/toolbox-utilities/src/app/app.module.ts b/projects/toolbox-utilities/src/app/app.module.ts index c161f3e..befa580 100644 --- a/projects/toolbox-utilities/src/app/app.module.ts +++ b/projects/toolbox-utilities/src/app/app.module.ts @@ -5,6 +5,7 @@ import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { CRR_DatapackPreparerComponent } from './crafting-recipe-randomizer/datapack-preparer/datapack-preparer.component'; import { LTR_DatapackPreparerComponent } from './loot-table-randomizer/datapack-preparer/datapack-preparer.component'; +import { PanoramaGeneratorComponent } from './panorama-generator/panorama-generator.component'; import { SafeLinkPipe } from './pipes/safe-link/safe-link.pipe'; @NgModule({ @@ -12,7 +13,8 @@ import { SafeLinkPipe } from './pipes/safe-link/safe-link.pipe'; AppComponent, LTR_DatapackPreparerComponent, CRR_DatapackPreparerComponent, - SafeLinkPipe + SafeLinkPipe, + PanoramaGeneratorComponent ], imports: [ BrowserModule, diff --git a/projects/toolbox-utilities/src/app/loot-table-randomizer/datapack-preparer/datapack-preparer.component.ts b/projects/toolbox-utilities/src/app/loot-table-randomizer/datapack-preparer/datapack-preparer.component.ts index 4b20faa..fe1eaa9 100644 --- a/projects/toolbox-utilities/src/app/loot-table-randomizer/datapack-preparer/datapack-preparer.component.ts +++ b/projects/toolbox-utilities/src/app/loot-table-randomizer/datapack-preparer/datapack-preparer.component.ts @@ -43,134 +43,6 @@ export class LTR_DatapackPreparerComponent { this._changeDetector.detectChanges(); } - - // public async showRunner(data: Map) { - // console.log("#### Getting required files ####"); - - // let datapackData = data.get("datapack")!; - // let assetDefinitions = data.get("asset_definitions")!; - // let defaultSelectionExclusions = data.get("unselected")!; - - // console.log("#### Converting datapack zip to object ####"); - - // let datapackObject = await convertZipToObject(await readFileBuffer(datapackData)); - - // console.log("#### Updating asset definition ####"); - - // let additionalAssetDefinitions: any = document.querySelectorAll("input[name='missing_asset_definitions']"); - // additionalAssetDefinitions = Object.fromEntries([...additionalAssetDefinitions].map(x => [x.id, x.value])); - - // let existingAssetDefinitions = JSON.parse(await readFileString(assetDefinitions)); - - // let assetDefinitionsObject = await this.generateAssetDefinition(datapackObject, { ...existingAssetDefinitions, ...additionalAssetDefinitions }); - - // // if (!this.finalizable) return; - - // console.log("#### Generating web selection menu ####"); - - // let selectionMenu = await this.generateSelectionMenu(datapackObject, JSON.parse(await readFileString(defaultSelectionExclusions))); - - // for (const key of Object.keys(selectionMenu)) { - // selectionMenu[key].sort((a: any, b: any) => a.assetId.localeCompare(b.assetId)); - // } - - // console.log("#### Saving processed output ####"); - - // let dataZip = await dataToZip({ - // "loot-table.json": datapackObject, - // "selection_menu.json": selectionMenu - // }); - - // let zip = new fflateZip(); - - // zip.add("data.zip", new Uint8Array(await dataZip.arrayBuffer())); - // zip.add("asset_definitions.json", assetDefinitionsObject); - - // this.finalFile = window.URL.createObjectURL(await zip.finalize()); - // } - - // private async generateAssetDefinition(datapackObject: any, assetDefinitionObject: any) { - // let assetKeyList: string[] = []; - - // let lootTables = datapackObject["data"]["minecraft"]["loot_tables"]; - // for (let folder in lootTables) { - // let key = `${lootTableKeys[folder]}.minecraft.`; - - // assetListGenerator(lootTables[folder], key); - // } - - // assetKeyList.sort(); - - // let newAssetDefinitionsObject: any = {}; - - // for (let index of assetKeyList) { - // let assetDefinition = assetDefinitionObject[index]; - - // if (!assetDefinition) { - // console.log(`Asset definition for key '${index}' could not be found.`); - // this.missingAssetDefinitions[index] = ""; - // // this.finalizable = false; - // } - // else { - // newAssetDefinitionsObject[index] = assetDefinition; - // } - // } - - // return { ...assetDefinitionObject, ...newAssetDefinitionsObject }; - - // function assetListGenerator(list: any, key: string) { - // for (let file in list) { - // if (file.endsWith(".json")) { - // assetKeyList.push(key + file.replace(".json", "")); - // } - // else { - // assetListGenerator(list[file], key + file + "."); - // } - // } - // } - // } - - // private async generateSelectionMenu(datapackObject: any, defaultSelectionExclusions: string[]) { - // let selectionMenu: any = {}; - - // let lootTables = datapackObject["data"]["minecraft"]["loot_tables"]; - // for (let folder in lootTables) { - // if (Object.keys(lootTables[folder]).length < 1) { - // //Not a folder, skip. - // continue; - // } - // optionGroupGenerator(lootTables[folder], defaultSelectionExclusions, { - // optGroup: `group.minecraft.loot_tables.${folder}`, - // value: `data/minecraft/loot_tables/${folder}/`, - // assetId: `${lootTableKeys[folder]}.minecraft.` - // }); - // } - - // return selectionMenu; - - // function optionGroupGenerator(list: any, defaultSelectionExclusions: string[], params: SelectionParameters) { - // selectionMenu[params.optGroup] = selectionMenu[params.optGroup] ?? []; - - // for (let file in list) { - // if (file.endsWith(".json")) { - // let fullLootTableName = params.value + file; - - // selectionMenu[params.optGroup].push({ - // selected: !defaultSelectionExclusions.includes(fullLootTableName), - // assetId: params.assetId + file.replace(".json", ""), - // value: fullLootTableName - // }); - // } - // else { - // optionGroupGenerator(list[file], defaultSelectionExclusions, { - // ...params, - // value: `${params.value}${file}/`, - // assetId: `${params.assetId}${file}.` - // }); - // } - // } - // } - // } } function convertZipToObject(zip: ArrayBuffer) { @@ -233,13 +105,6 @@ async function dataToZip(files: FileDefinition) { return zip.finalize(); } -// let lootTableKeys: any = { -// blocks: "block", -// chests: "chest", -// entities: "entity", -// gameplay: "gameplay" -// } - interface SelectionParameters { optGroup: string; value: string; diff --git a/projects/toolbox-utilities/src/app/panorama-generator/panorama-generator.component.html b/projects/toolbox-utilities/src/app/panorama-generator/panorama-generator.component.html new file mode 100644 index 0000000..ff93ac1 --- /dev/null +++ b/projects/toolbox-utilities/src/app/panorama-generator/panorama-generator.component.html @@ -0,0 +1,7 @@ +

Panorama generator

+
+ Panorama Images + + +
+ \ No newline at end of file diff --git a/projects/toolbox-main/src/app/common/elements/input/input.component.html b/projects/toolbox-utilities/src/app/panorama-generator/panorama-generator.component.scss similarity index 100% rename from projects/toolbox-main/src/app/common/elements/input/input.component.html rename to projects/toolbox-utilities/src/app/panorama-generator/panorama-generator.component.scss diff --git a/projects/toolbox-utilities/src/app/panorama-generator/panorama-generator.component.ts b/projects/toolbox-utilities/src/app/panorama-generator/panorama-generator.component.ts new file mode 100644 index 0000000..3fc8402 --- /dev/null +++ b/projects/toolbox-utilities/src/app/panorama-generator/panorama-generator.component.ts @@ -0,0 +1,71 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'tbx-panorama-generator', + templateUrl: './panorama-generator.component.html', + styleUrls: ['./panorama-generator.component.scss'] +}) +export class PanoramaGeneratorComponent { + public finalFile?: string; + + public async prepare(e: Event) { + e.preventDefault(); + e.stopPropagation(); + + let formData = new FormData(e.target); + let files = formData.getAll("panoramas"); + + let images = await Promise.all(files.map(this.readAsImage)); + + images = [ + images.find(x => x.name.endsWith("0"))!, // Front + images.find(x => x.name.endsWith("2"))!, // Back + images.find(x => x.name.endsWith("4"))!, // Top + images.find(x => x.name.endsWith("5"))!, // Bottom + images.find(x => x.name.endsWith("1"))!, // Right + images.find(x => x.name.endsWith("3"))!, // Left + ] + + let canvas = document.createElement("canvas"); + let ctx = canvas.getContext("2d")!; + + canvas.width = images[0].data.naturalWidth * 3; + canvas.height = images[0].data.naturalHeight * 2; + + for (let i = 0; i < images.length; i++) { + let img = images[i].data; + let x = i % 3; + let y = Math.floor(i / 3); + console.log(x, y); + + ctx.drawImage(img, x * img.naturalWidth, y * img.naturalHeight); + } + + this.finalFile = canvas.toDataURL(); + } + + private async readAsImage(file: File) { + let asyncData = async () => { + return new Promise((resolve, reject) => { + let reader = new FileReader(); + + reader.addEventListener("load", () => { + let img = new Image(); + + img.addEventListener("load", () => { + resolve(img); + }); + + img.src = reader.result; + }); + + reader.readAsDataURL(file); + }); + } + + return { + name: file.name.substring(0, file.name.lastIndexOf(".")), + data: await asyncData() + } + } +}