Initial commit of ship project

This commit is contained in:
Oleg 2025-08-15 16:02:18 +00:00
commit 1e74491310
29 changed files with 105173 additions and 0 deletions

1
.env.example Normal file
View File

@ -0,0 +1 @@
APP_BASE_PATH = /ship

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.env
*.log
.idea/
node_modules/
1.bat

1689
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

13
package.json Normal file
View File

@ -0,0 +1,13 @@
{
"dependencies": {
"canvas": "^2.11.2",
"dotenv": "^16.5.0",
"ejs": "^3.1.8",
"express": "^4.18.2",
"mysql2": "^3.2.0",
"path": "^0.12.7",
"sequelize": "^6.29.1",
"socket.io": "^4.6.1",
"sweetalert2": "^11.7.3"
}
}

BIN
public/Rock_color.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

BIN
public/Rock_color2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

BIN
public/Walking.fbx Normal file

Binary file not shown.

3
public/assets/models.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
public/blue_whale.fbx Normal file

Binary file not shown.

142
public/css/style.css Normal file
View File

@ -0,0 +1,142 @@
html, body {
margin: 0;
height: 100%;
}
#c {
width: 750px;
height: 500px;
display: block;
}
#canvas_temp, #canvas_temp2{
display: none;
position: absolute;
left: 0;
top: 0;
pointer-events: none;
}
#canvas_temp{
z-index: 1;
}
.top-div {
display: flex;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
padding: 10px;
font-family: "Arial";
font-size: 10px;
background: black;
color: white;
}
.top-div::after {
content: "Загрузка";
display: flex;
position: absolute;
width: 100%;
height: 100%;
animation: loading 1s ease-in-out infinite;
font-size: 30px;
justify-content: center;
align-items: center;
}
@keyframes loading{
0% {
content: "Загрузка\00a0\00a0\00a0";
}
35% {
content: "Загрузка.\00a0\00a0";
}
65% {
content: "Загрузка..\00a0";
}
100% {
content: "Загрузка...";
}
}
a{
margin-top: 10px;
background: lightgray;
padding: 10px;
border-radius: 5px;
text-decoration: none;
color: black;
font-size: 15px;
line-height: 15px;
border: 2px solid gray;
}
.container{
position: relative;
display: flex;
width: 100vw;
height: 100vh;
flex-direction: column;
justify-content: center;
align-items: center;
}
.big_container{
display: flex;
flex-direction: column;
overflow: hidden;
}
.menu_ico{
width: 21px;
height: 21px;
position:absolute;
top: 10px;
right: 22px;
z-index: 2;
cursor: pointer;
}
.menu_ico > div:first-child, .menu_ico > div:first-child:after, .menu_ico > div:first-child:before{
width: 12px;
height: 2px;
cursor: pointer;
}
.menu_ico > div:first-child{
position:absolute;
margin-left: 4px;
display: inline;
background: darkgray;
transform: scale(1.7);
}
.menu_ico > div:first-child:after{
position:absolute;
content: "";
background: darkgray;
top: 4px;
left: 0px;
}
.menu_ico > div:first-child:before{
position:absolute;
content: "";
background: darkgray;
top: 8px;
left: 0px;
}
.slidecontainer {
display: none;
position: fixed;
left: 0;
top: 0;
width: 100%;
padding: 10px;
background: white;
z-index: 1;
}
.menu_ico:hover .slidecontainer{
display: flex;
}
#checkbox-container{
display: grid;
columns: 2;
row-gap: 10px;
padding-left: 20px;
}
.sheet{
width: 400px;
}
.hide{
display: none;
}

4074
public/scripts/FBXLoader.js Normal file

File diff suppressed because it is too large Load Diff

4507
public/scripts/GLTFLoader.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,451 @@
function findSpan( p, u, U ) {
const n = U.length - p - 1;
if ( u >= U[ n ] ) {
return n - 1;
}
if ( u <= U[ p ] ) {
return p;
}
let low = p;
let high = n;
let mid = Math.floor( ( low + high ) / 2 );
while ( u < U[ mid ] || u >= U[ mid + 1 ] ) {
if ( u < U[ mid ] ) {
high = mid;
} else {
low = mid;
}
mid = Math.floor( ( low + high ) / 2 );
}
return mid;
}
/*
Calculate basis functions. See The NURBS Book, page 70, algorithm A2.2
span : span in which u lies
u : parametric point
p : degree
U : knot vector
returns array[p+1] with basis functions values.
*/
function calcBasisFunctions( span, u, p, U ) {
const N = [];
const left = [];
const right = [];
N[ 0 ] = 1.0;
for ( let j = 1; j <= p; ++ j ) {
left[ j ] = u - U[ span + 1 - j ];
right[ j ] = U[ span + j ] - u;
let saved = 0.0;
for ( let r = 0; r < j; ++ r ) {
const rv = right[ r + 1 ];
const lv = left[ j - r ];
const temp = N[ r ] / ( rv + lv );
N[ r ] = saved + rv * temp;
saved = lv * temp;
}
N[ j ] = saved;
}
return N;
}
/*
Calculate B-Spline curve points. See The NURBS Book, page 82, algorithm A3.1.
p : degree of B-Spline
U : knot vector
P : control points (x, y, z, w)
u : parametric point
returns point for given u
*/
function calcBSplinePoint( p, U, P, u ) {
const span = findSpan( p, u, U );
const N = calcBasisFunctions( span, u, p, U );
const C = new Vector4( 0, 0, 0, 0 );
for ( let j = 0; j <= p; ++ j ) {
const point = P[ span - p + j ];
const Nj = N[ j ];
const wNj = point.w * Nj;
C.x += point.x * wNj;
C.y += point.y * wNj;
C.z += point.z * wNj;
C.w += point.w * Nj;
}
return C;
}
/*
Calculate basis functions derivatives. See The NURBS Book, page 72, algorithm A2.3.
span : span in which u lies
u : parametric point
p : degree
n : number of derivatives to calculate
U : knot vector
returns array[n+1][p+1] with basis functions derivatives
*/
function calcBasisFunctionDerivatives( span, u, p, n, U ) {
const zeroArr = [];
for ( let i = 0; i <= p; ++ i )
zeroArr[ i ] = 0.0;
const ders = [];
for ( let i = 0; i <= n; ++ i )
ders[ i ] = zeroArr.slice( 0 );
const ndu = [];
for ( let i = 0; i <= p; ++ i )
ndu[ i ] = zeroArr.slice( 0 );
ndu[ 0 ][ 0 ] = 1.0;
const left = zeroArr.slice( 0 );
const right = zeroArr.slice( 0 );
for ( let j = 1; j <= p; ++ j ) {
left[ j ] = u - U[ span + 1 - j ];
right[ j ] = U[ span + j ] - u;
let saved = 0.0;
for ( let r = 0; r < j; ++ r ) {
const rv = right[ r + 1 ];
const lv = left[ j - r ];
ndu[ j ][ r ] = rv + lv;
const temp = ndu[ r ][ j - 1 ] / ndu[ j ][ r ];
ndu[ r ][ j ] = saved + rv * temp;
saved = lv * temp;
}
ndu[ j ][ j ] = saved;
}
for ( let j = 0; j <= p; ++ j ) {
ders[ 0 ][ j ] = ndu[ j ][ p ];
}
for ( let r = 0; r <= p; ++ r ) {
let s1 = 0;
let s2 = 1;
const a = [];
for ( let i = 0; i <= p; ++ i ) {
a[ i ] = zeroArr.slice( 0 );
}
a[ 0 ][ 0 ] = 1.0;
for ( let k = 1; k <= n; ++ k ) {
let d = 0.0;
const rk = r - k;
const pk = p - k;
if ( r >= k ) {
a[ s2 ][ 0 ] = a[ s1 ][ 0 ] / ndu[ pk + 1 ][ rk ];
d = a[ s2 ][ 0 ] * ndu[ rk ][ pk ];
}
const j1 = ( rk >= - 1 ) ? 1 : - rk;
const j2 = ( r - 1 <= pk ) ? k - 1 : p - r;
for ( let j = j1; j <= j2; ++ j ) {
a[ s2 ][ j ] = ( a[ s1 ][ j ] - a[ s1 ][ j - 1 ] ) / ndu[ pk + 1 ][ rk + j ];
d += a[ s2 ][ j ] * ndu[ rk + j ][ pk ];
}
if ( r <= pk ) {
a[ s2 ][ k ] = - a[ s1 ][ k - 1 ] / ndu[ pk + 1 ][ r ];
d += a[ s2 ][ k ] * ndu[ r ][ pk ];
}
ders[ k ][ r ] = d;
const j = s1;
s1 = s2;
s2 = j;
}
}
let r = p;
for ( let k = 1; k <= n; ++ k ) {
for ( let j = 0; j <= p; ++ j ) {
ders[ k ][ j ] *= r;
}
r *= p - k;
}
return ders;
}
/*
Calculate derivatives of a B-Spline. See The NURBS Book, page 93, algorithm A3.2.
p : degree
U : knot vector
P : control points
u : Parametric points
nd : number of derivatives
returns array[d+1] with derivatives
*/
function calcBSplineDerivatives( p, U, P, u, nd ) {
const du = nd < p ? nd : p;
const CK = [];
const span = findSpan( p, u, U );
const nders = calcBasisFunctionDerivatives( span, u, p, du, U );
const Pw = [];
for ( let i = 0; i < P.length; ++ i ) {
const point = P[ i ].clone();
const w = point.w;
point.x *= w;
point.y *= w;
point.z *= w;
Pw[ i ] = point;
}
for ( let k = 0; k <= du; ++ k ) {
const point = Pw[ span - p ].clone().multiplyScalar( nders[ k ][ 0 ] );
for ( let j = 1; j <= p; ++ j ) {
point.add( Pw[ span - p + j ].clone().multiplyScalar( nders[ k ][ j ] ) );
}
CK[ k ] = point;
}
for ( let k = du + 1; k <= nd + 1; ++ k ) {
CK[ k ] = new Vector4( 0, 0, 0 );
}
return CK;
}
/*
Calculate "K over I"
returns k!/(i!(k-i)!)
*/
function calcKoverI( k, i ) {
let nom = 1;
for ( let j = 2; j <= k; ++ j ) {
nom *= j;
}
let denom = 1;
for ( let j = 2; j <= i; ++ j ) {
denom *= j;
}
for ( let j = 2; j <= k - i; ++ j ) {
denom *= j;
}
return nom / denom;
}
/*
Calculate derivatives (0-nd) of rational curve. See The NURBS Book, page 127, algorithm A4.2.
Pders : result of function calcBSplineDerivatives
returns array with derivatives for rational curve.
*/
function calcRationalCurveDerivatives( Pders ) {
const nd = Pders.length;
const Aders = [];
const wders = [];
for ( let i = 0; i < nd; ++ i ) {
const point = Pders[ i ];
Aders[ i ] = new Vector3( point.x, point.y, point.z );
wders[ i ] = point.w;
}
const CK = [];
for ( let k = 0; k < nd; ++ k ) {
const v = Aders[ k ].clone();
for ( let i = 1; i <= k; ++ i ) {
v.sub( CK[ k - i ].clone().multiplyScalar( calcKoverI( k, i ) * wders[ i ] ) );
}
CK[ k ] = v.divideScalar( wders[ 0 ] );
}
return CK;
}
/*
Calculate NURBS curve derivatives. See The NURBS Book, page 127, algorithm A4.2.
p : degree
U : knot vector
P : control points in homogeneous space
u : parametric points
nd : number of derivatives
returns array with derivatives.
*/
function calcNURBSDerivatives( p, U, P, u, nd ) {
const Pders = calcBSplineDerivatives( p, U, P, u, nd );
return calcRationalCurveDerivatives( Pders );
}
/*
Calculate rational B-Spline surface point. See The NURBS Book, page 134, algorithm A4.3.
p1, p2 : degrees of B-Spline surface
U1, U2 : knot vectors
P : control points (x, y, z, w)
u, v : parametric values
returns point for given (u, v)
*/
function calcSurfacePoint( p, q, U, V, P, u, v, target ) {
const uspan = findSpan( p, u, U );
const vspan = findSpan( q, v, V );
const Nu = calcBasisFunctions( uspan, u, p, U );
const Nv = calcBasisFunctions( vspan, v, q, V );
const temp = [];
for ( let l = 0; l <= q; ++ l ) {
temp[ l ] = new Vector4( 0, 0, 0, 0 );
for ( let k = 0; k <= p; ++ k ) {
const point = P[ uspan - p + k ][ vspan - q + l ].clone();
const w = point.w;
point.x *= w;
point.y *= w;
point.z *= w;
temp[ l ].add( point.multiplyScalar( Nu[ k ] ) );
}
}
const Sw = new Vector4( 0, 0, 0, 0 );
for ( let l = 0; l <= q; ++ l ) {
Sw.add( temp[ l ].multiplyScalar( Nv[ l ] ) );
}
Sw.divideScalar( Sw.w );
target.set( Sw.x, Sw.y, Sw.z );
}

