master
ink-soul 2023-06-16 09:32:15 +08:00
commit 713d8be98c
94 changed files with 15739 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 KiB

View File

@ -0,0 +1,23 @@
# Blender MTL File: 'Marry.blend'
# Material Count: 2
newmtl MC003_Kozakura_Mari
Ns 900.000000
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.000000 0.000000 0.000000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 1
map_Kd MC003_Kozakura_Mari.png
newmtl 材质
Ns 900.000000
Ka 1.000000 1.000000 1.000000
Kd 0.270588 0.552941 0.874510
Ks 0.000000 0.000000 0.000000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 1

Binary file not shown.

BIN
hw0/assignment.pdf 100644

Binary file not shown.

52
hw0/index.html 100644
View File

@ -0,0 +1,52 @@
<!DOCTYPE html>
<html>
<head>
<style>
html,
body {
margin: 0;
background-color: black;
height: 100%;
width: 100%;
overflow: hidden;
}
#glcanvas {
top: 0;
width: 100%;
height: 100%;
}
</style>
<script src="lib/three.js" defer></script>
<script src="lib/OrbitControls.js" defer></script>
<script type="text/javascript" src="lib/MTLLoader.js" defer></script>
<script type="text/javascript" src="lib/OBJLoader.js" defer></script>
<script type="text/javascript" src="lib/dat.gui.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"
integrity="sha512-zhHQR0/H5SEBL3Wn6yYSaTTZej12z0hVZKOv3TwCUXT1z5qeqGcXJLLrbERYRScEDDpYIJhPC1fk31gqR783iQ=="
crossorigin="anonymous" defer> </script>
<script src="src/shaders/InternalShader.js" defer></script>
<script src="src/shaders/Shader.js" defer></script>
<script src="src/materials/Material.js" defer></script>
<script src="src/textures/Texture.js" defer></script>
<script src="src/objects/Mesh.js" defer></script>
<script src="src/loads/loadOBJ.js" defer></script>
<script src="src/loads/loadShader.js" defer></script>
<script src="src/lights/Light.js" defer></script>
<script src="src/lights/PointLight.js" defer></script>
<script src="src/renderers/WebGLRenderer.js" defer></script>
<script src="src/renderers/MeshRender.js" defer></script>
<script src="src/engine.js" defer></script>
</head>
<body>
<canvas id="glcanvas"></canvas>
</body>
</html>

View File

@ -0,0 +1,549 @@
/**
* Loads a Wavefront .mtl file specifying materials
*/
THREE.MTLLoader = function ( manager ) {
THREE.Loader.call( this, manager );
};
THREE.MTLLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype ), {
constructor: THREE.MTLLoader,
/**
* Loads and parses a MTL asset from a URL.
*
* @param {String} url - URL to the MTL file.
* @param {Function} [onLoad] - Callback invoked with the loaded object.
* @param {Function} [onProgress] - Callback for download progress.
* @param {Function} [onError] - Callback for download errors.
*
* @see setPath setResourcePath
*
* @note In order for relative texture references to resolve correctly
* you must call setResourcePath() explicitly prior to load.
*/
load: function ( url, onLoad, onProgress, onError ) {
var scope = this;
var path = ( this.path === '' ) ? THREE.LoaderUtils.extractUrlBase( url ) : this.path;
var loader = new THREE.FileLoader( this.manager );
loader.setPath( this.path );
loader.setRequestHeader( this.requestHeader );
loader.setWithCredentials( this.withCredentials );
loader.load( url, function ( text ) {
try {
onLoad( scope.parse( text, path ) );
} catch ( e ) {
if ( onError ) {
onError( e );
} else {
console.error( e );
}
scope.manager.itemError( url );
}
}, onProgress, onError );
},
setMaterialOptions: function ( value ) {
this.materialOptions = value;
return this;
},
/**
* Parses a MTL file.
*
* @param {String} text - Content of MTL file
* @return {THREE.MTLLoader.MaterialCreator}
*
* @see setPath setResourcePath
*
* @note In order for relative texture references to resolve correctly
* you must call setResourcePath() explicitly prior to parse.
*/
parse: function ( text, path ) {
var lines = text.split( '\n' );
var info = {};
var delimiter_pattern = /\s+/;
var materialsInfo = {};
for ( var i = 0; i < lines.length; i ++ ) {
var line = lines[ i ];
line = line.trim();
if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
// Blank line or comment ignore
continue;
}
var pos = line.indexOf( ' ' );
var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line;
key = key.toLowerCase();
var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : '';
value = value.trim();
if ( key === 'newmtl' ) {
// New material
info = { name: value };
materialsInfo[ value ] = info;
} else {
if ( key === 'ka' || key === 'kd' || key === 'ks' || key === 'ke' ) {
var ss = value.split( delimiter_pattern, 3 );
info[ key ] = [ parseFloat( ss[ 0 ] ), parseFloat( ss[ 1 ] ), parseFloat( ss[ 2 ] ) ];
} else {
info[ key ] = value;
}
}
}
var materialCreator = new THREE.MTLLoader.MaterialCreator( this.resourcePath || path, this.materialOptions );
materialCreator.setCrossOrigin( this.crossOrigin );
materialCreator.setManager( this.manager );
materialCreator.setMaterials( materialsInfo );
return materialCreator;
}
} );
/**
* Create a new THREE.MTLLoader.MaterialCreator
* @param baseUrl - Url relative to which textures are loaded
* @param options - Set of options on how to construct the materials
* side: Which side to apply the material
* THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide
* wrap: What type of wrapping to apply for textures
* THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping
* normalizeRGB: RGBs need to be normalized to 0-1 from 0-255
* Default: false, assumed to be already normalized
* ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's
* Default: false
* @constructor
*/
THREE.MTLLoader.MaterialCreator = function ( baseUrl, options ) {
this.baseUrl = baseUrl || '';
this.options = options;
this.materialsInfo = {};
this.materials = {};
this.materialsArray = [];
this.nameLookup = {};
this.side = ( this.options && this.options.side ) ? this.options.side : THREE.FrontSide;
this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : THREE.RepeatWrapping;
};
THREE.MTLLoader.MaterialCreator.prototype = {
constructor: THREE.MTLLoader.MaterialCreator,
crossOrigin: 'anonymous',
setCrossOrigin: function ( value ) {
this.crossOrigin = value;
return this;
},
setManager: function ( value ) {
this.manager = value;
},
setMaterials: function ( materialsInfo ) {
this.materialsInfo = this.convert( materialsInfo );
this.materials = {};
this.materialsArray = [];
this.nameLookup = {};
},
convert: function ( materialsInfo ) {
if ( ! this.options ) return materialsInfo;
var converted = {};
for ( var mn in materialsInfo ) {
// Convert materials info into normalized form based on options
var mat = materialsInfo[ mn ];
var covmat = {};
converted[ mn ] = covmat;
for ( var prop in mat ) {
var save = true;
var value = mat[ prop ];
var lprop = prop.toLowerCase();
switch ( lprop ) {
case 'kd':
case 'ka':
case 'ks':
// Diffuse color (color under white light) using RGB values
if ( this.options && this.options.normalizeRGB ) {
value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ];
}
if ( this.options && this.options.ignoreZeroRGBs ) {
if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 2 ] === 0 ) {
// ignore
save = false;
}
}
break;
default:
break;
}
if ( save ) {
covmat[ lprop ] = value;
}
}
}
return converted;
},
preload: function () {
for ( var mn in this.materialsInfo ) {
this.create( mn );
}
},
getIndex: function ( materialName ) {
return this.nameLookup[ materialName ];
},
getAsArray: function () {
var index = 0;
for ( var mn in this.materialsInfo ) {
this.materialsArray[ index ] = this.create( mn );
this.nameLookup[ mn ] = index;
index ++;
}
return this.materialsArray;
},
create: function ( materialName ) {
if ( this.materials[ materialName ] === undefined ) {
this.createMaterial_( materialName );
}
return this.materials[ materialName ];
},
createMaterial_: function ( materialName ) {
// Create material
var scope = this;
var mat = this.materialsInfo[ materialName ];
var params = {
name: materialName,
side: this.side
};
function resolveURL( baseUrl, url ) {
if ( typeof url !== 'string' || url === '' )
return '';
// Absolute URL
if ( /^https?:\/\//i.test( url ) ) return url;
return baseUrl + url;
}
function setMapForType( mapType, value ) {
if ( params[ mapType ] ) return; // Keep the first encountered texture
var texParams = scope.getTextureParams( value, params );
var map = scope.loadTexture( resolveURL( scope.baseUrl, texParams.url ) );
map.repeat.copy( texParams.scale );
map.offset.copy( texParams.offset );
map.wrapS = scope.wrap;
map.wrapT = scope.wrap;
params[ mapType ] = map;
}
for ( var prop in mat ) {
var value = mat[ prop ];
var n;
if ( value === '' ) continue;
switch ( prop.toLowerCase() ) {
// Ns is material specular exponent
case 'kd':
// Diffuse color (color under white light) using RGB values
params.color = new THREE.Color().fromArray( value );
break;
case 'ks':
// Specular color (color when light is reflected from shiny surface) using RGB values
params.specular = new THREE.Color().fromArray( value );
break;
case 'ke':
// Emissive using RGB values
params.emissive = new THREE.Color().fromArray( value );
break;
case 'map_kd':
// Diffuse texture map
setMapForType( 'map', value );
break;
case 'map_ks':
// Specular map
setMapForType( 'specularMap', value );
break;
case 'map_ke':
// Emissive map
setMapForType( 'emissiveMap', value );
break;
case 'norm':
setMapForType( 'normalMap', value );
break;
case 'map_bump':
case 'bump':
// Bump texture map
setMapForType( 'bumpMap', value );
break;
case 'map_d':
// Alpha map
setMapForType( 'alphaMap', value );
params.transparent = true;
break;
case 'ns':
// The specular exponent (defines the focus of the specular highlight)
// A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000.
params.shininess = parseFloat( value );
break;
case 'd':
n = parseFloat( value );
if ( n < 1 ) {
params.opacity = n;
params.transparent = true;
}
break;
case 'tr':
n = parseFloat( value );
if ( this.options && this.options.invertTrProperty ) n = 1 - n;
if ( n > 0 ) {
params.opacity = 1 - n;
params.transparent = true;
}
break;
default:
break;
}
}
this.materials[ materialName ] = new THREE.MeshPhongMaterial( params );
return this.materials[ materialName ];
},
getTextureParams: function ( value, matParams ) {
var texParams = {
scale: new THREE.Vector2( 1, 1 ),
offset: new THREE.Vector2( 0, 0 )
};
var items = value.split( /\s+/ );
var pos;
pos = items.indexOf( '-bm' );
if ( pos >= 0 ) {
matParams.bumpScale = parseFloat( items[ pos + 1 ] );
items.splice( pos, 2 );
}
pos = items.indexOf( '-s' );
if ( pos >= 0 ) {
texParams.scale.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) );
items.splice( pos, 4 ); // we expect 3 parameters here!
}
pos = items.indexOf( '-o' );
if ( pos >= 0 ) {
texParams.offset.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) );
items.splice( pos, 4 ); // we expect 3 parameters here!
}
texParams.url = items.join( ' ' ).trim();
return texParams;
},
loadTexture: function ( url, mapping, onLoad, onProgress, onError ) {
var texture;
var manager = ( this.manager !== undefined ) ? this.manager : THREE.DefaultLoadingManager;
var loader = manager.getHandler( url );
if ( loader === null ) {
loader = new THREE.TextureLoader( manager );
}
if ( loader.setCrossOrigin ) loader.setCrossOrigin( this.crossOrigin );
texture = loader.load( url, onLoad, onProgress, onError );
if ( mapping !== undefined ) texture.mapping = mapping;
return texture;
}
};

View File

@ -0,0 +1,900 @@
THREE.OBJLoader = ( function () {
// o object_name | g group_name
var object_pattern = /^[og]\s*(.+)?/;
// mtllib file_reference
var material_library_pattern = /^mtllib /;
// usemtl material_name
var material_use_pattern = /^usemtl /;
// usemap map_name
var map_use_pattern = /^usemap /;
var vA = new THREE.Vector3();
var vB = new THREE.Vector3();
var vC = new THREE.Vector3();
var ab = new THREE.Vector3();
var cb = new THREE.Vector3();
function ParserState() {
var state = {
objects: [],
object: {},
vertices: [],
normals: [],
colors: [],
uvs: [],
materials: {},
materialLibraries: [],
startObject: function ( name, fromDeclaration ) {
// If the current object (initial from reset) is not from a g/o declaration in the parsed
// file. We need to use it for the first parsed g/o to keep things in sync.
if ( this.object && this.object.fromDeclaration === false ) {
this.object.name = name;
this.object.fromDeclaration = ( fromDeclaration !== false );
return;
}
var previousMaterial = ( this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined );
if ( this.object && typeof this.object._finalize === 'function' ) {
this.object._finalize( true );
}
this.object = {
name: name || '',
fromDeclaration: ( fromDeclaration !== false ),
geometry: {
vertices: [],
normals: [],
colors: [],
uvs: [],
hasUVIndices: false
},
materials: [],
smooth: true,
startMaterial: function ( name, libraries ) {
var previous = this._finalize( false );
// New usemtl declaration overwrites an inherited material, except if faces were declared
// after the material, then it must be preserved for proper MultiMaterial continuation.
if ( previous && ( previous.inherited || previous.groupCount <= 0 ) ) {
this.materials.splice( previous.index, 1 );
}
var material = {
index: this.materials.length,
name: name || '',
mtllib: ( Array.isArray( libraries ) && libraries.length > 0 ? libraries[ libraries.length - 1 ] : '' ),
smooth: ( previous !== undefined ? previous.smooth : this.smooth ),
groupStart: ( previous !== undefined ? previous.groupEnd : 0 ),
groupEnd: - 1,
groupCount: - 1,
inherited: false,
clone: function ( index ) {
var cloned = {
index: ( typeof index === 'number' ? index : this.index ),
name: this.name,
mtllib: this.mtllib,
smooth: this.smooth,
groupStart: 0,
groupEnd: - 1,
groupCount: - 1,
inherited: false
};
cloned.clone = this.clone.bind( cloned );
return cloned;
}
};
this.materials.push( material );
return material;
},
currentMaterial: function () {
if ( this.materials.length > 0 ) {
return this.materials[ this.materials.length - 1 ];
}
return undefined;
},
_finalize: function ( end ) {
var lastMultiMaterial = this.currentMaterial();
if ( lastMultiMaterial && lastMultiMaterial.groupEnd === - 1 ) {
lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3;
lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart;
lastMultiMaterial.inherited = false;
}
// Ignore objects tail materials if no face declarations followed them before a new o/g started.
if ( end && this.materials.length > 1 ) {
for ( var mi = this.materials.length - 1; mi >= 0; mi -- ) {
if ( this.materials[ mi ].groupCount <= 0 ) {
this.materials.splice( mi, 1 );
}
}
}
// Guarantee at least one empty material, this makes the creation later more straight forward.
if ( end && this.materials.length === 0 ) {
this.materials.push( {
name: '',
smooth: this.smooth
} );
}
return lastMultiMaterial;
}
};
// Inherit previous objects material.
// Spec tells us that a declared material must be set to all objects until a new material is declared.
// If a usemtl declaration is encountered while this new object is being parsed, it will
// overwrite the inherited material. Exception being that there was already face declarations
// to the inherited material, then it will be preserved for proper MultiMaterial continuation.
if ( previousMaterial && previousMaterial.name && typeof previousMaterial.clone === 'function' ) {
var declared = previousMaterial.clone( 0 );
declared.inherited = true;
this.object.materials.push( declared );
}
this.objects.push( this.object );
},
finalize: function () {
if ( this.object && typeof this.object._finalize === 'function' ) {
this.object._finalize( true );
}
},
parseVertexIndex: function ( value, len ) {
var index = parseInt( value, 10 );
return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
},
parseNormalIndex: function ( value, len ) {
var index = parseInt( value, 10 );
return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
},
parseUVIndex: function ( value, len ) {
var index = parseInt( value, 10 );
return ( index >= 0 ? index - 1 : index + len / 2 ) * 2;
},
addVertex: function ( a, b, c ) {
var src = this.vertices;
var dst = this.object.geometry.vertices;
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
},
addVertexPoint: function ( a ) {
var src = this.vertices;
var dst = this.object.geometry.vertices;
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
},
addVertexLine: function ( a ) {
var src = this.vertices;
var dst = this.object.geometry.vertices;
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
},
addNormal: function ( a, b, c ) {
var src = this.normals;
var dst = this.object.geometry.normals;
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
},
addFaceNormal: function ( a, b, c ) {
var src = this.vertices;
var dst = this.object.geometry.normals;
vA.fromArray( src, a );
vB.fromArray( src, b );
vC.fromArray( src, c );
cb.subVectors( vC, vB );
ab.subVectors( vA, vB );
cb.cross( ab );
cb.normalize();
dst.push( cb.x, cb.y, cb.z );
dst.push( cb.x, cb.y, cb.z );
dst.push( cb.x, cb.y, cb.z );
},
addColor: function ( a, b, c ) {
var src = this.colors;
var dst = this.object.geometry.colors;
if ( src[ a ] !== undefined ) dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
if ( src[ b ] !== undefined ) dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
if ( src[ c ] !== undefined ) dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
},
addUV: function ( a, b, c ) {
var src = this.uvs;
var dst = this.object.geometry.uvs;
dst.push( src[ a + 0 ], src[ a + 1 ] );
dst.push( src[ b + 0 ], src[ b + 1 ] );
dst.push( src[ c + 0 ], src[ c + 1 ] );
},
addDefaultUV: function () {
var dst = this.object.geometry.uvs;
dst.push( 0, 0 );
dst.push( 0, 0 );
dst.push( 0, 0 );
},
addUVLine: function ( a ) {
var src = this.uvs;
var dst = this.object.geometry.uvs;
dst.push( src[ a + 0 ], src[ a + 1 ] );
},
addFace: function ( a, b, c, ua, ub, uc, na, nb, nc ) {
var vLen = this.vertices.length;
var ia = this.parseVertexIndex( a, vLen );
var ib = this.parseVertexIndex( b, vLen );
var ic = this.parseVertexIndex( c, vLen );
this.addVertex( ia, ib, ic );
this.addColor( ia, ib, ic );
// normals
if ( na !== undefined && na !== '' ) {
var nLen = this.normals.length;
ia = this.parseNormalIndex( na, nLen );
ib = this.parseNormalIndex( nb, nLen );
ic = this.parseNormalIndex( nc, nLen );
this.addNormal( ia, ib, ic );
} else {
this.addFaceNormal( ia, ib, ic );
}
// uvs
if ( ua !== undefined && ua !== '' ) {
var uvLen = this.uvs.length;
ia = this.parseUVIndex( ua, uvLen );
ib = this.parseUVIndex( ub, uvLen );
ic = this.parseUVIndex( uc, uvLen );
this.addUV( ia, ib, ic );
this.object.geometry.hasUVIndices = true;
} else {
// add placeholder values (for inconsistent face definitions)
this.addDefaultUV();
}
},
addPointGeometry: function ( vertices ) {
this.object.geometry.type = 'Points';
var vLen = this.vertices.length;
for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
var index = this.parseVertexIndex( vertices[ vi ], vLen );
this.addVertexPoint( index );
this.addColor( index );
}
},
addLineGeometry: function ( vertices, uvs ) {
this.object.geometry.type = 'Line';
var vLen = this.vertices.length;
var uvLen = this.uvs.length;
for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
this.addVertexLine( this.parseVertexIndex( vertices[ vi ], vLen ) );
}
for ( var uvi = 0, l = uvs.length; uvi < l; uvi ++ ) {
this.addUVLine( this.parseUVIndex( uvs[ uvi ], uvLen ) );
}
}
};
state.startObject( '', false );
return state;
}
//
function OBJLoader( manager ) {
THREE.Loader.call( this, manager );
this.materials = null;
}
OBJLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype ), {
constructor: OBJLoader,
load: function ( url, onLoad, onProgress, onError ) {
var scope = this;
var loader = new THREE.FileLoader( this.manager );
loader.setPath( this.path );
loader.setRequestHeader( this.requestHeader );
loader.setWithCredentials( this.withCredentials );
loader.load( url, function ( text ) {
try {
onLoad( scope.parse( text ) );
} catch ( e ) {
if ( onError ) {
onError( e );
} else {
console.error( e );
}
scope.manager.itemError( url );
}
}, onProgress, onError );
},
setMaterials: function ( materials ) {
this.materials = materials;
return this;
},
parse: function ( text ) {
var state = new ParserState();
if ( text.indexOf( '\r\n' ) !== - 1 ) {
// This is faster than String.split with regex that splits on both
text = text.replace( /\r\n/g, '\n' );
}
if ( text.indexOf( '\\\n' ) !== - 1 ) {
// join lines separated by a line continuation character (\)
text = text.replace( /\\\n/g, '' );
}
var lines = text.split( '\n' );
var line = '', lineFirstChar = '';
var lineLength = 0;
var result = [];
// Faster to just trim left side of the line. Use if available.
var trimLeft = ( typeof ''.trimLeft === 'function' );
for ( var i = 0, l = lines.length; i < l; i ++ ) {
line = lines[ i ];
line = trimLeft ? line.trimLeft() : line.trim();
lineLength = line.length;
if ( lineLength === 0 ) continue;
lineFirstChar = line.charAt( 0 );
// @todo invoke passed in handler if any
if ( lineFirstChar === '#' ) continue;
if ( lineFirstChar === 'v' ) {
var data = line.split( /\s+/ );
switch ( data[ 0 ] ) {
case 'v':
state.vertices.push(
parseFloat( data[ 1 ] ),
parseFloat( data[ 2 ] ),
parseFloat( data[ 3 ] )
);
if ( data.length >= 7 ) {
state.colors.push(
parseFloat( data[ 4 ] ),
parseFloat( data[ 5 ] ),
parseFloat( data[ 6 ] )
);
} else {
// if no colors are defined, add placeholders so color and vertex indices match
state.colors.push( undefined, undefined, undefined );
}
break;
case 'vn':
state.normals.push(
parseFloat( data[ 1 ] ),
parseFloat( data[ 2 ] ),
parseFloat( data[ 3 ] )
);
break;
case 'vt':
state.uvs.push(
parseFloat( data[ 1 ] ),
parseFloat( data[ 2 ] )
);
break;
}
} else if ( lineFirstChar === 'f' ) {
var lineData = line.substr( 1 ).trim();
var vertexData = lineData.split( /\s+/ );
var faceVertices = [];
// Parse the face vertex data into an easy to work with format
for ( var j = 0, jl = vertexData.length; j < jl; j ++ ) {
var vertex = vertexData[ j ];
if ( vertex.length > 0 ) {
var vertexParts = vertex.split( '/' );
faceVertices.push( vertexParts );
}
}
// Draw an edge between the first vertex and all subsequent vertices to form an n-gon
var v1 = faceVertices[ 0 ];
for ( var j = 1, jl = faceVertices.length - 1; j < jl; j ++ ) {
var v2 = faceVertices[ j ];
var v3 = faceVertices[ j + 1 ];
state.addFace(
v1[ 0 ], v2[ 0 ], v3[ 0 ],
v1[ 1 ], v2[ 1 ], v3[ 1 ],
v1[ 2 ], v2[ 2 ], v3[ 2 ]
);
}
} else if ( lineFirstChar === 'l' ) {
var lineParts = line.substring( 1 ).trim().split( ' ' );
var lineVertices = [], lineUVs = [];
if ( line.indexOf( '/' ) === - 1 ) {
lineVertices = lineParts;
} else {
for ( var li = 0, llen = lineParts.length; li < llen; li ++ ) {
var parts = lineParts[ li ].split( '/' );
if ( parts[ 0 ] !== '' ) lineVertices.push( parts[ 0 ] );
if ( parts[ 1 ] !== '' ) lineUVs.push( parts[ 1 ] );
}
}
state.addLineGeometry( lineVertices, lineUVs );
} else if ( lineFirstChar === 'p' ) {
var lineData = line.substr( 1 ).trim();
var pointData = lineData.split( ' ' );
state.addPointGeometry( pointData );
} else if ( ( result = object_pattern.exec( line ) ) !== null ) {
// o object_name
// or
// g group_name
// WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869
// var name = result[ 0 ].substr( 1 ).trim();
var name = ( ' ' + result[ 0 ].substr( 1 ).trim() ).substr( 1 );
state.startObject( name );
} else if ( material_use_pattern.test( line ) ) {
// material
state.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries );
} else if ( material_library_pattern.test( line ) ) {
// mtl file
state.materialLibraries.push( line.substring( 7 ).trim() );
} else if ( map_use_pattern.test( line ) ) {
// the line is parsed but ignored since the loader assumes textures are defined MTL files
// (according to https://www.okino.com/conv/imp_wave.htm, 'usemap' is the old-style Wavefront texture reference method)
console.warn( 'THREE.OBJLoader: Rendering identifier "usemap" not supported. Textures must be defined in MTL files.' );
} else if ( lineFirstChar === 's' ) {
result = line.split( ' ' );
// smooth shading
// @todo Handle files that have varying smooth values for a set of faces inside one geometry,
// but does not define a usemtl for each face set.
// This should be detected and a dummy material created (later MultiMaterial and geometry groups).
// This requires some care to not create extra material on each smooth value for "normal" obj files.
// where explicit usemtl defines geometry groups.
// Example asset: examples/models/obj/cerberus/Cerberus.obj
/*
* http://paulbourke.net/dataformats/obj/
* or
* http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf
*
* From chapter "Grouping" Syntax explanation "s group_number":
* "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off.
* Polygonal elements use group numbers to put elements in different smoothing groups. For free-form
* surfaces, smoothing groups are either turned on or off; there is no difference between values greater
* than 0."
*/
if ( result.length > 1 ) {
var value = result[ 1 ].trim().toLowerCase();
state.object.smooth = ( value !== '0' && value !== 'off' );
} else {
// ZBrush can produce "s" lines #11707
state.object.smooth = true;
}
var material = state.object.currentMaterial();
if ( material ) material.smooth = state.object.smooth;
} else {
// Handle null terminated files without exception
if ( line === '\0' ) continue;
console.warn( 'THREE.OBJLoader: Unexpected line: "' + line + '"' );
}
}
state.finalize();
var container = new THREE.Group();
container.materialLibraries = [].concat( state.materialLibraries );
var hasPrimitives = ! ( state.objects.length === 1 && state.objects[ 0 ].geometry.vertices.length === 0 );
if ( hasPrimitives === true ) {
for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
var object = state.objects[ i ];
var geometry = object.geometry;
var materials = object.materials;
var isLine = ( geometry.type === 'Line' );
var isPoints = ( geometry.type === 'Points' );
var hasVertexColors = false;
// Skip o/g line declarations that did not follow with any faces
if ( geometry.vertices.length === 0 ) continue;
var buffergeometry = new THREE.BufferGeometry();
buffergeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( geometry.vertices, 3 ) );
if ( geometry.normals.length > 0 ) {
buffergeometry.setAttribute( 'normal', new THREE.Float32BufferAttribute( geometry.normals, 3 ) );
}
if ( geometry.colors.length > 0 ) {
hasVertexColors = true;
buffergeometry.setAttribute( 'color', new THREE.Float32BufferAttribute( geometry.colors, 3 ) );
}
if ( geometry.hasUVIndices === true ) {
buffergeometry.setAttribute( 'uv', new THREE.Float32BufferAttribute( geometry.uvs, 2 ) );
}
// Create materials
var createdMaterials = [];
for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
var sourceMaterial = materials[ mi ];
var materialHash = sourceMaterial.name + '_' + sourceMaterial.smooth + '_' + hasVertexColors;
var material = state.materials[ materialHash ];
if ( this.materials !== null ) {
material = this.materials.create( sourceMaterial.name );
// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) {
var materialLine = new THREE.LineBasicMaterial();
THREE.Material.prototype.copy.call( materialLine, material );
materialLine.color.copy( material.color );
material = materialLine;
} else if ( isPoints && material && ! ( material instanceof THREE.PointsMaterial ) ) {
var materialPoints = new THREE.PointsMaterial( { size: 10, sizeAttenuation: false } );
THREE.Material.prototype.copy.call( materialPoints, material );
materialPoints.color.copy( material.color );
materialPoints.map = material.map;
material = materialPoints;
}
}
if ( material === undefined ) {
if ( isLine ) {
material = new THREE.LineBasicMaterial();
} else if ( isPoints ) {
material = new THREE.PointsMaterial( { size: 1, sizeAttenuation: false } );
} else {
material = new THREE.MeshPhongMaterial();
}
material.name = sourceMaterial.name;
material.flatShading = sourceMaterial.smooth ? false : true;
material.vertexColors = hasVertexColors;
state.materials[ materialHash ] = material;
}
createdMaterials.push( material );
}
// Create mesh
var mesh;
if ( createdMaterials.length > 1 ) {
for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
var sourceMaterial = materials[ mi ];
buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
}
if ( isLine ) {
mesh = new THREE.LineSegments( buffergeometry, createdMaterials );
} else if ( isPoints ) {
mesh = new THREE.Points( buffergeometry, createdMaterials );
} else {
mesh = new THREE.Mesh( buffergeometry, createdMaterials );
}
} else {
if ( isLine ) {
mesh = new THREE.LineSegments( buffergeometry, createdMaterials[ 0 ] );
} else if ( isPoints ) {
mesh = new THREE.Points( buffergeometry, createdMaterials[ 0 ] );
} else {
mesh = new THREE.Mesh( buffergeometry, createdMaterials[ 0 ] );
}
}
mesh.name = object.name;
container.add( mesh );
}
} else {
// if there is only the default parser state object with no geometry data, interpret data as point cloud
if ( state.vertices.length > 0 ) {
var material = new THREE.PointsMaterial( { size: 1, sizeAttenuation: false } );
var buffergeometry = new THREE.BufferGeometry();
buffergeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( state.vertices, 3 ) );
if ( state.colors.length > 0 && state.colors[ 0 ] !== undefined ) {
buffergeometry.setAttribute( 'color', new THREE.Float32BufferAttribute( state.colors, 3 ) );
material.vertexColors = true;
}
var points = new THREE.Points( buffergeometry, material );
container.add( points );
}
}
return container;
}
} );
return OBJLoader;
} )();

File diff suppressed because it is too large Load Diff

