Changed normal of the ground to GL, Now the normal mapping is correct.

This commit is contained in:
2026-01-04 16:11:18 +01:00
parent 296aad3f84
commit b417f016af
20 changed files with 985 additions and 254 deletions

View File

@@ -5,21 +5,21 @@
*
*/
import scene from './scene.js';
import scene from "./scene.js";
import shader from './shader.js';
import shader from "./shader.js";
import mesh from './mesh.js';
import mesh from "./mesh.js";
import material from './material.js';
import material from "./material.js";
import sampler2D from './sampler2D.js';
import sampler2D from "./sampler2D.js";
import entity from './entity.js';
import entity from "./entity.js";
import {matrix3, matrix4} from './math.js';
import {matrix3, matrix4} from "./math.js";
import boundingBox from './boundingBox.js';
import boundingBox from "./boundingBox.js";
/**
* Player object
@@ -153,6 +153,14 @@ class assimp
this.scene = scene;
this.currentModelFile = jsonFilename;
if( typeof kepler !== "undefined" && kepler && kepler.resources ) {
kepler.resources.updateFilePreloaderProgress( this.name, 0, 0, false );
}
var data = this.loadTextFileSynchronous( this.baseUrl + this.modelPath + jsonFilename );
this.processFiles(data);
@@ -217,10 +225,14 @@ class assimp
request.onprogress = callback = function (evt) {
console.log("onupdate", evt);
//kepler.loader.update( evt.target.responseURL, evt.loaded, evt.total );
if( typeof kepler !== "undefined" && kepler && kepler.resources && evt && evt.target && evt.target.assimp ) {
var label = evt.target.assimp.name;
kepler.resources.updateFilePreloaderProgress( label, evt.loaded, evt.total, false );
}
};
@@ -231,6 +243,22 @@ class assimp
//console.log(request.responseText);
this.assimp.filesToLoad.push({data: request.responseText, filename: request.responseURL });
//'this.assimp.filesToLoad.push(evt.responseText);
if( typeof kepler !== "undefined" && kepler && kepler.resources && this.assimp ) {
var label = this.assimp.name;
var totalBytes = 0;
if( request.responseText ) {
totalBytes = request.responseText.length;
}
kepler.resources.updateFilePreloaderProgress( label, totalBytes, totalBytes, true );
}
@@ -792,4 +820,4 @@ console.log('diffuseAdress', diffuseAdress);
}
}
export {assimp as default};
export {assimp as default};

View File

@@ -81,6 +81,8 @@
this.assimpLoader = new assimp();
this.preloaderCleared = false;
}
addViewport(viewport){
@@ -142,9 +144,18 @@
*/
tick() {
if( this.preloaderCleared === false && this.resources ) {
this.resources.hidePreloader( );
this.preloaderCleared = true;
}
requestAnimFrame( this.tick.bind( this ) );
this.render();
}
/**

View File

@@ -6,10 +6,11 @@
* Author: Kaj Dijksta
*
**/
import programInfo from './programInfo.js';
import {vector2, vector3} from './math.js';
import programInfo from "./programInfo.js";
import {vector2, vector3} from "./math.js";
@@ -17,28 +18,53 @@ import {vector2, vector3} from './math.js';
* ResourceManager object
*/
class resourceManager{
constructor( engine ) {
this.engine = engine;
this.gl = engine.gl;
this.baseUrl = "";
this.directory = "media/textures/";
this.numberOfModels = 0;
this.numberOfTextures = 0;
this.textureTotal = 0;
this.textures = [];
this.models = [];
this.shaders = [];
this.finishCallback;
this.loadingList = [];
this.fileCache = [];
this.programCashe = [];
this.engine = engine;
this.gl = engine.gl;
this.baseUrl = "";
this.directory = "media/textures/";
this.numberOfModels = 0;
this.numberOfTextures = 0;
this.textureTotal = 0;
this.textures = new Array();
this.models = new Array();
this.shaders = new Array();
this.finishCallback = null;
this.loadingList = new Array();
this.fileCache = new Array();
this.programCashe = new Array();
this.preloaderElement = null;
this.preloaderTextElement = null;
this.preloaderBarElement = null;
this.preloaderVisible = false;
this.fileLoading = false;
this.fileLoadedBytes = 0;
this.fileTotalBytes = 0;
this.fileLabel = "";
}
@@ -320,6 +346,294 @@ import {vector2, vector3} from './math.js';
}
formatBytesToMegabytes( bytes ) {
if( bytes <= 0 ) {
return "0.00 MB";
}
var megabytes = bytes / ( 1024 * 1024 );
var rounded = Math.round( megabytes * 100 ) / 100;
var text = rounded.toFixed( 2 ) + " MB";
return text;
}
createPreloaderElements( ) {
if( this.preloaderElement !== null ) {
return;
}
var canvasElement = document.getElementById( "keplerEngine" );
var holder = document.createElement( "div" );
holder.className = "kepler-preloader-holder";
holder.style.position = "absolute";
holder.style.left = "0";
holder.style.top = "0";
holder.style.right = "0";
holder.style.bottom = "0";
holder.style.display = "flex";
holder.style.alignItems = "center";
holder.style.justifyContent = "center";
holder.style.backgroundColor = "rgba(0, 0, 0, 0.6)";
var inner = document.createElement( "div" );
inner.className = "kepler-preloader-inner";
inner.style.minWidth = "200px";
inner.style.maxWidth = "400px";
inner.style.width = "60%";
inner.style.backgroundColor = "#202020";
inner.style.padding = "16px";
inner.style.boxSizing = "border-box";
inner.style.borderRadius = "4px";
var textElement = document.createElement( "div" );
textElement.className = "kepler-preloader-text";
textElement.style.color = "#ffffff";
textElement.style.fontFamily = "sans-serif";
textElement.style.fontSize = "14px";
textElement.style.marginBottom = "8px";
textElement.textContent = "Loading textures...";
var barOuter = document.createElement( "div" );
barOuter.className = "kepler-preloader-bar";
barOuter.style.width = "100%";
barOuter.style.height = "6px";
barOuter.style.backgroundColor = "#444444";
barOuter.style.overflow = "hidden";
var barInner = document.createElement( "div" );
barInner.className = "kepler-preloader-bar-inner";
barInner.style.width = "0%";
barInner.style.height = "100%";
barInner.style.backgroundColor = "#66ccff";
barOuter.appendChild( barInner );
inner.appendChild( textElement );
inner.appendChild( barOuter );
holder.appendChild( inner );
this.preloaderElement = holder;
this.preloaderTextElement = textElement;
this.preloaderBarElement = barInner;
if( canvasElement !== null && canvasElement.parentNode !== null ) {
canvasElement.parentNode.style.position = "relative";
canvasElement.parentNode.appendChild( holder );
} else {
document.body.appendChild( holder );
}
}
showPreloaderIfNeeded( ) {
if( this.preloaderVisible ) {
return;
}
this.createPreloaderElements( );
if( this.preloaderElement !== null ) {
this.preloaderElement.style.display = "flex";
}
this.preloaderVisible = true;
}
updatePreloaderProgress( ) {
if( this.preloaderElement === null ) {
return;
}
if( this.textureTotal <= 0 ) {
if( this.fileTotalBytes <= 0 ) {
return;
}
}
var loaded = this.textureTotal - this.numberOfTextures;
if( loaded < 0 ) {
loaded = 0;
}
if( loaded > this.textureTotal ) {
loaded = this.textureTotal;
}
var ratio = 0;
if( this.textureTotal > 0 ) {
ratio = loaded / this.textureTotal;
}
var percentage = Math.round( ratio * 100 );
if( this.preloaderTextElement !== null ) {
var text = "Loading textures " + loaded + " / " + this.textureTotal;
if( this.fileLoading && this.fileTotalBytes > 0 ) {
var label = this.fileLabel;
if( label === "" ) {
label = "model";
}
var loadedText = this.formatBytesToMegabytes( this.fileLoadedBytes );
var totalText = this.formatBytesToMegabytes( this.fileTotalBytes );
text = "Loading model " + label + " " + loadedText + " / " + totalText + " | " + text;
}
this.preloaderTextElement.textContent = text;
}
if( this.preloaderBarElement !== null ) {
this.preloaderBarElement.style.width = percentage + "%";
}
}
hidePreloader( ) {
if( this.preloaderElement === null ) {
return;
}
this.preloaderElement.style.display = "none";
this.preloaderVisible = false;
}
updateFilePreloaderProgress( label, loadedBytes, totalBytes, done ) {
if( done ) {
this.fileLoading = false;
this.fileLabel = "";
this.fileLoadedBytes = 0;
this.fileTotalBytes = 0;
if( this.textureTotal <= 0 ) {
this.hidePreloader( );
} else {
this.updatePreloaderProgress( );
}
} else {
this.fileLoading = true;
this.fileLabel = label;
this.fileLoadedBytes = loadedBytes;
this.fileTotalBytes = totalBytes;
this.showPreloaderIfNeeded( );
this.updatePreloaderProgress( );
}
}
/**
* Add Texture
* @param {string} address
@@ -327,20 +641,31 @@ import {vector2, vector3} from './math.js';
* @param {boolean} direct
*/
addTexture(adress, name, direct) {
this.numberOfTextures++;
this.textureTotal++;
this.showPreloaderIfNeeded( );
this.updatePreloaderProgress( );
var texture = {};
texture.adress = adress;
texture.name = name;
texture.loaded = false;
this.loadingList.push(texture);
if(direct)
this.loadNextTexture(adress, name);
this.loadingList.push( texture );
if( direct ) {
this.loadNextTexture( adress, name );
}
};
@@ -420,6 +745,13 @@ import {vector2, vector3} from './math.js';
image.onerror = function (a) {
this.engine.resources.loadNextTexture();
if( this.engine.resources.textureTotal > 0 ) {
this.engine.resources.textureTotal--;
}
this.engine.resources.CheckIfAllObjectsTexturesLoaded();
}
@@ -450,28 +782,48 @@ import {vector2, vector3} from './math.js';
*/
getTexture( name ) {
if(this.hasExtension(name)) {
var url = name;
this.addTexture(this.directory + url, name);
}
for(var c = 0;c<this.loadingList.length; c++) {
if(this.loadingList[c].name == name){
return this.loadingList[c];
var index;
for( index = 0; index < this.textures.length; index++ ) {
if( this.textures[ index ].name === name ) {
return this.textures[ index ];
}
}
for( index = 0; index < this.loadingList.length; index++ ) {
if( this.loadingList[ index ].name === name ) {
return this.loadingList[ index ];
}
}
if( this.hasExtension( name ) ) {
var url = name;
this.addTexture( this.directory + url, name );
}
for( index = 0; index < this.loadingList.length; index++ ) {
if( this.loadingList[ index ].name === name ) {
return this.loadingList[ index ];
}
}
return false;
};
/**
* Get texture by name
@@ -499,10 +851,17 @@ import {vector2, vector3} from './math.js';
* Check if all objects are loaded
*/
CheckIfAllObjectsTexturesLoaded() {
this.numberOfTextures--;
if( this.numberOfTextures == 0 ) {
this.updatePreloaderProgress( );
if( this.numberOfTextures === 0 ) {
this.hidePreloader( );
this.finishCallback();
}
};
@@ -735,4 +1094,3 @@ var randomImage2 = new Uint8Array ( [149,123,253,255, 126,3,96,255, 164,246,
52,83,220,255, 1,142,142,255, 32,57,79,255, 48,159,32,255,
56,232,114,255, 177,216,203,255, 69,196,217,255, 240,165,81,255,
224,56,85,255, 232,89,189,255, 143,25,202,255, 117,73,12,255] );

View File

@@ -5,186 +5,302 @@
*
*/
import texture from './texture.js';
import {math} from './math.js';
import texture from "./texture.js";
import { math } from "./math.js";
/**
* Sampler2D
**/
class sampler2D{
constructor( ){
class sampler2D {
this.texture;
this.id = ++kepler.samplerId;
this.alpha = 1.0;
this.binded = false;
this.anisotropic = false;
constructor ( ) {
}
setViewport( viewport ){
this.viewport = viewport;
this.gl = viewport.gl;
this.FLIP_Y = true;
this.filter = this.gl.NEAREST;
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;
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;
}
setTarget( viewport ) {
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();
var textureObject = new texture();
textureObject.setViewport( viewport );
this.addTexture( textureObject );
}
getTexture( face ) {
getTexture ( face ) {
return this.texture;
}
getTextures( ) {
return [this.texture];
getTextures ( ) {
return [ this.texture ];
}
addTexture( textureObject ) {
addTexture ( textureObject ) {
this.texture = textureObject;
this.texture = textureObject;
}
setType(type) {
//this.texture.type = "type";
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;
//this.type = type;
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.
if (type == "framebuffer" ) {
this.texture.glTexture = this.texture.data;
type = txt.type;
} else {
var mips = [];
var width = txt.width;
var height = txt.height;
//if(!this.binded)
this.id = 1 + shader.samplerId++;
gl.activeTexture(gl.TEXTURE0 + this.id);
var width = txt.width;
var height = txt.height;
gl.enable ( this.gl.BLEND ) ;
gl.bindTexture(gl.TEXTURE_2D, txt.glTexture );
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, this.FLIP_Y);
// 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 ) {
//serialize txt data type
switch( type ) {
case "float":
{
this.type = this.gl.FLOAT;
gl.texImage2D(gl.TEXTURE_2D, 0, kepler.extensions.EXT_sRGB , width, height, 0, this.internalFormat, this.type, data);
// 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);
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);
{
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;
gl.texImage2D(gl.TEXTURE_2D, 0, this.format, this.internalFormat,this.type, data);
// 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);
{
gl.texImage2D(
gl.TEXTURE_2D,
0,
this.format,
width,
height,
0,
this.internalFormat,
this.UNSIGNED_BYTE,
data
);
}
break;
case "COMPRESSED_RGBA":
//var textureCompression = kepler.extensions.textureCompression;
{
var mipmaps = txt.mipmaps;
var width = mipmaps[0].width;
var height = mipmaps[0].height;
for(var i = 0; i < mipmaps.length; i++) {
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);
gl.compressedTexImage2D(
gl.TEXTURE_2D,
i,
mipmap.internalFormat,
mipmap.width,
mipmap.height,
0,
mipmap.byteArray
);
}
}
break;
}
if(this.anisotropic) {
//var extension = kepler.extensions.anisotropic;
//gl.texParameteri( this.gl.TEXTURE_2D, extension.TEXTURE_MAX_ANISOTROPY_EXT, this.anisotropic );
// 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 );
}
if (math.isPowerOfTwo(width) && math.isPowerOfTwo(height) ) {
gl.texParameteri(gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR_MIPMAP_LINEAR);// mipmaps > 1 ? this.gl.LINEAR_MIPMAP_LINEAR : this.gl.LINEAR
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);
if(this.type != this.gl.FLOAT)
gl.generateMipmap(gl.TEXTURE_2D);
// 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.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);
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};
export { sampler2D as default };