View File

@ -0,0 +1,450 @@
function findSpan( p, u, U ) {
const n = U.length - p - 1;
if ( u >= U[ n ] ) {
return n - 1;
}
if ( u <= U[ p ] ) {
return p;
}
let low = p;
let high = n;
let mid = Math.floor( ( low + high ) / 2 );
while ( u < U[ mid ] || u >= U[ mid + 1 ] ) {
if ( u < U[ mid ] ) {
high = mid;
} else {
low = mid;
}
mid = Math.floor( ( low + high ) / 2 );
}
return mid;
}
/*
Calculate basis functions. See The NURBS Book, page 70, algorithm A2.2
span : span in which u lies
u : parametric point
p : degree
U : knot vector
returns array[p+1] with basis functions values.
*/
function calcBasisFunctions( span, u, p, U ) {
const N = [];
const left = [];
const right = [];
N[ 0 ] = 1.0;
for ( let j = 1; j <= p; ++ j ) {
left[ j ] = u - U[ span + 1 - j ];
right[ j ] = U[ span + j ] - u;
let saved = 0.0;
for ( let r = 0; r < j; ++ r ) {
const rv = right[ r + 1 ];
const lv = left[ j - r ];
const temp = N[ r ] / ( rv + lv );
N[ r ] = saved + rv * temp;
saved = lv * temp;
}
N[ j ] = saved;
}
return N;
}
/*
Calculate B-Spline curve points. See The NURBS Book, page 82, algorithm A3.1.
p : degree of B-Spline
U : knot vector
P : control points (x, y, z, w)
u : parametric point
returns point for given u
*/
function calcBSplinePoint( p, U, P, u ) {
const span = findSpan( p, u, U );
const N = calcBasisFunctions( span, u, p, U );
const C = new Vector4( 0, 0, 0, 0 );
for ( let j = 0; j <= p; ++ j ) {
const point = P[ span - p + j ];
const Nj = N[ j ];
const wNj = point.w * Nj;
C.x += point.x * wNj;
C.y += point.y * wNj;
C.z += point.z * wNj;
C.w += point.w * Nj;
}
return C;
}
/*
Calculate basis functions derivatives. See The NURBS Book, page 72, algorithm A2.3.
span : span in which u lies
u : parametric point
p : degree
n : number of derivatives to calculate
U : knot vector
returns array[n+1][p+1] with basis functions derivatives
*/
function calcBasisFunctionDerivatives( span, u, p, n, U ) {
const zeroArr = [];
for ( let i = 0; i <= p; ++ i )
zeroArr[ i ] = 0.0;
const ders = [];
for ( let i = 0; i <= n; ++ i )
ders[ i ] = zeroArr.slice( 0 );
const ndu = [];
for ( let i = 0; i <= p; ++ i )
ndu[ i ] = zeroArr.slice( 0 );
ndu[ 0 ][ 0 ] = 1.0;
const left = zeroArr.slice( 0 );
const right = zeroArr.slice( 0 );
for ( let j = 1; j <= p; ++ j ) {
left[ j ] = u - U[ span + 1 - j ];
right[ j ] = U[ span + j ] - u;
let saved = 0.0;
for ( let r = 0; r < j; ++ r ) {
const rv = right[ r + 1 ];
const lv = left[ j - r ];
ndu[ j ][ r ] = rv + lv;
const temp = ndu[ r ][ j - 1 ] / ndu[ j ][ r ];
ndu[ r ][ j ] = saved + rv * temp;
saved = lv * temp;
}
ndu[ j ][ j ] = saved;
}
for ( let j = 0; j <= p; ++ j ) {
ders[ 0 ][ j ] = ndu[ j ][ p ];
}
for ( let r = 0; r <= p; ++ r ) {
let s1 = 0;
let s2 = 1;
const a = [];
for ( let i = 0; i <= p; ++ i ) {
a[ i ] = zeroArr.slice( 0 );
}
a[ 0 ][ 0 ] = 1.0;
for ( let k = 1; k <= n; ++ k ) {
let d = 0.0;
const rk = r - k;
const pk = p - k;
if ( r >= k ) {
a[ s2 ][ 0 ] = a[ s1 ][ 0 ] / ndu[ pk + 1 ][ rk ];
d = a[ s2 ][ 0 ] * ndu[ rk ][ pk ];
}
const j1 = ( rk >= - 1 ) ? 1 : - rk;
const j2 = ( r - 1 <= pk ) ? k - 1 : p - r;
for ( let j = j1; j <= j2; ++ j ) {
a[ s2 ][ j ] = ( a[ s1 ][ j ] - a[ s1 ][ j - 1 ] ) / ndu[ pk + 1 ][ rk + j ];
d += a[ s2 ][ j ] * ndu[ rk + j ][ pk ];
}
if ( r <= pk ) {
a[ s2 ][ k ] = - a[ s1 ][ k - 1 ] / ndu[ pk + 1 ][ r ];
d += a[ s2 ][ k ] * ndu[ r ][ pk ];
}
ders[ k ][ r ] = d;
const j = s1;
s1 = s2;
s2 = j;
}
}
let r = p;
for ( let k = 1; k <= n; ++ k ) {
for ( let j = 0; j <= p; ++ j ) {
ders[ k ][ j ] *= r;
}
r *= p - k;
}
return ders;
}
/*
Calculate derivatives of a B-Spline. See The NURBS Book, page 93, algorithm A3.2.
p : degree
U : knot vector
P : control points
u : Parametric points
nd : number of derivatives
returns array[d+1] with derivatives
*/
function calcBSplineDerivatives( p, U, P, u, nd ) {
const du = nd < p ? nd : p;
const CK = [];
const span = findSpan( p, u, U );
const nders = calcBasisFunctionDerivatives( span, u, p, du, U );
const Pw = [];
for ( let i = 0; i < P.length; ++ i ) {
const point = P[ i ].clone();
const w = point.w;
point.x *= w;
point.y *= w;
point.z *= w;
Pw[ i ] = point;
}
for ( let k = 0; k <= du; ++ k ) {
const point = Pw[ span - p ].clone().multiplyScalar( nders[ k ][ 0 ] );
for ( let j = 1; j <= p; ++ j ) {
point.add( Pw[ span - p + j ].clone().multiplyScalar( nders[ k ][ j ] ) );
}
CK[ k ] = point;
}
for ( let k = du + 1; k <= nd + 1; ++ k ) {
CK[ k ] = new Vector4( 0, 0, 0 );
}
return CK;
}
/*
Calculate "K over I"
returns k!/(i!(k-i)!)
*/
function calcKoverI( k, i ) {
let nom = 1;
for ( let j = 2; j <= k; ++ j ) {
nom *= j;
}
let denom = 1;
for ( let j = 2; j <= i; ++ j ) {
denom *= j;
}
for ( let j = 2; j <= k - i; ++ j ) {
denom *= j;
}
return nom / denom;
}
/*
Calculate derivatives (0-nd) of rational curve. See The NURBS Book, page 127, algorithm A4.2.
Pders : result of function calcBSplineDerivatives
returns array with derivatives for rational curve.
*/
function calcRationalCurveDerivatives( Pders ) {
const nd = Pders.length;
const Aders = [];
const wders = [];
for ( let i = 0; i < nd; ++ i ) {
const point = Pders[ i ];
Aders[ i ] = new Vector3( point.x, point.y, point.z );
wders[ i ] = point.w;
}
const CK = [];
for ( let k = 0; k < nd; ++ k ) {
const v = Aders[ k ].clone();
for ( let i = 1; i <= k; ++ i ) {
v.sub( CK[ k - i ].clone().multiplyScalar( calcKoverI( k, i ) * wders[ i ] ) );
}
CK[ k ] = v.divideScalar( wders[ 0 ] );
}
return CK;
}
/*
Calculate NURBS curve derivatives. See The NURBS Book, page 127, algorithm A4.2.
p : degree
U : knot vector
P : control points in homogeneous space
u : parametric points
nd : number of derivatives
returns array with derivatives.
*/
function calcNURBSDerivatives( p, U, P, u, nd ) {
const Pders = calcBSplineDerivatives( p, U, P, u, nd );
return calcRationalCurveDerivatives( Pders );
}
/*
Calculate rational B-Spline surface point. See The NURBS Book, page 134, algorithm A4.3.
p1, p2 : degrees of B-Spline surface
U1, U2 : knot vectors
P : control points (x, y, z, w)
u, v : parametric values
returns point for given (u, v)
*/
function calcSurfacePoint( p, q, U, V, P, u, v, target ) {
const uspan = findSpan( p, u, U );
const vspan = findSpan( q, v, V );
const Nu = calcBasisFunctions( uspan, u, p, U );
const Nv = calcBasisFunctions( vspan, v, q, V );
const temp = [];
for ( let l = 0; l <= q; ++ l ) {
temp[ l ] = new Vector4( 0, 0, 0, 0 );
for ( let k = 0; k <= p; ++ k ) {
const point = P[ uspan - p + k ][ vspan - q + l ].clone();
const w = point.w;
point.x *= w;
point.y *= w;
point.z *= w;
temp[ l ].add( point.multiplyScalar( Nu[ k ] ) );
}
}
const Sw = new Vector4( 0, 0, 0, 0 );
for ( let l = 0; l <= q; ++ l ) {
Sw.add( temp[ l ].multiplyScalar( Nv[ l ] ) );
}
Sw.divideScalar( Sw.w );
target.set( Sw.x, Sw.y, Sw.z );
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,230 @@
const UnpackDepthRGBAShader = {
name: 'UnpackDepthRGBAShader',
uniforms: {
'tDiffuse': { value: null },
'opacity': { value: 1.0 }
},
vertexShader: /* glsl */`
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`,
fragmentShader: /* glsl */`
uniform float opacity;
uniform sampler2D tDiffuse;
varying vec2 vUv;
#include <packing>
void main() {
float depth = 1.0 - unpackRGBAToDepth( texture2D( tDiffuse, vUv ) );
gl_FragColor = vec4( vec3( depth ), opacity );
}`
};
/**
* This is a helper for visualising a given light's shadow map.
* It works for shadow casting lights: DirectionalLight and SpotLight.
* It renders out the shadow map and displays it on a HUD.
*
* Example usage:
* 1) Import ShadowMapViewer into your app.
*
* 2) Create a shadow casting light and name it optionally:
* let light = new DirectionalLight( 0xffffff, 1 );
* light.castShadow = true;
* light.name = 'Sun';
*
* 3) Create a shadow map viewer for that light and set its size and position optionally:
* let shadowMapViewer = new ShadowMapViewer( light );
* shadowMapViewer.size.set( 128, 128 ); //width, height default: 256, 256
* shadowMapViewer.position.set( 10, 10 ); //x, y in pixel default: 0, 0 (top left corner)
*
* 4) Render the shadow map viewer in your render loop:
* shadowMapViewer.render( renderer );
*
* 5) Optionally: Update the shadow map viewer on window resize:
* shadowMapViewer.updateForWindowResize();
*
* 6) If you set the position or size members directly, you need to call shadowMapViewer.update();
*/
class ShadowMapViewer {
constructor( light ) {
//- Internals
const scope = this;
const doRenderLabel = ( light.name !== undefined && light.name !== '' );
let userAutoClearSetting;
//Holds the initial position and dimension of the HUD
const frame = {
x: 10,
y: 10,
width: 256,
height: 256
};
const camera = new OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, 1, 10 );
camera.position.set( 0, 0, 2 );
const scene = new Scene();
//HUD for shadow map
const shader = UnpackDepthRGBAShader;
const uniforms = UniformsUtils.clone( shader.uniforms );
const material = new ShaderMaterial( {
uniforms: uniforms,
vertexShader: shader.vertexShader,
fragmentShader: shader.fragmentShader
} );
const plane = new PlaneGeometry( frame.width, frame.height );
window.mesh = new Mesh( plane, material );
scene.add( mesh );
//Label for light's name
let labelCanvas, labelMesh;
if ( doRenderLabel ) {
labelCanvas = document.createElement( 'canvas' );
const context = labelCanvas.getContext( '2d' );
context.font = 'Bold 20px Arial';
const labelWidth = context.measureText( light.name ).width;
labelCanvas.width = labelWidth;
labelCanvas.height = 25; //25 to account for g, p, etc.
context.font = 'Bold 20px Arial';
context.fillStyle = 'rgba( 255, 0, 0, 1 )';
context.fillText( light.name, 0, 20 );
const labelTexture = new CanvasTexture( labelCanvas );
const labelMaterial = new MeshBasicMaterial( { map: labelTexture, side: DoubleSide, transparent: true } );
const labelPlane = new PlaneGeometry( labelCanvas.width, labelCanvas.height );
labelMesh = new Mesh( labelPlane, labelMaterial );
scene.add( labelMesh );
}
function resetPosition() {
scope.position.set( scope.position.x, scope.position.y );
}
//- API
// Set to false to disable displaying this shadow map
this.enabled = true;
// Set the size of the displayed shadow map on the HUD
this.size = {
width: frame.width,
height: frame.height,
set: function ( width, height ) {
this.width = width;
this.height = height;
mesh.scale.set( this.width / frame.width, this.height / frame.height, 1 );
//Reset the position as it is off when we scale stuff
resetPosition();
}
};
// Set the position of the displayed shadow map on the HUD
this.position = {
x: frame.x,
y: frame.y,
set: function ( x, y ) {
this.x = x;
this.y = y;
const width = scope.size.width;
const height = scope.size.height;
mesh.position.set( - window.innerWidth / 2 + width / 2 + this.x, window.innerHeight / 2 - height / 2 - this.y, 0 );
if ( doRenderLabel ) labelMesh.position.set( mesh.position.x, mesh.position.y - scope.size.height / 2 + labelCanvas.height / 2, 0 );
}
};
this.render = function ( renderer ) {
if ( this.enabled ) {
//Because a light's .shadowMap is only initialised after the first render pass
//we have to make sure the correct map is sent into the shader, otherwise we
//always end up with the scene's first added shadow casting light's shadowMap
//in the shader
//See: https://github.com/mrdoob/three.js/issues/5932
uniforms.tDiffuse.value = light.shadow.map.texture;
userAutoClearSetting = renderer.autoClear;
renderer.autoClear = false; // To allow render overlay
renderer.clearDepth();
renderer.render( scene, camera );
renderer.autoClear = userAutoClearSetting; //Restore user's setting
}
};
this.updateForWindowResize = function () {
if ( this.enabled ) {
camera.left = window.innerWidth / - 2;
camera.right = window.innerWidth / 2;
camera.top = window.innerHeight / 2;
camera.bottom = window.innerHeight / - 2;
camera.updateProjectionMatrix();
this.update();
}
};
this.update = function () {
this.position.set( this.position.x, this.position.y );
this.size.set( this.size.width, this.size.height );
};
//Force an update to set position/size
this.update();
}
}