2538
hw0/lib/dat.gui.js 100644

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -0,0 +1,816 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('imgui-js')) :
typeof define === 'function' && define.amd ? define(['exports', 'imgui-js'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ImGui_Impl = {}, global.ImGui));
}(this, (function (exports, ImGui) { 'use strict';
let clipboard_text = "";
let canvas = null;
exports.gl = null;
let g_ShaderHandle = null;
let g_VertHandle = null;
let g_FragHandle = null;
let g_AttribLocationTex = null;
let g_AttribLocationProjMtx = null;
let g_AttribLocationPosition = -1;
let g_AttribLocationUV = -1;
let g_AttribLocationColor = -1;
let g_VboHandle = null;
let g_ElementsHandle = null;
let g_FontTexture = null;
exports.ctx = null;
let prev_time = 0;
function document_on_copy(event) {
if (event.clipboardData) {
event.clipboardData.setData("text/plain", clipboard_text);
}
// console.log(`${event.type}: "${clipboard_text}"`);
event.preventDefault();
}
function document_on_cut(event) {
if (event.clipboardData) {
event.clipboardData.setData("text/plain", clipboard_text);
}
// console.log(`${event.type}: "${clipboard_text}"`);
event.preventDefault();
}
function document_on_paste(event) {
if (event.clipboardData) {
clipboard_text = event.clipboardData.getData("text/plain");
}
// console.log(`${event.type}: "${clipboard_text}"`);
event.preventDefault();
}
function window_on_resize() {
if (canvas !== null) {
const devicePixelRatio = window.devicePixelRatio || 1;
canvas.width = Math.floor(canvas.scrollWidth * devicePixelRatio);
canvas.height = Math.floor(canvas.scrollHeight * devicePixelRatio);
}
}
function window_on_gamepadconnected(event /* GamepadEvent */) {
console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.", event.gamepad.index, event.gamepad.id, event.gamepad.buttons.length, event.gamepad.axes.length);
}
function window_on_gamepaddisconnected(event /* GamepadEvent */) {
console.log("Gamepad disconnected at index %d: %s.", event.gamepad.index, event.gamepad.id);
}
function canvas_on_blur(event) {
const io = ImGui.GetIO();
io.KeyCtrl = false;
io.KeyShift = false;
io.KeyAlt = false;
io.KeySuper = false;
for (let i = 0; i < io.KeysDown.length; ++i) {
io.KeysDown[i] = false;
}
for (let i = 0; i < io.MouseDown.length; ++i) {
io.MouseDown[i] = false;
}
}
const key_code_to_index = {
"NumpadEnter": 176,
};
function canvas_on_keydown(event) {
// console.log(event.type, event.key, event.code, event.keyCode);
const io = ImGui.GetIO();
io.KeyCtrl = event.ctrlKey;
io.KeyShift = event.shiftKey;
io.KeyAlt = event.altKey;
io.KeySuper = event.metaKey;
const key_index = key_code_to_index[event.code] || event.keyCode;
ImGui.ASSERT(key_index >= 0 && key_index < ImGui.ARRAYSIZE(io.KeysDown));
io.KeysDown[key_index] = true;
// forward to the keypress event
if ( /*io.WantCaptureKeyboard ||*/event.key === "Tab") {
event.preventDefault();
}
}
function canvas_on_keyup(event) {
// console.log(event.type, event.key, event.code, event.keyCode);
const io = ImGui.GetIO();
io.KeyCtrl = event.ctrlKey;
io.KeyShift = event.shiftKey;
io.KeyAlt = event.altKey;
io.KeySuper = event.metaKey;
const key_index = key_code_to_index[event.code] || event.keyCode;
ImGui.ASSERT(key_index >= 0 && key_index < ImGui.ARRAYSIZE(io.KeysDown));
io.KeysDown[key_index] = false;
if (io.WantCaptureKeyboard) {
event.preventDefault();
}
}
function canvas_on_keypress(event) {
// console.log(event.type, event.key, event.code, event.keyCode);
const io = ImGui.GetIO();
io.AddInputCharacter(event.charCode);
if (io.WantCaptureKeyboard) {
event.preventDefault();
}
}
function canvas_on_pointermove(event) {
const io = ImGui.GetIO();
io.MousePos.x = event.offsetX;
io.MousePos.y = event.offsetY;
if (io.WantCaptureMouse) {
event.preventDefault();
}
}
// MouseEvent.button
// A number representing a given button:
// 0: Main button pressed, usually the left button or the un-initialized state
// 1: Auxiliary button pressed, usually the wheel button or the middle button (if present)
// 2: Secondary button pressed, usually the right button
// 3: Fourth button, typically the Browser Back button
// 4: Fifth button, typically the Browser Forward button
const mouse_button_map = [0, 2, 1, 3, 4];
function canvas_on_pointerdown(event) {
const io = ImGui.GetIO();
io.MousePos.x = event.offsetX;
io.MousePos.y = event.offsetY;
io.MouseDown[mouse_button_map[event.button]] = true;
// if (io.WantCaptureMouse) {
// event.preventDefault();
// }
}
function canvas_on_contextmenu(event) {
const io = ImGui.GetIO();
if (io.WantCaptureMouse) {
event.preventDefault();
}
}
function canvas_on_pointerup(event) {
const io = ImGui.GetIO();
io.MouseDown[mouse_button_map[event.button]] = false;
if (io.WantCaptureMouse) {
event.preventDefault();
}
}
function canvas_on_wheel(event) {
const io = ImGui.GetIO();
let scale = 1.0;
switch (event.deltaMode) {
case event.DOM_DELTA_PIXEL:
scale = 0.01;
break;
case event.DOM_DELTA_LINE:
scale = 0.2;
break;
case event.DOM_DELTA_PAGE:
scale = 1.0;
break;
}
io.MouseWheelH = event.deltaX * scale;
io.MouseWheel = -event.deltaY * scale; // Mouse wheel: 1 unit scrolls about 5 lines text.
if (io.WantCaptureMouse) {
event.preventDefault();
}
}
function Init(value) {
const io = ImGui.GetIO();
if (typeof (window) !== "undefined") {
io.BackendPlatformName = "imgui_impl_browser";
ImGui.LoadIniSettingsFromMemory(window.localStorage.getItem("imgui.ini") || "");
}
else {
io.BackendPlatformName = "imgui_impl_console";
}
if (typeof (navigator) !== "undefined") {
io.ConfigMacOSXBehaviors = navigator.platform.match(/Mac/) !== null;
}
if (typeof (document) !== "undefined") {
document.body.addEventListener("copy", document_on_copy);
document.body.addEventListener("cut", document_on_cut);
document.body.addEventListener("paste", document_on_paste);
}
io.SetClipboardTextFn = (user_data, text) => {
clipboard_text = text;
// console.log(`set clipboard_text: "${clipboard_text}"`);
if (typeof navigator !== "undefined" && typeof navigator.clipboard !== "undefined") {
// console.log(`clipboard.writeText: "${clipboard_text}"`);
navigator.clipboard.writeText(clipboard_text).then(() => {
// console.log(`clipboard.writeText: "${clipboard_text}" done.`);
});
}
};
io.GetClipboardTextFn = (user_data) => {
// if (typeof navigator !== "undefined" && typeof (navigator as any).clipboard !== "undefined") {
// console.log(`clipboard.readText: "${clipboard_text}"`);
// (navigator as any).clipboard.readText().then((text: string): void => {
// clipboard_text = text;
// console.log(`clipboard.readText: "${clipboard_text}" done.`);
// });
// }
// console.log(`get clipboard_text: "${clipboard_text}"`);
return clipboard_text;
};
io.ClipboardUserData = null;
if (typeof (window) !== "undefined") {
window.addEventListener("resize", window_on_resize);
window.addEventListener("gamepadconnected", window_on_gamepadconnected);
window.addEventListener("gamepaddisconnected", window_on_gamepaddisconnected);
}
if (typeof (window) !== "undefined") {
if (value instanceof (HTMLCanvasElement)) {
canvas = value;
value = canvas.getContext("webgl2", { alpha: false }) || canvas.getContext("webgl", { alpha: false }) || canvas.getContext("2d");
}
if (typeof WebGL2RenderingContext !== "undefined" && value instanceof (WebGL2RenderingContext)) {
io.BackendRendererName = "imgui_impl_webgl2";
canvas = canvas || value.canvas;
exports.gl = value;
}
else if (typeof WebGLRenderingContext !== "undefined" && value instanceof (WebGLRenderingContext)) {
io.BackendRendererName = "imgui_impl_webgl";
canvas = canvas || value.canvas;
exports.gl = value;
}
else if (typeof CanvasRenderingContext2D !== "undefined" && value instanceof (CanvasRenderingContext2D)) {
io.BackendRendererName = "imgui_impl_2d";
canvas = canvas || value.canvas;
exports.ctx = value;
}
}
if (canvas !== null) {
window_on_resize();
canvas.style.touchAction = "none"; // Disable browser handling of all panning and zooming gestures.
canvas.addEventListener("blur", canvas_on_blur);
canvas.addEventListener("keydown", canvas_on_keydown);
canvas.addEventListener("keyup", canvas_on_keyup);
canvas.addEventListener("keypress", canvas_on_keypress);
canvas.addEventListener("pointermove", canvas_on_pointermove);
canvas.addEventListener("pointerdown", canvas_on_pointerdown);
canvas.addEventListener("contextmenu", canvas_on_contextmenu);
canvas.addEventListener("pointerup", canvas_on_pointerup);
canvas.addEventListener("wheel", canvas_on_wheel);
}
// Setup back-end capabilities flags
io.BackendFlags |= ImGui.BackendFlags.HasMouseCursors; // We can honor GetMouseCursor() values (optional)
// Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array.
io.KeyMap[ImGui.Key.Tab] = 9;
io.KeyMap[ImGui.Key.LeftArrow] = 37;
io.KeyMap[ImGui.Key.RightArrow] = 39;
io.KeyMap[ImGui.Key.UpArrow] = 38;
io.KeyMap[ImGui.Key.DownArrow] = 40;
io.KeyMap[ImGui.Key.PageUp] = 33;
io.KeyMap[ImGui.Key.PageDown] = 34;
io.KeyMap[ImGui.Key.Home] = 36;
io.KeyMap[ImGui.Key.End] = 35;
io.KeyMap[ImGui.Key.Insert] = 45;
io.KeyMap[ImGui.Key.Delete] = 46;
io.KeyMap[ImGui.Key.Backspace] = 8;
io.KeyMap[ImGui.Key.Space] = 32;
io.KeyMap[ImGui.Key.Enter] = 13;
io.KeyMap[ImGui.Key.Escape] = 27;
io.KeyMap[ImGui.Key.KeyPadEnter] = key_code_to_index["NumpadEnter"];
io.KeyMap[ImGui.Key.A] = 65;
io.KeyMap[ImGui.Key.C] = 67;
io.KeyMap[ImGui.Key.V] = 86;
io.KeyMap[ImGui.Key.X] = 88;
io.KeyMap[ImGui.Key.Y] = 89;
io.KeyMap[ImGui.Key.Z] = 90;
CreateDeviceObjects();
}
function Shutdown() {
DestroyDeviceObjects();
if (canvas !== null) {
canvas.removeEventListener("blur", canvas_on_blur);
canvas.removeEventListener("keydown", canvas_on_keydown);
canvas.removeEventListener("keyup", canvas_on_keyup);
canvas.removeEventListener("keypress", canvas_on_keypress);
canvas.removeEventListener("pointermove", canvas_on_pointermove);
canvas.removeEventListener("pointerdown", canvas_on_pointerdown);
canvas.removeEventListener("contextmenu", canvas_on_contextmenu);
canvas.removeEventListener("pointerup", canvas_on_pointerup);
canvas.removeEventListener("wheel", canvas_on_wheel);
}
exports.gl = null;
exports.ctx = null;
canvas = null;
if (typeof (window) !== "undefined") {
window.removeEventListener("resize", window_on_resize);
window.removeEventListener("gamepadconnected", window_on_gamepadconnected);
window.removeEventListener("gamepaddisconnected", window_on_gamepaddisconnected);
}
if (typeof (document) !== "undefined") {
document.body.removeEventListener("copy", document_on_copy);
document.body.removeEventListener("cut", document_on_cut);
document.body.removeEventListener("paste", document_on_paste);
}
}
function NewFrame(time) {
const io = ImGui.GetIO();
if (io.WantSaveIniSettings) {
io.WantSaveIniSettings = false;
if (typeof (window) !== "undefined") {
window.localStorage.setItem("imgui.ini", ImGui.SaveIniSettingsToMemory());
}
}
const w = canvas && canvas.scrollWidth || 640;
const h = canvas && canvas.scrollHeight || 480;
const display_w = exports.gl && exports.gl.drawingBufferWidth || w;
const display_h = exports.gl && exports.gl.drawingBufferHeight || h;
io.DisplaySize.x = w;
io.DisplaySize.y = h;
io.DisplayFramebufferScale.x = w > 0 ? (display_w / w) : 0;
io.DisplayFramebufferScale.y = h > 0 ? (display_h / h) : 0;
const dt = time - prev_time;
prev_time = time;
io.DeltaTime = dt / 1000;
if (io.WantSetMousePos) {
console.log("TODO: MousePos", io.MousePos.x, io.MousePos.y);
}
if (typeof (document) !== "undefined") {
if (io.MouseDrawCursor) {
document.body.style.cursor = "none";
}
else {
switch (ImGui.GetMouseCursor()) {
case ImGui.MouseCursor.None:
document.body.style.cursor = "none";
break;
default:
case ImGui.MouseCursor.Arrow:
document.body.style.cursor = "default";
break;
case ImGui.MouseCursor.TextInput:
document.body.style.cursor = "text";
break; // When hovering over InputText, etc.
case ImGui.MouseCursor.ResizeAll:
document.body.style.cursor = "all-scroll";
break; // Unused
case ImGui.MouseCursor.ResizeNS:
document.body.style.cursor = "ns-resize";
break; // When hovering over an horizontal border
case ImGui.MouseCursor.ResizeEW:
document.body.style.cursor = "ew-resize";
break; // When hovering over a vertical border or a column
case ImGui.MouseCursor.ResizeNESW:
document.body.style.cursor = "nesw-resize";
break; // When hovering over the bottom-left corner of a window
case ImGui.MouseCursor.ResizeNWSE:
document.body.style.cursor = "nwse-resize";
break; // When hovering over the bottom-right corner of a window
case ImGui.MouseCursor.Hand:
document.body.style.cursor = "move";
break;
case ImGui.MouseCursor.NotAllowed:
document.body.style.cursor = "not-allowed";
break;
}
}
}
// Gamepad navigation mapping [BETA]
for (let i = 0; i < io.NavInputs.length; ++i) {
// TODO: This is currently causing an issue and I have no gamepad to test with.
// The error is: ''set' on proxy: trap returned falsish for property '21'
// I think that the NavInputs are zeroed out by ImGui at the start of each frame anyway
// so I am not sure if the following is even necessary.
//io.NavInputs[i] = 0.0;
}
if (io.ConfigFlags & ImGui.ConfigFlags.NavEnableGamepad) {
// Update gamepad inputs
const gamepads = (typeof (navigator) !== "undefined" && typeof (navigator.getGamepads) === "function") ? navigator.getGamepads() : [];
for (let i = 0; i < gamepads.length; ++i) {
const gamepad = gamepads[i];
if (!gamepad) {
continue;
}
io.BackendFlags |= ImGui.BackendFlags.HasGamepad;
const buttons_count = gamepad.buttons.length;
const axes_count = gamepad.axes.length;
function MAP_BUTTON(NAV_NO, BUTTON_NO) {
if (!gamepad) {
return;
}
if (buttons_count > BUTTON_NO && gamepad.buttons[BUTTON_NO].pressed)
io.NavInputs[NAV_NO] = 1.0;
}
function MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) {
if (!gamepad) {
return;
}
let v = (axes_count > AXIS_NO) ? gamepad.axes[AXIS_NO] : V0;
v = (v - V0) / (V1 - V0);
if (v > 1.0)
v = 1.0;
if (io.NavInputs[NAV_NO] < v)
io.NavInputs[NAV_NO] = v;
}
// TODO: map input based on vendor and product id
// https://developer.mozilla.org/en-US/docs/Web/API/Gamepad/id
const match = gamepad.id.match(/^([0-9a-f]{4})-([0-9a-f]{4})-.*$/);
const match_chrome = gamepad.id.match(/^.*\(.*Vendor: ([0-9a-f]{4}) Product: ([0-9a-f]{4})\).*$/);
const vendor = (match && match[1]) || (match_chrome && match_chrome[1]) || "0000";
const product = (match && match[2]) || (match_chrome && match_chrome[2]) || "0000";
switch (vendor + product) {
case "046dc216": // Logitech Logitech Dual Action (Vendor: 046d Product: c216)
MAP_BUTTON(ImGui.NavInput.Activate, 1); // Cross / A
MAP_BUTTON(ImGui.NavInput.Cancel, 2); // Circle / B
MAP_BUTTON(ImGui.NavInput.Menu, 0); // Square / X
MAP_BUTTON(ImGui.NavInput.Input, 3); // Triangle / Y
MAP_ANALOG(ImGui.NavInput.DpadLeft, 4, -0.3, -0.9); // D-Pad Left
MAP_ANALOG(ImGui.NavInput.DpadRight, 4, +0.3, +0.9); // D-Pad Right
MAP_ANALOG(ImGui.NavInput.DpadUp, 5, -0.3, -0.9); // D-Pad Up
MAP_ANALOG(ImGui.NavInput.DpadDown, 5, +0.3, +0.9); // D-Pad Down
MAP_BUTTON(ImGui.NavInput.FocusPrev, 4); // L1 / LB
MAP_BUTTON(ImGui.NavInput.FocusNext, 5); // R1 / RB
MAP_BUTTON(ImGui.NavInput.TweakSlow, 6); // L2 / LT
MAP_BUTTON(ImGui.NavInput.TweakFast, 7); // R2 / RT
MAP_ANALOG(ImGui.NavInput.LStickLeft, 0, -0.3, -0.9);
MAP_ANALOG(ImGui.NavInput.LStickRight, 0, +0.3, +0.9);
MAP_ANALOG(ImGui.NavInput.LStickUp, 1, -0.3, -0.9);
MAP_ANALOG(ImGui.NavInput.LStickDown, 1, +0.3, +0.9);
break;
case "046dc21d": // Logitech Gamepad F310 (STANDARD GAMEPAD Vendor: 046d Product: c21d)
MAP_BUTTON(ImGui.NavInput.Activate, 0); // Cross / A
MAP_BUTTON(ImGui.NavInput.Cancel, 1); // Circle / B
MAP_BUTTON(ImGui.NavInput.Menu, 2); // Square / X
MAP_BUTTON(ImGui.NavInput.Input, 3); // Triangle / Y
MAP_BUTTON(ImGui.NavInput.DpadLeft, 14); // D-Pad Left
MAP_BUTTON(ImGui.NavInput.DpadRight, 15); // D-Pad Right
MAP_BUTTON(ImGui.NavInput.DpadUp, 12); // D-Pad Up
MAP_BUTTON(ImGui.NavInput.DpadDown, 13); // D-Pad Down
MAP_BUTTON(ImGui.NavInput.FocusPrev, 4); // L1 / LB
MAP_BUTTON(ImGui.NavInput.FocusNext, 5); // R1 / RB
MAP_ANALOG(ImGui.NavInput.TweakSlow, 6, +0.3, +0.9); // L2 / LT
MAP_ANALOG(ImGui.NavInput.TweakFast, 7, +0.3, +0.9); // R2 / RT
MAP_ANALOG(ImGui.NavInput.LStickLeft, 0, -0.3, -0.9);
MAP_ANALOG(ImGui.NavInput.LStickRight, 0, +0.3, +0.9);
MAP_ANALOG(ImGui.NavInput.LStickUp, 1, -0.3, -0.9);
MAP_ANALOG(ImGui.NavInput.LStickDown, 1, +0.3, +0.9);
break;
case "2dc86001": // 8Bitdo SN30 Pro 8Bitdo SN30 Pro (Vendor: 2dc8 Product: 6001)
case "2dc86101": // 8Bitdo SN30 Pro (Vendor: 2dc8 Product: 6101)
MAP_BUTTON(ImGui.NavInput.Activate, 1); // Cross / A
MAP_BUTTON(ImGui.NavInput.Cancel, 0); // Circle / B
MAP_BUTTON(ImGui.NavInput.Menu, 4); // Square / X
MAP_BUTTON(ImGui.NavInput.Input, 3); // Triangle / Y
MAP_ANALOG(ImGui.NavInput.DpadLeft, 6, -0.3, -0.9); // D-Pad Left
MAP_ANALOG(ImGui.NavInput.DpadRight, 6, +0.3, +0.9); // D-Pad Right
MAP_ANALOG(ImGui.NavInput.DpadUp, 7, -0.3, -0.9); // D-Pad Up
MAP_ANALOG(ImGui.NavInput.DpadDown, 7, +0.3, +0.9); // D-Pad Down
MAP_BUTTON(ImGui.NavInput.FocusPrev, 6); // L1 / LB
MAP_BUTTON(ImGui.NavInput.FocusNext, 7); // R1 / RB
MAP_BUTTON(ImGui.NavInput.TweakSlow, 8); // L2 / LT
MAP_BUTTON(ImGui.NavInput.TweakFast, 9); // R2 / RT
MAP_ANALOG(ImGui.NavInput.LStickLeft, 0, -0.3, -0.9);
MAP_ANALOG(ImGui.NavInput.LStickRight, 0, +0.3, +0.9);
MAP_ANALOG(ImGui.NavInput.LStickUp, 1, -0.3, -0.9);
MAP_ANALOG(ImGui.NavInput.LStickDown, 1, +0.3, +0.9);
break;
default: // standard gamepad: https://w3c.github.io/gamepad/#remapping
MAP_BUTTON(ImGui.NavInput.Activate, 0); // Cross / A
MAP_BUTTON(ImGui.NavInput.Cancel, 1); // Circle / B
MAP_BUTTON(ImGui.NavInput.Menu, 2); // Square / X
MAP_BUTTON(ImGui.NavInput.Input, 3); // Triangle / Y
MAP_BUTTON(ImGui.NavInput.DpadLeft, 14); // D-Pad Left
MAP_BUTTON(ImGui.NavInput.DpadRight, 15); // D-Pad Right
MAP_BUTTON(ImGui.NavInput.DpadUp, 12); // D-Pad Up
MAP_BUTTON(ImGui.NavInput.DpadDown, 13); // D-Pad Down
MAP_BUTTON(ImGui.NavInput.FocusPrev, 4); // L1 / LB
MAP_BUTTON(ImGui.NavInput.FocusNext, 5); // R1 / RB
MAP_BUTTON(ImGui.NavInput.TweakSlow, 6); // L2 / LT
MAP_BUTTON(ImGui.NavInput.TweakFast, 7); // R2 / RT
MAP_ANALOG(ImGui.NavInput.LStickLeft, 0, -0.3, -0.9);
MAP_ANALOG(ImGui.NavInput.LStickRight, 0, +0.3, +0.9);
MAP_ANALOG(ImGui.NavInput.LStickUp, 1, -0.3, -0.9);
MAP_ANALOG(ImGui.NavInput.LStickDown, 1, +0.3, +0.9);
break;
}
}
}
}
function RenderDrawData(draw_data = ImGui.GetDrawData()) {
const io = ImGui.GetIO();
if (draw_data === null) {
throw new Error();
}
exports.gl || exports.ctx || console.log(draw_data);
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
const fb_width = io.DisplaySize.x * io.DisplayFramebufferScale.x;
const fb_height = io.DisplaySize.y * io.DisplayFramebufferScale.y;
if (fb_width === 0 || fb_height === 0) {
return;
}
draw_data.ScaleClipRects(io.DisplayFramebufferScale);
const gl2 = typeof WebGL2RenderingContext !== "undefined" && exports.gl instanceof WebGL2RenderingContext && exports.gl || null;
const gl_vao = exports.gl && exports.gl.getExtension("OES_vertex_array_object") || null;
// Backup GL state
const last_active_texture = exports.gl && exports.gl.getParameter(exports.gl.ACTIVE_TEXTURE) || null;
const last_program = exports.gl && exports.gl.getParameter(exports.gl.CURRENT_PROGRAM) || null;
const last_texture = exports.gl && exports.gl.getParameter(exports.gl.TEXTURE_BINDING_2D) || null;
const last_array_buffer = exports.gl && exports.gl.getParameter(exports.gl.ARRAY_BUFFER_BINDING) || null;
const last_element_array_buffer = exports.gl && exports.gl.getParameter(exports.gl.ELEMENT_ARRAY_BUFFER_BINDING) || null;
const last_vertex_array_object = gl2 && gl2.getParameter(gl2.VERTEX_ARRAY_BINDING) || exports.gl && gl_vao && exports.gl.getParameter(gl_vao.VERTEX_ARRAY_BINDING_OES) || null;
// GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
const last_viewport = exports.gl && exports.gl.getParameter(exports.gl.VIEWPORT) || null;
const last_scissor_box = exports.gl && exports.gl.getParameter(exports.gl.SCISSOR_BOX) || null;
const last_blend_src_rgb = exports.gl && exports.gl.getParameter(exports.gl.BLEND_SRC_RGB) || null;
const last_blend_dst_rgb = exports.gl && exports.gl.getParameter(exports.gl.BLEND_DST_RGB) || null;
const last_blend_src_alpha = exports.gl && exports.gl.getParameter(exports.gl.BLEND_SRC_ALPHA) || null;
const last_blend_dst_alpha = exports.gl && exports.gl.getParameter(exports.gl.BLEND_DST_ALPHA) || null;
const last_blend_equation_rgb = exports.gl && exports.gl.getParameter(exports.gl.BLEND_EQUATION_RGB) || null;
const last_blend_equation_alpha = exports.gl && exports.gl.getParameter(exports.gl.BLEND_EQUATION_ALPHA) || null;
const last_enable_blend = exports.gl && exports.gl.getParameter(exports.gl.BLEND) || null;
const last_enable_cull_face = exports.gl && exports.gl.getParameter(exports.gl.CULL_FACE) || null;
const last_enable_depth_test = exports.gl && exports.gl.getParameter(exports.gl.DEPTH_TEST) || null;
const last_enable_scissor_test = exports.gl && exports.gl.getParameter(exports.gl.SCISSOR_TEST) || null;
// Setup desired GL state
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
const vertex_array_object = gl2 && gl2.createVertexArray() || gl_vao && gl_vao.createVertexArrayOES();
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
exports.gl && exports.gl.enable(exports.gl.BLEND);
exports.gl && exports.gl.blendEquation(exports.gl.FUNC_ADD);
exports.gl && exports.gl.blendFunc(exports.gl.SRC_ALPHA, exports.gl.ONE_MINUS_SRC_ALPHA);
exports.gl && exports.gl.disable(exports.gl.CULL_FACE);
exports.gl && exports.gl.disable(exports.gl.DEPTH_TEST);
exports.gl && exports.gl.enable(exports.gl.SCISSOR_TEST);
// glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps.
exports.gl && exports.gl.viewport(0, 0, fb_width, fb_height);
const L = draw_data.DisplayPos.x;
const R = draw_data.DisplayPos.x + draw_data.DisplaySize.x;
const T = draw_data.DisplayPos.y;
const B = draw_data.DisplayPos.y + draw_data.DisplaySize.y;
const ortho_projection = new Float32Array([
2.0 / (R - L), 0.0, 0.0, 0.0,
0.0, 2.0 / (T - B), 0.0, 0.0,
0.0, 0.0, -1.0, 0.0,
(R + L) / (L - R), (T + B) / (B - T), 0.0, 1.0,
]);
exports.gl && exports.gl.useProgram(g_ShaderHandle);
exports.gl && exports.gl.uniform1i(g_AttribLocationTex, 0);
exports.gl && g_AttribLocationProjMtx && exports.gl.uniformMatrix4fv(g_AttribLocationProjMtx, false, ortho_projection);
gl2 && gl2.bindVertexArray(vertex_array_object) || gl_vao && gl_vao.bindVertexArrayOES(vertex_array_object);
// Render command lists
exports.gl && exports.gl.bindBuffer(exports.gl.ARRAY_BUFFER, g_VboHandle);
exports.gl && exports.gl.enableVertexAttribArray(g_AttribLocationPosition);
exports.gl && exports.gl.enableVertexAttribArray(g_AttribLocationUV);
exports.gl && exports.gl.enableVertexAttribArray(g_AttribLocationColor);
exports.gl && exports.gl.vertexAttribPointer(g_AttribLocationPosition, 2, exports.gl.FLOAT, false, ImGui.DrawVertSize, ImGui.DrawVertPosOffset);
exports.gl && exports.gl.vertexAttribPointer(g_AttribLocationUV, 2, exports.gl.FLOAT, false, ImGui.DrawVertSize, ImGui.DrawVertUVOffset);
exports.gl && exports.gl.vertexAttribPointer(g_AttribLocationColor, 4, exports.gl.UNSIGNED_BYTE, true, ImGui.DrawVertSize, ImGui.DrawVertColOffset);
// Draw
const pos = draw_data.DisplayPos;
const idx_buffer_type = exports.gl && ((ImGui.DrawIdxSize === 4) ? exports.gl.UNSIGNED_INT : exports.gl.UNSIGNED_SHORT) || 0;
draw_data.IterateDrawLists((draw_list) => {
exports.gl || exports.ctx || console.log(draw_list);
exports.gl || exports.ctx || console.log("VtxBuffer.length", draw_list.VtxBuffer.length);
exports.gl || exports.ctx || console.log("IdxBuffer.length", draw_list.IdxBuffer.length);
let idx_buffer_offset = 0;
exports.gl && exports.gl.bindBuffer(exports.gl.ARRAY_BUFFER, g_VboHandle);
exports.gl && exports.gl.bufferData(exports.gl.ARRAY_BUFFER, draw_list.VtxBuffer, exports.gl.STREAM_DRAW);
exports.gl && exports.gl.bindBuffer(exports.gl.ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
exports.gl && exports.gl.bufferData(exports.gl.ELEMENT_ARRAY_BUFFER, draw_list.IdxBuffer, exports.gl.STREAM_DRAW);
draw_list.IterateDrawCmds((draw_cmd) => {
exports.gl || exports.ctx || console.log(draw_cmd);
exports.gl || exports.ctx || console.log("ElemCount", draw_cmd.ElemCount);
exports.gl || exports.ctx || console.log("ClipRect", draw_cmd.ClipRect.x, fb_height - draw_cmd.ClipRect.w, draw_cmd.ClipRect.z - draw_cmd.ClipRect.x, draw_cmd.ClipRect.w - draw_cmd.ClipRect.y);
exports.gl || exports.ctx || console.log("TextureId", draw_cmd.TextureId);
if (!exports.gl && !exports.ctx) {
console.log("i: pos.x pos.y uv.x uv.y col");
for (let i = 0; i < Math.min(3, draw_cmd.ElemCount); ++i) {
const view = new ImGui.DrawVert(draw_list.VtxBuffer.buffer, draw_list.VtxBuffer.byteOffset + i * ImGui.DrawVertSize);
console.log(`${i}: ${view.pos[0].toFixed(2)} ${view.pos[1].toFixed(2)} ${view.uv[0].toFixed(5)} ${view.uv[1].toFixed(5)} ${("00000000" + view.col[0].toString(16)).substr(-8)}`);
}
}
if (draw_cmd.UserCallback !== null) {
// User callback (registered via ImDrawList::AddCallback)
draw_cmd.UserCallback(draw_list, draw_cmd);
}
else {
const clip_rect = new ImGui.Vec4(draw_cmd.ClipRect.x - pos.x, draw_cmd.ClipRect.y - pos.y, draw_cmd.ClipRect.z - pos.x, draw_cmd.ClipRect.w - pos.y);
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0 && clip_rect.w >= 0.0) {
// Apply scissor/clipping rectangle
exports.gl && exports.gl.scissor(clip_rect.x, fb_height - clip_rect.w, clip_rect.z - clip_rect.x, clip_rect.w - clip_rect.y);
// Bind texture, Draw
exports.gl && exports.gl.activeTexture(exports.gl.TEXTURE0);
exports.gl && exports.gl.bindTexture(exports.gl.TEXTURE_2D, draw_cmd.TextureId);
exports.gl && exports.gl.drawElements(exports.gl.TRIANGLES, draw_cmd.ElemCount, idx_buffer_type, idx_buffer_offset);
if (exports.ctx) {
exports.ctx.save();
exports.ctx.beginPath();
exports.ctx.rect(clip_rect.x, clip_rect.y, clip_rect.z - clip_rect.x, clip_rect.w - clip_rect.y);
exports.ctx.clip();
const idx = ImGui.DrawIdxSize === 4 ?
new Uint32Array(draw_list.IdxBuffer.buffer, draw_list.IdxBuffer.byteOffset + idx_buffer_offset) :
new Uint16Array(draw_list.IdxBuffer.buffer, draw_list.IdxBuffer.byteOffset + idx_buffer_offset);
for (let i = 0; i < draw_cmd.ElemCount; i += 3) {
const i0 = idx[i + 0];
const i1 = idx[i + 1];
const i2 = idx[i + 2];
const v0 = new ImGui.DrawVert(draw_list.VtxBuffer.buffer, draw_list.VtxBuffer.byteOffset + i0 * ImGui.DrawVertSize);
const v1 = new ImGui.DrawVert(draw_list.VtxBuffer.buffer, draw_list.VtxBuffer.byteOffset + i1 * ImGui.DrawVertSize);
const v2 = new ImGui.DrawVert(draw_list.VtxBuffer.buffer, draw_list.VtxBuffer.byteOffset + i2 * ImGui.DrawVertSize);
const i3 = idx[i + 3];
const i4 = idx[i + 4];
const i5 = idx[i + 5];
const v3 = new ImGui.DrawVert(draw_list.VtxBuffer.buffer, draw_list.VtxBuffer.byteOffset + i3 * ImGui.DrawVertSize);
const v4 = new ImGui.DrawVert(draw_list.VtxBuffer.buffer, draw_list.VtxBuffer.byteOffset + i4 * ImGui.DrawVertSize);
const v5 = new ImGui.DrawVert(draw_list.VtxBuffer.buffer, draw_list.VtxBuffer.byteOffset + i5 * ImGui.DrawVertSize);
let quad = true;
let minmin = v0;
let minmax = v0;
let maxmin = v0;
let maxmax = v0;
for (const v of [v1, v2, v3, v4, v5]) {
let found = false;
if (v.pos[0] <= minmin.pos[0] && v.pos[1] <= minmin.pos[1]) {
minmin = v;
found = true;
}
if (v.pos[0] <= minmax.pos[0] && v.pos[1] >= minmax.pos[1]) {
minmax = v;
found = true;
}
if (v.pos[0] >= maxmin.pos[0] && v.pos[1] <= maxmin.pos[1]) {
maxmin = v;
found = true;
}
if (v.pos[0] >= maxmax.pos[0] && v.pos[1] >= maxmax.pos[1]) {
maxmax = v;
found = true;
}
if (!found) {
quad = false;
}
}
quad = quad && (minmin.pos[0] === minmax.pos[0]);
quad = quad && (maxmin.pos[0] === maxmax.pos[0]);
quad = quad && (minmin.pos[1] === maxmin.pos[1]);
quad = quad && (minmax.pos[1] === maxmax.pos[1]);
if (quad) {
if (minmin.uv[0] === maxmax.uv[0] || minmin.uv[1] === maxmax.uv[1]) {
// one vertex color
exports.ctx.beginPath();
exports.ctx.rect(minmin.pos[0], minmin.pos[1], maxmax.pos[0] - minmin.pos[0], maxmax.pos[1] - minmin.pos[1]);
exports.ctx.fillStyle = `rgba(${v0.col[0] >> 0 & 0xff}, ${v0.col[0] >> 8 & 0xff}, ${v0.col[0] >> 16 & 0xff}, ${(v0.col[0] >> 24 & 0xff) / 0xff})`;
exports.ctx.fill();
}
else {
// no vertex color
const image = draw_cmd.TextureId; // HACK
const width = image instanceof HTMLVideoElement ? image.videoWidth : image.width;
const height = image instanceof HTMLVideoElement ? image.videoHeight : image.height;
image && exports.ctx.drawImage(image, minmin.uv[0] * width, minmin.uv[1] * height, (maxmax.uv[0] - minmin.uv[0]) * width, (maxmax.uv[1] - minmin.uv[1]) * height, minmin.pos[0], minmin.pos[1], maxmax.pos[0] - minmin.pos[0], maxmax.pos[1] - minmin.pos[1]);
// ctx.beginPath();
// ctx.rect(minmin.pos[0], minmin.pos[1], maxmax.pos[0] - minmin.pos[0], maxmax.pos[1] - minmin.pos[1]);
// ctx.strokeStyle = "yellow";
// ctx.stroke();
}
i += 3;
}
else {
// one vertex color, no texture
exports.ctx.beginPath();
exports.ctx.moveTo(v0.pos[0], v0.pos[1]);
exports.ctx.lineTo(v1.pos[0], v1.pos[1]);
exports.ctx.lineTo(v2.pos[0], v2.pos[1]);
exports.ctx.closePath();
exports.ctx.fillStyle = `rgba(${v0.col[0] >> 0 & 0xff}, ${v0.col[0] >> 8 & 0xff}, ${v0.col[0] >> 16 & 0xff}, ${(v0.col[0] >> 24 & 0xff) / 0xff})`;
exports.ctx.fill();
}
}
exports.ctx.restore();
}
}
}
idx_buffer_offset += draw_cmd.ElemCount * ImGui.DrawIdxSize;
});
});
// Destroy the temporary VAO
gl2 && gl2.deleteVertexArray(vertex_array_object) || gl_vao && gl_vao.deleteVertexArrayOES(vertex_array_object);
// Restore modified GL state
exports.gl && (last_program !== null) && exports.gl.useProgram(last_program);
exports.gl && (last_texture !== null) && exports.gl.bindTexture(exports.gl.TEXTURE_2D, last_texture);
exports.gl && (last_active_texture !== null) && exports.gl.activeTexture(last_active_texture);
gl2 && gl2.bindVertexArray(last_vertex_array_object) || gl_vao && gl_vao.bindVertexArrayOES(last_vertex_array_object);
exports.gl && (last_array_buffer !== null) && exports.gl.bindBuffer(exports.gl.ARRAY_BUFFER, last_array_buffer);
exports.gl && (last_element_array_buffer !== null) && exports.gl.bindBuffer(exports.gl.ELEMENT_ARRAY_BUFFER, last_element_array_buffer);
exports.gl && (last_blend_equation_rgb !== null && last_blend_equation_alpha !== null) && exports.gl.blendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
exports.gl && (last_blend_src_rgb !== null && last_blend_src_alpha !== null && last_blend_dst_rgb !== null && last_blend_dst_alpha !== null) && exports.gl.blendFuncSeparate(last_blend_src_rgb, last_blend_src_alpha, last_blend_dst_rgb, last_blend_dst_alpha);
exports.gl && (last_enable_blend ? exports.gl.enable(exports.gl.BLEND) : exports.gl.disable(exports.gl.BLEND));
exports.gl && (last_enable_cull_face ? exports.gl.enable(exports.gl.CULL_FACE) : exports.gl.disable(exports.gl.CULL_FACE));
exports.gl && (last_enable_depth_test ? exports.gl.enable(exports.gl.DEPTH_TEST) : exports.gl.disable(exports.gl.DEPTH_TEST));
exports.gl && (last_enable_scissor_test ? exports.gl.enable(exports.gl.SCISSOR_TEST) : exports.gl.disable(exports.gl.SCISSOR_TEST));
// glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
exports.gl && (last_viewport !== null) && exports.gl.viewport(last_viewport[0], last_viewport[1], last_viewport[2], last_viewport[3]);
exports.gl && (last_scissor_box !== null) && exports.gl.scissor(last_scissor_box[0], last_scissor_box[1], last_scissor_box[2], last_scissor_box[3]);
}
function CreateFontsTexture() {
const io = ImGui.GetIO();
// Backup GL state
const last_texture = exports.gl && exports.gl.getParameter(exports.gl.TEXTURE_BINDING_2D);
// Build texture atlas
// const width: number = 256;
// const height: number = 256;
// const pixels: Uint8Array = new Uint8Array(4 * width * height).fill(0xff);
const { width, height, pixels } = io.Fonts.GetTexDataAsRGBA32(); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// console.log(`font texture ${width} x ${height} @ ${pixels.length}`);
// Upload texture to graphics system
g_FontTexture = exports.gl && exports.gl.createTexture();
exports.gl && exports.gl.bindTexture(exports.gl.TEXTURE_2D, g_FontTexture);
exports.gl && exports.gl.texParameteri(exports.gl.TEXTURE_2D, exports.gl.TEXTURE_MIN_FILTER, exports.gl.LINEAR);
exports.gl && exports.gl.texParameteri(exports.gl.TEXTURE_2D, exports.gl.TEXTURE_MAG_FILTER, exports.gl.LINEAR);
// gl && gl.pixelStorei(gl.UNPACK_ROW_LENGTH); // WebGL2
exports.gl && exports.gl.texImage2D(exports.gl.TEXTURE_2D, 0, exports.gl.RGBA, width, height, 0, exports.gl.RGBA, exports.gl.UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts.TexID = g_FontTexture || { foo: "bar" };
// console.log("font texture id", g_FontTexture);
if (exports.ctx) {
const image_canvas = document.createElement("canvas");
image_canvas.width = width;
image_canvas.height = height;
const image_ctx = image_canvas.getContext("2d");
if (image_ctx === null) {
throw new Error();
}
const image_data = image_ctx.getImageData(0, 0, width, height);
image_data.data.set(pixels);
image_ctx.putImageData(image_data, 0, 0);
io.Fonts.TexID = image_canvas;
}
// Restore modified GL state
exports.gl && last_texture && exports.gl.bindTexture(exports.gl.TEXTURE_2D, last_texture);
}
function DestroyFontsTexture() {
const io = ImGui.GetIO();
io.Fonts.TexID = null;
exports.gl && exports.gl.deleteTexture(g_FontTexture);
g_FontTexture = null;
}
function CreateDeviceObjects() {
const vertex_shader = [
"uniform mat4 ProjMtx;",
"attribute vec2 Position;",
"attribute vec2 UV;",
"attribute vec4 Color;",
"varying vec2 Frag_UV;",
"varying vec4 Frag_Color;",
"void main() {",
" Frag_UV = UV;",
" Frag_Color = Color;",
" gl_Position = ProjMtx * vec4(Position.xy,0,1);",
"}",
];
const fragment_shader = [
"precision mediump float;",
"uniform sampler2D Texture;",
"varying vec2 Frag_UV;",
"varying vec4 Frag_Color;",
"void main() {",
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV);",
"}",
];
g_ShaderHandle = exports.gl && exports.gl.createProgram();
g_VertHandle = exports.gl && exports.gl.createShader(exports.gl.VERTEX_SHADER);
g_FragHandle = exports.gl && exports.gl.createShader(exports.gl.FRAGMENT_SHADER);
exports.gl && exports.gl.shaderSource(g_VertHandle, vertex_shader.join("\n"));
exports.gl && exports.gl.shaderSource(g_FragHandle, fragment_shader.join("\n"));
exports.gl && exports.gl.compileShader(g_VertHandle);
exports.gl && exports.gl.compileShader(g_FragHandle);
exports.gl && exports.gl.attachShader(g_ShaderHandle, g_VertHandle);
exports.gl && exports.gl.attachShader(g_ShaderHandle, g_FragHandle);
exports.gl && exports.gl.linkProgram(g_ShaderHandle);
g_AttribLocationTex = exports.gl && exports.gl.getUniformLocation(g_ShaderHandle, "Texture");
g_AttribLocationProjMtx = exports.gl && exports.gl.getUniformLocation(g_ShaderHandle, "ProjMtx");
g_AttribLocationPosition = exports.gl && exports.gl.getAttribLocation(g_ShaderHandle, "Position") || 0;
g_AttribLocationUV = exports.gl && exports.gl.getAttribLocation(g_ShaderHandle, "UV") || 0;
g_AttribLocationColor = exports.gl && exports.gl.getAttribLocation(g_ShaderHandle, "Color") || 0;
g_VboHandle = exports.gl && exports.gl.createBuffer();
g_ElementsHandle = exports.gl && exports.gl.createBuffer();
CreateFontsTexture();
}
function DestroyDeviceObjects() {
DestroyFontsTexture();
exports.gl && exports.gl.deleteBuffer(g_VboHandle);
g_VboHandle = null;
exports.gl && exports.gl.deleteBuffer(g_ElementsHandle);
g_ElementsHandle = null;
g_AttribLocationTex = null;
g_AttribLocationProjMtx = null;
g_AttribLocationPosition = -1;
g_AttribLocationUV = -1;
g_AttribLocationColor = -1;
exports.gl && exports.gl.deleteProgram(g_ShaderHandle);
g_ShaderHandle = null;
exports.gl && exports.gl.deleteShader(g_VertHandle);
g_VertHandle = null;
exports.gl && exports.gl.deleteShader(g_FragHandle);
g_FragHandle = null;
}
exports.CreateDeviceObjects = CreateDeviceObjects;
exports.CreateFontsTexture = CreateFontsTexture;
exports.DestroyDeviceObjects = DestroyDeviceObjects;
exports.DestroyFontsTexture = DestroyFontsTexture;
exports.Init = Init;
exports.NewFrame = NewFrame;
exports.RenderDrawData = RenderDrawData;
exports.Shutdown = Shutdown;
Object.defineProperty(exports, '__esModule', { value: true });
})));

