Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-add support for selecting texture based on engine capabilities #7786

Merged
merged 3 commits into from
Mar 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dist/preview release/what's new.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
### Engine

- Allow logging of shader code when a compilation error occurs ([Popov72](https://github.com/Popov72))
- Add back support for selecting textures based on engine capabilities ([bghgary](https://github.com/bghgary))

### Cameras

Expand Down
26 changes: 15 additions & 11 deletions src/Engines/Extensions/engine.cubeTexture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ declare module "../../Engines/thinEngine" {

/**
* Creates a cube texture
* @param rootUrl defines the url where the files to load is located
* @param url defines the url where the files to load is located
* @param scene defines the current scene
* @param files defines the list of files to load (1 per face)
* @param noMipmap defines a boolean indicating that no mipmaps shall be generated (false by default)
Expand All @@ -35,13 +35,13 @@ declare module "../../Engines/thinEngine" {
* @param fallback defines texture to use while falling back when (compressed) texture file not found.
* @returns the cube texture as an InternalTexture
*/
createCubeTexture(rootUrl: string, scene: Nullable<Scene>, files: Nullable<string[]>, noMipmap: boolean | undefined,
createCubeTexture(url: string, scene: Nullable<Scene>, files: Nullable<string[]>, noMipmap: boolean | undefined,
onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>,
format: number | undefined, forcedExtension: any, createPolynomials: boolean, lodScale: number, lodOffset: number, fallback: Nullable<InternalTexture>): InternalTexture;

/**
* Creates a cube texture
* @param rootUrl defines the url where the files to load is located
* @param url defines the url where the files to load is located
* @param scene defines the current scene
* @param files defines the list of files to load (1 per face)
* @param noMipmap defines a boolean indicating that no mipmaps shall be generated (false by default)
Expand All @@ -51,13 +51,13 @@ declare module "../../Engines/thinEngine" {
* @param forcedExtension defines the extension to use to pick the right loader
* @returns the cube texture as an InternalTexture
*/
createCubeTexture(rootUrl: string, scene: Nullable<Scene>, files: Nullable<string[]>, noMipmap: boolean,
createCubeTexture(url: string, scene: Nullable<Scene>, files: Nullable<string[]>, noMipmap: boolean,
onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>,
format: number | undefined, forcedExtension: any): InternalTexture;

/**
* Creates a cube texture
* @param rootUrl defines the url where the files to load is located
* @param url defines the url where the files to load is located
* @param scene defines the current scene
* @param files defines the list of files to load (1 per face)
* @param noMipmap defines a boolean indicating that no mipmaps shall be generated (false by default)
Expand All @@ -70,7 +70,7 @@ declare module "../../Engines/thinEngine" {
* @param lodOffset defines the offset applied to environment texture. This manages first LOD level used for IBL according to the roughness
* @returns the cube texture as an InternalTexture
*/
createCubeTexture(rootUrl: string, scene: Nullable<Scene>, files: Nullable<string[]>, noMipmap: boolean,
createCubeTexture(url: string, scene: Nullable<Scene>, files: Nullable<string[]>, noMipmap: boolean,
onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>,
format: number | undefined, forcedExtension: any, createPolynomials: boolean, lodScale: number, lodOffset: number): InternalTexture;

Expand Down Expand Up @@ -215,12 +215,12 @@ ThinEngine.prototype._setCubeMapTextureParams = function(loadMipmap: boolean): v
this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
};

ThinEngine.prototype.createCubeTexture = function(rootUrl: string, scene: Nullable<Scene>, files: Nullable<string[]>, noMipmap?: boolean, onLoad: Nullable<(data?: any) => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null, format?: number, forcedExtension: any = null, createPolynomials: boolean = false, lodScale: number = 0, lodOffset: number = 0, fallback: Nullable<InternalTexture> = null): InternalTexture {
ThinEngine.prototype.createCubeTexture = function(url: string, scene: Nullable<Scene>, files: Nullable<string[]>, noMipmap?: boolean, onLoad: Nullable<(data?: any) => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null, format?: number, forcedExtension: any = null, createPolynomials: boolean = false, lodScale: number = 0, lodOffset: number = 0, fallback: Nullable<InternalTexture> = null): InternalTexture {
var gl = this._gl;

var texture = fallback ? fallback : new InternalTexture(this, InternalTextureSource.Cube);
texture.isCube = true;
texture.url = rootUrl;
texture.url = url;
texture.generateMipMaps = !noMipmap;
texture._lodGenerationScale = lodScale;
texture._lodGenerationOffset = lodOffset;
Expand All @@ -230,8 +230,12 @@ ThinEngine.prototype.createCubeTexture = function(rootUrl: string, scene: Nullab
texture._files = files;
}

var lastDot = rootUrl.lastIndexOf('.');
var extension = forcedExtension ? forcedExtension : (lastDot > -1 ? rootUrl.substring(lastDot).toLowerCase() : "");
if (this._transformTextureUrl) {
url = this._transformTextureUrl(url);
}

var lastDot = url.lastIndexOf('.');
var extension = forcedExtension ? forcedExtension : (lastDot > -1 ? url.substring(lastDot).toLowerCase() : "");

let loader: Nullable<IInternalTextureLoader> = null;
for (let availableLoader of ThinEngine._TextureLoaders) {
Expand Down Expand Up @@ -265,7 +269,7 @@ ThinEngine.prototype.createCubeTexture = function(rootUrl: string, scene: Nullab
}
}
else {
this._loadFile(rootUrl, (data) => onloaddata(new Uint8Array(data as ArrayBuffer)), undefined, undefined, true, onInternalError);
this._loadFile(url, (data) => onloaddata(new Uint8Array(data as ArrayBuffer)), undefined, undefined, true, onInternalError);
}
}
else {
Expand Down
114 changes: 114 additions & 0 deletions src/Engines/Extensions/engine.textureSelector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { Nullable } from '../../types';
import { Engine } from '../engine';

declare module "../../Engines/engine" {
export interface Engine {
/** @hidden */
_excludedCompressedTextures: string[];

/** @hidden */
_textureFormatInUse: string;

/**
* Gets the list of texture formats supported
*/
readonly texturesSupported: Array<string>;

/**
* Gets the texture format in use
*/
readonly textureFormatInUse: Nullable<string>;

/**
* Set the compressed texture extensions or file names to skip.
*
* @param skippedFiles defines the list of those texture files you want to skip
* Example: [".dds", ".env", "myfile.png"]
*/
setCompressedTextureExclusions(skippedFiles: Array<string>): void;

/**
* Set the compressed texture format to use, based on the formats you have, and the formats
* supported by the hardware / browser.
*
* Khronos Texture Container (.ktx) files are used to support this. This format has the
* advantage of being specifically designed for OpenGL. Header elements directly correspond
* to API arguments needed to compressed textures. This puts the burden on the container
* generator to house the arcane code for determining these for current & future formats.
*
* for description see https://www.khronos.org/opengles/sdk/tools/KTX/
* for file layout see https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
*
* Note: The result of this call is not taken into account when a texture is base64.
*
* @param formatsAvailable defines the list of those format families you have created
* on your server. Syntax: '-' + format family + '.ktx'. (Case and order do not matter.)
*
* Current families are astc, dxt, pvrtc, etc2, & etc1.
* @returns The extension selected.
*/
setTextureFormatToUse(formatsAvailable: Array<string>): Nullable<string>;
}
}

function transformTextureUrl(this: Engine, url: string): string {
const excludeFn = (entry: string) => {
const strRegExPattern: string = '\\b' + entry + '\\b';
return (url && (url === entry || url.match(new RegExp(strRegExPattern, 'g'))));
};

if (this._excludedCompressedTextures && this._excludedCompressedTextures.some(excludeFn)) {
return url;
}

const lastDot = url.lastIndexOf('.');
return (lastDot > -1 ? url.substring(0, lastDot) : url) + this._textureFormatInUse;
}

Object.defineProperty(Engine.prototype, "texturesSupported", {
get: function(this: Engine) {
// Intelligently add supported compressed formats in order to check for.
// Check for ASTC support first as it is most powerful and to be very cross platform.
// Next PVRTC & DXT, which are probably superior to ETC1/2.
// Likely no hardware which supports both PVR & DXT, so order matters little.
// ETC2 is newer and handles ETC1 (no alpha capability), so check for first.
const texturesSupported = new Array<string>();
if (this._caps.astc) { texturesSupported.push('-astc.ktx'); }
if (this._caps.s3tc) { texturesSupported.push('-dxt.ktx'); }
if (this._caps.pvrtc) { texturesSupported.push('-pvrtc.ktx'); }
if (this._caps.etc2) { texturesSupported.push('-etc2.ktx'); }
if (this._caps.etc1) { texturesSupported.push('-etc1.ktx'); }
return texturesSupported;
},
enumerable: true,
configurable: true
});

Object.defineProperty(Engine.prototype, "textureFormatInUse", {
get: function(this: Engine) {
return this._textureFormatInUse || null;
},
enumerable: true,
configurable: true
});

Engine.prototype.setCompressedTextureExclusions = function(skippedFiles: Array<string>): void {
this._excludedCompressedTextures = skippedFiles;
};

Engine.prototype.setTextureFormatToUse = function(formatsAvailable: Array<string>): Nullable<string> {
const texturesSupported = this.texturesSupported;
for (let i = 0, len1 = texturesSupported.length; i < len1; i++) {
for (let j = 0, len2 = formatsAvailable.length; j < len2; j++) {
if (texturesSupported[i] === formatsAvailable[j].toLowerCase()) {
this._transformTextureUrl = transformTextureUrl.bind(this);
return this._textureFormatInUse = texturesSupported[i];
}
}
}
// actively set format to nothing, to allow this to be called more than once
// and possibly fail the 2nd time
delete this._textureFormatInUse;
delete this._transformTextureUrl;
return null;
};
3 changes: 2 additions & 1 deletion src/Engines/Extensions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export * from "./engine.renderTarget";
export * from "./engine.renderTargetCube";
export * from "./engine.webVR";
export * from "./engine.uniformBuffer";
export * from "./engine.views";
export * from "./engine.views";
export * from "./engine.textureSelector";
36 changes: 6 additions & 30 deletions src/Engines/thinEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,11 +380,8 @@ export class ThinEngine {

private _activeRequests = new Array<IFileRequest>();

// Hardware supported Compressed Textures
protected _texturesSupported = new Array<string>();

/** @hidden */
public _textureFormatInUse: Nullable<string>;
public _transformTextureUrl: (url: string) => string;

protected get _supportsHardwareTextureRescaling() {
return false;
Expand All @@ -401,20 +398,6 @@ export class ThinEngine {
this._framebufferDimensionsObject = dimensions;
}

/**
* Gets the list of texture formats supported
*/
public get texturesSupported(): Array<string> {
return this._texturesSupported;
}

/**
* Gets the list of texture formats in use
*/
public get textureFormatInUse(): Nullable<string> {
return this._textureFormatInUse;
}

/**
* Gets the current viewport
*/
Expand Down Expand Up @@ -730,7 +713,7 @@ export class ThinEngine {
}
}

private _initGLContext(): void {
protected _initGLContext(): void {
// Caps
this._caps = {
maxTexturesImageUnits: this._gl.getParameter(this._gl.MAX_TEXTURE_IMAGE_UNITS),
Expand Down Expand Up @@ -886,17 +869,6 @@ export class ThinEngine {
}
}

// Intelligently add supported compressed formats in order to check for.
// Check for ASTC support first as it is most powerful and to be very cross platform.
// Next PVRTC & DXT, which are probably superior to ETC1/2.
// Likely no hardware which supports both PVR & DXT, so order matters little.
// ETC2 is newer and handles ETC1 (no alpha capability), so check for first.
if (this._caps.astc) { this.texturesSupported.push('-astc.ktx'); }
if (this._caps.s3tc) { this.texturesSupported.push('-dxt.ktx'); }
if (this._caps.pvrtc) { this.texturesSupported.push('-pvrtc.ktx'); }
if (this._caps.etc2) { this.texturesSupported.push('-etc2.ktx'); }
if (this._caps.etc1) { this.texturesSupported.push('-etc1.ktx'); }

if (this._gl.getShaderPrecisionFormat) {
var vertex_highp = this._gl.getShaderPrecisionFormat(this._gl.VERTEX_SHADER, this._gl.HIGH_FLOAT);
var fragment_highp = this._gl.getShaderPrecisionFormat(this._gl.FRAGMENT_SHADER, this._gl.HIGH_FLOAT);
Expand Down Expand Up @@ -2796,6 +2768,10 @@ export class ThinEngine {

let texture = fallback ? fallback : new InternalTexture(this, InternalTextureSource.Url);

if (this._transformTextureUrl) {
url = this._transformTextureUrl(url);
}

// establish the file extension, if possible
var lastDot = url.lastIndexOf('.');
var extension = forcedExtension ? forcedExtension : (lastDot > -1 ? url.substring(lastDot).toLowerCase() : "");
Expand Down