View File

@ -0,0 +1,441 @@
var AnimationClip = THREE.AnimationClip;
var AnimationMixer = THREE.AnimationMixer;
var Matrix4 = THREE.Matrix4;
var Quaternion = THREE.Quaternion;
var QuaternionKeyframeTrack = THREE.QuaternionKeyframeTrack;
var SkeletonHelper = THREE.SkeletonHelper;
var Vector3 = THREE.Vector3;
var VectorKeyframeTrack = THREE.VectorKeyframeTrack;
function getBoneName( bone, options ) {
if ( options.getBoneName !== undefined ) {
return options.getBoneName( bone );
}
return options.names[ bone.name ];
}
function retarget( target, source, options = {} ) {
const quat = new Quaternion(),
scale = new Vector3(),
relativeMatrix = new Matrix4(),
globalMatrix = new Matrix4();
options.preserveBoneMatrix = options.preserveBoneMatrix !== undefined ? options.preserveBoneMatrix : true;
options.preserveBonePositions = options.preserveBonePositions !== undefined ? options.preserveBonePositions : true;
options.useTargetMatrix = options.useTargetMatrix !== undefined ? options.useTargetMatrix : false;
options.hip = options.hip !== undefined ? options.hip : 'hip';
options.hipInfluence = options.hipInfluence !== undefined ? options.hipInfluence : new Vector3( 1, 1, 1 );
options.scale = options.scale !== undefined ? options.scale : 1;
options.names = options.names || {};
const sourceBones = source.isObject3D ? source.skeleton.bones : getBones( source ),
bones = target.isObject3D ? target.skeleton.bones : getBones( target );
let bone, name, boneTo,
bonesPosition;
// reset bones
if ( target.isObject3D ) {
target.skeleton.pose();
} else {
options.useTargetMatrix = true;
options.preserveBoneMatrix = false;
}
if ( options.preserveBonePositions ) {
bonesPosition = [];
for ( let i = 0; i < bones.length; i ++ ) {
bonesPosition.push( bones[ i ].position.clone() );
}
}
if ( options.preserveBoneMatrix ) {
// reset matrix
target.updateMatrixWorld();
target.matrixWorld.identity();
// reset children matrix
for ( let i = 0; i < target.children.length; ++ i ) {
target.children[ i ].updateMatrixWorld( true );
}
}
for ( let i = 0; i < bones.length; ++ i ) {
bone = bones[ i ];
name = getBoneName( bone, options );
boneTo = getBoneByName( name, sourceBones );
globalMatrix.copy( bone.matrixWorld );
if ( boneTo ) {
boneTo.updateMatrixWorld();
if ( options.useTargetMatrix ) {
relativeMatrix.copy( boneTo.matrixWorld );
} else {
relativeMatrix.copy( target.matrixWorld ).invert();
relativeMatrix.multiply( boneTo.matrixWorld );
}
// ignore scale to extract rotation
scale.setFromMatrixScale( relativeMatrix );
relativeMatrix.scale( scale.set( 1 / scale.x, 1 / scale.y, 1 / scale.z ) );
// apply to global matrix
globalMatrix.makeRotationFromQuaternion( quat.setFromRotationMatrix( relativeMatrix ) );
if ( target.isObject3D ) {
if ( options.localOffsets ) {
if ( options.localOffsets[ bone.name ] ) {
globalMatrix.multiply( options.localOffsets[ bone.name ] );
}
}
}
globalMatrix.copyPosition( relativeMatrix );
}
if ( name === options.hip ) {
globalMatrix.elements[ 12 ] *= options.scale * options.hipInfluence.x;
globalMatrix.elements[ 13 ] *= options.scale * options.hipInfluence.y;
globalMatrix.elements[ 14 ] *= options.scale * options.hipInfluence.z;
if ( options.hipPosition !== undefined ) {
globalMatrix.elements[ 12 ] += options.hipPosition.x * options.scale;
globalMatrix.elements[ 13 ] += options.hipPosition.y * options.scale;
globalMatrix.elements[ 14 ] += options.hipPosition.z * options.scale;
}
}
if ( bone.parent ) {
bone.matrix.copy( bone.parent.matrixWorld ).invert();
bone.matrix.multiply( globalMatrix );
} else {
bone.matrix.copy( globalMatrix );
}
bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
bone.updateMatrixWorld();
}
if ( options.preserveBonePositions ) {
for ( let i = 0; i < bones.length; ++ i ) {
bone = bones[ i ];
name = getBoneName( bone, options ) || bone.name;
if ( name !== options.hip ) {
bone.position.copy( bonesPosition[ i ] );
}
}
}
if ( options.preserveBoneMatrix ) {
// restore matrix
target.updateMatrixWorld( true );
}
}
function retargetClip( target, source, clip, options = {} ) {
options.useFirstFramePosition = options.useFirstFramePosition !== undefined ? options.useFirstFramePosition : false;
// Calculate the fps from the source clip based on the track with the most frames, unless fps is already provided.
options.fps = options.fps !== undefined ? options.fps : ( Math.max( ...clip.tracks.map( track => track.times.length ) ) / clip.duration );
options.names = options.names || [];
if ( ! source.isObject3D ) {
source = getHelperFromSkeleton( source );
}
const numFrames = Math.round( clip.duration * ( options.fps / 1000 ) * 1000 ),
delta = clip.duration / ( numFrames - 1 ),
convertedTracks = [],
mixer = new AnimationMixer( source ),
bones = getBones( target.skeleton ),
boneDatas = [];
let positionOffset,
bone, boneTo, boneData,
name;
mixer.clipAction( clip ).play();
// trim
let start = 0, end = numFrames;
if ( options.trim !== undefined ) {
start = Math.round( options.trim[ 0 ] * options.fps );
end = Math.min( Math.round( options.trim[ 1 ] * options.fps ), numFrames ) - start;
mixer.update( options.trim[ 0 ] );
} else {
mixer.update( 0 );
}
source.updateMatrixWorld();
//
for ( let frame = 0; frame < end; ++ frame ) {
const time = frame * delta;
retarget( target, source, options );
for ( let j = 0; j < bones.length; ++ j ) {
bone = bones[ j ];
name = getBoneName( bone, options ) || bone.name;
boneTo = getBoneByName( name, source.skeleton );
if ( boneTo ) {
boneData = boneDatas[ j ] = boneDatas[ j ] || { bone: bone };
if ( options.hip === name ) {
if ( ! boneData.pos ) {
boneData.pos = {
times: new Float32Array( end ),
values: new Float32Array( end * 3 )
};
}
if ( options.useFirstFramePosition ) {
if ( frame === 0 ) {
positionOffset = bone.position.clone();
}
bone.position.sub( positionOffset );
}
boneData.pos.times[ frame ] = time;
bone.position.toArray( boneData.pos.values, frame * 3 );
}
if ( ! boneData.quat ) {
boneData.quat = {
times: new Float32Array( end ),
values: new Float32Array( end * 4 )
};
}
boneData.quat.times[ frame ] = time;
bone.quaternion.toArray( boneData.quat.values, frame * 4 );
}
}
if ( frame === end - 2 ) {
// last mixer update before final loop iteration
// make sure we do not go over or equal to clip duration
mixer.update( delta - 0.0000001 );
} else {
mixer.update( delta );
}
source.updateMatrixWorld();
}
for ( let i = 0; i < boneDatas.length; ++ i ) {
boneData = boneDatas[ i ];
if ( boneData ) {
if ( boneData.pos ) {
convertedTracks.push( new VectorKeyframeTrack(
'.bones[' + boneData.bone.name + '].position',
boneData.pos.times,
boneData.pos.values
) );
}
convertedTracks.push( new QuaternionKeyframeTrack(
'.bones[' + boneData.bone.name + '].quaternion',
boneData.quat.times,
boneData.quat.values
) );
}
}
mixer.uncacheAction( clip );
return new AnimationClip( clip.name, - 1, convertedTracks );
}
function clone( source ) {
const sourceLookup = new Map();
const cloneLookup = new Map();
const clone = source.clone();
parallelTraverse( source, clone, function ( sourceNode, clonedNode ) {
sourceLookup.set( clonedNode, sourceNode );
cloneLookup.set( sourceNode, clonedNode );
} );
clone.traverse( function ( node ) {
if ( ! node.isSkinnedMesh ) return;
const clonedMesh = node;
const sourceMesh = sourceLookup.get( node );
const sourceBones = sourceMesh.skeleton.bones;
clonedMesh.skeleton = sourceMesh.skeleton.clone();
clonedMesh.bindMatrix.copy( sourceMesh.bindMatrix );
clonedMesh.skeleton.bones = sourceBones.map( function ( bone ) {
return cloneLookup.get( bone );
} );
clonedMesh.bind( clonedMesh.skeleton, clonedMesh.bindMatrix );
} );
return clone;
}
// internal helper
function getBoneByName( name, skeleton ) {
for ( let i = 0, bones = getBones( skeleton ); i < bones.length; i ++ ) {
if ( name === bones[ i ].name )
return bones[ i ];
}
}
function getBones( skeleton ) {
return Array.isArray( skeleton ) ? skeleton : skeleton.bones;
}
function getHelperFromSkeleton( skeleton ) {
const source = new SkeletonHelper( skeleton.bones[ 0 ] );
source.skeleton = skeleton;
return source;
}
function parallelTraverse( a, b, callback ) {
callback( a, b );
for ( let i = 0; i < a.children.length; i ++ ) {
parallelTraverse( a.children[ i ], b.children[ i ], callback );
}
}
export {
retarget,
retargetClip,
clone,
};