BIN
hw0/lib/three.js 100644

Binary file not shown.

73
hw0/src/engine.js 100644
View File

@ -0,0 +1,73 @@
var cameraPosition = [-20, 180, 250];
GAMES202Main();
function GAMES202Main() {
const canvas = document.querySelector('#glcanvas');
canvas.width = window.screen.width;
canvas.height = window.screen.height;
const gl = canvas.getContext('webgl');
if (!gl) {
alert('Unable to initialize WebGL. Your browser or machine may not support it.');
return;
}
const camera = new THREE.PerspectiveCamera(75, gl.canvas.clientWidth / gl.canvas.clientHeight, 0.1, 1000);
const cameraControls = new THREE.OrbitControls(camera, canvas);
cameraControls.enableZoom = true;
cameraControls.enableRotate = true;
cameraControls.enablePan = true;
cameraControls.rotateSpeed = 0.3;
cameraControls.zoomSpeed = 1.0;
cameraControls.panSpeed = 2.0;
function setSize(width, height) {
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
setSize(canvas.clientWidth, canvas.clientHeight);
window.addEventListener('resize', () => setSize(canvas.clientWidth, canvas.clientHeight));
camera.position.set(cameraPosition[0], cameraPosition[1], cameraPosition[2]);
cameraControls.target.set(0, 1, 0);
const pointLight = new PointLight(250, [1, 1, 1]);
const renderer = new WebGLRenderer(gl, camera);
renderer.addLight(pointLight);
loadOBJ(renderer, 'assets/mary/', 'Marry');
var guiParams = {
modelTransX: 0,
modelTransY: 0,
modelTransZ: 0,
modelScaleX: 52,
modelScaleY: 52,
modelScaleZ: 52,
}
function createGUI() {
const gui = new dat.gui.GUI();
const panelModel = gui.addFolder('Model properties');
const panelModelTrans = panelModel.addFolder('Translation');
const panelModelScale = panelModel.addFolder('Scale');
panelModelTrans.add(guiParams, 'modelTransX').name('X');
panelModelTrans.add(guiParams, 'modelTransY').name('Y');
panelModelTrans.add(guiParams, 'modelTransZ').name('Z');
panelModelScale.add(guiParams, 'modelScaleX').name('X');
panelModelScale.add(guiParams, 'modelScaleY').name('Y');
panelModelScale.add(guiParams, 'modelScaleZ').name('Z');
panelModel.open();
panelModelTrans.open();
panelModelScale.open();
}
createGUI();
function mainLoop(now) {
cameraControls.update();
renderer.render(guiParams);
requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
}

View File

@ -0,0 +1,12 @@
class EmissiveMaterial extends Material {
constructor(lightIntensity, lightColor) {
super({
'uLigIntensity': { type: '1f', value: lightIntensity },
'uLightColor': { type: '3fv', value: lightColor }
}, [], LightCubeVertexShader, LightCubeFragmentShader);
this.intensity = lightIntensity;
this.color = lightColor;
}
}

View File

@ -0,0 +1,13 @@
class PointLight {
/**
* Creates an instance of PointLight.
* @param {float} lightIntensity The intensity of the PointLight.
* @param {vec3f} lightColor The color of the PointLight.
* @memberof PointLight
*/
constructor(lightIntensity, lightColor) {
this.mesh = Mesh.cube();
this.mat = new EmissiveMaterial(lightIntensity, lightColor);
}
}

View File

@ -0,0 +1,64 @@
function loadOBJ(renderer, path, name) {
const manager = new THREE.LoadingManager();
manager.onProgress = function (item, loaded, total) {
console.log(item, loaded, total);
};
function onProgress(xhr) {
if (xhr.lengthComputable) {
const percentComplete = xhr.loaded / xhr.total * 100;
console.log('model ' + Math.round(percentComplete, 2) + '% downloaded');
}
}
function onError() { }
new THREE.MTLLoader(manager)
.setPath(path)
.load(name + '.mtl', function (materials) {
materials.preload();
new THREE.OBJLoader(manager)
.setMaterials(materials)
.setPath(path)
.load(name + '.obj', function (object) {
object.traverse(function (child) {
if (child.isMesh) {
let geo = child.geometry;
let mat;
if (Array.isArray(child.material)) mat = child.material[0];
else mat = child.material;
var indices = Array.from({ length: geo.attributes.position.count }, (v, k) => k);
let mesh = new Mesh({ name: 'aVertexPosition', array: geo.attributes.position.array },
{ name: 'aNormalPosition', array: geo.attributes.normal.array },
{ name: 'aTextureCoord', array: geo.attributes.uv.array },
indices);
let colorMap = null;
if (mat.map != null) colorMap = new Texture(renderer.gl, mat.map.image);
// MARK: You can change the myMaterial object to your own Material instance
let textureSample = 0;
let myMaterial;
if (colorMap != null) {
textureSample = 1;
myMaterial = new Material({
'uSampler': { type: 'texture', value: colorMap },
'uTextureSample': { type: '1i', value: textureSample },
'uKd': { type: '3fv', value: mat.color.toArray() }
},[],VertexShader, FragmentShader);
}else{
myMaterial = new Material({
'uTextureSample': { type: '1i', value: textureSample },
'uKd': { type: '3fv', value: mat.color.toArray() }
},[],VertexShader, FragmentShader);
}
let meshRender = new MeshRender(renderer.gl, mesh, myMaterial);
renderer.addMesh(meshRender);
}
});
}, onProgress, onError);
});
}

View File

@ -0,0 +1,11 @@
function loadShaderFile(filename) {
return new Promise((resolve, reject) => {
const loader = new THREE.FileLoader();
loader.load(filename, (data) => {
resolve(data);
//console.log(data);
});
});
}

View File

@ -0,0 +1,33 @@
class Material {
#flatten_uniforms;
#flatten_attribs;
#vsSrc;
#fsSrc;
// Uniforms is a map, attribs is a Array
constructor(uniforms, attribs, vsSrc, fsSrc) {
this.uniforms = uniforms;
this.attribs = attribs;
this.#vsSrc = vsSrc;
this.#fsSrc = fsSrc;
this.#flatten_uniforms = ['uModelViewMatrix', 'uProjectionMatrix', 'uCameraPos', 'uLightPos'];
for (let k in uniforms) {
this.#flatten_uniforms.push(k);
}
this.#flatten_attribs = attribs;
}
setMeshAttribs(extraAttribs) {
for (let i = 0; i < extraAttribs.length; i++) {
this.#flatten_attribs.push(extraAttribs[i]);
}
}
compile(gl) {
return new Shader(gl, this.#vsSrc, this.#fsSrc,
{
uniforms: this.#flatten_uniforms,
attribs: this.#flatten_attribs
});
}
}

View File

@ -0,0 +1,75 @@
class Mesh {
constructor(verticesAttrib, normalsAttrib, texcoordsAttrib, indices) {
this.indices = indices;
this.count = indices.length;
this.hasVertices = false;
this.hasNormals = false;
this.hasTexcoords = false;
let extraAttribs = [];
if (verticesAttrib != null) {
this.hasVertices = true;
this.vertices = verticesAttrib.array;
this.verticesName = verticesAttrib.name;
}
if (normalsAttrib != null) {
this.hasNormals = true;
this.normals = normalsAttrib.array;
this.normalsName = normalsAttrib.name;
}
if (texcoordsAttrib != null) {
this.hasTexcoords = true;
this.texcoords = texcoordsAttrib.array;
this.texcoordsName = texcoordsAttrib.name;
}
}
static cube() {
const positions = [
// Front face
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
// Back face
-1.0, -1.0, -1.0,
-1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
// Top face
-1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
// Bottom face
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0, 1.0,
-1.0, -1.0, 1.0,
// Right face
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
1.0, 1.0, 1.0,
1.0, -1.0, 1.0,
// Left face
-1.0, -1.0, -1.0,
-1.0, -1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, 1.0, -1.0,
];
const indices = [
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
];
return new Mesh({ name: 'aVertexPosition', array: new Float32Array(positions) }, null, null, indices);
}
}

View File

@ -0,0 +1,165 @@
class MeshRender {
#vertexBuffer;
#normalBuffer;
#texcoordBuffer;
#indicesBuffer;
constructor(gl, mesh, material) {
this.gl = gl;
this.mesh = mesh;
this.material = material;
this.#vertexBuffer = gl.createBuffer();
this.#normalBuffer = gl.createBuffer();
this.#texcoordBuffer = gl.createBuffer();
this.#indicesBuffer = gl.createBuffer();
let extraAttribs = []
if (mesh.hasVertices) {
extraAttribs.push(mesh.verticesName);
gl.bindBuffer(gl.ARRAY_BUFFER, this.#vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, mesh.vertices, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
if (mesh.hasNormals) {
extraAttribs.push(mesh.normalsName);
gl.bindBuffer(gl.ARRAY_BUFFER, this.#normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, mesh.normals, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
if (mesh.hasTexcoords) {
extraAttribs.push(mesh.texcoordsName);
gl.bindBuffer(gl.ARRAY_BUFFER, this.#texcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, mesh.texcoords, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.#indicesBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(mesh.indices), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
this.material.setMeshAttribs(extraAttribs);
this.shader = this.material.compile(gl);
}
draw(camera, transform) {
const gl = this.gl;
let modelViewMatrix = mat4.create();
let projectionMatrix = mat4.create();
camera.updateMatrixWorld();
mat4.invert(modelViewMatrix, camera.matrixWorld.elements);
mat4.translate(modelViewMatrix, modelViewMatrix, transform.translate);
mat4.scale(modelViewMatrix, modelViewMatrix, transform.scale);
mat4.copy(projectionMatrix, camera.projectionMatrix.elements);
if (this.mesh.hasVertices) {
const numComponents = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, this.#vertexBuffer);
gl.vertexAttribPointer(
this.shader.program.attribs[this.mesh.verticesName],
numComponents,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(
this.shader.program.attribs[this.mesh.verticesName]);
}
if (this.mesh.hasNormals) {
const numComponents = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, this.#normalBuffer);
gl.vertexAttribPointer(
this.shader.program.attribs[this.mesh.normalsName],
numComponents,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(
this.shader.program.attribs[this.mesh.normalsName]);
}
if (this.mesh.hasTexcoords) {
const numComponents = 2;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, this.#texcoordBuffer);
gl.vertexAttribPointer(
this.shader.program.attribs[this.mesh.texcoordsName],
numComponents,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(
this.shader.program.attribs[this.mesh.texcoordsName]);
}
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.#indicesBuffer);
gl.useProgram(this.shader.program.glShaderProgram);
gl.uniformMatrix4fv(
this.shader.program.uniforms.uProjectionMatrix,
false,
projectionMatrix);
gl.uniformMatrix4fv(
this.shader.program.uniforms.uModelViewMatrix,
false,
modelViewMatrix);
// Specific the camera uniforms
gl.uniform3fv(
this.shader.program.uniforms.uCameraPos,
[camera.position.x, camera.position.y, camera.position.z]);
for (let k in this.material.uniforms) {
if (this.material.uniforms[k].type == 'matrix4fv') {
gl.uniformMatrix4fv(
this.shader.program.uniforms[k],
false,
this.material.uniforms[k].value);
} else if (this.material.uniforms[k].type == '3fv') {
gl.uniform3fv(
this.shader.program.uniforms[k],
this.material.uniforms[k].value);
} else if (this.material.uniforms[k].type == '1f') {
gl.uniform1f(
this.shader.program.uniforms[k],
this.material.uniforms[k].value);
} else if (this.material.uniforms[k].type == '1i') {
gl.uniform1i(
this.shader.program.uniforms[k],
this.material.uniforms[k].value);
} else if (this.material.uniforms[k].type == 'texture') {
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this.material.uniforms[k].value.texture);
gl.uniform1i(this.shader.program.uniforms[k], 0);
}
}
{
const vertexCount = this.mesh.count;
const type = gl.UNSIGNED_SHORT;
const offset = 0;
gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);
}
}
}

View File

@ -0,0 +1,64 @@
// Remain rotatation
class TRSTransform {
constructor(translate = [0, 0, 0], scale = [1, 1, 1]) {
this.translate = translate;
this.scale = scale;
}
}
class WebGLRenderer {
meshes = [];
lights = [];
constructor(gl, camera) {
this.gl = gl;
this.camera = camera;
}
addLight(light) { this.lights.push({ entity: light, meshRender: new MeshRender(this.gl, light.mesh, light.mat) }); }
addMesh(mesh) { this.meshes.push(mesh); }
render(guiParams) {
const gl = this.gl;
gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque
gl.clearDepth(1.0); // Clear everything
gl.enable(gl.DEPTH_TEST); // Enable depth testing
gl.depthFunc(gl.LEQUAL); // Near things obscure far things
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// Handle light
const timer = Date.now() * 0.00025;
let lightPos = [ Math.sin(timer * 6) * 100,
Math.cos(timer * 4) * 150,
Math.cos(timer * 2) * 100 ];
if (this.lights.length != 0) {
for (let l = 0; l < this.lights.length; l++) {
let trans = new TRSTransform(lightPos);
this.lights[l].meshRender.draw(this.camera, trans);
for (let i = 0; i < this.meshes.length; i++) {
const mesh = this.meshes[i];
const modelTranslation = [guiParams.modelTransX, guiParams.modelTransY, guiParams.modelTransZ];
const modelScale = [guiParams.modelScaleX, guiParams.modelScaleY, guiParams.modelScaleZ];
let meshTrans = new TRSTransform(modelTranslation, modelScale);
this.gl.useProgram(mesh.shader.program.glShaderProgram);
this.gl.uniform3fv(mesh.shader.program.uniforms.uLightPos, lightPos);
mesh.draw(this.camera, meshTrans);
}
}
} else {
// Handle mesh(no light)
for (let i = 0; i < this.meshes.length; i++) {
const mesh = this.meshes[i];
let trans = new TRSTransform();
mesh.draw(this.camera, trans);
}
}
}
}

View File

@ -0,0 +1,77 @@
const LightCubeVertexShader = `
attribute vec3 aVertexPosition;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
void main(void) {
gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aVertexPosition, 1.0);
}
`;
const LightCubeFragmentShader = `
#ifdef GL_ES
precision mediump float;
#endif
uniform float uLigIntensity;
uniform vec3 uLightColor;
void main(void) {
//gl_FragColor = vec4(1,1,1, 1.0);
gl_FragColor = vec4(uLightColor, 1.0);
}
`;
const VertexShader = `
attribute vec3 aVertexPosition;
attribute vec3 aNormalPosition;
attribute vec2 aTextureCoord;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
varying highp vec3 vFragPos;
varying highp vec3 vNormal;
varying highp vec2 vTextureCoord;
void main(void) {
vFragPos = aVertexPosition;
vNormal = aNormalPosition;
gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aVertexPosition, 1.0);
vTextureCoord = aTextureCoord;
}
`;
const FragmentShader = `
#ifdef GL_ES
precision mediump float;
#endif
uniform int uTextureSample;
uniform vec3 uKd;
uniform sampler2D uSampler;
uniform vec3 uLightPos;
uniform vec3 uCameraPos;
varying highp vec3 vFragPos;
varying highp vec3 vNormal;
varying highp vec2 vTextureCoord;
void main(void) {
if (uTextureSample == 1) {
gl_FragColor = texture2D(uSampler, vTextureCoord);
} else {
gl_FragColor = vec4(uKd,1);
}
}
`;

View File

@ -0,0 +1,63 @@
class Shader {
constructor(gl, vsSrc, fsSrc, shaderLocations) {
this.gl = gl;
const vs = this.compileShader(vsSrc, gl.VERTEX_SHADER);
const fs = this.compileShader(fsSrc, gl.FRAGMENT_SHADER);
this.program = this.addShaderLocations({
glShaderProgram: this.linkShader(vs, fs),
}, shaderLocations);
}
compileShader(shaderSource, shaderType) {
const gl = this.gl;
var shader = gl.createShader(shaderType);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error(shaderSource);
console.error(('shader compiler error:\n' + gl.getShaderInfoLog(shader)));
}
return shader;
};
linkShader(vs, fs) {
const gl = this.gl;
var prog = gl.createProgram();
gl.attachShader(prog, vs);
gl.attachShader(prog, fs);
gl.linkProgram(prog);
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
abort('shader linker error:\n' + gl.getProgramInfoLog(prog));
}
return prog;
};
addShaderLocations(result, shaderLocations) {
const gl = this.gl;
result.uniforms = {};
result.attribs = {};
if (shaderLocations && shaderLocations.uniforms && shaderLocations.uniforms.length) {
for (let i = 0; i < shaderLocations.uniforms.length; ++i) {
result.uniforms = Object.assign(result.uniforms, {
[shaderLocations.uniforms[i]]: gl.getUniformLocation(result.glShaderProgram, shaderLocations.uniforms[i]),
});
//console.log(gl.getUniformLocation(result.glShaderProgram, 'uKd'));
}
}
if (shaderLocations && shaderLocations.attribs && shaderLocations.attribs.length) {
for (let i = 0; i < shaderLocations.attribs.length; ++i) {
result.attribs = Object.assign(result.attribs, {
[shaderLocations.attribs[i]]: gl.getAttribLocation(result.glShaderProgram, shaderLocations.attribs[i]),
});
}
}
return result;
}
}

View File

@ -0,0 +1,12 @@
#ifdef GL_ES
precision mediump float;
#endif
uniform float uLigIntensity;
uniform vec3 uLightColor;
void main(void) {
//gl_FragColor = vec4(1,1,1, 1.0);
gl_FragColor = vec4(uLightColor, 1.0);
}

View File

@ -0,0 +1,11 @@
attribute vec3 aVertexPosition;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
void main(void) {
gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aVertexPosition, 1.0);
}

View File

@ -0,0 +1,40 @@
#ifdef GL_ES
precision mediump float;
#endif
uniform sampler2D uSampler;
uniform vec3 uKd;
uniform vec3 uKs;
uniform vec3 uLightPos;
uniform vec3 uCameraPos;
uniform float uLightIntensity;
uniform int uTextureSample;
varying highp vec2 vTextureCoord;
varying highp vec3 vFragPos;
varying highp vec3 vNormal;
void main(void) {
vec3 color;
if (uTextureSample == 1) {
color = pow(texture2D(uSampler, vTextureCoord).rgb, vec3(2.2));
} else {
color = uKd;
}
vec3 ambient = 0.05 * color;
vec3 lightDir = normalize(uLightPos - vFragPos);
vec3 normal = normalize(vNormal);
float diff = max(dot(lightDir, normal), 0.0);
float light_atten_coff = uLightIntensity / length(uLightPos - vFragPos);
vec3 diffuse = diff * light_atten_coff * color;
vec3 viewDir = normalize(uCameraPos - vFragPos);
float spec = 0.0;
vec3 reflectDir = reflect(-lightDir, normal);
spec = pow (max(dot(viewDir, reflectDir), 0.0), 35.0);
vec3 specular = uKs * light_atten_coff * spec;
gl_FragColor = vec4(pow((ambient + diffuse + specular), vec3(1.0/2.2)), 1.0);
}

View File

@ -0,0 +1,22 @@
attribute vec3 aVertexPosition;
attribute vec3 aNormalPosition;
attribute vec2 aTextureCoord;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
varying highp vec2 vTextureCoord;
varying highp vec3 vFragPos;
varying highp vec3 vNormal;
void main(void) {
vFragPos = aVertexPosition;
vNormal = aNormalPosition;
gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aVertexPosition, 1.0);
vTextureCoord = aTextureCoord;
}

View File

@ -0,0 +1 @@
jainzhou

View File

@ -0,0 +1,50 @@
class Texture {
constructor(gl, img) {
this.texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.texture);
// Because images have to be download over the internet
// they might take a moment until they are ready.
// Until then put a single pixel in the texture so we can
// use it immediately. When the image has finished downloading
// we'll update the texture with the contents of the image.
const level = 0;
const internalFormat = gl.RGBA;
const width = 1;
const height = 1;
const border = 0;
const srcFormat = gl.RGBA;
const srcType = gl.UNSIGNED_BYTE;
const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
width, height, border, srcFormat, srcType,
pixel);
gl.bindTexture(gl.TEXTURE_2D, this.texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
srcFormat, srcType, img);
// WebGL1 has different requirements for power of 2 images
// vs non power of 2 images so check if the image is a
// power of 2 in both dimensions.
if (isPowerOf2(img.width) && isPowerOf2(img.height)) {
// Yes, it's a power of 2. Generate mips.
gl.generateMipmap(gl.TEXTURE_2D);
} else {
// No, it's not a power of 2. Turn of mips and set
// wrapping to clamp to edge
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEATE);
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEATE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
}
gl.bindTexture(gl.TEXTURE_2D, null);
}
}
function isPowerOf2(value) {
return (value & (value - 1)) == 0;
}

7
hw1/LICENSE 100644
View File

@ -0,0 +1,7 @@
Copyright (c) 2021 Lingqi Yan <lingqi@cs.ucsb.edu>, All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

22
hw1/README.md 100644
View File

@ -0,0 +1,22 @@
# GAMES202 homework0
## Usage
### For Visual Studio Code
Install plugin `Live Sever` and run with `index.html` directly
### For Node.js users
To install:
```
npm install http-server -g
```
To run(from `index.html` directory):
```
http-server . -p 8000
```
## In-web operation
- Hold right mouse to rotate the camera
- Scroll mouse wheel to zoom in/out
- Hold left mouse to move the camera
- Hold left mouse only to drag the GUI

View File

@ -0,0 +1,10 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl None
Ns 500
Ka 0.8 0.8 0.8
Kd 0.8 0.8 0.8
Ks 0.8 0.8 0.8
d 1
illum 2

View File

@ -0,0 +1,16 @@
# Blender v2.91.0 OBJ File: ''
# www.blender.org
mtllib floor.mtl
o Plane
v -27.924055 0.000000 27.924055
v 27.924055 0.000000 27.924055
v -27.924055 0.000000 -27.924055
v 27.924055 0.000000 -27.924055
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vn 0.0000 1.0000 0.0000
usemtl None
s off
f 1/1/1 2/2/1 4/3/1 3/4/1

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 KiB

View File

@ -0,0 +1,23 @@
# Blender MTL File: 'Marry.blend'
# Material Count: 2
newmtl MC003_Kozakura_Mari
Ns 900.000000
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.000000 0.000000 0.000000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 1
map_Kd MC003_Kozakura_Mari.png
newmtl 材质
Ns 900.000000
Ka 1.000000 1.000000 1.000000
Kd 0.270588 0.552941 0.874510
Ks 0.000000 0.000000 0.000000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 1

Binary file not shown.

View File

@ -0,0 +1,10 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl None
Ns 500
Ka 0.8 0.8 0.8
Kd 0.0 1.0 0.0
Ks 0.8 0.8 0.8
d 1
illum 2

View File

@ -0,0 +1,64 @@
# Blender v2.91.0 OBJ File: ''
# www.blender.org
mtllib untitled.mtl
o Cube
v -1.000000 -1.000000 1.000000
v -0.676804 1.000000 0.676804
v -1.000000 -1.000000 -1.000000
v -0.676804 1.000000 -0.676804
v 1.000000 -1.000000 1.000000
v 0.676804 1.000000 0.676804
v 1.000000 -1.000000 -1.000000
v 0.676804 1.000000 -0.676804
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
v 1.000000 1.000000 -1.000000
v 1.000000 1.000000 1.000000
v -0.676804 3.528965 0.676804
v -0.676804 3.528965 -0.676804
v 0.676804 3.528965 -0.676804
v 0.676804 3.528965 0.676804
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.834601 0.709601
vt 0.665400 0.709601
vt 0.665400 0.709601
vt 0.834601 0.709601
vt 0.834601 0.540400
vt 0.875000 0.500000
vt 0.875000 0.750000
vt 0.665400 0.540400
vt 0.665400 0.540400
vt 0.834601 0.540400
vn -1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 1.0000
vn 0.0000 -1.0000 0.0000
vn 0.0000 1.0000 0.0000
usemtl None
s off
f 1/1/1 9/2/1 10/3/1 3/4/1
f 3/4/2 10/3/2 11/5/2 7/6/2
f 7/6/3 11/5/3 12/7/3 5/8/3
f 5/8/4 12/7/4 9/9/4 1/10/4
f 3/11/5 7/6/5 5/8/5 1/12/5
f 2/13/4 6/14/4 16/15/4 13/16/4
f 2/13/6 4/17/6 10/18/6 9/19/6
f 4/17/6 8/20/6 11/5/6 10/18/6
f 8/20/6 6/14/6 12/7/6 11/5/6
f 6/14/6 2/13/6 9/19/6 12/7/6
f 15/21/6 14/22/6 13/16/6 16/15/6
f 8/20/2 4/17/2 14/22/2 15/21/2
f 6/14/3 8/20/3 15/21/3 16/15/3
f 4/17/1 2/13/1 13/16/1 14/22/1

BIN
hw1/assignment1.pdf 100644

Binary file not shown.

56
hw1/index.html 100644
View File

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html>
<head>
<style>
html,
body {
margin: 0;
background-color: black;
height: 100%;
width: 100%;
overflow: hidden;
}
#glcanvas {
top: 0;
width: 100%;
height: 100%;
}
</style>
<script src="lib/three.js" defer></script>
<script src="lib/OrbitControls.js" defer></script>
<script src="lib/gl-matrix-min.js" defer></script>
<script type="text/javascript" src="lib/MTLLoader.js" defer></script>
<script type="text/javascript" src="lib/OBJLoader.js" defer></script>
<script type="text/javascript" src="lib/dat.gui.js" defer></script>
<script src="src/shaders/Shader.js" defer></script>
<script src="src/shaders/InternalShader.js" defer></script>
<script src="src/materials/Material.js" defer></script>
<script src="src/materials/ShadowMaterial.js" defer></script>
<script src="src/materials/PhongMaterial.js" defer></script>
<script src="src/textures/Texture.js" defer></script>
<script src="src/textures/FBO.js" defer></script>
<script src="src/objects/Mesh.js" defer></script>
<script src="src/loads/loadOBJ.js" defer></script>
<script src="src/loads/loadShader.js" defer></script>
<script src="src/lights/Light.js" defer></script>
<script src="src/lights/DirectionalLight.js" defer></script>
<script src="src/lights/PointLight.js" defer></script>
<script src="src/renderers/WebGLRenderer.js" defer></script>
<script src="src/renderers/MeshRender.js" defer></script>
<script src="src/engine.js" defer></script>
</head>
<body>
<canvas id="glcanvas"></canvas>
</body>
</html>

View File

@ -0,0 +1,549 @@
/**
* Loads a Wavefront .mtl file specifying materials
*/
THREE.MTLLoader = function ( manager ) {
THREE.Loader.call( this, manager );
};
THREE.MTLLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype ), {
constructor: THREE.MTLLoader,
/**
* Loads and parses a MTL asset from a URL.
*
* @param {String} url - URL to the MTL file.
* @param {Function} [onLoad] - Callback invoked with the loaded object.
* @param {Function} [onProgress] - Callback for download progress.
* @param {Function} [onError] - Callback for download errors.
*
* @see setPath setResourcePath
*
* @note In order for relative texture references to resolve correctly
* you must call setResourcePath() explicitly prior to load.
*/
load: function ( url, onLoad, onProgress, onError ) {
var scope = this;
var path = ( this.path === '' ) ? THREE.LoaderUtils.extractUrlBase( url ) : this.path;
var loader = new THREE.FileLoader( this.manager );
loader.setPath( this.path );
loader.setRequestHeader( this.requestHeader );
loader.setWithCredentials( this.withCredentials );
loader.load( url, function ( text ) {
try {
onLoad( scope.parse( text, path ) );
} catch ( e ) {
if ( onError ) {
onError( e );
} else {
console.error( e );
}
scope.manager.itemError( url );
}
}, onProgress, onError );
},
setMaterialOptions: function ( value ) {
this.materialOptions = value;
return this;
},
/**
* Parses a MTL file.
*
* @param {String} text - Content of MTL file
* @return {THREE.MTLLoader.MaterialCreator}
*
* @see setPath setResourcePath
*
* @note In order for relative texture references to resolve correctly
* you must call setResourcePath() explicitly prior to parse.
*/
parse: function ( text, path ) {
var lines = text.split( '\n' );
var info = {};
var delimiter_pattern = /\s+/;
var materialsInfo = {};
for ( var i = 0; i < lines.length; i ++ ) {
var line = lines[ i ];
line = line.trim();
if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
// Blank line or comment ignore
continue;
}
var pos = line.indexOf( ' ' );
var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line;
key = key.toLowerCase();
var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : '';
value = value.trim();
if ( key === 'newmtl' ) {
// New material
info = { name: value };
materialsInfo[ value ] = info;
} else {
if ( key === 'ka' || key === 'kd' || key === 'ks' || key === 'ke' ) {
var ss = value.split( delimiter_pattern, 3 );
info[ key ] = [ parseFloat( ss[ 0 ] ), parseFloat( ss[ 1 ] ), parseFloat( ss[ 2 ] ) ];
} else {
info[ key ] = value;
}
}
}
var materialCreator = new THREE.MTLLoader.MaterialCreator( this.resourcePath || path, this.materialOptions );
materialCreator.setCrossOrigin( this.crossOrigin );
materialCreator.setManager( this.manager );
materialCreator.setMaterials( materialsInfo );
return materialCreator;
}
} );
/**
* Create a new THREE.MTLLoader.MaterialCreator
* @param baseUrl - Url relative to which textures are loaded
* @param options - Set of options on how to construct the materials
* side: Which side to apply the material
* THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide
* wrap: What type of wrapping to apply for textures
* THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping
* normalizeRGB: RGBs need to be normalized to 0-1 from 0-255
* Default: false, assumed to be already normalized
* ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's
* Default: false
* @constructor
*/
THREE.MTLLoader.MaterialCreator = function ( baseUrl, options ) {
this.baseUrl = baseUrl || '';
this.options = options;
this.materialsInfo = {};
this.materials = {};
this.materialsArray = [];
this.nameLookup = {};
this.side = ( this.options && this.options.side ) ? this.options.side : THREE.FrontSide;
this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : THREE.RepeatWrapping;
};
THREE.MTLLoader.MaterialCreator.prototype = {
constructor: THREE.MTLLoader.MaterialCreator,
crossOrigin: 'anonymous',
setCrossOrigin: function ( value ) {
this.crossOrigin = value;
return this;
},
setManager: function ( value ) {
this.manager = value;
},
setMaterials: function ( materialsInfo ) {
this.materialsInfo = this.convert( materialsInfo );
this.materials = {};
this.materialsArray = [];
this.nameLookup = {};
},
convert: function ( materialsInfo ) {
if ( ! this.options ) return materialsInfo;
var converted = {};
for ( var mn in materialsInfo ) {
// Convert materials info into normalized form based on options
var mat = materialsInfo[ mn ];
var covmat = {};
converted[ mn ] = covmat;
for ( var prop in mat ) {
var save = true;
var value = mat[ prop ];
var lprop = prop.toLowerCase();
switch ( lprop ) {
case 'kd':
case 'ka':
case 'ks':
// Diffuse color (color under white light) using RGB values
if ( this.options && this.options.normalizeRGB ) {
value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ];
}
if ( this.options && this.options.ignoreZeroRGBs ) {
if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 2 ] === 0 ) {
// ignore
save = false;
}
}
break;
default:
break;
}
if ( save ) {
covmat[ lprop ] = value;
}
}
}
return converted;
},
preload: function () {
for ( var mn in this.materialsInfo ) {
this.create( mn );
}
},
getIndex: function ( materialName ) {
return this.nameLookup[ materialName ];
},
getAsArray: function () {
var index = 0;
for ( var mn in this.materialsInfo ) {
this.materialsArray[ index ] = this.create( mn );
this.nameLookup[ mn ] = index;
index ++;
}
return this.materialsArray;
},
create: function ( materialName ) {
if ( this.materials[ materialName ] === undefined ) {
this.createMaterial_( materialName );
}
return this.materials[ materialName ];
},
createMaterial_: function ( materialName ) {
// Create material
var scope = this;
var mat = this.materialsInfo[ materialName ];
var params = {
name: materialName,
side: this.side
};
function resolveURL( baseUrl, url ) {
if ( typeof url !== 'string' || url === '' )
return '';
// Absolute URL
if ( /^https?:\/\//i.test( url ) ) return url;
return baseUrl + url;
}
function setMapForType( mapType, value ) {
if ( params[ mapType ] ) return; // Keep the first encountered texture
var texParams = scope.getTextureParams( value, params );
var map = scope.loadTexture( resolveURL( scope.baseUrl, texParams.url ) );
map.repeat.copy( texParams.scale );
map.offset.copy( texParams.offset );
map.wrapS = scope.wrap;
map.wrapT = scope.wrap;
params[ mapType ] = map;
}
for ( var prop in mat ) {
var value = mat[ prop ];
var n;
if ( value === '' ) continue;
switch ( prop.toLowerCase() ) {
// Ns is material specular exponent
case 'kd':
// Diffuse color (color under white light) using RGB values
params.color = new THREE.Color().fromArray( value );
break;
case 'ks':
// Specular color (color when light is reflected from shiny surface) using RGB values
params.specular = new THREE.Color().fromArray( value );
break;
case 'ke':
// Emissive using RGB values
params.emissive = new THREE.Color().fromArray( value );
break;
case 'map_kd':
// Diffuse texture map
setMapForType( 'map', value );
break;
case 'map_ks':
// Specular map
setMapForType( 'specularMap', value );
break;
case 'map_ke':
// Emissive map
setMapForType( 'emissiveMap', value );
break;
case 'norm':
setMapForType( 'normalMap', value );
break;
case 'map_bump':
case 'bump':
// Bump texture map
setMapForType( 'bumpMap', value );
break;
case 'map_d':
// Alpha map
setMapForType( 'alphaMap', value );
params.transparent = true;
break;
case 'ns':
// The specular exponent (defines the focus of the specular highlight)
// A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000.
params.shininess = parseFloat( value );
break;
case 'd':
n = parseFloat( value );
if ( n < 1 ) {
params.opacity = n;
params.transparent = true;
}
break;
case 'tr':
n = parseFloat( value );
if ( this.options && this.options.invertTrProperty ) n = 1 - n;
if ( n > 0 ) {
params.opacity = 1 - n;
params.transparent = true;
}
break;
default:
break;
}
}
this.materials[ materialName ] = new THREE.MeshPhongMaterial( params );
return this.materials[ materialName ];
},
getTextureParams: function ( value, matParams ) {
var texParams = {
scale: new THREE.Vector2( 1, 1 ),
offset: new THREE.Vector2( 0, 0 )
};
var items = value.split( /\s+/ );
var pos;
pos = items.indexOf( '-bm' );
if ( pos >= 0 ) {
matParams.bumpScale = parseFloat( items[ pos + 1 ] );
items.splice( pos, 2 );
}
pos = items.indexOf( '-s' );
if ( pos >= 0 ) {
texParams.scale.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) );
items.splice( pos, 4 ); // we expect 3 parameters here!
}
pos = items.indexOf( '-o' );
if ( pos >= 0 ) {
texParams.offset.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) );
items.splice( pos, 4 ); // we expect 3 parameters here!
}
texParams.url = items.join( ' ' ).trim();
return texParams;
},
loadTexture: function ( url, mapping, onLoad, onProgress, onError ) {
var texture;
var manager = ( this.manager !== undefined ) ? this.manager : THREE.DefaultLoadingManager;
var loader = manager.getHandler( url );
if ( loader === null ) {
loader = new THREE.TextureLoader( manager );
}
if ( loader.setCrossOrigin ) loader.setCrossOrigin( this.crossOrigin );
texture = loader.load( url, onLoad, onProgress, onError );
if ( mapping !== undefined ) texture.mapping = mapping;
return texture;
}
};

