Files
Kepler/engine/sampler2D.js

307 lines
6.2 KiB
JavaScript
Executable File

/*
* Copyright 2013-2019, kaj dijkstra,
* Author, Kaj Dijkstra.
* All rights reserved.
*
*/
import texture from "./texture.js";
import { math } from "./math.js";
/**
* Sampler2D
**/
class sampler2D {
constructor ( ) {
this.texture = null;
this.id = ++kepler.samplerId;
this.alpha = 1.0;
this.binded = false;
this.anisotropic = false;
// If true, do not generate mipmaps (reduces normal-map shimmer)
this.isNormalMap = false;
}
setViewport ( viewport ) {
this.viewport = viewport;
this.gl = viewport.gl;
this.FLIP_Y = true;
this.filter = this.gl.LINEAR;
this.MIN_FILTER = this.gl.LINEAR;
this.MAG_FILTER = this.gl.LINEAR;
this.WRAP_S = this.gl.REPEAT;
this.WRAP_T = this.gl.REPEAT;
this.datatype = this.gl.RGBA;
this.format = this.gl.RGBA;
this.internalFormat = this.gl.RGBA;
this.target = this.gl.TEXTURE_2D;
this.type = this.gl.FLOAT;
}
setTarget ( viewport ) {
var textureObject = new texture();
textureObject.setViewport( viewport );
this.addTexture( textureObject );
}
getTexture ( face ) {
return this.texture;
}
getTextures ( ) {
return [ this.texture ];
}
addTexture ( textureObject ) {
this.texture = textureObject;
}
setType ( type ) {
// this.texture.type = type;
}
setAsNormalMap ( ) {
this.isNormalMap = true;
}
/**
* bind sampler to shader
* @param {(shader)} shader
**/
bind ( shader ) {
var txt = this.texture;
var data = this.texture.data;
var type = txt.dataType;
var gl = this.gl;
if ( type == "framebuffer" ) {
// Preserve your original behaviour:
// framebuffer "data" is already a GL texture handle
this.texture.glTexture = this.texture.data;
// Continue as if it is a normal GL texture object (no texImage2D)
// NOTE: do NOT change filtering/mips logic here beyond safe defaults.
} else {
var width = txt.width;
var height = txt.height;
// Always assign sampler slot like you did
this.id = 1 + shader.samplerId++;
gl.activeTexture( gl.TEXTURE0 + this.id );
gl.enable( this.gl.BLEND );
gl.bindTexture( gl.TEXTURE_2D, txt.glTexture );
gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, this.FLIP_Y );
// serialize txt data type
switch ( type ) {
case "float":
{
this.type = this.gl.FLOAT;
// WebGL2 prefers sized internal formats for float textures.
// Keep fallback to gl.RGBA for compatibility if RGBA32F is not present.
var internal = gl.RGBA32F ? gl.RGBA32F : gl.RGBA;
gl.texImage2D(
gl.TEXTURE_2D,
0,
internal,
width,
height,
0,
this.internalFormat,
this.type,
data
);
}
break;
case "int":
{
this.type = this.gl.UNSIGNED_BYTE;
gl.texImage2D(
gl.TEXTURE_2D,
0,
this.format,
width,
height,
0,
this.internalFormat,
this.type,
data
);
}
break;
case "depth":
{
gl.texImage2D(
gl.TEXTURE_2D,
0,
this.gl.DEPTH_COMPONENT,
width,
height,
0,
this.gl.DEPTH_COMPONENT,
this.gl.UNSIGNED_SHORT,
null
);
}
break;
case "image":
{
this.type = this.gl.UNSIGNED_BYTE;
// Keep your original call shape (works because format == internalFormat == RGBA)
gl.texImage2D(
gl.TEXTURE_2D,
0,
this.format,
this.internalFormat,
this.type,
data
);
}
break;
case "canvas":
{
gl.texImage2D(
gl.TEXTURE_2D,
0,
this.format,
width,
height,
0,
this.internalFormat,
this.UNSIGNED_BYTE,
data
);
}
break;
case "COMPRESSED_RGBA":
{
var mipmaps = txt.mipmaps;
for ( var i = 0; i < mipmaps.length; i++ ) {
var mipmap = mipmaps[i];
gl.compressedTexImage2D(
gl.TEXTURE_2D,
i,
mipmap.internalFormat,
mipmap.width,
mipmap.height,
0,
mipmap.byteArray
);
}
}
break;
}
// Anisotropy kept as in your original (disabled)
if ( this.anisotropic ) {
// var extension = kepler.extensions.anisotropic;
// gl.texParameteri( this.gl.TEXTURE_2D, extension.TEXTURE_MAX_ANISOTROPY_EXT, this.anisotropic );
}
// POT logic (this is where we apply the safe normal-map fix)
if ( math.isPowerOfTwo( width ) && math.isPowerOfTwo( height ) ) {
gl.texParameteri( gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR );
if ( this.isNormalMap ) {
// Safe fix: do not use mipmaps for normal maps
gl.texParameteri( gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR );
} else {
gl.texParameteri( gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR_MIPMAP_LINEAR );
if ( this.type != this.gl.FLOAT ) {
gl.generateMipmap( gl.TEXTURE_2D );
}
}
gl.texParameteri( gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.WRAP_S );
gl.texParameteri( gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.WRAP_T );
} else {
gl.texParameteri( gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR );
gl.texParameteri( gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE );
gl.texParameteri( gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE );
}
gl.bindTexture( gl.TEXTURE_2D, null );
this.binded = true;
return;
}
// framebuffer path: bind the already-created GL texture handle
// and apply basic sampling state without re-upload.
{
var width_fb = txt.width;
var height_fb = txt.height;
this.id = 1 + shader.samplerId++;
gl.activeTexture( gl.TEXTURE0 + this.id );
gl.bindTexture( gl.TEXTURE_2D, txt.glTexture );
// Clamp + linear for attachments (safe default)
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
gl.bindTexture( gl.TEXTURE_2D, null );
this.binded = true;
}
}
}
export { sampler2D as default };