File diff suppressed because it is too large Load Diff

1700
public/scripts/main.js Normal file

File diff suppressed because it is too large Load Diff

35459
public/scripts/three.js Normal file

File diff suppressed because one or more lines are too long

51464
public/scripts/three.module.js Normal file

File diff suppressed because one or more lines are too long

395
public/scripts/water.js Normal file
View File

@ -0,0 +1,395 @@
( function () {
/**
* Work based on :
* https://github.com/Slayvin: Flat mirror for three.js
* https://home.adelphi.edu/~stemkoski/ : An implementation of water shader based on the flat mirror
* http://29a.ch/ && http://29a.ch/slides/2012/webglwater/ : Water shader explanations in WebGL
*/
class Water extends THREE.Mesh {
constructor( geometry, options = {} ) {
super( geometry );
this.isWater = true;
const scope = this;
const textureWidth = options.textureWidth !== undefined ? options.textureWidth : 512;
const textureHeight = options.textureHeight !== undefined ? options.textureHeight : 512;
const clipBias = options.clipBias !== undefined ? options.clipBias : 0.0;
const alpha = options.alpha !== undefined ? options.alpha : 1.0;
const time = options.time !== undefined ? options.time : 0.0;
const wave_time = options.wave_time !== undefined ? options.wave_time : 0.0;
const normalSampler = options.waterNormals !== undefined ? options.waterNormals : null;
const sunDirection = options.sunDirection !== undefined ? options.sunDirection : new THREE.Vector3( 0.70707, 0.70707, 0.0 );
const sunColor = new THREE.Color( options.sunColor !== undefined ? options.sunColor : 0xffffff );
const waterColor = new THREE.Color( options.waterColor !== undefined ? options.waterColor : 0x7F7F7F );
const eye = options.eye !== undefined ? options.eye : new THREE.Vector3( 0, 0, 0 );
const distortionScale = options.distortionScale !== undefined ? options.distortionScale : 20.0;
const side = options.side !== undefined ? options.side : THREE.FrontSide;
const fog = options.fog !== undefined ? options.fog : false; //
const mirrorPlane = new THREE.Plane();
const normal = new THREE.Vector3();
const mirrorWorldPosition = new THREE.Vector3();
const cameraWorldPosition = new THREE.Vector3();
const rotationMatrix = new THREE.Matrix4();
const lookAtPosition = new THREE.Vector3( 0, 0, - 1 );
const clipPlane = new THREE.Vector4();
const view = new THREE.Vector3();
const target = new THREE.Vector3();
const q = new THREE.Vector4();
const textureMatrix = new THREE.Matrix4();
const mirrorCamera = new THREE.PerspectiveCamera();
const renderTarget = new THREE.WebGLRenderTarget( textureWidth, textureHeight );
window.mirrorShader = {
uniforms: THREE.UniformsUtils.merge( [ THREE.UniformsLib[ 'fog' ], THREE.UniformsLib[ 'lights' ], {
'normalSampler': {
value: null
},
'mirrorSampler': {
value: null
},
'alpha': {
value: 1.0
},
'time': {
value: 0.0
},
'wave_time': {
value: 0.0
},
'size': {
value: 1.0
},
'distortionScale': {
value: 20.0
},
'textureMatrix': {
value: new THREE.Matrix4()
},
'sunColor': {
value: new THREE.Color( 0x7F7F7F )
},
'sunDirection': {
value: new THREE.Vector3( 0.70707, 0.70707, 0 )
},
'eye': {
value: new THREE.Vector3()
},
'waterColor': {
value: new THREE.Color( 0x555555 )
}
} ] ),
vertexShader:
/* glsl */
`
uniform mat4 textureMatrix;
uniform float time;
uniform float wave_time;
varying vec4 mirrorCoord;
varying vec4 worldPosition;
varying vec2 vUv;
#include <common>
#include <fog_pars_vertex>
#include <shadowmap_pars_vertex>
#include <logdepthbuf_pars_vertex>
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 mod289(vec4 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 permute(vec4 x) {
return mod289(((x*34.0)+1.0)*x);
}
vec4 taylorInvSqrt(vec4 r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
float snoise(vec3 v) {
const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
// First corner
vec3 i = floor(v + dot(v, C.yyy) );
vec3 x0 = v - i + dot(i, C.xxx) ;
// Other corners
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min( g.xyz, l.zxy );
vec3 i2 = max( g.xyz, l.zxy );
// x0 = x0 - 0.0 + 0.0 * C.xxx;
// x1 = x0 - i1 + 1.0 * C.xxx;
// x2 = x0 - i2 + 2.0 * C.xxx;
// x3 = x0 - 1.0 + 3.0 * C.xxx;
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
// Permutations
i = mod289(i);
vec4 p = permute( permute( permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
// Gradients: 7x7 points over a square, mapped onto an octahedron.
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
float n_ = 0.142857142857; // 1.0/7.0
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
vec4 x = x_ *ns.x + ns.yyyy;
vec4 y = y_ *ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4( x.xy, y.xy );
vec4 b1 = vec4( x.zw, y.zw );
//vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
//vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
vec4 s0 = floor(b0)*2.0 + 1.0;
vec4 s1 = floor(b1)*2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
vec3 p0 = vec3(a0.xy,h.x);
vec3 p1 = vec3(a0.zw,h.y);
vec3 p2 = vec3(a1.xy,h.z);
vec3 p3 = vec3(a1.zw,h.w);
// Normalise gradients
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
// Mix final noise value
vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
m = m * m;
return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
dot(p2,x2), dot(p3,x3) ) );
}
void main() {
mirrorCoord = modelMatrix * vec4( position, 1.0 );
worldPosition = mirrorCoord.xyzw;
mirrorCoord = textureMatrix * mirrorCoord;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
vUv = uv;
vec3 pos = position;
float noiseFreq = 3.5;
float noiseAmp = 0.45;
vec3 noisePos = vec3(pos.x * noiseFreq + time, pos.y, pos.z);
pos.z += snoise(noisePos) * noiseAmp;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.);
#include <beginnormal_vertex>
#include <defaultnormal_vertex>
#include <logdepthbuf_vertex>
#include <fog_vertex>
#include <shadowmap_vertex>
}`,
fragmentShader:
/* glsl */
`
uniform sampler2D mirrorSampler;
uniform float alpha;
uniform float time;
uniform float size;
uniform float distortionScale;
uniform sampler2D normalSampler;
uniform vec3 sunColor;
uniform vec3 sunDirection;
uniform vec3 eye;
uniform vec3 waterColor;
varying vec4 mirrorCoord;
varying vec4 worldPosition;
vec4 getNoise( vec2 uv ) {
vec2 uv0 = ( uv / 103.0 ) + vec2(time / 17.0, time / 29.0);
vec2 uv1 = uv / 107.0-vec2( time / -19.0, time / 31.0 );
vec2 uv2 = uv / vec2( 8907.0, 9803.0 ) + vec2( time / 101.0, time / 97.0 );
vec2 uv3 = uv / vec2( 1091.0, 1027.0 ) - vec2( time / 109.0, time / -113.0 );
vec4 noise = texture2D( normalSampler, uv0 ) +
texture2D( normalSampler, uv1 ) +
texture2D( normalSampler, uv2 ) +
texture2D( normalSampler, uv3 );
return noise * 0.5 - 1.0;
}
void sunLight( const vec3 surfaceNormal, const vec3 eyeDirection, float shiny, float spec, float diffuse, inout vec3 diffuseColor, inout vec3 specularColor ) {
vec3 reflection = normalize( reflect( -sunDirection, surfaceNormal ) );
float direction = max( 0.0, dot( eyeDirection, reflection ) );
specularColor += pow( direction, shiny ) * sunColor * spec;
diffuseColor += max( dot( sunDirection, surfaceNormal ), 0.0 ) * sunColor * diffuse;
}
#include <common>
#include <packing>
#include <bsdfs>
#include <fog_pars_fragment>
#include <logdepthbuf_pars_fragment>
#include <lights_pars_begin>
#include <shadowmap_pars_fragment>
#include <shadowmask_pars_fragment>
void main() {
#include <logdepthbuf_fragment>
vec4 noise = getNoise( worldPosition.xz * size );
vec3 surfaceNormal = normalize( noise.xzy * vec3( 1.5, 1.0, 1.5 ) );
vec3 diffuseLight = vec3(0.0);
vec3 specularLight = vec3(0.0);
vec3 worldToEye = eye-worldPosition.xyz;
vec3 eyeDirection = normalize( worldToEye );
sunLight( surfaceNormal, eyeDirection, 100.0, 2.0, 0.5, diffuseLight, specularLight );
float distance = length(worldToEye);
vec2 distortion = surfaceNormal.xz * ( 0.001 + 1.0 / distance ) * distortionScale;
vec3 reflectionSample = vec3( texture2D( mirrorSampler, mirrorCoord.xy / mirrorCoord.w + distortion ) );
float theta = max( dot( eyeDirection, surfaceNormal ), 0.0 );
float rf0 = 0.3;
float reflectance = rf0 + ( 1.0 - rf0 ) * pow( ( 1.0 - theta ), 5.0 );
vec3 scatter = max( 0.0, dot( surfaceNormal, eyeDirection ) ) * waterColor;
vec3 albedo = mix( ( sunColor * diffuseLight * 0.3 + scatter ) * getShadowMask(), ( vec3( 0.1 ) + reflectionSample * 0.9 + reflectionSample * specularLight ), reflectance);
vec3 outgoingLight = albedo;
gl_FragColor = vec4( outgoingLight, alpha );
#include <tonemapping_fragment>
#include <fog_fragment>
}`
};
const material = new THREE.ShaderMaterial( {
fragmentShader: mirrorShader.fragmentShader,
vertexShader: mirrorShader.vertexShader,
uniforms: THREE.UniformsUtils.clone( mirrorShader.uniforms ),
lights: true,
side: side,
fog: fog
} );
material.uniforms[ 'mirrorSampler' ].value = renderTarget.texture;
material.uniforms[ 'textureMatrix' ].value = textureMatrix;
material.uniforms[ 'alpha' ].value = alpha;
material.uniforms[ 'time' ].value = time;
material.uniforms[ 'wave_time' ].value = time;
material.uniforms[ 'normalSampler' ].value = normalSampler;
material.uniforms[ 'sunColor' ].value = sunColor;
material.uniforms[ 'waterColor' ].value = waterColor;
material.uniforms[ 'sunDirection' ].value = sunDirection;
material.uniforms[ 'distortionScale' ].value = distortionScale;
material.uniforms[ 'eye' ].value = eye;
scope.material = material;
scope.onBeforeRender = function ( renderer, scene, camera ) {
mirrorWorldPosition.setFromMatrixPosition( scope.matrixWorld );
cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld );
rotationMatrix.extractRotation( scope.matrixWorld );
normal.set( 0, 0, 1 );
normal.applyMatrix4( rotationMatrix );
view.subVectors( mirrorWorldPosition, cameraWorldPosition ); // Avoid rendering when mirror is facing away
if ( view.dot( normal ) > 0 ) return;
view.reflect( normal ).negate();
view.add( mirrorWorldPosition );
rotationMatrix.extractRotation( camera.matrixWorld );
lookAtPosition.set( 0, 0, - 1 );
lookAtPosition.applyMatrix4( rotationMatrix );
lookAtPosition.add( cameraWorldPosition );
target.subVectors( mirrorWorldPosition, lookAtPosition );
target.reflect( normal ).negate();
target.add( mirrorWorldPosition );
mirrorCamera.position.copy( view );
mirrorCamera.up.set( 0, 1, 0 );
mirrorCamera.up.applyMatrix4( rotationMatrix );
mirrorCamera.up.reflect( normal );
mirrorCamera.lookAt( target );
mirrorCamera.far = camera.far; // Used in WebGLBackground
mirrorCamera.updateMatrixWorld();
mirrorCamera.projectionMatrix.copy( camera.projectionMatrix ); // Update the texture matrix
textureMatrix.set( 0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0 );
textureMatrix.multiply( mirrorCamera.projectionMatrix );
textureMatrix.multiply( mirrorCamera.matrixWorldInverse ); // Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html
// Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf
mirrorPlane.setFromNormalAndCoplanarPoint( normal, mirrorWorldPosition );
mirrorPlane.applyMatrix4( mirrorCamera.matrixWorldInverse );
clipPlane.set( mirrorPlane.normal.x, mirrorPlane.normal.y, mirrorPlane.normal.z, mirrorPlane.constant );
const projectionMatrix = mirrorCamera.projectionMatrix;
q.x = ( Math.sign( clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
q.y = ( Math.sign( clipPlane.y ) + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ];
q.z = - 1.0;
q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ]; // Calculate the scaled plane vector
clipPlane.multiplyScalar( 2.0 / clipPlane.dot( q ) ); // Replacing the third row of the projection matrix
projectionMatrix.elements[ 2 ] = clipPlane.x;
projectionMatrix.elements[ 6 ] = clipPlane.y;
projectionMatrix.elements[ 10 ] = clipPlane.z + 1.0 - clipBias;
projectionMatrix.elements[ 14 ] = clipPlane.w;
eye.setFromMatrixPosition( camera.matrixWorld ); // Render
const currentRenderTarget = renderer.getRenderTarget();
const currentXrEnabled = renderer.xr.enabled;
const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
scope.visible = false;
renderer.xr.enabled = false; // Avoid camera modification and recursion
renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows
renderer.setRenderTarget( renderTarget );
renderer.state.buffers.depth.setMask( true ); // make sure the depth buffer is writable so it can be properly cleared, see #18897
if ( renderer.autoClear === false ) renderer.clear();
renderer.render( scene, mirrorCamera );
scope.visible = true;
renderer.xr.enabled = currentXrEnabled;
renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
renderer.setRenderTarget( currentRenderTarget ); // Restore viewport
const viewport = camera.viewport;
if ( viewport !== undefined ) {
renderer.state.viewport( viewport );
}
};
}
}
THREE.Water = Water;
} )();