View File

@ -0,0 +1,900 @@
THREE.OBJLoader = ( function () {
// o object_name | g group_name
var object_pattern = /^[og]\s*(.+)?/;
// mtllib file_reference
var material_library_pattern = /^mtllib /;
// usemtl material_name
var material_use_pattern = /^usemtl /;
// usemap map_name
var map_use_pattern = /^usemap /;
var vA = new THREE.Vector3();
var vB = new THREE.Vector3();
var vC = new THREE.Vector3();
var ab = new THREE.Vector3();
var cb = new THREE.Vector3();
function ParserState() {
var state = {
objects: [],
object: {},
vertices: [],
normals: [],
colors: [],
uvs: [],
materials: {},
materialLibraries: [],
startObject: function ( name, fromDeclaration ) {
// If the current object (initial from reset) is not from a g/o declaration in the parsed
// file. We need to use it for the first parsed g/o to keep things in sync.
if ( this.object && this.object.fromDeclaration === false ) {
this.object.name = name;
this.object.fromDeclaration = ( fromDeclaration !== false );
return;
}
var previousMaterial = ( this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined );
if ( this.object && typeof this.object._finalize === 'function' ) {
this.object._finalize( true );
}
this.object = {
name: name || '',
fromDeclaration: ( fromDeclaration !== false ),
geometry: {
vertices: [],
normals: [],
colors: [],
uvs: [],
hasUVIndices: false
},
materials: [],
smooth: true,
startMaterial: function ( name, libraries ) {
var previous = this._finalize( false );
// New usemtl declaration overwrites an inherited material, except if faces were declared
// after the material, then it must be preserved for proper MultiMaterial continuation.
if ( previous && ( previous.inherited || previous.groupCount <= 0 ) ) {
this.materials.splice( previous.index, 1 );
}
var material = {
index: this.materials.length,
name: name || '',
mtllib: ( Array.isArray( libraries ) && libraries.length > 0 ? libraries[ libraries.length - 1 ] : '' ),
smooth: ( previous !== undefined ? previous.smooth : this.smooth ),
groupStart: ( previous !== undefined ? previous.groupEnd : 0 ),
groupEnd: - 1,
groupCount: - 1,
inherited: false,
clone: function ( index ) {
var cloned = {
index: ( typeof index === 'number' ? index : this.index ),
name: this.name,
mtllib: this.mtllib,
smooth: this.smooth,
groupStart: 0,
groupEnd: - 1,
groupCount: - 1,
inherited: false
};
cloned.clone = this.clone.bind( cloned );
return cloned;
}
};
this.materials.push( material );
return material;
},
currentMaterial: function () {
if ( this.materials.length > 0 ) {
return this.materials[ this.materials.length - 1 ];
}
return undefined;
},
_finalize: function ( end ) {
var lastMultiMaterial = this.currentMaterial();
if ( lastMultiMaterial && lastMultiMaterial.groupEnd === - 1 ) {
lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3;
lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart;
lastMultiMaterial.inherited = false;
}
// Ignore objects tail materials if no face declarations followed them before a new o/g started.
if ( end && this.materials.length > 1 ) {
for ( var mi = this.materials.length - 1; mi >= 0; mi -- ) {
if ( this.materials[ mi ].groupCount <= 0 ) {
this.materials.splice( mi, 1 );
}
}
}
// Guarantee at least one empty material, this makes the creation later more straight forward.
if ( end && this.materials.length === 0 ) {
this.materials.push( {
name: '',
smooth: this.smooth
} );
}
return lastMultiMaterial;
}
};
// Inherit previous objects material.
// Spec tells us that a declared material must be set to all objects until a new material is declared.
// If a usemtl declaration is encountered while this new object is being parsed, it will
// overwrite the inherited material. Exception being that there was already face declarations
// to the inherited material, then it will be preserved for proper MultiMaterial continuation.
if ( previousMaterial && previousMaterial.name && typeof previousMaterial.clone === 'function' ) {
var declared = previousMaterial.clone( 0 );
declared.inherited = true;
this.object.materials.push( declared );
}
this.objects.push( this.object );
},
finalize: function () {
if ( this.object && typeof this.object._finalize === 'function' ) {
this.object._finalize( true );
}
},
parseVertexIndex: function ( value, len ) {
var index = parseInt( value, 10 );
return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
},
parseNormalIndex: function ( value, len ) {
var index = parseInt( value, 10 );
return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
},
parseUVIndex: function ( value, len ) {
var index = parseInt( value, 10 );
return ( index >= 0 ? index - 1 : index + len / 2 ) * 2;
},
addVertex: function ( a, b, c ) {
var src = this.vertices;
var dst = this.object.geometry.vertices;
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
},
addVertexPoint: function ( a ) {
var src = this.vertices;
var dst = this.object.geometry.vertices;
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
},
addVertexLine: function ( a ) {
var src = this.vertices;
var dst = this.object.geometry.vertices;
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
},
addNormal: function ( a, b, c ) {
var src = this.normals;
var dst = this.object.geometry.normals;
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
},
addFaceNormal: function ( a, b, c ) {
var src = this.vertices;
var dst = this.object.geometry.normals;
vA.fromArray( src, a );
vB.fromArray( src, b );
vC.fromArray( src, c );
cb.subVectors( vC, vB );
ab.subVectors( vA, vB );
cb.cross( ab );
cb.normalize();
dst.push( cb.x, cb.y, cb.z );
dst.push( cb.x, cb.y, cb.z );
dst.push( cb.x, cb.y, cb.z );
},
addColor: function ( a, b, c ) {
var src = this.colors;
var dst = this.object.geometry.colors;
if ( src[ a ] !== undefined ) dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
if ( src[ b ] !== undefined ) dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
if ( src[ c ] !== undefined ) dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
},
addUV: function ( a, b, c ) {
var src = this.uvs;
var dst = this.object.geometry.uvs;
dst.push( src[ a + 0 ], src[ a + 1 ] );
dst.push( src[ b + 0 ], src[ b + 1 ] );
dst.push( src[ c + 0 ], src[ c + 1 ] );
},
addDefaultUV: function () {
var dst = this.object.geometry.uvs;
dst.push( 0, 0 );
dst.push( 0, 0 );
dst.push( 0, 0 );
},
addUVLine: function ( a ) {
var src = this.uvs;
var dst = this.object.geometry.uvs;
dst.push( src[ a + 0 ], src[ a + 1 ] );
},
addFace: function ( a, b, c, ua, ub, uc, na, nb, nc ) {
var vLen = this.vertices.length;
var ia = this.parseVertexIndex( a, vLen );
var ib = this.parseVertexIndex( b, vLen );
var ic = this.parseVertexIndex( c, vLen );
this.addVertex( ia, ib, ic );
this.addColor( ia, ib, ic );
// normals
if ( na !== undefined && na !== '' ) {
var nLen = this.normals.length;
ia = this.parseNormalIndex( na, nLen );
ib = this.parseNormalIndex( nb, nLen );
ic = this.parseNormalIndex( nc, nLen );
this.addNormal( ia, ib, ic );
} else {
this.addFaceNormal( ia, ib, ic );
}
// uvs
if ( ua !== undefined && ua !== '' ) {
var uvLen = this.uvs.length;
ia = this.parseUVIndex( ua, uvLen );
ib = this.parseUVIndex( ub, uvLen );
ic = this.parseUVIndex( uc, uvLen );
this.addUV( ia, ib, ic );
this.object.geometry.hasUVIndices = true;
} else {
// add placeholder values (for inconsistent face definitions)
this.addDefaultUV();
}
},
addPointGeometry: function ( vertices ) {
this.object.geometry.type = 'Points';
var vLen = this.vertices.length;
for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
var index = this.parseVertexIndex( vertices[ vi ], vLen );
this.addVertexPoint( index );
this.addColor( index );
}
},
addLineGeometry: function ( vertices, uvs ) {
this.object.geometry.type = 'Line';
var vLen = this.vertices.length;
var uvLen = this.uvs.length;
for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
this.addVertexLine( this.parseVertexIndex( vertices[ vi ], vLen ) );
}
for ( var uvi = 0, l = uvs.length; uvi < l; uvi ++ ) {
this.addUVLine( this.parseUVIndex( uvs[ uvi ], uvLen ) );
}
}
};
state.startObject( '', false );
return state;
}
//
function OBJLoader( manager ) {
THREE.Loader.call( this, manager );
this.materials = null;
}
OBJLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype ), {
constructor: OBJLoader,
load: function ( url, onLoad, onProgress, onError ) {
var scope = this;
var loader = new THREE.FileLoader( this.manager );
loader.setPath( this.path );
loader.setRequestHeader( this.requestHeader );
loader.setWithCredentials( this.withCredentials );
loader.load( url, function ( text ) {
try {
onLoad( scope.parse( text ) );
} catch ( e ) {
if ( onError ) {
onError( e );
} else {
console.error( e );
}
scope.manager.itemError( url );
}
}, onProgress, onError );
},
setMaterials: function ( materials ) {
this.materials = materials;
return this;
},
parse: function ( text ) {
var state = new ParserState();
if ( text.indexOf( '\r\n' ) !== - 1 ) {
// This is faster than String.split with regex that splits on both
text = text.replace( /\r\n/g, '\n' );
}
if ( text.indexOf( '\\\n' ) !== - 1 ) {
// join lines separated by a line continuation character (\)
text = text.replace( /\\\n/g, '' );
}
var lines = text.split( '\n' );
var line = '', lineFirstChar = '';
var lineLength = 0;
var result = [];
// Faster to just trim left side of the line. Use if available.
var trimLeft = ( typeof ''.trimLeft === 'function' );
for ( var i = 0, l = lines.length; i < l; i ++ ) {
line = lines[ i ];
line = trimLeft ? line.trimLeft() : line.trim();
lineLength = line.length;
if ( lineLength === 0 ) continue;
lineFirstChar = line.charAt( 0 );
// @todo invoke passed in handler if any
if ( lineFirstChar === '#' ) continue;
if ( lineFirstChar === 'v' ) {
var data = line.split( /\s+/ );
switch ( data[ 0 ] ) {
case 'v':
state.vertices.push(
parseFloat( data[ 1 ] ),
parseFloat( data[ 2 ] ),
parseFloat( data[ 3 ] )
);
if ( data.length >= 7 ) {
state.colors.push(
parseFloat( data[ 4 ] ),
parseFloat( data[ 5 ] ),
parseFloat( data[ 6 ] )
);
} else {
// if no colors are defined, add placeholders so color and vertex indices match
state.colors.push( undefined, undefined, undefined );
}
break;
case 'vn':
state.normals.push(
parseFloat( data[ 1 ] ),
parseFloat( data[ 2 ] ),
parseFloat( data[ 3 ] )
);
break;
case 'vt':
state.uvs.push(
parseFloat( data[ 1 ] ),
parseFloat( data[ 2 ] )
);
break;
}
} else if ( lineFirstChar === 'f' ) {
var lineData = line.substr( 1 ).trim();
var vertexData = lineData.split( /\s+/ );
var faceVertices = [];
// Parse the face vertex data into an easy to work with format
for ( var j = 0, jl = vertexData.length; j < jl; j ++ ) {
var vertex = vertexData[ j ];
if ( vertex.length > 0 ) {
var vertexParts = vertex.split( '/' );
faceVertices.push( vertexParts );
}
}
// Draw an edge between the first vertex and all subsequent vertices to form an n-gon
var v1 = faceVertices[ 0 ];
for ( var j = 1, jl = faceVertices.length - 1; j < jl; j ++ ) {
var v2 = faceVertices[ j ];
var v3 = faceVertices[ j + 1 ];
state.addFace(
v1[ 0 ], v2[ 0 ], v3[ 0 ],
v1[ 1 ], v2[ 1 ], v3[ 1 ],
v1[ 2 ], v2[ 2 ], v3[ 2 ]
);
}
} else if ( lineFirstChar === 'l' ) {
var lineParts = line.substring( 1 ).trim().split( ' ' );
var lineVertices = [], lineUVs = [];
if ( line.indexOf( '/' ) === - 1 ) {
lineVertices = lineParts;
} else {
for ( var li = 0, llen = lineParts.length; li < llen; li ++ ) {
var parts = lineParts[ li ].split( '/' );
if ( parts[ 0 ] !== '' ) lineVertices.push( parts[ 0 ] );
if ( parts[ 1 ] !== '' ) lineUVs.push( parts[ 1 ] );
}
}
state.addLineGeometry( lineVertices, lineUVs );
} else if ( lineFirstChar === 'p' ) {
var lineData = line.substr( 1 ).trim();
var pointData = lineData.split( ' ' );
state.addPointGeometry( pointData );
} else if ( ( result = object_pattern.exec( line ) ) !== null ) {
// o object_name
// or
// g group_name
// WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869
// var name = result[ 0 ].substr( 1 ).trim();
var name = ( ' ' + result[ 0 ].substr( 1 ).trim() ).substr( 1 );
state.startObject( name );
} else if ( material_use_pattern.test( line ) ) {
// material
state.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries );
} else if ( material_library_pattern.test( line ) ) {
// mtl file
state.materialLibraries.push( line.substring( 7 ).trim() );
} else if ( map_use_pattern.test( line ) ) {
// the line is parsed but ignored since the loader assumes textures are defined MTL files
// (according to https://www.okino.com/conv/imp_wave.htm, 'usemap' is the old-style Wavefront texture reference method)
console.warn( 'THREE.OBJLoader: Rendering identifier "usemap" not supported. Textures must be defined in MTL files.' );
} else if ( lineFirstChar === 's' ) {
result = line.split( ' ' );
// smooth shading
// @todo Handle files that have varying smooth values for a set of faces inside one geometry,
// but does not define a usemtl for each face set.
// This should be detected and a dummy material created (later MultiMaterial and geometry groups).
// This requires some care to not create extra material on each smooth value for "normal" obj files.
// where explicit usemtl defines geometry groups.
// Example asset: examples/models/obj/cerberus/Cerberus.obj
/*
* http://paulbourke.net/dataformats/obj/
* or
* http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf
*
* From chapter "Grouping" Syntax explanation "s group_number":
* "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off.
* Polygonal elements use group numbers to put elements in different smoothing groups. For free-form
* surfaces, smoothing groups are either turned on or off; there is no difference between values greater
* than 0."
*/
if ( result.length > 1 ) {
var value = result[ 1 ].trim().toLowerCase();
state.object.smooth = ( value !== '0' && value !== 'off' );
} else {
// ZBrush can produce "s" lines #11707
state.object.smooth = true;
}
var material = state.object.currentMaterial();
if ( material ) material.smooth = state.object.smooth;
} else {
// Handle null terminated files without exception
if ( line === '\0' ) continue;
console.warn( 'THREE.OBJLoader: Unexpected line: "' + line + '"' );
}
}
state.finalize();
var container = new THREE.Group();
container.materialLibraries = [].concat( state.materialLibraries );
var hasPrimitives = ! ( state.objects.length === 1 && state.objects[ 0 ].geometry.vertices.length === 0 );
if ( hasPrimitives === true ) {
for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
var object = state.objects[ i ];
var geometry = object.geometry;
var materials = object.materials;
var isLine = ( geometry.type === 'Line' );
var isPoints = ( geometry.type === 'Points' );
var hasVertexColors = false;
// Skip o/g line declarations that did not follow with any faces
if ( geometry.vertices.length === 0 ) continue;
var buffergeometry = new THREE.BufferGeometry();
buffergeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( geometry.vertices, 3 ) );
if ( geometry.normals.length > 0 ) {
buffergeometry.setAttribute( 'normal', new THREE.Float32BufferAttribute( geometry.normals, 3 ) );
}
if ( geometry.colors.length > 0 ) {
hasVertexColors = true;
buffergeometry.setAttribute( 'color', new THREE.Float32BufferAttribute( geometry.colors, 3 ) );
}
if ( geometry.hasUVIndices === true ) {
buffergeometry.setAttribute( 'uv', new THREE.Float32BufferAttribute( geometry.uvs, 2 ) );
}
// Create materials
var createdMaterials = [];
for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
var sourceMaterial = materials[ mi ];
var materialHash = sourceMaterial.name + '_' + sourceMaterial.smooth + '_' + hasVertexColors;
var material = state.materials[ materialHash ];
if ( this.materials !== null ) {
material = this.materials.create( sourceMaterial.name );
// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) {
var materialLine = new THREE.LineBasicMaterial();
THREE.Material.prototype.copy.call( materialLine, material );
materialLine.color.copy( material.color );
material = materialLine;
} else if ( isPoints && material && ! ( material instanceof THREE.PointsMaterial ) ) {
var materialPoints = new THREE.PointsMaterial( { size: 10, sizeAttenuation: false } );
THREE.Material.prototype.copy.call( materialPoints, material );
materialPoints.color.copy( material.color );
materialPoints.map = material.map;
material = materialPoints;
}
}
if ( material === undefined ) {
if ( isLine ) {
material = new THREE.LineBasicMaterial();
} else if ( isPoints ) {
material = new THREE.PointsMaterial( { size: 1, sizeAttenuation: false } );
} else {
material = new THREE.MeshPhongMaterial();
}
material.name = sourceMaterial.name;
material.flatShading = sourceMaterial.smooth ? false : true;
material.vertexColors = hasVertexColors;
state.materials[ materialHash ] = material;
}
createdMaterials.push( material );
}
// Create mesh
var mesh;
if ( createdMaterials.length > 1 ) {
for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
var sourceMaterial = materials[ mi ];
buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
}
if ( isLine ) {
mesh = new THREE.LineSegments( buffergeometry, createdMaterials );
} else if ( isPoints ) {
mesh = new THREE.Points( buffergeometry, createdMaterials );
} else {
mesh = new THREE.Mesh( buffergeometry, createdMaterials );
}
} else {
if ( isLine ) {
mesh = new THREE.LineSegments( buffergeometry, createdMaterials[ 0 ] );
} else if ( isPoints ) {
mesh = new THREE.Points( buffergeometry, createdMaterials[ 0 ] );
} else {
mesh = new THREE.Mesh( buffergeometry, createdMaterials[ 0 ] );
}
}
mesh.name = object.name;
container.add( mesh );
}
} else {
// if there is only the default parser state object with no geometry data, interpret data as point cloud
if ( state.vertices.length > 0 ) {
var material = new THREE.PointsMaterial( { size: 1, sizeAttenuation: false } );
var buffergeometry = new THREE.BufferGeometry();
buffergeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( state.vertices, 3 ) );
if ( state.colors.length > 0 && state.colors[ 0 ] !== undefined ) {
buffergeometry.setAttribute( 'color', new THREE.Float32BufferAttribute( state.colors, 3 ) );
material.vertexColors = true;
}
var points = new THREE.Points( buffergeometry, material );
container.add( points );
}
}
return container;
}
} );
return OBJLoader;
} )();

