From db0434c0a0a573fdf71c6449697a1f40b977f647 Mon Sep 17 00:00:00 2001 From: Lars Moastuen Date: Sat, 21 Mar 2020 17:48:04 +0100 Subject: [PATCH 1/7] Add utility for computing power-of-two texture size to hold N elements. --- .../determinePowerOfTwoDimensions.test.ts | 16 ++++++++++++++++ .../src/utils/determinePowerOfTwoDimensions.ts | 18 ++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 viewer/src/__tests__/utils/determinePowerOfTwoDimensions.test.ts create mode 100644 viewer/src/utils/determinePowerOfTwoDimensions.ts diff --git a/viewer/src/__tests__/utils/determinePowerOfTwoDimensions.test.ts b/viewer/src/__tests__/utils/determinePowerOfTwoDimensions.test.ts new file mode 100644 index 00000000000..6b788809fdb --- /dev/null +++ b/viewer/src/__tests__/utils/determinePowerOfTwoDimensions.test.ts @@ -0,0 +1,16 @@ +/*! + * Copyright 2020 Cognite AS + */ + +import { determineTextureDimensions } from '../../utils/determinePowerOfTwoDimensions'; + +test('determinePowerOfTwoDimensions', () => { + expect(determineTextureDimensions(0)).toEqual({ width: 1, height: 1 }); + expect(determineTextureDimensions(1)).toEqual({ width: 1, height: 1 }); + expect(determineTextureDimensions(2)).toEqual({ width: 2, height: 1 }); + expect(determineTextureDimensions(3)).toEqual({ width: 2, height: 2 }); + expect(determineTextureDimensions(1025)).toEqual({ width: 64, height: 32 }); + expect(determineTextureDimensions(2048)).toEqual({ width: 64, height: 32 }); + expect(determineTextureDimensions(2049)).toEqual({ width: 64, height: 64 }); + expect(determineTextureDimensions(4096 * 4096 - 1)).toEqual({ width: 4096, height: 4096 }); +}); diff --git a/viewer/src/utils/determinePowerOfTwoDimensions.ts b/viewer/src/utils/determinePowerOfTwoDimensions.ts new file mode 100644 index 00000000000..e4404ff4e6d --- /dev/null +++ b/viewer/src/utils/determinePowerOfTwoDimensions.ts @@ -0,0 +1,18 @@ +/*! + * Copyright 2020 Cognite AS + */ + +/** + * Computes minimal power-of-two width and height that holds at least the number of elements provided. + * This is useful to compute texture sizes. + */ +export function determineTextureDimensions(elementCount: number): { width: number; height: number } { + const width = Math.max(1, ceilToPowerOfTwo(Math.sqrt(elementCount))); + const height = Math.max(1, ceilToPowerOfTwo(elementCount / width)); + return { width, height }; +} + +const log2 = Math.log(2); +function ceilToPowerOfTwo(v: number): number { + return Math.pow(2, Math.ceil(Math.log(v) / log2)); +} From 1c066b038021643e85f906031e62f202a75027cf Mon Sep 17 00:00:00 2001 From: Lars Moastuen Date: Sat, 21 Mar 2020 17:50:09 +0100 Subject: [PATCH 2/7] Typo. --- .../determinePowerOfTwoDimensions.test.ts | 18 +++++++++--------- .../src/utils/determinePowerOfTwoDimensions.ts | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/viewer/src/__tests__/utils/determinePowerOfTwoDimensions.test.ts b/viewer/src/__tests__/utils/determinePowerOfTwoDimensions.test.ts index 6b788809fdb..8aa3e85816e 100644 --- a/viewer/src/__tests__/utils/determinePowerOfTwoDimensions.test.ts +++ b/viewer/src/__tests__/utils/determinePowerOfTwoDimensions.test.ts @@ -2,15 +2,15 @@ * Copyright 2020 Cognite AS */ -import { determineTextureDimensions } from '../../utils/determinePowerOfTwoDimensions'; +import { determinePowerOfTwoDimensions } from '../../utils/determinePowerOfTwoDimensions'; test('determinePowerOfTwoDimensions', () => { - expect(determineTextureDimensions(0)).toEqual({ width: 1, height: 1 }); - expect(determineTextureDimensions(1)).toEqual({ width: 1, height: 1 }); - expect(determineTextureDimensions(2)).toEqual({ width: 2, height: 1 }); - expect(determineTextureDimensions(3)).toEqual({ width: 2, height: 2 }); - expect(determineTextureDimensions(1025)).toEqual({ width: 64, height: 32 }); - expect(determineTextureDimensions(2048)).toEqual({ width: 64, height: 32 }); - expect(determineTextureDimensions(2049)).toEqual({ width: 64, height: 64 }); - expect(determineTextureDimensions(4096 * 4096 - 1)).toEqual({ width: 4096, height: 4096 }); + expect(determinePowerOfTwoDimensions(0)).toEqual({ width: 1, height: 1 }); + expect(determinePowerOfTwoDimensions(1)).toEqual({ width: 1, height: 1 }); + expect(determinePowerOfTwoDimensions(2)).toEqual({ width: 2, height: 1 }); + expect(determinePowerOfTwoDimensions(3)).toEqual({ width: 2, height: 2 }); + expect(determinePowerOfTwoDimensions(1025)).toEqual({ width: 64, height: 32 }); + expect(determinePowerOfTwoDimensions(2048)).toEqual({ width: 64, height: 32 }); + expect(determinePowerOfTwoDimensions(2049)).toEqual({ width: 64, height: 64 }); + expect(determinePowerOfTwoDimensions(4096 * 4096 - 1)).toEqual({ width: 4096, height: 4096 }); }); diff --git a/viewer/src/utils/determinePowerOfTwoDimensions.ts b/viewer/src/utils/determinePowerOfTwoDimensions.ts index e4404ff4e6d..5822f09d7da 100644 --- a/viewer/src/utils/determinePowerOfTwoDimensions.ts +++ b/viewer/src/utils/determinePowerOfTwoDimensions.ts @@ -6,7 +6,7 @@ * Computes minimal power-of-two width and height that holds at least the number of elements provided. * This is useful to compute texture sizes. */ -export function determineTextureDimensions(elementCount: number): { width: number; height: number } { +export function determinePowerOfTwoDimensions(elementCount: number): { width: number; height: number } { const width = Math.max(1, ceilToPowerOfTwo(Math.sqrt(elementCount))); const height = Math.max(1, ceilToPowerOfTwo(elementCount / width)); return { width, height }; From 99a4592c9d521384a25c96eb37037ac6fae9c85a Mon Sep 17 00:00:00 2001 From: Lars Moastuen Date: Sat, 21 Mar 2020 18:09:59 +0100 Subject: [PATCH 3/7] Allocate textures of correct size for holding color and visibility. --- viewer/src/views/threejs/cad/CadNode.ts | 3 ++- .../src/views/threejs/cad/MaterialManager.ts | 10 +++------- viewer/src/views/threejs/cad/materials.ts | 20 +++++++++---------- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/viewer/src/views/threejs/cad/CadNode.ts b/viewer/src/views/threejs/cad/CadNode.ts index 31685cca006..be3f15c0756 100644 --- a/viewer/src/views/threejs/cad/CadNode.ts +++ b/viewer/src/views/threejs/cad/CadNode.ts @@ -64,7 +64,8 @@ export class CadNode extends THREE.Object3D { super(); this.type = 'CadNode'; this.name = 'Sector model'; - this._materialManager = new MaterialManager(options ? options.nodeAppearance : undefined); + const treeIndexCount = model.scene.maxTreeIndex + 1; + this._materialManager = new MaterialManager(treeIndexCount, options ? options.nodeAppearance : undefined); const rootSector = new RootSectorNode(model, this._materialManager.materials); this._repository = new CachedRepository(model); diff --git a/viewer/src/views/threejs/cad/MaterialManager.ts b/viewer/src/views/threejs/cad/MaterialManager.ts index 9dba1b822c5..bcce781e91d 100644 --- a/viewer/src/views/threejs/cad/MaterialManager.ts +++ b/viewer/src/views/threejs/cad/MaterialManager.ts @@ -3,11 +3,7 @@ */ import { createMaterials, Materials } from './materials'; -import { - VisibilityDelegate, - ColorDelegate, - NodeAppearance -} from '../../common/cad/NodeAppearance'; +import { VisibilityDelegate, ColorDelegate, NodeAppearance } from '../../common/cad/NodeAppearance'; function updateColors(getColor: ColorDelegate, materials: Materials, treeIndices: number[]) { for (const treeIndex of treeIndices) { @@ -29,8 +25,8 @@ export class MaterialManager { public readonly materials: Materials; private readonly _options?: NodeAppearance; - constructor(options?: NodeAppearance) { - this.materials = createMaterials(); + constructor(treeIndexCount: number, options?: NodeAppearance) { + this.materials = createMaterials(treeIndexCount); this._options = options; } diff --git a/viewer/src/views/threejs/cad/materials.ts b/viewer/src/views/threejs/cad/materials.ts index e55522d0fbb..437113f252f 100644 --- a/viewer/src/views/threejs/cad/materials.ts +++ b/viewer/src/views/threejs/cad/materials.ts @@ -5,6 +5,7 @@ import * as THREE from 'three'; import { sectorShaders, shaderDefines } from './shaders'; import { RenderMode } from '../materials'; +import { determinePowerOfTwoDimensions } from '../../../utils/determinePowerOfTwoDimensions'; export interface Materials { // Materials @@ -28,18 +29,15 @@ export interface Materials { overrideVisibilityPerTreeIndex: THREE.DataTexture; } -export function createMaterials(): Materials { - const pixelCount = 2048; - const colorCount = pixelCount * pixelCount; - const visibilityCount = pixelCount * pixelCount; +export function createMaterials(treeIndexCount: number): Materials { + const textureDims = determinePowerOfTwoDimensions(treeIndexCount); + const textureElementCount = textureDims.width * textureDims.height; - const colors = new Uint8Array(4 * colorCount); - const visibility = new Uint8Array(4 * visibilityCount); - for (let i = 0; i < 4 * visibilityCount; i++) { - visibility[i] = 255; - } - const overrideColorPerTreeIndex = new THREE.DataTexture(colors, pixelCount, pixelCount); - const overrideVisibilityPerTreeIndex = new THREE.DataTexture(visibility, pixelCount, pixelCount); + const colors = new Uint8Array(4 * textureElementCount); + const visibility = new Uint8Array(4 * textureElementCount); + visibility.fill(255); + const overrideColorPerTreeIndex = new THREE.DataTexture(colors, textureDims.width, textureDims.height); + const overrideVisibilityPerTreeIndex = new THREE.DataTexture(visibility, textureDims.width, textureDims.height); const boxMaterial = new THREE.ShaderMaterial({ name: 'Primitives (Box)', From 07f265316caae6233986eb3e7dba9708a324c203 Mon Sep 17 00:00:00 2001 From: Lars Moastuen Date: Sat, 21 Mar 2020 18:21:58 +0100 Subject: [PATCH 4/7] Add test for createMaterials() --- .../views/threejs/sector/materials.test.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 viewer/src/__tests__/views/threejs/sector/materials.test.ts diff --git a/viewer/src/__tests__/views/threejs/sector/materials.test.ts b/viewer/src/__tests__/views/threejs/sector/materials.test.ts new file mode 100644 index 00000000000..0f61bb2769a --- /dev/null +++ b/viewer/src/__tests__/views/threejs/sector/materials.test.ts @@ -0,0 +1,14 @@ +/*! + * Copyright 2020 Cognite AS + */ + +import { createMaterials } from '../../../../views/threejs/cad/materials'; + +describe('createMaterials', () => { + test('Positive treeIndexCount, creates materials', () => { + const materials = createMaterials(32); + for (const entry of Object.entries(materials)) { + expect(entry[1]).not.toBeNull(); + } + }); +}); From 386198766a64924d8f7b5949d32809dff3ed3522 Mon Sep 17 00:00:00 2001 From: Lars Moastuen Date: Sat, 21 Mar 2020 19:54:28 +0100 Subject: [PATCH 5/7] Fix broken test. --- viewer/src/__tests__/views/threejs/sector/primitives.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/viewer/src/__tests__/views/threejs/sector/primitives.test.ts b/viewer/src/__tests__/views/threejs/sector/primitives.test.ts index 33378e9c7f5..18770d5bc35 100644 --- a/viewer/src/__tests__/views/threejs/sector/primitives.test.ts +++ b/viewer/src/__tests__/views/threejs/sector/primitives.test.ts @@ -8,9 +8,8 @@ import { PrimitiveAttributes } from '../../../../workers/types/parser.types'; import { createEmptySector } from '../../../models/cad/emptySector'; import { createMaterials } from '../../../../views/threejs/cad/materials'; -const materials = createMaterials(); - describe('createPrimitives', () => { + const materials = createMaterials(64); let emptySector: Sector; beforeEach(() => { From 14436e0a24be9584eb8a451cf7e93283bd82b6db Mon Sep 17 00:00:00 2001 From: Lars Moastuen Date: Mon, 23 Mar 2020 14:08:01 +0100 Subject: [PATCH 6/7] Fix broken test --- .../__tests__/views/threejs/sector/consumeSectorSimple.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/viewer/src/__tests__/views/threejs/sector/consumeSectorSimple.test.ts b/viewer/src/__tests__/views/threejs/sector/consumeSectorSimple.test.ts index 7ad4fbfc726..fe1bd04c40c 100644 --- a/viewer/src/__tests__/views/threejs/sector/consumeSectorSimple.test.ts +++ b/viewer/src/__tests__/views/threejs/sector/consumeSectorSimple.test.ts @@ -9,7 +9,7 @@ import { consumeSectorSimple } from '../../../../views/threejs/cad/consumeSector import { createMaterials } from '../../../../views/threejs/cad/materials'; import 'jest-extended'; -const materials = createMaterials(); +const materials = createMaterials(64); describe('consumeSectorDetailed', () => { const metadata: SectorMetadata = { From b5caea26359f54f0b156607c28f6ced19b9b86f0 Mon Sep 17 00:00:00 2001 From: Lars Moastuen Date: Thu, 26 Mar 2020 01:22:01 +0100 Subject: [PATCH 7/7] Fix broken test. --- .../views/threejs/sector/consumeSectorDetailed.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/viewer/src/__tests__/views/threejs/sector/consumeSectorDetailed.test.ts b/viewer/src/__tests__/views/threejs/sector/consumeSectorDetailed.test.ts index c356d399579..4d5552ec669 100644 --- a/viewer/src/__tests__/views/threejs/sector/consumeSectorDetailed.test.ts +++ b/viewer/src/__tests__/views/threejs/sector/consumeSectorDetailed.test.ts @@ -10,7 +10,7 @@ import { createEmptySector } from '../../../models/cad/emptySector'; import { createMaterials } from '../../../../views/threejs/cad/materials'; import 'jest-extended'; -const materials = createMaterials(); +const materials = createMaterials(10); describe('consumeSectorDetailed', () => { const metadata: SectorMetadata = {