BIN
public/seagull.glb Normal file

Binary file not shown.

435
public/shaders/shaders.js Normal file
View File

@ -0,0 +1,435 @@
const island_vertecies = `
#include <common>
#include <shadowmap_pars_vertex>
varying vec3 vNormal;
varying vec3 vViewDir;
varying vec2 vUv;
varying vec3 FragPos;
void main() {
vUv = uv;
#include <beginnormal_vertex>
#include <defaultnormal_vertex>
#include <begin_vertex>
#include <worldpos_vertex>
#include <shadowmap_vertex>
vec4 modelPosition = modelMatrix * vec4(position, 1.0);
vec4 viewPosition = viewMatrix * modelPosition;
vec4 clipPosition = projectionMatrix * viewPosition;
vNormal = normalize(normal);
// vNormal = normalize(normalMatrix * normal);
vViewDir = normalize(-viewPosition.xyz);
FragPos = vec3(modelMatrix * vec4(position, 1.0));
gl_Position = clipPosition;
}
`;
const island_fragments = `
#include <common>
#include <packing>
#include <lights_pars_begin>
#include <shadowmap_pars_fragment>
#include <shadowmask_pars_fragment>
uniform sampler2D texture1;
uniform sampler2D texture2;
uniform sampler2D texture3;
uniform float intensity;
uniform float pointLightsPosX;
uniform float pointLightsPosY;
uniform float pointLightsPosZ;
varying vec3 vNormal;
varying vec3 FragPos;
varying vec2 vUv;
void main() {
vec3 pointLightsPos = vec3(9.0, 10.0, -16.0);
vec3 pointLightsColor = pointLights[0].color;
vec4 t1 = texture2D( texture1, vUv );
vec4 t2 = texture2D( texture2, vUv );
vec4 t3 = texture2D( texture3, vUv );
vec4 interrim = mix( t1, t2, t2.a );
vec4 interrim2 = mix( interrim, t3, t3.a );
// vec3 lightDir = normalize(pointLightsPos - FragPos);
vec3 lightDir = normalize(pointLightsPos);
float diff = max(dot(vNormal, -lightDir), 0.0);
vec3 diffuse = diff * pointLightsColor * interrim2.rgb;
vec3 result = ( (ambientLightColor + diffuse) / 14.0);
vec4 color = vec4(result, 1.0);
gl_FragColor = vec4(color);
}
`;
const palm_vertecies = `
#define STANDARD
uniform float uTime;
varying vec3 vViewPosition;
#ifdef USE_TRANSMISSION
varying vec3 vWorldPosition;
#endif
#include <common>
#include <uv_pars_vertex>
#include <uv2_pars_vertex>
#include <displacementmap_pars_vertex>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <normal_pars_vertex>
#include <morphtarget_pars_vertex>
#include <skinning_pars_vertex>
#include <shadowmap_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
void main() {
#include <uv_vertex>
#include <uv2_vertex>
#include <color_vertex>
#include <morphcolor_vertex>
#include <beginnormal_vertex>
#include <morphnormal_vertex>
#include <skinbase_vertex>
#include <skinnormal_vertex>
#include <defaultnormal_vertex>
#include <normal_vertex>
#include <begin_vertex>
#include <morphtarget_vertex>
#include <skinning_vertex>
#include <displacementmap_vertex>
#include <project_vertex>
#include <logdepthbuf_vertex>
#include <clipping_planes_vertex>
vViewPosition = - mvPosition.xyz;
#include <worldpos_vertex>
#include <shadowmap_vertex>
#include <fog_vertex>
vUv = uv;
vec3 delta = normal * sin(position.x * position.y * uTime * 0.0001) * 10.0;
vec3 newPosition = position + delta;
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
#ifdef USE_TRANSMISSION
vWorldPosition = worldPosition.xyz;
#endif
}`;
const palm_fragments = `
#define STANDARD
#ifdef PHYSICAL
#define IOR
#define SPECULAR
#endif
uniform vec3 diffuse;
uniform vec3 emissive;
uniform float roughness;
uniform float metalness;
uniform float opacity;
#ifdef IOR
uniform float ior;
#endif
#ifdef SPECULAR
uniform float specularIntensity;
uniform vec3 specularColor;
#ifdef USE_SPECULARINTENSITYMAP
uniform sampler2D specularIntensityMap;
#endif
#ifdef USE_SPECULARCOLORMAP
uniform sampler2D specularColorMap;
#endif
#endif
#ifdef USE_CLEARCOAT
uniform float clearcoat;
uniform float clearcoatRoughness;
#endif
#ifdef USE_IRIDESCENCE
uniform float iridescence;
uniform float iridescenceIOR;
uniform float iridescenceThicknessMinimum;
uniform float iridescenceThicknessMaximum;
#endif
#ifdef USE_SHEEN
uniform vec3 sheenColor;
uniform float sheenRoughness;
#ifdef USE_SHEENCOLORMAP
uniform sampler2D sheenColorMap;
#endif
#ifdef USE_SHEENROUGHNESSMAP
uniform sampler2D sheenRoughnessMap;
#endif
#endif
varying vec3 vViewPosition;
#include <common>
#include <packing>
#include <dithering_pars_fragment>
#include <color_pars_fragment>
#include <uv_pars_fragment>
#include <uv2_pars_fragment>
#include <map_pars_fragment>
#include <alphamap_pars_fragment>
#include <alphatest_pars_fragment>
#include <aomap_pars_fragment>
#include <lightmap_pars_fragment>
#include <emissivemap_pars_fragment>
#include <bsdfs>
#include <iridescence_fragment>
#include <cube_uv_reflection_fragment>
#include <envmap_common_pars_fragment>
#include <envmap_physical_pars_fragment>
#include <fog_pars_fragment>
#include <lights_pars_begin>
#include <normal_pars_fragment>
#include <lights_physical_pars_fragment>
#include <transmission_pars_fragment>
#include <shadowmap_pars_fragment>
#include <bumpmap_pars_fragment>
#include <normalmap_pars_fragment>
#include <clearcoat_pars_fragment>
#include <iridescence_pars_fragment>
#include <roughnessmap_pars_fragment>
#include <metalnessmap_pars_fragment>
#include <logdepthbuf_pars_fragment>
#include <clipping_planes_pars_fragment>
void main() {
#include <clipping_planes_fragment>
vec4 diffuseColor = vec4( diffuse, opacity );
ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
vec3 totalEmissiveRadiance = emissive;
#include <logdepthbuf_fragment>
#include <map_fragment>
#include <color_fragment>
#include <alphamap_fragment>
#include <alphatest_fragment>
#include <roughnessmap_fragment>
#include <metalnessmap_fragment>
#include <normal_fragment_begin>
#include <normal_fragment_maps>
#include <clearcoat_normal_fragment_begin>
#include <clearcoat_normal_fragment_maps>
#include <emissivemap_fragment>
#include <lights_physical_fragment>
#include <lights_fragment_begin>
#include <lights_fragment_maps>
#include <lights_fragment_end>
#include <aomap_fragment>
vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;
vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;
#include <transmission_fragment>
vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;
#ifdef USE_SHEEN
float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor );
outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular;
#endif
#ifdef USE_CLEARCOAT
float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );
vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );
outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat;
#endif
#include <output_fragment>
#include <tonemapping_fragment>
#include <encodings_fragment>
#include <fog_fragment>
#include <premultiplied_alpha_fragment>
#include <dithering_fragment>
}
`;
const particle_vertecies = `
attribute float rots;
varying float vRots;
varying vec3 pos;
varying vec2 vUv;
uniform float time;
attribute float size;
uniform float scale;
#include <common>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <morphtarget_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 mod289(vec4 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 permute(vec4 x) {
return mod289(((x*34.0)+1.0)*x);
}
vec4 taylorInvSqrt(vec4 r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
float snoise(vec3 v) {
const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
// First corner
vec3 i = floor(v + dot(v, C.yyy) );
vec3 x0 = v - i + dot(i, C.xxx) ;
// Other corners
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min( g.xyz, l.zxy );
vec3 i2 = max( g.xyz, l.zxy );
// x0 = x0 - 0.0 + 0.0 * C.xxx;
// x1 = x0 - i1 + 1.0 * C.xxx;
// x2 = x0 - i2 + 2.0 * C.xxx;
// x3 = x0 - 1.0 + 3.0 * C.xxx;
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
// Permutations
i = mod289(i);
vec4 p = permute( permute( permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
// Gradients: 7x7 points over a square, mapped onto an octahedron.
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
float n_ = 0.142857142857; // 1.0/7.0
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
vec4 x = x_ *ns.x + ns.yyyy;
vec4 y = y_ *ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4( x.xy, y.xy );
vec4 b1 = vec4( x.zw, y.zw );
//vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
//vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
vec4 s0 = floor(b0)*2.0 + 1.0;
vec4 s1 = floor(b1)*2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
vec3 p0 = vec3(a0.xy,h.x);
vec3 p1 = vec3(a0.zw,h.y);
vec3 p2 = vec3(a1.xy,h.z);
vec3 p3 = vec3(a1.zw,h.w);
// Normalise gradients
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
// Mix final noise value
vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
m = m * m;
return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
dot(p2,x2), dot(p3,x3) ) );
}
void main() {
#include <color_vertex>
#include <morphcolor_vertex>
#include <begin_vertex>
#include <morphtarget_vertex>
#include <project_vertex>
gl_PointSize = size;
pos = position;
float noiseFreq = 3.5;
float noiseAmp = 0.15;
vec3 noisePos = vec3(pos.x * noiseFreq + ${Math.random() * 0.5 + 0.1}, pos.y, pos.z);
gl_Position.x += snoise(noisePos) * noiseAmp * 10.0;
gl_Position.y += snoise(noisePos) * noiseAmp * 1.0;
#ifdef USE_SIZEATTENUATION
bool isPerspective = isPerspectiveMatrix( projectionMatrix );
if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );
#endif
#include <logdepthbuf_vertex>
#include <clipping_planes_vertex>
#include <worldpos_vertex>
#include <fog_vertex>
vRots = rots;
}
`;
const particle_fragments = `
varying float vRots;
uniform sampler2D tDiffuse;
varying vec2 vUv;
uniform vec3 diffuse;
uniform float opacity;
varying vec3 pos;
#include <common>
#include <color_pars_fragment>
#include <map_particle_pars_fragment>
#include <alphatest_pars_fragment>
#include <fog_pars_fragment>
#include <logdepthbuf_pars_fragment>
#include <clipping_planes_pars_fragment>
// float rand(vec2 co)
// {
// float a = 12.9898;
// float b = 78.233;
// float c = 43758.5453;
// float dt= dot(co.xy ,vec2(a,b));
// float sn= mod(dt,3.14);
// return fract(sin(sn) * c);
// }
void main() {
#include <clipping_planes_fragment>
vec3 outgoingLight = vec3( 0.0 );
vec4 diffuseColor = vec4( diffuse, opacity );
if ( length( gl_PointCoord - vec2( 0.5, 0.5 ) ) > 0.475 ) discard;
// outgoingLight = diffuseColor.rgb + sin(rand(vec2( time, time) ) );
// gl_FragColor.a = 0.1;
// gl_FragColor = texture2D(tDiffuse, vUv);
// gl_FragColor.r = 256.0;
// gl_FragColor.g = 256.0;
// gl_FragColor = diffuseColor;
#include <logdepthbuf_fragment>
#include <map_particle_fragment>
#include <color_fragment>
#include <alphatest_fragment>
outgoingLight = diffuseColor.rgb + rand(vec2( pos.x, pos.y) ) ;
#include <output_fragment>
#include <tonemapping_fragment>
#include <encodings_fragment>
#include <fog_fragment>
#include <premultiplied_alpha_fragment>
}
`