File diff suppressed because it is too large Load Diff

2538
hw1/lib/dat.gui.js 100644

File diff suppressed because one or more lines are too long

28
hw1/lib/gl-matrix-min.js vendored 100644

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -0,0 +1,816 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('imgui-js')) :
typeof define === 'function' && define.amd ? define(['exports', 'imgui-js'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ImGui_Impl = {}, global.ImGui));
}(this, (function (exports, ImGui) { 'use strict';
let clipboard_text = "";
let canvas = null;
exports.gl = null;
let g_ShaderHandle = null;
let g_VertHandle = null;
let g_FragHandle = null;
let g_AttribLocationTex = null;
let g_AttribLocationProjMtx = null;
let g_AttribLocationPosition = -1;
let g_AttribLocationUV = -1;
let g_AttribLocationColor = -1;
let g_VboHandle = null;
let g_ElementsHandle = null;
let g_FontTexture = null;
exports.ctx = null;
let prev_time = 0;
function document_on_copy(event) {
if (event.clipboardData) {
event.clipboardData.setData("text/plain", clipboard_text);
}
// console.log(`${event.type}: "${clipboard_text}"`);
event.preventDefault();
}
function document_on_cut(event) {
if (event.clipboardData) {
event.clipboardData.setData("text/plain", clipboard_text);
}
// console.log(`${event.type}: "${clipboard_text}"`);
event.preventDefault();
}
function document_on_paste(event) {
if (event.clipboardData) {
clipboard_text = event.clipboardData.getData("text/plain");
}
// console.log(`${event.type}: "${clipboard_text}"`);
event.preventDefault();
}
function window_on_resize() {
if (canvas !== null) {
const devicePixelRatio = window.devicePixelRatio || 1;
canvas.width = Math.floor(canvas.scrollWidth * devicePixelRatio);
canvas.height = Math.floor(canvas.scrollHeight * devicePixelRatio);
}
}
function window_on_gamepadconnected(event /* GamepadEvent */) {
console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.", event.gamepad.index, event.gamepad.id, event.gamepad.buttons.length, event.gamepad.axes.length);
}
function window_on_gamepaddisconnected(event /* GamepadEvent */) {
console.log("Gamepad disconnected at index %d: %s.", event.gamepad.index, event.gamepad.id);
}
function canvas_on_blur(event) {
const io = ImGui.GetIO();
io.KeyCtrl = false;
io.KeyShift = false;
io.KeyAlt = false;
io.KeySuper = false;
for (let i = 0; i < io.KeysDown.length; ++i) {
io.KeysDown[i] = false;
}
for (let i = 0; i < io.MouseDown.length; ++i) {
io.MouseDown[i] = false;
}
}
const key_code_to_index = {
"NumpadEnter": 176,
};
function canvas_on_keydown(event) {
// console.log(event.type, event.key, event.code, event.keyCode);
const io = ImGui.GetIO();
io.KeyCtrl = event.ctrlKey;
io.KeyShift = event.shiftKey;
io.KeyAlt = event.altKey;
io.KeySuper = event.metaKey;
const key_index = key_code_to_index[event.code] || event.keyCode;
ImGui.ASSERT(key_index >= 0 && key_index < ImGui.ARRAYSIZE(io.KeysDown));
io.KeysDown[key_index] = true;
// forward to the keypress event
if ( /*io.WantCaptureKeyboard ||*/event.key === "Tab") {
event.preventDefault();
}
}
function canvas_on_keyup(event) {
// console.log(event.type, event.key, event.code, event.keyCode);
const io = ImGui.GetIO();
io.KeyCtrl = event.ctrlKey;
io.KeyShift = event.shiftKey;
io.KeyAlt = event.altKey;
io.KeySuper = event.metaKey;
const key_index = key_code_to_index[event.code] || event.keyCode;
ImGui.ASSERT(key_index >= 0 && key_index < ImGui.ARRAYSIZE(io.KeysDown));
io.KeysDown[key_index] = false;
if (io.WantCaptureKeyboard) {
event.preventDefault();
}
}
function canvas_on_keypress(event) {
// console.log(event.type, event.key, event.code, event.keyCode);
const io = ImGui.GetIO();
io.AddInputCharacter(event.charCode);
if (io.WantCaptureKeyboard) {
event.preventDefault();
}
}
function canvas_on_pointermove(event) {
const io = ImGui.GetIO();
io.MousePos.x = event.offsetX;
io.MousePos.y = event.offsetY;
if (io.WantCaptureMouse) {
event.preventDefault();
}
}
// MouseEvent.button
// A number representing a given button:
// 0: Main button pressed, usually the left button or the un-initialized state
// 1: Auxiliary button pressed, usually the wheel button or the middle button (if present)
// 2: Secondary button pressed, usually the right button
// 3: Fourth button, typically the Browser Back button
// 4: Fifth button, typically the Browser Forward button
const mouse_button_map = [0, 2, 1, 3, 4];
function canvas_on_pointerdown(event) {
const io = ImGui.GetIO();
io.MousePos.x = event.offsetX;
io.MousePos.y = event.offsetY;
io.MouseDown[mouse_button_map[event.button]] = true;
// if (io.WantCaptureMouse) {
// event.preventDefault();
// }
}
function canvas_on_contextmenu(event) {
const io = ImGui.GetIO();
if (io.WantCaptureMouse) {
event.preventDefault();
}
}
function canvas_on_pointerup(event) {
const io = ImGui.GetIO();
io.MouseDown[mouse_button_map[event.button]] = false;
if (io.WantCaptureMouse) {
event.preventDefault();
}
}
function canvas_on_wheel(event) {
const io = ImGui.GetIO();
let scale = 1.0;
switch (event.deltaMode) {
case event.DOM_DELTA_PIXEL:
scale = 0.01;
break;
case event.DOM_DELTA_LINE:
scale = 0.2;
break;
case event.DOM_DELTA_PAGE:
scale = 1.0;
break;
}
io.MouseWheelH = event.deltaX * scale;
io.MouseWheel = -event.deltaY * scale; // Mouse wheel: 1 unit scrolls about 5 lines text.
if (io.WantCaptureMouse) {
event.preventDefault();
}
}
function Init(value) {
const io = ImGui.GetIO();
if (typeof (window) !== "undefined") {
io.BackendPlatformName = "imgui_impl_browser";
ImGui.LoadIniSettingsFromMemory(window.localStorage.getItem("imgui.ini") || "");
}
else {
io.BackendPlatformName = "imgui_impl_console";
}
if (typeof (navigator) !== "undefined") {
io.ConfigMacOSXBehaviors = navigator.platform.match(/Mac/) !== null;
}
if (typeof (document) !== "undefined") {
document.body.addEventListener("copy", document_on_copy);
document.body.addEventListener("cut", document_on_cut);
document.body.addEventListener("paste", document_on_paste);
}
io.SetClipboardTextFn = (user_data, text) => {
clipboard_text = text;
// console.log(`set clipboard_text: "${clipboard_text}"`);
if (typeof navigator !== "undefined" && typeof navigator.clipboard !== "undefined") {
// console.log(`clipboard.writeText: "${clipboard_text}"`);
navigator.clipboard.writeText(clipboard_text).then(() => {
// console.log(`clipboard.writeText: "${clipboard_text}" done.`);
});
}
};
io.GetClipboardTextFn = (user_data) => {
// if (typeof navigator !== "undefined" && typeof (navigator as any).clipboard !== "undefined") {
// console.log(`clipboard.readText: "${clipboard_text}"`);
// (navigator as any).clipboard.readText().then((text: string): void => {
// clipboard_text = text;
// console.log(`clipboard.readText: "${clipboard_text}" done.`);
// });
// }
// console.log(`get clipboard_text: "${clipboard_text}"`);
return clipboard_text;
};
io.ClipboardUserData = null;
if (typeof (window) !== "undefined") {
window.addEventListener("resize", window_on_resize);
window.addEventListener("gamepadconnected", window_on_gamepadconnected);
window.addEventListener("gamepaddisconnected", window_on_gamepaddisconnected);
}
if (typeof (window) !== "undefined") {
if (value instanceof (HTMLCanvasElement)) {
canvas = value;
value = canvas.getContext("webgl2", { alpha: false }) || canvas.getContext("webgl", { alpha: false }) || canvas.getContext("2d");
}
if (typeof WebGL2RenderingContext !== "undefined" && value instanceof (WebGL2RenderingContext)) {
io.BackendRendererName = "imgui_impl_webgl2";
canvas = canvas || value.canvas;
exports.gl = value;
}
else if (typeof WebGLRenderingContext !== "undefined" && value instanceof (WebGLRenderingContext)) {
io.BackendRendererName = "imgui_impl_webgl";
canvas = canvas || value.canvas;
exports.gl = value;
}
else if (typeof CanvasRenderingContext2D !== "undefined" && value instanceof (CanvasRenderingContext2D)) {
io.BackendRendererName = "imgui_impl_2d";
canvas = canvas || value.canvas;
exports.ctx = value;
}
}
if (canvas !== null) {
window_on_resize();
canvas.style.touchAction = "none"; // Disable browser handling of all panning and zooming gestures.
canvas.addEventListener("blur", canvas_on_blur);
canvas.addEventListener("keydown", canvas_on_keydown);
canvas.addEventListener("keyup", canvas_on_keyup);
canvas.addEventListener("keypress", canvas_on_keypress);
canvas.addEventListener("pointermove", canvas_on_pointermove);
canvas.addEventListener("pointerdown", canvas_on_pointerdown);
canvas.addEventListener("contextmenu", canvas_on_contextmenu);
canvas.addEventListener("pointerup", canvas_on_pointerup);
canvas.addEventListener("wheel", canvas_on_wheel);
}
// Setup back-end capabilities flags
io.BackendFlags |= ImGui.BackendFlags.HasMouseCursors; // We can honor GetMouseCursor() values (optional)
// Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array.
io.KeyMap[ImGui.Key.Tab] = 9;
io.KeyMap[ImGui.Key.LeftArrow] = 37;
io.KeyMap[ImGui.Key.RightArrow] = 39;
io.KeyMap[ImGui.Key.UpArrow] = 38;
io.KeyMap[ImGui.Key.DownArrow] = 40;
io.KeyMap[ImGui.Key.PageUp] = 33;
io.KeyMap[ImGui.Key.PageDown] = 34;
io.KeyMap[ImGui.Key.Home] = 36;
io.KeyMap[ImGui.Key.End] = 35;
io.KeyMap[ImGui.Key.Insert] = 45;
io.KeyMap[ImGui.Key.Delete] = 46;
io.KeyMap[ImGui.Key.Backspace] = 8;
io.KeyMap[ImGui.Key.Space] = 32;
io.KeyMap[ImGui.Key.Enter] = 13;
io.KeyMap[ImGui.Key.Escape] = 27;
io.KeyMap[ImGui.Key.KeyPadEnter] = key_code_to_index["NumpadEnter"];
io.KeyMap[ImGui.Key.A] = 65;
io.KeyMap[ImGui.Key.C] = 67;
io.KeyMap[ImGui.Key.V] = 86;
io.KeyMap[ImGui.Key.X] = 88;
io.KeyMap[ImGui.Key.Y] = 89;
io.KeyMap[ImGui.Key.Z] = 90;
CreateDeviceObjects();
}
function Shutdown() {
DestroyDeviceObjects();
if (canvas !== null) {
canvas.removeEventListener("blur", canvas_on_blur);
canvas.removeEventListener("keydown", canvas_on_keydown);
canvas.removeEventListener("keyup", canvas_on_keyup);
canvas.removeEventListener("keypress", canvas_on_keypress);
canvas.removeEventListener("pointermove", canvas_on_pointermove);
canvas.removeEventListener("pointerdown", canvas_on_pointerdown);
canvas.removeEventListener("contextmenu", canvas_on_contextmenu);
canvas.removeEventListener("pointerup", canvas_on_pointerup);
canvas.removeEventListener("wheel", canvas_on_wheel);
}
exports.gl = null;
exports.ctx = null;
canvas = null;
if (typeof (window) !== "undefined") {
window.removeEventListener("resize", window_on_resize);
window.removeEventListener("gamepadconnected", window_on_gamepadconnected);
window.removeEventListener("gamepaddisconnected", window_on_gamepaddisconnected);
}
if (typeof (document) !== "undefined") {
document.body.removeEventListener("copy", document_on_copy);
document.body.removeEventListener("cut", document_on_cut);
document.body.removeEventListener("paste", document_on_paste);
}
}
function NewFrame(time) {
const io = ImGui.GetIO();
if (io.WantSaveIniSettings) {
io.WantSaveIniSettings = false;
if (typeof (window) !== "undefined") {
window.localStorage.setItem("imgui.ini", ImGui.SaveIniSettingsToMemory());
}
}
const w = canvas && canvas.scrollWidth || 640;
const h = canvas && canvas.scrollHeight || 480;
const display_w = exports.gl && exports.gl.drawingBufferWidth || w;
const display_h = exports.gl && exports.gl.drawingBufferHeight || h;
io.DisplaySize.x = w;
io.DisplaySize.y = h;
io.DisplayFramebufferScale.x = w > 0 ? (display_w / w) : 0;
io.DisplayFramebufferScale.y = h > 0 ? (display_h / h) : 0;
const dt = time - prev_time;
prev_time = time;
io.DeltaTime = dt / 1000;
if (io.WantSetMousePos) {
console.log("TODO: MousePos", io.MousePos.x, io.MousePos.y);
}
if (typeof (document) !== "undefined") {
if (io.MouseDrawCursor) {
document.body.style.cursor = "none";
}
else {
switch (ImGui.GetMouseCursor()) {
case ImGui.MouseCursor.None:
document.body.style.cursor = "none";
break;
default:
case ImGui.MouseCursor.Arrow:
document.body.style.cursor = "default";
break;
case ImGui.MouseCursor.TextInput:
document.body.style.cursor = "text";
break; // When hovering over InputText, etc.
case ImGui.MouseCursor.ResizeAll:
document.body.style.cursor = "all-scroll";
break; // Unused
case ImGui.MouseCursor.ResizeNS:
document.body.style.cursor = "ns-resize";
break; // When hovering over an horizontal border
case ImGui.MouseCursor.ResizeEW:
document.body.style.cursor = "ew-resize";
break; // When hovering over a vertical border or a column
case ImGui.MouseCursor.ResizeNESW:
document.body.style.cursor = "nesw-resize";
break; // When hovering over the bottom-left corner of a window
case ImGui.MouseCursor.ResizeNWSE:
document.body.style.cursor = "nwse-resize";
break; // When hovering over the bottom-right corner of a window
case ImGui.MouseCursor.Hand:
document.body.style.cursor = "move";
break;
case ImGui.MouseCursor.NotAllowed:
document.body.style.cursor = "not-allowed";
break;
}
}
}
// Gamepad navigation mapping [BETA]
for (let i = 0; i < io.NavInputs.length; ++i) {
// TODO: This is currently causing an issue and I have no gamepad to test with.
// The error is: ''set' on proxy: trap returned falsish for property '21'
// I think that the NavInputs are zeroed out by ImGui at the start of each frame anyway
// so I am not sure if the following is even necessary.
//io.NavInputs[i] = 0.0;
}
if (io.ConfigFlags & ImGui.ConfigFlags.NavEnableGamepad) {
// Update gamepad inputs
const gamepads = (typeof (navigator) !== "undefined" && typeof (navigator.getGamepads) === "function") ? navigator.getGamepads() : [];
for (let i = 0; i < gamepads.length; ++i) {
const gamepad = gamepads[i];
if (!gamepad) {
continue;
}
io.BackendFlags |= ImGui.BackendFlags.HasGamepad;
const buttons_count = gamepad.buttons.length;
const axes_count = gamepad.axes.length;
function MAP_BUTTON(NAV_NO, BUTTON_NO) {
if (!gamepad) {
return;
}
if (buttons_count > BUTTON_NO && gamepad.buttons[BUTTON_NO].pressed)
io.NavInputs[NAV_NO] = 1.0;
}
function MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) {
if (!gamepad) {
return;
}
let v = (axes_count > AXIS_NO) ? gamepad.axes[AXIS_NO] : V0;
v = (v - V0) / (V1 - V0);
if (v > 1.0)
v = 1.0;
if (io.NavInputs[NAV_NO] < v)
io.NavInputs[NAV_NO] = v;
}
// TODO: map input based on vendor and product id
// https://developer.mozilla.org/en-US/docs/Web/API/Gamepad/id
const match = gamepad.id.match(/^([0-9a-f]{4})-([0-9a-f]{4})-.*$/);
const match_chrome = gamepad.id.match(/^.*\(.*Vendor: ([0-9a-f]{4}) Product: ([0-9a-f]{4})\).*$/);
const vendor = (match && match[1]) || (match_chrome && match_chrome[1]) || "0000";
const product = (match && match[2]) || (match_chrome && match_chrome[2]) || "0000";
switch (vendor + product) {
case "046dc216": // Logitech Logitech Dual Action (Vendor: 046d Product: c216)
MAP_BUTTON(ImGui.NavInput.Activate, 1); // Cross / A
MAP_BUTTON(ImGui.NavInput.Cancel, 2); // Circle / B
MAP_BUTTON(ImGui.NavInput.Menu, 0); // Square / X
MAP_BUTTON(ImGui.NavInput.Input, 3); // Triangle / Y
MAP_ANALOG(ImGui.NavInput.DpadLeft, 4, -0.3, -0.9); // D-Pad Left
MAP_ANALOG(ImGui.NavInput.DpadRight, 4, +0.3, +0.9); // D-Pad Right
MAP_ANALOG(ImGui.NavInput.DpadUp, 5, -0.3, -0.9); // D-Pad Up
MAP_ANALOG(ImGui.NavInput.DpadDown, 5, +0.3, +0.9); // D-Pad Down
MAP_BUTTON(ImGui.NavInput.FocusPrev, 4); // L1 / LB
MAP_BUTTON(ImGui.NavInput.FocusNext, 5); // R1 / RB
MAP_BUTTON(ImGui.NavInput.TweakSlow, 6); // L2 / LT
MAP_BUTTON(ImGui.NavInput.TweakFast, 7); // R2 / RT
MAP_ANALOG(ImGui.NavInput.LStickLeft, 0, -0.3, -0.9);
MAP_ANALOG(ImGui.NavInput.LStickRight, 0, +0.3, +0.9);
MAP_ANALOG(ImGui.NavInput.LStickUp, 1, -0.3, -0.9);
MAP_ANALOG(ImGui.NavInput.LStickDown, 1, +0.3, +0.9);
break;
case "046dc21d": // Logitech Gamepad F310 (STANDARD GAMEPAD Vendor: 046d Product: c21d)
MAP_BUTTON(ImGui.NavInput.Activate, 0); // Cross / A
MAP_BUTTON(ImGui.NavInput.Cancel, 1); // Circle / B
MAP_BUTTON(ImGui.NavInput.Menu, 2); // Square / X
MAP_BUTTON(ImGui.NavInput.Input, 3); // Triangle / Y
MAP_BUTTON(ImGui.NavInput.DpadLeft, 14); // D-Pad Left
MAP_BUTTON(ImGui.NavInput.DpadRight, 15); // D-Pad Right
MAP_BUTTON(ImGui.NavInput.DpadUp, 12); // D-Pad Up
MAP_BUTTON(ImGui.NavInput.DpadDown, 13); // D-Pad Down
MAP_BUTTON(ImGui.NavInput.FocusPrev, 4); // L1 / LB
MAP_BUTTON(ImGui.NavInput.FocusNext, 5); // R1 / RB
MAP_ANALOG(ImGui.NavInput.TweakSlow, 6, +0.3, +0.9); // L2 / LT
MAP_ANALOG(ImGui.NavInput.TweakFast, 7, +0.3, +0.9); // R2 / RT
MAP_ANALOG(ImGui.NavInput.LStickLeft, 0, -0.3, -0.9);
MAP_ANALOG(ImGui.NavInput.LStickRight, 0, +0.3, +0.9);
MAP_ANALOG(ImGui.NavInput.LStickUp, 1, -0.3, -0.9);
MAP_ANALOG(ImGui.NavInput.LStickDown, 1, +0.3, +0.9);
break;
case "2dc86001": // 8Bitdo SN30 Pro 8Bitdo SN30 Pro (Vendor: 2dc8 Product: 6001)
case "2dc86101": // 8Bitdo SN30 Pro (Vendor: 2dc8 Product: 6101)
MAP_BUTTON(ImGui.NavInput.Activate, 1); // Cross / A
MAP_BUTTON(ImGui.NavInput.Cancel, 0); // Circle / B
MAP_BUTTON(ImGui.NavInput.Menu, 4); // Square / X
MAP_BUTTON(ImGui.NavInput.Input, 3); // Triangle / Y
MAP_ANALOG(ImGui.NavInput.DpadLeft, 6, -0.3, -0.9); // D-Pad Left
MAP_ANALOG(ImGui.NavInput.DpadRight, 6, +0.3, +0.9); // D-Pad Right
MAP_ANALOG(ImGui.NavInput.DpadUp, 7, -0.3, -0.9); // D-Pad Up
MAP_ANALOG(ImGui.NavInput.DpadDown, 7, +0.3, +0.9); // D-Pad Down
MAP_BUTTON(ImGui.NavInput.FocusPrev, 6); // L1 / LB
MAP_BUTTON(ImGui.NavInput.FocusNext, 7); // R1 / RB
MAP_BUTTON(ImGui.NavInput.TweakSlow, 8); // L2 / LT
MAP_BUTTON(ImGui.NavInput.TweakFast, 9); // R2 / RT
MAP_ANALOG(ImGui.NavInput.LStickLeft, 0, -0.3, -0.9);
MAP_ANALOG(ImGui.NavInput.LStickRight, 0, +0.3, +0.9);
MAP_ANALOG(ImGui.NavInput.LStickUp, 1, -0.3, -0.9);
MAP_ANALOG(ImGui.NavInput.LStickDown, 1, +0.3, +0.9);
break;
default: // standard gamepad: https://w3c.github.io/gamepad/#remapping
MAP_BUTTON(ImGui.NavInput.Activate, 0); // Cross / A
MAP_BUTTON(ImGui.NavInput.Cancel, 1); // Circle / B
MAP_BUTTON(ImGui.NavInput.Menu, 2); // Square / X
MAP_BUTTON(ImGui.NavInput.Input, 3); // Triangle / Y
MAP_BUTTON(ImGui.NavInput.DpadLeft, 14); // D-Pad Left
MAP_BUTTON(ImGui.NavInput.DpadRight, 15); // D-Pad Right
MAP_BUTTON(ImGui.NavInput.DpadUp, 12); // D-Pad Up
MAP_BUTTON(ImGui.NavInput.DpadDown, 13); // D-Pad Down
MAP_BUTTON(ImGui.NavInput.FocusPrev, 4); // L1 / LB
MAP_BUTTON(ImGui.NavInput.FocusNext, 5); // R1 / RB
MAP_BUTTON(ImGui.NavInput.TweakSlow, 6); // L2 / LT
MAP_BUTTON(ImGui.NavInput.TweakFast, 7); // R2 / RT
MAP_ANALOG(ImGui.NavInput.LStickLeft, 0, -0.3, -0.9);
MAP_ANALOG(ImGui.NavInput.LStickRight, 0, +0.3, +0.9);
MAP_ANALOG(ImGui.NavInput.LStickUp, 1, -0.3, -0.9);
MAP_ANALOG(ImGui.NavInput.LStickDown, 1, +0.3, +0.9);
break;
}
}
}
}
function RenderDrawData(draw_data = ImGui.GetDrawData()) {
const io = ImGui.GetIO();
if (draw_data === null) {
throw new Error();
}
exports.gl || exports.ctx || console.log(draw_data);
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
const fb_width = io.DisplaySize.x * io.DisplayFramebufferScale.x;
const fb_height = io.DisplaySize.y * io.DisplayFramebufferScale.y;
if (fb_width === 0 || fb_height === 0) {
return;
}
draw_data.ScaleClipRects(io.DisplayFramebufferScale);
const gl2 = typeof WebGL2RenderingContext !== "undefined" && exports.gl instanceof WebGL2RenderingContext && exports.gl || null;
const gl_vao = exports.gl && exports.gl.getExtension("OES_vertex_array_object") || null;
// Backup GL state
const last_active_texture = exports.gl && exports.gl.getParameter(exports.gl.ACTIVE_TEXTURE) || null;
const last_program = exports.gl && exports.gl.getParameter(exports.gl.CURRENT_PROGRAM) || null;
const last_texture = exports.gl && exports.gl.getParameter(exports.gl.TEXTURE_BINDING_2D) || null;
const last_array_buffer = exports.gl && exports.gl.getParameter(exports.gl.ARRAY_BUFFER_BINDING) || null;
const last_element_array_buffer = exports.gl && exports.gl.getParameter(exports.gl.ELEMENT_ARRAY_BUFFER_BINDING) || null;
const last_vertex_array_object = gl2 && gl2.getParameter(gl2.VERTEX_ARRAY_BINDING) || exports.gl && gl_vao && exports.gl.getParameter(gl_vao.VERTEX_ARRAY_BINDING_OES) || null;
// GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
const last_viewport = exports.gl && exports.gl.getParameter(exports.gl.VIEWPORT) || null;
const last_scissor_box = exports.gl && exports.gl.getParameter(exports.gl.SCISSOR_BOX) || null;
const last_blend_src_rgb = exports.gl && exports.gl.getParameter(exports.gl.BLEND_SRC_RGB) || null;
const last_blend_dst_rgb = exports.gl && exports.gl.getParameter(exports.gl.BLEND_DST_RGB) || null;
const last_blend_src_alpha = exports.gl && exports.gl.getParameter(exports.gl.BLEND_SRC_ALPHA) || null;
const last_blend_dst_alpha = exports.gl && exports.gl.getParameter(exports.gl.BLEND_DST_ALPHA) || null;
const last_blend_equation_rgb = exports.gl && exports.gl.getParameter(exports.gl.BLEND_EQUATION_RGB) || null;
const last_blend_equation_alpha = exports.gl && exports.gl.getParameter(exports.gl.BLEND_EQUATION_ALPHA) || null;
const last_enable_blend = exports.gl && exports.gl.getParameter(exports.gl.BLEND) || null;
const last_enable_cull_face = exports.gl && exports.gl.getParameter(exports.gl.CULL_FACE) || null;
const last_enable_depth_test = exports.gl && exports.gl.getParameter(exports.gl.DEPTH_TEST) || null;
const last_enable_scissor_test = exports.gl && exports.gl.getParameter(exports.gl.SCISSOR_TEST) || null;
// Setup desired GL state
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
const vertex_array_object = gl2 && gl2.createVertexArray() || gl_vao && gl_vao.createVertexArrayOES();
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
exports.gl && exports.gl.enable(exports.gl.BLEND);
exports.gl && exports.gl.blendEquation(exports.gl.FUNC_ADD);
exports.gl && exports.gl.blendFunc(exports.gl.SRC_ALPHA, exports.gl.ONE_MINUS_SRC_ALPHA);
exports.gl && exports.gl.disable(exports.gl.CULL_FACE);
exports.gl && exports.gl.disable(exports.gl.DEPTH_TEST);
exports.gl && exports.gl.enable(exports.gl.SCISSOR_TEST);
// glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps.
exports.gl && exports.gl.viewport(0, 0, fb_width, fb_height);
const L = draw_data.DisplayPos.x;
const R = draw_data.DisplayPos.x + draw_data.DisplaySize.x;
const T = draw_data.DisplayPos.y;
const B = draw_data.DisplayPos.y + draw_data.DisplaySize.y;
const ortho_projection = new Float32Array([
2.0 / (R - L), 0.0, 0.0, 0.0,
0.0, 2.0 / (T - B), 0.0, 0.0,
0.0, 0.0, -1.0, 0.0,
(R + L) / (L - R), (T + B) / (B - T), 0.0, 1.0,
]);
exports.gl && exports.gl.useProgram(g_ShaderHandle);
exports.gl && exports.gl.uniform1i(g_AttribLocationTex, 0);
exports.gl && g_AttribLocationProjMtx && exports.gl.uniformMatrix4fv(g_AttribLocationProjMtx, false, ortho_projection);
gl2 && gl2.bindVertexArray(vertex_array_object) || gl_vao && gl_vao.bindVertexArrayOES(vertex_array_object);
// Render command lists
exports.gl && exports.gl.bindBuffer(exports.gl.ARRAY_BUFFER, g_VboHandle);
exports.gl && exports.gl.enableVertexAttribArray(g_AttribLocationPosition);
exports.gl && exports.gl.enableVertexAttribArray(g_AttribLocationUV);
exports.gl && exports.gl.enableVertexAttribArray(g_AttribLocationColor);
exports.gl && exports.gl.vertexAttribPointer(g_AttribLocationPosition, 2, exports.gl.FLOAT, false, ImGui.DrawVertSize, ImGui.DrawVertPosOffset);
exports.gl && exports.gl.vertexAttribPointer(g_AttribLocationUV, 2, exports.gl.FLOAT, false, ImGui.DrawVertSize, ImGui.DrawVertUVOffset);
exports.gl && exports.gl.vertexAttribPointer(g_AttribLocationColor, 4, exports.gl.UNSIGNED_BYTE, true, ImGui.DrawVertSize, ImGui.DrawVertColOffset);
// Draw
const pos = draw_data.DisplayPos;
const idx_buffer_type = exports.gl && ((ImGui.DrawIdxSize === 4) ? exports.gl.UNSIGNED_INT : exports.gl.UNSIGNED_SHORT) || 0;
draw_data.IterateDrawLists((draw_list) => {
exports.gl || exports.ctx || console.log(draw_list);
exports.gl || exports.ctx || console.log("VtxBuffer.length", draw_list.VtxBuffer.length);
exports.gl || exports.ctx || console.log("IdxBuffer.length", draw_list.IdxBuffer.length);
let idx_buffer_offset = 0;
exports.gl && exports.gl.bindBuffer(exports.gl.ARRAY_BUFFER, g_VboHandle);
exports.gl && exports.gl.bufferData(exports.gl.ARRAY_BUFFER, draw_list.VtxBuffer, exports.gl.STREAM_DRAW);
exports.gl && exports.gl.bindBuffer(exports.gl.ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
exports.gl && exports.gl.bufferData(exports.gl.ELEMENT_ARRAY_BUFFER, draw_list.IdxBuffer, exports.gl.STREAM_DRAW);
draw_list.IterateDrawCmds((draw_cmd) => {
exports.gl || exports.ctx || console.log(draw_cmd);
exports.gl || exports.ctx || console.log("ElemCount", draw_cmd.ElemCount);
exports.gl || exports.ctx || console.log("ClipRect", draw_cmd.ClipRect.x, fb_height - draw_cmd.ClipRect.w, draw_cmd.ClipRect.z - draw_cmd.ClipRect.x, draw_cmd.ClipRect.w - draw_cmd.ClipRect.y);
exports.gl || exports.ctx || console.log("TextureId", draw_cmd.TextureId);
if (!exports.gl && !exports.ctx) {
console.log("i: pos.x pos.y uv.x uv.y col");
for (let i = 0; i < Math.min(3, draw_cmd.ElemCount); ++i) {
const view = new ImGui.DrawVert(draw_list.VtxBuffer.buffer, draw_list.VtxBuffer.byteOffset + i * ImGui.DrawVertSize);
console.log(`${i}: ${view.pos[0].toFixed(2)} ${view.pos[1].toFixed(2)} ${view.uv[0].toFixed(5)} ${view.uv[1].toFixed(5)} ${("00000000" + view.col[0].toString(16)).substr(-8)}`);
}
}
if (draw_cmd.UserCallback !== null) {
// User callback (registered via ImDrawList::AddCallback)
draw_cmd.UserCallback(draw_list, draw_cmd);
}
else {
const clip_rect = new ImGui.Vec4(draw_cmd.ClipRect.x - pos.x, draw_cmd.ClipRect.y - pos.y, draw_cmd.ClipRect.z - pos.x, draw_cmd.ClipRect.w - pos.y);
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0 && clip_rect.w >= 0.0) {
// Apply scissor/clipping rectangle
exports.gl && exports.gl.scissor(clip_rect.x, fb_height - clip_rect.w, clip_rect.z - clip_rect.x, clip_rect.w - clip_rect.y);
// Bind texture, Draw
exports.gl && exports.gl.activeTexture(exports.gl.TEXTURE0);
exports.gl && exports.gl.bindTexture(exports.gl.TEXTURE_2D, draw_cmd.TextureId);
exports.gl && exports.gl.drawElements(exports.gl.TRIANGLES, draw_cmd.ElemCount, idx_buffer_type, idx_buffer_offset);
if (exports.ctx) {
exports.ctx.save();
exports.ctx.beginPath();
exports.ctx.rect(clip_rect.x, clip_rect.y, clip_rect.z - clip_rect.x, clip_rect.w - clip_rect.y);
exports.ctx.clip();
const idx = ImGui.DrawIdxSize === 4 ?
new Uint32Array(draw_list.IdxBuffer.buffer, draw_list.IdxBuffer.byteOffset + idx_buffer_offset) :
new Uint16Array(draw_list.IdxBuffer.buffer, draw_list.IdxBuffer.byteOffset + idx_buffer_offset);
for (let i = 0; i < draw_cmd.ElemCount; i += 3) {
const i0 = idx[i + 0];
const i1 = idx[i + 1];
const i2 = idx[i + 2];
const v0 = new ImGui.DrawVert(draw_list.VtxBuffer.buffer, draw_list.VtxBuffer.byteOffset + i0 * ImGui.DrawVertSize);
const v1 = new ImGui.DrawVert(draw_list.VtxBuffer.buffer, draw_list.VtxBuffer.byteOffset + i1 * ImGui.DrawVertSize);
const v2 = new ImGui.DrawVert(draw_list.VtxBuffer.buffer, draw_list.VtxBuffer.byteOffset + i2 * ImGui.DrawVertSize);
const i3 = idx[i + 3];
const i4 = idx[i + 4];
const i5 = idx[i + 5];
const v3 = new ImGui.DrawVert(draw_list.VtxBuffer.buffer, draw_list.VtxBuffer.byteOffset + i3 * ImGui.DrawVertSize);
const v4 = new ImGui.DrawVert(draw_list.VtxBuffer.buffer, draw_list.VtxBuffer.byteOffset + i4 * ImGui.DrawVertSize);
const v5 = new ImGui.DrawVert(draw_list.VtxBuffer.buffer, draw_list.VtxBuffer.byteOffset + i5 * ImGui.DrawVertSize);
let quad = true;
let minmin = v0;
let minmax = v0;
let maxmin = v0;
let maxmax = v0;
for (const v of [v1, v2, v3, v4, v5]) {
let found = false;
if (v.pos[0] <= minmin.pos[0] && v.pos[1] <= minmin.pos[1]) {
minmin = v;
found = true;
}
if (v.pos[0] <= minmax.pos[0] && v.pos[1] >= minmax.pos[1]) {
minmax = v;
found = true;
}
if (v.pos[0] >= maxmin.pos[0] && v.pos[1] <= maxmin.pos[1]) {
maxmin = v;
found = true;
}
if (v.pos[0] >= maxmax.pos[0] && v.pos[1] >= maxmax.pos[1]) {
maxmax = v;
found = true;
}
if (!found) {
quad = false;
}
}
quad = quad && (minmin.pos[0] === minmax.pos[0]);
quad = quad && (maxmin.pos[0] === maxmax.pos[0]);
quad = quad && (minmin.pos[1] === maxmin.pos[1]);
quad = quad && (minmax.pos[1] === maxmax.pos[1]);
if (quad) {
if (minmin.uv[0] === maxmax.uv[0] || minmin.uv[1] === maxmax.uv[1]) {
// one vertex color
exports.ctx.beginPath();
exports.ctx.rect(minmin.pos[0], minmin.pos[1], maxmax.pos[0] - minmin.pos[0], maxmax.pos[1] - minmin.pos[1]);
exports.ctx.fillStyle = `rgba(${v0.col[0] >> 0 & 0xff}, ${v0.col[0] >> 8 & 0xff}, ${v0.col[0] >> 16 & 0xff}, ${(v0.col[0] >> 24 & 0xff) / 0xff})`;
exports.ctx.fill();
}
else {
// no vertex color
const image = draw_cmd.TextureId; // HACK
const width = image instanceof HTMLVideoElement ? image.videoWidth : image.width;
const height = image instanceof HTMLVideoElement ? image.videoHeight : image.height;
image && exports.ctx.drawImage(image, minmin.uv[0] * width, minmin.uv[1] * height, (maxmax.uv[0] - minmin.uv[0]) * width, (maxmax.uv[1] - minmin.uv[1]) * height, minmin.pos[0], minmin.pos[1], maxmax.pos[0] - minmin.pos[0], maxmax.pos[1] - minmin.pos[1]);
// ctx.beginPath();
// ctx.rect(minmin.pos[0], minmin.pos[1], maxmax.pos[0] - minmin.pos[0], maxmax.pos[1] - minmin.pos[1]);
// ctx.strokeStyle = "yellow";
// ctx.stroke();
}
i += 3;
}
else {
// one vertex color, no texture
exports.ctx.beginPath();
exports.ctx.moveTo(v0.pos[0], v0.pos[1]);
exports.ctx.lineTo(v1.pos[0], v1.pos[1]);
exports.ctx.lineTo(v2.pos[0], v2.pos[1]);
exports.ctx.closePath();
exports.ctx.fillStyle = `rgba(${v0.col[0] >> 0 & 0xff}, ${v0.col[0] >> 8 & 0xff}, ${v0.col[0] >> 16 & 0xff}, ${(v0.col[0] >> 24 & 0xff) / 0xff})`;
exports.ctx.fill();
}
}
exports.ctx.restore();
}
}
}
idx_buffer_offset += draw_cmd.ElemCount * ImGui.DrawIdxSize;
});
});
// Destroy the temporary VAO
gl2 && gl2.deleteVertexArray(vertex_array_object) || gl_vao && gl_vao.deleteVertexArrayOES(vertex_array_object);
// Restore modified GL state
exports.gl && (last_program !== null) && exports.gl.useProgram(last_program);
exports.gl && (last_texture !== null) && exports.gl.bindTexture(exports.gl.TEXTURE_2D, last_texture);
exports.gl && (last_active_texture !== null) && exports.gl.activeTexture(last_active_texture);
gl2 && gl2.bindVertexArray(last_vertex_array_object) || gl_vao && gl_vao.bindVertexArrayOES(last_vertex_array_object);
exports.gl && (last_array_buffer !== null) && exports.gl.bindBuffer(exports.gl.ARRAY_BUFFER, last_array_buffer);
exports.gl && (last_element_array_buffer !== null) && exports.gl.bindBuffer(exports.gl.ELEMENT_ARRAY_BUFFER, last_element_array_buffer);
exports.gl && (last_blend_equation_rgb !== null && last_blend_equation_alpha !== null) && exports.gl.blendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
exports.gl && (last_blend_src_rgb !== null && last_blend_src_alpha !== null && last_blend_dst_rgb !== null && last_blend_dst_alpha !== null) && exports.gl.blendFuncSeparate(last_blend_src_rgb, last_blend_src_alpha, last_blend_dst_rgb, last_blend_dst_alpha);
exports.gl && (last_enable_blend ? exports.gl.enable(exports.gl.BLEND) : exports.gl.disable(exports.gl.BLEND));
exports.gl && (last_enable_cull_face ? exports.gl.enable(exports.gl.CULL_FACE) : exports.gl.disable(exports.gl.CULL_FACE));
exports.gl && (last_enable_depth_test ? exports.gl.enable(exports.gl.DEPTH_TEST) : exports.gl.disable(exports.gl.DEPTH_TEST));
exports.gl && (last_enable_scissor_test ? exports.gl.enable(exports.gl.SCISSOR_TEST) : exports.gl.disable(exports.gl.SCISSOR_TEST));
// glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
exports.gl && (last_viewport !== null) && exports.gl.viewport(last_viewport[0], last_viewport[1], last_viewport[2], last_viewport[3]);
exports.gl && (last_scissor_box !== null) && exports.gl.scissor(last_scissor_box[0], last_scissor_box[1], last_scissor_box[2], last_scissor_box[3]);
}
function CreateFontsTexture() {
const io = ImGui.GetIO();
// Backup GL state
const last_texture = exports.gl && exports.gl.getParameter(exports.gl.TEXTURE_BINDING_2D);
// Build texture atlas
// const width: number = 256;
// const height: number = 256;
// const pixels: Uint8Array = new Uint8Array(4 * width * height).fill(0xff);
const { width, height, pixels } = io.Fonts.GetTexDataAsRGBA32(); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// console.log(`font texture ${width} x ${height} @ ${pixels.length}`);
// Upload texture to graphics system
g_FontTexture = exports.gl && exports.gl.createTexture();
exports.gl && exports.gl.bindTexture(exports.gl.TEXTURE_2D, g_FontTexture);
exports.gl && exports.gl.texParameteri(exports.gl.TEXTURE_2D, exports.gl.TEXTURE_MIN_FILTER, exports.gl.LINEAR);
exports.gl && exports.gl.texParameteri(exports.gl.TEXTURE_2D, exports.gl.TEXTURE_MAG_FILTER, exports.gl.LINEAR);
// gl && gl.pixelStorei(gl.UNPACK_ROW_LENGTH); // WebGL2
exports.gl && exports.gl.texImage2D(exports.gl.TEXTURE_2D, 0, exports.gl.RGBA, width, height, 0, exports.gl.RGBA, exports.gl.UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts.TexID = g_FontTexture || { foo: "bar" };
// console.log("font texture id", g_FontTexture);
if (exports.ctx) {
const image_canvas = document.createElement("canvas");
image_canvas.width = width;
image_canvas.height = height;
const image_ctx = image_canvas.getContext("2d");
if (image_ctx === null) {
throw new Error();
}
const image_data = image_ctx.getImageData(0, 0, width, height);
image_data.data.set(pixels);
image_ctx.putImageData(image_data, 0, 0);
io.Fonts.TexID = image_canvas;
}
// Restore modified GL state
exports.gl && last_texture && exports.gl.bindTexture(exports.gl.TEXTURE_2D, last_texture);
}
function DestroyFontsTexture() {
const io = ImGui.GetIO();
io.Fonts.TexID = null;
exports.gl && exports.gl.deleteTexture(g_FontTexture);
g_FontTexture = null;
}
function CreateDeviceObjects() {
const vertex_shader = [
"uniform mat4 ProjMtx;",
"attribute vec2 Position;",
"attribute vec2 UV;",
"attribute vec4 Color;",
"varying vec2 Frag_UV;",
"varying vec4 Frag_Color;",
"void main() {",
" Frag_UV = UV;",
" Frag_Color = Color;",
" gl_Position = ProjMtx * vec4(Position.xy,0,1);",
"}",
];
const fragment_shader = [
"precision mediump float;",
"uniform sampler2D Texture;",
"varying vec2 Frag_UV;",
"varying vec4 Frag_Color;",
"void main() {",
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV);",
"}",
];
g_ShaderHandle = exports.gl && exports.gl.createProgram();
g_VertHandle = exports.gl && exports.gl.createShader(exports.gl.VERTEX_SHADER);
g_FragHandle = exports.gl && exports.gl.createShader(exports.gl.FRAGMENT_SHADER);
exports.gl && exports.gl.shaderSource(g_VertHandle, vertex_shader.join("\n"));
exports.gl && exports.gl.shaderSource(g_FragHandle, fragment_shader.join("\n"));
exports.gl && exports.gl.compileShader(g_VertHandle);
exports.gl && exports.gl.compileShader(g_FragHandle);
exports.gl && exports.gl.attachShader(g_ShaderHandle, g_VertHandle);
exports.gl && exports.gl.attachShader(g_ShaderHandle, g_FragHandle);
exports.gl && exports.gl.linkProgram(g_ShaderHandle);
g_AttribLocationTex = exports.gl && exports.gl.getUniformLocation(g_ShaderHandle, "Texture");
g_AttribLocationProjMtx = exports.gl && exports.gl.getUniformLocation(g_ShaderHandle, "ProjMtx");
g_AttribLocationPosition = exports.gl && exports.gl.getAttribLocation(g_ShaderHandle, "Position") || 0;
g_AttribLocationUV = exports.gl && exports.gl.getAttribLocation(g_ShaderHandle, "UV") || 0;
g_AttribLocationColor = exports.gl && exports.gl.getAttribLocation(g_ShaderHandle, "Color") || 0;
g_VboHandle = exports.gl && exports.gl.createBuffer();
g_ElementsHandle = exports.gl && exports.gl.createBuffer();
CreateFontsTexture();
}
function DestroyDeviceObjects() {
DestroyFontsTexture();
exports.gl && exports.gl.deleteBuffer(g_VboHandle);
g_VboHandle = null;
exports.gl && exports.gl.deleteBuffer(g_ElementsHandle);
g_ElementsHandle = null;
g_AttribLocationTex = null;
g_AttribLocationProjMtx = null;
g_AttribLocationPosition = -1;
g_AttribLocationUV = -1;
g_AttribLocationColor = -1;
exports.gl && exports.gl.deleteProgram(g_ShaderHandle);
g_ShaderHandle = null;
exports.gl && exports.gl.deleteShader(g_VertHandle);
g_VertHandle = null;
exports.gl && exports.gl.deleteShader(g_FragHandle);
g_FragHandle = null;
}
exports.CreateDeviceObjects = CreateDeviceObjects;
exports.CreateFontsTexture = CreateFontsTexture;
exports.DestroyDeviceObjects = DestroyDeviceObjects;
exports.DestroyFontsTexture = DestroyFontsTexture;
exports.Init = Init;
exports.NewFrame = NewFrame;
exports.RenderDrawData = RenderDrawData;
exports.Shutdown = Shutdown;
Object.defineProperty(exports, '__esModule', { value: true });
})));

