提交 1870761d 编写于 作者: 6 6370cc9e5824896eaeb80ac3

remove mythos

上级 3a010da7
/* Copy this into a file named apiKey.js */
const apiKey = '1DpF4lnxxGr0IjKbKlOm58A9GuKbFyKaxh2PDK87x';
const appId = 'io.croquet.mythos';
export default {apiKey, appId};
\ No newline at end of file
const apiKey = "paste your apiKey from croquet.io/keys";
const appId = "type your own appId such as com.example.david.mymicroverse";
export default {apiKey, appId};
// For development on local computer and local network, use a development key
// from croquet.io/keys and create a file called apiKey-dev.js.
// For deployment to a publicly accessible server, create a production key
// from croquet.io/keys and create a file called apiKey.js.
// you may export other Croquet session parameters to override default values.
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg width="178" height="178" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 178 178" style="enable-background:new 0 0 178 178;" xml:space="preserve">
<style type="text/css">
.st0{fill:#F0493E;}
.st1{fill:#4E4E4E;}
.st2{fill:url(#SVGID_1_);}
.st3{fill:#F2F2F2;}
.st4{filter:url(#Adobe_OpacityMaskFilter);}
.st5{filter:url(#Adobe_OpacityMaskFilter_1_);}
.st6{mask:url(#SVGID_2_);fill:url(#SVGID_3_);}
.st7{fill:url(#SVGID_4_);}
.st8{filter:url(#Adobe_OpacityMaskFilter_2_);}
.st9{filter:url(#Adobe_OpacityMaskFilter_3_);}
.st10{mask:url(#SVGID_5_);fill:url(#SVGID_6_);}
.st11{fill:url(#SVGID_7_);}
.st12{filter:url(#Adobe_OpacityMaskFilter_4_);}
.st13{filter:url(#Adobe_OpacityMaskFilter_5_);}
.st14{mask:url(#SVGID_8_);fill:url(#SVGID_9_);}
.st15{fill:url(#SVGID_10_);}
.st16{fill:#E6E6E5;}
</style>
<path class="st0" d="M150.37,85.98c0,16.01-5.68,29.78-14.98,40.28v29.61c0,1.03-0.17,1.72-0.52,2.07c-0.52,0.52-1.38,0.52-2.58,0
c-9.81-4.48-26.34-10.84-29.09-11.88c-5.16,1.38-10.67,2.07-16.01,2.07c-36.84,0-63.17-27.2-63.17-62.31
c0-37.01,28.75-62.14,63.17-62.14C120.07,23.67,150.37,47.6,150.37,85.98z"/>
<g id="Sphere">
<g>
<g>
<ellipse class="st1" cx="87.21" cy="108.88" rx="6.58" ry="2.61"/>
</g>
</g>
<g>
<radialGradient id="SVGID_1_" cx="81.1021" cy="80.6184" r="38.7143" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.3979" style="stop-color:#FFFFFF"/>
<stop offset="0.7906" style="stop-color:#E6E7E7"/>
<stop offset="1" style="stop-color:#FAFBFC"/>
</radialGradient>
<path class="st2" d="M113.76,85.51c0,14.67-11.89,26.56-26.56,26.56c-14.67,0-26.56-11.89-26.56-26.56
c0-14.67,11.89-26.56,26.56-26.56C101.87,58.95,113.76,70.84,113.76,85.51z"/>
<path class="st3" d="M113.76,85.47c0,14.69-11.89,26.6-26.56,26.6c-14.67,0-26.56-11.91-26.56-26.6c0-14.69,11.89-26.6,26.56-26.6
C101.87,58.87,113.76,70.78,113.76,85.47z"/>
<defs>
<filter id="Adobe_OpacityMaskFilter" filterUnits="userSpaceOnUse" x="60.65" y="58.95" width="53.12" height="53.12">
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
</filter>
</defs>
<mask maskUnits="userSpaceOnUse" x="60.65" y="58.95" width="53.12" height="53.12" id="SVGID_2_">
<g class="st4">
<defs>
<filter id="Adobe_OpacityMaskFilter_1_" filterUnits="userSpaceOnUse" x="60.65" y="58.95" width="53.12" height="53.12">
<feFlood style="flood-color:white;flood-opacity:1" result="back"/>
<feBlend in="SourceGraphic" in2="back" mode="normal"/>
</filter>
</defs>
<mask maskUnits="userSpaceOnUse" x="60.65" y="58.95" width="53.12" height="53.12" id="SVGID_2_">
<g class="st5">
</g>
</mask>
<radialGradient id="SVGID_3_" cx="79.3369" cy="77.7214" r="37.9679" gradientUnits="userSpaceOnUse">
<stop offset="0.0681" style="stop-color:#CCCCCC"/>
<stop offset="0.4274" style="stop-color:#000000"/>
<stop offset="0.8872" style="stop-color:#4D4D4D"/>
<stop offset="0.9201" style="stop-color:#505050"/>
<stop offset="0.9505" style="stop-color:#5B5B5B"/>
<stop offset="0.9799" style="stop-color:#6E6E6E"/>
<stop offset="1" style="stop-color:#808080"/>
</radialGradient>
<path class="st6" d="M113.76,85.51c0,14.67-11.89,26.56-26.56,26.56c-14.67,0-26.56-11.89-26.56-26.56
c0-14.67,11.89-26.56,26.56-26.56C101.87,58.95,113.76,70.84,113.76,85.51z"/>
</g>
</mask>
<radialGradient id="SVGID_4_" cx="79.3369" cy="77.7214" r="37.9679" gradientUnits="userSpaceOnUse">
<stop offset="0.0681" style="stop-color:#FFFFFF"/>
<stop offset="0.4274" style="stop-color:#FFFFFF"/>
<stop offset="0.8872" style="stop-color:#FFFFFF"/>
<stop offset="0.9361" style="stop-color:#F9F9F9"/>
<stop offset="0.9812" style="stop-color:#EDEDED"/>
<stop offset="1" style="stop-color:#E6E7E7"/>
</radialGradient>
<path class="st7" d="M113.76,85.51c0,14.67-11.89,26.56-26.56,26.56c-14.67,0-26.56-11.89-26.56-26.56
c0-14.67,11.89-26.56,26.56-26.56C101.87,58.95,113.76,70.84,113.76,85.51z"/>
<defs>
<filter id="Adobe_OpacityMaskFilter_2_" filterUnits="userSpaceOnUse" x="60.65" y="58.95" width="53.12" height="53.12">
<feFlood style="flood-color:white;flood-opacity:1" result="back"/>
<feBlend in="SourceGraphic" in2="back" mode="normal"/>
</filter>
</defs>
<mask maskUnits="userSpaceOnUse" x="60.65" y="58.95" width="53.12" height="53.12" id="SVGID_5_">
<g class="st8">
<defs>
<filter id="Adobe_OpacityMaskFilter_3_" filterUnits="userSpaceOnUse" x="60.65" y="58.95" width="53.12" height="53.12">
<feFlood style="flood-color:white;flood-opacity:1" result="back"/>
<feBlend in="SourceGraphic" in2="back" mode="normal"/>
</filter>
</defs>
<mask maskUnits="userSpaceOnUse" x="60.65" y="58.95" width="53.12" height="53.12" id="SVGID_5_">
<g class="st9">
</g>
</mask>
<radialGradient id="SVGID_6_" cx="77.6154" cy="78.1318" r="39.3995" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#000000"/>
<stop offset="0.1636" style="stop-color:#030303"/>
<stop offset="0.3055" style="stop-color:#0E0E0E"/>
<stop offset="0.4392" style="stop-color:#202020"/>
<stop offset="0.5681" style="stop-color:#393939"/>
<stop offset="0.6921" style="stop-color:#595959"/>
<stop offset="0.8115" style="stop-color:#808080"/>
<stop offset="1" style="stop-color:#B3B3B3"/>
</radialGradient>
<path class="st10" d="M113.76,85.51c0,14.67-11.89,26.56-26.56,26.56c-14.67,0-26.56-11.89-26.56-26.56
c0-14.67,11.89-26.56,26.56-26.56C101.87,58.95,113.76,70.84,113.76,85.51z"/>
</g>
</mask>
<radialGradient id="SVGID_7_" cx="76.0346" cy="72.7713" r="44.5723" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.1561" style="stop-color:#F9F8F8"/>
<stop offset="0.3085" style="stop-color:#ECEBEA"/>
<stop offset="0.4594" style="stop-color:#D9D8D8"/>
<stop offset="0.6085" style="stop-color:#C1C0C0"/>
<stop offset="0.6764" style="stop-color:#B4B4B4"/>
<stop offset="0.8809" style="stop-color:#676767"/>
<stop offset="0.9063" style="stop-color:#6A6A6A"/>
<stop offset="0.9295" style="stop-color:#737272"/>
<stop offset="0.9519" style="stop-color:#838282"/>
<stop offset="0.9737" style="stop-color:#9C9A9A"/>
<stop offset="0.995" style="stop-color:#C0BFBF"/>
<stop offset="1" style="stop-color:#CCCCCC"/>
</radialGradient>
<path class="st11" d="M113.76,85.51c0,14.67-11.89,26.56-26.56,26.56c-14.67,0-26.56-11.89-26.56-26.56
c0-14.67,11.89-26.56,26.56-26.56C101.87,58.95,113.76,70.84,113.76,85.51z"/>
<defs>
<filter id="Adobe_OpacityMaskFilter_4_" filterUnits="userSpaceOnUse" x="68.8" y="60.25" width="24.99" height="16.47">
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
</filter>
</defs>
<mask maskUnits="userSpaceOnUse" x="68.8" y="60.25" width="24.99" height="16.47" id="SVGID_8_">
<g class="st12">
<defs>
<filter id="Adobe_OpacityMaskFilter_5_" filterUnits="userSpaceOnUse" x="68.8" y="60.25" width="24.99" height="16.47">
<feFlood style="flood-color:white;flood-opacity:1" result="back"/>
<feBlend in="SourceGraphic" in2="back" mode="normal"/>
</filter>
</defs>
<mask maskUnits="userSpaceOnUse" x="68.8" y="60.25" width="24.99" height="16.47" id="SVGID_8_">
<g class="st13">
</g>
</mask>
<radialGradient id="SVGID_9_" cx="1817.975" cy="2025.5864" r="18.1632" gradientTransform="matrix(0.6766 -0.207 0.1234 0.4034 -1400.4542 -374.2097)" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.0851" style="stop-color:#F6F6F6"/>
<stop offset="0.2212" style="stop-color:#DFDFDF"/>
<stop offset="0.3913" style="stop-color:#B9B9B9"/>
<stop offset="0.5888" style="stop-color:#848484"/>
<stop offset="0.8063" style="stop-color:#414141"/>
<stop offset="1" style="stop-color:#000000"/>
</radialGradient>
<path class="st14" d="M93.58,64.72c1.24,4.05-3.26,9.01-10.05,11.09c-6.78,2.08-13.29,0.48-14.53-3.57
c-1.24-4.05,3.26-9.01,10.05-11.09C85.84,59.08,92.35,60.67,93.58,64.72z"/>
</g>
</mask>
<radialGradient id="SVGID_10_" cx="1817.975" cy="2025.5864" r="18.1632" gradientTransform="matrix(0.6766 -0.207 0.1234 0.4034 -1400.4542 -374.2097)" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</radialGradient>
<path class="st15" d="M93.58,64.72c1.24,4.05-3.26,9.01-10.05,11.09c-6.78,2.08-13.29,0.48-14.53-3.57
c-1.24-4.05,3.26-9.01,10.05-11.09C85.84,59.08,92.35,60.67,93.58,64.72z"/>
</g>
<g>
<path class="st16" d="M87.21,112.16c-7.12,0-13.81-2.77-18.85-7.81c-5.03-5.03-7.8-11.73-7.8-18.85c0-7.12,2.77-13.81,7.8-18.85
c5.03-5.03,11.73-7.81,18.85-7.81c7.12,0,13.81,2.77,18.85,7.81c5.03,5.03,7.81,11.73,7.81,18.85c0,7.12-2.77,13.81-7.81,18.85
C101.02,109.39,94.32,112.16,87.21,112.16z M87.21,59.05c-14.59,0-26.46,11.87-26.46,26.46c0,14.59,11.87,26.46,26.46,26.46
c14.59,0,26.46-11.87,26.46-26.46C113.67,70.92,101.8,59.05,87.21,59.05z"/>
</g>
</g>
</svg>
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 64 (93537) - https://sketch.com -->
<title>icon/material/edit</title>
<desc>Created with Sketch.</desc>
<g id="icon/material/edit" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="ic-round-edit">
<g id="Icon" fill="#4D4D4D">
<path d="M3,17.46 L3,20.5 C3,20.78 3.22,21 3.5,21 L6.54,21 C6.67,21 6.8,20.95 6.89,20.85 L17.81,9.94 L14.06,6.19 L3.15,17.1 C3.05,17.2 3,17.32 3,17.46 Z M20.71,7.04 C20.8972281,6.85315541 21.002444,6.59950947 21.002444,6.335 C21.002444,6.07049053 20.8972281,5.81684459 20.71,5.63 L18.37,3.29 C18.1831554,3.10277191 17.9295095,2.99755597 17.665,2.99755597 C17.4004905,2.99755597 17.1468446,3.10277191 16.96,3.29 L15.13,5.12 L18.88,8.87 L20.71,7.04 Z" id="Icon-Shape"></path>
</g>
<rect id="ViewBox" fill-rule="nonzero" x="0" y="0" width="24" height="24"></rect>
</g>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
<circle cx="25" cy="25" r="20"/>
</svg>
#joystick {
position: fixed;
left: 50%;
bottom: 50px;
-ms-transform: translate(-50%, 0%);
transform: translate(-50%, 0%);
width: 120px;
height: 120px;
border: 3px solid #FFF;
border-radius: 50%;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
opacity: 0.75;
background-color: rgba(0, 0, 0, 0.5);
z-index: 200;
}
#knob {
position: fixed;
transform: translate(30px, 30px);
left: 0px;
top: 0px;
width: 60px;
height: 60px;
border-radius: 40px;
background-color: rgba(224, 224, 224, 0.8);
z-index: 310;
pointer-events: none;
}
#trackingknob {
position: fixed;
transform: translate(0px, 0px);
width: 120px;
height: 120px;
border-radius: 50%;
background-color: rgba(124, 124, 124, 0.2);
}
@media (max-width:600px), (max-height:600px) {
#trackingknob {
width: 86px;
height: 86px;
}
#joystick {
bottom: 18px;
width: 86px;
height: 86px;
}
#knob {
width: 43px;
height: 43px;
}
}
@media (max-height:600px) {
#joystick {
bottom: 24px;
}
}
@media (min-width:1900px) {
#trackingknob {
width: 140px;
height: 140px;
}
#joystick {
bottom: 65px;
width: 140px;
height: 140px;
}
#knob {
width: 70px;
height: 70px;
}
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
export function fragmentShader(){return `
varying vec2 vUv;
varying float noise;
uniform sampler2D tExplosion;
float random( vec3 scale, float seed ){
return fract( sin( dot( gl_FragCoord.xyz + seed, scale ) ) * 43758.5453 + seed ) ;
}
void main() {
float r = .01 * random( vec3( 12.9898, 78.233, 151.7182 ), 0.0 );
vec2 tPos = vec2( 0, 1.3 * noise + r );
vec4 color = texture2D( tExplosion, tPos );
gl_FragColor = vec4( color.rgb, 1.0 );
}`
}
\ No newline at end of file
export function vertexShader(){return `
//
// GLSL textureless classic 3D noise "cnoise",
// with an RSL-style periodic variant "pnoise".
// Author: Stefan Gustavson (stefan.gustavson@liu.se)
// Version: 2011-10-11
//
// Many thanks to Ian McEwan of Ashima Arts for the
// ideas for permutation and gradient selection.
//
// Copyright (c) 2011 Stefan Gustavson. All rights reserved.
// Distributed under the MIT license. See LICENSE file.
// https://github.com/ashima/webgl-noise
//
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;
}
vec3 fade(vec3 t) {
return t*t*t*(t*(t*6.0-15.0)+10.0);
}
// Classic Perlin noise
float cnoise(vec3 P)
{
vec3 Pi0 = floor(P); // Integer part for indexing
vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1
Pi0 = mod289(Pi0);
Pi1 = mod289(Pi1);
vec3 Pf0 = fract(P); // Fractional part for interpolation
vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
vec4 iy = vec4(Pi0.yy, Pi1.yy);
vec4 iz0 = Pi0.zzzz;
vec4 iz1 = Pi1.zzzz;
vec4 ixy = permute(permute(ix) + iy);
vec4 ixy0 = permute(ixy + iz0);
vec4 ixy1 = permute(ixy + iz1);
vec4 gx0 = ixy0 * (1.0 / 7.0);
vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;
gx0 = fract(gx0);
vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
vec4 sz0 = step(gz0, vec4(0.0));
gx0 -= sz0 * (step(0.0, gx0) - 0.5);
gy0 -= sz0 * (step(0.0, gy0) - 0.5);
vec4 gx1 = ixy1 * (1.0 / 7.0);
vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;
gx1 = fract(gx1);
vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
vec4 sz1 = step(gz1, vec4(0.0));
gx1 -= sz1 * (step(0.0, gx1) - 0.5);
gy1 -= sz1 * (step(0.0, gy1) - 0.5);
vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
g000 *= norm0.x;
g010 *= norm0.y;
g100 *= norm0.z;
g110 *= norm0.w;
vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
g001 *= norm1.x;
g011 *= norm1.y;
g101 *= norm1.z;
g111 *= norm1.w;
float n000 = dot(g000, Pf0);
float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
float n111 = dot(g111, Pf1);
vec3 fade_xyz = fade(Pf0);
vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
return 2.2 * n_xyz;
}
// Classic Perlin noise, periodic variant
float pnoise(vec3 P, vec3 rep)
{
vec3 Pi0 = mod(floor(P), rep); // Integer part, modulo period
vec3 Pi1 = mod(Pi0 + vec3(1.0), rep); // Integer part + 1, mod period
Pi0 = mod289(Pi0);
Pi1 = mod289(Pi1);
vec3 Pf0 = fract(P); // Fractional part for interpolation
vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
vec4 iy = vec4(Pi0.yy, Pi1.yy);
vec4 iz0 = Pi0.zzzz;
vec4 iz1 = Pi1.zzzz;
vec4 ixy = permute(permute(ix) + iy);
vec4 ixy0 = permute(ixy + iz0);
vec4 ixy1 = permute(ixy + iz1);
vec4 gx0 = ixy0 * (1.0 / 7.0);
vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;
gx0 = fract(gx0);
vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
vec4 sz0 = step(gz0, vec4(0.0));
gx0 -= sz0 * (step(0.0, gx0) - 0.5);
gy0 -= sz0 * (step(0.0, gy0) - 0.5);
vec4 gx1 = ixy1 * (1.0 / 7.0);
vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;
gx1 = fract(gx1);
vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
vec4 sz1 = step(gz1, vec4(0.0));
gx1 -= sz1 * (step(0.0, gx1) - 0.5);
gy1 -= sz1 * (step(0.0, gy1) - 0.5);
vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
g000 *= norm0.x;
g010 *= norm0.y;
g100 *= norm0.z;
g110 *= norm0.w;
vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
g001 *= norm1.x;
g011 *= norm1.y;
g101 *= norm1.z;
g111 *= norm1.w;
float n000 = dot(g000, Pf0);
float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
float n111 = dot(g111, Pf1);
vec3 fade_xyz = fade(Pf0);
vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
return 2.2 * n_xyz;
}
// Include the Ashima code here!
varying vec2 vUv;
varying float noise;
uniform float time;
float turbulence( vec3 p ) {
float w = 100.0;
float t = -.5;
for (float f = 1.0 ; f <= 10.0 ; f++ ){
float power = pow( 2.0, f );
t += abs( pnoise( vec3( power * p ), vec3( 10.0, 10.0, 10.0 ) ) / power );
}
return t;
}
void main() {
vUv = uv;
noise = 10.0 * -.10 * turbulence( .5 * normal + time );
float b = 5.0 * pnoise( 0.05 * position + vec3( 2.0 * time ), vec3( 100.0 ) );
float displacement = - 10. * noise + b;
vec3 newPosition = position + normal * displacement;
gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );
}`
}
\ No newline at end of file
// LICENSE: MIT
// Copyright (c) 2017 by Mike Linkovich
precision highp float;
uniform sampler2D map;
uniform sampler2D heightMap;
uniform vec3 fogColor;
uniform float fogNear;
uniform float fogFar;
uniform float grassFogFar;
varying vec2 vSamplePos;
varying vec4 vColor;
varying vec2 vUv;
void main() {
vec4 color = vec4(vColor) * texture2D(map, vUv);
vec4 hdata = texture2D(heightMap, vSamplePos);
float depth = gl_FragCoord.z / gl_FragCoord.w;
// make grass transparent as it approachs outer view distance perimeter
color.a = 1.0 - smoothstep(grassFogFar * 0.55, grassFogFar * 0.8, depth);
// apply terrain lightmap
float light = hdata.g;
color.r *= light;
color.g *= light;
color.b *= light;
// then apply atmosphere fog
float fogFactor = smoothstep(fogNear, fogFar, depth);
color.rgb = mix(color.rgb, fogColor, fogFactor);
// output
gl_FragColor = color;
}
// LICENSE: MIT
// Copyright (c) 2017 by Mike Linkovich
precision highp float;
#define PI 3.141592654
// These define values should be replaced by app before compiled
#define PATCH_SIZE (%%PATCH_SIZE%%)
#define BLADE_SEGS (%%BLADE_SEGS%%) // # of blade segments
#define BLADE_HEIGHT_TALL (%%BLADE_HEIGHT_TALL%%) // height of a tall blade
#define BLADE_DIVS (BLADE_SEGS + 1.0) // # of divisions
#define BLADE_VERTS (BLADE_DIVS * 2.0) // # of vertices (per side, so 1/2 total)
#define TRANSITION_LOW (%%TRANSITION_LOW%%) // elevation of beach-grass transition (start)
#define TRANSITION_HIGH (%%TRANSITION_HIGH%%) // (end)
#define TRANSITION_NOISE 0.06 // transition noise scale
const vec3 LIGHT_COLOR = vec3(1.0, 1.0, 0.99);
const vec3 SPECULAR_COLOR = vec3(1.0, 1.0, 0.0);
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform vec3 lightDir;
uniform vec3 camDir; // direction cam is looking at
uniform vec2 drawPos; // centre of where we want to draw
uniform float time; // used to animate blades
uniform sampler2D heightMap;
uniform vec3 heightMapScale;
uniform vec3 grassColor;
uniform float windIntensity;
attribute float vindex; // Which vertex are we drawing - the main thing we need to know
attribute vec4 offset; // {x:x, y:y, z:z, w:rot} (blade's position & rotation)
attribute vec4 shape; // {x:width, y:height, z:lean, w:curve} (blade's shape properties)
varying vec2 vSamplePos;
varying vec4 vColor;
varying vec2 vUv;
// Rotate by an angle
vec2 rotate (float x, float y, float r) {
float c = cos(r);
float s = sin(r);
return vec2(x * c - y * s, x * s + y * c);
}
// Rotate by a vector
vec2 rotate (float x, float y, vec2 r) {
return vec2(x * r.x - y * r.y, x * r.y + y * r.x);
}
void main() {
float vi = mod(vindex, BLADE_VERTS); // vertex index for this side of the blade
float di = floor(vi / 2.0); // div index (0 .. BLADE_DIVS)
float hpct = di / BLADE_SEGS; // percent of height of blade this vertex is at
float bside = floor(vindex / BLADE_VERTS); // front/back side of blade
float bedge = mod(vi, 2.0); // left/right edge (x=0 or x=1)
// Vertex position - start with 2D shape, no bend applied
vec3 vpos = vec3(
shape.x * (bedge - 0.5) * (1.0 - pow(hpct, 3.0)), // taper blade edges as approach tip
0.0, // flat y, unbent
shape.y * di / BLADE_SEGS // height of vtx, unbent
);
// Start computing a normal for this vertex
vec3 normal = vec3(rotate(0.0, bside * 2.0 - 1.0, offset.w), 0.0);
// Apply blade's natural curve amount
float curve = shape.w;
// Then add animated curve amount by time using this blade's
// unique properties to randomize its oscillation
curve += shape.w + 0.125 * (sin(time * 4.0 + offset.w * 0.2 * shape.y + offset.x + offset.y));
// put lean and curve together
float rot = shape.z + curve * hpct;
vec2 rotv = vec2(cos(rot), sin(rot));
vpos.yz = rotate(vpos.y, vpos.z, rotv);
normal.yz = rotate(normal.y, normal.z, rotv);
// rotation of this blade as a vector
rotv = vec2(cos(offset.w), sin(offset.w));
vpos.xy = rotate(vpos.x, vpos.y, rotv);
// Based on centre of view cone position, what grid tile should
// this piece of grass be drawn at?
vec2 gridOffset = vec2(
floor((drawPos.x - offset.x) / PATCH_SIZE) * PATCH_SIZE + PATCH_SIZE / 2.0,
floor((drawPos.y - offset.y) / PATCH_SIZE) * PATCH_SIZE + PATCH_SIZE / 2.0
);
// Find the blade mesh world x,y position
vec2 bladePos = vec2(offset.xy + gridOffset);
// height/light map sample position
vSamplePos = bladePos.xy * heightMapScale.xy + vec2(0.5, 0.5);
// Compute wind effect
// Using the lighting channel as noise seems make the best looking wind for some reason!
float wind = texture2D(heightMap, vec2(vSamplePos.x - time / 2500.0, vSamplePos.y - time / 200.0) * 6.0).g;
//float wind = texture2D(heightMap, vec2(vSamplePos.x - time / 2500.0, vSamplePos.y - time / 100.0) * 6.0).r;
//float wind = texture2D(heightMap, vec2(vSamplePos.x - time / 2500.0, vSamplePos.y - time / 100.0) * 4.0).b;
// Apply some exaggeration to wind
//wind = (clamp(wind, 0.125, 0.875) - 0.125) * (1.0 / 0.75);
wind = (clamp(wind, 0.25, 1.0) - 0.25) * (1.0 / 0.75);
wind = wind * wind * windIntensity;
wind *= hpct; // scale wind by height of blade
wind = -wind;
rotv = vec2(cos(wind), sin(wind));
// Wind blows in axis-aligned direction to make things simpler
vpos.yz = rotate(vpos.y, vpos.z, rotv);
normal.yz = rotate(normal.y, normal.z, rotv);
// Sample the heightfield data texture to get altitude for this blade position
vec4 hdata = texture2D(heightMap, vSamplePos);
float altitude = hdata.r;
// Determine if we want the grass to appear or not
// Use the noise channel to perturb the altitude grass starts growing at.
float noisyAltitude = altitude + hdata.b * TRANSITION_NOISE - (TRANSITION_NOISE / 2.0);
float degenerate = (clamp(noisyAltitude, TRANSITION_LOW, TRANSITION_HIGH) - TRANSITION_LOW)
* (1.0 / (TRANSITION_HIGH - TRANSITION_LOW));
// Transition geometry toward degenerate as we approach beach altitude
vpos *= degenerate;
// Vertex color must be brighter because it is multiplied with blade texture
vec3 color = min(vec3(grassColor.r * 1.25, grassColor.g * 1.25, grassColor.b * 0.95), 1.0);
altitude *= heightMapScale.z;
// Compute directional (sun) light for this vertex
float diffuse = abs(dot(normal, lightDir)); // max(-dot(normal, lightDir), 0.0);
float specMag = max(-dot(normal, lightDir), 0.0) * max(-dot(normal, camDir), 0.0);
specMag = pow(specMag, 1.5); // * specMag * specMag;
vec3 specular = specMag * SPECULAR_COLOR * 0.4;
// Directional plus ambient
float light = 0.35 * diffuse + 0.65;
// Ambient occlusion shading - the lower vertex, the darker
float heightLight = 1.0 - hpct;
heightLight = heightLight * heightLight;
light = max(light - heightLight * 0.5, 0.0);
vColor = vec4(
// Each blade is randomly colourized a bit by its position
light * 0.75 + cos(offset.x * 80.0) * 0.1,
light * 0.95 + sin(offset.y * 140.0) * 0.05,
light * 0.95 + sin(offset.x * 99.0) * 0.05,
1.0
);
vColor.rgb = vColor.rgb * LIGHT_COLOR * color;
vColor.rgb = min(vColor.rgb + specular, 1.0);
// grass texture coordinate for this vertex
vUv = vec2(bedge, di * 2.0);
// Translate to world coordinates
vpos.x += bladePos.x;
vpos.y += bladePos.y;
vpos.z += altitude;
gl_Position = projectionMatrix * modelViewMatrix * vec4(vpos, 1.0);
}
// LICENSE: MIT
// Copyright (c) 2017 by Mike Linkovich
precision highp float;
#define TRANSITION_LOW (%%TRANSITION_LOW%%)
#define TRANSITION_HIGH (%%TRANSITION_HIGH%%)
#define TRANSITION_NOISE 0.06
const vec3 LIGHT_COLOR = vec3(1.0, 1.0, 0.9);
const vec3 DIRT_COLOR = vec3(0.77, 0.67, 0.45);
uniform sampler2D map1;
uniform sampler2D map2;
uniform sampler2D heightMap;
uniform vec3 fogColor;
uniform float fogNear;
uniform float fogFar;
uniform float grassFogFar;
varying vec2 vUv;
varying vec2 vSamplePos;
void main() {
vec4 hdata = texture2D(heightMap, vSamplePos);
float altitude = hdata.r;
// perturb altitude with some noise using the B channel.
float noise = hdata.b;
altitude += noise * TRANSITION_NOISE - (TRANSITION_NOISE / 2.0);
// Determine whether this position is more grass or more dirt
float grassAmount = (clamp(altitude, TRANSITION_LOW, TRANSITION_HIGH) - TRANSITION_LOW)
* (1.0 / (TRANSITION_HIGH - TRANSITION_LOW));
// Sample both textures and mix proportionately
vec3 color = mix(
texture2D(map2, vUv).rgb,
texture2D(map1, vUv).rgb,
grassAmount
);
vec3 light = hdata.g * LIGHT_COLOR;
float depth = gl_FragCoord.z / gl_FragCoord.w;
// If terrain is covered by grass geometry, blend color to 'dirt'
float dirtFactor = 1.0 - smoothstep(grassFogFar * 0.2, grassFogFar * 0.65, depth);
// If we're not on a grass terrain type, don't shade it...
dirtFactor *= grassAmount;
float dirtShade = (color.r + color.g + color.b) / 3.0;
// Compute terrain color
color = mix(color, DIRT_COLOR * dirtShade, dirtFactor) * light;
// then apply atmosphere fog
float fogFactor = smoothstep(fogNear, fogFar, depth);
color = mix(color, fogColor, fogFactor);
gl_FragColor = vec4(color, 1.0);
}
// LICENSE: MIT
// Copyright (c) 2017 by Mike Linkovich
precision highp float;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform vec2 offset;
uniform vec2 uvOffset;
uniform sampler2D heightMap;
uniform vec3 heightMapScale;
attribute vec3 position;
attribute vec2 uv;
varying vec2 vUv;
varying vec2 vSamplePos;
void main() {
vec2 pos = vec2(position.xy + offset);
vSamplePos = pos * heightMapScale.xy + vec2(0.5, 0.5);
vec4 ch = texture2D(heightMap, vSamplePos);
float height = ch.r * heightMapScale.z;
vUv = uv + uvOffset;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, height, 1.0);
}
// LICENSE: MIT
// Copyright (c) 2017 by Mike Linkovich
precision highp float;
#define PI 3.141592654
uniform sampler2D map;
uniform float waterLevel;
uniform vec3 viewPos;
uniform float time;
uniform vec3 waterColor;
uniform vec3 fogColor;
uniform float fogNear;
uniform float fogFar;
varying vec3 vSurfacePos;
void main() {
// Get the direction from camera through this point on the surface of the water.
// Then project that on to an upside-down skydome to get the UV from the skydome texture.
// X & Y of plane are moving with viewer, so we only need Z difference to get dir.
vec3 viewDir = normalize(vec3(vSurfacePos.xy, waterLevel - viewPos.z));
vec2 uv = vec2(
// horizontal angle converted to a texcoord between 0.0 and 1.0
atan(viewDir.y, viewDir.x) / (PI * 2.0),
// down angle converted to V tex coord between 0.0 and 1.0
asin(-viewDir.z) / (PI / 2.0)
);
// wave distortion
float distortScale = 1.0 / length(vSurfacePos - viewPos);
vec2 distort = vec2(
cos((vSurfacePos.x - viewPos.x) / 1.5 + time) + sin((vSurfacePos.y - viewPos.y) / 1.5 + time),
0.0
) * distortScale;
uv += distort;
// Now we can sample the env map
vec4 color = texture2D(map, uv);
// Mix with water colour
color.rgb = color.rgb * waterColor;
// Apply fog
float depth = gl_FragCoord.z / gl_FragCoord.w;
float fogFactor = smoothstep(fogNear, fogFar, depth);
color.rgb = mix(color.rgb, fogColor, fogFactor);
gl_FragColor = color;
}
// LICENSE: MIT
// Copyright (c) 2017 by Mike Linkovich
precision highp float;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec3 position;
varying vec3 vSurfacePos;
void main() {
vSurfacePos = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
/**
* 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
*/
export 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 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 );
const 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 },
'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;
varying vec4 mirrorCoord;
varying vec4 worldPosition;
#include <common>
#include <fog_pars_vertex>
#include <shadowmap_pars_vertex>
#include <logdepthbuf_pars_vertex>
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;
#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[ '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 );
}
};
}
}
//"use strict";
// LICENSE: MIT
// Copyright (c) 2016 by Mike Linkovich
// modified by David A Smith, Croquet Corporation
//
// Creates & animates a large patch of grass to fill the foreground.
// One simple blade of grass mesh is repeated many times using instanced arrays.
// Uses grass shaders (see: shader/grass.*.glsl)
var BLADE_SEGS = 4; // # of blade segments
var BLADE_VERTS = (BLADE_SEGS + 1) * 2; // # of vertices per blade (1 side)
var BLADE_INDICES = BLADE_SEGS * 12;
var BLADE_WIDTH = 0.15;
var BLADE_HEIGHT_MIN = 2.25;
var BLADE_HEIGHT_MAX = 3.0;
/**
* Creates a patch of grass mesh.
*/
export class Grass {
constructor(opts) {
// Buffers to use for instances of blade mesh
//console.log("grass.CreateMesh", opts)
var buffers = {
// Tells the shader which vertex of the blade its working on.
// Rather than supplying positions, they are computed from this vindex.
vindex: new Float32Array(BLADE_VERTS * 2 * 1),
// Shape properties of all blades
shape: new Float32Array(4 * opts.numBlades),
// Positon & rotation of all blades
offset: new Float32Array(4 * opts.numBlades),
// Indices for a blade
index: new Uint16Array(BLADE_INDICES)
};
this.initBladeIndices(buffers.index, 0, BLADE_VERTS, 0);
this.initBladeOffsetVerts(buffers.offset, opts.numBlades, opts.radius);
this.initBladeShapeVerts(buffers.shape, opts.numBlades, buffers.offset, opts.simplex);
this.initBladeIndexVerts(buffers.vindex);
var geo = new THREE.InstancedBufferGeometry();
// Because there are no position vertices, we must create our own bounding sphere.
// (Not used because we disable frustum culling)
geo.boundingSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0), Math.sqrt(opts.radius * opts.radius * 2.0) * 10000.0);
geo.setAttribute('vindex', new THREE.BufferAttribute(buffers.vindex, 1));
geo.setAttribute('shape', new THREE.InstancedBufferAttribute(buffers.shape, 4));
geo.setAttribute('offset', new THREE.InstancedBufferAttribute(buffers.offset, 4));
geo.setIndex(new THREE.BufferAttribute(buffers.index, 1));
var tex = opts.texture;
tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
var htex = opts.heightMap;
htex.wrapS = htex.wrapT = THREE.RepeatWrapping;
var hscale = opts.heightMapScale;
var lightDir = opts.lightDir.clone();
lightDir.z *= 0.5;
lightDir.normalize();
// Fill in some constants that never change bet ween draw calls
var vertScript = opts.vertScript.replace('%%BLADE_HEIGHT_TALL%%', (BLADE_HEIGHT_MAX * 1.5).toFixed(1)).replace('%%BLADE_SEGS%%', BLADE_SEGS.toFixed(1)).replace('%%PATCH_SIZE%%', (opts.radius * 2.0).toFixed(1)).replace('%%TRANSITION_LOW%%', opts.transitionLow.toString()).replace('%%TRANSITION_HIGH%%', opts.transitionHigh.toString());
// Setup shader
var mat = new THREE.RawShaderMaterial({
uniforms: {
lightDir: { type: '3f', value: lightDir.toArray() },
time: { type: 'f', value: 0.0 },
map: { type: 't', value: tex },
heightMap: { type: 't', value: htex },
heightMapScale: { type: '3f', value: [hscale.x, hscale.y, hscale.z] },
camDir: { type: '3f', value: [1.0, 0.0, 0.0] },
drawPos: { type: '2f', value: [100.0, 0.0] },
fogColor: { type: '3f', value: opts.fogColor.toArray() },
fogNear: { type: 'f', value: 1.0 },
fogFar: { type: 'f', value: opts.fogFar },
grassColor: { type: '3f', value: opts.grassColor.toArray() },
grassFogFar: { type: 'f', value: opts.grassFogFar },
windIntensity: { type: 'f', value: opts.windIntensity }
},
vertexShader: vertScript,
fragmentShader: opts.fragScript,
transparent: true
});
//console.log("GRASS", mat)
this.mesh = new THREE.Mesh(geo, mat);
this.mesh.frustumCulled = false; // always draw, never cull
}
/**
* Sets up indices for single blade mesh.
* @param id array of indices
* @param vc1 vertex start offset for front side of blade
* @param vc2 vertex start offset for back side of blade
* @param i index offset
*/
initBladeIndices(id, vc1, vc2, i) {
var seg;
// blade front side
for (seg = 0; seg < BLADE_SEGS; ++seg) {
id[i++] = vc1 + 0; // tri 1
id[i++] = vc1 + 1;
id[i++] = vc1 + 2;
id[i++] = vc1 + 2; // tri 2
id[i++] = vc1 + 1;
id[i++] = vc1 + 3;
vc1 += 2;
}
// blade back side
for (seg = 0; seg < BLADE_SEGS; ++seg) {
id[i++] = vc2 + 2; // tri 1
id[i++] = vc2 + 1;
id[i++] = vc2 + 0;
id[i++] = vc2 + 3; // tri 2
id[i++] = vc2 + 1;
id[i++] = vc2 + 2;
vc2 += 2;
}
}
/** Set up shape variations for each blade of grass */
initBladeShapeVerts(shape, numBlades, offset, simplex) {
var noise = 0;
for (var i = 0; i < numBlades; ++i) {
noise = Math.abs(simplex.simplex(offset[i * 4 + 0] * 0.03, offset[i * 4 + 1] * 0.03));
noise = noise * noise * noise;
noise *= 5.0;
shape[i * 4 + 0] = BLADE_WIDTH + Math.random() * BLADE_WIDTH * 0.5; // width
shape[i * 4 + 1] = BLADE_HEIGHT_MIN + Math.pow(Math.random(), 4.0) * (BLADE_HEIGHT_MAX - BLADE_HEIGHT_MIN) + // height
noise;
shape[i * 4 + 2] = 0.0 + Math.random() * 0.3; // lean
shape[i * 4 + 3] = 0.05 + Math.random() * 0.3; // curve
}
}
/** Set up positons & rotation for each blade of grass */
initBladeOffsetVerts(offset, numBlades, patchRadius) {
/** A random number from -1.0 to 1.0 */
function nrand() {
return Math.random() * 2.0 - 1.0;
}
for (var i = 0; i < numBlades; ++i) {
offset[i * 4 + 0] = (0, nrand)() * patchRadius; // x
offset[i * 4 + 1] = (0, nrand)() * patchRadius; // y
offset[i * 4 + 2] = 0.0; // z
offset[i * 4 + 3] = Math.PI * 2.0 * Math.random(); // rot
}
}
/** Set up indices for 1 blade */
initBladeIndexVerts(vindex) {
for (var i = 0; i < vindex.length; ++i) {
vindex[i] = i;
}
}
/**
* Call each frame to animate grass blades.
* @param mesh The patch of grass mesh returned from createMesh
* @param time Time in seconds
* @param x X coordinate of centre position to draw at
* @param y Y coord
*/
update(time, camDir, drawPos) {
var mat = this.mesh.material;
mat.uniforms['time'].value = time;
var p = mat.uniforms['camDir'].value;
p[0] = camDir.x;
p[1] = camDir.y;
p[2] = camDir.z;
p = mat.uniforms['drawPos'].value;
p[0] = drawPos.x;
p[1] = drawPos.y;
}
}
// LICENSE: MIT
// Copyright (c) 2016 by Mike Linkovich
/**
* Create a Heightfield using the given options.
* Use either an image OR xCount, yCount and a heights array.
*/
export class Heightfield{
constructor(info){
this.cellSize = (info.cellSize && info.cellSize > 0) ? info.cellSize : 1.0,
this.minHeight = (typeof info.minHeight === 'number') ? info.minHeight : 0.0,
this.maxHeight = (typeof info.maxHeight === 'number') ? info.maxHeight : 1.0,
this.xCount = 0,
this.yCount = 0,
this.xSize = 0,
this.ySize = 0,
this.heights = new Float32Array(0),
this.faceNormals = new Float32Array(0),
this.vtxNormals = new Float32Array(0);
this.hInfo = {
i: 0, t: 0, z: 0.0, n: new THREE.Vector3()
};
if (info.image) {
this.genFromImg(info.image);
}
else {
this.xCount = info.xCount && info.xCount > 0 ? Math.floor(info.xCount) : 1;
this.yCount = info.yCount && info.yCount > 0 ? Math.floor(info.yCount) : 1;
this.xSize = info.xCount * this.cellSize;
this.ySize = info.yCount * this.cellSize;
this.heights = info.heights || new Float32Array((this.xCount + 1) *
(this.yCount + 1));
// 2 normals per cell (quad)
this.faceNormals = new Float32Array(3 * 2 * this.xCount * this.yCount);
this.vtxNormals = new Float32Array(3 * (this.xCount + 1) * (this.yCount + 1));
this.calcFaceNormals();
}
}
/** Always positive modulus */
pmod(n, m) {
return ((n % m + m) % m);
}
infoAt(x, y, wrap) {
var ox = -(this.xSize / 2.0); // bottom left of heightfield
var oy = -(this.ySize / 2.0);
if (x < ox || x >= -ox || y < oy || y >= -oy) {
if (!wrap) {
// out of bounds
this.hInfo.i = -1;
this.hInfo.z = this.minHeight;
this.hInfo.n.x = this.hInfo.n.y = this.hInfo.n.z = 0;
this.hInfo.t = 0;
return;
}
// wrap around
x = this.pmod(x - ox, this.xSize) + ox;
y = this.pmod(y - oy, this.ySize) + oy;
}
var csz = this.cellSize, normals = this.faceNormals,
n = this.hInfo.n, ix = Math.floor((x - ox) / csz),
iy = Math.floor((y - oy) / csz), ih = ix + iy * (this.xCount + 1), // height index
px = (x - ox) % csz, // relative x,y within this quad
py = (y - oy) % csz;
var i = ix + iy * this.xCount; // tile index
if (py > 0 && px / py < 1.0) {
// top left tri
this.hInfo.t = 0;
n.x = normals[i * 6 + 0];
n.y = normals[i * 6 + 1];
n.z = normals[i * 6 + 2];
}
else {
// bottom right tri
this.hInfo.t = 1;
n.x = normals[i * 6 + 3];
n.y = normals[i * 6 + 4];
n.z = normals[i * 6 + 5];
}
this.hInfo.i = i;
this.hInfo.z = this.getPlaneZ(n, this.heights[ih], px, py);
}
heightAt(x, y, wrap) {
if (wrap === void 0) { wrap = false; }
this.infoAt(x, y, wrap);
return this.hInfo.z;
}
/**
* Given a plane with normal n and z=z0 at (x=0,y=0) find z at x,y.
* @param n Normal vector of the plane.
* @param z0 Height (z) coordinate of the plane at x=0,y=0.
* @param x X coordinate to find height (z) at.
* @param y Y coordinate to find height (z) at.
*/
getPlaneZ(n, z0, x, y) {
return z0 - (n.x * x + n.y * y) / n.z;
}
genFromImg(image) {
var x, y, i, height;
var w = image.width, h = image.height, heightRange = this.maxHeight - this.minHeight;
this.xCount = w - 1;
this.yCount = h - 1;
this.xSize = this.xCount * this.cellSize;
this.ySize = this.yCount * this.cellSize;
// Draw to a canvas so we can get the data
var canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0, w, h);
// array of canvas pixel data [r,g,b,a, r,g,b,a, ...]
var data = ctx.getImageData(0, 0, w, h).data;
var heights = new Float32Array(w * h);
for (y = 0; y < h; ++y) {
for (x = 0; x < w; ++x) {
// flip vertical because textures are Y+
i = (x + (h - y - 1) * w) * 4;
//i = (x + y * w) * 4
// normalized altitude value (0-1)
// assume image is grayscale, so we only need 1 color component
height = data[i] / 255.0;
//height = (data[i+0] + data[i+1] + data[i+2]) / (255+255+255)
// scale & store this altitude
heights[x + y * w] = this.minHeight + height * heightRange;
}
}
// Free these resources soon as possible
data = ctx = canvas = null;
this.heights = heights;
// 2 normals per cell (quad)
this.faceNormals = new Float32Array(3 * 2 * this.xCount * this.yCount);
this.vtxNormals = new Float32Array(3 * (this.xCount + 1) * (this.yCount + 1));
this.calcFaceNormals();
this.calcVertexNormals();
}
/**
* Calculate normals.
* 2 face normals per quad (1 per tri)
*/
calcFaceNormals() {
var csz = this.cellSize, xc = this.xCount, // tile X & Y counts
yc = this.yCount, hxc = this.xCount + 1, // height X count (1 larger than tile count)
heights = this.heights, // 1 less indirection
normals = this.faceNormals, v0 = new THREE.Vector3(), v1 = new THREE.Vector3(), n = new THREE.Vector3(); // used to compute normals
var i = 0;
var tStart = Date.now();
for (var iy = 0; iy < yc; ++iy) {
for (var ix = 0; ix < xc; ++ix) {
i = 6 * (ix + iy * xc);
var ih = ix + iy * hxc;
var z = heights[ih];
// 2 vectors of top-left tri
v0.x = csz;
v0.y = csz;
v0.z = heights[ih + hxc + 1] - z;
v1.x = 0.0;
v1.y = csz;
v1.z = heights[ih + hxc] - z;
v0.cross(v1);
v0.normalize();
normals[i + 0] = v0.x;
normals[i + 1] = v0.y;
normals[i + 2] = v0.z;
// 2 vectors of bottom-right tri
v0.x = csz;
v0.y = 0.0;
v0.z = heights[ih + 1] - z;
v1.x = csz;
v1.y = csz;
v1.z = heights[ih + hxc + 1] - z;
v0.cross(v1);
v0.normalize();
normals[i + 3] = v0.x;
normals[i + 4] = v0.y;
normals[i + 5] = v0.z;
}
}
var dt = Date.now() - tStart;
console.log("computed ".concat(i, " heightfield face normals in ").concat(dt, "ms"));
}
calcVertexNormals() {
var vnorms = this.vtxNormals;
var w = this.xCount + 1;
var h = this.yCount + 1;
var n = new THREE.Vector3();
var i = 0;
var tStart = Date.now();
for (var y = 0; y < h; ++y) {
for (var x = 0; x < w; ++x) {
this.computeVertexNormal(x, y, n);
i = (y * w + x) * 3;
vnorms[i++] = n.x;
vnorms[i++] = n.y;
vnorms[i++] = n.z;
}
}
var dt = Date.now() - tStart;
console.log("computed ".concat(w * h, " vertex normals in ").concat(dt, "ms"));
}
/**
* Compute a vertex normal by averaging the adjacent face normals.
*/
computeVertexNormal(vx, vy, n) {
var fnorms = this.faceNormals;
// This vertex is belongs to 4 quads
// Do the faces this vertex is the 1st point of for this quad.
// This is the quad up and to the right
var qx = vx % this.xCount;
var qy = vy % this.yCount;
var ni = (qy * this.xCount + qx) * 3 * 2;
n.x = fnorms[ni + 0];
n.y = fnorms[ni + 1];
n.z = fnorms[ni + 2];
ni += 3;
n.x += fnorms[ni + 0];
n.y += fnorms[ni + 1];
n.z += fnorms[ni + 2];
// 2nd tri of quad up and to the left
qx = this.pmod(qx - 1, this.xCount);
ni = (qy * this.xCount + qx) * 3 * 2 + 3;
n.x += fnorms[ni + 0];
n.y += fnorms[ni + 1];
n.z += fnorms[ni + 2];
// both tris of quad down and to the left
qy = this.pmod(qy - 1, this.yCount);
ni = (qy * this.xCount + qx) * 3 * 2;
n.x += fnorms[ni + 0];
n.y += fnorms[ni + 1];
n.z += fnorms[ni + 2];
ni += 3;
n.x += fnorms[ni + 0];
n.y += fnorms[ni + 1];
n.z += fnorms[ni + 2];
// 1st tri of quad down and to the right
qx = (qx + 1) % this.xCount;
ni = (qy * this.xCount + qx) * 3 * 2;
n.x += fnorms[ni + 0];
n.y += fnorms[ni + 1];
n.z += fnorms[ni + 2];
// Normalize to 'average' the result normal
n.normalize();
}
}
\ No newline at end of file
/*
* A speed-improved perlin and simplex noise algorithms for 2D.
*
* Based on example code by Stefan Gustavson (stegu@itn.liu.se).
* Optimisations by Peter Eastman (peastman@drizzle.stanford.edu).
* Better rank ordering method by Stefan Gustavson in 2012.
* Converted to Javascript by Joseph Gentle.
*
* Version 2012-03-09
*
* This code was placed in the public domain by its original author,
* Stefan Gustavson. You may use it as you see fit, but
* attribution is appreciated.
*
* --------------------
* TypeScriptified 2016
* UnTypeScriptified 2021 by David A Smith, Croquet Corporation
*/
var Grad = /** @class */ (function () {
function Grad(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
Grad.prototype.dot2 = function (x, y) {
return this.x * x + this.y * y;
};
Grad.prototype.dot3 = function (x, y, z) {
return this.x * x + this.y * y + this.z * z;
};
return Grad;
}());
var F2 = 0.5 * (Math.sqrt(3) - 1);
var G2 = (3 - Math.sqrt(3)) / 6;
var perm = new Array(512);
var gradP = new Array(512);
var grad3 = [
new Grad(1, 1, 0), new Grad(-1, 1, 0), new Grad(1, -1, 0), new Grad(-1, -1, 0),
new Grad(1, 0, 1), new Grad(-1, 0, 1), new Grad(1, 0, -1), new Grad(-1, 0, -1),
new Grad(0, 1, 1), new Grad(0, -1, 1), new Grad(0, 1, -1), new Grad(0, -1, -1)
];
var p = [
151, 160, 137, 91, 90, 15,
131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23,
190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33,
88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166,
77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244,
102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196,
135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123,
5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42,
223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9,
129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228,
251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107,
49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254,
138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180
];
// This isn't a very good seeding function, but it works ok. It supports 2^16
// different seed values. Write something better if you need more seeds.
function seed(seed) {
if (seed > 0 && seed < 1) {
// Scale the seed out
seed *= 65536;
}
seed = Math.floor(seed);
if (seed < 256) {
seed |= seed << 8;
}
for (var i = 0; i < 256; i++) {
var v = void 0;
if (i & 1) {
v = p[i] ^ (seed & 255);
}
else {
v = p[i] ^ ((seed >> 8) & 255);
}
perm[i] = perm[i + 256] = v;
gradP[i] = gradP[i + 256] = grad3[v % 12];
}
}
seed(0);
// 2D simplex noise
export function simplex(xin, yin) {
var n0, n1, n2; // Noise contributions from the three corners
// Skew the input space to determine which simplex cell we're in
var s = (xin + yin) * F2; // Hairy factor for 2D
var i = Math.floor(xin + s);
var j = Math.floor(yin + s);
var t = (i + j) * G2;
var x0 = xin - i + t; // The x,y distances from the cell origin, unskewed.
var y0 = yin - j + t;
// For the 2D case, the simplex shape is an equilateral triangle.
// Determine which simplex we are in.
var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
if (x0 > y0) { // lower triangle, XY order: (0,0)->(1,0)->(1,1)
i1 = 1;
j1 = 0;
}
else { // upper triangle, YX order: (0,0)->(0,1)->(1,1)
i1 = 0;
j1 = 1;
}
// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
// c = (3-sqrt(3))/6
var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
var y1 = y0 - j1 + G2;
var x2 = x0 - 1 + 2 * G2; // Offsets for last corner in (x,y) unskewed coords
var y2 = y0 - 1 + 2 * G2;
// Work out the hashed gradient indices of the three simplex corners
i &= 255;
j &= 255;
var gi0 = gradP[i + perm[j]];
var gi1 = gradP[i + i1 + perm[j + j1]];
var gi2 = gradP[i + 1 + perm[j + 1]];
// Calculate the contribution from the three corners
var t0 = 0.5 - x0 * x0 - y0 * y0;
if (t0 < 0) {
n0 = 0;
}
else {
t0 *= t0;
n0 = t0 * t0 * gi0.dot2(x0, y0); // (x,y) of grad3 used for 2D gradient
}
var t1 = 0.5 - x1 * x1 - y1 * y1;
if (t1 < 0) {
n1 = 0;
}
else {
t1 *= t1;
n1 = t1 * t1 * gi1.dot2(x1, y1);
}
var t2 = 0.5 - x2 * x2 - y2 * y2;
if (t2 < 0) {
n2 = 0;
}
else {
t2 *= t2;
n2 = t2 * t2 * gi2.dot2(x2, y2);
}
// Add contributions from each corner to get the final noise value.
// The result is scaled to return values in the interval [-1,1].
return 70 * (n0 + n1 + n2);
}
// LICENSE: MIT
// Copyright (c) 2016 by Mike Linkovich
// Terrain uses a custom shader so that we can apply the same
// type of fog as is applied to the grass. This way they both
// blend to green first, then blend to atmosphere color in the
// distance.
// Uses terrain shaders (see: shader/terrain.*.glsl)
var MAX_INDICES = 262144; // 65536
var TEX_SCALE = 1.0 / 6.0; // texture scale per quad
export class Terrain {
constructor(opts){
// max square x,y divisions that will fit in max indices
var xCellCount = Math.floor(Math.sqrt(MAX_INDICES / (3 * 2)));
var yCellCount = xCellCount;
var cellSize = 1.0 / opts.heightMapScale.x / xCellCount;
this.cellSize = cellSize,
this.xCellCount = xCellCount,
this.yCellCount = yCellCount,
this.xSize = xCellCount * cellSize,
this.ySize = yCellCount * cellSize,
this.mesh = this.createMesh(opts)
}
update(x, y) {
var ix = Math.floor(x / this.cellSize);
var iy = Math.floor(y / this.cellSize);
var ox = ix * this.cellSize;
var oy = iy * this.cellSize;
var mat = this.mesh.material;
var p = mat.uniforms['offset'].value;
p[0] = ox;
p[1] = oy;
p = mat.uniforms['uvOffset'].value;
p[0] = iy * TEX_SCALE; // not sure why x,y need to be swapped here...
p[1] = ix * TEX_SCALE;
}
// Internal helpers...
/** Creates a textured plane larger than the viewer will ever travel */
createMesh(opts) {
// max x,y divisions that will fit 65536 indices
var xCellCount = Math.floor(Math.sqrt(MAX_INDICES / (3 * 2)));
var yCellCount = xCellCount;
var cellSize = 1.0 / opts.heightMapScale.x / xCellCount;
var texs = opts.textures;
texs.forEach(function (tex) {
tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
tex.anisotropy = 9;
});
var htex = opts.heightMap;
htex.wrapS = htex.wrapT = THREE.RepeatWrapping;
var vtxBufs = this.createVtxBuffers(cellSize, xCellCount + 1, yCellCount + 1);
var idBuf = this.createIdBuffer(xCellCount + 1, yCellCount + 1);
var geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.BufferAttribute(vtxBufs.position, 3));
geo.setAttribute('uv', new THREE.BufferAttribute(vtxBufs.uv, 2));
geo.setIndex(new THREE.BufferAttribute(idBuf, 1));
var hscale = opts.heightMapScale;
var fragScript = opts.fragScript.replace('%%TRANSITION_LOW%%', opts.transitionLow.toString()).replace('%%TRANSITION_HIGH%%', opts.transitionHigh.toString());
var mat = new THREE.RawShaderMaterial({
uniforms: {
offset: { type: '2f', value: [0.0, 0.0] },
uvOffset: { type: '2f', value: [0.0, 0.0] },
map1: { type: 't', value: texs[0] },
map2: { type: 't', value: texs[1] },
heightMap: { type: 't', value: htex },
heightMapScale: { type: '3f', value: [hscale.x, hscale.y, hscale.z] },
fogColor: { type: '3f', value: opts.fogColor.toArray() },
fogNear: { type: 'f', value: 1.0 },
fogFar: { type: 'f', value: opts.fogFar },
grassFogFar: { type: 'f', value: opts.grassFogFar }
},
vertexShader: opts.vertScript,
fragmentShader: fragScript
});
var mesh = new THREE.Mesh(geo, mat);
mesh.receiveShadow = true;
mesh.frustumCulled = false;
return mesh;
}
/**
* @param cellSize Size of each mesh cell (quad)
* @param xcount X vertex count
* @param ycount Y vertex count
*/
createVtxBuffers(cellSize, xcount, ycount) {
var pos = new Float32Array(xcount * ycount * 3);
var uv = new Float32Array(xcount * ycount * 2);
var ix, iy;
var x, y;
var u, v;
var i = 0;
var j = 0;
for (iy = 0; iy < ycount; ++iy) {
y = (iy - ycount / 2.0) * cellSize;
u = iy;
for (ix = 0; ix < xcount; ++ix) {
x = (ix - xcount / 2.0) * cellSize;
v = ix;
pos[i++] = x;
pos[i++] = y;
pos[i++] = 4.0 * Math.cos(ix * 1.0) + 4.0 * Math.sin(iy * 1.0);
uv[j++] = u * TEX_SCALE;
uv[j++] = v * TEX_SCALE;
}
}
return { position: pos, uv: uv };
}
/**
* @param xcount X vertex count
* @param ycount Y vertex count
*/
createIdBuffer(xcount, ycount) {
var idSize = (xcount - 1) * (ycount - 1) * 3 * 2;
var id;
if (idSize <= 65536) {
id = new Uint16Array(idSize);
}
else {
id = new Uint32Array(idSize);
}
var xc = xcount - 1;
var yc = ycount - 1;
var x, y;
for (y = 0; y < yc; ++y) {
for (x = 0; x < xc; ++x) {
var i = 6 * (y * xc + x);
// tri 1
id[i + 0] = (y + 0) * xcount + (x + 0);
id[i + 1] = (y + 0) * xcount + (x + 1);
id[i + 2] = (y + 1) * xcount + (x + 1);
// tri 2
id[i + 3] = (y + 1) * xcount + (x + 1);
id[i + 4] = (y + 1) * xcount + (x + 0);
id[i + 5] = (y + 0) * xcount + (x + 0);
}
}
return id;
}
}
// LICENSE: MIT
// Copyright (c) 2016 by Mike Linkovich
/**
* Create a texture containing height, lighting, etc. data
* encoded into RGBA channels.
*/
export function createTexture(hf, lightDir, imgWind) {
//console.log('xxxxxx', hf, hf.xCount)
var canvas = document.createElement('canvas');
var canvasWidth = hf.xCount + 1;
var canvasHeight = hf.yCount + 1;
canvas.width = canvasWidth;
canvas.height = canvasHeight;
var ctx = canvas.getContext('2d');
//console.log(hf, hf.xCount, canvasWidth, canvasHeight)
var imgData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
// Fill R (height) and G (light) values from heightfield data and computed light
computeData(hf, lightDir, imgData.data);
// Add wind intensity to B channel
addWindData(imgWind, imgData.data);
ctx.putImageData(imgData, 0, 0);
var tex = new THREE.Texture(canvas);
tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
tex.needsUpdate = true;
return tex;
}
/**
* Pack heights and lighting into RGBA data
*/
function computeData(hf, lightDir, buf) {
var vnorms = hf.vtxNormals;
var w = hf.xCount + 1;
var h = hf.yCount + 1;
var n = new THREE.Vector3();
var tStart = Date.now();
for (var y = 0; y < h; ++y) {
for (var x = 0; x < w; ++x) {
var iSrc = y * w + x;
var iDst = (h - y - 1) * w + x;
// Get height, scale & store in R component
buf[iDst * 4 + 0] = Math.round(hf.heights[iSrc] / hf.maxHeight * 255.0);
// Get normal at this location to compute light
var ni = iSrc * 3;
n.x = vnorms[ni++];
n.y = vnorms[ni++];
n.z = vnorms[ni++];
// Compute light & store in G component
var light = Math.max(-n.dot(lightDir), 0.0);
light *= computeShade(hf, lightDir, x, y);
buf[iDst * 4 + 1] = Math.round(light * 255.0);
//buf[iDst * 4 + 2] = ... // B channel for terrain type?
buf[iDst * 4 + 3] = 255; // must set alpha to some value > 0
}
}
var dt = Date.now() - tStart;
//console.log("computed terrain data texture (".concat(w, "x").concat(h, ") values in ").concat(dt, "ms"));
return buf;
}
var _v = new THREE.Vector2();
function pmod(n, m) {
return ((n % m + m) % m);
}
function computeShade(hf, lightDir, ix, iy) {
// Make a normalized 2D direction vector we'll use to walk horizontally
// toward the lightsource until z reaches max height
var shadGradRange = 5.0;
var hdir = _v;
var w = hf.xCount + 1;
var h = hf.yCount + 1;
var i = iy * w + ix;
var height = hf.heights[i]; // height at this point
hdir.x = -lightDir.x;
hdir.y = -lightDir.y;
hdir.normalize();
var zstep = (hdir.length() / lightDir.length()) * (-lightDir.z);
var x = ix;
var y = iy;
// Walk along the direction until we discover this point
// is in shade or the light vector is too high to be shaded
while (height < hf.maxHeight) {
x += hdir.x;
y += hdir.y;
height += zstep;
var qx = pmod(Math.round(x), w);
var qy = pmod(Math.round(y), h);
var sampleHeight = hf.heights[qy * w + qx];
if (sampleHeight > height) {
if (sampleHeight - height > shadGradRange)
return 0.7; // this point is in shade
else
return 0.7 + 0.3 * (shadGradRange - (sampleHeight - height)) / shadGradRange;
}
}
return 1.0;
}
/**
* Put wind data from the wind image to the b channel
*/
function addWindData(imgWind, buf) {
var canvas = document.createElement('canvas');
var w = imgWind.naturalWidth;
var h = imgWind.naturalHeight;
canvas.width = w;
canvas.height = h;
var ctxSrc = canvas.getContext('2d');
ctxSrc.drawImage(imgWind, 0, 0);
var windData = ctxSrc.getImageData(0, 0, w, h).data;
for (var y = 0; y < h; ++y) {
for (var x = 0; x < w; ++x) {
var i = (y * w + x) * 4;
// Get R channel from src. We only use the single channel
// because assume src img is grayscale.
var p = windData[i];
// Now set the B channel of the buffer we're writing to
buf[i + 2] = p;
}
}
}
class AvatarPawn {
setup() {
if (!this.isMyPlayerPawn) {return;}
this.addFirstResponder("pointerTap", {ctrlKey: true, altKey: true}, this);
this.addEventListener("pointerTap", this.pointerTap);
this.addFirstResponder("pointerDown", {ctrlKey: true, altKey: true}, this);
this.addLastResponder("pointerDown", {}, this);
this.addEventListener("pointerDown", this.pointerDown);
this.addFirstResponder("pointerMove", {ctrlKey: true, altKey: true}, this);
this.addLastResponder("pointerMove", {}, this);
this.addEventListener("pointerMove", this.pointerMove);
this.addLastResponder("pointerUp", {ctrlKey: true, altKey: true}, this);
this.addEventListener("pointerUp", this.pointerUp);
this.addLastResponder("pointerWheel", {ctrlKey: true, altKey: true}, this);
this.addEventListener("pointerWheel", this.pointerWheel);
this.removeEventListener("pointerDoubleDown", "onPointerDoubleDown");
this.addFirstResponder("pointerDoubleDown", {shiftKey: true}, this);
this.addEventListener("pointerDoubleDown", this.addSticky);
this.addLastResponder("keyDown", {ctrlKey: true}, this);
this.addEventListener("keyDown", this.keyDown);
this.addLastResponder("keyUp", {ctrlKey: true}, this);
this.addEventListener("keyUp", this.keyUp);
}
teardown() {
if (!this.isMyPlayerPawn) {return;}
console.log("avatar event handler detached");
this.removeFirstResponder("pointerTap", {ctrlKey: true, altKey: true}, this);
this.removeEventListener("pointerTap", this.pointerTap);
this.removeFirstResponder("pointerDown", {ctrlKey: true, altKey: true}, this);
this.removeLastResponder("pointerDown", {}, this);
this.removeEventListener("pointerDown", this.pointerDown);
this.removeFirstResponder("pointerMove", {ctrlKey: true, altKey: true}, this);
this.removeLastResponder("pointerMove", {}, this);
this.removeEventListener("pointerMove", this.pointerMove);
this.removeLastResponder("pointerUp", {ctrlKey: true, altKey: true}, this);
this.removeEventListener("pointerUp", this.pointerUp);
this.removeLastResponder("pointerWheel", {ctrlKey: true, altKey: true}, this);
this.removeEventListener("pointerWheel", this.pointerWheel);
this.removeEventListener("pointerDoubleDown", "onPointerDoubleDown");
this.removeFirstResponder("pointerDoubleDown", {shiftKey: true}, this);
this.removeEventListener("pointerDoubleDown", this.addSticky);
this.removeLastResponder("keyDown", {ctrlKey: true}, this);
this.removeEventListener("keyDown", this.keyDown);
this.removeLastResponder("keyUp", {ctrlKey: true}, this);
this.removeEventListener("keyUp", this.keyUp);
}
}
export class WalkerPawn {
walkTerrain(vq) {
let walkLayer = this.service("ThreeRenderManager").threeLayer("walk");
if (!walkLayer) return vq;
let collideList = walkLayer.filter(obj => obj.collider);
if (collideList.length === 0) {return vq;}
return this.collideBVH(collideList, vq);
}
checkPortal(vq, _time, _delta) {
let collided = this.collidePortal(vq);
return [vq, collided];
}
checkFall(vq, _time, _delta) {
if (!this.isFalling) {return [vq, false];}
let v = vq.v;
v = [v[0], v[1] - this.fallDistance, v[2]];
this.isFalling = false;
if (v[1] < this.maxFall) {
this.goHome();
return [{v: [0, 0, 0], q: [0, 0, 0, 1]}, true];
}
return [{v: v, q: vq.q}, false];
}
backoutFromFall(vq, _time, _delta) {
if (!this.checkFloor(vq)) {
// if the new position leads to a position where there is no walkable floor below
// it tries to move the avatar the opposite side of the previous good position.
vq.v = Microverse.v3_lerp(this.lastCollideTranslation, vq.v, -1);
} else {
this.lastCollideTranslation = vq.v;
}
return [vq, false];
}
bvh(vq, time, _delta) {
let collide_throttle = this.collide_throttle || 50;
if ((this.actor.fall || this.spectator) && time - this.lastCollideTime > collide_throttle) {
this.lastCollideTime = time;
let result = this.checkFall(vq);
if (result[1]) {return result;}
vq = this.walkTerrain(result[0]);
}
return [vq, false];
}
}
export default {
modules: [
{
name: "AvatarEventHandler",
pawnBehaviors: [AvatarPawn],
},
{
name: "BuiltinWalker",
pawnBehaviors: [WalkerPawn],
}
]
}
/* globals Microverse */
// Copyright 2022 by Croquet Corporation, Inc. All Rights Reserved.
// https://croquet.io
// info@croquet.io
/*
A behaviour that, when added to an object, causes it to turn to face the camera
in each user's view. It does this by overriding the rotation value that the
pawn would otherwise be taking from its actor.
By default, the object's local direction [0, 0, 1] is turned to face the camera.
A pawn can provide a hitNormal property (or getter) to specify a different
reference vector to use.
*/
class BillboardingPawn {
setup() {
let moduleName = this._behavior.module.externalName;
this.addUpdateRequest([`${moduleName}$BillboardingPawn`, "update"]);
this.lastUpdate = 0;
this.lastTarget = null;
this.lastRotation = null;
}
update() {
// adapted from Widget3.update in Worldcore
const {
v3_isZero, v3_equals, v3_sub, v3_normalize, v3_rotate, m4_getTranslation, m4_getRotation, q_lookAt
} = Microverse;
const render = this.service("ThreeRenderManager");
const cameraMatrix = render.camera.matrix;
let v = new Microverse.THREE.Vector3().setFromMatrixPosition(cameraMatrix);
const cameraXZ = [v.x, 0, v.z];
const widgetXZ = m4_getTranslation(this.global);
widgetXZ[1] = 0;
const camRelative = v3_sub(cameraXZ, widgetXZ);
if (v3_isZero(camRelative)) return; // never going to happen during movement. could happen on setup.
const target = v3_normalize(camRelative);
if (!this.lastTarget || !v3_equals(target, this.lastTarget)) {
this.lastTarget = target;
let forward = this.hitNormal || [0, 0, 1];
if (this.parent) forward = v3_rotate(forward, m4_getRotation(this.parent.global));
const up = [0, 1, 0];
this._rotation = q_lookAt(forward, up, target);
this.lastRotation = [...this._rotation];
} else this._rotation = [...this.lastRotation];
this.onLocalChanged();
}
teardown() {
console.log("Billboard teardown");
let moduleName = this._behavior.module.externalName;
this.removeUpdateRequest([`${moduleName}$BillboardingPawn`, "update"]);
}
}
export default {
modules: [
{
name: "Billboard",
pawnBehaviors: [BillboardingPawn],
}
]
}
/* globals Microverse */
export default {
modules: [
{
name: "FileDragAndDropHandler",
}
]
}
// Copyright 2022 by Croquet Corporation, Inc. All Rights Reserved.
// https://croquet.io
// info@croquet.io
/*
It is useful to be able to "elect" one peer to take a certain action
on its view side. For example, the elected view may fetch an external
data stream from internet and feed data in the shared session for all
peers.
This module provides a generic way to elect one peer. It is expected be used with another behavior modules
that actually takes an action (such as fetching data) based on the
election result. See examples in behaviors/default/bitcoinTraker.js
and behaviors/default/flightTracker.js.
*/
/*
ElectedActor keeps track of the currently elected view. It picks the first
of all players currently in the world as the leader of the peers.
setup() may be called multiple times in its life cycle. But since
firstEligibleView prefers the currently elected view, it is safe to call again.
*/
class ElectedActor {
setup() {
this.subscribe("playerManager", "create", "updateElectedView");
this.subscribe("playerManager", "destroy", "updateElectedView");
this.subscribe("playerManager", "enter", "updateElectedView");
this.subscribe("playerManager", "leave", "updateElectedView");
this.updateElectedView();
}
// previous versions of this behavior have used these methods
viewJoined() { this.unsubscribe(this.sessionId, "view-join"); }
viewExited() { this.unsubscribe(this.sessionId, "view-exit"); }
firstEligibleView() {
const manager = this.service("PlayerManager");
// prefer currently elected view, if player is in the world
const current = manager.player(this.electedViewId);
if (current && current.inWorld) return this.electedViewId;
// prefer in-world players (in particular, no spectators)
for (const [viewId, player] of manager.players) {
if (player.inWorld) return viewId;
}
// otherwise, pick the first player
for (const [viewId] of manager.players) {
return viewId;
}
}
updateElectedView() {
const electedBefore = this.electedViewId;
const electedAfter = this.firstEligibleView();
if (electedBefore !== electedAfter) {
this.electedViewId = electedAfter;
this.say("view-elected", electedAfter);
}
}
}
/*
ElectedPawn publishes an event when the elected peer changes. The
electionStatusRequested() method is called when the accompanying
behavior is being setup(), so that the view can check if the view is
already elected.
For other cases, the onViewElected() method is called when the model
chooses a new leader and checks if the peer was either newly unelected
or elected. It publishes the event handleElected and handleUnelected,
and the accompanying behavior attached to the same object is expected
to handle the event.
*/
class ElectedPawn {
setup() {
if (this.electedViewId === undefined) {
this.electedViewId = "";
this.onViewElected(this.actorCall("ElectedActor", "electedViewId"));
}
this.listen({event: "view-elected", handling: "oncePerFrame"}, "onViewElected");
this.listen("handleElected", "handleElected");
this.listen("handleUnelected", "handleUnelected");
this.listen("electionStatusRequested", "electionStatusRequested");
}
isElected() { return this.viewId === this.electedViewId; }
electionStatusRequested() {
if (this.isElected()) {
this.say("handleElected");
}
}
onViewElected(viewId) {
const wasElected = this.isElected();
this.electedViewId = viewId;
if (wasElected !== this.isElected()) {
if (wasElected) {
this.say("handleUnelected", {from: this.electedViewId, to: viewId});
} else {
this.say("handleElected", {from: this.electedViewId, to: viewId});
}
} else {
console.log('%cView Elected: %s (this view %s %s)', 'color: #CC0', this.electedViewId || '<none>', this.viewId,
wasElected ? 'still elected ✅' : 'unaffected ❌');
}
}
handleElected() {
console.log('%cView Elected: %s (this view %s elected ✅)', 'color: #0C0', this.electedViewId || '<none>', this.viewId);
}
handleUnelected() {
console.log('%cView Elected: %s (this view %s unelected ❌)', 'color: #C00', this.electedViewId || '<none>', this.viewId);
}
teardown() {
this.onViewElected("");
}
}
export default {
modules: [
{
name: "Elected",
actorBehaviors: [ElectedActor],
pawnBehaviors: [ElectedPawn]
}
]
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
// Copyright 2022 by Croquet Corporation, Inc. All Rights Reserved.
// https://croquet.io
// info@croquet.io
/*
This is a wrapper to call Rapier Physics Engine features. It is expected to be used
a user-defined behavior module that creates a rigid body and a
collider description. (see behaviors/default/cascade.js for an
example.)
*/
class PhysicsActor {
teardown() {
this.removeImpulseJoint();
this.removeCollider();
this.removeRigidBody();
delete this._myPhysicsWorld;
// _myPhysicsWorld is there so that a destroyed card, which lost its parent
// property when this teardown is executed, still removes itself from the physics world.
}
getRigidBody() {
/*
A "dollar-property" is a special model-side property naming
convention which excludes the data to be stored in the
snapshot. In this case, rigidBody is a cache to hold onto
the rigidBody object.
When a user joins an existing session, the snapshot will not
contain this.$rigidBody. So it is lazily initialized when it
is accessed.
The implementation of PhysicsManager is in src/physics2.js.
*/
if (!this.$rigidBody) {
if (this.rigidBodyHandle === undefined) return undefined;
const physicsWorld = this.physicsWorld;
this.$rigidBody = physicsWorld.world.getRigidBody(this.rigidBodyHandle);
}
return this.$rigidBody;
}
createRigidBody(rbd) {
this.removeRigidBody();
rbd.translation = new Microverse.Physics.Vector3(...this.translation);
rbd.rotation = new Microverse.Physics.Quaternion(...this.rotation);
this._myPhysicsWorld = this.physicsWorld;
this.$rigidBody = this._myPhysicsWorld.world.createRigidBody(rbd);
this.rigidBodyHandle = this.$rigidBody.handle;
this._myPhysicsWorld.rigidBodies.set(this.rigidBodyHandle, this._target);
/*
Those events are handled so that when a position-based object
was moved from the user program, the object's position and
rotatino in the simulation are updated.
*/
if (this.getRigidBody().bodyType() === Microverse.Physics.RigidBodyType.KinematicPositionBased) {
this.listen("translationSet", "Physics$PhysicsActor.setKinematicTranslation");
this.listen("rotationSet", "Physics$PhysicsActor.setKinematicRotation");
}
}
setKinematicTranslation(data) {
this.getRigidBody().setNextKinematicTranslation(new Microverse.Physics.Vector3(...data.v));
}
setKinematicRotation(data) {
this.getRigidBody().setNextKinematicRotation(new Microverse.Physics.Quaternion(...data.v));
}
removeRigidBody() {
let r = this.getRigidBody();
if (!r) return;
const physicsWorld = this._myPhysicsWorld;
if (!physicsWorld) {return;}
physicsWorld.world.removeRigidBody(r);
physicsWorld.rigidBodies.delete(this.rigidBodyHandle)
delete this.rigidBodyHandle;
delete this.$rigidBody;
}
createCollider(cd) {
this.removeCollider();
const physicsWorld = this.physicsWorld;
this._myPhysicsWorld = physicsWorld;
let collider = physicsWorld.world.createCollider(cd, this.getRigidBody());
this.colliderHandle = collider.handle;
return this.colliderHandle;
}
removeCollider() {
if (this.colliderHandle === undefined) return;
const physicsWorld = this._myPhysicsWorld;
if (!physicsWorld) {return;}
let world = physicsWorld.world;
let collider = world.getCollider(this.colliderHandle);
if (collider) {
world.removeCollider(collider);
}
delete this.colliderHandle;
}
createImpulseJoint(type, body1, body2, ...params) {
this.removeImpulseJoint();
const physicsWorld = this.physicsWorld;
if (!physicsWorld) {return;}
this._myPhysicsWorld = physicsWorld;
// compatibility with Rapier 0.7.3 based spec
if (type === "ball") {type = "spherical";}
let func = Microverse.Physics.JointData[type];
if (!func) {throw new Error("unkown joint types");}
let jointParams = func.call(Microverse.Physics.JointData, ...params);
let joint = physicsWorld.world.createImpulseJoint(jointParams, body1.rigidBody, body2.rigidBody);
this.jointHandle = joint.handle;
return this.jointHandle;
}
removeImpulseJoint() {
if (this.jointHandle === undefined) return;
const physicsWorld = this._myPhysicsWorld;
if (!physicsWorld) {return;}
let world = physicsWorld.world;
let joint = world.getImpulseJoint(this.jointHandle);
if (joint) {
world.removeJoint(joint);
}
delete this.jointHandle;
}
}
export default {
modules: [
{
name: "Physics",
actorBehaviors: [PhysicsActor]
}
]
}
/* globals Microverse */
此差异已折叠。
// Copyright 2022 by Croquet Corporation, Inc. All Rights Reserved.
// https://croquet.io
// info@croquet.io
/*
This is a wrapper to call Rapier features. It is expected to be used
a user-defined behavior module that creates a rigid body and a
collider description. (see behaviors/default/cascade.js for an
example.)
*/
class RapierActor {
setup() {
this._oldRapier07 = true;
}
teardown() {
this.removeImpulseJoint();
this.removeCollider();
this.removeRigidBody();
}
getRigidBody() {
return null;
}
createRigidBody(rbd) {
}
setKinematicTranslation(data) {
}
setKinematicRotation(data) {
}
removeRigidBody() {
}
createCollider(cd) {
return null;
}
removeCollider() {
}
createImpulseJoint(type, body1, body2, ...params) {
return null;
}
removeImpulseJoint() {
}
}
export default {
modules: [
{
name: "Rapier",
actorBehaviors: [RapierActor]
}
]
}
/* globals Microverse */
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册