47
ship.js Normal file
View File

@ -0,0 +1,47 @@
// const {sequelize, DataTypes} = require('./db/sql_connection');
require('dotenv').config({ path: require('node:path').resolve(process.cwd(), '.env') });
const APP_BASE_PATH = process.env.APP_BASE_PATH || "";
const express = require("express");
const app = express(); //
// const fs = require("fs");
const http = require("http").createServer(app);
const io = require("socket.io")(http);
// const path = require("path");
var id = [];
var obj_collection = {};
http.listen(process.env.PORT, process.env.HOST, function () {
console.log("game server is started");
})
io.on("connection", function (socket) {
console.log("user " + socket.id + " is connected");
socket.on("mouseclick", (coords) => {
console.log("coords");
console.log(coords);
socket.broadcast.emit("coords", coords);
id[socket.id] = coords.id;
obj_collection[coords.id] = coords;
})
socket.on("disconnect", () => {
console.log("user " + socket.id + " is disconnected");
socket.broadcast.emit("remove", id[socket.id]);
delete obj_collection[id[socket.id]];
delete id[socket.id];
})
socket.on("loaded", () => {
console.log("loaded");
io.emit("send_objects", obj_collection);
})
})
app.set("view engine", "ejs");
app.use(express.static("public"));
app.use(express.json({extended: false}));
app.get("/", (req, res) => {
res.render("game", {
base_path: APP_BASE_PATH,
});
});