BIN
hw1/lib/three.js 100644

Binary file not shown.

99
hw1/src/engine.js 100644
View File

@ -0,0 +1,99 @@
var cameraPosition = [30, 30, 30]
//生成的纹理的分辨率,纹理必须是标准的尺寸 256*256 1024*1024 2048*2048
var resolution = 2048;
var fbo;
GAMES202Main();
function GAMES202Main() {
// Init canvas and gl
const canvas = document.querySelector('#glcanvas');
canvas.width = window.screen.width;
canvas.height = window.screen.height;
const gl = canvas.getContext('webgl');
if (!gl) {
alert('Unable to initialize WebGL. Your browser or machine may not support it.');
return;
}
// Add camera
const camera = new THREE.PerspectiveCamera(75, gl.canvas.clientWidth / gl.canvas.clientHeight, 1e-2, 1000);
camera.position.set(cameraPosition[0], cameraPosition[1], cameraPosition[2]);
// Add resize listener
function setSize(width, height) {
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
setSize(canvas.clientWidth, canvas.clientHeight);
window.addEventListener('resize', () => setSize(canvas.clientWidth, canvas.clientHeight));
// Add camera control
const cameraControls = new THREE.OrbitControls(camera, canvas);
cameraControls.enableZoom = true;
cameraControls.enableRotate = true;
cameraControls.enablePan = true;
cameraControls.rotateSpeed = 0.3;
cameraControls.zoomSpeed = 1.0;
cameraControls.panSpeed = 0.8;
cameraControls.target.set(0, 0, 0);
// Add renderer
const renderer = new WebGLRenderer(gl, camera);
// Add lights
// light - is open shadow map == true
let lightPos = [0, 80, 80];
let focalPoint = [0, 0, 0];
let lightUp = [0, 1, 0]
const directionLight = new DirectionalLight(5000, [1, 1, 1], lightPos, focalPoint, lightUp, true, renderer.gl);
renderer.addLight(directionLight);
// Add shapes
let floorTransform = setTransform(0, 0, -30, 4, 4, 4);
let obj1Transform = setTransform(0, 0, 0, 20, 20, 20);
let obj2Transform = setTransform(40, 0, -40, 10, 10, 10);
loadOBJ(renderer, 'assets/mary/', 'Marry', 'PhongMaterial', obj1Transform);
loadOBJ(renderer, 'assets/mary/', 'Marry', 'PhongMaterial', obj2Transform);
loadOBJ(renderer, 'assets/floor/', 'floor', 'PhongMaterial', floorTransform);
// let floorTransform = setTransform(0, 0, 0, 100, 100, 100);
// let cubeTransform = setTransform(0, 50, 0, 10, 50, 10);
// let sphereTransform = setTransform(30, 10, 0, 10, 10, 10);
//loadOBJ(renderer, 'assets/basic/', 'cube', 'PhongMaterial', cubeTransform);
// loadOBJ(renderer, 'assets/basic/', 'sphere', 'PhongMaterial', sphereTransform);
//loadOBJ(renderer, 'assets/basic/', 'plane', 'PhongMaterial', floorTransform);
function createGUI() {
const gui = new dat.gui.GUI();
// const panelModel = gui.addFolder('Model properties');
// panelModelTrans.add(GUIParams, 'x').name('X');
// panelModel.open();
}
createGUI();
function mainLoop(now) {
cameraControls.update();
renderer.render();
requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
}
function setTransform(t_x, t_y, t_z, s_x, s_y, s_z) {
return {
modelTransX: t_x,
modelTransY: t_y,
modelTransZ: t_z,
modelScaleX: s_x,
modelScaleY: s_y,
modelScaleZ: s_z,
};
}

View File

@ -0,0 +1,35 @@
class DirectionalLight {
constructor(lightIntensity, lightColor, lightPos, focalPoint, lightUp, hasShadowMap, gl) {
this.mesh = Mesh.cube(setTransform(0, 0, 0, 0.2, 0.2, 0.2, 0));
this.mat = new EmissiveMaterial(lightIntensity, lightColor);
this.lightPos = lightPos;
this.focalPoint = focalPoint;
this.lightUp = lightUp
this.hasShadowMap = hasShadowMap;
this.fbo = new FBO(gl);
if (!this.fbo) {
console.log("无法设置帧缓冲区对象");
return;
}
}
CalcLightMVP(translate, scale) {
let lightMVP = mat4.create();
let modelMatrix = mat4.create();
let viewMatrix = mat4.create();
let projectionMatrix = mat4.create();
// Model transform
// View transform
// Projection transform
mat4.multiply(lightMVP, projectionMatrix, viewMatrix);
mat4.multiply(lightMVP, lightMVP, modelMatrix);
return lightMVP;
}
}

View File

@ -0,0 +1,16 @@
class EmissiveMaterial extends Material {
constructor(lightIntensity, lightColor) {
super({
'uLigIntensity': { type: '1f', value: lightIntensity },
'uLightColor': { type: '3fv', value: lightColor }
}, [], LightCubeVertexShader, LightCubeFragmentShader);
this.intensity = lightIntensity;
this.color = lightColor;
}
GetIntensity() {
return [this.intensity * this.color[0], this.intensity * this.color[1], this.intensity * this.color[2]]
}
}

View File

@ -0,0 +1,19 @@
class PointLight {
/**
* Creates an instance of PointLight.
* @param {float} lightIntensity The intensity of the PointLight.
* @param {vec3f} lightColor The color of the PointLight.
* @memberof PointLight
*/
constructor(lightIntensity, lightColor, hasShadowMap, gl) {
this.mesh = Mesh.cube(setTransform(0, 0, 0, 0.2, 0.2, 0.2, 0));
this.mat = new EmissiveMaterial(lightIntensity, lightColor);
this.hasShadowMap = hasShadowMap;
this.fbo = new FBO(gl);
if (!this.fbo) {
console.log("无法设置帧缓冲区对象");
return;
}
}
}

View File

@ -0,0 +1,69 @@
function loadOBJ(renderer, path, name, objMaterial, transform) {
const manager = new THREE.LoadingManager();
manager.onProgress = function (item, loaded, total) {
console.log(item, loaded, total);
};
function onProgress(xhr) {
if (xhr.lengthComputable) {
const percentComplete = xhr.loaded / xhr.total * 100;
console.log('model ' + Math.round(percentComplete, 2) + '% downloaded');
}
}
function onError() { }
new THREE.MTLLoader(manager)
.setPath(path)
.load(name + '.mtl', function (materials) {
materials.preload();
new THREE.OBJLoader(manager)
.setMaterials(materials)
.setPath(path)
.load(name + '.obj', function (object) {
object.traverse(function (child) {
if (child.isMesh) {
let geo = child.geometry;
let mat;
if (Array.isArray(child.material)) mat = child.material[0];
else mat = child.material;
var indices = Array.from({ length: geo.attributes.position.count }, (v, k) => k);
let mesh = new Mesh({ name: 'aVertexPosition', array: geo.attributes.position.array },
{ name: 'aNormalPosition', array: geo.attributes.normal.array },
{ name: 'aTextureCoord', array: geo.attributes.uv.array },
indices, transform);
let colorMap = new Texture();
if (mat.map != null) {
colorMap.CreateImageTexture(renderer.gl, mat.map.image);
}
else {
colorMap.CreateConstantTexture(renderer.gl, mat.color.toArray());
}
let material, shadowMaterial;
let Translation = [transform.modelTransX, transform.modelTransY, transform.modelTransZ];
let Scale = [transform.modelScaleX, transform.modelScaleY, transform.modelScaleZ];
let light = renderer.lights[0].entity;
switch (objMaterial) {
case 'PhongMaterial':
material = buildPhongMaterial(colorMap, mat.specular.toArray(), light, Translation, Scale, "./src/shaders/phongShader/phongVertex.glsl", "./src/shaders/phongShader/phongFragment.glsl");
shadowMaterial = buildShadowMaterial(light, Translation, Scale, "./src/shaders/shadowShader/shadowVertex.glsl", "./src/shaders/shadowShader/shadowFragment.glsl");
break;
}
material.then((data) => {
let meshRender = new MeshRender(renderer.gl, mesh, data);
renderer.addMeshRender(meshRender);
});
shadowMaterial.then((data) => {
let shadowMeshRender = new MeshRender(renderer.gl, mesh, data);
renderer.addShadowMeshRender(shadowMeshRender);
});
}
});
}, onProgress, onError);
});
}

View File

@ -0,0 +1,21 @@
async function loadShaderFile(filename) {
return new Promise((resolve, reject) => {
const loader = new THREE.FileLoader();
loader.load(filename, (data) => {
resolve(data);
//console.log(data);
});
});
}
async function getShaderString(filename) {
let val = ''
await this.loadShaderFile(filename).then(result => {
val = result;
});
//console.log(val);
return val;
}

View File

@ -0,0 +1,35 @@
class Material {
#flatten_uniforms;
#flatten_attribs;
#vsSrc;
#fsSrc;
// Uniforms is a map, attribs is a Array
constructor(uniforms, attribs, vsSrc, fsSrc, frameBuffer) {
this.uniforms = uniforms;
this.attribs = attribs;
this.#vsSrc = vsSrc;
this.#fsSrc = fsSrc;
this.#flatten_uniforms = ['uViewMatrix','uModelMatrix', 'uProjectionMatrix', 'uCameraPos', 'uLightPos'];
for (let k in uniforms) {
this.#flatten_uniforms.push(k);
}
this.#flatten_attribs = attribs;
this.frameBuffer = frameBuffer;
}
setMeshAttribs(extraAttribs) {
for (let i = 0; i < extraAttribs.length; i++) {
this.#flatten_attribs.push(extraAttribs[i]);
}
}
compile(gl) {
return new Shader(gl, this.#vsSrc, this.#fsSrc,
{
uniforms: this.#flatten_uniforms,
attribs: this.#flatten_attribs
});
}
}

View File

@ -0,0 +1,28 @@
class PhongMaterial extends Material {
constructor(color, specular, light, translate, scale, vertexShader, fragmentShader) {
let lightMVP = light.CalcLightMVP(translate, scale);
let lightIntensity = light.mat.GetIntensity();
super({
// Phong
'uSampler': { type: 'texture', value: color },
'uKs': { type: '3fv', value: specular },
'uLightIntensity': { type: '3fv', value: lightIntensity },
// Shadow
'uShadowMap': { type: 'texture', value: light.fbo },
'uLightMVP': { type: 'matrix4fv', value: lightMVP },
}, [], vertexShader, fragmentShader);
}
}
async function buildPhongMaterial(color, specular, light, translate, scale, vertexPath, fragmentPath) {
let vertexShader = await getShaderString(vertexPath);
let fragmentShader = await getShaderString(fragmentPath);
return new PhongMaterial(color, specular, light, translate, scale, vertexShader, fragmentShader);
}

View File

@ -0,0 +1,20 @@
class ShadowMaterial extends Material {
constructor(light, translate, scale, vertexShader, fragmentShader) {
let lightMVP = light.CalcLightMVP(translate, scale);
super({
'uLightMVP': { type: 'matrix4fv', value: lightMVP }
}, [], vertexShader, fragmentShader, light.fbo);
}
}
async function buildShadowMaterial(light, translate, scale, vertexPath, fragmentPath) {
let vertexShader = await getShaderString(vertexPath);
let fragmentShader = await getShaderString(fragmentPath);
return new ShadowMaterial(light, translate, scale, vertexShader, fragmentShader);
}

View File

@ -0,0 +1,88 @@
class TRSTransform {
constructor(translate = [0, 0, 0], scale = [1, 1, 1]) {
this.translate = translate;
this.scale = scale;
}
}
class Mesh {
constructor(verticesAttrib, normalsAttrib, texcoordsAttrib, indices, transform) {
this.indices = indices;
this.count = indices.length;
this.hasVertices = false;
this.hasNormals = false;
this.hasTexcoords = false;
const modelTranslation = [transform.modelTransX, transform.modelTransY, transform.modelTransZ];
const modelScale = [transform.modelScaleX, transform.modelScaleY, transform.modelScaleZ];
let meshTrans = new TRSTransform(modelTranslation, modelScale);
this.transform = meshTrans;
let extraAttribs = [];
if (verticesAttrib != null) {
this.hasVertices = true;
this.vertices = verticesAttrib.array;
this.verticesName = verticesAttrib.name;
}
if (normalsAttrib != null) {
this.hasNormals = true;
this.normals = normalsAttrib.array;
this.normalsName = normalsAttrib.name;
}
if (texcoordsAttrib != null) {
this.hasTexcoords = true;
this.texcoords = texcoordsAttrib.array;
this.texcoordsName = texcoordsAttrib.name;
}
}
static cube(transform) {
const positions = [
// Front face
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
// Back face
-1.0, -1.0, -1.0,
-1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
// Top face
-1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
// Bottom face
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0, 1.0,
-1.0, -1.0, 1.0,
// Right face
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
1.0, 1.0, 1.0,
1.0, -1.0, 1.0,
// Left face
-1.0, -1.0, -1.0,
-1.0, -1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, 1.0, -1.0,
];
const indices = [
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
];
return new Mesh({ name: 'aVertexPosition', array: new Float32Array(positions) }, null, null, indices, transform);
}
}

View File

@ -0,0 +1,206 @@
class MeshRender {
#vertexBuffer;
#normalBuffer;
#texcoordBuffer;
#indicesBuffer;
constructor(gl, mesh, material) {
this.gl = gl;
this.mesh = mesh;
this.material = material;
this.#vertexBuffer = gl.createBuffer();
this.#normalBuffer = gl.createBuffer();
this.#texcoordBuffer = gl.createBuffer();
this.#indicesBuffer = gl.createBuffer();
let extraAttribs = []
if (mesh.hasVertices) {
extraAttribs.push(mesh.verticesName);
gl.bindBuffer(gl.ARRAY_BUFFER, this.#vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, mesh.vertices, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
if (mesh.hasNormals) {
extraAttribs.push(mesh.normalsName);
gl.bindBuffer(gl.ARRAY_BUFFER, this.#normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, mesh.normals, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
if (mesh.hasTexcoords) {
extraAttribs.push(mesh.texcoordsName);
gl.bindBuffer(gl.ARRAY_BUFFER, this.#texcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, mesh.texcoords, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.#indicesBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(mesh.indices), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
this.material.setMeshAttribs(extraAttribs);
this.shader = this.material.compile(gl);
}
bindGeometryInfo() {
const gl = this.gl;
if (this.mesh.hasVertices) {
const numComponents = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, this.#vertexBuffer);
gl.vertexAttribPointer(
this.shader.program.attribs[this.mesh.verticesName],
numComponents,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(
this.shader.program.attribs[this.mesh.verticesName]);
}
if (this.mesh.hasNormals) {
const numComponents = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, this.#normalBuffer);
gl.vertexAttribPointer(
this.shader.program.attribs[this.mesh.normalsName],
numComponents,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(
this.shader.program.attribs[this.mesh.normalsName]);
}
if (this.mesh.hasTexcoords) {
const numComponents = 2;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, this.#texcoordBuffer);
gl.vertexAttribPointer(
this.shader.program.attribs[this.mesh.texcoordsName],
numComponents,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(
this.shader.program.attribs[this.mesh.texcoordsName]);
}
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.#indicesBuffer);
}
bindCameraParameters(camera) {
const gl = this.gl;
let modelMatrix = mat4.create();
let viewMatrix = mat4.create();
let projectionMatrix = mat4.create();
// Model transform
mat4.identity(modelMatrix);
mat4.translate(modelMatrix, modelMatrix, this.mesh.transform.translate);
mat4.scale(modelMatrix, modelMatrix, this.mesh.transform.scale);
// View transform
camera.updateMatrixWorld();
mat4.invert(viewMatrix, camera.matrixWorld.elements);
// mat4.lookAt(viewMatrix, cameraPosition, [0,0,0], [0,1,0]);
// Projection transform
mat4.copy(projectionMatrix, camera.projectionMatrix.elements);
gl.uniformMatrix4fv(
this.shader.program.uniforms.uProjectionMatrix,
false,
projectionMatrix);
gl.uniformMatrix4fv(
this.shader.program.uniforms.uModelMatrix,
false,
modelMatrix);
gl.uniformMatrix4fv(
this.shader.program.uniforms.uViewMatrix,
false,
viewMatrix);
gl.uniform3fv(
this.shader.program.uniforms.uCameraPos,
[camera.position.x, camera.position.y, camera.position.z]);
}
bindMaterialParameters() {
const gl = this.gl;
let textureNum = 0;
for (let k in this.material.uniforms) {
if (this.material.uniforms[k].type == 'matrix4fv') {
gl.uniformMatrix4fv(
this.shader.program.uniforms[k],
false,
this.material.uniforms[k].value);
} else if (this.material.uniforms[k].type == '3fv') {
gl.uniform3fv(
this.shader.program.uniforms[k],
this.material.uniforms[k].value);
} else if (this.material.uniforms[k].type == '1f') {
gl.uniform1f(
this.shader.program.uniforms[k],
this.material.uniforms[k].value);
} else if (this.material.uniforms[k].type == '1i') {
gl.uniform1i(
this.shader.program.uniforms[k],
this.material.uniforms[k].value);
} else if (this.material.uniforms[k].type == 'texture') {
gl.activeTexture(gl.TEXTURE0 + textureNum);
gl.bindTexture(gl.TEXTURE_2D, this.material.uniforms[k].value.texture);
gl.uniform1i(this.shader.program.uniforms[k], textureNum);
textureNum += 1;
}
}
}
draw(camera) {
const gl = this.gl;
gl.bindFramebuffer(gl.FRAMEBUFFER, this.material.frameBuffer);
if (this.material.frameBuffer != null) {
// Shadow map
gl.viewport(0.0, 0.0, resolution, resolution);
} else {
gl.viewport(0.0, 0.0, window.screen.width, window.screen.height);
}
gl.useProgram(this.shader.program.glShaderProgram);
// Bind geometry information
this.bindGeometryInfo();
// Bind Camera parameters
this.bindCameraParameters(camera);
// Bind material parameters
this.bindMaterialParameters();
// Draw
{
const vertexCount = this.mesh.count;
const type = gl.UNSIGNED_SHORT;
const offset = 0;
gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);
}
}
}

View File

@ -0,0 +1,52 @@
class WebGLRenderer {
meshes = [];
shadowMeshes = [];
lights = [];
constructor(gl, camera) {
this.gl = gl;
this.camera = camera;
}
addLight(light) {
this.lights.push({
entity: light,
meshRender: new MeshRender(this.gl, light.mesh, light.mat)
});
}
addMeshRender(mesh) { this.meshes.push(mesh); }
addShadowMeshRender(mesh) { this.shadowMeshes.push(mesh); }
render() {
const gl = this.gl;
gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque
gl.clearDepth(1.0); // Clear everything
gl.enable(gl.DEPTH_TEST); // Enable depth testing
gl.depthFunc(gl.LEQUAL); // Near things obscure far things
console.assert(this.lights.length != 0, "No light");
console.assert(this.lights.length == 1, "Multiple lights");
for (let l = 0; l < this.lights.length; l++) {
// Draw light
// TODO: Support all kinds of transform
this.lights[l].meshRender.mesh.transform.translate = this.lights[l].entity.lightPos;
this.lights[l].meshRender.draw(this.camera);
// Shadow pass
if (this.lights[l].entity.hasShadowMap == true) {
for (let i = 0; i < this.shadowMeshes.length; i++) {
this.shadowMeshes[i].draw(this.camera);
}
}
// Camera pass
for (let i = 0; i < this.meshes.length; i++) {
this.gl.useProgram(this.meshes[i].shader.program.glShaderProgram);
this.gl.uniform3fv(this.meshes[i].shader.program.uniforms.uLightPos, this.lights[l].entity.lightPos);
this.meshes[i].draw(this.camera);
}
}
}
}

View File

@ -0,0 +1,28 @@
const LightCubeVertexShader = `
attribute vec3 aVertexPosition;
uniform mat4 uModelMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uProjectionMatrix;
void main(void) {
gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aVertexPosition, 1.0);
}
`;
const LightCubeFragmentShader = `
#ifdef GL_ES
precision mediump float;
#endif
uniform float uLigIntensity;
uniform vec3 uLightColor;
void main(void) {
gl_FragColor = vec4(uLightColor, 1.0);
}
`;

View File

@ -0,0 +1,62 @@
class Shader {
constructor(gl, vsSrc, fsSrc, shaderLocations) {
this.gl = gl;
const vs = this.compileShader(vsSrc, gl.VERTEX_SHADER);
const fs = this.compileShader(fsSrc, gl.FRAGMENT_SHADER);
this.program = this.addShaderLocations({
glShaderProgram: this.linkShader(vs, fs),
}, shaderLocations);
}
compileShader(shaderSource, shaderType) {
const gl = this.gl;
var shader = gl.createShader(shaderType);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error(shaderSource);
console.error('shader compiler error:\n' + gl.getShaderInfoLog(shader));
}
return shader;
};
linkShader(vs, fs) {
const gl = this.gl;
var prog = gl.createProgram();
gl.attachShader(prog, vs);
gl.attachShader(prog, fs);
gl.linkProgram(prog);
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
abort('shader linker error:\n' + gl.getProgramInfoLog(prog));
}
return prog;
};
addShaderLocations(result, shaderLocations) {
const gl = this.gl;
result.uniforms = {};
result.attribs = {};
if (shaderLocations && shaderLocations.uniforms && shaderLocations.uniforms.length) {
for (let i = 0; i < shaderLocations.uniforms.length; ++i) {
result.uniforms = Object.assign(result.uniforms, {
[shaderLocations.uniforms[i]]: gl.getUniformLocation(result.glShaderProgram, shaderLocations.uniforms[i]),
});
}
}
if (shaderLocations && shaderLocations.attribs && shaderLocations.attribs.length) {
for (let i = 0; i < shaderLocations.attribs.length; ++i) {
result.attribs = Object.assign(result.attribs, {
[shaderLocations.attribs[i]]: gl.getAttribLocation(result.glShaderProgram, shaderLocations.attribs[i]),
});
}
}
return result;
}
}

View File

@ -0,0 +1,8 @@
#ifdef GL_ES
precision mediump float;
#endif
uniform float uLigIntensity;
uniform vec3 uLightColor;
void main(void) { gl_FragColor = vec4(uLightColor, 1.0); }

View File

@ -0,0 +1,11 @@
attribute vec3 aVertexPosition;
uniform mat4 uModelMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uProjectionMatrix;
void main(void) {
gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix *
vec4(aVertexPosition, 1.0);
}

View File

@ -0,0 +1,145 @@
#ifdef GL_ES
precision mediump float;
#endif
// Phong related variables
uniform sampler2D uSampler;
uniform vec3 uKd;
uniform vec3 uKs;
uniform vec3 uLightPos;
uniform vec3 uCameraPos;
uniform vec3 uLightIntensity;
varying highp vec2 vTextureCoord;
varying highp vec3 vFragPos;
varying highp vec3 vNormal;
// Shadow map related variables
#define NUM_SAMPLES 20
#define BLOCKER_SEARCH_NUM_SAMPLES NUM_SAMPLES
#define PCF_NUM_SAMPLES NUM_SAMPLES
#define NUM_RINGS 10
#define EPS 1e-3
#define PI 3.141592653589793
#define PI2 6.283185307179586
uniform sampler2D uShadowMap;
varying vec4 vPositionFromLight;
highp float rand_1to1(highp float x ) {
// -1 -1
return fract(sin(x)*10000.0);
}
highp float rand_2to1(vec2 uv ) {
// 0 - 1
const highp float a = 12.9898, b = 78.233, c = 43758.5453;
highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );
return fract(sin(sn) * c);
}
float unpack(vec4 rgbaDepth) {
const vec4 bitShift = vec4(1.0, 1.0/256.0, 1.0/(256.0*256.0), 1.0/(256.0*256.0*256.0));
return dot(rgbaDepth, bitShift);
}
vec2 poissonDisk[NUM_SAMPLES];
void poissonDiskSamples( const in vec2 randomSeed ) {
float ANGLE_STEP = PI2 * float( NUM_RINGS ) / float( NUM_SAMPLES );
float INV_NUM_SAMPLES = 1.0 / float( NUM_SAMPLES );
float angle = rand_2to1( randomSeed ) * PI2;
float radius = INV_NUM_SAMPLES;
float radiusStep = radius;
for( int i = 0; i < NUM_SAMPLES; i ++ ) {
poissonDisk[i] = vec2( cos( angle ), sin( angle ) ) * pow( radius, 0.75 );
radius += radiusStep;
angle += ANGLE_STEP;
}
}
void uniformDiskSamples( const in vec2 randomSeed ) {
float randNum = rand_2to1(randomSeed);
float sampleX = rand_1to1( randNum ) ;
float sampleY = rand_1to1( sampleX ) ;
float angle = sampleX * PI2;
float radius = sqrt(sampleY);
for( int i = 0; i < NUM_SAMPLES; i ++ ) {
poissonDisk[i] = vec2( radius * cos(angle) , radius * sin(angle) );
sampleX = rand_1to1( sampleY ) ;
sampleY = rand_1to1( sampleX ) ;
angle = sampleX * PI2;
radius = sqrt(sampleY);
}
}
float findBlocker( sampler2D shadowMap, vec2 uv, float zReceiver ) {
return 1.0;
}
float PCF(sampler2D shadowMap, vec4 coords) {
return 1.0;
}
float PCSS(sampler2D shadowMap, vec4 coords){
// STEP 1: avgblocker depth
// STEP 2: penumbra size
// STEP 3: filtering
return 1.0;
}
float useShadowMap(sampler2D shadowMap, vec4 shadowCoord){
return 1.0;
}
vec3 blinnPhong() {
vec3 color = texture2D(uSampler, vTextureCoord).rgb;
color = pow(color, vec3(2.2));
vec3 ambient = 0.05 * color;
vec3 lightDir = normalize(uLightPos);
vec3 normal = normalize(vNormal);
float diff = max(dot(lightDir, normal), 0.0);
vec3 light_atten_coff =
uLightIntensity / pow(length(uLightPos - vFragPos), 2.0);
vec3 diffuse = diff * light_atten_coff * color;
vec3 viewDir = normalize(uCameraPos - vFragPos);
vec3 halfDir = normalize((lightDir + viewDir));
float spec = pow(max(dot(halfDir, normal), 0.0), 32.0);
vec3 specular = uKs * light_atten_coff * spec;
vec3 radiance = (ambient + diffuse + specular);
vec3 phongColor = pow(radiance, vec3(1.0 / 2.2));
return phongColor;
}
void main(void) {
float visibility;
//visibility = useShadowMap(uShadowMap, vec4(shadowCoord, 1.0));
//visibility = PCF(uShadowMap, vec4(shadowCoord, 1.0));
//visibility = PCSS(uShadowMap, vec4(shadowCoord, 1.0));
vec3 phongColor = blinnPhong();
//gl_FragColor = vec4(phongColor * visibility, 1.0);
gl_FragColor = vec4(phongColor, 1.0);
}

View File

@ -0,0 +1,25 @@
attribute vec3 aVertexPosition;
attribute vec3 aNormalPosition;
attribute vec2 aTextureCoord;
uniform mat4 uModelMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uProjectionMatrix;
uniform mat4 uLightMVP;
varying highp vec2 vTextureCoord;
varying highp vec3 vFragPos;
varying highp vec3 vNormal;
varying highp vec4 vPositionFromLight;
void main(void) {
vFragPos = (uModelMatrix * vec4(aVertexPosition, 1.0)).xyz;
vNormal = (uModelMatrix * vec4(aNormalPosition, 0.0)).xyz;
gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix *
vec4(aVertexPosition, 1.0);
vTextureCoord = aTextureCoord;
vPositionFromLight = uLightMVP * vec4(aVertexPosition, 1.0);
}

View File

@ -0,0 +1,25 @@
#ifdef GL_ES
precision mediump float;
#endif
uniform vec3 uLightPos;
uniform vec3 uCameraPos;
varying highp vec3 vNormal;
varying highp vec2 vTextureCoord;
vec4 pack (float depth) {
// 使用rgba 4字节共32位来存储z值,1个字节精度为1/256
const vec4 bitShift = vec4(1.0, 256.0, 256.0 * 256.0, 256.0 * 256.0 * 256.0);
const vec4 bitMask = vec4(1.0/256.0, 1.0/256.0, 1.0/256.0, 0.0);
// gl_FragCoord:片元的坐标,fract():返回数值的小数部分
vec4 rgbaDepth = fract(depth * bitShift); //计算每个点的z值
rgbaDepth -= rgbaDepth.gbaa * bitMask; // Cut off the value which do not fit in 8 bits
return rgbaDepth;
}
void main(){
//gl_FragColor = vec4( 1.0, 0.0, 0.0, gl_FragCoord.z);
gl_FragColor = pack(gl_FragCoord.z);
}

View File

@ -0,0 +1,19 @@
attribute vec3 aVertexPosition;
attribute vec3 aNormalPosition;
attribute vec2 aTextureCoord;
uniform mat4 uModelMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uProjectionMatrix;
uniform mat4 uLightMVP;
varying highp vec3 vNormal;
varying highp vec2 vTextureCoord;
void main(void) {
vNormal = aNormalPosition;
vTextureCoord = aTextureCoord;
gl_Position = uLightMVP * vec4(aVertexPosition, 1.0);
}

View File

@ -0,0 +1,63 @@
class FBO{
constructor(gl){
var framebuffer, texture, depthBuffer;
//定义错误函数
function error() {
if(framebuffer) gl.deleteFramebuffer(framebuffer);
if(texture) gl.deleteFramebuffer(texture);
if(depthBuffer) gl.deleteFramebuffer(depthBuffer);
return null;
}
//创建帧缓冲区对象
framebuffer = gl.createFramebuffer();
if(!framebuffer){
console.log("无法创建帧缓冲区对象");
return error();
}
//创建纹理对象并设置其尺寸和参数
texture = gl.createTexture();
if(!texture){
console.log("无法创建纹理对象");
return error();
}
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, resolution, resolution, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
framebuffer.texture = texture;//将纹理对象存入framebuffer
//创建渲染缓冲区对象并设置其尺寸和参数
depthBuffer = gl.createRenderbuffer();
if(!depthBuffer){
console.log("无法创建渲染缓冲区对象");
return error();
}
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, resolution, resolution);
//将纹理和渲染缓冲区对象关联到帧缓冲区对象上
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER,depthBuffer);
//检查帧缓冲区对象是否被正确设置
var e = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
if(gl.FRAMEBUFFER_COMPLETE !== e){
console.log("渲染缓冲区设置错误"+e.toString());
return error();
}
//取消当前的focus对象
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.bindTexture(gl.TEXTURE_2D, null);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
return framebuffer;
}
}

View File

@ -0,0 +1,85 @@
class Texture {
constructor() {}
CreateImageTexture(gl, image) {
this.texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.texture);
// Because images have to be download over the internet
// they might take a moment until they are ready.
// Until then put a single pixel in the texture so we can
// use it immediately. When the image has finished downloading
// we'll update the texture with the contents of the image.
const level = 0;
const internalFormat = gl.RGBA;
const width = 1;
const height = 1;
const border = 0;
const srcFormat = gl.RGBA;
const srcType = gl.UNSIGNED_BYTE;
const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
width, height, border, srcFormat, srcType,
pixel);
gl.bindTexture(gl.TEXTURE_2D, this.texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
srcFormat, srcType, image);
gl.bindTexture(gl.TEXTURE_2D, null);
this.CreateMipmap(gl, image.width, image.height);
}
CreateConstantTexture(gl, buffer) {
this.texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.texture);
// Because images have to be download over the internet
// they might take a moment until they are ready.
// Until then put a single pixel in the texture so we can
// use it immediately. When the image has finished downloading
// we'll update the texture with the contents of the image.
const level = 0;
const internalFormat = gl.RGB;
const width = 1;
const height = 1;
const border = 0;
const srcFormat = gl.RGB;
const srcType = gl.UNSIGNED_BYTE;
const pixel = new Uint8Array([buffer[0] * 255, buffer[1] * 255, buffer[2] * 255, 255]); // opaque blue
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
width, height, border, srcFormat, srcType,
pixel);
gl.bindTexture(gl.TEXTURE_2D, null);
this.CreateMipmap(gl, width, height);
}
CreateMipmap(gl, width, height) {
gl.bindTexture(gl.TEXTURE_2D, this.texture);
// WebGL1 has different requirements for power of 2 images
// vs non power of 2 images so check if the image is a
// power of 2 in both dimensions.
if (isPowerOf2(width) && isPowerOf2(height)) {
// Yes, it's a power of 2. Generate mips.
gl.generateMipmap(gl.TEXTURE_2D);
} else {
// No, it's not a power of 2. Turn of mips and set
// wrapping to clamp to edge
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEATE);
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEATE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
}
gl.bindTexture(gl.TEXTURE_2D, null);
}
}
function isPowerOf2(value) {
return (value & (value - 1)) == 0;
}

42
hw5/CMakeLists.txt 100644
View File

@ -0,0 +1,42 @@
cmake_minimum_required (VERSION 3.2)
project (Denoise)
set (CMAKE_CXX_STANDARD 17)
########################################
include_directories(
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/src/ext
)
file(GLOB SOURCE_FILE
${CMAKE_SOURCE_DIR}/src/*.h
${CMAKE_SOURCE_DIR}/src/*.cpp
${CMAKE_SOURCE_DIR}/src/util/*.h
${CMAKE_SOURCE_DIR}/src/util/*.cpp
${CMAKE_SOURCE_DIR}/src/ext/*/*.h
)
# Sort the file into different folders
foreach(_source_file IN ITEMS ${SOURCE_FILE})
get_filename_component(_source_path "${_source_file}" PATH)
string(REPLACE "${CMAKE_SOURCE_DIR}" "" _group_path "${_source_path}")
string(REPLACE "/" "\\" _group_path "${_group_path}")
source_group("${_group_path}" FILES "${_source_file}")
endforeach()
########################################
# OpenMP
FIND_PACKAGE(OpenMP)
if(OPENMP_FOUND)
message("OPENMP FOUND")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif()
########################################
add_executable(Denoise ${SOURCE_FILE})

21
hw5/LICENSE 100644
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 GAMES202
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
hw5/README.md 100644
View File

@ -0,0 +1 @@
# Denoise

BIN
hw5/assignment.pdf 100644

Binary file not shown.

5
hw5/build.bat 100644
View File

@ -0,0 +1,5 @@
rd /s /q build
mkdir build
cd build
cmake ..
cd ..

6
hw5/build.sh 100644
View File

@ -0,0 +1,6 @@
rm -rf build
mkdir build
cd build
cmake ..
make -j8
cd ..

View File

@ -0,0 +1,4 @@
ffmpeg -y -gamma 2.2 -r 20 -i examples\pink-room\input\beauty_%%d.exr -vcodec libx264 -pix_fmt yuv420p -preset slow -crf 18 pinkroom-input.mp4
ffmpeg -y -gamma 2.2 -r 20 -i examples\pink-room\output\result_%%d.exr -vcodec libx264 -pix_fmt yuv420p -preset slow -crf 18 pinkroom-result.mp4
ffmpeg -y -gamma 2.2 -r 20 -i examples\box\input\beauty_%%d.exr -vcodec libx264 -pix_fmt yuv420p -preset slow -crf 18 box-input.mp4
ffmpeg -y -gamma 2.2 -r 20 -i examples\box\output\result_%%d.exr -vcodec libx264 -pix_fmt yuv420p -preset slow -crf 18 box-result.mp4

View File

@ -0,0 +1,4 @@
ffmpeg -y -gamma 2.2 -r 20 -i ./examples/pink-room/input/beauty_%d.exr -vcodec libx264 -pix_fmt yuv420p -preset slow -crf 18 pinkroom-input.mp4
ffmpeg -y -gamma 2.2 -r 20 -i ./examples/pink-room/output/result_%d.exr -vcodec libx264 -pix_fmt yuv420p -preset slow -crf 18 pinkroom-result.mp4
ffmpeg -y -gamma 2.2 -r 20 -i ./examples/box/input/beauty_%d.exr -vcodec libx264 -pix_fmt yuv420p -preset slow -crf 18 box-input.mp4
ffmpeg -y -gamma 2.2 -r 20 -i ./examples/box/output/result_%d.exr -vcodec libx264 -pix_fmt yuv420p -preset slow -crf 18 box-result.mp4

View File

@ -0,0 +1,8 @@
BasedOnStyle: LLVM
AccessModifierOffset: -2
IndentCaseLabels: false
PointerBindsToType: false
IndentWidth: 4
AlwaysBreakTemplateDeclarations: true
ColumnLimit: 90

View File

@ -0,0 +1,84 @@
#include "denoiser.h"
Denoiser::Denoiser() : m_useTemportal(false) {}
void Denoiser::Reprojection(const FrameInfo &frameInfo) {
int height = m_accColor.m_height;
int width = m_accColor.m_width;
Matrix4x4 preWorldToScreen =
m_preFrameInfo.m_matrix[m_preFrameInfo.m_matrix.size() - 1];
Matrix4x4 preWorldToCamera =
m_preFrameInfo.m_matrix[m_preFrameInfo.m_matrix.size() - 2];
#pragma omp parallel for
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// TODO: Reproject
m_valid(x, y) = false;
m_misc(x, y) = Float3(0.f);
}
}
std::swap(m_misc, m_accColor);
}
void Denoiser::TemporalAccumulation(const Buffer2D<Float3> &curFilteredColor) {
int height = m_accColor.m_height;
int width = m_accColor.m_width;
int kernelRadius = 3;
#pragma omp parallel for
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// TODO: Temporal clamp
Float3 color = m_accColor(x, y);
// TODO: Exponential moving average
float alpha = 1.0f;
m_misc(x, y) = Lerp(color, curFilteredColor(x, y), alpha);
}
}
std::swap(m_misc, m_accColor);
}
Buffer2D<Float3> Denoiser::Filter(const FrameInfo &frameInfo) {
int height = frameInfo.m_beauty.m_height;
int width = frameInfo.m_beauty.m_width;
Buffer2D<Float3> filteredImage = CreateBuffer2D<Float3>(width, height);
int kernelRadius = 16;
#pragma omp parallel for
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// TODO: Joint bilateral filter
filteredImage(x, y) = frameInfo.m_beauty(x, y);
}
}
return filteredImage;
}
void Denoiser::Init(const FrameInfo &frameInfo, const Buffer2D<Float3> &filteredColor) {
m_accColor.Copy(filteredColor);
int height = m_accColor.m_height;
int width = m_accColor.m_width;
m_misc = CreateBuffer2D<Float3>(width, height);
m_valid = CreateBuffer2D<bool>(width, height);
}
void Denoiser::Maintain(const FrameInfo &frameInfo) { m_preFrameInfo = frameInfo; }
Buffer2D<Float3> Denoiser::ProcessFrame(const FrameInfo &frameInfo) {
// Filter current frame
Buffer2D<Float3> filteredColor;
filteredColor = Filter(frameInfo);
// Reproject previous frame color to current
if (m_useTemportal) {
Reprojection(frameInfo);
TemporalAccumulation(filteredColor);
} else {
Init(frameInfo, filteredColor);
}
// Maintain
Maintain(frameInfo);
if (!m_useTemportal) {
m_useTemportal = true;
}
return m_accColor;
}

47
hw5/src/denoiser.h 100644
View File

@ -0,0 +1,47 @@
#pragma once
#define NOMINMAX
#include <string>
#include "filesystem/path.h"
#include "util/image.h"
#include "util/mathutil.h"
struct FrameInfo {
public:
Buffer2D<Float3> m_beauty;
Buffer2D<float> m_depth;
Buffer2D<Float3> m_normal;
Buffer2D<Float3> m_position;
Buffer2D<float> m_id;
std::vector<Matrix4x4> m_matrix;
};
class Denoiser {
public:
Denoiser();
void Init(const FrameInfo &frameInfo, const Buffer2D<Float3> &filteredColor);
void Maintain(const FrameInfo &frameInfo);
void Reprojection(const FrameInfo &frameInfo);
void TemporalAccumulation(const Buffer2D<Float3> &curFilteredColor);
Buffer2D<Float3> Filter(const FrameInfo &frameInfo);
Buffer2D<Float3> ProcessFrame(const FrameInfo &frameInfo);
public:
FrameInfo m_preFrameInfo;
Buffer2D<Float3> m_accColor;
Buffer2D<Float3> m_misc;
Buffer2D<bool> m_valid;
bool m_useTemportal;
float m_alpha = 0.2f;
float m_sigmaPlane = 0.1f;
float m_sigmaColor = 0.6f;
float m_sigmaNormal = 0.1f;
float m_sigmaCoord = 32.0f;
float m_colorBoxK = 1.0f;
};

View File

@ -0,0 +1,24 @@
/*
fwd.h -- Forward declarations for path.h and resolver.h
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#pragma once
#if !defined(NAMESPACE_BEGIN)
#define NAMESPACE_BEGIN(name) namespace name {
#endif
#if !defined(NAMESPACE_END)
#define NAMESPACE_END(name) }
#endif
NAMESPACE_BEGIN(filesystem)
class path;
class resolver;
NAMESPACE_END(filesystem)

View File

@ -0,0 +1,438 @@
/*
path.h -- A simple class for manipulating paths on Linux/Windows/Mac OS
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#pragma once
#include "fwd.h"
#include <algorithm>
#include <string>
#include <vector>
#include <stdexcept>
#include <sstream>
#include <cctype>
#include <cstdlib>
#include <cerrno>
#include <cstring>
#if defined(_WIN32)
# include <windows.h>
# include <ShlObj.h>
#else
# include <unistd.h>
#endif
#include <sys/stat.h>
#if defined(__linux)
# include <linux/limits.h>
#endif
#if defined(__APPLE__)
# include <sys/syslimits.h>
#endif
NAMESPACE_BEGIN(filesystem)
/**
* \brief Simple class for manipulating paths on Linux/Windows/Mac OS
*
* This class is just a temporary workaround to avoid the heavy boost
* dependency until boost::filesystem is integrated into the standard template
* library at some point in the future.
*/
class path {
public:
enum path_type {
windows_path = 0,
posix_path = 1,
#if defined(_WIN32)
native_path = windows_path
#else
native_path = posix_path
#endif
};
path() : m_type(native_path), m_absolute(false), m_smb(false) { }
path(const path &path)
: m_type(path.m_type), m_path(path.m_path), m_absolute(path.m_absolute), m_smb(path.m_smb) {}
path(path &&path)
: m_type(path.m_type), m_path(std::move(path.m_path)),
m_absolute(path.m_absolute), m_smb(path.m_smb) {}
path(const char *string) { set(string); }
path(const std::string &string) { set(string); }
#if defined(_WIN32)
path(const std::wstring &wstring) { set(wstring); }
path(const wchar_t *wstring) { set(wstring); }
#endif
size_t length() const { return m_path.size(); }
bool empty() const { return m_path.empty(); }
bool is_absolute() const { return m_absolute; }
path make_absolute() const {
#if !defined(_WIN32)
char temp[PATH_MAX];
if (realpath(str().c_str(), temp) == NULL)
throw std::runtime_error("Internal error in realpath(): " + std::string(strerror(errno)));
return path(temp);
#else
std::wstring value = wstr(), out(MAX_PATH_WINDOWS, '\0');
DWORD length = GetFullPathNameW(value.c_str(), MAX_PATH_WINDOWS, &out[0], NULL);
if (length == 0)
throw std::runtime_error("Internal error in realpath(): " + std::to_string(GetLastError()));
return path(out.substr(0, length));
#endif
}
bool exists() const {
#if defined(_WIN32)
return GetFileAttributesW(wstr().c_str()) != INVALID_FILE_ATTRIBUTES;
#else
struct stat sb;
return stat(str().c_str(), &sb) == 0;
#endif
}
size_t file_size() const {
#if defined(_WIN32)
struct _stati64 sb;
if (_wstati64(wstr().c_str(), &sb) != 0)
throw std::runtime_error("path::file_size(): cannot stat file \"" + str() + "\"!");
#else
struct stat sb;
if (stat(str().c_str(), &sb) != 0)
throw std::runtime_error("path::file_size(): cannot stat file \"" + str() + "\"!");
#endif
return (size_t) sb.st_size;
}
bool is_directory() const {
#if defined(_WIN32)
DWORD result = GetFileAttributesW(wstr().c_str());
if (result == INVALID_FILE_ATTRIBUTES)
return false;
return (result & FILE_ATTRIBUTE_DIRECTORY) != 0;
#else
struct stat sb;
if (stat(str().c_str(), &sb))
return false;
return S_ISDIR(sb.st_mode);
#endif
}
bool is_file() const {
#if defined(_WIN32)
DWORD attr = GetFileAttributesW(wstr().c_str());
return (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0);
#else
struct stat sb;
if (stat(str().c_str(), &sb))
return false;
return S_ISREG(sb.st_mode);
#endif
}
std::string extension() const {
const std::string &name = filename();
size_t pos = name.find_last_of(".");
if (pos == std::string::npos)
return "";
return name.substr(pos+1);
}
std::string filename() const {
if (empty())
return "";
const std::string &last = m_path[m_path.size()-1];
return last;
}
path parent_path() const {
path result;
result.m_absolute = m_absolute;
result.m_smb = m_smb;
if (m_path.empty()) {
if (!m_absolute)
result.m_path.push_back("..");
} else {
size_t until = m_path.size() - 1;
for (size_t i = 0; i < until; ++i)
result.m_path.push_back(m_path[i]);
}
return result;
}
path operator/(const path &other) const {
if (other.m_absolute)
throw std::runtime_error("path::operator/(): expected a relative path!");
if (m_type != other.m_type)
throw std::runtime_error("path::operator/(): expected a path of the same type!");
path result(*this);
for (size_t i=0; i<other.m_path.size(); ++i)
result.m_path.push_back(other.m_path[i]);
return result;
}
std::string str(path_type type = native_path) const {
std::ostringstream oss;
if (m_absolute) {
if (m_type == posix_path)
oss << "/";
else {
size_t length = 0;
for (size_t i = 0; i < m_path.size(); ++i)
// No special case for the last segment to count the NULL character
length += m_path[i].length() + 1;
if (m_smb)
length += 2;
// Windows requires a \\?\ prefix to handle paths longer than MAX_PATH
// (including their null character). NOTE: relative paths >MAX_PATH are
// not supported at all in Windows.
if (length > MAX_PATH_WINDOWS_LEGACY) {
if (m_smb)
oss << "\\\\?\\UNC\\";
else
oss << "\\\\?\\";
} else if (m_smb)
oss << "\\\\";
}
}
for (size_t i=0; i<m_path.size(); ++i) {
oss << m_path[i];
if (i+1 < m_path.size()) {
if (type == posix_path)
oss << '/';
else
oss << '\\';
}
}
return oss.str();
}
void set(const std::string &str, path_type type = native_path) {
m_type = type;
if (type == windows_path) {
std::string tmp = str;
// Long windows paths (sometimes) begin with the prefix \\?\. It should only
// be used when the path is >MAX_PATH characters long, so we remove it
// for convenience and add it back (if necessary) in str()/wstr().
static const std::string LONG_PATH_PREFIX = "\\\\?\\";
if (tmp.length() >= LONG_PATH_PREFIX.length()
&& std::mismatch(std::begin(LONG_PATH_PREFIX), std::end(LONG_PATH_PREFIX), std::begin(tmp)).first == std::end(LONG_PATH_PREFIX)) {
tmp.erase(0, LONG_PATH_PREFIX.length());
}
// Special-case handling of absolute SMB paths, which start with the prefix "\\".
if (tmp.length() >= 2 && tmp[0] == '\\' && tmp[1] == '\\') {
m_path = {};
tmp.erase(0, 2);
// Interestingly, there is a special-special case where relative paths may be specified as beginning with a "\\"
// when a non-SMB file with a more-than-260-characters-long absolute _local_ path is double-clicked. This seems to
// only happen with single-segment relative paths, so we can check for this condition by making sure no further
// path separators are present.
if (tmp.find_first_of("/\\") != std::string::npos)
m_absolute = m_smb = true;
else
m_absolute = m_smb = false;
// Special-case handling of absolute SMB paths, which start with the prefix "UNC\"
} else if (tmp.length() >= 4 && tmp[0] == 'U' && tmp[1] == 'N' && tmp[2] == 'C' && tmp[3] == '\\') {
m_path = {};
tmp.erase(0, 4);
m_absolute = true;
m_smb = true;
// Special-case handling of absolute local paths, which start with the drive letter and a colon "X:\"
} else if (tmp.length() >= 3 && std::isalpha(tmp[0]) && tmp[1] == ':' && (tmp[2] == '\\' || tmp[2] == '/')) {
m_path = {tmp.substr(0, 2)};
tmp.erase(0, 3);
m_absolute = true;
m_smb = false;
// Relative path
} else {
m_path = {};
m_absolute = false;
m_smb = false;
}
std::vector<std::string> tokenized = tokenize(tmp, "/\\");
m_path.insert(std::end(m_path), std::begin(tokenized), std::end(tokenized));
} else {
m_path = tokenize(str, "/");
m_absolute = !str.empty() && str[0] == '/';
}
}
path &operator=(const path &path) {
m_type = path.m_type;
m_path = path.m_path;
m_absolute = path.m_absolute;
m_smb = path.m_smb;
return *this;
}
path &operator=(path &&path) {
if (this != &path) {
m_type = path.m_type;
m_path = std::move(path.m_path);
m_absolute = path.m_absolute;
m_smb = path.m_smb;
}
return *this;
}
friend std::ostream &operator<<(std::ostream &os, const path &path) {
os << path.str();
return os;
}
bool remove_file() {
#if !defined(_WIN32)
return std::remove(str().c_str()) == 0;
#else
return DeleteFileW(wstr().c_str()) != 0;
#endif
}
bool resize_file(size_t target_length) {
#if !defined(_WIN32)
return ::truncate(str().c_str(), (off_t) target_length) == 0;
#else
HANDLE handle = CreateFileW(wstr().c_str(), GENERIC_WRITE, 0, nullptr, 0, FILE_ATTRIBUTE_NORMAL, nullptr);
if (handle == INVALID_HANDLE_VALUE)
return false;
LARGE_INTEGER size;
size.QuadPart = (LONGLONG) target_length;
if (SetFilePointerEx(handle, size, NULL, FILE_BEGIN) == 0) {
CloseHandle(handle);
return false;
}
if (SetEndOfFile(handle) == 0) {
CloseHandle(handle);
return false;
}
CloseHandle(handle);
return true;
#endif
}
static path getcwd() {
#if !defined(_WIN32)
char temp[PATH_MAX];
if (::getcwd(temp, PATH_MAX) == NULL)
throw std::runtime_error("Internal error in getcwd(): " + std::string(strerror(errno)));
return path(temp);
#else
std::wstring temp(MAX_PATH_WINDOWS, '\0');
if (!_wgetcwd(&temp[0], MAX_PATH_WINDOWS))
throw std::runtime_error("Internal error in getcwd(): " + std::to_string(GetLastError()));
return path(temp.c_str());
#endif
}
#if defined(_WIN32)
std::wstring wstr(path_type type = native_path) const {
std::string temp = str(type);
int size = MultiByteToWideChar(CP_UTF8, 0, &temp[0], (int)temp.size(), NULL, 0);
std::wstring result(size, 0);
MultiByteToWideChar(CP_UTF8, 0, &temp[0], (int)temp.size(), &result[0], size);
return result;
}
void set(const std::wstring &wstring, path_type type = native_path) {
std::string string;
if (!wstring.empty()) {
int size = WideCharToMultiByte(CP_UTF8, 0, &wstring[0], (int)wstring.size(),
NULL, 0, NULL, NULL);
string.resize(size, 0);
WideCharToMultiByte(CP_UTF8, 0, &wstring[0], (int)wstring.size(),
&string[0], size, NULL, NULL);
}
set(string, type);
}
path &operator=(const std::wstring &str) { set(str); return *this; }
#endif
bool operator==(const path &p) const { return p.m_path == m_path; }
bool operator!=(const path &p) const { return p.m_path != m_path; }
protected:
static std::vector<std::string> tokenize(const std::string &string, const std::string &delim) {
std::string::size_type lastPos = 0, pos = string.find_first_of(delim, lastPos);
std::vector<std::string> tokens;
while (lastPos != std::string::npos) {
if (pos != lastPos)
tokens.push_back(string.substr(lastPos, pos - lastPos));
lastPos = pos;
if (lastPos == std::string::npos || lastPos + 1 == string.length())
break;
pos = string.find_first_of(delim, ++lastPos);
}
return tokens;
}
protected:
#if defined(_WIN32)
static const size_t MAX_PATH_WINDOWS = 32767;
#endif
static const size_t MAX_PATH_WINDOWS_LEGACY = 260;
path_type m_type;
std::vector<std::string> m_path;
bool m_absolute;
bool m_smb; // Unused, except for on Windows
};
inline bool create_directory(const path& p) {
#if defined(_WIN32)
return CreateDirectoryW(p.wstr().c_str(), NULL) != 0;
#else
return mkdir(p.str().c_str(), S_IRWXU) == 0;
#endif
}
inline bool create_directories(const path& p) {
#if defined(_WIN32)
return SHCreateDirectory(nullptr, p.make_absolute().wstr().c_str()) == ERROR_SUCCESS;
#else
if (create_directory(p.str().c_str()))
return true;
if (p.empty())
return false;
if (errno == ENOENT) {
if (create_directory(p.parent_path()))
return mkdir(p.str().c_str(), S_IRWXU) == 0;
else
return false;
}
return false;
#endif
}
NAMESPACE_END(filesystem)