0
tmp/restart.txt Normal file
View File

108
views/game.ejs Normal file
View File

@ -0,0 +1,108 @@
<!DOCTYPE html>
<html>
<meta Content-Type: application/javascript; charset=UTF-8>
<link rel="stylesheet" type="text/css" href="./css/style.css">
<body>
<div class="big_container">
<div class="menu_ico">
<div>
</div>
<div class="slidecontainer">
<div id="sheet1" class="sheet">
<small>Поворот сцены — мышь + зажата левая нопка</small>
<br>
<small>Перемещение сцены — мышь + левая кнопка + shift</small>
<br>
<small>Масштаб — прокрутка или зажатие колесика</small>
<br>
<br>
<div>Море</div>
<input id="range" type="range" min="1" max="50" value="1" class="slider">
</div>
<div id="sheet2" class="sheet hide">
<small>Клик выделяет кубик — точку границы коллизии</small>
<br>
<small>Мышь + зажатая левая кнопка — перемещение кубика</small>
<br>
<small>Кнопка "Проверить" активирует движение корабля</small>
<br>
<br>
<div>Море</div>
<input id="range" type="range" min="1" max="50" value="1" class="slider">
</div>
<div id="checkbox-container">
<div>
<input type="checkbox" id="map" name="map" />
<label for="map">Карта</label>
</div>
<div>
<input type="checkbox" id="collision_edit" name="collision_edit" />
<label for="collision_edit">Редактировать коллизию</label>
</div>
<div>
<input type="checkbox" id="collision_edit2" name="collision_edit" disabled = "true" checked />
<label for="collision_edit2">Обход по кривой Лагранжа</label>
<button id="lagr_test" disabled = "true">Проверить путь</button>
</div>
<div>
<input type="radio" id="border_left" name="border" value="left" />
<label for="border_left">Левая граница</label>
<input type="radio" id="border_right" name="border" value="right" checked/>
<label for="border_right">Правая граница</label>
</div>
</div>
</div>
</div>
<div class="container">
<canvas id="c"></canvas>
<canvas id="canvas_temp"></canvas>
<canvas id="canvas_temp2"></canvas>
<div class="top-div"></div>
</div>
</div>
<script>
window.container = document.querySelector(".container");
window.canvas = document.querySelector('#c');
window.canvas2 = document.querySelector('#canvas_temp');
window.ctx2 = canvas2.getContext("2d");
canvas2.width = 750;
canvas2.height = 500;
window.canvas3 = document.querySelector('#canvas_temp2');
window.ctx3 = canvas3.getContext("2d");
canvas3.width = 750;
canvas3.height = 500;
// container.appendChild(canvas2);
window.loading_text = document.querySelector('.top-div');
const base_path = "<%=base_path%>";
</script>
<script src="./scripts/three.js"></script>
<script src="<%=base_path%>/socket.io/socket.io.js"></script>
<script src="./scripts/three.module.js"></script>
<script src="./scripts/OrbitControls.js"></script>
<script src="./scripts/GLTFLoader.js"></script>
<script src="./scripts/fflate.module.js"></script>
<script src="./scripts/NURBSUtils.js"></script>
<script src="./scripts/NURBSCurve.js"></script>
<script src="./scripts/FBXLoader.js"></script>
<script src="./assets/textures.js"></script>
<script src="./assets/models.js"></script>
<script src="./assets/models_man.js"></script>
<script src="./scripts/water.js"></script>
<script src='./shaders/shaders.js'>
</script>
<script type='module'>
import * as SkeletonUtils from './scripts/SkeletonUtils.js';
window.SkeletonUtils = SkeletonUtils;
</script>
<script src="./scripts/main.js"></script>
</body>
</html>