View File

@ -0,0 +1,72 @@
/*
resolver.h -- A simple class for cross-platform path resolution
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#pragma once
#include "path.h"
NAMESPACE_BEGIN(filesystem)
/**
* \brief Simple class for resolving paths on Linux/Windows/Mac OS
*
* This convenience class looks for a file or directory given its name
* and a set of search paths. The implementation walks through the
* search paths in order and stops once the file is found.
*/
class resolver {
public:
typedef std::vector<path>::iterator iterator;
typedef std::vector<path>::const_iterator const_iterator;
resolver() {
m_paths.push_back(path::getcwd());
}
size_t size() const { return m_paths.size(); }
iterator begin() { return m_paths.begin(); }
iterator end() { return m_paths.end(); }
const_iterator begin() const { return m_paths.begin(); }
const_iterator end() const { return m_paths.end(); }
void erase(iterator it) { m_paths.erase(it); }
void prepend(const path &path) { m_paths.insert(m_paths.begin(), path); }
void append(const path &path) { m_paths.push_back(path); }
const path &operator[](size_t index) const { return m_paths[index]; }
path &operator[](size_t index) { return m_paths[index]; }
path resolve(const path &value) const {
for (const_iterator it = m_paths.begin(); it != m_paths.end(); ++it) {
path combined = *it / value;
if (combined.exists())
return combined;
}
return value;
}
friend std::ostream &operator<<(std::ostream &os, const resolver &r) {
os << "resolver[" << std::endl;
for (size_t i = 0; i < r.m_paths.size(); ++i) {
os << " \"" << r.m_paths[i] << "\"";
if (i + 1 < r.m_paths.size())
os << ",";
os << std::endl;
}
os << "]";
return os;
}
private:
std::vector<path> m_paths;
};
NAMESPACE_END(filesystem)

Binary file not shown.

68
hw5/src/main.cpp 100644
View File

@ -0,0 +1,68 @@
#include <fstream>
#include <string>
#include "denoiser.h"
#include "util/image.h"
#include "util/mathutil.h"
std::vector<Matrix4x4> ReadMatrix(const std::string &filename) {
std::ifstream is;
is.open(filename, std::ios::binary);
CHECK(is.is_open());
int shapeNum;
is.read(reinterpret_cast<char *>(&shapeNum), sizeof(int));
std::vector<Matrix4x4> matrix(shapeNum + 2);
for (int i = 0; i < shapeNum + 2; i++) {
is.read(reinterpret_cast<char *>(&matrix[i]), sizeof(Matrix4x4));
}
is.close();
return matrix;
}
FrameInfo LoadFrameInfo(const filesystem::path &inputDir, const int &idx) {
Buffer2D<Float3> beauty =
ReadFloat3Image((inputDir / ("beauty_" + std::to_string(idx) + ".exr")).str());
Buffer2D<Float3> normal =
ReadFloat3Image((inputDir / ("normal_" + std::to_string(idx) + ".exr")).str());
Buffer2D<Float3> position =
ReadFloat3Image((inputDir / ("position_" + std::to_string(idx) + ".exr")).str());
Buffer2D<float> depth =
ReadFloatImage((inputDir / ("depth_" + std::to_string(idx) + ".exr")).str());
Buffer2D<float> id =
ReadFloatImage((inputDir / ("ID_" + std::to_string(idx) + ".exr")).str());
std::vector<Matrix4x4> matrix =
ReadMatrix((inputDir / ("matrix_" + std::to_string(idx) + ".mat")).str());
FrameInfo frameInfo = {beauty, depth, normal, position, id, matrix};
return frameInfo;
}
void Denoise(const filesystem::path &inputDir, const filesystem::path &outputDir,
const int &frameNum) {
Denoiser denoiser;
for (int i = 0; i < frameNum; i++) {
std::cout << "Frame: " << i << std::endl;
FrameInfo frameInfo = LoadFrameInfo(inputDir, i);
Buffer2D<Float3> image = denoiser.ProcessFrame(frameInfo);
std::string filename =
(outputDir / ("result_" + std::to_string(i) + ".exr")).str();
WriteFloat3Image(image, filename);
}
}
int main() {
// Box
filesystem::path inputDir("examples/box/input");
filesystem::path outputDir("examples/box/output");
int frameNum = 20;
/*
// Pink room
filesystem::path inputDir("examples/pink-room/input");
filesystem::path outputDir("examples/pink-room/output");
int frameNum = 80;
*/
Denoise(inputDir, outputDir, frameNum);
return 0;
}

View File

@ -0,0 +1,78 @@
#pragma once
#include <cstring>
#include <memory>
#include "common.h"
template <typename T>
class Buffer {
public:
Buffer(T *buffer, const int &size);
virtual void Copy(const Buffer<T> &buffer);
int m_size;
std::shared_ptr<T[]> m_buffer = nullptr;
};
template <typename T>
inline Buffer<T>::Buffer(T *buffer, const int &size) : m_buffer(buffer), m_size(size) {}
template <typename T>
inline void Buffer<T>::Copy(const Buffer<T> &buffer) {
if (m_buffer == buffer.m_buffer) {
return;
}
m_size = buffer.m_size;
m_buffer = std::shared_ptr<T[]>(new T[m_size]);
std::memcpy(m_buffer.get(), buffer.m_buffer.get(), sizeof(T) * m_size);
}
template <typename T>
class Buffer2D : public Buffer<T> {
public:
Buffer2D();
Buffer2D(T *buffer, const int &width, const int &height);
void Copy(const Buffer2D<T> &buffer);
T operator()(const int &x, const int &y) const;
T &operator()(const int &x, const int &y);
int m_width, m_height;
};
template <typename T>
inline Buffer2D<T>::Buffer2D() : Buffer<T>(nullptr, 0), m_width(0), m_height(0) {}
template <typename T>
inline Buffer2D<T>::Buffer2D(T *buffer, const int &width, const int &height)
: Buffer<T>(buffer, width * height), m_width(width), m_height(height) {}
template <typename T>
inline void Buffer2D<T>::Copy(const Buffer2D<T> &buffer) {
Buffer<T>::Copy(buffer);
m_width = buffer.m_width;
m_height = buffer.m_height;
}
template <typename T>
inline T &Buffer2D<T>::operator()(const int &x, const int &y) {
CHECK(0 <= x && x < m_width && 0 <= y && y < m_height);
return this->m_buffer[y * m_width + x];
}
template <typename T>
inline T Buffer2D<T>::operator()(const int &x, const int &y) const {
if (0 <= x && x < m_width && 0 <= y && y < m_height) {
return this->m_buffer[y * m_width + x];
} else {
return T(0.0);
}
}
template <typename T>
inline Buffer2D<T> CreateBuffer2D(const int &width, const int &height) {
T *buffer = new T[width * height];
return Buffer2D<T>(buffer, width, height);
}

View File

@ -0,0 +1,17 @@
#pragma once
#define NOMINMAX
#include <iostream>
#define LOG(msg) \
std::cout << "[" << __FILE__ << ", " << __FUNCTION__ << ", " << __LINE__ \
<< "]: " << msg << std::endl;
#define CHECK(cond) \
do { \
if (!(cond)) { \
LOG("Runtime Error."); \
exit(-1); \
} \
} while (false)

View File

@ -0,0 +1,51 @@
#include "image.h"
Buffer2D<float> ReadFloatImage(const std::string &filename) {
int width, height;
float *buffer = ReadImage(filename, width, height, 1);
CHECK(buffer != nullptr);
return Buffer2D<float>(buffer, width, height);
}
Buffer2D<float> ReadFloatImageLayer(const std::string &filename,
const std::string &layername) {
int width, height;
float *buffer = ReadImageLayer(filename, layername, width, height, 1);
CHECK(buffer != nullptr);
return Buffer2D<float>(buffer, width, height);
}
Buffer2D<Float3> ReadFloat3Image(const std::string &filename) {
int width, height;
float *_buffer = ReadImage(filename, width, height, 3);
CHECK(_buffer != nullptr);
Float3 *buffer = new Float3[width * height];
for (int i = 0; i < width * height; i++) {
buffer[i] = Float3(_buffer[i * 3 + 0], _buffer[i * 3 + 1], _buffer[i * 3 + 2]);
}
delete[] _buffer;
return Buffer2D<Float3>(buffer, width, height);
}
Buffer2D<Float3> ReadFloat3ImageLayer(const std::string &filename,
const std::string &layername) {
int width, height;
float *_buffer = ReadImageLayer(filename, layername, width, height, 3);
CHECK(_buffer != nullptr);
Float3 *buffer = new Float3[width * height];
for (int i = 0; i < width * height; i++) {
buffer[i] = Float3(_buffer[i * 3 + 0], _buffer[i * 3 + 1], _buffer[i * 3 + 2]);
}
delete[] _buffer;
return Buffer2D<Float3>(buffer, width, height);
}
void WriteFloatImage(const Buffer2D<float> &imageBuffer, const std::string &filename) {
WriteImage(filename, imageBuffer.m_width, imageBuffer.m_height, 1,
(float *)imageBuffer.m_buffer.get());
}
void WriteFloat3Image(const Buffer2D<Float3> &imageBuffer, const std::string &filename) {
WriteImage(filename, imageBuffer.m_width, imageBuffer.m_height, 3,
(float *)imageBuffer.m_buffer.get());
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "buffer.h"
#include "imageutil.h"
#include "mathutil.h"
Buffer2D<float> ReadFloatImage(const std::string &filename);
Buffer2D<float> ReadFloatImageLayer(const std::string &filename,
const std::string &layername);
Buffer2D<Float3> ReadFloat3Image(const std::string &filename);
Buffer2D<Float3> ReadFloat3ImageLayer(const std::string &filename,
const std::string &layername);
void WriteFloatImage(const Buffer2D<float> &imageBuffer, const std::string &filename);
void WriteFloat3Image(const Buffer2D<Float3> &imageBuffer, const std::string &filename);

View File

@ -0,0 +1,148 @@
#include "imageutil.h"
#include "common.h"
#define TINYEXR_IMPLEMENTATION
#include "tinyexr/tinyexr.h"
std::string GetExtension(const std::string &s) {
size_t pos = s.rfind('.');
CHECK(pos != std::string::npos);
std::string ext = s.substr(pos + 1);
return ext;
}
float *ReadImage(const std::string &filename, int &width, int &height,
const int &channel) {
CHECK(channel == 1 || channel == 3);
std::string ext = GetExtension(filename);
CHECK(ext == "exr");
float *out; // width * height * RGBA
const char *err = nullptr;
int ret = LoadEXR(&out, &width, &height, filename.c_str(), &err);
float *buffer = new float[width * height * channel];
if (ret != TINYEXR_SUCCESS) {
if (err) {
fprintf(stderr, "ERR : %s\n", err);
FreeEXRErrorMessage(err); // release memory of error message.
delete[] buffer;
buffer = nullptr;
}
} else {
for (int i = 0; i < width * height; i++) {
for (int j = 0; j < channel; j++) {
buffer[i * channel + j] = out[i * 4 + j];
}
}
delete[] out; // release memory of image data
}
return buffer;
}
float *ReadImageLayer(const std::string &filename, const std::string &layername,
int &width, int &height, const int &channel) {
CHECK(channel == 1 || channel == 3);
std::string ext = GetExtension(filename);
CHECK(ext == "exr");
float *out; // width * height * RGBA
const char *err = nullptr;
int ret = LoadEXRWithLayer(&out, &width, &height, filename.c_str(), layername.c_str(),
&err);
float *buffer = new float[width * height * channel];
if (ret != TINYEXR_SUCCESS) {
if (err) {
fprintf(stderr, "ERR : %s\n", err);
FreeEXRErrorMessage(err); // release memory of error message.
delete[] buffer;
buffer = nullptr;
}
} else {
for (int i = 0; i < width * height; i++) {
for (int j = 0; j < channel; j++) {
buffer[i * channel + j] = out[i * 4 + j];
}
}
delete[] out; // release memory of image data
}
return buffer;
}
bool WriteImage(const std::string &filename, const int &width, const int &height,
const int &channel, const float *buffer) {
CHECK(channel == 1 || channel == 3);
EXRHeader header;
InitEXRHeader(&header);
EXRImage image;
InitEXRImage(&image);
image.num_channels = channel;
std::vector<float> images[3];
for (int i = 0; i < channel; i++) {
images[i].resize(width * height);
}
// Split RGBRGBRGB... into R, G and B layer
for (int i = 0; i < width * height; i++) {
for (int j = 0; j < channel; j++) {
images[j][i] = buffer[channel * i + j];
}
}
float *image_ptr[3];
if (channel == 3) {
image_ptr[0] = &(images[2].at(0)); // B
image_ptr[1] = &(images[1].at(0)); // G
image_ptr[2] = &(images[0].at(0)); // R
} else if (channel == 1) {
image_ptr[0] = &(images[0].at(0)); // Y
image_ptr[1] = nullptr;
image_ptr[2] = nullptr;
}
image.images = (unsigned char **)image_ptr;
image.width = width;
image.height = height;
header.num_channels = channel;
header.channels =
(EXRChannelInfo *)malloc(sizeof(EXRChannelInfo) * header.num_channels);
if (header.num_channels == 3) {
// Must be (A)BGR order, since most of EXR viewers expect this
// channel order.
strncpy(header.channels[0].name, "B", 255);
header.channels[0].name[strlen("B")] = '\0';
strncpy(header.channels[1].name, "G", 255);
header.channels[1].name[strlen("G")] = '\0';
strncpy(header.channels[2].name, "R", 255);
header.channels[2].name[strlen("R")] = '\0';
} else if (header.num_channels == 1) {
strncpy(header.channels[0].name, "Y", 255);
header.channels[0].name[strlen("Y")] = '\0';
}
header.pixel_types = (int *)malloc(sizeof(int) * header.num_channels);
header.requested_pixel_types = (int *)malloc(sizeof(int) * header.num_channels);
for (int i = 0; i < header.num_channels; i++) {
header.pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image
header.requested_pixel_types[i] =
TINYEXR_PIXELTYPE_HALF; // pixel type of output image to be
// stored in .EXR
}
const char *err = nullptr;
int ret = SaveEXRImageToFile(&image, &header, filename.c_str(), &err);
if (ret != TINYEXR_SUCCESS) {
fprintf(stderr, "Save EXR err: %s\n", err);
FreeEXRErrorMessage(err); // free's buffer for an error message
return ret;
}
free(header.channels);
free(header.pixel_types);
free(header.requested_pixel_types);
return true;
}

View File

@ -0,0 +1,12 @@
#pragma once
#include <string>
float *ReadImage(const std::string &filename, int &width, int &height,
const int &channel);
float *ReadImageLayer(const std::string &filename, const std::string &layername,
int &width, int &height, const int &channel);
bool WriteImage(const std::string &filename, const int &width, const int &height,
const int &channel, const float *buffer);

View File

@ -0,0 +1,133 @@
#include "mathutil.h"
Matrix4x4 Inverse(const Matrix4x4 &mat) {
float inv[4][4];
inv[0][0] = +mat.m[1][1] * mat.m[2][2] * mat.m[3][3] +
mat.m[1][2] * mat.m[2][3] * mat.m[3][1] +
mat.m[1][3] * mat.m[2][1] * mat.m[3][2] -
mat.m[1][1] * mat.m[2][3] * mat.m[3][2] -
mat.m[1][2] * mat.m[2][1] * mat.m[3][3] -
mat.m[1][3] * mat.m[2][2] * mat.m[3][1];
inv[1][0] = -mat.m[1][0] * mat.m[2][2] * mat.m[3][3] -
mat.m[1][2] * mat.m[2][3] * mat.m[3][0] -
mat.m[1][3] * mat.m[2][0] * mat.m[3][2] +
mat.m[1][0] * mat.m[2][3] * mat.m[3][2] +
mat.m[1][2] * mat.m[2][0] * mat.m[3][3] +
mat.m[1][3] * mat.m[2][2] * mat.m[3][0];
inv[2][0] = +mat.m[1][0] * mat.m[2][1] * mat.m[3][3] +
mat.m[1][1] * mat.m[2][3] * mat.m[3][0] +
mat.m[1][3] * mat.m[2][0] * mat.m[3][1] -
mat.m[1][0] * mat.m[2][3] * mat.m[3][1] -
mat.m[1][1] * mat.m[2][0] * mat.m[3][3] -
mat.m[1][3] * mat.m[2][1] * mat.m[3][0];
inv[3][0] = -mat.m[1][0] * mat.m[2][1] * mat.m[3][2] -
mat.m[1][1] * mat.m[2][2] * mat.m[3][0] -
mat.m[1][2] * mat.m[2][0] * mat.m[3][1] +
mat.m[1][0] * mat.m[2][2] * mat.m[3][1] +
mat.m[1][1] * mat.m[2][0] * mat.m[3][2] +
mat.m[1][2] * mat.m[2][1] * mat.m[3][0];
inv[0][1] = -mat.m[0][1] * mat.m[2][2] * mat.m[3][3] -
mat.m[0][2] * mat.m[2][3] * mat.m[3][1] -
mat.m[0][3] * mat.m[2][1] * mat.m[3][2] +
mat.m[0][1] * mat.m[2][3] * mat.m[3][2] +
mat.m[0][2] * mat.m[2][1] * mat.m[3][3] +
mat.m[0][3] * mat.m[2][2] * mat.m[3][1];
inv[1][1] = +mat.m[0][0] * mat.m[2][2] * mat.m[3][3] +
mat.m[0][2] * mat.m[2][3] * mat.m[3][0] +
mat.m[0][3] * mat.m[2][0] * mat.m[3][2] -
mat.m[0][0] * mat.m[2][3] * mat.m[3][2] -
mat.m[0][2] * mat.m[2][0] * mat.m[3][3] -
mat.m[0][3] * mat.m[2][2] * mat.m[3][0];
inv[2][1] = -mat.m[0][0] * mat.m[2][1] * mat.m[3][3] -
mat.m[0][1] * mat.m[2][3] * mat.m[3][0] -
mat.m[0][3] * mat.m[2][0] * mat.m[3][1] +
mat.m[0][0] * mat.m[2][3] * mat.m[3][1] +
mat.m[0][1] * mat.m[2][0] * mat.m[3][3] +
mat.m[0][3] * mat.m[2][1] * mat.m[3][0];
inv[3][1] = +mat.m[0][0] * mat.m[2][1] * mat.m[3][2] +
mat.m[0][1] * mat.m[2][2] * mat.m[3][0] +
mat.m[0][2] * mat.m[2][0] * mat.m[3][1] -
mat.m[0][0] * mat.m[2][2] * mat.m[3][1] -
mat.m[0][1] * mat.m[2][0] * mat.m[3][2] -
mat.m[0][2] * mat.m[2][1] * mat.m[3][0];
inv[0][2] = +mat.m[0][1] * mat.m[1][2] * mat.m[3][3] +
mat.m[0][2] * mat.m[1][3] * mat.m[3][1] +
mat.m[0][3] * mat.m[1][1] * mat.m[3][2] -
mat.m[0][1] * mat.m[1][3] * mat.m[3][2] -
mat.m[0][2] * mat.m[1][1] * mat.m[3][3] -
mat.m[0][3] * mat.m[1][2] * mat.m[3][1];
inv[1][2] = -mat.m[0][0] * mat.m[1][2] * mat.m[3][3] -
mat.m[0][2] * mat.m[1][3] * mat.m[3][0] -
mat.m[0][3] * mat.m[1][0] * mat.m[3][2] +
mat.m[0][0] * mat.m[1][3] * mat.m[3][2] +
mat.m[0][2] * mat.m[1][0] * mat.m[3][3] +
mat.m[0][3] * mat.m[1][2] * mat.m[3][0];
inv[2][2] = +mat.m[0][0] * mat.m[1][1] * mat.m[3][3] +
mat.m[0][1] * mat.m[1][3] * mat.m[3][0] +
mat.m[0][3] * mat.m[1][0] * mat.m[3][1] -
mat.m[0][0] * mat.m[1][3] * mat.m[3][1] -
mat.m[0][1] * mat.m[1][0] * mat.m[3][3] -
mat.m[0][3] * mat.m[1][1] * mat.m[3][0];
inv[3][2] = -mat.m[0][0] * mat.m[1][1] * mat.m[3][2] -
mat.m[0][1] * mat.m[1][2] * mat.m[3][0] -
mat.m[0][2] * mat.m[1][0] * mat.m[3][1] +
mat.m[0][0] * mat.m[1][2] * mat.m[3][1] +
mat.m[0][1] * mat.m[1][0] * mat.m[3][2] +
mat.m[0][2] * mat.m[1][1] * mat.m[3][0];
inv[0][3] = -mat.m[0][1] * mat.m[1][2] * mat.m[2][3] -
mat.m[0][2] * mat.m[1][3] * mat.m[2][1] -
mat.m[0][3] * mat.m[1][1] * mat.m[2][2] +
mat.m[0][1] * mat.m[1][3] * mat.m[2][2] +
mat.m[0][2] * mat.m[1][1] * mat.m[2][3] +
mat.m[0][3] * mat.m[1][2] * mat.m[2][1];
inv[1][3] = +mat.m[0][0] * mat.m[1][2] * mat.m[2][3] +
mat.m[0][2] * mat.m[1][3] * mat.m[2][0] +
mat.m[0][3] * mat.m[1][0] * mat.m[2][2] -
mat.m[0][0] * mat.m[1][3] * mat.m[2][2] -
mat.m[0][2] * mat.m[1][0] * mat.m[2][3] -
mat.m[0][3] * mat.m[1][2] * mat.m[2][0];
inv[2][3] = -mat.m[0][0] * mat.m[1][1] * mat.m[2][3] -
mat.m[0][1] * mat.m[1][3] * mat.m[2][0] -
mat.m[0][3] * mat.m[1][0] * mat.m[2][1] +
mat.m[0][0] * mat.m[1][3] * mat.m[2][1] +
mat.m[0][1] * mat.m[1][0] * mat.m[2][3] +
mat.m[0][3] * mat.m[1][1] * mat.m[2][0];
inv[3][3] = +mat.m[0][0] * mat.m[1][1] * mat.m[2][2] +
mat.m[0][1] * mat.m[1][2] * mat.m[2][0] +
mat.m[0][2] * mat.m[1][0] * mat.m[2][1] -
mat.m[0][0] * mat.m[1][2] * mat.m[2][1] -
mat.m[0][1] * mat.m[1][0] * mat.m[2][2] -
mat.m[0][2] * mat.m[1][1] * mat.m[2][0];
float det = mat.m[0][0] * inv[0][0] + mat.m[0][1] * inv[1][0] +
mat.m[0][2] * inv[2][0] + mat.m[0][3] * inv[3][0];
return Matrix4x4(inv) / det;
}
Matrix4x4 Transpose(const Matrix4x4 &mat) {
float m[4][4];
for (uint32_t i = 0; i < 4; i++) {
for (uint32_t j = 0; j < 4; j++) {
m[i][j] = mat.m[j][i];
}
}
return Matrix4x4(m);
}
Float3 Matrix4x4::operator()(const Float3 &p, const Float3::EType &type) const {
Float3 ret;
if (type == Float3::Point) {
float x = m[0][0] * p.x + m[0][1] * p.y + m[0][2] * p.z + m[0][3];
float y = m[1][0] * p.x + m[1][1] * p.y + m[1][2] * p.z + m[1][3];
float z = m[2][0] * p.x + m[2][1] * p.y + m[2][2] * p.z + m[2][3];
float w = m[3][0] * p.x + m[3][1] * p.y + m[3][2] * p.z + m[3][3];
ret = Float3(x, y, z) / w;
} else if (type == Float3::Vector) {
float x = m[0][0] * p.x + m[0][1] * p.y + m[0][2] * p.z;
float y = m[1][0] * p.x + m[1][1] * p.y + m[1][2] * p.z;
float z = m[2][0] * p.x + m[2][1] * p.y + m[2][2] * p.z;
ret = Float3(x, y, z);
} else {
CHECK(false);
}
return ret;
}

View File

@ -0,0 +1,170 @@
#pragma once
#include <cmath>
#include <cstring>
#include "util/common.h"
inline float Sqr(const float &v) { return v * v; }
inline float SafeSqrt(const float &v) { return std::sqrt(std::max(v, 0.f)); }
inline float SafeAcos(const float &v) {
return std::acos(std::min(std::max(v, 0.f), 1.f));
}
class Float3 {
public:
enum EType { Vector, Point };
Float3(const float &v = 0) : x(v), y(v), z(v) {}
Float3(const float &_x, const float &_y, const float &_z) : x(_x), y(_y), z(_z) {}
Float3 operator+(const Float3 &v) const { return Float3(x + v.x, y + v.y, z + v.z); }
Float3 operator-(const Float3 &v) const { return Float3(x - v.x, y - v.y, z - v.z); }
Float3 &operator+=(const Float3 &v) {
x += v.x;
y += v.y;
z += v.z;
return *this;
}
Float3 operator*(const float &v) const { return Float3(x * v, y * v, z * v); }
Float3 operator*(const Float3 &v) const { return Float3(x * v.x, y * v.y, z * v.z); }
Float3 operator/(const float &v) const {
CHECK(v != 0.0);
float inv = 1.0f / v;
return Float3(x * inv, y * inv, z * inv);
}
Float3 operator/(const Float3 &v) const {
CHECK(v.x != 0.0);
CHECK(v.y != 0.0);
CHECK(v.z != 0.0);
float invX = 1.0f / v.x;
float invY = 1.0f / v.y;
float invZ = 1.0f / v.z;
return Float3(x * invX, y * invY, z * invZ);
}
Float3 &operator/=(const float &v) {
CHECK(v != 0.0);
float inv = 1.0f / v;
x *= inv;
y *= inv;
z *= inv;
return *this;
}
float x, y, z;
};
inline Float3 Min(const Float3 &a, const Float3 &b) {
return Float3(std::min(a.x, b.x), std::min(a.y, b.y), std::min(a.z, b.z));
}
inline Float3 Max(const Float3 &a, const Float3 &b) {
return Float3(std::max(a.x, b.x), std::max(a.y, b.y), std::max(a.z, b.z));
}
inline float Dot(const Float3 &a, const Float3 &b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
inline float AbsSum(const Float3 &a, const Float3 &b) {
return std::fabs(a.x - b.x) + std::fabs(a.y - b.y) + std::fabs(a.z - b.z);
}
inline Float3 Abs(const Float3 &a) {
return Float3(std::fabs(a.x), std::fabs(a.y), std::fabs(a.z));
}
inline Float3 Sqr(const Float3 &a) { return Float3(Sqr(a.x), Sqr(a.y), Sqr(a.z)); }
inline Float3 SafeSqrt(const Float3 &a) {
return Float3(SafeSqrt(a.x), SafeSqrt(a.y), SafeSqrt(a.z));
}
// (1 - s) * u + s * v
inline Float3 Lerp(const Float3 &u, const Float3 &v, const float &s) {
return u + (v - u) * s;
}
inline Float3 Clamp(const Float3 &v, const Float3 &l, const Float3 &r) {
return Min(Max(v, l), r);
}
inline float SqrLength(const Float3 &a) { return Sqr(a.x) + Sqr(a.y) + Sqr(a.z); }
inline float Length(const Float3 &a) { return std::sqrt(Sqr(a.x) + Sqr(a.y) + Sqr(a.z)); }
inline Float3 Normalize(const Float3 &a) { return a / Length(a); }
inline float SqrDistance(const Float3 &a, const Float3 &b) { return SqrLength(a - b); }
inline float Distance(const Float3 &a, const Float3 &b) { return Length(a - b); }
inline float Luminance(const Float3 &rgb) {
return Dot(rgb, Float3(0.2126f, 0.7152f, 0.0722f));
}
inline Float3 RGB2YCoCg(const Float3 &RGB) {
float Co = RGB.x - RGB.z;
float tmp = RGB.z + Co / 2;
float Cg = RGB.y - tmp;
float Y = tmp + Cg / 2;
return Float3(Y, Co, Cg);
}
inline Float3 YCoCg2RGB(const Float3 &YCoCg) {
float tmp = YCoCg.x - YCoCg.z / 2;
float G = YCoCg.z + tmp;
float B = tmp - YCoCg.y / 2;
float R = B + YCoCg.y;
return Float3(R, G, B);
}
inline std::ostream &operator<<(std::ostream &os, const Float3 &v) {
os << "[ " << v.x << ", " << v.y << ", " << v.z << " ]";
return os;
}
class Matrix4x4 {
public:
Matrix4x4() {
memset(m, 0, sizeof(float) * 16);
m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1;
}
Matrix4x4(const float _m[4][4]) { memcpy(m, _m, sizeof(float) * 16); }
Matrix4x4(const float _m[16]) { memcpy(m, _m, sizeof(float) * 16); }
Matrix4x4 operator*(const float &v) const {
Matrix4x4 ret;
for (uint32_t i = 0; i < 4; i++) {
for (uint32_t j = 0; j < 4; j++) {
ret.m[i][j] = m[i][j] * v;
}
}
return ret;
}
Matrix4x4 operator/(const float &v) const {
CHECK(v != 0);
float inv = 1.f / v;
return *this * inv;
}
Matrix4x4 operator*(const Matrix4x4 &mat) const {
Matrix4x4 ret;
for (uint32_t i = 0; i < 4; i++) {
for (uint32_t j = 0; j < 4; j++) {
ret.m[i][j] = 0;
for (uint32_t k = 0; k < 4; k++) {
ret.m[i][j] += m[i][k] * mat.m[k][j];
}
}
}
return ret;
}
Float3 operator()(const Float3 &p, const Float3::EType &type) const;
float m[4][4];
public:
friend std::ostream &operator<<(std::ostream &os, const Matrix4x4 &mat) {
os << mat.m[0][0] << "\t" << mat.m[0][1] << "\t" << mat.m[0][2] << "\t"
<< mat.m[0][3] << "\n"
<< mat.m[1][0] << "\t" << mat.m[1][1] << "\t" << mat.m[1][2] << "\t"
<< mat.m[1][3] << "\n"
<< mat.m[2][0] << "\t" << mat.m[2][1] << "\t" << mat.m[2][2] << "\t"
<< mat.m[2][3] << "\n"
<< mat.m[3][0] << "\t" << mat.m[3][1] << "\t" << mat.m[3][2] << "\t"
<< mat.m[3][3];
return os;
}
friend Matrix4x4 Inverse(const Matrix4x4 &mat);
friend Matrix4x4 Transpose(const Matrix4x4 &mat);
};