vendor: 1,458,113 bytes, lines 1-60586
1/** 2 * @license 3 * Copyright 2010-2026 Three.js Authors 4 * SPDX-License-Identifier: MIT 5 */ 6const REVISION = '186'; 7 8/** 9 * Represents mouse buttons and interaction types in context of controls. 10 * 11 * @type {ConstantsMouse} 12 * @constant 13 */ 14const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }; 15 16/** 17 * Represents touch interaction types in context of controls. 18 * 19 * @type {ConstantsTouch} 20 * @constant 21 */ 22const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 }; 23 24/** 25 * Disables face culling. 26 * 27 * @type {number} 28 * @constant 29 */ 30const CullFaceNone = 0; 31 32/** 33 * Culls back faces. 34 * 35 * @type {number} 36 * @constant 37 */ 38const CullFaceBack = 1; 39 40/** 41 * Culls front faces. 42 * 43 * @type {number} 44 * @constant 45 */ 46const CullFaceFront = 2; 47 48/** 49 * Culls both front and back faces. 50 * 51 * @type {number} 52 * @constant 53 */ 54const CullFaceFrontBack = 3; 55 56/** 57 * Gives unfiltered shadow maps - fastest, but lowest quality. 58 * 59 * @type {number} 60 * @constant 61 */ 62const BasicShadowMap = 0; 63 64/** 65 * Filters shadow maps using the Percentage-Closer Filtering (PCF) algorithm. 66 * 67 * @type {number} 68 * @constant 69 */ 70const PCFShadowMap = 1; 71 72/** 73 * Filters shadow maps using the Percentage-Closer Filtering (PCF) algorithm with 74 * better soft shadows especially when using low-resolution shadow maps. 75 * 76 * @type {number} 77 * @constant 78 * @deprecated since r186. Use `PCFShadowMap` instead. 79 */ 80const PCFSoftShadowMap = 2; 81 82/** 83 * Filters shadow maps using the Variance Shadow Map (VSM) algorithm. 84 * When using VSMShadowMap all shadow receivers will also cast shadows. 85 * 86 * @type {number} 87 * @constant 88 */ 89const VSMShadowMap = 3; 90 91/** 92 * Only front faces are rendered. 93 * 94 * @type {number} 95 * @constant 96 */ 97const FrontSide = 0; 98 99/** 100 * Only back faces are rendered. 101 * 102 * @type {number} 103 * @constant 104 */ 105const BackSide = 1; 106 107/** 108 * Both front and back faces are rendered. 109 * 110 * @type {number} 111 * @constant 112 */ 113const DoubleSide = 2; 114 115/** 116 * No blending is performed which effectively disables 117 * alpha transparency. 118 * 119 * @type {number} 120 * @constant 121 */ 122const NoBlending = 0; 123 124/** 125 * The default blending. 126 * 127 * @type {number} 128 * @constant 129 */ 130const NormalBlending = 1; 131 132/** 133 * Represents additive blending. 134 * 135 * @type {number} 136 * @constant 137 */ 138const AdditiveBlending = 2; 139 140/** 141 * Represents subtractive blending. 142 * 143 * @type {number} 144 * @constant 145 */ 146const SubtractiveBlending = 3; 147 148/** 149 * Represents multiply blending. 150 * 151 * @type {number} 152 * @constant 153 */ 154const MultiplyBlending = 4; 155 156/** 157 * Represents custom blending. 158 * 159 * @type {number} 160 * @constant 161 */ 162const CustomBlending = 5; 163 164/** 165 * Represents material blending. 166 * 167 * @type {number} 168 * @constant 169 */ 170const MaterialBlending = 6; 171 172/** 173 * A `source + destination` blending equation. 174 * 175 * @type {number} 176 * @constant 177 */ 178const AddEquation = 100; 179 180/** 181 * A `source - destination` blending equation. 182 * 183 * @type {number} 184 * @constant 185 */ 186const SubtractEquation = 101; 187 188/** 189 * A `destination - source` blending equation. 190 * 191 * @type {number} 192 * @constant 193 */ 194const ReverseSubtractEquation = 102; 195 196/** 197 * A blend equation that uses the minimum of source and destination. 198 * 199 * @type {number} 200 * @constant 201 */ 202const MinEquation = 103; 203 204/** 205 * A blend equation that uses the maximum of source and destination. 206 * 207 * @type {number} 208 * @constant 209 */ 210const MaxEquation = 104; 211 212/** 213 * Multiplies all colors by `0`. 214 * 215 * @type {number} 216 * @constant 217 */ 218const ZeroFactor = 200; 219 220/** 221 * Multiplies all colors by `1`. 222 * 223 * @type {number} 224 * @constant 225 */ 226const OneFactor = 201; 227 228/** 229 * Multiplies all colors by the source colors. 230 * 231 * @type {number} 232 * @constant 233 */ 234const SrcColorFactor = 202; 235 236/** 237 * Multiplies all colors by `1` minus each source color. 238 * 239 * @type {number} 240 * @constant 241 */ 242const OneMinusSrcColorFactor = 203; 243 244/** 245 * Multiplies all colors by the source alpha value. 246 * 247 * @type {number} 248 * @constant 249 */ 250const SrcAlphaFactor = 204; 251 252/** 253 * Multiplies all colors by 1 minus the source alpha value. 254 * 255 * @type {number} 256 * @constant 257 */ 258const OneMinusSrcAlphaFactor = 205; 259 260/** 261 * Multiplies all colors by the destination alpha value. 262 * 263 * @type {number} 264 * @constant 265 */ 266const DstAlphaFactor = 206; 267 268/** 269 * Multiplies all colors by `1` minus the destination alpha value. 270 * 271 * @type {number} 272 * @constant 273 */ 274const OneMinusDstAlphaFactor = 207; 275 276/** 277 * Multiplies all colors by the destination color. 278 * 279 * @type {number} 280 * @constant 281 */ 282const DstColorFactor = 208; 283 284/** 285 * Multiplies all colors by `1` minus each destination color. 286 * 287 * @type {number} 288 * @constant 289 */ 290const OneMinusDstColorFactor = 209; 291 292/** 293 * Multiplies the RGB colors by the smaller of either the source alpha 294 * value or the value of `1` minus the destination alpha value. The alpha 295 * value is multiplied by `1`. 296 * 297 * @type {number} 298 * @constant 299 */ 300const SrcAlphaSaturateFactor = 210; 301 302/** 303 * Multiplies all colors by a constant color. 304 * 305 * @type {number} 306 * @constant 307 */ 308const ConstantColorFactor = 211; 309 310/** 311 * Multiplies all colors by `1` minus a constant color. 312 * 313 * @type {number} 314 * @constant 315 */ 316const OneMinusConstantColorFactor = 212; 317 318/** 319 * Multiplies all colors by a constant alpha value. 320 * 321 * @type {number} 322 * @constant 323 */ 324const ConstantAlphaFactor = 213; 325 326/** 327 * Multiplies all colors by 1 minus a constant alpha value. 328 * 329 * @type {number} 330 * @constant 331 */ 332const OneMinusConstantAlphaFactor = 214; 333 334/** 335 * Never pass. 336 * 337 * @type {number} 338 * @constant 339 */ 340const NeverDepth = 0; 341 342/** 343 * Always pass. 344 * 345 * @type {number} 346 * @constant 347 */ 348const AlwaysDepth = 1; 349 350/** 351 * Pass if the incoming value is less than the depth buffer value. 352 * 353 * @type {number} 354 * @constant 355 */ 356const LessDepth = 2; 357 358/** 359 * Pass if the incoming value is less than or equal to the depth buffer value. 360 * 361 * @type {number} 362 * @constant 363 */ 364const LessEqualDepth = 3; 365 366/** 367 * Pass if the incoming value equals the depth buffer value. 368 * 369 * @type {number} 370 * @constant 371 */ 372const EqualDepth = 4; 373 374/** 375 * Pass if the incoming value is greater than or equal to the depth buffer value. 376 * 377 * @type {number} 378 * @constant 379 */ 380const GreaterEqualDepth = 5; 381 382/** 383 * Pass if the incoming value is greater than the depth buffer value. 384 * 385 * @type {number} 386 * @constant 387 */ 388const GreaterDepth = 6; 389 390/** 391 * Pass if the incoming value is not equal to the depth buffer value. 392 * 393 * @type {number} 394 * @constant 395 */ 396const NotEqualDepth = 7; 397 398/** 399 * Multiplies the environment map color with the surface color. 400 * 401 * @type {number} 402 * @constant 403 */ 404const MultiplyOperation = 0; 405 406/** 407 * Uses reflectivity to blend between the two colors. 408 * 409 * @type {number} 410 * @constant 411 */ 412const MixOperation = 1; 413 414/** 415 * Adds the two colors. 416 * 417 * @type {number} 418 * @constant 419 */ 420const AddOperation = 2; 421 422/** 423 * No tone mapping is applied. 424 * 425 * @type {number} 426 * @constant 427 */ 428const NoToneMapping = 0; 429 430/** 431 * Linear tone mapping. 432 * 433 * @type {number} 434 * @constant 435 */ 436const LinearToneMapping = 1; 437 438/** 439 * Reinhard tone mapping. 440 * 441 * @type {number} 442 * @constant 443 */ 444const ReinhardToneMapping = 2; 445 446/** 447 * Cineon tone mapping. 448 * 449 * @type {number} 450 * @constant 451 */ 452const CineonToneMapping = 3; 453 454/** 455 * ACES Filmic tone mapping. 456 * 457 * @type {number} 458 * @constant 459 */ 460const ACESFilmicToneMapping = 4; 461 462/** 463 * Custom tone mapping. 464 * 465 * Expects a custom implementation by modifying shader code of the material's fragment shader. 466 * 467 * @type {number} 468 * @constant 469 */ 470const CustomToneMapping = 5; 471 472/** 473 * AgX tone mapping. 474 * 475 * @type {number} 476 * @constant 477 */ 478const AgXToneMapping = 6; 479 480/** 481 * Neutral tone mapping. 482 * 483 * Implementation based on the Khronos 3D Commerce Group standard tone mapping. 484 * 485 * @type {number} 486 * @constant 487 */ 488const NeutralToneMapping = 7; 489 490/** 491 * The skinned mesh shares the same world space as the skeleton. 492 * 493 * @type {string} 494 * @constant 495 */ 496const AttachedBindMode = 'attached'; 497 498/** 499 * The skinned mesh does not share the same world space as the skeleton. 500 * This is useful when a skeleton is shared across multiple skinned meshes. 501 * 502 * @type {string} 503 * @constant 504 */ 505const DetachedBindMode = 'detached'; 506 507/** 508 * Maps textures using the geometry's UV coordinates. 509 * 510 * @type {number} 511 * @constant 512 */ 513const UVMapping = 300; 514 515/** 516 * Reflection mapping for cube textures. 517 * 518 * @type {number} 519 * @constant 520 */ 521const CubeReflectionMapping = 301; 522 523/** 524 * Refraction mapping for cube textures. 525 * 526 * @type {number} 527 * @constant 528 */ 529const CubeRefractionMapping = 302; 530 531/** 532 * Reflection mapping for equirectangular textures. 533 * 534 * @type {number} 535 * @constant 536 */ 537const EquirectangularReflectionMapping = 303; 538 539/** 540 * Refraction mapping for equirectangular textures. 541 * 542 * @type {number} 543 * @constant 544 */ 545const EquirectangularRefractionMapping = 304; 546 547/** 548 * Reflection mapping for PMREM textures. 549 * 550 * @type {number} 551 * @constant 552 */ 553const CubeUVReflectionMapping = 306; 554 555/** 556 * The texture will simply repeat to infinity. 557 * 558 * @type {number} 559 * @constant 560 */ 561const RepeatWrapping = 1000; 562 563/** 564 * The last pixel of the texture stretches to the edge of the mesh. 565 * 566 * @type {number} 567 * @constant 568 */ 569const ClampToEdgeWrapping = 1001; 570 571/** 572 * The texture will repeats to infinity, mirroring on each repeat. 573 * 574 * @type {number} 575 * @constant 576 */ 577const MirroredRepeatWrapping = 1002; 578 579/** 580 * Returns the value of the texture element that is nearest (in Manhattan distance) 581 * to the specified texture coordinates. 582 * 583 * @type {number} 584 * @constant 585 */ 586const NearestFilter = 1003; 587 588/** 589 * Chooses the mipmap that most closely matches the size of the pixel being textured 590 * and uses the `NearestFilter` criterion (the texel nearest to the center of the pixel) 591 * to produce a texture value. 592 * 593 * @type {number} 594 * @constant 595 */ 596const NearestMipmapNearestFilter = 1004; 597const NearestMipMapNearestFilter = 1004; // legacy 598 599/** 600 * Chooses the two mipmaps that most closely match the size of the pixel being textured and 601 * uses the `NearestFilter` criterion to produce a texture value from each mipmap. 602 * The final texture value is a weighted average of those two values. 603 * 604 * @type {number} 605 * @constant 606 */ 607const NearestMipmapLinearFilter = 1005; 608const NearestMipMapLinearFilter = 1005; // legacy 609 610/** 611 * Returns the weighted average of the four texture elements that are closest to the specified 612 * texture coordinates, and can include items wrapped or repeated from other parts of a texture, 613 * depending on the values of `wrapS` and `wrapT`, and on the exact mapping. 614 * 615 * @type {number} 616 * @constant 617 */ 618const LinearFilter = 1006; 619 620/** 621 * Chooses the mipmap that most closely matches the size of the pixel being textured and uses 622 * the `LinearFilter` criterion (a weighted average of the four texels that are closest to the 623 * center of the pixel) to produce a texture value. 624 * 625 * @type {number} 626 * @constant 627 */ 628const LinearMipmapNearestFilter = 1007; 629const LinearMipMapNearestFilter = 1007; // legacy 630 631/** 632 * Chooses the two mipmaps that most closely match the size of the pixel being textured and uses 633 * the `LinearFilter` criterion to produce a texture value from each mipmap. The final texture value 634 * is a weighted average of those two values. 635 * 636 * @type {number} 637 * @constant 638 */ 639const LinearMipmapLinearFilter = 1008; 640const LinearMipMapLinearFilter = 1008; // legacy 641 642/** 643 * An unsigned byte data type for textures. 644 * 645 * @type {number} 646 * @constant 647 */ 648const UnsignedByteType = 1009; 649 650/** 651 * A byte data type for textures. 652 * 653 * @type {number} 654 * @constant 655 */ 656const ByteType = 1010; 657 658/** 659 * A short data type for textures. 660 * 661 * @type {number} 662 * @constant 663 */ 664const ShortType = 1011; 665 666/** 667 * An unsigned short data type for textures. 668 * 669 * @type {number} 670 * @constant 671 */ 672const UnsignedShortType = 1012; 673 674/** 675 * An int data type for textures. 676 * 677 * @type {number} 678 * @constant 679 */ 680const IntType = 1013; 681 682/** 683 * An unsigned int data type for textures. 684 * 685 * @type {number} 686 * @constant 687 */ 688const UnsignedIntType = 1014; 689 690/** 691 * A float data type for textures. 692 * 693 * @type {number} 694 * @constant 695 */ 696const FloatType = 1015; 697 698/** 699 * A half float data type for textures. 700 * 701 * @type {number} 702 * @constant 703 */ 704const HalfFloatType = 1016; 705 706/** 707 * An unsigned short 4_4_4_4 (packed) data type for textures. 708 * 709 * @type {number} 710 * @constant 711 */ 712const UnsignedShort4444Type = 1017; 713 714/** 715 * An unsigned short 5_5_5_1 (packed) data type for textures. 716 * 717 * @type {number} 718 * @constant 719 */ 720const UnsignedShort5551Type = 1018; 721 722/** 723 * An unsigned int 24_8 data type for textures. 724 * 725 * @type {number} 726 * @constant 727 */ 728const UnsignedInt248Type = 1020; 729 730/** 731 * An unsigned int 5_9_9_9 (packed) data type for textures. 732 * 733 * @type {number} 734 * @constant 735 */ 736const UnsignedInt5999Type = 35902; 737 738/** 739 * An unsigned int 10_11_11 (packed) data type for textures. 740 * 741 * @type {number} 742 * @constant 743 */ 744const UnsignedInt101111Type = 35899; 745 746/** 747 * Discards the red, green and blue components and reads just the alpha component. 748 * 749 * @type {number} 750 * @constant 751 */ 752const AlphaFormat = 1021; 753 754/** 755 * Discards the alpha component and reads the red, green and blue component. 756 * 757 * @type {number} 758 * @constant 759 */ 760const RGBFormat = 1022; 761 762/** 763 * Reads the red, green, blue and alpha components. 764 * 765 * @type {number} 766 * @constant 767 */ 768const RGBAFormat = 1023; 769 770/** 771 * Reads each element as a single depth value, converts it to floating point, and clamps to the range `[0,1]`. 772 * 773 * @type {number} 774 * @constant 775 */ 776const DepthFormat = 1026; 777 778/** 779 * Reads each element is a pair of depth and stencil values. The depth component of the pair is interpreted as 780 * in `DepthFormat`. The stencil component is interpreted based on the depth + stencil internal format. 781 * 782 * @type {number} 783 * @constant 784 */ 785const DepthStencilFormat = 1027; 786 787/** 788 * Discards the green, blue and alpha components and reads just the red component. 789 * 790 * @type {number} 791 * @constant 792 */ 793const RedFormat = 1028; 794 795/** 796 * Discards the green, blue and alpha components and reads just the red component. The texels are read as integers instead of floating point. 797 * 798 * @type {number} 799 * @constant 800 */ 801const RedIntegerFormat = 1029; 802 803/** 804 * Discards the alpha, and blue components and reads the red, and green components. 805 * 806 * @type {number} 807 * @constant 808 */ 809const RGFormat = 1030; 810 811/** 812 * Discards the alpha, and blue components and reads the red, and green components. The texels are read as integers instead of floating point. 813 * 814 * @type {number} 815 * @constant 816 */ 817const RGIntegerFormat = 1031; 818 819/** 820 * Discards the alpha component and reads the red, green and blue component. The texels are read as integers instead of floating point. 821 * 822 * @type {number} 823 * @constant 824 */ 825const RGBIntegerFormat = 1032; 826 827/** 828 * Reads the red, green, blue and alpha components. The texels are read as integers instead of floating point. 829 * 830 * @type {number} 831 * @constant 832 */ 833const RGBAIntegerFormat = 1033; 834 835/** 836 * A DXT1-compressed image in an RGB image format. 837 * 838 * @type {number} 839 * @constant 840 */ 841const RGB_S3TC_DXT1_Format = 33776; 842 843/** 844 * A DXT1-compressed image in an RGB image format with a simple on/off alpha value. 845 * 846 * @type {number} 847 * @constant 848 */ 849const RGBA_S3TC_DXT1_Format = 33777; 850 851/** 852 * A DXT3-compressed image in an RGBA image format. Compared to a 32-bit RGBA texture, it offers 4:1 compression. 853 * 854 * @type {number} 855 * @constant 856 */ 857const RGBA_S3TC_DXT3_Format = 33778; 858 859/** 860 * A DXT5-compressed image in an RGBA image format. It also provides a 4:1 compression, but differs to the DXT3 861 * compression in how the alpha compression is done. 862 * 863 * @type {number} 864 * @constant 865 */ 866const RGBA_S3TC_DXT5_Format = 33779; 867 868/** 869 * PVRTC RGB compression in 4-bit mode. One block for each 4Ã4 pixels. 870 * 871 * @type {number} 872 * @constant 873 */ 874const RGB_PVRTC_4BPPV1_Format = 35840; 875 876/** 877 * PVRTC RGB compression in 2-bit mode. One block for each 8Ã4 pixels. 878 * 879 * @type {number} 880 * @constant 881 */ 882const RGB_PVRTC_2BPPV1_Format = 35841; 883 884/** 885 * PVRTC RGBA compression in 4-bit mode. One block for each 4Ã4 pixels. 886 * 887 * @type {number} 888 * @constant 889 */ 890const RGBA_PVRTC_4BPPV1_Format = 35842; 891 892/** 893 * PVRTC RGBA compression in 2-bit mode. One block for each 8Ã4 pixels. 894 * 895 * @type {number} 896 * @constant 897 */ 898const RGBA_PVRTC_2BPPV1_Format = 35843; 899 900/** 901 * ETC1 RGB format. 902 * 903 * @type {number} 904 * @constant 905 */ 906const RGB_ETC1_Format = 36196; 907 908/** 909 * ETC2 RGB format. 910 * 911 * @type {number} 912 * @constant 913 */ 914const RGB_ETC2_Format = 37492; 915 916/** 917 * ETC2 RGBA format. 918 * 919 * @type {number} 920 * @constant 921 */ 922const RGBA_ETC2_EAC_Format = 37496; 923 924/** 925 * EAC R11 UNORM format. 926 * 927 * @type {number} 928 * @constant 929 */ 930const R11_EAC_Format = 37488; // 0x9270 931 932/** 933 * EAC R11 SNORM format. 934 * 935 * @type {number} 936 * @constant 937 */ 938const SIGNED_R11_EAC_Format = 37489; // 0x9271 939 940/** 941 * EAC RG11 UNORM format. 942 * 943 * @type {number} 944 * @constant 945 */ 946const RG11_EAC_Format = 37490; // 0x9272 947 948/** 949 * EAC RG11 SNORM format. 950 * 951 * @type {number} 952 * @constant 953 */ 954const SIGNED_RG11_EAC_Format = 37491; // 0x9273 955 956/** 957 * ASTC RGBA 4x4 format. 958 * 959 * @type {number} 960 * @constant 961 */ 962const RGBA_ASTC_4x4_Format = 37808; 963 964/** 965 * ASTC RGBA 5x4 format. 966 * 967 * @type {number} 968 * @constant 969 */ 970const RGBA_ASTC_5x4_Format = 37809; 971 972/** 973 * ASTC RGBA 5x5 format. 974 * 975 * @type {number} 976 * @constant 977 */ 978const RGBA_ASTC_5x5_Format = 37810; 979 980/** 981 * ASTC RGBA 6x5 format. 982 * 983 * @type {number} 984 * @constant 985 */ 986const RGBA_ASTC_6x5_Format = 37811; 987 988/** 989 * ASTC RGBA 6x6 format. 990 * 991 * @type {number} 992 * @constant 993 */ 994const RGBA_ASTC_6x6_Format = 37812; 995 996/** 997 * ASTC RGBA 8x5 format. 998 * 999 * @type {number} 1000 * @constant 1001 */ 1002const RGBA_ASTC_8x5_Format = 37813; 1003 1004/** 1005 * ASTC RGBA 8x6 format. 1006 * 1007 * @type {number} 1008 * @constant 1009 */ 1010const RGBA_ASTC_8x6_Format = 37814; 1011 1012/** 1013 * ASTC RGBA 8x8 format. 1014 * 1015 * @type {number} 1016 * @constant 1017 */ 1018const RGBA_ASTC_8x8_Format = 37815; 1019 1020/** 1021 * ASTC RGBA 10x5 format. 1022 * 1023 * @type {number} 1024 * @constant 1025 */ 1026const RGBA_ASTC_10x5_Format = 37816; 1027 1028/** 1029 * ASTC RGBA 10x6 format. 1030 * 1031 * @type {number} 1032 * @constant 1033 */ 1034const RGBA_ASTC_10x6_Format = 37817; 1035 1036/** 1037 * ASTC RGBA 10x8 format. 1038 * 1039 * @type {number} 1040 * @constant 1041 */ 1042const RGBA_ASTC_10x8_Format = 37818; 1043 1044/** 1045 * ASTC RGBA 10x10 format. 1046 * 1047 * @type {number} 1048 * @constant 1049 */ 1050const RGBA_ASTC_10x10_Format = 37819; 1051 1052/** 1053 * ASTC RGBA 12x10 format. 1054 * 1055 * @type {number} 1056 * @constant 1057 */ 1058const RGBA_ASTC_12x10_Format = 37820; 1059 1060/** 1061 * ASTC RGBA 12x12 format. 1062 * 1063 * @type {number} 1064 * @constant 1065 */ 1066const RGBA_ASTC_12x12_Format = 37821; 1067 1068/** 1069 * BPTC RGBA format. 1070 * 1071 * @type {number} 1072 * @constant 1073 */ 1074const RGBA_BPTC_Format = 36492; 1075 1076/** 1077 * BPTC Signed RGB format. 1078 * 1079 * @type {number} 1080 * @constant 1081 */ 1082const RGB_BPTC_SIGNED_Format = 36494; 1083 1084/** 1085 * BPTC Unsigned RGB format. 1086 * 1087 * @type {number} 1088 * @constant 1089 */ 1090const RGB_BPTC_UNSIGNED_Format = 36495; 1091 1092/** 1093 * RGTC1 Red format. 1094 * 1095 * @type {number} 1096 * @constant 1097 */ 1098const RED_RGTC1_Format = 36283; 1099 1100/** 1101 * RGTC1 Signed Red format. 1102 * 1103 * @type {number} 1104 * @constant 1105 */ 1106const SIGNED_RED_RGTC1_Format = 36284; 1107 1108/** 1109 * RGTC2 Red Green format. 1110 * 1111 * @type {number} 1112 * @constant 1113 */ 1114const RED_GREEN_RGTC2_Format = 36285; 1115 1116/** 1117 * RGTC2 Signed Red Green format. 1118 * 1119 * @type {number} 1120 * @constant 1121 */ 1122const SIGNED_RED_GREEN_RGTC2_Format = 36286; 1123 1124/** 1125 * Animations are played once. 1126 * 1127 * @type {number} 1128 * @constant 1129 */ 1130const LoopOnce = 2200; 1131 1132/** 1133 * Animations are played with a chosen number of repetitions, each time jumping from 1134 * the end of the clip directly to its beginning. 1135 * 1136 * @type {number} 1137 * @constant 1138 */ 1139const LoopRepeat = 2201; 1140 1141/** 1142 * Animations are played with a chosen number of repetitions, alternately playing forward 1143 * and backward. 1144 * 1145 * @type {number} 1146 * @constant 1147 */ 1148const LoopPingPong = 2202; 1149 1150/** 1151 * Discrete interpolation mode for keyframe tracks. 1152 * 1153 * @type {number} 1154 * @constant 1155 */ 1156const InterpolateDiscrete = 2300; 1157 1158/** 1159 * Linear interpolation mode for keyframe tracks. 1160 * 1161 * @type {number} 1162 * @constant 1163 */ 1164const InterpolateLinear = 2301; 1165 1166/** 1167 * Smooth interpolation mode for keyframe tracks. 1168 * 1169 * @type {number} 1170 * @constant 1171 */ 1172const InterpolateSmooth = 2302; 1173 1174/** 1175 * Bezier interpolation mode for keyframe tracks. 1176 * 1177 * Uses cubic Bezier curves with explicit 2D control points. 1178 * Requires tangent data to be set on the track. 1179 * 1180 * @type {number} 1181 * @constant 1182 */ 1183const InterpolateBezier = 2303; 1184 1185/** 1186 * Zero curvature ending for animations. 1187 * 1188 * @type {number} 1189 * @constant 1190 */ 1191const ZeroCurvatureEnding = 2400; 1192 1193/** 1194 * Zero slope ending for animations. 1195 * 1196 * @type {number} 1197 * @constant 1198 */ 1199const ZeroSlopeEnding = 2401; 1200 1201/** 1202 * Wrap around ending for animations. 1203 * 1204 * @type {number} 1205 * @constant 1206 */ 1207const WrapAroundEnding = 2402; 1208 1209/** 1210 * Default animation blend mode. 1211 * 1212 * @type {number} 1213 * @constant 1214 */ 1215const NormalAnimationBlendMode = 2500; 1216 1217/** 1218 * Additive animation blend mode. Can be used to layer motions on top of 1219 * each other to build complex performances from smaller re-usable assets. 1220 * 1221 * @type {number} 1222 * @constant 1223 */ 1224const AdditiveAnimationBlendMode = 2501; 1225 1226/** 1227 * For every three vertices draw a single triangle. 1228 * 1229 * @type {number} 1230 * @constant 1231 */ 1232const TrianglesDrawMode = 0; 1233 1234/** 1235 * For each vertex draw a triangle from the last three vertices. 1236 * 1237 * @type {number} 1238 * @constant 1239 */ 1240const TriangleStripDrawMode = 1; 1241 1242/** 1243 * For each vertex draw a triangle from the first vertex and the last two vertices. 1244 * 1245 * @type {number} 1246 * @constant 1247 */ 1248const TriangleFanDrawMode = 2; 1249 1250/** 1251 * The depth value is inverted (1.0 - z) for visualization purposes. 1252 * 1253 * @type {number} 1254 * @constant 1255 */ 1256const BasicDepthPacking = 3200; 1257 1258/** 1259 * The depth value is packed into 32 bit RGBA. 1260 * 1261 * @type {number} 1262 * @constant 1263 */ 1264const RGBADepthPacking = 3201; 1265 1266/** 1267 * The depth value is packed into 24 bit RGB. 1268 * 1269 * @type {number} 1270 * @constant 1271 */ 1272const RGBDepthPacking = 3202; 1273 1274/** 1275 * The depth value is packed into 16 bit RG. 1276 * 1277 * @type {number} 1278 * @constant 1279 */ 1280const RGDepthPacking = 3203; 1281 1282/** 1283 * Normal information is relative to the underlying surface. 1284 * 1285 * @type {number} 1286 * @constant 1287 */ 1288const TangentSpaceNormalMap = 0; 1289 1290/** 1291 * Normal information is relative to the object orientation. 1292 * 1293 * @type {number} 1294 * @constant 1295 */ 1296const ObjectSpaceNormalMap = 1; 1297 1298// Color space string identifiers, matching CSS Color Module Level 4 and WebGPU names where available. 1299 1300/** 1301 * No color space. 1302 * 1303 * @type {string} 1304 * @constant 1305 */ 1306const NoColorSpace = ''; 1307 1308/** 1309 * sRGB color space. 1310 * 1311 * @type {string} 1312 * @constant 1313 */ 1314const SRGBColorSpace = 'srgb'; 1315 1316/** 1317 * sRGB-linear color space. 1318 * 1319 * @type {string} 1320 * @constant 1321 */ 1322const LinearSRGBColorSpace = 'srgb-linear'; 1323 1324/** 1325 * Linear transfer function. 1326 * 1327 * @type {string} 1328 * @constant 1329 */ 1330const LinearTransfer = 'linear'; 1331 1332/** 1333 * sRGB transfer function. 1334 * 1335 * @type {string} 1336 * @constant 1337 */ 1338const SRGBTransfer = 'srgb'; 1339 1340/** 1341 * No normal map packing. 1342 * 1343 * @type {string} 1344 * @constant 1345 */ 1346const NoNormalPacking = ''; 1347 1348/** 1349 * Normal RG packing. 1350 * 1351 * @type {string} 1352 * @constant 1353 */ 1354const NormalRGPacking = 'rg'; 1355 1356/** 1357 * Normal GA packing. 1358 * 1359 * @type {string} 1360 * @constant 1361 */ 1362const NormalGAPacking = 'ga'; 1363 1364/** 1365 * Sets the stencil buffer value to `0`. 1366 * 1367 * @type {number} 1368 * @constant 1369 */ 1370const ZeroStencilOp = 0; 1371 1372/** 1373 * Keeps the current value. 1374 * 1375 * @type {number} 1376 * @constant 1377 */ 1378const KeepStencilOp = 7680; 1379 1380/** 1381 * Sets the stencil buffer value to the specified reference value. 1382 * 1383 * @type {number} 1384 * @constant 1385 */ 1386const ReplaceStencilOp = 7681; 1387 1388/** 1389 * Increments the current stencil buffer value. Clamps to the maximum representable unsigned value. 1390 * 1391 * @type {number} 1392 * @constant 1393 */ 1394const IncrementStencilOp = 7682; 1395 1396/** 1397 * Decrements the current stencil buffer value. Clamps to `0`. 1398 * 1399 * @type {number} 1400 * @constant 1401 */ 1402const DecrementStencilOp = 7683; 1403 1404/** 1405 * Increments the current stencil buffer value. Wraps stencil buffer value to zero when incrementing 1406 * the maximum representable unsigned value. 1407 * 1408 * @type {number} 1409 * @constant 1410 */ 1411const IncrementWrapStencilOp = 34055; 1412 1413/** 1414 * Decrements the current stencil buffer value. Wraps stencil buffer value to the maximum representable 1415 * unsigned value when decrementing a stencil buffer value of `0`. 1416 * 1417 * @type {number} 1418 * @constant 1419 */ 1420const DecrementWrapStencilOp = 34056; 1421 1422/** 1423 * Inverts the current stencil buffer value bitwise. 1424 * 1425 * @type {number} 1426 * @constant 1427 */ 1428const InvertStencilOp = 5386; 1429 1430/** 1431 * Will never return true. 1432 * 1433 * @type {number} 1434 * @constant 1435 */ 1436const NeverStencilFunc = 512; 1437 1438/** 1439 * Will return true if the stencil reference value is less than the current stencil value. 1440 * 1441 * @type {number} 1442 * @constant 1443 */ 1444const LessStencilFunc = 513; 1445 1446/** 1447 * Will return true if the stencil reference value is equal to the current stencil value. 1448 * 1449 * @type {number} 1450 * @constant 1451 */ 1452const EqualStencilFunc = 514; 1453 1454/** 1455 * Will return true if the stencil reference value is less than or equal to the current stencil value. 1456 * 1457 * @type {number} 1458 * @constant 1459 */ 1460const LessEqualStencilFunc = 515; 1461 1462/** 1463 * Will return true if the stencil reference value is greater than the current stencil value. 1464 * 1465 * @type {number} 1466 * @constant 1467 */ 1468const GreaterStencilFunc = 516; 1469 1470/** 1471 * Will return true if the stencil reference value is not equal to the current stencil value. 1472 * 1473 * @type {number} 1474 * @constant 1475 */ 1476const NotEqualStencilFunc = 517; 1477 1478/** 1479 * Will return true if the stencil reference value is greater than or equal to the current stencil value. 1480 * 1481 * @type {number} 1482 * @constant 1483 */ 1484const GreaterEqualStencilFunc = 518; 1485 1486/** 1487 * Will always return true. 1488 * 1489 * @type {number} 1490 * @constant 1491 */ 1492const AlwaysStencilFunc = 519; 1493 1494/** 1495 * Never pass. 1496 * 1497 * @type {number} 1498 * @constant 1499 */ 1500const NeverCompare = 512; 1501 1502/** 1503 * Pass if the incoming value is less than the texture value. 1504 * 1505 * @type {number} 1506 * @constant 1507 */ 1508const LessCompare = 513; 1509 1510/** 1511 * Pass if the incoming value equals the texture value. 1512 * 1513 * @type {number} 1514 * @constant 1515 */ 1516const EqualCompare = 514; 1517 1518/** 1519 * Pass if the incoming value is less than or equal to the texture value. 1520 * 1521 * @type {number} 1522 * @constant 1523 */ 1524const LessEqualCompare = 515; 1525 1526/** 1527 * Pass if the incoming value is greater than the texture value. 1528 * 1529 * @type {number} 1530 * @constant 1531 */ 1532const GreaterCompare = 516; 1533 1534/** 1535 * Pass if the incoming value is not equal to the texture value. 1536 * 1537 * @type {number} 1538 * @constant 1539 */ 1540const NotEqualCompare = 517; 1541 1542/** 1543 * Pass if the incoming value is greater than or equal to the texture value. 1544 * 1545 * @type {number} 1546 * @constant 1547 */ 1548const GreaterEqualCompare = 518; 1549 1550/** 1551 * Always pass. 1552 * 1553 * @type {number} 1554 * @constant 1555 */ 1556const AlwaysCompare = 519; 1557 1558/** 1559 * The contents are intended to be specified once by the application, and used many 1560 * times as the source for drawing and image specification commands. 1561 * 1562 * @type {number} 1563 * @constant 1564 */ 1565const StaticDrawUsage = 35044; 1566 1567/** 1568 * The contents are intended to be respecified repeatedly by the application, and 1569 * used many times as the source for drawing and image specification commands. 1570 * 1571 * @type {number} 1572 * @constant 1573 */ 1574const DynamicDrawUsage = 35048; 1575 1576/** 1577 * The contents are intended to be specified once by the application, and used at most 1578 * a few times as the source for drawing and image specification commands. 1579 * 1580 * @type {number} 1581 * @constant 1582 */ 1583const StreamDrawUsage = 35040; 1584 1585/** 1586 * The contents are intended to be specified once by reading data from the 3D API, and queried 1587 * many times by the application. 1588 * 1589 * @type {number} 1590 * @constant 1591 */ 1592const StaticReadUsage = 35045; 1593 1594/** 1595 * The contents are intended to be respecified repeatedly by reading data from the 3D API, and queried 1596 * many times by the application. 1597 * 1598 * @type {number} 1599 * @constant 1600 */ 1601const DynamicReadUsage = 35049; 1602 1603/** 1604 * The contents are intended to be specified once by reading data from the 3D API, and queried at most 1605 * a few times by the application 1606 * 1607 * @type {number} 1608 * @constant 1609 */ 1610const StreamReadUsage = 35041; 1611 1612/** 1613 * The contents are intended to be specified once by reading data from the 3D API, and used many times as 1614 * the source for WebGL drawing and image specification commands. 1615 * 1616 * @type {number} 1617 * @constant 1618 */ 1619const StaticCopyUsage = 35046; 1620 1621/** 1622 * The contents are intended to be respecified repeatedly by reading data from the 3D API, and used many times 1623 * as the source for WebGL drawing and image specification commands. 1624 * 1625 * @type {number} 1626 * @constant 1627 */ 1628const DynamicCopyUsage = 35050; 1629 1630/** 1631 * The contents are intended to be specified once by reading data from the 3D API, and used at most a few times 1632 * as the source for WebGL drawing and image specification commands. 1633 * 1634 * @type {number} 1635 * @constant 1636 */ 1637const StreamCopyUsage = 35042; 1638 1639/** 1640 * GLSL 1 shader code. 1641 * 1642 * @type {string} 1643 * @constant 1644 */ 1645const GLSL1 = '100'; 1646 1647/** 1648 * GLSL 3 shader code. 1649 * 1650 * @type {string} 1651 * @constant 1652 */ 1653const GLSL3 = '300 es'; 1654 1655/** 1656 * WebGL coordinate system. 1657 * 1658 * @type {number} 1659 * @constant 1660 */ 1661const WebGLCoordinateSystem = 2000; 1662 1663/** 1664 * WebGPU coordinate system. 1665 * 1666 * @type {number} 1667 * @constant 1668 */ 1669const WebGPUCoordinateSystem = 2001; 1670 1671/** 1672 * Represents the different timestamp query types. 1673 * 1674 * @type {ConstantsTimestampQuery} 1675 * @constant 1676 */ 1677const TimestampQuery = { 1678 COMPUTE: 'compute', 1679 RENDER: 'render' 1680}; 1681 1682/** 1683 * Represents mouse buttons and interaction types in context of controls. 1684 * 1685 * @type {ConstantsInterpolationSamplingType} 1686 * @constant 1687 */ 1688const InterpolationSamplingType = { 1689 PERSPECTIVE: 'perspective', 1690 LINEAR: 'linear', 1691 FLAT: 'flat' 1692}; 1693 1694/** 1695 * Represents the different interpolation sampling modes. 1696 * 1697 * @type {ConstantsInterpolationSamplingMode} 1698 * @constant 1699 */ 1700const InterpolationSamplingMode = { 1701 NORMAL: 'normal', 1702 CENTROID: 'centroid', 1703 SAMPLE: 'sample', 1704 FIRST: 'first', 1705 EITHER: 'either' 1706}; 1707 1708/** 1709 * Compatibility flags for features that may not be supported across all platforms. 1710 * 1711 * @type {Object} 1712 * @constant 1713 */ 1714const Compatibility = { 1715 TEXTURE_COMPARE: 'depthTextureCompare' 1716}; 1717 1718/** 1719 * Represents the refresh types of render objects. 1720 * 1721 * @type {ConstantsRenderObjectRefreshType} 1722 * @constant 1723 */ 1724const RenderObjectRefreshType = { 1725 NONE: 0, 1726 SHARED: 1, 1727 FULL: 2 1728}; 1729 1730/** 1731 * This type represents mouse buttons and interaction types in context of controls. 1732 * 1733 * @typedef {Object} ConstantsMouse 1734 * @property {number} MIDDLE - The left mouse button. 1735 * @property {number} LEFT - The middle mouse button. 1736 * @property {number} RIGHT - The right mouse button. 1737 * @property {number} ROTATE - A rotate interaction. 1738 * @property {number} DOLLY - A dolly interaction. 1739 * @property {number} PAN - A pan interaction. 1740 **/ 1741 1742/** 1743 * This type represents touch interaction types in context of controls. 1744 * 1745 * @typedef {Object} ConstantsTouch 1746 * @property {number} ROTATE - A rotate interaction. 1747 * @property {number} PAN - A pan interaction. 1748 * @property {number} DOLLY_PAN - The dolly-pan interaction. 1749 * @property {number} DOLLY_ROTATE - A dolly-rotate interaction. 1750 **/ 1751 1752/** 1753 * This type represents the different timestamp query types. 1754 * 1755 * @typedef {Object} ConstantsTimestampQuery 1756 * @property {string} COMPUTE - A `compute` timestamp query. 1757 * @property {string} RENDER - A `render` timestamp query. 1758 **/ 1759 1760/** 1761 * Represents the different interpolation sampling types. 1762 * 1763 * @typedef {Object} ConstantsInterpolationSamplingType 1764 * @property {string} PERSPECTIVE - Perspective-correct interpolation. 1765 * @property {string} LINEAR - Linear interpolation. 1766 * @property {string} FLAT - Flat interpolation. 1767 */ 1768 1769/** 1770 * Represents the different interpolation sampling modes. 1771 * 1772 * @typedef {Object} ConstantsInterpolationSamplingMode 1773 * @property {string} NORMAL - Normal sampling mode. 1774 * @property {string} CENTROID - Centroid sampling mode. 1775 * @property {string} SAMPLE - Sample-specific sampling mode. 1776 * @property {string} FIRST - Flat interpolation using the first vertex. 1777 * @property {string} EITHER - Flat interpolation using either vertex. 1778 */ 1779 1780/** 1781 * Represents the refresh types of render objects. 1782 * 1783 * @typedef {Object} ConstantsRenderObjectRefreshType 1784 * @property {number} NONE - No refresh required. 1785 * @property {number} SHARED - Only shared uniform buffers require an update. 1786 * @property {number} FULL - The render object requires a full refresh. 1787 */ 1788 1789/** 1790 * Checks if an array contains values that require Uint32 representation. 1791 * 1792 * This function determines whether the array contains any values >= 65535, 1793 * which would require a Uint32Array rather than a Uint16Array for proper storage. 1794 * The function iterates from the end of the array, assuming larger values are 1795 * typically located at the end. 1796 * 1797 * @private 1798 * @param {Array<number>} array - The array to check. 1799 * @return {boolean} True if the array contains values >= 65535, false otherwise. 1800 */ 1801function arrayNeedsUint32( array ) { 1802 1803 // assumes larger values usually on last 1804 1805 for ( let i = array.length - 1; i >= 0; -- i ) { 1806 1807 if ( array[ i ] >= 65535 ) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565 1808 1809 } 1810 1811 return false; 1812 1813} 1814 1815/** 1816 * Map of typed array constructor names to their constructors. 1817 * This mapping enables dynamic creation of typed arrays based on string type names. 1818 * 1819 * @private 1820 * @constant 1821 * @type {Object<string, TypedArrayConstructor>} 1822 */ 1823const TYPED_ARRAYS = { 1824 Int8Array: Int8Array, 1825 Uint8Array: Uint8Array, 1826 Uint8ClampedArray: Uint8ClampedArray, 1827 Int16Array: Int16Array, 1828 Uint16Array: Uint16Array, 1829 Int32Array: Int32Array, 1830 Uint32Array: Uint32Array, 1831 Float32Array: Float32Array, 1832 Float64Array: Float64Array 1833}; 1834 1835/** 1836 * Creates a typed array of the specified type from the given buffer. 1837 * 1838 * @private 1839 * @param {string} type - The name of the typed array type (e.g., 'Float32Array', 'Uint16Array'). 1840 * @param {ArrayBuffer} buffer - The buffer to create the typed array from. 1841 * @return {TypedArray} A new typed array of the specified type. 1842 */ 1843function getTypedArray( type, buffer ) { 1844 1845 return new TYPED_ARRAYS[ type ]( buffer ); 1846 1847} 1848 1849/** 1850 * Returns `true` if the given object is a typed array. 1851 * 1852 * @param {any} array - The object to check. 1853 * @return {boolean} Whether the given object is a typed array. 1854 */ 1855function isTypedArray( array ) { 1856 1857 return ArrayBuffer.isView( array ) && ! ( array instanceof DataView ); 1858 1859} 1860 1861/** 1862 * Creates an XHTML element with the specified tag name. 1863 * 1864 * This function uses the XHTML namespace to create DOM elements, 1865 * ensuring proper element creation in XML-based contexts. 1866 * 1867 * @private 1868 * @param {string} name - The tag name of the element to create (e.g., 'canvas', 'div'). 1869 * @return {HTMLElement} The created XHTML element. 1870 */ 1871function createElementNS( name ) { 1872 1873 return document.createElementNS( 'http://www.w3.org/1999/xhtml', name ); 1874 1875} 1876 1877/** 1878 * Creates a canvas element configured for block display. 1879 * 1880 * This is a convenience function that creates a canvas element with 1881 * display style set to 'block', which is commonly used in three.js 1882 * rendering contexts to avoid inline element spacing issues. 1883 * 1884 * @return {HTMLCanvasElement} A canvas element with display set to 'block'. 1885 */ 1886function createCanvasElement() { 1887 1888 const canvas = createElementNS( 'canvas' ); 1889 canvas.style.display = 'block'; 1890 return canvas; 1891 1892} 1893 1894/** 1895 * Internal cache for tracking warning messages to prevent duplicate warnings. 1896 * 1897 * @private 1898 * @type {Object<string, boolean>} 1899 */ 1900const _cache = {}; 1901 1902/** 1903 * Custom console function handler for intercepting log, warn, and error calls. 1904 * 1905 * @private 1906 * @type {Function|null} 1907 */ 1908let _setConsoleFunction = null; 1909 1910/** 1911 * Sets a custom function to handle console output. 1912 * 1913 * This allows external code to intercept and handle console.log, console.warn, 1914 * and console.error calls made by three.js, which is useful for custom logging, 1915 * testing, or debugging workflows. 1916 * 1917 * @param {Function} fn - The function to handle console output. Should accept 1918 * (type, message, ...params) where type is 'log', 'warn', or 'error'. 1919 */ 1920function setConsoleFunction( fn ) { 1921 1922 _setConsoleFunction = fn; 1923 1924} 1925 1926/** 1927 * Gets the currently set custom console function. 1928 * 1929 * @return {Function|null} The custom console function, or null if not set. 1930 */ 1931function getConsoleFunction() { 1932 1933 return _setConsoleFunction; 1934 1935} 1936 1937/** 1938 * Logs an informational message with the 'THREE.' prefix. 1939 * 1940 * If a custom console function is set via setConsoleFunction(), it will be used 1941 * instead of the native console.log. The first parameter is treated as the 1942 * method name and is automatically prefixed with 'THREE.'. 1943 * 1944 * @param {...any} params - The message components. The first param is used as 1945 * the method name and prefixed with 'THREE.'. 1946 */ 1947function log( ...params ) { 1948 1949 const message = 'THREE.' + params.shift(); 1950 1951 if ( _setConsoleFunction ) { 1952 1953 _setConsoleFunction( 'log', message, ...params ); 1954 1955 } else { 1956 1957 console.log( message, ...params ); 1958 1959 } 1960 1961} 1962 1963/** 1964 * Enhances log/warn/error messages related to TSL. 1965 * 1966 * @param {Array<any>} params - The original message parameters. 1967 * @returns {Array<any>} The filtered and enhanced message parameters. 1968 */ 1969function enhanceLogMessage( params ) { 1970 1971 const message = params[ 0 ]; 1972 1973 if ( typeof message === 'string' && message.startsWith( 'TSL:' ) ) { 1974 1975 const stackTrace = params[ 1 ]; 1976 1977 if ( stackTrace && stackTrace.isStackTrace ) { 1978 1979 params[ 0 ] += ' ' + stackTrace.getLocation(); 1980 1981 } else { 1982 1983 params[ 1 ] = 'Stack trace not available. Enable "THREE.Node.captureStackTrace" to capture stack traces.'; 1984 1985 } 1986 1987 } 1988 1989 return params; 1990 1991} 1992 1993/** 1994 * Logs a warning message with the 'THREE.' prefix. 1995 * 1996 * If a custom console function is set via setConsoleFunction(), it will be used 1997 * instead of the native console.warn. The first parameter is treated as the 1998 * method name and is automatically prefixed with 'THREE.'. 1999 * 2000 * @param {...any} params - The message components. The first param is used as 2001 * the method name and prefixed with 'THREE.'. 2002 */ 2003function warn( ...params ) { 2004 2005 params = enhanceLogMessage( params ); 2006 2007 const message = 'THREE.' + params.shift(); 2008 2009 if ( _setConsoleFunction ) { 2010 2011 _setConsoleFunction( 'warn', message, ...params ); 2012 2013 } else { 2014 2015 const stackTrace = params[ 0 ]; 2016 2017 if ( stackTrace && stackTrace.isStackTrace ) { 2018 2019 console.warn( stackTrace.getError( message ) ); 2020 2021 } else { 2022 2023 console.warn( message, ...params ); 2024 2025 } 2026 2027 } 2028 2029} 2030 2031/** 2032 * Logs an error message with the 'THREE.' prefix. 2033 * 2034 * If a custom console function is set via setConsoleFunction(), it will be used 2035 * instead of the native console.error. The first parameter is treated as the 2036 * method name and is automatically prefixed with 'THREE.'. 2037 * 2038 * @param {...any} params - The message components. The first param is used as 2039 * the method name and prefixed with 'THREE.'. 2040 */ 2041function error( ...params ) { 2042 2043 params = enhanceLogMessage( params ); 2044 2045 const message = 'THREE.' + params.shift(); 2046 2047 if ( _setConsoleFunction ) { 2048 2049 _setConsoleFunction( 'error', message, ...params ); 2050 2051 } else { 2052 2053 const stackTrace = params[ 0 ]; 2054 2055 if ( stackTrace && stackTrace.isStackTrace ) { 2056 2057 console.error( stackTrace.getError( message ) ); 2058 2059 } else { 2060 2061 console.error( message, ...params ); 2062 2063 } 2064 2065 } 2066 2067} 2068 2069/** 2070 * Logs a warning message only once, preventing duplicate warnings. 2071 * 2072 * This function maintains an internal cache of warning messages and will only 2073 * output each unique warning message once. Useful for warnings that may be 2074 * triggered repeatedly but should only be shown to the user once. 2075 * 2076 * @param {...any} params - The warning message components. 2077 */ 2078function warnOnce( ...params ) { 2079 2080 const message = params.join( ' ' ); 2081 2082 if ( message in _cache ) return; 2083 2084 _cache[ message ] = true; 2085 2086 warn( ...params ); 2087 2088} 2089 2090/** 2091 * Yields execution to the main thread to allow rendering and other tasks. 2092 * Uses scheduler.yield() when available (Chrome 115+), falls back to requestAnimationFrame. 2093 * 2094 * @return {Promise<void>} 2095 */ 2096function yieldToMain() { 2097 2098 if ( typeof self !== 'undefined' && typeof self.scheduler !== 'undefined' && typeof self.scheduler.yield !== 'undefined' ) { 2099 2100 return self.scheduler.yield(); 2101 2102 } 2103 2104 return new Promise( resolve => { 2105 2106 requestAnimationFrame( resolve ); 2107 2108 } ); 2109 2110} 2111 2112/** 2113 * Asynchronously probes for WebGL sync object completion. 2114 * 2115 * This function creates a promise that resolves when the WebGL sync object 2116 * signals completion or rejects if the sync operation fails. It uses polling 2117 * at the specified interval to check the sync status without blocking the 2118 * main thread. This is useful for GPU-CPU synchronization in WebGL contexts. 2119 * 2120 * @private 2121 * @param {WebGL2RenderingContext} gl - The WebGL rendering context. 2122 * @param {WebGLSync} sync - The WebGL sync object to wait for. 2123 * @param {number} interval - The polling interval in milliseconds. 2124 * @return {Promise<void>} A promise that resolves when the sync completes or rejects if it fails. 2125 */ 2126function probeAsync( gl, sync, interval ) { 2127 2128 return new Promise( function ( resolve, reject ) { 2129 2130 function probe() { 2131 2132 switch ( gl.clientWaitSync( sync, gl.SYNC_FLUSH_COMMANDS_BIT, 0 ) ) { 2133 2134 case gl.WAIT_FAILED: 2135 reject(); 2136 break; 2137 2138 case gl.TIMEOUT_EXPIRED: 2139 setTimeout( probe, interval ); 2140 break; 2141 2142 default: 2143 resolve(); 2144 2145 } 2146 2147 } 2148 2149 setTimeout( probe, interval ); 2150 2151 } ); 2152 2153} 2154 2155/** 2156 * Used to select the correct depth functions 2157 * when reversed depth buffer is used. 2158 * 2159 * @private 2160 * @type {Object} 2161 */ 2162const ReversedDepthFuncs = { 2163 [ NeverDepth ]: AlwaysDepth, 2164 [ LessDepth ]: GreaterDepth, 2165 [ EqualDepth ]: NotEqualDepth, 2166 [ LessEqualDepth ]: GreaterEqualDepth, 2167 2168 [ AlwaysDepth ]: NeverDepth, 2169 [ GreaterDepth ]: LessDepth, 2170 [ NotEqualDepth ]: EqualDepth, 2171 [ GreaterEqualDepth ]: LessEqualDepth, 2172}; 2173 2174/** 2175 * This modules allows to dispatch event objects on custom JavaScript objects. 2176 * 2177 * Main repository: [eventdispatcher.js](https://github.com/mrdoob/eventdispatcher.js/) 2178 * 2179 * Code Example: 2180 * ```js 2181 * class Car extends EventDispatcher { 2182 * start() { 2183 * this.dispatchEvent( { type: 'start', message: 'vroom vroom!' } ); 2184 * } 2185 *}; 2186 * 2187 * // Using events with the custom object 2188 * const car = new Car(); 2189 * car.addEventListener( 'start', function ( event ) { 2190 * alert( event.message ); 2191 * } ); 2192 * 2193 * car.start(); 2194 * ``` 2195 */ 2196class EventDispatcher { 2197 2198 /** 2199 * Adds the given event listener to the given event type. 2200 * 2201 * @param {string} type - The type of event to listen to. 2202 * @param {Function} listener - The function that gets called when the event is fired. 2203 */ 2204 addEventListener( type, listener ) { 2205 2206 if ( this._listeners === undefined ) this._listeners = {}; 2207 2208 const listeners = this._listeners; 2209 2210 if ( listeners[ type ] === undefined ) { 2211 2212 listeners[ type ] = []; 2213 2214 } 2215 2216 if ( listeners[ type ].indexOf( listener ) === -1 ) { 2217 2218 listeners[ type ].push( listener ); 2219 2220 } 2221 2222 } 2223 2224 /** 2225 * Returns `true` if the given event listener has been added to the given event type. 2226 * 2227 * @param {string} type - The type of event. 2228 * @param {Function} listener - The listener to check. 2229 * @return {boolean} Whether the given event listener has been added to the given event type. 2230 */ 2231 hasEventListener( type, listener ) { 2232 2233 const listeners = this._listeners; 2234 2235 if ( listeners === undefined ) return false; 2236 2237 return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== -1; 2238 2239 } 2240 2241 /** 2242 * Removes the given event listener from the given event type. 2243 * 2244 * @param {string} type - The type of event. 2245 * @param {Function} listener - The listener to remove. 2246 */ 2247 removeEventListener( type, listener ) { 2248 2249 const listeners = this._listeners; 2250 2251 if ( listeners === undefined ) return; 2252 2253 const listenerArray = listeners[ type ]; 2254 2255 if ( listenerArray !== undefined ) { 2256 2257 const index = listenerArray.indexOf( listener ); 2258 2259 if ( index !== -1 ) { 2260 2261 listenerArray.splice( index, 1 ); 2262 2263 } 2264 2265 } 2266 2267 } 2268 2269 /** 2270 * Dispatches an event object. 2271 * 2272 * @param {Object} event - The event that gets fired. 2273 */ 2274 dispatchEvent( event ) { 2275 2276 const listeners = this._listeners; 2277 2278 if ( listeners === undefined ) return; 2279 2280 const listenerArray = listeners[ event.type ]; 2281 2282 if ( listenerArray !== undefined ) { 2283 2284 event.target = this; 2285 2286 // Make a copy, in case listeners are removed while iterating. 2287 const array = listenerArray.slice( 0 ); 2288 2289 for ( let i = 0, l = array.length; i < l; i ++ ) { 2290 2291 array[ i ].call( this, event ); 2292 2293 } 2294 2295 event.target = null; 2296 2297 } 2298 2299 } 2300 2301} 2302 2303const _lut = [ '00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '0a', '0b', '0c', '0d', '0e', '0f', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1a', '1b', '1c', '1d', '1e', '1f', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '2a', '2b', '2c', '2d', '2e', '2f', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '3a', '3b', '3c', '3d', '3e', '3f', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '4a', '4b', '4c', '4d', '4e', '4f', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '5a', '5b', '5c', '5d', '5e', '5f', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '6a', '6b', '6c', '6d', '6e', '6f', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '7a', '7b', '7c', '7d', '7e', '7f', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '8a', '8b', '8c', '8d', '8e', '8f', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '9a', '9b', '9c', '9d', '9e', '9f', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'aa', 'ab', 'ac', 'ad', 'ae', 'af', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'ba', 'bb', 'bc', 'bd', 'be', 'bf', 'c0', 'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9', 'ca', 'cb', 'cc', 'cd', 'ce', 'cf', 'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', 'da', 'db', 'dc', 'dd', 'de', 'df', 'e0', 'e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8', 'e9', 'ea', 'eb', 'ec', 'ed', 'ee', 'ef', 'f0', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'fa', 'fb', 'fc', 'fd', 'fe', 'ff' ]; 2304 2305let _seed = 1234567; 2306 2307 2308const DEG2RAD = Math.PI / 180; 2309const RAD2DEG = 180 / Math.PI; 2310 2311/** 2312 * Generate a [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier) 2313 * (universally unique identifier). 2314 * 2315 * @return {string} The UUID. 2316 */ 2317function generateUUID() { 2318 2319 // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 2320 2321 const d0 = Math.random() * 0xffffffff | 0; 2322 const d1 = Math.random() * 0xffffffff | 0; 2323 const d2 = Math.random() * 0xffffffff | 0; 2324 const d3 = Math.random() * 0xffffffff | 0; 2325 const uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + '-' + 2326 _lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + '-' + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + '-' + 2327 _lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + '-' + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] + 2328 _lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ]; 2329 2330 // .toLowerCase() here flattens concatenated strings to save heap memory space. 2331 return uuid.toLowerCase(); 2332 2333} 2334 2335/** 2336 * Clamps the given value between min and max. 2337 * 2338 * @param {number} value - The value to clamp. 2339 * @param {number} min - The min value. 2340 * @param {number} max - The max value. 2341 * @return {number} The clamped value. 2342 */ 2343function clamp( value, min, max ) { 2344 2345 return Math.max( min, Math.min( max, value ) ); 2346 2347} 2348 2349/** 2350 * Computes the Euclidean modulo of the given parameters that 2351 * is `( ( n % m ) + m ) % m`. 2352 * 2353 * @param {number} n - The first parameter. 2354 * @param {number} m - The second parameter. 2355 * @return {number} The Euclidean modulo. 2356 */ 2357function euclideanModulo( n, m ) { 2358 2359 // https://en.wikipedia.org/wiki/Modulo_operation 2360 2361 return ( ( n % m ) + m ) % m; 2362 2363} 2364 2365/** 2366 * Performs a linear mapping from range `<a1, a2>` to range `<b1, b2>` 2367 * for the given value. `a2` must be greater than `a1`. 2368 * 2369 * @param {number} x - The value to be mapped. 2370 * @param {number} a1 - Minimum value for range A. 2371 * @param {number} a2 - Maximum value for range A. 2372 * @param {number} b1 - Minimum value for range B. 2373 * @param {number} b2 - Maximum value for range B. 2374 * @return {number} The mapped value. 2375 */ 2376function mapLinear( x, a1, a2, b1, b2 ) { 2377 2378 return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); 2379 2380} 2381 2382/** 2383 * Returns the percentage in the closed interval `[0, 1]` of the given value 2384 * between the start and end point. 2385 * 2386 * @param {number} x - The start point 2387 * @param {number} y - The end point. 2388 * @param {number} value - A value between start and end. 2389 * @return {number} The interpolation factor. 2390 */ 2391function inverseLerp( x, y, value ) { 2392 2393 // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/ 2394 2395 if ( x !== y ) { 2396 2397 return ( value - x ) / ( y - x ); 2398 2399 } else { 2400 2401 return 0; 2402 2403 } 2404 2405} 2406 2407/** 2408 * Returns a value linearly interpolated from two known points based on the given interval - 2409 * `t = 0` will return `x` and `t = 1` will return `y`. 2410 * 2411 * @param {number} x - The start point 2412 * @param {number} y - The end point. 2413 * @param {number} t - The interpolation factor in the closed interval `[0, 1]`. 2414 * @return {number} The interpolated value. 2415 */ 2416function lerp( x, y, t ) { 2417 2418 return ( 1 - t ) * x + t * y; 2419 2420} 2421 2422/** 2423 * Smoothly interpolate a number from `x` to `y` in a spring-like manner using a delta 2424 * time to maintain frame rate independent movement. For details, see 2425 * [Frame rate independent damping using lerp](http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/). 2426 * 2427 * @param {number} x - The current point. 2428 * @param {number} y - The target point. 2429 * @param {number} lambda - A higher lambda value will make the movement more sudden, 2430 * and a lower value will make the movement more gradual. 2431 * @param {number} dt - Delta time in seconds. 2432 * @return {number} The interpolated value. 2433 */ 2434function damp( x, y, lambda, dt ) { 2435 2436 return lerp( x, y, 1 - Math.exp( - lambda * dt ) ); 2437 2438} 2439 2440/** 2441 * Returns a value that alternates between `0` and the given `length` parameter. 2442 * 2443 * @param {number} x - The value to pingpong. 2444 * @param {number} [length=1] - The positive value the function will pingpong to. 2445 * @return {number} The alternated value. 2446 */ 2447function pingpong( x, length = 1 ) { 2448 2449 // https://www.desmos.com/calculator/vcsjnyz7x4 2450 2451 return length - Math.abs( euclideanModulo( x, length * 2 ) - length ); 2452 2453} 2454 2455/** 2456 * Returns a value in the range `[0,1]` that represents the percentage that `x` has 2457 * moved between `min` and `max`, but smoothed or slowed down the closer `x` is to 2458 * the `min` and `max`. 2459 * 2460 * See [Smoothstep](http://en.wikipedia.org/wiki/Smoothstep) for more details. 2461 * 2462 * @param {number} x - The value to evaluate based on its position between `min` and `max`. 2463 * @param {number} min - The min value. Any `x` value below `min` will be `0`. `min` must be lower than `max`. 2464 * @param {number} max - The max value. Any `x` value above `max` will be `1`. `max` must be greater than `min`. 2465 * @return {number} The alternated value. 2466 */ 2467function smoothstep( x, min, max ) { 2468 2469 if ( x <= min ) return 0; 2470 if ( x >= max ) return 1; 2471 2472 x = ( x - min ) / ( max - min ); 2473 2474 return x * x * ( 3 - 2 * x ); 2475 2476} 2477 2478/** 2479 * A [variation on smoothstep](https://en.wikipedia.org/wiki/Smoothstep#Variations) 2480 * that has zero 1st and 2nd order derivatives at `x=0` and `x=1`. 2481 * 2482 * @param {number} x - The value to evaluate based on its position between `min` and `max`. 2483 * @param {number} min - The min value. Any `x` value below `min` will be `0`. `min` must be lower than `max`. 2484 * @param {number} max - The max value. Any `x` value above `max` will be `1`. `max` must be greater than `min`. 2485 * @return {number} The alternated value. 2486 */ 2487function smootherstep( x, min, max ) { 2488 2489 if ( x <= min ) return 0; 2490 if ( x >= max ) return 1; 2491 2492 x = ( x - min ) / ( max - min ); 2493 2494 return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); 2495 2496} 2497 2498/** 2499 * Returns a random integer from `<low, high>` interval. 2500 * 2501 * @param {number} low - The lower value boundary. 2502 * @param {number} high - The upper value boundary 2503 * @return {number} A random integer. 2504 */ 2505function randInt( low, high ) { 2506 2507 return low + Math.floor( Math.random() * ( high - low + 1 ) ); 2508 2509} 2510 2511/** 2512 * Returns a random float from `<low, high>` interval. 2513 * 2514 * @param {number} low - The lower value boundary. 2515 * @param {number} high - The upper value boundary 2516 * @return {number} A random float. 2517 */ 2518function randFloat( low, high ) { 2519 2520 return low + Math.random() * ( high - low ); 2521 2522} 2523 2524/** 2525 * Returns a random integer from `<-range/2, range/2>` interval. 2526 * 2527 * @param {number} range - Defines the value range. 2528 * @return {number} A random float. 2529 */ 2530function randFloatSpread( range ) { 2531 2532 return range * ( 0.5 - Math.random() ); 2533 2534} 2535 2536/** 2537 * Returns a deterministic pseudo-random float in the interval `[0, 1]`. 2538 * 2539 * @param {number} [s] - The integer seed. 2540 * @return {number} A random float. 2541 */ 2542function seededRandom( s ) { 2543 2544 if ( s !== undefined ) _seed = s; 2545 2546 // Mulberry32 generator 2547 2548 let t = _seed += 0x6D2B79F5; 2549 2550 t = Math.imul( t ^ t >>> 15, t | 1 ); 2551 2552 t ^= t + Math.imul( t ^ t >>> 7, t | 61 ); 2553 2554 return ( ( t ^ t >>> 14 ) >>> 0 ) / 4294967296; 2555 2556} 2557 2558/** 2559 * Converts degrees to radians. 2560 * 2561 * @param {number} degrees - A value in degrees. 2562 * @return {number} The converted value in radians. 2563 */ 2564function degToRad( degrees ) { 2565 2566 return degrees * DEG2RAD; 2567 2568} 2569 2570/** 2571 * Converts radians to degrees. 2572 * 2573 * @param {number} radians - A value in radians. 2574 * @return {number} The converted value in degrees. 2575 */ 2576function radToDeg( radians ) { 2577 2578 return radians * RAD2DEG; 2579 2580} 2581 2582/** 2583 * Returns `true` if the given integer is a power of two. 2584 * 2585 * @param {number} value - The value to check. 2586 * @return {boolean} Whether the given integer is a power of two or not. 2587 */ 2588function isPowerOfTwo( value ) { 2589 2590 return value > 0 && Number.isInteger( value ) && 2 ** Math.round( Math.log2( value ) ) === value; 2591 2592} 2593 2594/** 2595 * Returns the smallest power of two that is greater than or equal to the given number. 2596 * 2597 * @param {number} value - The value to find a POT for. Must be greater than `0`. 2598 * @return {number} The smallest power of two that is greater than or equal to the given number. 2599 */ 2600function ceilPowerOfTwo( value ) { 2601 2602 return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); 2603 2604} 2605 2606/** 2607 * Returns the largest power of two that is less than or equal to the given number. 2608 * 2609 * @param {number} value - The value to find a POT for. Must be greater than `0`. 2610 * @return {number} The largest power of two that is less than or equal to the given number. 2611 */ 2612function floorPowerOfTwo( value ) { 2613 2614 return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); 2615 2616} 2617 2618/** 2619 * Sets the given quaternion from the [Intrinsic Proper Euler Angles](https://en.wikipedia.org/wiki/Euler_angles) 2620 * defined by the given angles and order. 2621 * 2622 * Rotations are applied to the axes in the order specified by order: 2623 * rotation by angle `a` is applied first, then by angle `b`, then by angle `c`. 2624 * 2625 * @param {Quaternion} q - The quaternion to set. 2626 * @param {number} a - The rotation applied to the first axis, in radians. 2627 * @param {number} b - The rotation applied to the second axis, in radians. 2628 * @param {number} c - The rotation applied to the third axis, in radians. 2629 * @param {('XYX'|'XZX'|'YXY'|'YZY'|'ZXZ'|'ZYZ')} order - A string specifying the axes order. 2630 */ 2631function setQuaternionFromProperEuler( q, a, b, c, order ) { 2632 2633 const cos = Math.cos; 2634 const sin = Math.sin; 2635 2636 const c2 = cos( b / 2 ); 2637 const s2 = sin( b / 2 ); 2638 2639 const c13 = cos( ( a + c ) / 2 ); 2640 const s13 = sin( ( a + c ) / 2 ); 2641 2642 const c1_3 = cos( ( a - c ) / 2 ); 2643 const s1_3 = sin( ( a - c ) / 2 ); 2644 2645 const c3_1 = cos( ( c - a ) / 2 ); 2646 const s3_1 = sin( ( c - a ) / 2 ); 2647 2648 switch ( order ) { 2649 2650 case 'XYX': 2651 q.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 ); 2652 break; 2653 2654 case 'YZY': 2655 q.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 ); 2656 break; 2657 2658 case 'ZXZ': 2659 q.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 ); 2660 break; 2661 2662 case 'XZX': 2663 q.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 ); 2664 break; 2665 2666 case 'YXY': 2667 q.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 ); 2668 break; 2669 2670 case 'ZYZ': 2671 q.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 ); 2672 break; 2673 2674 default: 2675 warn( 'MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order ); 2676 2677 } 2678 2679} 2680 2681/** 2682 * Denormalizes the given value according to the given typed array. 2683 * 2684 * @param {number} value - The value to denormalize. 2685 * @param {TypedArray} array - The typed array that defines the data type of the value. 2686 * @return {number} The denormalize (float) value in the range `[0,1]`. 2687 */ 2688function denormalize( value, array ) { 2689 2690 switch ( array.constructor ) { 2691 2692 case Float32Array: 2693 2694 return value; 2695 2696 case Uint32Array: 2697 2698 return value / 4294967295.0; 2699 2700 case Uint16Array: 2701 2702 return value / 65535.0; 2703 2704 case Uint8Array: 2705 case Uint8ClampedArray: 2706 2707 return value / 255.0; 2708 2709 case Int32Array: 2710 2711 return Math.max( value / 2147483647.0, -1 ); 2712 2713 case Int16Array: 2714 2715 return Math.max( value / 32767.0, -1 ); 2716 2717 case Int8Array: 2718 2719 return Math.max( value / 127.0, -1 ); 2720 2721 default: 2722 2723 throw new Error( 'THREE.MathUtils: Invalid component type.' ); 2724 2725 } 2726 2727} 2728 2729/** 2730 * Normalizes the given value according to the given typed array. 2731 * 2732 * @param {number} value - The float value in the range `[0,1]` to normalize. 2733 * @param {TypedArray} array - The typed array that defines the data type of the value. 2734 * @return {number} The normalize value. 2735 */ 2736function normalize( value, array ) { 2737 2738 switch ( array.constructor ) { 2739 2740 case Float32Array: 2741 2742 return value; 2743 2744 case Uint32Array: 2745 2746 return Math.round( value * 4294967295.0 ); 2747 2748 case Uint16Array: 2749 2750 return Math.round( value * 65535.0 ); 2751 2752 case Uint8Array: 2753 case Uint8ClampedArray: 2754 2755 return Math.round( value * 255.0 ); 2756 2757 case Int32Array: 2758 2759 return Math.round( value * 2147483647.0 ); 2760 2761 case Int16Array: 2762 2763 return Math.round( value * 32767.0 ); 2764 2765 case Int8Array: 2766 2767 return Math.round( value * 127.0 ); 2768 2769 default: 2770 2771 throw new Error( 'THREE.MathUtils: Invalid component type.' ); 2772 2773 } 2774 2775} 2776 2777/** 2778 * @class 2779 * @classdesc A collection of math utility functions. 2780 * @hideconstructor 2781 */ 2782const MathUtils = { 2783 DEG2RAD: DEG2RAD, 2784 RAD2DEG: RAD2DEG, 2785 /** 2786 * Generate a [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier) 2787 * (universally unique identifier). 2788 * 2789 * @static 2790 * @method 2791 * @return {string} The UUID. 2792 */ 2793 generateUUID: generateUUID, 2794 /** 2795 * Clamps the given value between min and max. 2796 * 2797 * @static 2798 * @method 2799 * @param {number} value - The value to clamp. 2800 * @param {number} min - The min value. 2801 * @param {number} max - The max value. 2802 * @return {number} The clamped value. 2803 */ 2804 clamp: clamp, 2805 /** 2806 * Computes the Euclidean modulo of the given parameters that 2807 * is `( ( n % m ) + m ) % m`. 2808 * 2809 * @static 2810 * @method 2811 * @param {number} n - The first parameter. 2812 * @param {number} m - The second parameter. 2813 * @return {number} The Euclidean modulo. 2814 */ 2815 euclideanModulo: euclideanModulo, 2816 /** 2817 * Performs a linear mapping from range `<a1, a2>` to range `<b1, b2>` 2818 * for the given value. 2819 * 2820 * @static 2821 * @method 2822 * @param {number} x - The value to be mapped. 2823 * @param {number} a1 - Minimum value for range A. 2824 * @param {number} a2 - Maximum value for range A. 2825 * @param {number} b1 - Minimum value for range B. 2826 * @param {number} b2 - Maximum value for range B. 2827 * @return {number} The mapped value. 2828 */ 2829 mapLinear: mapLinear, 2830 /** 2831 * Returns the percentage in the closed interval `[0, 1]` of the given value 2832 * between the start and end point. 2833 * 2834 * @static 2835 * @method 2836 * @param {number} x - The start point 2837 * @param {number} y - The end point. 2838 * @param {number} value - A value between start and end. 2839 * @return {number} The interpolation factor. 2840 */ 2841 inverseLerp: inverseLerp, 2842 /** 2843 * Returns a value linearly interpolated from two known points based on the given interval - 2844 * `t = 0` will return `x` and `t = 1` will return `y`. 2845 * 2846 * @static 2847 * @method 2848 * @param {number} x - The start point 2849 * @param {number} y - The end point. 2850 * @param {number} t - The interpolation factor in the closed interval `[0, 1]`. 2851 * @return {number} The interpolated value. 2852 */ 2853 lerp: lerp, 2854 /** 2855 * Smoothly interpolate a number from `x` to `y` in a spring-like manner using a delta 2856 * time to maintain frame rate independent movement. For details, see 2857 * [Frame rate independent damping using lerp](http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/). 2858 * 2859 * @static 2860 * @method 2861 * @param {number} x - The current point. 2862 * @param {number} y - The target point. 2863 * @param {number} lambda - A higher lambda value will make the movement more sudden, 2864 * and a lower value will make the movement more gradual. 2865 * @param {number} dt - Delta time in seconds. 2866 * @return {number} The interpolated value. 2867 */ 2868 damp: damp, 2869 /** 2870 * Returns a value that alternates between `0` and the given `length` parameter. 2871 * 2872 * @static 2873 * @method 2874 * @param {number} x - The value to pingpong. 2875 * @param {number} [length=1] - The positive value the function will pingpong to. 2876 * @return {number} The alternated value. 2877 */ 2878 pingpong: pingpong, 2879 /** 2880 * Returns a value in the range `[0,1]` that represents the percentage that `x` has 2881 * moved between `min` and `max`, but smoothed or slowed down the closer `x` is to 2882 * the `min` and `max`. 2883 * 2884 * See [Smoothstep](http://en.wikipedia.org/wiki/Smoothstep) for more details. 2885 * 2886 * @static 2887 * @method 2888 * @param {number} x - The value to evaluate based on its position between min and max. 2889 * @param {number} min - The min value. Any x value below min will be `0`. 2890 * @param {number} max - The max value. Any x value above max will be `1`. 2891 * @return {number} The alternated value. 2892 */ 2893 smoothstep: smoothstep, 2894 /** 2895 * A [variation on smoothstep](https://en.wikipedia.org/wiki/Smoothstep#Variations) 2896 * that has zero 1st and 2nd order derivatives at x=0 and x=1. 2897 * 2898 * @static 2899 * @method 2900 * @param {number} x - The value to evaluate based on its position between min and max. 2901 * @param {number} min - The min value. Any x value below min will be `0`. 2902 * @param {number} max - The max value. Any x value above max will be `1`. 2903 * @return {number} The alternated value. 2904 */ 2905 smootherstep: smootherstep, 2906 /** 2907 * Returns a random integer from `<low, high>` interval. 2908 * 2909 * @static 2910 * @method 2911 * @param {number} low - The lower value boundary. 2912 * @param {number} high - The upper value boundary 2913 * @return {number} A random integer. 2914 */ 2915 randInt: randInt, 2916 /** 2917 * Returns a random float from `<low, high>` interval. 2918 * 2919 * @static 2920 * @method 2921 * @param {number} low - The lower value boundary. 2922 * @param {number} high - The upper value boundary 2923 * @return {number} A random float. 2924 */ 2925 randFloat: randFloat, 2926 /** 2927 * Returns a random integer from `<-range/2, range/2>` interval. 2928 * 2929 * @static 2930 * @method 2931 * @param {number} range - Defines the value range. 2932 * @return {number} A random float. 2933 */ 2934 randFloatSpread: randFloatSpread, 2935 /** 2936 * Returns a deterministic pseudo-random float in the interval `[0, 1]`. 2937 * 2938 * @static 2939 * @method 2940 * @param {number} [s] - The integer seed. 2941 * @return {number} A random float. 2942 */ 2943 seededRandom: seededRandom, 2944 /** 2945 * Converts degrees to radians. 2946 * 2947 * @static 2948 * @method 2949 * @param {number} degrees - A value in degrees. 2950 * @return {number} The converted value in radians. 2951 */ 2952 degToRad: degToRad, 2953 /** 2954 * Converts radians to degrees. 2955 * 2956 * @static 2957 * @method 2958 * @param {number} radians - A value in radians. 2959 * @return {number} The converted value in degrees. 2960 */ 2961 radToDeg: radToDeg, 2962 /** 2963 * Returns `true` if the given number is a power of two. 2964 * 2965 * @static 2966 * @method 2967 * @param {number} value - The value to check. 2968 * @return {boolean} Whether the given number is a power of two or not. 2969 */ 2970 isPowerOfTwo: isPowerOfTwo, 2971 /** 2972 * Returns the smallest power of two that is greater than or equal to the given number. 2973 * 2974 * @static 2975 * @method 2976 * @param {number} value - The value to find a POT for. 2977 * @return {number} The smallest power of two that is greater than or equal to the given number. 2978 */ 2979 ceilPowerOfTwo: ceilPowerOfTwo, 2980 /** 2981 * Returns the largest power of two that is less than or equal to the given number. 2982 * 2983 * @static 2984 * @method 2985 * @param {number} value - The value to find a POT for. 2986 * @return {number} The largest power of two that is less than or equal to the given number. 2987 */ 2988 floorPowerOfTwo: floorPowerOfTwo, 2989 /** 2990 * Sets the given quaternion from the [Intrinsic Proper Euler Angles](https://en.wikipedia.org/wiki/Euler_angles) 2991 * defined by the given angles and order. 2992 * 2993 * Rotations are applied to the axes in the order specified by order: 2994 * rotation by angle `a` is applied first, then by angle `b`, then by angle `c`. 2995 * 2996 * @static 2997 * @method 2998 * @param {Quaternion} q - The quaternion to set. 2999 * @param {number} a - The rotation applied to the first axis, in radians. 3000 * @param {number} b - The rotation applied to the second axis, in radians. 3001 * @param {number} c - The rotation applied to the third axis, in radians. 3002 * @param {('XYX'|'XZX'|'YXY'|'YZY'|'ZXZ'|'ZYZ')} order - A string specifying the axes order. 3003 */ 3004 setQuaternionFromProperEuler: setQuaternionFromProperEuler, 3005 /** 3006 * Normalizes the given value according to the given typed array. 3007 * 3008 * @static 3009 * @method 3010 * @param {number} value - The float value in the range `[0,1]` to normalize. 3011 * @param {TypedArray} array - The typed array that defines the data type of the value. 3012 * @return {number} The normalize value. 3013 */ 3014 normalize: normalize, 3015 /** 3016 * Denormalizes the given value according to the given typed array. 3017 * 3018 * @static 3019 * @method 3020 * @param {number} value - The value to denormalize. 3021 * @param {TypedArray} array - The typed array that defines the data type of the value. 3022 * @return {number} The denormalize (float) value in the range `[0,1]`. 3023 */ 3024 denormalize: denormalize 3025}; 3026 3027/** 3028 * Class representing a 2D vector. A 2D vector is an ordered pair of numbers 3029 * (labeled x and y), which can be used to represent a number of things, such as: 3030 * 3031 * - A point in 2D space (i.e. a position on a plane). 3032 * - A direction and length across a plane. In three.js the length will 3033 * always be the Euclidean distance(straight-line distance) from `(0, 0)` to `(x, y)` 3034 * and the direction is also measured from `(0, 0)` towards `(x, y)`. 3035 * - Any arbitrary ordered pair of numbers. 3036 * 3037 * There are other things a 2D vector can be used to represent, such as 3038 * momentum vectors, complex numbers and so on, however these are the most 3039 * common uses in three.js. 3040 * 3041 * Iterating through a vector instance will yield its components `(x, y)` in 3042 * the corresponding order. 3043 * ```js 3044 * const a = new THREE.Vector2( 0, 1 ); 3045 * 3046 * //no arguments; will be initialised to (0, 0) 3047 * const b = new THREE.Vector2( ); 3048 * 3049 * const d = a.distanceTo( b ); 3050 * ``` 3051 */ 3052class Vector2 { 3053 3054 static { 3055 3056 /** 3057 * This flag can be used for type testing. 3058 * 3059 * @type {boolean} 3060 * @readonly 3061 * @default true 3062 */ 3063 Vector2.prototype.isVector2 = true; 3064 3065 } 3066 3067 /** 3068 * Constructs a new 2D vector. 3069 * 3070 * @param {number} [x=0] - The x value of this vector. 3071 * @param {number} [y=0] - The y value of this vector. 3072 */ 3073 constructor( x = 0, y = 0 ) { 3074 3075 /** 3076 * The x value of this vector. 3077 * 3078 * @type {number} 3079 */ 3080 this.x = x; 3081 3082 /** 3083 * The y value of this vector. 3084 * 3085 * @type {number} 3086 */ 3087 this.y = y; 3088 3089 } 3090 3091 /** 3092 * Alias for {@link Vector2#x}. 3093 * 3094 * @type {number} 3095 */ 3096 get width() { 3097 3098 return this.x; 3099 3100 } 3101 3102 set width( value ) { 3103 3104 this.x = value; 3105 3106 } 3107 3108 /** 3109 * Alias for {@link Vector2#y}. 3110 * 3111 * @type {number} 3112 */ 3113 get height() { 3114 3115 return this.y; 3116 3117 } 3118 3119 set height( value ) { 3120 3121 this.y = value; 3122 3123 } 3124 3125 /** 3126 * Sets the vector components. 3127 * 3128 * @param {number} x - The value of the x component. 3129 * @param {number} y - The value of the y component. 3130 * @return {Vector2} A reference to this vector. 3131 */ 3132 set( x, y ) { 3133 3134 this.x = x; 3135 this.y = y; 3136 3137 return this; 3138 3139 } 3140 3141 /** 3142 * Sets the vector components to the same value. 3143 * 3144 * @param {number} scalar - The value to set for all vector components. 3145 * @return {Vector2} A reference to this vector. 3146 */ 3147 setScalar( scalar ) { 3148 3149 this.x = scalar; 3150 this.y = scalar; 3151 3152 return this; 3153 3154 } 3155 3156 /** 3157 * Sets the vector's x component to the given value 3158 * 3159 * @param {number} x - The value to set. 3160 * @return {Vector2} A reference to this vector. 3161 */ 3162 setX( x ) { 3163 3164 this.x = x; 3165 3166 return this; 3167 3168 } 3169 3170 /** 3171 * Sets the vector's y component to the given value 3172 * 3173 * @param {number} y - The value to set. 3174 * @return {Vector2} A reference to this vector. 3175 */ 3176 setY( y ) { 3177 3178 this.y = y; 3179 3180 return this; 3181 3182 } 3183 3184 /** 3185 * Allows to set a vector component with an index. 3186 * 3187 * @param {number} index - The component index. `0` equals to x, `1` equals to y. 3188 * @param {number} value - The value to set. 3189 * @return {Vector2} A reference to this vector. 3190 */ 3191 setComponent( index, value ) { 3192 3193 switch ( index ) { 3194 3195 case 0: this.x = value; break; 3196 case 1: this.y = value; break; 3197 default: throw new Error( 'THREE.Vector2: index is out of range: ' + index ); 3198 3199 } 3200 3201 return this; 3202 3203 } 3204 3205 /** 3206 * Returns the value of the vector component which matches the given index. 3207 * 3208 * @param {number} index - The component index. `0` equals to x, `1` equals to y. 3209 * @return {number} A vector component value. 3210 */ 3211 getComponent( index ) { 3212 3213 switch ( index ) { 3214 3215 case 0: return this.x; 3216 case 1: return this.y; 3217 default: throw new Error( 'THREE.Vector2: index is out of range: ' + index ); 3218 3219 } 3220 3221 } 3222 3223 /** 3224 * Returns a new vector with copied values from this instance. 3225 * 3226 * @return {Vector2} A clone of this instance. 3227 */ 3228 clone() { 3229 3230 return new this.constructor( this.x, this.y ); 3231 3232 } 3233 3234 /** 3235 * Copies the values of the given vector to this instance. 3236 * 3237 * @param {Vector2} v - The vector to copy. 3238 * @return {Vector2} A reference to this vector. 3239 */ 3240 copy( v ) { 3241 3242 this.x = v.x; 3243 this.y = v.y; 3244 3245 return this; 3246 3247 } 3248 3249 /** 3250 * Adds the given vector to this instance. 3251 * 3252 * @param {Vector2} v - The vector to add. 3253 * @return {Vector2} A reference to this vector. 3254 */ 3255 add( v ) { 3256 3257 this.x += v.x; 3258 this.y += v.y; 3259 3260 return this; 3261 3262 } 3263 3264 /** 3265 * Adds the given scalar value to all components of this instance. 3266 * 3267 * @param {number} s - The scalar to add. 3268 * @return {Vector2} A reference to this vector. 3269 */ 3270 addScalar( s ) { 3271 3272 this.x += s; 3273 this.y += s; 3274 3275 return this; 3276 3277 } 3278 3279 /** 3280 * Adds the given vectors and stores the result in this instance. 3281 * 3282 * @param {Vector2} a - The first vector. 3283 * @param {Vector2} b - The second vector. 3284 * @return {Vector2} A reference to this vector. 3285 */ 3286 addVectors( a, b ) { 3287 3288 this.x = a.x + b.x; 3289 this.y = a.y + b.y; 3290 3291 return this; 3292 3293 } 3294 3295 /** 3296 * Adds the given vector scaled by the given factor to this instance. 3297 * 3298 * @param {Vector2} v - The vector. 3299 * @param {number} s - The factor that scales `v`. 3300 * @return {Vector2} A reference to this vector. 3301 */ 3302 addScaledVector( v, s ) { 3303 3304 this.x += v.x * s; 3305 this.y += v.y * s; 3306 3307 return this; 3308 3309 } 3310 3311 /** 3312 * Subtracts the given vector from this instance. 3313 * 3314 * @param {Vector2} v - The vector to subtract. 3315 * @return {Vector2} A reference to this vector. 3316 */ 3317 sub( v ) { 3318 3319 this.x -= v.x; 3320 this.y -= v.y; 3321 3322 return this; 3323 3324 } 3325 3326 /** 3327 * Subtracts the given scalar value from all components of this instance. 3328 * 3329 * @param {number} s - The scalar to subtract. 3330 * @return {Vector2} A reference to this vector. 3331 */ 3332 subScalar( s ) { 3333 3334 this.x -= s; 3335 this.y -= s; 3336 3337 return this; 3338 3339 } 3340 3341 /** 3342 * Subtracts the given vectors and stores the result in this instance. 3343 * 3344 * @param {Vector2} a - The first vector. 3345 * @param {Vector2} b - The second vector. 3346 * @return {Vector2} A reference to this vector. 3347 */ 3348 subVectors( a, b ) { 3349 3350 this.x = a.x - b.x; 3351 this.y = a.y - b.y; 3352 3353 return this; 3354 3355 } 3356 3357 /** 3358 * Multiplies the given vector with this instance. 3359 * 3360 * @param {Vector2} v - The vector to multiply. 3361 * @return {Vector2} A reference to this vector. 3362 */ 3363 multiply( v ) { 3364 3365 this.x *= v.x; 3366 this.y *= v.y; 3367 3368 return this; 3369 3370 } 3371 3372 /** 3373 * Multiplies the given scalar value with all components of this instance. 3374 * 3375 * @param {number} scalar - The scalar to multiply. 3376 * @return {Vector2} A reference to this vector. 3377 */ 3378 multiplyScalar( scalar ) { 3379 3380 this.x *= scalar; 3381 this.y *= scalar; 3382 3383 return this; 3384 3385 } 3386 3387 /** 3388 * Divides this instance by the given vector. 3389 * 3390 * @param {Vector2} v - The vector to divide. 3391 * @return {Vector2} A reference to this vector. 3392 */ 3393 divide( v ) { 3394 3395 this.x /= v.x; 3396 this.y /= v.y; 3397 3398 return this; 3399 3400 } 3401 3402 /** 3403 * Divides this vector by the given scalar. 3404 * 3405 * @param {number} scalar - The scalar to divide. 3406 * @return {Vector2} A reference to this vector. 3407 */ 3408 divideScalar( scalar ) { 3409 3410 return this.multiplyScalar( 1 / scalar ); 3411 3412 } 3413 3414 /** 3415 * Multiplies this vector (with an implicit 1 as the 3rd component) by 3416 * the given 3x3 matrix. 3417 * 3418 * @param {Matrix3} m - The matrix to apply. 3419 * @return {Vector2} A reference to this vector. 3420 */ 3421 applyMatrix3( m ) { 3422 3423 const x = this.x, y = this.y; 3424 const e = m.elements; 3425 3426 this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ]; 3427 this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ]; 3428 3429 return this; 3430 3431 } 3432 3433 /** 3434 * If this vector's x or y value is greater than the given vector's x or y 3435 * value, replace that value with the corresponding min value. 3436 * 3437 * @param {Vector2} v - The vector. 3438 * @return {Vector2} A reference to this vector. 3439 */ 3440 min( v ) { 3441 3442 this.x = Math.min( this.x, v.x ); 3443 this.y = Math.min( this.y, v.y ); 3444 3445 return this; 3446 3447 } 3448 3449 /** 3450 * If this vector's x or y value is less than the given vector's x or y 3451 * value, replace that value with the corresponding max value. 3452 * 3453 * @param {Vector2} v - The vector. 3454 * @return {Vector2} A reference to this vector. 3455 */ 3456 max( v ) { 3457 3458 this.x = Math.max( this.x, v.x ); 3459 this.y = Math.max( this.y, v.y ); 3460 3461 return this; 3462 3463 } 3464 3465 /** 3466 * If this vector's x or y value is greater than the max vector's x or y 3467 * value, it is replaced by the corresponding value. 3468 * If this vector's x or y value is less than the min vector's x or y value, 3469 * it is replaced by the corresponding value. 3470 * 3471 * @param {Vector2} min - The minimum x and y values. 3472 * @param {Vector2} max - The maximum x and y values in the desired range. 3473 * @return {Vector2} A reference to this vector. 3474 */ 3475 clamp( min, max ) { 3476 3477 // assumes min < max, componentwise 3478 3479 this.x = clamp( this.x, min.x, max.x ); 3480 this.y = clamp( this.y, min.y, max.y ); 3481 3482 return this; 3483 3484 } 3485 3486 /** 3487 * If this vector's x or y values are greater than the max value, they are 3488 * replaced by the max value. 3489 * If this vector's x or y values are less than the min value, they are 3490 * replaced by the min value. 3491 * 3492 * @param {number} minVal - The minimum value the components will be clamped to. 3493 * @param {number} maxVal - The maximum value the components will be clamped to. 3494 * @return {Vector2} A reference to this vector. 3495 */ 3496 clampScalar( minVal, maxVal ) { 3497 3498 this.x = clamp( this.x, minVal, maxVal ); 3499 this.y = clamp( this.y, minVal, maxVal ); 3500 3501 return this; 3502 3503 } 3504 3505 /** 3506 * If this vector's length is greater than the max value, it is replaced by 3507 * the max value. 3508 * If this vector's length is less than the min value, it is replaced by the 3509 * min value. 3510 * 3511 * @param {number} min - The minimum value the vector length will be clamped to. 3512 * @param {number} max - The maximum value the vector length will be clamped to. 3513 * @return {Vector2} A reference to this vector. 3514 */ 3515 clampLength( min, max ) { 3516 3517 const length = this.length(); 3518 3519 return this.divideScalar( length || 1 ).multiplyScalar( clamp( length, min, max ) ); 3520 3521 } 3522 3523 /** 3524 * The components of this vector are rounded down to the nearest integer value. 3525 * 3526 * @return {Vector2} A reference to this vector. 3527 */ 3528 floor() { 3529 3530 this.x = Math.floor( this.x ); 3531 this.y = Math.floor( this.y ); 3532 3533 return this; 3534 3535 } 3536 3537 /** 3538 * The components of this vector are rounded up to the nearest integer value. 3539 * 3540 * @return {Vector2} A reference to this vector. 3541 */ 3542 ceil() { 3543 3544 this.x = Math.ceil( this.x ); 3545 this.y = Math.ceil( this.y ); 3546 3547 return this; 3548 3549 } 3550 3551 /** 3552 * The components of this vector are rounded to the nearest integer value 3553 * 3554 * @return {Vector2} A reference to this vector. 3555 */ 3556 round() { 3557 3558 this.x = Math.round( this.x ); 3559 this.y = Math.round( this.y ); 3560 3561 return this; 3562 3563 } 3564 3565 /** 3566 * The components of this vector are rounded towards zero (up if negative, 3567 * down if positive) to an integer value. 3568 * 3569 * @return {Vector2} A reference to this vector. 3570 */ 3571 roundToZero() { 3572 3573 this.x = Math.trunc( this.x ); 3574 this.y = Math.trunc( this.y ); 3575 3576 return this; 3577 3578 } 3579 3580 /** 3581 * Inverts this vector - i.e. sets x = -x and y = -y. 3582 * 3583 * @return {Vector2} A reference to this vector. 3584 */ 3585 negate() { 3586 3587 this.x = - this.x; 3588 this.y = - this.y; 3589 3590 return this; 3591 3592 } 3593 3594 /** 3595 * Calculates the dot product of the given vector with this instance. 3596 * 3597 * @param {Vector2} v - The vector to compute the dot product with. 3598 * @return {number} The result of the dot product. 3599 */ 3600 dot( v ) { 3601 3602 return this.x * v.x + this.y * v.y; 3603 3604 } 3605 3606 /** 3607 * Calculates the cross product of the given vector with this instance. 3608 * 3609 * @param {Vector2} v - The vector to compute the cross product with. 3610 * @return {number} The result of the cross product. 3611 */ 3612 cross( v ) { 3613 3614 return this.x * v.y - this.y * v.x; 3615 3616 } 3617 3618 /** 3619 * Computes the square of the Euclidean length (straight-line length) from 3620 * (0, 0) to (x, y). If you are comparing the lengths of vectors, you should 3621 * compare the length squared instead as it is slightly more efficient to calculate. 3622 * 3623 * @return {number} The square length of this vector. 3624 */ 3625 lengthSq() { 3626 3627 return this.x * this.x + this.y * this.y; 3628 3629 } 3630 3631 /** 3632 * Computes the Euclidean length (straight-line length) from (0, 0) to (x, y). 3633 * 3634 * @return {number} The length of this vector. 3635 */ 3636 length() { 3637 3638 return Math.sqrt( this.x * this.x + this.y * this.y ); 3639 3640 } 3641 3642 /** 3643 * Computes the Manhattan length of this vector. 3644 * 3645 * @return {number} The length of this vector. 3646 */ 3647 manhattanLength() { 3648 3649 return Math.abs( this.x ) + Math.abs( this.y ); 3650 3651 } 3652 3653 /** 3654 * Converts this vector to a unit vector - that is, sets it equal to a vector 3655 * with the same direction as this one, but with a vector length of `1`. 3656 * 3657 * @return {Vector2} A reference to this vector. 3658 */ 3659 normalize() { 3660 3661 return this.divideScalar( this.length() || 1 ); 3662 3663 } 3664 3665 /** 3666 * Computes the angle in radians of this vector with respect to the positive x-axis. 3667 * 3668 * @return {number} The angle in radians. 3669 */ 3670 angle() { 3671 3672 const angle = Math.atan2( - this.y, - this.x ) + Math.PI; 3673 3674 return angle; 3675 3676 } 3677 3678 /** 3679 * Returns the angle between the given vector and this instance in radians. 3680 * 3681 * @param {Vector2} v - The vector to compute the angle with. 3682 * @return {number} The angle in radians. 3683 */ 3684 angleTo( v ) { 3685 3686 const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); 3687 3688 if ( denominator === 0 ) return Math.PI / 2; 3689 3690 const theta = this.dot( v ) / denominator; 3691 3692 // clamp, to handle numerical problems 3693 3694 return Math.acos( clamp( theta, -1, 1 ) ); 3695 3696 } 3697 3698 /** 3699 * Computes the distance from the given vector to this instance. 3700 * 3701 * @param {Vector2} v - The vector to compute the distance to. 3702 * @return {number} The distance. 3703 */ 3704 distanceTo( v ) { 3705 3706 return Math.sqrt( this.distanceToSquared( v ) ); 3707 3708 } 3709 3710 /** 3711 * Computes the squared distance from the given vector to this instance. 3712 * If you are just comparing the distance with another distance, you should compare 3713 * the distance squared instead as it is slightly more efficient to calculate. 3714 * 3715 * @param {Vector2} v - The vector to compute the squared distance to. 3716 * @return {number} The squared distance. 3717 */ 3718 distanceToSquared( v ) { 3719 3720 const dx = this.x - v.x, dy = this.y - v.y; 3721 return dx * dx + dy * dy; 3722 3723 } 3724 3725 /** 3726 * Computes the Manhattan distance from the given vector to this instance. 3727 * 3728 * @param {Vector2} v - The vector to compute the Manhattan distance to. 3729 * @return {number} The Manhattan distance. 3730 */ 3731 manhattanDistanceTo( v ) { 3732 3733 return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); 3734 3735 } 3736 3737 /** 3738 * Sets this vector to a vector with the same direction as this one, but 3739 * with the specified length. 3740 * 3741 * @param {number} length - The new length of this vector. 3742 * @return {Vector2} A reference to this vector. 3743 */ 3744 setLength( length ) { 3745 3746 return this.normalize().multiplyScalar( length ); 3747 3748 } 3749 3750 /** 3751 * Linearly interpolates between the given vector and this instance, where 3752 * alpha is the percent distance along the line - alpha = 0 will be this 3753 * vector, and alpha = 1 will be the given one. 3754 * 3755 * @param {Vector2} v - The vector to interpolate towards. 3756 * @param {number} alpha - The interpolation factor, typically in the closed interval `[0, 1]`. 3757 * @return {Vector2} A reference to this vector. 3758 */ 3759 lerp( v, alpha ) { 3760 3761 this.x += ( v.x - this.x ) * alpha; 3762 this.y += ( v.y - this.y ) * alpha; 3763 3764 return this; 3765 3766 } 3767 3768 /** 3769 * Linearly interpolates between the given vectors, where alpha is the percent 3770 * distance along the line - alpha = 0 will be first vector, and alpha = 1 will 3771 * be the second one. The result is stored in this instance. 3772 * 3773 * @param {Vector2} v1 - The first vector. 3774 * @param {Vector2} v2 - The second vector. 3775 * @param {number} alpha - The interpolation factor, typically in the closed interval `[0, 1]`. 3776 * @return {Vector2} A reference to this vector. 3777 */ 3778 lerpVectors( v1, v2, alpha ) { 3779 3780 this.x = v1.x + ( v2.x - v1.x ) * alpha; 3781 this.y = v1.y + ( v2.y - v1.y ) * alpha; 3782 3783 return this; 3784 3785 } 3786 3787 /** 3788 * Returns `true` if this vector is equal with the given one. 3789 * 3790 * @param {Vector2} v - The vector to test for equality. 3791 * @return {boolean} Whether this vector is equal with the given one. 3792 */ 3793 equals( v ) { 3794 3795 return ( ( v.x === this.x ) && ( v.y === this.y ) ); 3796 3797 } 3798 3799 /** 3800 * Sets this vector's x value to be `array[ offset ]` and y 3801 * value to be `array[ offset + 1 ]`. 3802 * 3803 * @param {Array<number>} array - An array holding the vector component values. 3804 * @param {number} [offset=0] - The offset into the array. 3805 * @return {Vector2} A reference to this vector. 3806 */ 3807 fromArray( array, offset = 0 ) { 3808 3809 this.x = array[ offset ]; 3810 this.y = array[ offset + 1 ]; 3811 3812 return this; 3813 3814 } 3815 3816 /** 3817 * Writes the components of this vector to the given array. If no array is provided, 3818 * the method returns a new instance. 3819 * 3820 * @param {Array<number>} [array=[]] - The target array holding the vector components. 3821 * @param {number} [offset=0] - Index of the first element in the array. 3822 * @return {Array<number>} The vector components. 3823 */ 3824 toArray( array = [], offset = 0 ) { 3825 3826 array[ offset ] = this.x; 3827 array[ offset + 1 ] = this.y; 3828 3829 return array; 3830 3831 } 3832 3833 /** 3834 * Sets the components of this vector from the given buffer attribute. 3835 * 3836 * @param {BufferAttribute} attribute - The buffer attribute holding vector data. 3837 * @param {number} index - The index into the attribute. 3838 * @return {Vector2} A reference to this vector. 3839 */ 3840 fromBufferAttribute( attribute, index ) { 3841 3842 this.x = attribute.getX( index ); 3843 this.y = attribute.getY( index ); 3844 3845 return this; 3846 3847 } 3848 3849 /** 3850 * Rotates this vector around the given center by the given angle. 3851 * 3852 * @param {Vector2} center - The point around which to rotate. 3853 * @param {number} angle - The angle to rotate, in radians. 3854 * @return {Vector2} A reference to this vector. 3855 */ 3856 rotateAround( center, angle ) { 3857 3858 const c = Math.cos( angle ), s = Math.sin( angle ); 3859 3860 const x = this.x - center.x; 3861 const y = this.y - center.y; 3862 3863 this.x = x * c - y * s + center.x; 3864 this.y = x * s + y * c + center.y; 3865 3866 return this; 3867 3868 } 3869 3870 /** 3871 * Sets each component of this vector to a pseudo-random value between `0` and 3872 * `1`, excluding `1`. 3873 * 3874 * @return {Vector2} A reference to this vector. 3875 */ 3876 random() { 3877 3878 this.x = Math.random(); 3879 this.y = Math.random(); 3880 3881 return this; 3882 3883 } 3884 3885 *[ Symbol.iterator ]() { 3886 3887 yield this.x; 3888 yield this.y; 3889 3890 } 3891 3892} 3893 3894/** 3895 * Class for representing a Quaternion. Quaternions are used in three.js to represent rotations. 3896 * 3897 * Iterating through a vector instance will yield its components `(x, y, z, w)` in 3898 * the corresponding order. 3899 * 3900 * Note that three.js expects Quaternions to be normalized. 3901 * ```js 3902 * const quaternion = new THREE.Quaternion(); 3903 * quaternion.setFromAxisAngle( new THREE.Vector3( 0, 1, 0 ), Math.PI / 2 ); 3904 * 3905 * const vector = new THREE.Vector3( 1, 0, 0 ); 3906 * vector.applyQuaternion( quaternion ); 3907 * ``` 3908 */ 3909class Quaternion { 3910 3911 /** 3912 * Constructs a new quaternion. 3913 * 3914 * @param {number} [x=0] - The x value of this quaternion. 3915 * @param {number} [y=0] - The y value of this quaternion. 3916 * @param {number} [z=0] - The z value of this quaternion. 3917 * @param {number} [w=1] - The w value of this quaternion. 3918 */ 3919 constructor( x = 0, y = 0, z = 0, w = 1 ) { 3920 3921 /** 3922 * This flag can be used for type testing. 3923 * 3924 * @type {boolean} 3925 * @readonly 3926 * @default true 3927 */ 3928 this.isQuaternion = true; 3929 3930 this._x = x; 3931 this._y = y; 3932 this._z = z; 3933 this._w = w; 3934 3935 } 3936 3937 /** 3938 * Interpolates between two quaternions via SLERP. This implementation assumes the 3939 * quaternion data are managed in flat arrays. 3940 * 3941 * @param {Array<number>} dst - The destination array. 3942 * @param {number} dstOffset - An offset into the destination array. 3943 * @param {Array<number>} src0 - The source array of the first quaternion. 3944 * @param {number} srcOffset0 - An offset into the first source array. 3945 * @param {Array<number>} src1 - The source array of the second quaternion. 3946 * @param {number} srcOffset1 - An offset into the second source array. 3947 * @param {number} t - The interpolation factor. A value in the range `[0,1]` will interpolate. A value outside the range `[0,1]` will extrapolate. 3948 * @see {@link Quaternion#slerp} 3949 */ 3950 static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { 3951 3952 let x0 = src0[ srcOffset0 + 0 ], 3953 y0 = src0[ srcOffset0 + 1 ], 3954 z0 = src0[ srcOffset0 + 2 ], 3955 w0 = src0[ srcOffset0 + 3 ]; 3956 3957 let x1 = src1[ srcOffset1 + 0 ], 3958 y1 = src1[ srcOffset1 + 1 ], 3959 z1 = src1[ srcOffset1 + 2 ], 3960 w1 = src1[ srcOffset1 + 3 ]; 3961 3962 if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { 3963 3964 let dot = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1; 3965 3966 if ( dot < 0 ) { 3967 3968 x1 = - x1; 3969 y1 = - y1; 3970 z1 = - z1; 3971 w1 = - w1; 3972 3973 dot = - dot; 3974 3975 } 3976 3977 let s = 1 - t; 3978 3979 if ( dot < 0.9995 ) { 3980 3981 // slerp 3982 3983 const theta = Math.acos( dot ); 3984 const sin = Math.sin( theta ); 3985 3986 s = Math.sin( s * theta ) / sin; 3987 t = Math.sin( t * theta ) / sin; 3988 3989 x0 = x0 * s + x1 * t; 3990 y0 = y0 * s + y1 * t; 3991 z0 = z0 * s + z1 * t; 3992 w0 = w0 * s + w1 * t; 3993 3994 } else { 3995 3996 // for small angles, lerp then normalize 3997 3998 x0 = x0 * s + x1 * t; 3999 y0 = y0 * s + y1 * t; 4000 z0 = z0 * s + z1 * t; 4001 w0 = w0 * s + w1 * t; 4002 4003 const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); 4004 4005 x0 *= f; 4006 y0 *= f; 4007 z0 *= f; 4008 w0 *= f; 4009 4010 } 4011 4012 } 4013 4014 dst[ dstOffset ] = x0; 4015 dst[ dstOffset + 1 ] = y0; 4016 dst[ dstOffset + 2 ] = z0; 4017 dst[ dstOffset + 3 ] = w0; 4018 4019 } 4020 4021 /** 4022 * Multiplies two quaternions. This implementation assumes the quaternion data are managed 4023 * in flat arrays. 4024 * 4025 * @param {Array<number>} dst - The destination array. 4026 * @param {number} dstOffset - An offset into the destination array. 4027 * @param {Array<number>} src0 - The source array of the first quaternion. 4028 * @param {number} srcOffset0 - An offset into the first source array. 4029 * @param {Array<number>} src1 - The source array of the second quaternion. 4030 * @param {number} srcOffset1 - An offset into the second source array. 4031 * @return {Array<number>} The destination array. 4032 * @see {@link Quaternion#multiplyQuaternions}. 4033 */ 4034 static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) { 4035 4036 const x0 = src0[ srcOffset0 ]; 4037 const y0 = src0[ srcOffset0 + 1 ]; 4038 const z0 = src0[ srcOffset0 + 2 ]; 4039 const w0 = src0[ srcOffset0 + 3 ]; 4040 4041 const x1 = src1[ srcOffset1 ]; 4042 const y1 = src1[ srcOffset1 + 1 ]; 4043 const z1 = src1[ srcOffset1 + 2 ]; 4044 const w1 = src1[ srcOffset1 + 3 ]; 4045 4046 dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; 4047 dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; 4048 dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; 4049 dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; 4050 4051 return dst; 4052 4053 } 4054 4055 /** 4056 * The x value of this quaternion. 4057 * 4058 * @type {number} 4059 * @default 0 4060 */ 4061 get x() { 4062 4063 return this._x; 4064 4065 } 4066 4067 set x( value ) { 4068 4069 this._x = value; 4070 this._onChangeCallback(); 4071 4072 } 4073 4074 /** 4075 * The y value of this quaternion. 4076 * 4077 * @type {number} 4078 * @default 0 4079 */ 4080 get y() { 4081 4082 return this._y; 4083 4084 } 4085 4086 set y( value ) { 4087 4088 this._y = value; 4089 this._onChangeCallback(); 4090 4091 } 4092 4093 /** 4094 * The z value of this quaternion. 4095 * 4096 * @type {number} 4097 * @default 0 4098 */ 4099 get z() { 4100 4101 return this._z; 4102 4103 } 4104 4105 set z( value ) { 4106 4107 this._z = value; 4108 this._onChangeCallback(); 4109 4110 } 4111 4112 /** 4113 * The w value of this quaternion. 4114 * 4115 * @type {number} 4116 * @default 1 4117 */ 4118 get w() { 4119 4120 return this._w; 4121 4122 } 4123 4124 set w( value ) { 4125 4126 this._w = value; 4127 this._onChangeCallback(); 4128 4129 } 4130 4131 /** 4132 * Sets the quaternion components. 4133 * 4134 * @param {number} x - The x value of this quaternion. 4135 * @param {number} y - The y value of this quaternion. 4136 * @param {number} z - The z value of this quaternion. 4137 * @param {number} w - The w value of this quaternion. 4138 * @return {Quaternion} A reference to this quaternion. 4139 */ 4140 set( x, y, z, w ) { 4141 4142 this._x = x; 4143 this._y = y; 4144 this._z = z; 4145 this._w = w; 4146 4147 this._onChangeCallback(); 4148 4149 return this; 4150 4151 } 4152 4153 /** 4154 * Returns a new quaternion with copied values from this instance. 4155 * 4156 * @return {Quaternion} A clone of this instance. 4157 */ 4158 clone() { 4159 4160 return new this.constructor( this._x, this._y, this._z, this._w ); 4161 4162 } 4163 4164 /** 4165 * Copies the values of the given quaternion to this instance. 4166 * 4167 * @param {Quaternion} quaternion - The quaternion to copy. 4168 * @return {Quaternion} A reference to this quaternion. 4169 */ 4170 copy( quaternion ) { 4171 4172 this._x = quaternion.x; 4173 this._y = quaternion.y; 4174 this._z = quaternion.z; 4175 this._w = quaternion.w; 4176 4177 this._onChangeCallback(); 4178 4179 return this; 4180 4181 } 4182 4183 /** 4184 * Sets this quaternion from the rotation specified by the given 4185 * Euler angles. 4186 * 4187 * @param {Euler} euler - The Euler angles. 4188 * @param {boolean} [update=true] - Whether the internal `onChange` callback should be executed or not. 4189 * @return {Quaternion} A reference to this quaternion. 4190 */ 4191 setFromEuler( euler, update = true ) { 4192 4193 const x = euler._x, y = euler._y, z = euler._z, order = euler._order; 4194 4195 const cos = Math.cos; 4196 const sin = Math.sin; 4197 4198 const c1 = cos( x / 2 ); 4199 const c2 = cos( y / 2 ); 4200 const c3 = cos( z / 2 ); 4201 4202 const s1 = sin( x / 2 ); 4203 const s2 = sin( y / 2 ); 4204 const s3 = sin( z / 2 ); 4205 4206 switch ( order ) { 4207 4208 case 'XYZ': 4209 this._x = s1 * c2 * c3 + c1 * s2 * s3; 4210 this._y = c1 * s2 * c3 - s1 * c2 * s3; 4211 this._z = c1 * c2 * s3 + s1 * s2 * c3; 4212 this._w = c1 * c2 * c3 - s1 * s2 * s3; 4213 break; 4214 4215 case 'YXZ': 4216 this._x = s1 * c2 * c3 + c1 * s2 * s3; 4217 this._y = c1 * s2 * c3 - s1 * c2 * s3; 4218 this._z = c1 * c2 * s3 - s1 * s2 * c3; 4219 this._w = c1 * c2 * c3 + s1 * s2 * s3; 4220 break; 4221 4222 case 'ZXY': 4223 this._x = s1 * c2 * c3 - c1 * s2 * s3; 4224 this._y = c1 * s2 * c3 + s1 * c2 * s3; 4225 this._z = c1 * c2 * s3 + s1 * s2 * c3; 4226 this._w = c1 * c2 * c3 - s1 * s2 * s3; 4227 break; 4228 4229 case 'ZYX': 4230 this._x = s1 * c2 * c3 - c1 * s2 * s3; 4231 this._y = c1 * s2 * c3 + s1 * c2 * s3; 4232 this._z = c1 * c2 * s3 - s1 * s2 * c3; 4233 this._w = c1 * c2 * c3 + s1 * s2 * s3; 4234 break; 4235 4236 case 'YZX': 4237 this._x = s1 * c2 * c3 + c1 * s2 * s3; 4238 this._y = c1 * s2 * c3 + s1 * c2 * s3; 4239 this._z = c1 * c2 * s3 - s1 * s2 * c3; 4240 this._w = c1 * c2 * c3 - s1 * s2 * s3; 4241 break; 4242 4243 case 'XZY': 4244 this._x = s1 * c2 * c3 - c1 * s2 * s3; 4245 this._y = c1 * s2 * c3 - s1 * c2 * s3; 4246 this._z = c1 * c2 * s3 + s1 * s2 * c3; 4247 this._w = c1 * c2 * c3 + s1 * s2 * s3; 4248 break; 4249 4250 default: 4251 warn( 'Quaternion: .setFromEuler() encountered an unknown order: ' + order ); 4252 4253 } 4254 4255 if ( update === true ) this._onChangeCallback(); 4256 4257 return this; 4258 4259 } 4260 4261 /** 4262 * Sets this quaternion from the given axis and angle. 4263 * 4264 * @param {Vector3} axis - The normalized axis. 4265 * @param {number} angle - The angle in radians. 4266 * @return {Quaternion} A reference to this quaternion. 4267 */ 4268 setFromAxisAngle( axis, angle ) { 4269 4270 const halfAngle = angle / 2, s = Math.sin( halfAngle ); 4271 4272 this._x = axis.x * s; 4273 this._y = axis.y * s; 4274 this._z = axis.z * s; 4275 this._w = Math.cos( halfAngle ); 4276 4277 this._onChangeCallback(); 4278 4279 return this; 4280 4281 } 4282 4283 /** 4284 * Sets this quaternion from the given rotation matrix. 4285 * 4286 * @param {Matrix4} m - A 4x4 matrix of which the upper 3x3 of matrix is a pure rotation matrix (i.e. unscaled). 4287 * @return {Quaternion} A reference to this quaternion. 4288 */ 4289 setFromRotationMatrix( m ) { 4290 4291 // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) 4292 4293 const te = m.elements, 4294 4295 m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], 4296 m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], 4297 m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], 4298 4299 trace = m11 + m22 + m33; 4300 4301 if ( trace > 0 ) { 4302 4303 const s = 0.5 / Math.sqrt( trace + 1.0 ); 4304 4305 this._w = 0.25 / s; 4306 this._x = ( m32 - m23 ) * s; 4307 this._y = ( m13 - m31 ) * s; 4308 this._z = ( m21 - m12 ) * s; 4309 4310 } else if ( m11 > m22 && m11 > m33 ) { 4311 4312 const s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); 4313 4314 this._w = ( m32 - m23 ) / s; 4315 this._x = 0.25 * s; 4316 this._y = ( m12 + m21 ) / s; 4317 this._z = ( m13 + m31 ) / s; 4318 4319 } else if ( m22 > m33 ) { 4320 4321 const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); 4322 4323 this._w = ( m13 - m31 ) / s; 4324 this._x = ( m12 + m21 ) / s; 4325 this._y = 0.25 * s; 4326 this._z = ( m23 + m32 ) / s; 4327 4328 } else { 4329 4330 const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); 4331 4332 this._w = ( m21 - m12 ) / s; 4333 this._x = ( m13 + m31 ) / s; 4334 this._y = ( m23 + m32 ) / s; 4335 this._z = 0.25 * s; 4336 4337 } 4338 4339 this._onChangeCallback(); 4340 4341 return this; 4342 4343 } 4344 4345 /** 4346 * Sets this quaternion to the rotation required to rotate the direction vector 4347 * `vFrom` to the direction vector `vTo`. 4348 * 4349 * @param {Vector3} vFrom - The first (normalized) direction vector. 4350 * @param {Vector3} vTo - The second (normalized) direction vector. 4351 * @return {Quaternion} A reference to this quaternion. 4352 */ 4353 setFromUnitVectors( vFrom, vTo ) { 4354 4355 // assumes direction vectors vFrom and vTo are normalized 4356 4357 let r = vFrom.dot( vTo ) + 1; 4358 4359 if ( r < 1e-8 ) { // the epsilon value has been discussed in #31286 4360 4361 // vFrom and vTo point in opposite directions 4362 4363 r = 0; 4364 4365 if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { 4366 4367 this._x = - vFrom.y; 4368 this._y = vFrom.x; 4369 this._z = 0; 4370 this._w = r; 4371 4372 } else { 4373 4374 this._x = 0; 4375 this._y = - vFrom.z; 4376 this._z = vFrom.y; 4377 this._w = r; 4378 4379 } 4380 4381 } else { 4382 4383 // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 4384 4385 this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; 4386 this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; 4387 this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; 4388 this._w = r; 4389 4390 } 4391 4392 return this.normalize(); 4393 4394 } 4395 4396 /** 4397 * Returns the angle between this quaternion and the given one in radians. 4398 * 4399 * @param {Quaternion} q - The quaternion to compute the angle with. 4400 * @return {number} The angle in radians. 4401 */ 4402 angleTo( q ) { 4403 4404 return 2 * Math.acos( Math.abs( clamp( this.dot( q ), -1, 1 ) ) ); 4405 4406 } 4407 4408 /** 4409 * Rotates this quaternion by a given angular step to the given quaternion. 4410 * The method ensures that the final quaternion will not overshoot `q`. 4411 * 4412 * @param {Quaternion} q - The target quaternion. 4413 * @param {number} step - The angular step in radians. 4414 * @return {Quaternion} A reference to this quaternion. 4415 */ 4416 rotateTowards( q, step ) { 4417 4418 const angle = this.angleTo( q ); 4419 4420 if ( angle === 0 ) return this; 4421 4422 const t = Math.min( 1, step / angle ); 4423 4424 this.slerp( q, t ); 4425 4426 return this; 4427 4428 } 4429 4430 /** 4431 * Sets this quaternion to the identity quaternion; that is, to the 4432 * quaternion that represents "no rotation". 4433 * 4434 * @return {Quaternion} A reference to this quaternion. 4435 */ 4436 identity() { 4437 4438 return this.set( 0, 0, 0, 1 ); 4439 4440 } 4441 4442 /** 4443 * Inverts this quaternion via {@link Quaternion#conjugate}. The 4444 * quaternion is assumed to have unit length. 4445 * 4446 * @return {Quaternion} A reference to this quaternion. 4447 */ 4448 invert() { 4449 4450 return this.conjugate(); 4451 4452 } 4453 4454 /** 4455 * Returns the rotational conjugate of this quaternion. The conjugate of a 4456 * quaternion represents the same rotation in the opposite direction about 4457 * the rotational axis. 4458 * 4459 * @return {Quaternion} A reference to this quaternion. 4460 */ 4461 conjugate() { 4462 4463 this._x *= -1; 4464 this._y *= -1; 4465 this._z *= -1; 4466 4467 this._onChangeCallback(); 4468 4469 return this; 4470 4471 } 4472 4473 /** 4474 * Calculates the dot product of this quaternion and the given one. 4475 * 4476 * @param {Quaternion} v - The quaternion to compute the dot product with. 4477 * @return {number} The result of the dot product. 4478 */ 4479 dot( v ) { 4480 4481 return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; 4482 4483 } 4484 4485 /** 4486 * Computes the squared Euclidean length (straight-line length) of this quaternion, 4487 * considered as a 4 dimensional vector. This can be useful if you are comparing the 4488 * lengths of two quaternions, as this is a slightly more efficient calculation than 4489 * {@link Quaternion#length}. 4490 * 4491 * @return {number} The squared Euclidean length. 4492 */ 4493 lengthSq() { 4494 4495 return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; 4496 4497 } 4498 4499 /** 4500 * Computes the Euclidean length (straight-line length) of this quaternion, 4501 * considered as a 4 dimensional vector. 4502 * 4503 * @return {number} The Euclidean length. 4504 */ 4505 length() { 4506 4507 return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); 4508 4509 } 4510 4511 /** 4512 * Normalizes this quaternion - that is, calculated the quaternion that performs 4513 * the same rotation as this one, but has a length equal to `1`. 4514 * 4515 * @return {Quaternion} A reference to this quaternion. 4516 */ 4517 normalize() { 4518 4519 let l = this.length(); 4520 4521 if ( l === 0 ) { 4522 4523 this._x = 0; 4524 this._y = 0; 4525 this._z = 0; 4526 this._w = 1; 4527 4528 } else { 4529 4530 l = 1 / l; 4531 4532 this._x = this._x * l; 4533 this._y = this._y * l; 4534 this._z = this._z * l; 4535 this._w = this._w * l; 4536 4537 } 4538 4539 this._onChangeCallback(); 4540 4541 return this; 4542 4543 } 4544 4545 /** 4546 * Multiplies this quaternion by the given one. 4547 * 4548 * @param {Quaternion} q - The quaternion. 4549 * @return {Quaternion} A reference to this quaternion. 4550 */ 4551 multiply( q ) { 4552 4553 return this.multiplyQuaternions( this, q ); 4554 4555 } 4556 4557 /** 4558 * Pre-multiplies this quaternion by the given one. 4559 * 4560 * @param {Quaternion} q - The quaternion. 4561 * @return {Quaternion} A reference to this quaternion. 4562 */ 4563 premultiply( q ) { 4564 4565 return this.multiplyQuaternions( q, this ); 4566 4567 } 4568 4569 /** 4570 * Multiplies the given quaternions and stores the result in this instance. 4571 * 4572 * @param {Quaternion} a - The first quaternion. 4573 * @param {Quaternion} b - The second quaternion. 4574 * @return {Quaternion} A reference to this quaternion. 4575 */ 4576 multiplyQuaternions( a, b ) { 4577 4578 const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; 4579 const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; 4580 4581 this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; 4582 this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; 4583 this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; 4584 this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; 4585 4586 this._onChangeCallback(); 4587 4588 return this; 4589 4590 } 4591 4592 /** 4593 * Performs a spherical linear interpolation between this quaternion and the target quaternion. 4594 * 4595 * @param {Quaternion} qb - The target quaternion. 4596 * @param {number} t - The interpolation factor. A value in the range `[0,1]` will interpolate. A value outside the range `[0,1]` will extrapolate. 4597 * @return {Quaternion} A reference to this quaternion. 4598 */ 4599 slerp( qb, t ) { 4600 4601 let x = qb._x, y = qb._y, z = qb._z, w = qb._w; 4602 4603 let dot = this.dot( qb ); 4604 4605 if ( dot < 0 ) { 4606 4607 x = - x; 4608 y = - y; 4609 z = - z; 4610 w = - w; 4611 4612 dot = - dot; 4613 4614 } 4615 4616 let s = 1 - t; 4617 4618 if ( dot < 0.9995 ) { 4619 4620 // slerp 4621 4622 const theta = Math.acos( dot ); 4623 const sin = Math.sin( theta ); 4624 4625 s = Math.sin( s * theta ) / sin; 4626 t = Math.sin( t * theta ) / sin; 4627 4628 this._x = this._x * s + x * t; 4629 this._y = this._y * s + y * t; 4630 this._z = this._z * s + z * t; 4631 this._w = this._w * s + w * t; 4632 4633 this._onChangeCallback(); 4634 4635 } else { 4636 4637 // for small angles, lerp then normalize 4638 4639 this._x = this._x * s + x * t; 4640 this._y = this._y * s + y * t; 4641 this._z = this._z * s + z * t; 4642 this._w = this._w * s + w * t; 4643 4644 this.normalize(); // normalize calls _onChangeCallback() 4645 4646 } 4647 4648 return this; 4649 4650 } 4651 4652 /** 4653 * Performs a spherical linear interpolation between the given quaternions 4654 * and stores the result in this quaternion. 4655 * 4656 * @param {Quaternion} qa - The source quaternion. 4657 * @param {Quaternion} qb - The target quaternion. 4658 * @param {number} t - The interpolation factor in the closed interval `[0, 1]`. 4659 * @return {Quaternion} A reference to this quaternion. 4660 */ 4661 slerpQuaternions( qa, qb, t ) { 4662 4663 return this.copy( qa ).slerp( qb, t ); 4664 4665 } 4666 4667 /** 4668 * Sets this quaternion to a uniformly random, normalized quaternion. 4669 * 4670 * @return {Quaternion} A reference to this quaternion. 4671 */ 4672 random() { 4673 4674 // Ken Shoemake 4675 // Uniform random rotations 4676 // D. Kirk, editor, Graphics Gems III, pages 124-132. Academic Press, New York, 1992. 4677 4678 const theta1 = 2 * Math.PI * Math.random(); 4679 const theta2 = 2 * Math.PI * Math.random(); 4680 4681 const x0 = Math.random(); 4682 const r1 = Math.sqrt( 1 - x0 ); 4683 const r2 = Math.sqrt( x0 ); 4684 4685 return this.set( 4686 r1 * Math.sin( theta1 ), 4687 r1 * Math.cos( theta1 ), 4688 r2 * Math.sin( theta2 ), 4689 r2 * Math.cos( theta2 ), 4690 ); 4691 4692 } 4693 4694 /** 4695 * Returns `true` if this quaternion is equal with the given one. 4696 * 4697 * @param {Quaternion} quaternion - The quaternion to test for equality. 4698 * @return {boolean} Whether this quaternion is equal with the given one. 4699 */ 4700 equals( quaternion ) { 4701 4702 return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); 4703 4704 } 4705 4706 /** 4707 * Sets this quaternion's components from the given array. 4708 * 4709 * @param {Array<number>} array - An array holding the quaternion component values. 4710 * @param {number} [offset=0] - The offset into the array. 4711 * @return {Quaternion} A reference to this quaternion. 4712 */ 4713 fromArray( array, offset = 0 ) { 4714 4715 this._x = array[ offset ]; 4716 this._y = array[ offset + 1 ]; 4717 this._z = array[ offset + 2 ]; 4718 this._w = array[ offset + 3 ]; 4719 4720 this._onChangeCallback(); 4721 4722 return this; 4723 4724 } 4725 4726 /** 4727 * Writes the components of this quaternion to the given array. If no array is provided, 4728 * the method returns a new instance. 4729 * 4730 * @param {Array<number>} [array=[]] - The target array holding the quaternion components. 4731 * @param {number} [offset=0] - Index of the first element in the array. 4732 * @return {Array<number>} The quaternion components. 4733 */ 4734 toArray( array = [], offset = 0 ) { 4735 4736 array[ offset ] = this._x; 4737 array[ offset + 1 ] = this._y; 4738 array[ offset + 2 ] = this._z; 4739 array[ offset + 3 ] = this._w; 4740 4741 return array; 4742 4743 } 4744 4745 /** 4746 * Sets the components of this quaternion from the given buffer attribute. 4747 * 4748 * @param {BufferAttribute} attribute - The buffer attribute holding quaternion data. 4749 * @param {number} index - The index into the attribute. 4750 * @return {Quaternion} A reference to this quaternion. 4751 */ 4752 fromBufferAttribute( attribute, index ) { 4753 4754 this._x = attribute.getX( index ); 4755 this._y = attribute.getY( index ); 4756 this._z = attribute.getZ( index ); 4757 this._w = attribute.getW( index ); 4758 4759 this._onChangeCallback(); 4760 4761 return this; 4762 4763 } 4764 4765 /** 4766 * This methods defines the serialization result of this class. Returns the 4767 * numerical elements of this quaternion in an array of format `[x, y, z, w]`. 4768 * 4769 * @return {Array<number>} The serialized quaternion. 4770 */ 4771 toJSON() { 4772 4773 return this.toArray(); 4774 4775 } 4776 4777 _onChange( callback ) { 4778 4779 this._onChangeCallback = callback; 4780 4781 return this; 4782 4783 } 4784 4785 _onChangeCallback() {} 4786 4787 *[ Symbol.iterator ]() { 4788 4789 yield this._x; 4790 yield this._y; 4791 yield this._z; 4792 yield this._w; 4793 4794 } 4795 4796} 4797 4798/** 4799 * Class representing a 3D vector. A 3D vector is an ordered triplet of numbers 4800 * (labeled x, y and z), which can be used to represent a number of things, such as: 4801 * 4802 * - A point in 3D space. 4803 * - A direction and length in 3D space. In three.js the length will 4804 * always be the Euclidean distance(straight-line distance) from `(0, 0, 0)` to `(x, y, z)` 4805 * and the direction is also measured from `(0, 0, 0)` towards `(x, y, z)`. 4806 * - Any arbitrary ordered triplet of numbers. 4807 * 4808 * There are other things a 3D vector can be used to represent, such as 4809 * momentum vectors and so on, however these are the most 4810 * common uses in three.js. 4811 * 4812 * Iterating through a vector instance will yield its components `(x, y, z)` in 4813 * the corresponding order. 4814 * ```js 4815 * const a = new THREE.Vector3( 0, 1, 0 ); 4816 * 4817 * //no arguments; will be initialised to (0, 0, 0) 4818 * const b = new THREE.Vector3( ); 4819 * 4820 * const d = a.distanceTo( b ); 4821 * ``` 4822 */ 4823class Vector3 { 4824 4825 static { 4826 4827 /** 4828 * This flag can be used for type testing. 4829 * 4830 * @type {boolean} 4831 * @readonly 4832 * @default true 4833 */ 4834 Vector3.prototype.isVector3 = true; 4835 4836 } 4837 4838 /** 4839 * Constructs a new 3D vector. 4840 * 4841 * @param {number} [x=0] - The x value of this vector. 4842 * @param {number} [y=0] - The y value of this vector. 4843 * @param {number} [z=0] - The z value of this vector. 4844 */ 4845 constructor( x = 0, y = 0, z = 0 ) { 4846 4847 /** 4848 * The x value of this vector. 4849 * 4850 * @type {number} 4851 */ 4852 this.x = x; 4853 4854 /** 4855 * The y value of this vector. 4856 * 4857 * @type {number} 4858 */ 4859 this.y = y; 4860 4861 /** 4862 * The z value of this vector. 4863 * 4864 * @type {number} 4865 */ 4866 this.z = z; 4867 4868 } 4869 4870 /** 4871 * Sets the vector components. 4872 * 4873 * @param {number} x - The value of the x component. 4874 * @param {number} y - The value of the y component. 4875 * @param {number} z - The value of the z component. 4876 * @return {Vector3} A reference to this vector. 4877 */ 4878 set( x, y, z ) { 4879 4880 if ( z === undefined ) z = this.z; // sprite.scale.set(x,y) 4881 4882 this.x = x; 4883 this.y = y; 4884 this.z = z; 4885 4886 return this; 4887 4888 } 4889 4890 /** 4891 * Sets the vector components to the same value. 4892 * 4893 * @param {number} scalar - The value to set for all vector components. 4894 * @return {Vector3} A reference to this vector. 4895 */ 4896 setScalar( scalar ) { 4897 4898 this.x = scalar; 4899 this.y = scalar; 4900 this.z = scalar; 4901 4902 return this; 4903 4904 } 4905 4906 /** 4907 * Sets the vector's x component to the given value. 4908 * 4909 * @param {number} x - The value to set. 4910 * @return {Vector3} A reference to this vector. 4911 */ 4912 setX( x ) { 4913 4914 this.x = x; 4915 4916 return this; 4917 4918 } 4919 4920 /** 4921 * Sets the vector's y component to the given value. 4922 * 4923 * @param {number} y - The value to set. 4924 * @return {Vector3} A reference to this vector. 4925 */ 4926 setY( y ) { 4927 4928 this.y = y; 4929 4930 return this; 4931 4932 } 4933 4934 /** 4935 * Sets the vector's z component to the given value. 4936 * 4937 * @param {number} z - The value to set. 4938 * @return {Vector3} A reference to this vector. 4939 */ 4940 setZ( z ) { 4941 4942 this.z = z; 4943 4944 return this; 4945 4946 } 4947 4948 /** 4949 * Allows to set a vector component with an index. 4950 * 4951 * @param {number} index - The component index. `0` equals to x, `1` equals to y, `2` equals to z. 4952 * @param {number} value - The value to set. 4953 * @return {Vector3} A reference to this vector. 4954 */ 4955 setComponent( index, value ) { 4956 4957 switch ( index ) { 4958 4959 case 0: this.x = value; break; 4960 case 1: this.y = value; break; 4961 case 2: this.z = value; break; 4962 default: throw new Error( 'THREE.Vector3: index is out of range: ' + index ); 4963 4964 } 4965 4966 return this; 4967 4968 } 4969 4970 /** 4971 * Returns the value of the vector component which matches the given index. 4972 * 4973 * @param {number} index - The component index. `0` equals to x, `1` equals to y, `2` equals to z. 4974 * @return {number} A vector component value. 4975 */ 4976 getComponent( index ) { 4977 4978 switch ( index ) { 4979 4980 case 0: return this.x; 4981 case 1: return this.y; 4982 case 2: return this.z; 4983 default: throw new Error( 'THREE.Vector3: index is out of range: ' + index ); 4984 4985 } 4986 4987 } 4988 4989 /** 4990 * Returns a new vector with copied values from this instance. 4991 * 4992 * @return {Vector3} A clone of this instance. 4993 */ 4994 clone() { 4995 4996 return new this.constructor( this.x, this.y, this.z ); 4997 4998 } 4999 5000 /** 5001 * Copies the values of the given vector to this instance. 5002 * 5003 * @param {Vector3} v - The vector to copy. 5004 * @return {Vector3} A reference to this vector. 5005 */ 5006 copy( v ) { 5007 5008 this.x = v.x; 5009 this.y = v.y; 5010 this.z = v.z; 5011 5012 return this; 5013 5014 } 5015 5016 /** 5017 * Adds the given vector to this instance. 5018 * 5019 * @param {Vector3} v - The vector to add. 5020 * @return {Vector3} A reference to this vector. 5021 */ 5022 add( v ) { 5023 5024 this.x += v.x; 5025 this.y += v.y; 5026 this.z += v.z; 5027 5028 return this; 5029 5030 } 5031 5032 /** 5033 * Adds the given scalar value to all components of this instance. 5034 * 5035 * @param {number} s - The scalar to add. 5036 * @return {Vector3} A reference to this vector. 5037 */ 5038 addScalar( s ) { 5039 5040 this.x += s; 5041 this.y += s; 5042 this.z += s; 5043 5044 return this; 5045 5046 } 5047 5048 /** 5049 * Adds the given vectors and stores the result in this instance. 5050 * 5051 * @param {Vector3} a - The first vector. 5052 * @param {Vector3} b - The second vector. 5053 * @return {Vector3} A reference to this vector. 5054 */ 5055 addVectors( a, b ) { 5056 5057 this.x = a.x + b.x; 5058 this.y = a.y + b.y; 5059 this.z = a.z + b.z; 5060 5061 return this; 5062 5063 } 5064 5065 /** 5066 * Adds the given vector scaled by the given factor to this instance. 5067 * 5068 * @param {Vector3|Vector4} v - The vector. 5069 * @param {number} s - The factor that scales `v`. 5070 * @return {Vector3} A reference to this vector. 5071 */ 5072 addScaledVector( v, s ) { 5073 5074 this.x += v.x * s; 5075 this.y += v.y * s; 5076 this.z += v.z * s; 5077 5078 return this; 5079 5080 } 5081 5082 /** 5083 * Subtracts the given vector from this instance. 5084 * 5085 * @param {Vector3} v - The vector to subtract. 5086 * @return {Vector3} A reference to this vector. 5087 */ 5088 sub( v ) { 5089 5090 this.x -= v.x; 5091 this.y -= v.y; 5092 this.z -= v.z; 5093 5094 return this; 5095 5096 } 5097 5098 /** 5099 * Subtracts the given scalar value from all components of this instance. 5100 * 5101 * @param {number} s - The scalar to subtract. 5102 * @return {Vector3} A reference to this vector. 5103 */ 5104 subScalar( s ) { 5105 5106 this.x -= s; 5107 this.y -= s; 5108 this.z -= s; 5109 5110 return this; 5111 5112 } 5113 5114 /** 5115 * Subtracts the given vectors and stores the result in this instance. 5116 * 5117 * @param {Vector3} a - The first vector. 5118 * @param {Vector3} b - The second vector. 5119 * @return {Vector3} A reference to this vector. 5120 */ 5121 subVectors( a, b ) { 5122 5123 this.x = a.x - b.x; 5124 this.y = a.y - b.y; 5125 this.z = a.z - b.z; 5126 5127 return this; 5128 5129 } 5130 5131 /** 5132 * Multiplies the given vector with this instance. 5133 * 5134 * @param {Vector3} v - The vector to multiply. 5135 * @return {Vector3} A reference to this vector. 5136 */ 5137 multiply( v ) { 5138 5139 this.x *= v.x; 5140 this.y *= v.y; 5141 this.z *= v.z; 5142 5143 return this; 5144 5145 } 5146 5147 /** 5148 * Multiplies the given scalar value with all components of this instance. 5149 * 5150 * @param {number} scalar - The scalar to multiply. 5151 * @return {Vector3} A reference to this vector. 5152 */ 5153 multiplyScalar( scalar ) { 5154 5155 this.x *= scalar; 5156 this.y *= scalar; 5157 this.z *= scalar; 5158 5159 return this; 5160 5161 } 5162 5163 /** 5164 * Multiplies the given vectors and stores the result in this instance. 5165 * 5166 * @param {Vector3} a - The first vector. 5167 * @param {Vector3} b - The second vector. 5168 * @return {Vector3} A reference to this vector. 5169 */ 5170 multiplyVectors( a, b ) { 5171 5172 this.x = a.x * b.x; 5173 this.y = a.y * b.y; 5174 this.z = a.z * b.z; 5175 5176 return this; 5177 5178 } 5179 5180 /** 5181 * Applies the given Euler rotation to this vector. 5182 * 5183 * @param {Euler} euler - The Euler angles. 5184 * @return {Vector3} A reference to this vector. 5185 */ 5186 applyEuler( euler ) { 5187 5188 return this.applyQuaternion( _quaternion$5.setFromEuler( euler ) ); 5189 5190 } 5191 5192 /** 5193 * Applies a rotation specified by an axis and an angle to this vector. 5194 * 5195 * @param {Vector3} axis - A normalized vector representing the rotation axis. 5196 * @param {number} angle - The angle in radians. 5197 * @return {Vector3} A reference to this vector. 5198 */ 5199 applyAxisAngle( axis, angle ) { 5200 5201 return this.applyQuaternion( _quaternion$5.setFromAxisAngle( axis, angle ) ); 5202 5203 } 5204 5205 /** 5206 * Multiplies this vector with the given 3x3 matrix. 5207 * 5208 * @param {Matrix3} m - The 3x3 matrix. 5209 * @return {Vector3} A reference to this vector. 5210 */ 5211 applyMatrix3( m ) { 5212 5213 const x = this.x, y = this.y, z = this.z; 5214 const e = m.elements; 5215 5216 this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; 5217 this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; 5218 this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; 5219 5220 return this; 5221 5222 } 5223 5224 /** 5225 * Multiplies this vector by the given normal matrix and normalizes 5226 * the result. 5227 * 5228 * @param {Matrix3} m - The normal matrix. 5229 * @return {Vector3} A reference to this vector. 5230 */ 5231 applyNormalMatrix( m ) { 5232 5233 return this.applyMatrix3( m ).normalize(); 5234 5235 } 5236 5237 /** 5238 * Multiplies this vector (with an implicit 1 in the 4th dimension) by m, and 5239 * divides by perspective. 5240 * 5241 * @param {Matrix4} m - The matrix to apply. 5242 * @return {Vector3} A reference to this vector. 5243 */ 5244 applyMatrix4( m ) { 5245 5246 const x = this.x, y = this.y, z = this.z; 5247 const e = m.elements; 5248 5249 const w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); 5250 5251 this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w; 5252 this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w; 5253 this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w; 5254 5255 return this; 5256 5257 } 5258 5259 /** 5260 * Applies the given Quaternion to this vector. 5261 * 5262 * @param {Quaternion} q - The Quaternion. 5263 * @return {Vector3} A reference to this vector. 5264 */ 5265 applyQuaternion( q ) { 5266 5267 // quaternion q is assumed to have unit length 5268 5269 const vx = this.x, vy = this.y, vz = this.z; 5270 const qx = q.x, qy = q.y, qz = q.z, qw = q.w; 5271 5272 // t = 2 * cross( q.xyz, v ); 5273 const tx = 2 * ( qy * vz - qz * vy ); 5274 const ty = 2 * ( qz * vx - qx * vz ); 5275 const tz = 2 * ( qx * vy - qy * vx ); 5276 5277 // v + q.w * t + cross( q.xyz, t ); 5278 this.x = vx + qw * tx + qy * tz - qz * ty; 5279 this.y = vy + qw * ty + qz * tx - qx * tz; 5280 this.z = vz + qw * tz + qx * ty - qy * tx; 5281 5282 return this; 5283 5284 } 5285 5286 /** 5287 * Projects this vector from world space into the camera's normalized 5288 * device coordinate (NDC) space. 5289 * 5290 * @param {Camera} camera - The camera. 5291 * @return {Vector3} A reference to this vector. 5292 */ 5293 project( camera ) { 5294 5295 return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix ); 5296 5297 } 5298 5299 /** 5300 * Unprojects this vector from the camera's normalized device coordinate (NDC) 5301 * space into world space. 5302 * 5303 * @param {Camera} camera - The camera. 5304 * @return {Vector3} A reference to this vector. 5305 */ 5306 unproject( camera ) { 5307 5308 return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld ); 5309 5310 } 5311 5312 /** 5313 * Transforms this vector by the upper left 3x3 sub-matrix of the given 4x4 matrix, 5314 * and normalizes the result. 5315 * 5316 * @param {Matrix4} m - The matrix. 5317 * @return {Vector3} A reference to this vector. 5318 */ 5319 transformDirection( m ) { 5320 5321 // input: THREE.Matrix4 affine matrix 5322 // vector interpreted as a direction 5323 5324 const x = this.x, y = this.y, z = this.z; 5325 const e = m.elements; 5326 5327 this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; 5328 this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; 5329 this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; 5330 5331 return this.normalize(); 5332 5333 } 5334 5335 /** 5336 * Divides this instance by the given vector. 5337 * 5338 * @param {Vector3} v - The vector to divide. 5339 * @return {Vector3} A reference to this vector. 5340 */ 5341 divide( v ) { 5342 5343 this.x /= v.x; 5344 this.y /= v.y; 5345 this.z /= v.z; 5346 5347 return this; 5348 5349 } 5350 5351 /** 5352 * Divides this vector by the given scalar. 5353 * 5354 * @param {number} scalar - The scalar to divide. 5355 * @return {Vector3} A reference to this vector. 5356 */ 5357 divideScalar( scalar ) { 5358 5359 return this.multiplyScalar( 1 / scalar ); 5360 5361 } 5362 5363 /** 5364 * If this vector's x, y or z value is greater than the given vector's x, y or z 5365 * value, replace that value with the corresponding min value. 5366 * 5367 * @param {Vector3} v - The vector. 5368 * @return {Vector3} A reference to this vector. 5369 */ 5370 min( v ) { 5371 5372 this.x = Math.min( this.x, v.x ); 5373 this.y = Math.min( this.y, v.y ); 5374 this.z = Math.min( this.z, v.z ); 5375 5376 return this; 5377 5378 } 5379 5380 /** 5381 * If this vector's x, y or z value is less than the given vector's x, y or z 5382 * value, replace that value with the corresponding max value. 5383 * 5384 * @param {Vector3} v - The vector. 5385 * @return {Vector3} A reference to this vector. 5386 */ 5387 max( v ) { 5388 5389 this.x = Math.max( this.x, v.x ); 5390 this.y = Math.max( this.y, v.y ); 5391 this.z = Math.max( this.z, v.z ); 5392 5393 return this; 5394 5395 } 5396 5397 /** 5398 * If this vector's x, y or z value is greater than the max vector's x, y or z 5399 * value, it is replaced by the corresponding value. 5400 * If this vector's x, y or z value is less than the min vector's x, y or z value, 5401 * it is replaced by the corresponding value. 5402 * 5403 * @param {Vector3} min - The minimum x, y and z values. 5404 * @param {Vector3} max - The maximum x, y and z values in the desired range. 5405 * @return {Vector3} A reference to this vector. 5406 */ 5407 clamp( min, max ) { 5408 5409 // assumes min < max, componentwise 5410 5411 this.x = clamp( this.x, min.x, max.x ); 5412 this.y = clamp( this.y, min.y, max.y ); 5413 this.z = clamp( this.z, min.z, max.z ); 5414 5415 return this; 5416 5417 } 5418 5419 /** 5420 * If this vector's x, y or z values are greater than the max value, they are 5421 * replaced by the max value. 5422 * If this vector's x, y or z values are less than the min value, they are 5423 * replaced by the min value. 5424 * 5425 * @param {number} minVal - The minimum value the components will be clamped to. 5426 * @param {number} maxVal - The maximum value the components will be clamped to. 5427 * @return {Vector3} A reference to this vector. 5428 */ 5429 clampScalar( minVal, maxVal ) { 5430 5431 this.x = clamp( this.x, minVal, maxVal ); 5432 this.y = clamp( this.y, minVal, maxVal ); 5433 this.z = clamp( this.z, minVal, maxVal ); 5434 5435 return this; 5436 5437 } 5438 5439 /** 5440 * If this vector's length is greater than the max value, it is replaced by 5441 * the max value. 5442 * If this vector's length is less than the min value, it is replaced by the 5443 * min value. 5444 * 5445 * @param {number} min - The minimum value the vector length will be clamped to. 5446 * @param {number} max - The maximum value the vector length will be clamped to. 5447 * @return {Vector3} A reference to this vector. 5448 */ 5449 clampLength( min, max ) { 5450 5451 const length = this.length(); 5452 5453 return this.divideScalar( length || 1 ).multiplyScalar( clamp( length, min, max ) ); 5454 5455 } 5456 5457 /** 5458 * The components of this vector are rounded down to the nearest integer value. 5459 * 5460 * @return {Vector3} A reference to this vector. 5461 */ 5462 floor() { 5463 5464 this.x = Math.floor( this.x ); 5465 this.y = Math.floor( this.y ); 5466 this.z = Math.floor( this.z ); 5467 5468 return this; 5469 5470 } 5471 5472 /** 5473 * The components of this vector are rounded up to the nearest integer value. 5474 * 5475 * @return {Vector3} A reference to this vector. 5476 */ 5477 ceil() { 5478 5479 this.x = Math.ceil( this.x ); 5480 this.y = Math.ceil( this.y ); 5481 this.z = Math.ceil( this.z ); 5482 5483 return this; 5484 5485 } 5486 5487 /** 5488 * The components of this vector are rounded to the nearest integer value 5489 * 5490 * @return {Vector3} A reference to this vector. 5491 */ 5492 round() { 5493 5494 this.x = Math.round( this.x ); 5495 this.y = Math.round( this.y ); 5496 this.z = Math.round( this.z ); 5497 5498 return this; 5499 5500 } 5501 5502 /** 5503 * The components of this vector are rounded towards zero (up if negative, 5504 * down if positive) to an integer value. 5505 * 5506 * @return {Vector3} A reference to this vector. 5507 */ 5508 roundToZero() { 5509 5510 this.x = Math.trunc( this.x ); 5511 this.y = Math.trunc( this.y ); 5512 this.z = Math.trunc( this.z ); 5513 5514 return this; 5515 5516 } 5517 5518 /** 5519 * Inverts this vector - i.e. sets x = -x, y = -y and z = -z. 5520 * 5521 * @return {Vector3} A reference to this vector. 5522 */ 5523 negate() { 5524 5525 this.x = - this.x; 5526 this.y = - this.y; 5527 this.z = - this.z; 5528 5529 return this; 5530 5531 } 5532 5533 /** 5534 * Calculates the dot product of the given vector with this instance. 5535 * 5536 * @param {Vector3} v - The vector to compute the dot product with. 5537 * @return {number} The result of the dot product. 5538 */ 5539 dot( v ) { 5540 5541 return this.x * v.x + this.y * v.y + this.z * v.z; 5542 5543 } 5544 5545 /** 5546 * Computes the square of the Euclidean length (straight-line length) from 5547 * (0, 0, 0) to (x, y, z). If you are comparing the lengths of vectors, you should 5548 * compare the length squared instead as it is slightly more efficient to calculate. 5549 * 5550 * @return {number} The square length of this vector. 5551 */ 5552 lengthSq() { 5553 5554 return this.x * this.x + this.y * this.y + this.z * this.z; 5555 5556 } 5557 5558 /** 5559 * Computes the Euclidean length (straight-line length) from (0, 0, 0) to (x, y, z). 5560 * 5561 * @return {number} The length of this vector. 5562 */ 5563 length() { 5564 5565 return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); 5566 5567 } 5568 5569 /** 5570 * Computes the Manhattan length of this vector. 5571 * 5572 * @return {number} The length of this vector. 5573 */ 5574 manhattanLength() { 5575 5576 return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); 5577 5578 } 5579 5580 /** 5581 * Converts this vector to a unit vector - that is, sets it equal to a vector 5582 * with the same direction as this one, but with a vector length of `1`. 5583 * 5584 * @return {Vector3} A reference to this vector. 5585 */ 5586 normalize() { 5587 5588 return this.divideScalar( this.length() || 1 ); 5589 5590 } 5591 5592 /** 5593 * Sets this vector to a vector with the same direction as this one, but 5594 * with the specified length. 5595 * 5596 * @param {number} length - The new length of this vector. 5597 * @return {Vector3} A reference to this vector. 5598 */ 5599 setLength( length ) { 5600 5601 return this.normalize().multiplyScalar( length ); 5602 5603 } 5604 5605 /** 5606 * Linearly interpolates between the given vector and this instance, where 5607 * alpha is the percent distance along the line - alpha = 0 will be this 5608 * vector, and alpha = 1 will be the given one. 5609 * 5610 * @param {Vector3} v - The vector to interpolate towards. 5611 * @param {number} alpha - The interpolation factor, typically in the closed interval `[0, 1]`. 5612 * @return {Vector3} A reference to this vector. 5613 */ 5614 lerp( v, alpha ) { 5615 5616 this.x += ( v.x - this.x ) * alpha; 5617 this.y += ( v.y - this.y ) * alpha; 5618 this.z += ( v.z - this.z ) * alpha; 5619 5620 return this; 5621 5622 } 5623 5624 /** 5625 * Linearly interpolates between the given vectors, where alpha is the percent 5626 * distance along the line - alpha = 0 will be first vector, and alpha = 1 will 5627 * be the second one. The result is stored in this instance. 5628 * 5629 * @param {Vector3} v1 - The first vector. 5630 * @param {Vector3} v2 - The second vector. 5631 * @param {number} alpha - The interpolation factor, typically in the closed interval `[0, 1]`. 5632 * @return {Vector3} A reference to this vector. 5633 */ 5634 lerpVectors( v1, v2, alpha ) { 5635 5636 this.x = v1.x + ( v2.x - v1.x ) * alpha; 5637 this.y = v1.y + ( v2.y - v1.y ) * alpha; 5638 this.z = v1.z + ( v2.z - v1.z ) * alpha; 5639 5640 return this; 5641 5642 } 5643 5644 /** 5645 * Calculates the cross product of the given vector with this instance. 5646 * 5647 * @param {Vector3} v - The vector to compute the cross product with. 5648 * @return {Vector3} The result of the cross product. 5649 */ 5650 cross( v ) { 5651 5652 return this.crossVectors( this, v ); 5653 5654 } 5655 5656 /** 5657 * Calculates the cross product of the given vectors and stores the result 5658 * in this instance. 5659 * 5660 * @param {Vector3} a - The first vector. 5661 * @param {Vector3} b - The second vector. 5662 * @return {Vector3} A reference to this vector. 5663 */ 5664 crossVectors( a, b ) { 5665 5666 const ax = a.x, ay = a.y, az = a.z; 5667 const bx = b.x, by = b.y, bz = b.z; 5668 5669 this.x = ay * bz - az * by; 5670 this.y = az * bx - ax * bz; 5671 this.z = ax * by - ay * bx; 5672 5673 return this; 5674 5675 } 5676 5677 /** 5678 * Projects this vector onto the given one. 5679 * 5680 * @param {Vector3} v - The vector to project to. 5681 * @return {Vector3} A reference to this vector. 5682 */ 5683 projectOnVector( v ) { 5684 5685 const denominator = v.lengthSq(); 5686 5687 if ( denominator === 0 ) return this.set( 0, 0, 0 ); 5688 5689 const scalar = v.dot( this ) / denominator; 5690 5691 return this.copy( v ).multiplyScalar( scalar ); 5692 5693 } 5694 5695 /** 5696 * Projects this vector onto a plane by subtracting this 5697 * vector projected onto the plane's normal from this vector. 5698 * 5699 * @param {Vector3} planeNormal - The plane normal. 5700 * @return {Vector3} A reference to this vector. 5701 */ 5702 projectOnPlane( planeNormal ) { 5703 5704 _vector$c.copy( this ).projectOnVector( planeNormal ); 5705 5706 return this.sub( _vector$c ); 5707 5708 } 5709 5710 /** 5711 * Reflects this vector off a plane orthogonal to the given normal vector. 5712 * 5713 * @param {Vector3} normal - The (normalized) normal vector. 5714 * @return {Vector3} A reference to this vector. 5715 */ 5716 reflect( normal ) { 5717 5718 return this.sub( _vector$c.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); 5719 5720 } 5721 /** 5722 * Returns the angle between the given vector and this instance in radians. 5723 * 5724 * @param {Vector3} v - The vector to compute the angle with. 5725 * @return {number} The angle in radians. 5726 */ 5727 angleTo( v ) { 5728 5729 const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); 5730 5731 if ( denominator === 0 ) return Math.PI / 2; 5732 5733 const theta = this.dot( v ) / denominator; 5734 5735 // clamp, to handle numerical problems 5736 5737 return Math.acos( clamp( theta, -1, 1 ) ); 5738 5739 } 5740 5741 /** 5742 * Computes the distance from the given vector to this instance. 5743 * 5744 * @param {Vector3} v - The vector to compute the distance to. 5745 * @return {number} The distance. 5746 */ 5747 distanceTo( v ) { 5748 5749 return Math.sqrt( this.distanceToSquared( v ) ); 5750 5751 } 5752 5753 /** 5754 * Computes the squared distance from the given vector to this instance. 5755 * If you are just comparing the distance with another distance, you should compare 5756 * the distance squared instead as it is slightly more efficient to calculate. 5757 * 5758 * @param {Vector3} v - The vector to compute the squared distance to. 5759 * @return {number} The squared distance. 5760 */ 5761 distanceToSquared( v ) { 5762 5763 const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; 5764 5765 return dx * dx + dy * dy + dz * dz; 5766 5767 } 5768 5769 /** 5770 * Computes the Manhattan distance from the given vector to this instance. 5771 * 5772 * @param {Vector3} v - The vector to compute the Manhattan distance to. 5773 * @return {number} The Manhattan distance. 5774 */ 5775 manhattanDistanceTo( v ) { 5776 5777 return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); 5778 5779 } 5780 5781 /** 5782 * Sets the vector components from the given spherical coordinates. 5783 * 5784 * @param {Spherical} s - The spherical coordinates. 5785 * @return {Vector3} A reference to this vector. 5786 */ 5787 setFromSpherical( s ) { 5788 5789 return this.setFromSphericalCoords( s.radius, s.phi, s.theta ); 5790 5791 } 5792 5793 /** 5794 * Sets the vector components from the given spherical coordinates. 5795 * 5796 * @param {number} radius - The radius. 5797 * @param {number} phi - The phi angle in radians. 5798 * @param {number} theta - The theta angle in radians. 5799 * @return {Vector3} A reference to this vector. 5800 */ 5801 setFromSphericalCoords( radius, phi, theta ) { 5802 5803 const sinPhiRadius = Math.sin( phi ) * radius; 5804 5805 this.x = sinPhiRadius * Math.sin( theta ); 5806 this.y = Math.cos( phi ) * radius; 5807 this.z = sinPhiRadius * Math.cos( theta ); 5808 5809 return this; 5810 5811 } 5812 5813 /** 5814 * Sets the vector components from the given cylindrical coordinates. 5815 * 5816 * @param {Cylindrical} c - The cylindrical coordinates. 5817 * @return {Vector3} A reference to this vector. 5818 */ 5819 setFromCylindrical( c ) { 5820 5821 return this.setFromCylindricalCoords( c.radius, c.theta, c.y ); 5822 5823 } 5824 5825 /** 5826 * Sets the vector components from the given cylindrical coordinates. 5827 * 5828 * @param {number} radius - The radius. 5829 * @param {number} theta - The theta angle in radians. 5830 * @param {number} y - The y value. 5831 * @return {Vector3} A reference to this vector. 5832 */ 5833 setFromCylindricalCoords( radius, theta, y ) { 5834 5835 this.x = radius * Math.sin( theta ); 5836 this.y = y; 5837 this.z = radius * Math.cos( theta ); 5838 5839 return this; 5840 5841 } 5842 5843 /** 5844 * Sets the vector components to the position elements of the 5845 * given transformation matrix. 5846 * 5847 * @param {Matrix4} m - The 4x4 matrix. 5848 * @return {Vector3} A reference to this vector. 5849 */ 5850 setFromMatrixPosition( m ) { 5851 5852 const e = m.elements; 5853 5854 this.x = e[ 12 ]; 5855 this.y = e[ 13 ]; 5856 this.z = e[ 14 ]; 5857 5858 return this; 5859 5860 } 5861 5862 /** 5863 * Sets the vector components to the scale elements of the 5864 * given transformation matrix. 5865 * 5866 * @param {Matrix4} m - The 4x4 matrix. 5867 * @return {Vector3} A reference to this vector. 5868 */ 5869 setFromMatrixScale( m ) { 5870 5871 const sx = this.setFromMatrixColumn( m, 0 ).length(); 5872 const sy = this.setFromMatrixColumn( m, 1 ).length(); 5873 const sz = this.setFromMatrixColumn( m, 2 ).length(); 5874 5875 this.x = sx; 5876 this.y = sy; 5877 this.z = sz; 5878 5879 return this; 5880 5881 } 5882 5883 /** 5884 * Sets the vector components from the specified matrix column. 5885 * 5886 * @param {Matrix4} m - The 4x4 matrix. 5887 * @param {number} index - The column index. 5888 * @return {Vector3} A reference to this vector. 5889 */ 5890 setFromMatrixColumn( m, index ) { 5891 5892 return this.fromArray( m.elements, index * 4 ); 5893 5894 } 5895 5896 /** 5897 * Sets the vector components from the specified matrix column. 5898 * 5899 * @param {Matrix3} m - The 3x3 matrix. 5900 * @param {number} index - The column index. 5901 * @return {Vector3} A reference to this vector. 5902 */ 5903 setFromMatrix3Column( m, index ) { 5904 5905 return this.fromArray( m.elements, index * 3 ); 5906 5907 } 5908 5909 /** 5910 * Sets the vector components from the given Euler angles. 5911 * 5912 * @param {Euler} e - The Euler angles to set. 5913 * @return {Vector3} A reference to this vector. 5914 */ 5915 setFromEuler( e ) { 5916 5917 this.x = e._x; 5918 this.y = e._y; 5919 this.z = e._z; 5920 5921 return this; 5922 5923 } 5924 5925 /** 5926 * Sets the vector components from the RGB components of the 5927 * given color. 5928 * 5929 * @param {Color} c - The color to set. 5930 * @return {Vector3} A reference to this vector. 5931 */ 5932 setFromColor( c ) { 5933 5934 this.x = c.r; 5935 this.y = c.g; 5936 this.z = c.b; 5937 5938 return this; 5939 5940 } 5941 5942 /** 5943 * Returns `true` if this vector is equal with the given one. 5944 * 5945 * @param {Vector3} v - The vector to test for equality. 5946 * @return {boolean} Whether this vector is equal with the given one. 5947 */ 5948 equals( v ) { 5949 5950 return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); 5951 5952 } 5953 5954 /** 5955 * Sets this vector's x value to be `array[ offset ]`, y value to be `array[ offset + 1 ]` 5956 * and z value to be `array[ offset + 2 ]`. 5957 * 5958 * @param {Array<number>} array - An array holding the vector component values. 5959 * @param {number} [offset=0] - The offset into the array. 5960 * @return {Vector3} A reference to this vector. 5961 */ 5962 fromArray( array, offset = 0 ) { 5963 5964 this.x = array[ offset ]; 5965 this.y = array[ offset + 1 ]; 5966 this.z = array[ offset + 2 ]; 5967 5968 return this; 5969 5970 } 5971 5972 /** 5973 * Writes the components of this vector to the given array. If no array is provided, 5974 * the method returns a new instance. 5975 * 5976 * @param {Array<number>} [array=[]] - The target array holding the vector components. 5977 * @param {number} [offset=0] - Index of the first element in the array. 5978 * @return {Array<number>} The vector components. 5979 */ 5980 toArray( array = [], offset = 0 ) { 5981 5982 array[ offset ] = this.x; 5983 array[ offset + 1 ] = this.y; 5984 array[ offset + 2 ] = this.z; 5985 5986 return array; 5987 5988 } 5989 5990 /** 5991 * Sets the components of this vector from the given buffer attribute. 5992 * 5993 * @param {BufferAttribute} attribute - The buffer attribute holding vector data. 5994 * @param {number} index - The index into the attribute. 5995 * @return {Vector3} A reference to this vector. 5996 */ 5997 fromBufferAttribute( attribute, index ) { 5998 5999 this.x = attribute.getX( index ); 6000 this.y = attribute.getY( index ); 6001 this.z = attribute.getZ( index ); 6002 6003 return this; 6004 6005 } 6006 6007 /** 6008 * Sets each component of this vector to a pseudo-random value between `0` and 6009 * `1`, excluding `1`. 6010 * 6011 * @return {Vector3} A reference to this vector. 6012 */ 6013 random() { 6014 6015 this.x = Math.random(); 6016 this.y = Math.random(); 6017 this.z = Math.random(); 6018 6019 return this; 6020 6021 } 6022 6023 /** 6024 * Sets this vector to a uniformly random point on a unit sphere. 6025 * 6026 * @return {Vector3} A reference to this vector. 6027 */ 6028 randomDirection() { 6029 6030 // https://mathworld.wolfram.com/SpherePointPicking.html 6031 6032 const theta = Math.random() * Math.PI * 2; 6033 const u = Math.random() * 2 - 1; 6034 const c = Math.sqrt( 1 - u * u ); 6035 6036 this.x = c * Math.cos( theta ); 6037 this.y = u; 6038 this.z = c * Math.sin( theta ); 6039 6040 return this; 6041 6042 } 6043 6044 *[ Symbol.iterator ]() { 6045 6046 yield this.x; 6047 yield this.y; 6048 yield this.z; 6049 6050 } 6051 6052} 6053 6054const _vector$c = /*@__PURE__*/ new Vector3(); 6055const _quaternion$5 = /*@__PURE__*/ new Quaternion(); 6056 6057/** 6058 * Represents a 3x3 matrix. 6059 * 6060 * A Note on Row-Major and Column-Major Ordering: 6061 * 6062 * The constructor and {@link Matrix3#set} method take arguments in 6063 * [row-major](https://en.wikipedia.org/wiki/Row-_and_column-major_order#Column-major_order) 6064 * order, while internally they are stored in the {@link Matrix3#elements} array in column-major order. 6065 * This means that calling: 6066 * ```js 6067 * const m = new THREE.Matrix(); 6068 * m.set( 11, 12, 13, 6069 * 21, 22, 23, 6070 * 31, 32, 33 ); 6071 * ``` 6072 * will result in the elements array containing: 6073 * ```js 6074 * m.elements = [ 11, 21, 31, 6075 * 12, 22, 32, 6076 * 13, 23, 33 ]; 6077 * ``` 6078 * and internally all calculations are performed using column-major ordering. 6079 * However, as the actual ordering makes no difference mathematically and 6080 * most people are used to thinking about matrices in row-major order, the 6081 * three.js documentation shows matrices in row-major order. Just bear in 6082 * mind that if you are reading the source code, you'll have to take the 6083 * transpose of any matrices outlined here to make sense of the calculations. 6084 */ 6085class Matrix3 { 6086 6087 static { 6088 6089 /** 6090 * This flag can be used for type testing. 6091 * 6092 * @type {boolean} 6093 * @readonly 6094 * @default true 6095 */ 6096 Matrix3.prototype.isMatrix3 = true; 6097 6098 } 6099 6100 /** 6101 * Constructs a new 3x3 matrix. The arguments are supposed to be 6102 * in row-major order. If no arguments are provided, the constructor 6103 * initializes the matrix as an identity matrix. 6104 * 6105 * @param {number} [n11] - 1-1 matrix element. 6106 * @param {number} [n12] - 1-2 matrix element. 6107 * @param {number} [n13] - 1-3 matrix element. 6108 * @param {number} [n21] - 2-1 matrix element. 6109 * @param {number} [n22] - 2-2 matrix element. 6110 * @param {number} [n23] - 2-3 matrix element. 6111 * @param {number} [n31] - 3-1 matrix element. 6112 * @param {number} [n32] - 3-2 matrix element. 6113 * @param {number} [n33] - 3-3 matrix element. 6114 */ 6115 constructor( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { 6116 6117 /** 6118 * A column-major list of matrix values. 6119 * 6120 * @type {Array<number>} 6121 */ 6122 this.elements = [ 6123 6124 1, 0, 0, 6125 0, 1, 0, 6126 0, 0, 1 6127 6128 ]; 6129 6130 if ( n11 !== undefined ) { 6131 6132 this.set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ); 6133 6134 } 6135 6136 } 6137 6138 /** 6139 * Sets the elements of the matrix.The arguments are supposed to be 6140 * in row-major order. 6141 * 6142 * @param {number} [n11] - 1-1 matrix element. 6143 * @param {number} [n12] - 1-2 matrix element. 6144 * @param {number} [n13] - 1-3 matrix element. 6145 * @param {number} [n21] - 2-1 matrix element. 6146 * @param {number} [n22] - 2-2 matrix element. 6147 * @param {number} [n23] - 2-3 matrix element. 6148 * @param {number} [n31] - 3-1 matrix element. 6149 * @param {number} [n32] - 3-2 matrix element. 6150 * @param {number} [n33] - 3-3 matrix element. 6151 * @return {Matrix3} A reference to this matrix. 6152 */ 6153 set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { 6154 6155 const te = this.elements; 6156 6157 te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; 6158 te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; 6159 te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; 6160 6161 return this; 6162 6163 } 6164 6165 /** 6166 * Sets this matrix to the 3x3 identity matrix. 6167 * 6168 * @return {Matrix3} A reference to this matrix. 6169 */ 6170 identity() { 6171 6172 this.set( 6173 6174 1, 0, 0, 6175 0, 1, 0, 6176 0, 0, 1 6177 6178 ); 6179 6180 return this; 6181 6182 } 6183 6184 /** 6185 * Copies the values of the given matrix to this instance. 6186 * 6187 * @param {Matrix3} m - The matrix to copy. 6188 * @return {Matrix3} A reference to this matrix. 6189 */ 6190 copy( m ) { 6191 6192 const te = this.elements; 6193 const me = m.elements; 6194 6195 te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; 6196 te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; 6197 te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; 6198 6199 return this; 6200 6201 } 6202 6203 /** 6204 * Extracts the basis of this matrix into the three axis vectors provided. 6205 * 6206 * @param {Vector3} xAxis - The basis's x axis. 6207 * @param {Vector3} yAxis - The basis's y axis. 6208 * @param {Vector3} zAxis - The basis's z axis. 6209 * @return {Matrix3} A reference to this matrix. 6210 */ 6211 extractBasis( xAxis, yAxis, zAxis ) { 6212 6213 xAxis.setFromMatrix3Column( this, 0 ); 6214 yAxis.setFromMatrix3Column( this, 1 ); 6215 zAxis.setFromMatrix3Column( this, 2 ); 6216 6217 return this; 6218 6219 } 6220 6221 /** 6222 * Set this matrix to the upper 3x3 matrix of the given 4x4 matrix. 6223 * 6224 * @param {Matrix4} m - The 4x4 matrix. 6225 * @return {Matrix3} A reference to this matrix. 6226 */ 6227 setFromMatrix4( m ) { 6228 6229 const me = m.elements; 6230 6231 this.set( 6232 6233 me[ 0 ], me[ 4 ], me[ 8 ], 6234 me[ 1 ], me[ 5 ], me[ 9 ], 6235 me[ 2 ], me[ 6 ], me[ 10 ] 6236 6237 ); 6238 6239 return this; 6240 6241 } 6242 6243 /** 6244 * Post-multiplies this matrix by the given 3x3 matrix. 6245 * 6246 * @param {Matrix3} m - The matrix to multiply with. 6247 * @return {Matrix3} A reference to this matrix. 6248 */ 6249 multiply( m ) { 6250 6251 return this.multiplyMatrices( this, m ); 6252 6253 } 6254 6255 /** 6256 * Pre-multiplies this matrix by the given 3x3 matrix. 6257 * 6258 * @param {Matrix3} m - The matrix to multiply with. 6259 * @return {Matrix3} A reference to this matrix. 6260 */ 6261 premultiply( m ) { 6262 6263 return this.multiplyMatrices( m, this ); 6264 6265 } 6266 6267 /** 6268 * Multiples the given 3x3 matrices and stores the result 6269 * in this matrix. 6270 * 6271 * @param {Matrix3} a - The first matrix. 6272 * @param {Matrix3} b - The second matrix. 6273 * @return {Matrix3} A reference to this matrix. 6274 */ 6275 multiplyMatrices( a, b ) { 6276 6277 const ae = a.elements; 6278 const be = b.elements; 6279 const te = this.elements; 6280 6281 const a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; 6282 const a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; 6283 const a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; 6284 6285 const b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; 6286 const b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; 6287 const b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; 6288 6289 te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; 6290 te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; 6291 te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; 6292 6293 te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31; 6294 te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32; 6295 te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33; 6296 6297 te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31; 6298 te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32; 6299 te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33; 6300 6301 return this; 6302 6303 } 6304 6305 /** 6306 * Multiplies every component of the matrix by the given scalar. 6307 * 6308 * @param {number} s - The scalar. 6309 * @return {Matrix3} A reference to this matrix. 6310 */ 6311 multiplyScalar( s ) { 6312 6313 const te = this.elements; 6314 6315 te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; 6316 te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; 6317 te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; 6318 6319 return this; 6320 6321 } 6322 6323 /** 6324 * Computes and returns the determinant of this matrix. 6325 * 6326 * @return {number} The determinant. 6327 */ 6328 determinant() { 6329 6330 const te = this.elements; 6331 6332 const a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], 6333 d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], 6334 g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; 6335 6336 return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; 6337 6338 } 6339 6340 /** 6341 * Inverts this matrix, using the [analytic method](https://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution). 6342 * You can not invert with a determinant of zero. If you attempt this, the method produces 6343 * a zero matrix instead. 6344 * 6345 * @return {Matrix3} A reference to this matrix. 6346 */ 6347 invert() { 6348 6349 const te = this.elements, 6350 6351 n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], 6352 n12 = te[ 3 ], n22 = te[ 4 ], n32 = te[ 5 ], 6353 n13 = te[ 6 ], n23 = te[ 7 ], n33 = te[ 8 ], 6354 6355 t11 = n33 * n22 - n32 * n23, 6356 t12 = n32 * n13 - n33 * n12, 6357 t13 = n23 * n12 - n22 * n13, 6358 6359 det = n11 * t11 + n21 * t12 + n31 * t13; 6360 6361 if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 ); 6362 6363 const detInv = 1 / det; 6364 6365 te[ 0 ] = t11 * detInv; 6366 te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; 6367 te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; 6368 6369 te[ 3 ] = t12 * detInv; 6370 te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv; 6371 te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv; 6372 6373 te[ 6 ] = t13 * detInv; 6374 te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv; 6375 te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv; 6376 6377 return this; 6378 6379 } 6380 6381 /** 6382 * Transposes this matrix in place. 6383 * 6384 * @return {Matrix3} A reference to this matrix. 6385 */ 6386 transpose() { 6387 6388 let tmp; 6389 const m = this.elements; 6390 6391 tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; 6392 tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; 6393 tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; 6394 6395 return this; 6396 6397 } 6398 6399 /** 6400 * Computes the normal matrix which is the inverse transpose of the upper 6401 * left 3x3 portion of the given 4x4 matrix. 6402 * 6403 * @param {Matrix4} matrix4 - The 4x4 matrix. 6404 * @return {Matrix3} A reference to this matrix. 6405 */ 6406 getNormalMatrix( matrix4 ) { 6407 6408 return this.setFromMatrix4( matrix4 ).invert().transpose(); 6409 6410 } 6411 6412 /** 6413 * Transposes this matrix into the supplied array, and returns itself unchanged. 6414 * 6415 * @param {Array<number>} r - An array to store the transposed matrix elements. 6416 * @return {Matrix3} A reference to this matrix. 6417 */ 6418 transposeIntoArray( r ) { 6419 6420 const m = this.elements; 6421 6422 r[ 0 ] = m[ 0 ]; 6423 r[ 1 ] = m[ 3 ]; 6424 r[ 2 ] = m[ 6 ]; 6425 r[ 3 ] = m[ 1 ]; 6426 r[ 4 ] = m[ 4 ]; 6427 r[ 5 ] = m[ 7 ]; 6428 r[ 6 ] = m[ 2 ]; 6429 r[ 7 ] = m[ 5 ]; 6430 r[ 8 ] = m[ 8 ]; 6431 6432 return this; 6433 6434 } 6435 6436 /** 6437 * Sets the UV transform matrix from offset, repeat, rotation, and center. 6438 * 6439 * @param {number} tx - Offset x. 6440 * @param {number} ty - Offset y. 6441 * @param {number} sx - Repeat x. 6442 * @param {number} sy - Repeat y. 6443 * @param {number} rotation - Rotation, in radians. Positive values rotate counterclockwise. 6444 * @param {number} cx - Center x of rotation. 6445 * @param {number} cy - Center y of rotation 6446 * @return {Matrix3} A reference to this matrix. 6447 */ 6448 setUvTransform( tx, ty, sx, sy, rotation, cx, cy ) { 6449 6450 const c = Math.cos( rotation ); 6451 const s = Math.sin( rotation ); 6452 6453 this.set( 6454 sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx, 6455 - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty, 6456 0, 0, 1 6457 ); 6458 6459 return this; 6460 6461 } 6462 6463 /** 6464 * Scales this matrix with the given scalar values. 6465 * 6466 * @deprecated 6467 * @param {number} sx - The amount to scale in the X axis. 6468 * @param {number} sy - The amount to scale in the Y axis. 6469 * @return {Matrix3} A reference to this matrix. 6470 */ 6471 scale( sx, sy ) { 6472 6473 warnOnce( 'Matrix3: .scale() is deprecated. Use .makeScale() instead.' ); // @deprecated r185 6474 6475 this.premultiply( _m3.makeScale( sx, sy ) ); 6476 6477 return this; 6478 6479 } 6480 6481 /** 6482 * Rotates this matrix by the given angle. 6483 * 6484 * @deprecated 6485 * @param {number} theta - The rotation in radians. 6486 * @return {Matrix3} A reference to this matrix. 6487 */ 6488 rotate( theta ) { 6489 6490 warnOnce( 'Matrix3: .rotate() is deprecated. Use .makeRotation() instead.' ); // @deprecated r185 6491 6492 this.premultiply( _m3.makeRotation( - theta ) ); 6493 6494 return this; 6495 6496 } 6497 6498 /** 6499 * Translates this matrix by the given scalar values. 6500 * 6501 * @deprecated 6502 * @param {number} tx - The amount to translate in the X axis. 6503 * @param {number} ty - The amount to translate in the Y axis. 6504 * @return {Matrix3} A reference to this matrix. 6505 */ 6506 translate( tx, ty ) { 6507 6508 warnOnce( 'Matrix3: .translate() is deprecated. Use .makeTranslation() instead.' ); // @deprecated r185 6509 6510 this.premultiply( _m3.makeTranslation( tx, ty ) ); 6511 6512 return this; 6513 6514 } 6515 6516 // for 2D Transforms 6517 6518 /** 6519 * Sets this matrix as a 2D translation transform. 6520 * 6521 * @param {number|Vector2} x - The amount to translate in the X axis or alternatively a translation vector. 6522 * @param {number} y - The amount to translate in the Y axis. 6523 * @return {Matrix3} A reference to this matrix. 6524 */ 6525 makeTranslation( x, y ) { 6526 6527 if ( x.isVector2 ) { 6528 6529 this.set( 6530 6531 1, 0, x.x, 6532 0, 1, x.y, 6533 0, 0, 1 6534 6535 ); 6536 6537 } else { 6538 6539 this.set( 6540 6541 1, 0, x, 6542 0, 1, y, 6543 0, 0, 1 6544 6545 ); 6546 6547 } 6548 6549 return this; 6550 6551 } 6552 6553 /** 6554 * Sets this matrix as a 2D rotational transformation. 6555 * 6556 * @param {number} theta - The rotation in radians. 6557 * @return {Matrix3} A reference to this matrix. 6558 */ 6559 makeRotation( theta ) { 6560 6561 // counterclockwise 6562 6563 const c = Math.cos( theta ); 6564 const s = Math.sin( theta ); 6565 6566 this.set( 6567 6568 c, - s, 0, 6569 s, c, 0, 6570 0, 0, 1 6571 6572 ); 6573 6574 return this; 6575 6576 } 6577 6578 /** 6579 * Sets this matrix as a 2D scale transform. 6580 * 6581 * @param {number} x - The amount to scale in the X axis. 6582 * @param {number} y - The amount to scale in the Y axis. 6583 * @return {Matrix3} A reference to this matrix. 6584 */ 6585 makeScale( x, y ) { 6586 6587 this.set( 6588 6589 x, 0, 0, 6590 0, y, 0, 6591 0, 0, 1 6592 6593 ); 6594 6595 return this; 6596 6597 } 6598 6599 /** 6600 * Returns `true` if this matrix is equal with the given one. 6601 * 6602 * @param {Matrix3} matrix - The matrix to test for equality. 6603 * @return {boolean} Whether this matrix is equal with the given one. 6604 */ 6605 equals( matrix ) { 6606 6607 const te = this.elements; 6608 const me = matrix.elements; 6609 6610 for ( let i = 0; i < 9; i ++ ) { 6611 6612 if ( te[ i ] !== me[ i ] ) return false; 6613 6614 } 6615 6616 return true; 6617 6618 } 6619 6620 /** 6621 * Sets the elements of the matrix from the given array. 6622 * 6623 * @param {Array<number>} array - The matrix elements in column-major order. 6624 * @param {number} [offset=0] - Index of the first element in the array. 6625 * @return {Matrix3} A reference to this matrix. 6626 */ 6627 fromArray( array, offset = 0 ) { 6628 6629 for ( let i = 0; i < 9; i ++ ) { 6630 6631 this.elements[ i ] = array[ i + offset ]; 6632 6633 } 6634 6635 return this; 6636 6637 } 6638 6639 /** 6640 * Writes the elements of this matrix to the given array. If no array is provided, 6641 * the method returns a new instance. 6642 * 6643 * @param {Array<number>} [array=[]] - The target array holding the matrix elements in column-major order. 6644 * @param {number} [offset=0] - Index of the first element in the array. 6645 * @return {Array<number>} The matrix elements in column-major order. 6646 */ 6647 toArray( array = [], offset = 0 ) { 6648 6649 const te = this.elements; 6650 6651 array[ offset ] = te[ 0 ]; 6652 array[ offset + 1 ] = te[ 1 ]; 6653 array[ offset + 2 ] = te[ 2 ]; 6654 6655 array[ offset + 3 ] = te[ 3 ]; 6656 array[ offset + 4 ] = te[ 4 ]; 6657 array[ offset + 5 ] = te[ 5 ]; 6658 6659 array[ offset + 6 ] = te[ 6 ]; 6660 array[ offset + 7 ] = te[ 7 ]; 6661 array[ offset + 8 ] = te[ 8 ]; 6662 6663 return array; 6664 6665 } 6666 6667 /** 6668 * Returns a matrix with copied values from this instance. 6669 * 6670 * @return {Matrix3} A clone of this instance. 6671 */ 6672 clone() { 6673 6674 return new this.constructor().fromArray( this.elements ); 6675 6676 } 6677 6678} 6679 6680const _m3 = /*@__PURE__*/ new Matrix3(); 6681 6682const LINEAR_REC709_TO_XYZ = /*@__PURE__*/ new Matrix3().set( 6683 0.4123908, 0.3575843, 0.1804808, 6684 0.2126390, 0.7151687, 0.0721923, 6685 0.0193308, 0.1191948, 0.9505322 6686); 6687 6688const XYZ_TO_LINEAR_REC709 = /*@__PURE__*/ new Matrix3().set( 6689 3.2409699, -1.5373832, -0.4986108, 6690 -0.9692436, 1.8759675, 0.0415551, 6691 0.0556301, -0.203977, 1.0569715 6692); 6693 6694function createColorManagement() { 6695 6696 const ColorManagement = { 6697 6698 enabled: true, 6699 6700 workingColorSpace: LinearSRGBColorSpace, 6701 6702 /** 6703 * Implementations of supported color spaces. 6704 * 6705 * Required: 6706 * - primaries: chromaticity coordinates [ rx ry gx gy bx by ] 6707 * - whitePoint: reference white [ x y ] 6708 * - transfer: transfer function (pre-defined) 6709 * - toXYZ: Matrix3 RGB to XYZ transform 6710 * - fromXYZ: Matrix3 XYZ to RGB transform 6711 * - luminanceCoefficients: RGB luminance coefficients 6712 * 6713 * Optional: 6714 * - outputColorSpaceConfig: { drawingBufferColorSpace: ColorSpace, toneMappingMode: 'extended' | 'standard' } 6715 * - workingColorSpaceConfig: { unpackColorSpace: ColorSpace } 6716 * 6717 * Reference: 6718 * - https://www.russellcottrell.com/photo/matrixCalculator.htm 6719 */ 6720 spaces: {}, 6721 6722 convert: function ( color, sourceColorSpace, targetColorSpace ) { 6723 6724 if ( this.enabled === false || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) { 6725 6726 return color; 6727 6728 } 6729 6730 if ( this.spaces[ sourceColorSpace ].transfer === SRGBTransfer ) { 6731 6732 color.r = SRGBToLinear( color.r ); 6733 color.g = SRGBToLinear( color.g ); 6734 color.b = SRGBToLinear( color.b ); 6735 6736 } 6737 6738 if ( this.spaces[ sourceColorSpace ].primaries !== this.spaces[ targetColorSpace ].primaries ) { 6739 6740 color.applyMatrix3( this.spaces[ sourceColorSpace ].toXYZ ); 6741 color.applyMatrix3( this.spaces[ targetColorSpace ].fromXYZ ); 6742 6743 } 6744 6745 if ( this.spaces[ targetColorSpace ].transfer === SRGBTransfer ) { 6746 6747 color.r = LinearToSRGB( color.r ); 6748 color.g = LinearToSRGB( color.g ); 6749 color.b = LinearToSRGB( color.b ); 6750 6751 } 6752 6753 return color; 6754 6755 }, 6756 6757 workingToColorSpace: function ( color, targetColorSpace ) { 6758 6759 return this.convert( color, this.workingColorSpace, targetColorSpace ); 6760 6761 }, 6762 6763 colorSpaceToWorking: function ( color, sourceColorSpace ) { 6764 6765 return this.convert( color, sourceColorSpace, this.workingColorSpace ); 6766 6767 }, 6768 6769 getPrimaries: function ( colorSpace ) { 6770 6771 return this.spaces[ colorSpace ].primaries; 6772 6773 }, 6774 6775 getTransfer: function ( colorSpace ) { 6776 6777 if ( colorSpace === NoColorSpace ) return LinearTransfer; 6778 6779 return this.spaces[ colorSpace ].transfer; 6780 6781 }, 6782 6783 getToneMappingMode: function ( colorSpace ) { 6784 6785 return this.spaces[ colorSpace ].outputColorSpaceConfig.toneMappingMode || 'standard'; 6786 6787 }, 6788 6789 getLuminanceCoefficients: function ( target, colorSpace = this.workingColorSpace ) { 6790 6791 return target.fromArray( this.spaces[ colorSpace ].luminanceCoefficients ); 6792 6793 }, 6794 6795 define: function ( colorSpaces ) { 6796 6797 Object.assign( this.spaces, colorSpaces ); 6798 6799 }, 6800 6801 // Internal APIs 6802 6803 _getMatrix: function ( targetMatrix, sourceColorSpace, targetColorSpace ) { 6804 6805 return targetMatrix 6806 .copy( this.spaces[ sourceColorSpace ].toXYZ ) 6807 .multiply( this.spaces[ targetColorSpace ].fromXYZ ); 6808 6809 }, 6810 6811 _getDrawingBufferColorSpace: function ( colorSpace ) { 6812 6813 return this.spaces[ colorSpace ].outputColorSpaceConfig.drawingBufferColorSpace; 6814 6815 }, 6816 6817 _getUnpackColorSpace: function ( colorSpace = this.workingColorSpace ) { 6818 6819 return this.spaces[ colorSpace ].workingColorSpaceConfig.unpackColorSpace; 6820 6821 }, 6822 6823 // Deprecated 6824 6825 fromWorkingColorSpace: function ( color, targetColorSpace ) { 6826 6827 warnOnce( 'ColorManagement: .fromWorkingColorSpace() has been renamed to .workingToColorSpace().' ); // @deprecated, r177 6828 6829 return ColorManagement.workingToColorSpace( color, targetColorSpace ); 6830 6831 }, 6832 6833 toWorkingColorSpace: function ( color, sourceColorSpace ) { 6834 6835 warnOnce( 'ColorManagement: .toWorkingColorSpace() has been renamed to .colorSpaceToWorking().' ); // @deprecated, r177 6836 6837 return ColorManagement.colorSpaceToWorking( color, sourceColorSpace ); 6838 6839 }, 6840 6841 }; 6842 6843 /****************************************************************************** 6844 * sRGB definitions 6845 */ 6846 6847 const REC709_PRIMARIES = [ 0.640, 0.330, 0.300, 0.600, 0.150, 0.060 ]; 6848 const REC709_LUMINANCE_COEFFICIENTS = [ 0.2126, 0.7152, 0.0722 ]; 6849 const D65 = [ 0.3127, 0.3290 ]; 6850 6851 ColorManagement.define( { 6852 6853 [ LinearSRGBColorSpace ]: { 6854 primaries: REC709_PRIMARIES, 6855 whitePoint: D65, 6856 transfer: LinearTransfer, 6857 toXYZ: LINEAR_REC709_TO_XYZ, 6858 fromXYZ: XYZ_TO_LINEAR_REC709, 6859 luminanceCoefficients: REC709_LUMINANCE_COEFFICIENTS, 6860 workingColorSpaceConfig: { unpackColorSpace: SRGBColorSpace }, 6861 outputColorSpaceConfig: { drawingBufferColorSpace: SRGBColorSpace } 6862 }, 6863 6864 [ SRGBColorSpace ]: { 6865 primaries: REC709_PRIMARIES, 6866 whitePoint: D65, 6867 transfer: SRGBTransfer, 6868 toXYZ: LINEAR_REC709_TO_XYZ, 6869 fromXYZ: XYZ_TO_LINEAR_REC709, 6870 luminanceCoefficients: REC709_LUMINANCE_COEFFICIENTS, 6871 outputColorSpaceConfig: { drawingBufferColorSpace: SRGBColorSpace } 6872 }, 6873 6874 } ); 6875 6876 return ColorManagement; 6877 6878} 6879 6880const ColorManagement = /*@__PURE__*/ createColorManagement(); 6881 6882function SRGBToLinear( c ) { 6883 6884 return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 ); 6885 6886} 6887 6888function LinearToSRGB( c ) { 6889 6890 return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055; 6891 6892} 6893 6894let _canvas; 6895 6896/** 6897 * A class containing utility functions for images. 6898 * 6899 * @hideconstructor 6900 */ 6901class ImageUtils { 6902 6903 /** 6904 * Returns a data URI containing a representation of the given image. 6905 * 6906 * @param {(HTMLImageElement|HTMLCanvasElement)} image - The image object. 6907 * @param {string} [type='image/png'] - Indicates the image format. 6908 * @return {string} The data URI. 6909 */ 6910 static getDataURL( image, type = 'image/png' ) { 6911 6912 if ( /^data:/i.test( image.src ) ) { 6913 6914 return image.src; 6915 6916 } 6917 6918 if ( typeof HTMLCanvasElement === 'undefined' ) { 6919 6920 return image.src; 6921 6922 } 6923 6924 let canvas; 6925 6926 if ( image instanceof HTMLCanvasElement ) { 6927 6928 canvas = image; 6929 6930 } else { 6931 6932 if ( _canvas === undefined ) _canvas = createElementNS( 'canvas' ); 6933 6934 _canvas.width = image.width; 6935 _canvas.height = image.height; 6936 6937 const context = _canvas.getContext( '2d' ); 6938 6939 if ( image instanceof ImageData ) { 6940 6941 context.putImageData( image, 0, 0 ); 6942 6943 } else { 6944 6945 context.drawImage( image, 0, 0, image.width, image.height ); 6946 6947 } 6948 6949 canvas = _canvas; 6950 6951 } 6952 6953 return canvas.toDataURL( type ); 6954 6955 } 6956 6957 /** 6958 * Converts the given sRGB image data to linear color space. 6959 * 6960 * @param {(HTMLImageElement|HTMLCanvasElement|ImageBitmap|Object)} image - The image object. 6961 * @return {HTMLCanvasElement|Object} The converted image. 6962 */ 6963 static sRGBToLinear( image ) { 6964 6965 if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || 6966 ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || 6967 ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { 6968 6969 const canvas = createElementNS( 'canvas' ); 6970 6971 canvas.width = image.width; 6972 canvas.height = image.height; 6973 6974 const context = canvas.getContext( '2d' ); 6975 context.drawImage( image, 0, 0, image.width, image.height ); 6976 6977 const imageData = context.getImageData( 0, 0, image.width, image.height ); 6978 const data = imageData.data; 6979 6980 for ( let i = 0; i < data.length; i ++ ) { 6981 6982 data[ i ] = SRGBToLinear( data[ i ] / 255 ) * 255; 6983 6984 } 6985 6986 context.putImageData( imageData, 0, 0 ); 6987 6988 return canvas; 6989 6990 } else if ( image.data ) { 6991 6992 const data = image.data.slice( 0 ); 6993 6994 for ( let i = 0; i < data.length; i ++ ) { 6995 6996 if ( data instanceof Uint8Array || data instanceof Uint8ClampedArray ) { 6997 6998 data[ i ] = Math.floor( SRGBToLinear( data[ i ] / 255 ) * 255 ); 6999 7000 } else { 7001 7002 // assuming float 7003 7004 data[ i ] = SRGBToLinear( data[ i ] ); 7005 7006 } 7007 7008 } 7009 7010 return { 7011 data: data, 7012 width: image.width, 7013 height: image.height 7014 }; 7015 7016 } else { 7017 7018 warn( 'ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.' ); 7019 return image; 7020 7021 } 7022 7023 } 7024 7025} 7026 7027let _sourceId = 0; 7028 7029/** 7030 * Represents the data source of a texture. 7031 * 7032 * The main purpose of this class is to decouple the data definition from the texture 7033 * definition so the same data can be used with multiple texture instances. 7034 */ 7035class TextureSource { 7036 7037 /** 7038 * Constructs a new texture source. 7039 * 7040 * @param {any} [data=null] - The data definition of a texture. 7041 */ 7042 constructor( data = null ) { 7043 7044 /** 7045 * This flag can be used for type testing. 7046 * 7047 * @type {boolean} 7048 * @readonly 7049 * @default true 7050 */ 7051 this.isTextureSource = true; 7052 7053 /** 7054 * The ID of the source. 7055 * 7056 * @name TextureSource#id 7057 * @type {number} 7058 * @readonly 7059 */ 7060 Object.defineProperty( this, 'id', { value: _sourceId ++ } ); 7061 7062 /** 7063 * The UUID of the source. 7064 * 7065 * @type {string} 7066 * @readonly 7067 */ 7068 this.uuid = generateUUID(); 7069 7070 /** 7071 * The data definition of a texture. 7072 * 7073 * @type {any} 7074 */ 7075 this.data = data; 7076 7077 /** 7078 * This property is only relevant when {@link TextureSource#needsUpdate} is set to `true` and 7079 * provides more control on how texture data should be processed. When `dataReady` is set 7080 * to `false`, the engine performs the memory allocation (if necessary) but does not transfer 7081 * the data into the GPU memory. 7082 * 7083 * @type {boolean} 7084 * @default true 7085 */ 7086 this.dataReady = true; 7087 7088 /** 7089 * This starts at `0` and counts how many times {@link TextureSource#needsUpdate} is set to `true`. 7090 * 7091 * @type {number} 7092 * @readonly 7093 * @default 0 7094 */ 7095 this.version = 0; 7096 7097 } 7098 7099 /** 7100 * Returns the dimensions of the source into the given target vector. 7101 * 7102 * @param {(Vector2|Vector3)} target - The target object the result is written into. 7103 * @return {(Vector2|Vector3)} The dimensions of the source. 7104 */ 7105 getSize( target ) { 7106 7107 const data = this.data; 7108 7109 if ( ( typeof HTMLVideoElement !== 'undefined' ) && ( data instanceof HTMLVideoElement ) ) { 7110 7111 target.set( data.videoWidth, data.videoHeight, 0 ); 7112 7113 } else if ( ( typeof VideoFrame !== 'undefined' ) && ( data instanceof VideoFrame ) ) { 7114 7115 target.set( data.displayWidth, data.displayHeight, 0 ); 7116 7117 } else if ( data !== null ) { 7118 7119 target.set( data.width, data.height, data.depth || 0 ); 7120 7121 } else { 7122 7123 target.set( 0, 0, 0 ); 7124 7125 } 7126 7127 return target; 7128 7129 } 7130 7131 /** 7132 * When the property is set to `true`, the engine allocates the memory 7133 * for the texture (if necessary) and triggers the actual texture upload 7134 * to the GPU next time the source is used. 7135 * 7136 * @type {boolean} 7137 * @default false 7138 * @param {boolean} value 7139 */ 7140 set needsUpdate( value ) { 7141 7142 if ( value === true ) this.version ++; 7143 7144 } 7145 7146 /** 7147 * Serializes the source into JSON. 7148 * 7149 * @param {?(Object|string)} meta - An optional value holding meta information about the serialization. 7150 * @return {Object} A JSON object representing the serialized source. 7151 * @see {@link ObjectLoader#parse} 7152 */ 7153 toJSON( meta ) { 7154 7155 const isRootObject = ( meta === undefined || typeof meta === 'string' ); 7156 7157 if ( ! isRootObject && meta.images[ this.uuid ] !== undefined ) { 7158 7159 return meta.images[ this.uuid ]; 7160 7161 } 7162 7163 const output = { 7164 uuid: this.uuid, 7165 url: '' 7166 }; 7167 7168 const data = this.data; 7169 7170 if ( data !== null ) { 7171 7172 let url; 7173 7174 if ( Array.isArray( data ) ) { 7175 7176 // cube texture 7177 7178 url = []; 7179 7180 for ( let i = 0, l = data.length; i < l; i ++ ) { 7181 7182 if ( data[ i ].isDataTexture ) { 7183 7184 url.push( serializeImage( data[ i ].image ) ); 7185 7186 } else { 7187 7188 url.push( serializeImage( data[ i ] ) ); 7189 7190 } 7191 7192 } 7193 7194 } else { 7195 7196 // texture 7197 7198 url = serializeImage( data ); 7199 7200 } 7201 7202 output.url = url; 7203 7204 } 7205 7206 if ( ! isRootObject ) { 7207 7208 meta.images[ this.uuid ] = output; 7209 7210 } 7211 7212 return output; 7213 7214 } 7215 7216} 7217 7218function serializeImage( image ) { 7219 7220 if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || 7221 ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || 7222 ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { 7223 7224 // default images 7225 7226 return ImageUtils.getDataURL( image ); 7227 7228 } else { 7229 7230 if ( image.data ) { 7231 7232 // images of DataTexture 7233 7234 return { 7235 data: Array.from( image.data ), 7236 width: image.width, 7237 height: image.height, 7238 type: image.data.constructor.name 7239 }; 7240 7241 } else { 7242 7243 warn( 'Texture: Unable to serialize Texture.' ); 7244 return {}; 7245 7246 } 7247 7248 } 7249 7250} 7251 7252/** 7253 * @deprecated since r186. Use {@link TextureSource} instead. `Source` has been renamed to `TextureSource`. 7254 */ 7255class Source extends TextureSource { 7256 7257 /** 7258 * Constructs a new texture source. 7259 * 7260 * @param {any} [data=null] - The data definition of a texture. 7261 * @deprecated since r186. Use {@link TextureSource} instead. 7262 */ 7263 constructor( data = null ) { 7264 7265 warnOnce( 'Source: "Source" has been renamed to "TextureSource". Please update your code to use "THREE.TextureSource" instead.' ); // @deprecated, r186 7266 7267 super( data ); 7268 7269 /** 7270 * This flag can be used for type testing. 7271 * 7272 * @deprecated since r186. Use {@link TextureSource#isTextureSource} instead. 7273 * @type {boolean} 7274 * @readonly 7275 * @default true 7276 */ 7277 this.isSource = true; 7278 7279 } 7280 7281} 7282 7283let _textureId = 0; 7284 7285const _tempVec3 = /*@__PURE__*/ new Vector3(); 7286 7287/** 7288 * Base class for all textures. 7289 * 7290 * Note: After the initial use of a texture, its dimensions, format, and type 7291 * cannot be changed. Instead, call {@link Texture#dispose} on the texture and instantiate a new one. 7292 * 7293 * @augments EventDispatcher 7294 */ 7295class Texture extends EventDispatcher { 7296 7297 /** 7298 * Constructs a new texture. 7299 * 7300 * @param {?Object} [image=Texture.DEFAULT_IMAGE] - The image holding the texture data. 7301 * @param {number} [mapping=Texture.DEFAULT_MAPPING] - The texture mapping. 7302 * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value. 7303 * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value. 7304 * @param {number} [magFilter=LinearFilter] - The mag filter value. 7305 * @param {number} [minFilter=LinearMipmapLinearFilter] - The min filter value. 7306 * @param {number} [format=RGBAFormat] - The texture format. 7307 * @param {number} [type=UnsignedByteType] - The texture type. 7308 * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value. 7309 * @param {string} [colorSpace=NoColorSpace] - The color space. 7310 */ 7311 constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = Texture.DEFAULT_ANISOTROPY, colorSpace = NoColorSpace ) { 7312 7313 super(); 7314 7315 /** 7316 * This flag can be used for type testing. 7317 * 7318 * @type {boolean} 7319 * @readonly 7320 * @default true 7321 */ 7322 this.isTexture = true; 7323 7324 /** 7325 * The ID of the texture. 7326 * 7327 * @name Texture#id 7328 * @type {number} 7329 * @readonly 7330 */ 7331 Object.defineProperty( this, 'id', { value: _textureId ++ } ); 7332 7333 /** 7334 * The UUID of the texture. 7335 * 7336 * @type {string} 7337 * @readonly 7338 */ 7339 this.uuid = generateUUID(); 7340 7341 /** 7342 * The name of the texture. 7343 * 7344 * @type {string} 7345 */ 7346 this.name = ''; 7347 7348 /** 7349 * The data definition of a texture. A reference to the data source can be 7350 * shared across textures. This is often useful in context of spritesheets 7351 * where multiple textures render the same data but with different texture 7352 * transformations. 7353 * 7354 * @type {TextureSource} 7355 */ 7356 this.source = new TextureSource( image ); 7357 7358 /** 7359 * An array holding user-defined mipmaps. 7360 * 7361 * @type {Array<Object>} 7362 */ 7363 this.mipmaps = []; 7364 7365 /** 7366 * How the texture is applied to the object. The value `UVMapping` 7367 * is the default, where texture or uv coordinates are used to apply the map. 7368 * 7369 * @type {(UVMapping|CubeReflectionMapping|CubeRefractionMapping|EquirectangularReflectionMapping|EquirectangularRefractionMapping|CubeUVReflectionMapping)} 7370 * @default UVMapping 7371 */ 7372 this.mapping = mapping; 7373 7374 /** 7375 * Lets you select the uv attribute to map the texture to. `0` for `uv`, 7376 * `1` for `uv1`, `2` for `uv2` and `3` for `uv3`. 7377 * 7378 * @type {number} 7379 * @default 0 7380 */ 7381 this.channel = 0; 7382 7383 /** 7384 * This defines how the texture is wrapped horizontally and corresponds to 7385 * *U* in UV mapping. 7386 * 7387 * @type {(RepeatWrapping|ClampToEdgeWrapping|MirroredRepeatWrapping)} 7388 * @default ClampToEdgeWrapping 7389 */ 7390 this.wrapS = wrapS; 7391 7392 /** 7393 * This defines how the texture is wrapped horizontally and corresponds to 7394 * *V* in UV mapping. 7395 * 7396 * @type {(RepeatWrapping|ClampToEdgeWrapping|MirroredRepeatWrapping)} 7397 * @default ClampToEdgeWrapping 7398 */ 7399 this.wrapT = wrapT; 7400 7401 /** 7402 * How the texture is sampled when a texel covers more than one pixel. 7403 * 7404 * @type {(NearestFilter|NearestMipmapNearestFilter|NearestMipmapLinearFilter|LinearFilter|LinearMipmapNearestFilter|LinearMipmapLinearFilter)} 7405 * @default LinearFilter 7406 */ 7407 this.magFilter = magFilter; 7408 7409 /** 7410 * How the texture is sampled when a texel covers less than one pixel. 7411 * 7412 * @type {(NearestFilter|NearestMipmapNearestFilter|NearestMipmapLinearFilter|LinearFilter|LinearMipmapNearestFilter|LinearMipmapLinearFilter)} 7413 * @default LinearMipmapLinearFilter 7414 */ 7415 this.minFilter = minFilter; 7416 7417 /** 7418 * The number of samples taken along the axis through the pixel that has the 7419 * highest density of texels. By default, this value is `1`. A higher value 7420 * gives a less blurry result than a basic mipmap, at the cost of more 7421 * texture samples being used. 7422 * 7423 * @type {number} 7424 * @default Texture.DEFAULT_ANISOTROPY 7425 */ 7426 this.anisotropy = anisotropy; 7427 7428 /** 7429 * The format of the texture. 7430 * 7431 * @type {number} 7432 * @default RGBAFormat 7433 */ 7434 this.format = format; 7435 7436 /** 7437 * The default internal format is derived from {@link Texture#format} and {@link Texture#type} and 7438 * defines how the texture data is going to be stored on the GPU. 7439 * 7440 * This property allows to overwrite the default format. 7441 * 7442 * @type {?string} 7443 * @default null 7444 */ 7445 this.internalFormat = null; 7446 7447 /** 7448 * The data type of the texture. 7449 * 7450 * @type {number} 7451 * @default UnsignedByteType 7452 */ 7453 this.type = type; 7454 7455 /** 7456 * How much a single repetition of the texture is offset from the beginning, 7457 * in each direction U and V. Typical range is `0.0` to `1.0`. 7458 * 7459 * @type {Vector2} 7460 * @default (0,0) 7461 */ 7462 this.offset = new Vector2( 0, 0 ); 7463 7464 /** 7465 * How many times the texture is repeated across the surface, in each 7466 * direction U and V. If repeat is set greater than `1` in either direction, 7467 * the corresponding wrap parameter should also be set to `RepeatWrapping` 7468 * or `MirroredRepeatWrapping` to achieve the desired tiling effect. 7469 * 7470 * @type {Vector2} 7471 * @default (1,1) 7472 */ 7473 this.repeat = new Vector2( 1, 1 ); 7474 7475 /** 7476 * The point around which rotation occurs. A value of `(0.5, 0.5)` corresponds 7477 * to the center of the texture. Default is `(0, 0)`, the lower left. 7478 * 7479 * @type {Vector2} 7480 * @default (0,0) 7481 */ 7482 this.center = new Vector2( 0, 0 ); 7483 7484 /** 7485 * How much the texture is rotated around the center point, in radians. 7486 * Positive values are counter-clockwise. 7487 * 7488 * @type {number} 7489 * @default 0 7490 */ 7491 this.rotation = 0; 7492 7493 /** 7494 * Whether to update the texture's uv-transformation {@link Texture#matrix} 7495 * from the properties {@link Texture#offset}, {@link Texture#repeat}, 7496 * {@link Texture#rotation}, and {@link Texture#center}. 7497 * 7498 * Set this to `false` if you are specifying the uv-transform matrix directly. 7499 * 7500 * @type {boolean} 7501 * @default true 7502 */ 7503 this.matrixAutoUpdate = true; 7504 7505 /** 7506 * The uv-transformation matrix of the texture. 7507 * 7508 * @type {Matrix3} 7509 */ 7510 this.matrix = new Matrix3(); 7511 7512 /** 7513 * Whether to generate mipmaps (if possible) for a texture. 7514 * 7515 * Set this to `false` if you are creating mipmaps manually. 7516 * 7517 * @type {boolean} 7518 * @default true 7519 */ 7520 this.generateMipmaps = true; 7521 7522 /** 7523 * If set to `true`, the alpha channel, if present, is multiplied into the 7524 * color channels when the texture is uploaded to the GPU. 7525 * 7526 * Note that this property has no effect when using `ImageBitmap`. You need to 7527 * configure premultiply alpha on bitmap creation instead. 7528 * 7529 * @type {boolean} 7530 * @default false 7531 */ 7532 this.premultiplyAlpha = false; 7533 7534 /** 7535 * If set to `true`, the texture is flipped along the vertical axis when 7536 * uploaded to the GPU. 7537 * 7538 * Note that this property has no effect when using `ImageBitmap`. You need to 7539 * configure the flip on bitmap creation instead. 7540 * 7541 * @type {boolean} 7542 * @default true 7543 */ 7544 this.flipY = true; 7545 7546 /** 7547 * Specifies the alignment requirements for the start of each pixel row in memory. 7548 * The allowable values are `1` (byte-alignment), `2` (rows aligned to even-numbered bytes), 7549 * `4` (word-alignment), and `8` (rows start on double-word boundaries). 7550 * 7551 * @type {number} 7552 * @default 4 7553 */ 7554 this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) 7555 7556 /** 7557 * Textures containing color data should be annotated with `SRGBColorSpace` or `LinearSRGBColorSpace`. 7558 * 7559 * @type {string} 7560 * @default NoColorSpace 7561 */ 7562 this.colorSpace = colorSpace; 7563 7564 /** 7565 * An object that can be used to store custom data about the texture. It 7566 * should not hold references to functions as these will not be cloned. 7567 * 7568 * @type {Object} 7569 */ 7570 this.userData = {}; 7571 7572 /** 7573 * This can be used to only update a subregion or specific rows of the texture (for example, just the 7574 * first 3 rows). Use the `addUpdateRange()` function to add ranges to this array. 7575 * 7576 * @type {Array<Object>} 7577 */ 7578 this.updateRanges = []; 7579 7580 /** 7581 * This starts at `0` and counts how many times {@link Texture#needsUpdate} is set to `true`. 7582 * 7583 * @type {number} 7584 * @readonly 7585 * @default 0 7586 */ 7587 this.version = 0; 7588 7589 /** 7590 * A callback function, called when the texture is updated (e.g., when 7591 * {@link Texture#needsUpdate} has been set to true and then the texture is used). 7592 * 7593 * @type {?Function} 7594 * @default null 7595 */ 7596 this.onUpdate = null; 7597 7598 /** 7599 * An optional back reference to the textures render target. 7600 * 7601 * @type {?(RenderTarget|WebGLRenderTarget)} 7602 * @default null 7603 */ 7604 this.renderTarget = null; 7605 7606 /** 7607 * Indicates whether a texture belongs to a render target or not. 7608 * 7609 * @type {boolean} 7610 * @readonly 7611 * @default false 7612 */ 7613 this.isRenderTargetTexture = false; 7614 7615 /** 7616 * Indicates if a texture should be handled like a texture array. 7617 * 7618 * @type {boolean} 7619 * @readonly 7620 * @default false 7621 */ 7622 this.isArrayTexture = image && image.depth && image.depth > 1 ? true : false; 7623 7624 /** 7625 * Indicates whether this texture should be processed by `PMREMGenerator` or not 7626 * (only relevant for render target textures). 7627 * 7628 * @type {number} 7629 * @readonly 7630 * @default 0 7631 */ 7632 this.pmremVersion = 0; 7633 7634 /** 7635 * Whether the texture should use one of the 16 bit integer formats which are normalized 7636 * to [0, 1] or [-1, 1] (depending on signed/unsigned) when sampled. 7637 * 7638 * @type {boolean} 7639 * @default false 7640 */ 7641 this.normalized = false; 7642 7643 } 7644 7645 /** 7646 * The width of the texture in pixels. 7647 */ 7648 get width() { 7649 7650 return this.source.getSize( _tempVec3 ).x; 7651 7652 } 7653 7654 /** 7655 * The height of the texture in pixels. 7656 */ 7657 get height() { 7658 7659 return this.source.getSize( _tempVec3 ).y; 7660 7661 } 7662 7663 /** 7664 * The depth of the texture in pixels. 7665 */ 7666 get depth() { 7667 7668 return this.source.getSize( _tempVec3 ).z; 7669 7670 } 7671 7672 /** 7673 * The image object holding the texture data. 7674 * 7675 * @type {?Object} 7676 */ 7677 get image() { 7678 7679 return this.source.data; 7680 7681 } 7682 7683 set image( value ) { 7684 7685 this.source.data = value; 7686 7687 } 7688 7689 /** 7690 * Updates the texture transformation matrix from the properties {@link Texture#offset}, 7691 * {@link Texture#repeat}, {@link Texture#rotation}, and {@link Texture#center}. 7692 */ 7693 updateMatrix() { 7694 7695 this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); 7696 7697 } 7698 7699 /** 7700 * Adds a range of data in the data texture to be updated on the GPU. 7701 * 7702 * @param {number} start - Position at which to start update. 7703 * @param {number} count - The number of components to update. 7704 */ 7705 addUpdateRange( start, count ) { 7706 7707 this.updateRanges.push( { start, count } ); 7708 7709 } 7710 7711 /** 7712 * Clears the update ranges. 7713 */ 7714 clearUpdateRanges() { 7715 7716 this.updateRanges.length = 0; 7717 7718 } 7719 7720 /** 7721 * Returns a new texture with copied values from this instance. 7722 * 7723 * @return {Texture} A clone of this instance. 7724 */ 7725 clone() { 7726 7727 return new this.constructor().copy( this ); 7728 7729 } 7730 7731 /** 7732 * Copies the values of the given texture to this instance. 7733 * 7734 * @param {Texture} source - The texture to copy. 7735 * @return {Texture} A reference to this instance. 7736 */ 7737 copy( source ) { 7738 7739 this.name = source.name; 7740 7741 this.source = source.source; 7742 this.mipmaps = source.mipmaps.slice( 0 ); 7743 7744 this.mapping = source.mapping; 7745 this.channel = source.channel; 7746 7747 this.wrapS = source.wrapS; 7748 this.wrapT = source.wrapT; 7749 7750 this.magFilter = source.magFilter; 7751 this.minFilter = source.minFilter; 7752 7753 this.anisotropy = source.anisotropy; 7754 7755 this.format = source.format; 7756 this.internalFormat = source.internalFormat; 7757 this.type = source.type; 7758 this.normalized = source.normalized; 7759 7760 this.offset.copy( source.offset ); 7761 this.repeat.copy( source.repeat ); 7762 this.center.copy( source.center ); 7763 this.rotation = source.rotation; 7764 7765 this.matrixAutoUpdate = source.matrixAutoUpdate; 7766 this.matrix.copy( source.matrix ); 7767 7768 this.generateMipmaps = source.generateMipmaps; 7769 this.premultiplyAlpha = source.premultiplyAlpha; 7770 this.flipY = source.flipY; 7771 this.unpackAlignment = source.unpackAlignment; 7772 this.colorSpace = source.colorSpace; 7773 7774 this.renderTarget = source.renderTarget; 7775 this.isRenderTargetTexture = source.isRenderTargetTexture; 7776 this.isArrayTexture = source.isArrayTexture; 7777 7778 this.userData = JSON.parse( JSON.stringify( source.userData ) ); 7779 7780 this.needsUpdate = true; 7781 7782 return this; 7783 7784 } 7785 7786 /** 7787 * Sets this texture's properties based on `values`. 7788 * @param {Object} values - A container with texture parameters. 7789 */ 7790 setValues( values ) { 7791 7792 for ( const key in values ) { 7793 7794 const newValue = values[ key ]; 7795 7796 if ( newValue === undefined ) { 7797 7798 warn( `Texture.setValues(): parameter '${ key }' has value of undefined.` ); 7799 continue; 7800 7801 } 7802 7803 const currentValue = this[ key ]; 7804 7805 if ( currentValue === undefined ) { 7806 7807 warn( `Texture.setValues(): property '${ key }' does not exist.` ); 7808 continue; 7809 7810 } 7811 7812 if ( ( currentValue && newValue ) && ( currentValue.isVector2 && newValue.isVector2 ) ) { 7813 7814 currentValue.copy( newValue ); 7815 7816 } else if ( ( currentValue && newValue ) && ( currentValue.isVector3 && newValue.isVector3 ) ) { 7817 7818 currentValue.copy( newValue ); 7819 7820 } else if ( ( currentValue && newValue ) && ( currentValue.isMatrix3 && newValue.isMatrix3 ) ) { 7821 7822 currentValue.copy( newValue ); 7823 7824 } else { 7825 7826 this[ key ] = newValue; 7827 7828 } 7829 7830 } 7831 7832 } 7833 7834 /** 7835 * Serializes the texture into JSON. 7836 * 7837 * @param {?(Object|string)} meta - An optional value holding meta information about the serialization. 7838 * @return {Object} A JSON object representing the serialized texture. 7839 * @see {@link ObjectLoader#parse} 7840 */ 7841 toJSON( meta ) { 7842 7843 const isRootObject = ( meta === undefined || typeof meta === 'string' ); 7844 7845 if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { 7846 7847 return meta.textures[ this.uuid ]; 7848 7849 } 7850 7851 const output = { 7852 7853 metadata: { 7854 version: 4.7, 7855 type: 'Texture', 7856 generator: 'Texture.toJSON' 7857 }, 7858 7859 uuid: this.uuid, 7860 name: this.name, 7861 7862 image: this.source.toJSON( meta ).uuid, 7863 7864 mapping: this.mapping, 7865 channel: this.channel, 7866 7867 repeat: [ this.repeat.x, this.repeat.y ], 7868 offset: [ this.offset.x, this.offset.y ], 7869 center: [ this.center.x, this.center.y ], 7870 rotation: this.rotation, 7871 7872 wrap: [ this.wrapS, this.wrapT ], 7873 7874 format: this.format, 7875 internalFormat: this.internalFormat, 7876 type: this.type, 7877 normalized: this.normalized, 7878 colorSpace: this.colorSpace, 7879 7880 minFilter: this.minFilter, 7881 magFilter: this.magFilter, 7882 anisotropy: this.anisotropy, 7883 7884 flipY: this.flipY, 7885 7886 generateMipmaps: this.generateMipmaps, 7887 premultiplyAlpha: this.premultiplyAlpha, 7888 unpackAlignment: this.unpackAlignment 7889 7890 }; 7891 7892 if ( Object.keys( this.userData ).length > 0 ) output.userData = this.userData; 7893 7894 if ( ! isRootObject ) { 7895 7896 meta.textures[ this.uuid ] = output; 7897 7898 } 7899 7900 return output; 7901 7902 } 7903 7904 /** 7905 * Frees the GPU-related resources allocated by this instance. Call this 7906 * method whenever this instance is no longer used in your app. 7907 * 7908 * @fires Texture#dispose 7909 */ 7910 dispose() { 7911 7912 /** 7913 * Fires when the texture has been disposed of. 7914 * 7915 * @event Texture#dispose 7916 * @type {Object} 7917 */ 7918 this.dispatchEvent( { type: 'dispose' } ); 7919 7920 } 7921 7922 /** 7923 * Transforms the given uv vector with the textures uv transformation matrix. 7924 * 7925 * @param {Vector2} uv - The uv vector. 7926 * @return {Vector2} The transformed uv vector. 7927 */ 7928 transformUv( uv ) { 7929 7930 if ( this.mapping !== UVMapping ) return uv; 7931 7932 uv.applyMatrix3( this.matrix ); 7933 7934 if ( uv.x < 0 || uv.x > 1 ) { 7935 7936 switch ( this.wrapS ) { 7937 7938 case RepeatWrapping: 7939 7940 uv.x = uv.x - Math.floor( uv.x ); 7941 break; 7942 7943 case ClampToEdgeWrapping: 7944 7945 uv.x = uv.x < 0 ? 0 : 1; 7946 break; 7947 7948 case MirroredRepeatWrapping: 7949 7950 if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { 7951 7952 uv.x = Math.ceil( uv.x ) - uv.x; 7953 7954 } else { 7955 7956 uv.x = uv.x - Math.floor( uv.x ); 7957 7958 } 7959 7960 break; 7961 7962 } 7963 7964 } 7965 7966 if ( uv.y < 0 || uv.y > 1 ) { 7967 7968 switch ( this.wrapT ) { 7969 7970 case RepeatWrapping: 7971 7972 uv.y = uv.y - Math.floor( uv.y ); 7973 break; 7974 7975 case ClampToEdgeWrapping: 7976 7977 uv.y = uv.y < 0 ? 0 : 1; 7978 break; 7979 7980 case MirroredRepeatWrapping: 7981 7982 if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { 7983 7984 uv.y = Math.ceil( uv.y ) - uv.y; 7985 7986 } else { 7987 7988 uv.y = uv.y - Math.floor( uv.y ); 7989 7990 } 7991 7992 break; 7993 7994 } 7995 7996 } 7997 7998 if ( this.flipY ) { 7999 8000 uv.y = 1 - uv.y; 8001 8002 } 8003 8004 return uv; 8005 8006 } 8007 8008 /** 8009 * Setting this property to `true` indicates the engine the texture 8010 * must be updated in the next render. This triggers a texture upload 8011 * to the GPU and ensures correct texture parameter configuration. 8012 * 8013 * @type {boolean} 8014 * @default false 8015 * @param {boolean} value 8016 */ 8017 set needsUpdate( value ) { 8018 8019 if ( value === true ) { 8020 8021 this.version ++; 8022 this.source.needsUpdate = true; 8023 8024 } 8025 8026 } 8027 8028 /** 8029 * Setting this property to `true` indicates the engine the PMREM 8030 * must be regenerated. 8031 * 8032 * @type {boolean} 8033 * @default false 8034 * @param {boolean} value 8035 */ 8036 set needsPMREMUpdate( value ) { 8037 8038 if ( value === true ) { 8039 8040 this.pmremVersion ++; 8041 8042 } 8043 8044 } 8045 8046} 8047 8048/** 8049 * The default image for all textures. 8050 * 8051 * @static 8052 * @type {?Image} 8053 * @default null 8054 */ 8055Texture.DEFAULT_IMAGE = null; 8056 8057/** 8058 * The default mapping for all textures. 8059 * 8060 * @static 8061 * @type {number} 8062 * @default UVMapping 8063 */ 8064Texture.DEFAULT_MAPPING = UVMapping; 8065 8066/** 8067 * The default anisotropy value for all textures. 8068 * 8069 * @static 8070 * @type {number} 8071 * @default 1 8072 */ 8073Texture.DEFAULT_ANISOTROPY = 1; 8074 8075/** 8076 * Class representing a 4D vector. A 4D vector is an ordered quadruplet of numbers 8077 * (labeled x, y, z and w), which can be used to represent a number of things, such as: 8078 * 8079 * - A point in 4D space. 8080 * - A direction and length in 4D space. In three.js the length will 8081 * always be the Euclidean distance(straight-line distance) from `(0, 0, 0, 0)` to `(x, y, z, w)` 8082 * and the direction is also measured from `(0, 0, 0, 0)` towards `(x, y, z, w)`. 8083 * - Any arbitrary ordered quadruplet of numbers. 8084 * 8085 * There are other things a 4D vector can be used to represent, however these 8086 * are the most common uses in *three.js*. 8087 * 8088 * Iterating through a vector instance will yield its components `(x, y, z, w)` in 8089 * the corresponding order. 8090 * ```js 8091 * const a = new THREE.Vector4( 0, 1, 0, 0 ); 8092 * 8093 * //no arguments; will be initialised to (0, 0, 0, 1) 8094 * const b = new THREE.Vector4( ); 8095 * 8096 * const d = a.dot( b ); 8097 * ``` 8098 */ 8099class Vector4 { 8100 8101 static { 8102 8103 /** 8104 * This flag can be used for type testing. 8105 * 8106 * @type {boolean} 8107 * @readonly 8108 * @default true 8109 */ 8110 Vector4.prototype.isVector4 = true; 8111 8112 } 8113 8114 /** 8115 * Constructs a new 4D vector. 8116 * 8117 * @param {number} [x=0] - The x value of this vector. 8118 * @param {number} [y=0] - The y value of this vector. 8119 * @param {number} [z=0] - The z value of this vector. 8120 * @param {number} [w=1] - The w value of this vector. 8121 */ 8122 constructor( x = 0, y = 0, z = 0, w = 1 ) { 8123 8124 /** 8125 * The x value of this vector. 8126 * 8127 * @type {number} 8128 */ 8129 this.x = x; 8130 8131 /** 8132 * The y value of this vector. 8133 * 8134 * @type {number} 8135 */ 8136 this.y = y; 8137 8138 /** 8139 * The z value of this vector. 8140 * 8141 * @type {number} 8142 */ 8143 this.z = z; 8144 8145 /** 8146 * The w value of this vector. 8147 * 8148 * @type {number} 8149 */ 8150 this.w = w; 8151 8152 } 8153 8154 /** 8155 * Alias for {@link Vector4#z}. 8156 * 8157 * @type {number} 8158 */ 8159 get width() { 8160 8161 return this.z; 8162 8163 } 8164 8165 set width( value ) { 8166 8167 this.z = value; 8168 8169 } 8170 8171 /** 8172 * Alias for {@link Vector4#w}. 8173 * 8174 * @type {number} 8175 */ 8176 get height() { 8177 8178 return this.w; 8179 8180 } 8181 8182 set height( value ) { 8183 8184 this.w = value; 8185 8186 } 8187 8188 /** 8189 * Sets the vector components. 8190 * 8191 * @param {number} x - The value of the x component. 8192 * @param {number} y - The value of the y component. 8193 * @param {number} z - The value of the z component. 8194 * @param {number} w - The value of the w component. 8195 * @return {Vector4} A reference to this vector. 8196 */ 8197 set( x, y, z, w ) { 8198 8199 this.x = x; 8200 this.y = y; 8201 this.z = z; 8202 this.w = w; 8203 8204 return this; 8205 8206 } 8207 8208 /** 8209 * Sets the vector components to the same value. 8210 * 8211 * @param {number} scalar - The value to set for all vector components. 8212 * @return {Vector4} A reference to this vector. 8213 */ 8214 setScalar( scalar ) { 8215 8216 this.x = scalar; 8217 this.y = scalar; 8218 this.z = scalar; 8219 this.w = scalar; 8220 8221 return this; 8222 8223 } 8224 8225 /** 8226 * Sets the vector's x component to the given value 8227 * 8228 * @param {number} x - The value to set. 8229 * @return {Vector4} A reference to this vector. 8230 */ 8231 setX( x ) { 8232 8233 this.x = x; 8234 8235 return this; 8236 8237 } 8238 8239 /** 8240 * Sets the vector's y component to the given value 8241 * 8242 * @param {number} y - The value to set. 8243 * @return {Vector4} A reference to this vector. 8244 */ 8245 setY( y ) { 8246 8247 this.y = y; 8248 8249 return this; 8250 8251 } 8252 8253 /** 8254 * Sets the vector's z component to the given value 8255 * 8256 * @param {number} z - The value to set. 8257 * @return {Vector4} A reference to this vector. 8258 */ 8259 setZ( z ) { 8260 8261 this.z = z; 8262 8263 return this; 8264 8265 } 8266 8267 /** 8268 * Sets the vector's w component to the given value 8269 * 8270 * @param {number} w - The value to set. 8271 * @return {Vector4} A reference to this vector. 8272 */ 8273 setW( w ) { 8274 8275 this.w = w; 8276 8277 return this; 8278 8279 } 8280 8281 /** 8282 * Allows to set a vector component with an index. 8283 * 8284 * @param {number} index - The component index. `0` equals to x, `1` equals to y, 8285 * `2` equals to z, `3` equals to w. 8286 * @param {number} value - The value to set. 8287 * @return {Vector4} A reference to this vector. 8288 */ 8289 setComponent( index, value ) { 8290 8291 switch ( index ) { 8292 8293 case 0: this.x = value; break; 8294 case 1: this.y = value; break; 8295 case 2: this.z = value; break; 8296 case 3: this.w = value; break; 8297 default: throw new Error( 'THREE.Vector4: index is out of range: ' + index ); 8298 8299 } 8300 8301 return this; 8302 8303 } 8304 8305 /** 8306 * Returns the value of the vector component which matches the given index. 8307 * 8308 * @param {number} index - The component index. `0` equals to x, `1` equals to y, 8309 * `2` equals to z, `3` equals to w. 8310 * @return {number} A vector component value. 8311 */ 8312 getComponent( index ) { 8313 8314 switch ( index ) { 8315 8316 case 0: return this.x; 8317 case 1: return this.y; 8318 case 2: return this.z; 8319 case 3: return this.w; 8320 default: throw new Error( 'THREE.Vector4: index is out of range: ' + index ); 8321 8322 } 8323 8324 } 8325 8326 /** 8327 * Returns a new vector with copied values from this instance. 8328 * 8329 * @return {Vector4} A clone of this instance. 8330 */ 8331 clone() { 8332 8333 return new this.constructor( this.x, this.y, this.z, this.w ); 8334 8335 } 8336 8337 /** 8338 * Copies the values of the given vector to this instance. 8339 * 8340 * @param {Vector3|Vector4} v - The vector to copy. 8341 * @return {Vector4} A reference to this vector. 8342 */ 8343 copy( v ) { 8344 8345 this.x = v.x; 8346 this.y = v.y; 8347 this.z = v.z; 8348 this.w = ( v.w !== undefined ) ? v.w : 1; 8349 8350 return this; 8351 8352 } 8353 8354 /** 8355 * Adds the given vector to this instance. 8356 * 8357 * @param {Vector4} v - The vector to add. 8358 * @return {Vector4} A reference to this vector. 8359 */ 8360 add( v ) { 8361 8362 this.x += v.x; 8363 this.y += v.y; 8364 this.z += v.z; 8365 this.w += v.w; 8366 8367 return this; 8368 8369 } 8370 8371 /** 8372 * Adds the given scalar value to all components of this instance. 8373 * 8374 * @param {number} s - The scalar to add. 8375 * @return {Vector4} A reference to this vector. 8376 */ 8377 addScalar( s ) { 8378 8379 this.x += s; 8380 this.y += s; 8381 this.z += s; 8382 this.w += s; 8383 8384 return this; 8385 8386 } 8387 8388 /** 8389 * Adds the given vectors and stores the result in this instance. 8390 * 8391 * @param {Vector4} a - The first vector. 8392 * @param {Vector4} b - The second vector. 8393 * @return {Vector4} A reference to this vector. 8394 */ 8395 addVectors( a, b ) { 8396 8397 this.x = a.x + b.x; 8398 this.y = a.y + b.y; 8399 this.z = a.z + b.z; 8400 this.w = a.w + b.w; 8401 8402 return this; 8403 8404 } 8405 8406 /** 8407 * Adds the given vector scaled by the given factor to this instance. 8408 * 8409 * @param {Vector4} v - The vector. 8410 * @param {number} s - The factor that scales `v`. 8411 * @return {Vector4} A reference to this vector. 8412 */ 8413 addScaledVector( v, s ) { 8414 8415 this.x += v.x * s; 8416 this.y += v.y * s; 8417 this.z += v.z * s; 8418 this.w += v.w * s; 8419 8420 return this; 8421 8422 } 8423 8424 /** 8425 * Subtracts the given vector from this instance. 8426 * 8427 * @param {Vector4} v - The vector to subtract. 8428 * @return {Vector4} A reference to this vector. 8429 */ 8430 sub( v ) { 8431 8432 this.x -= v.x; 8433 this.y -= v.y; 8434 this.z -= v.z; 8435 this.w -= v.w; 8436 8437 return this; 8438 8439 } 8440 8441 /** 8442 * Subtracts the given scalar value from all components of this instance. 8443 * 8444 * @param {number} s - The scalar to subtract. 8445 * @return {Vector4} A reference to this vector. 8446 */ 8447 subScalar( s ) { 8448 8449 this.x -= s; 8450 this.y -= s; 8451 this.z -= s; 8452 this.w -= s; 8453 8454 return this; 8455 8456 } 8457 8458 /** 8459 * Subtracts the given vectors and stores the result in this instance. 8460 * 8461 * @param {Vector4} a - The first vector. 8462 * @param {Vector4} b - The second vector. 8463 * @return {Vector4} A reference to this vector. 8464 */ 8465 subVectors( a, b ) { 8466 8467 this.x = a.x - b.x; 8468 this.y = a.y - b.y; 8469 this.z = a.z - b.z; 8470 this.w = a.w - b.w; 8471 8472 return this; 8473 8474 } 8475 8476 /** 8477 * Multiplies the given vector with this instance. 8478 * 8479 * @param {Vector4} v - The vector to multiply. 8480 * @return {Vector4} A reference to this vector. 8481 */ 8482 multiply( v ) { 8483 8484 this.x *= v.x; 8485 this.y *= v.y; 8486 this.z *= v.z; 8487 this.w *= v.w; 8488 8489 return this; 8490 8491 } 8492 8493 /** 8494 * Multiplies the given scalar value with all components of this instance. 8495 * 8496 * @param {number} scalar - The scalar to multiply. 8497 * @return {Vector4} A reference to this vector. 8498 */ 8499 multiplyScalar( scalar ) { 8500 8501 this.x *= scalar; 8502 this.y *= scalar; 8503 this.z *= scalar; 8504 this.w *= scalar; 8505 8506 return this; 8507 8508 } 8509 8510 /** 8511 * Multiplies this vector with the given 4x4 matrix. 8512 * 8513 * @param {Matrix4} m - The 4x4 matrix. 8514 * @return {Vector4} A reference to this vector. 8515 */ 8516 applyMatrix4( m ) { 8517 8518 const x = this.x, y = this.y, z = this.z, w = this.w; 8519 const e = m.elements; 8520 8521 this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; 8522 this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; 8523 this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; 8524 this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; 8525 8526 return this; 8527 8528 } 8529 8530 /** 8531 * Divides this instance by the given vector. 8532 * 8533 * @param {Vector4} v - The vector to divide. 8534 * @return {Vector4} A reference to this vector. 8535 */ 8536 divide( v ) { 8537 8538 this.x /= v.x; 8539 this.y /= v.y; 8540 this.z /= v.z; 8541 this.w /= v.w; 8542 8543 return this; 8544 8545 } 8546 8547 /** 8548 * Divides this vector by the given scalar. 8549 * 8550 * @param {number} scalar - The scalar to divide. 8551 * @return {Vector4} A reference to this vector. 8552 */ 8553 divideScalar( scalar ) { 8554 8555 return this.multiplyScalar( 1 / scalar ); 8556 8557 } 8558 8559 /** 8560 * Sets the x, y and z components of this 8561 * vector to the quaternion's axis and w to the angle. 8562 * 8563 * @param {Quaternion} q - The Quaternion to set. 8564 * @return {Vector4} A reference to this vector. 8565 */ 8566 setAxisAngleFromQuaternion( q ) { 8567 8568 // q is assumed to be normalized 8569 8570 this.w = 2 * Math.acos( q.w ); 8571 8572 const s = Math.sqrt( 1 - q.w * q.w ); 8573 8574 if ( s < 0.0001 ) { 8575 8576 this.x = 1; 8577 this.y = 0; 8578 this.z = 0; 8579 8580 } else { 8581 8582 this.x = q.x / s; 8583 this.y = q.y / s; 8584 this.z = q.z / s; 8585 8586 } 8587 8588 return this; 8589 8590 } 8591 8592 /** 8593 * Sets the x, y and z components of this 8594 * vector to the axis of rotation and w to the angle. 8595 * 8596 * @param {Matrix4} m - A 4x4 matrix of which the upper left 3x3 matrix is a pure rotation matrix. 8597 * @return {Vector4} A reference to this vector. 8598 */ 8599 setAxisAngleFromRotationMatrix( m ) { 8600 8601 // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) 8602 8603 let angle, x, y, z; // variables for result 8604 const epsilon = 0.01, // margin to allow for rounding errors 8605 epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees 8606 8607 te = m.elements, 8608 8609 m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], 8610 m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], 8611 m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; 8612 8613 if ( ( Math.abs( m12 - m21 ) < epsilon ) && 8614 ( Math.abs( m13 - m31 ) < epsilon ) && 8615 ( Math.abs( m23 - m32 ) < epsilon ) ) { 8616 8617 // singularity found 8618 // first check for identity matrix which must have +1 for all terms 8619 // in leading diagonal and zero in other terms 8620 8621 if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && 8622 ( Math.abs( m13 + m31 ) < epsilon2 ) && 8623 ( Math.abs( m23 + m32 ) < epsilon2 ) && 8624 ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { 8625 8626 // this singularity is identity matrix so angle = 0 8627 8628 this.set( 1, 0, 0, 0 ); 8629 8630 return this; // zero angle, arbitrary axis 8631 8632 } 8633 8634 // otherwise this singularity is angle = 180 8635 8636 angle = Math.PI; 8637 8638 const xx = ( m11 + 1 ) / 2; 8639 const yy = ( m22 + 1 ) / 2; 8640 const zz = ( m33 + 1 ) / 2; 8641 const xy = ( m12 + m21 ) / 4; 8642 const xz = ( m13 + m31 ) / 4; 8643 const yz = ( m23 + m32 ) / 4; 8644 8645 if ( ( xx > yy ) && ( xx > zz ) ) { 8646 8647 // m11 is the largest diagonal term 8648 8649 if ( xx < epsilon ) { 8650 8651 x = 0; 8652 y = 0.707106781; 8653 z = 0.707106781; 8654 8655 } else { 8656 8657 x = Math.sqrt( xx ); 8658 y = xy / x; 8659 z = xz / x; 8660 8661 } 8662 8663 } else if ( yy > zz ) { 8664 8665 // m22 is the largest diagonal term 8666 8667 if ( yy < epsilon ) { 8668 8669 x = 0.707106781; 8670 y = 0; 8671 z = 0.707106781; 8672 8673 } else { 8674 8675 y = Math.sqrt( yy ); 8676 x = xy / y; 8677 z = yz / y; 8678 8679 } 8680 8681 } else { 8682 8683 // m33 is the largest diagonal term so base result on this 8684 8685 if ( zz < epsilon ) { 8686 8687 x = 0.707106781; 8688 y = 0.707106781; 8689 z = 0; 8690 8691 } else { 8692 8693 z = Math.sqrt( zz ); 8694 x = xz / z; 8695 y = yz / z; 8696 8697 } 8698 8699 } 8700 8701 this.set( x, y, z, angle ); 8702 8703 return this; // return 180 deg rotation 8704 8705 } 8706 8707 // as we have reached here there are no singularities so we can handle normally 8708 8709 let s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + 8710 ( m13 - m31 ) * ( m13 - m31 ) + 8711 ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize 8712 8713 if ( Math.abs( s ) < 0.001 ) s = 1; 8714 8715 // prevent divide by zero, should not happen if matrix is orthogonal and should be 8716 // caught by singularity test above, but I've left it in just in case 8717 8718 this.x = ( m32 - m23 ) / s; 8719 this.y = ( m13 - m31 ) / s; 8720 this.z = ( m21 - m12 ) / s; 8721 this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); 8722 8723 return this; 8724 8725 } 8726 8727 /** 8728 * Sets the vector components to the position elements of the 8729 * given transformation matrix. 8730 * 8731 * @param {Matrix4} m - The 4x4 matrix. 8732 * @return {Vector4} A reference to this vector. 8733 */ 8734 setFromMatrixPosition( m ) { 8735 8736 const e = m.elements; 8737 8738 this.x = e[ 12 ]; 8739 this.y = e[ 13 ]; 8740 this.z = e[ 14 ]; 8741 this.w = e[ 15 ]; 8742 8743 return this; 8744 8745 } 8746 8747 /** 8748 * If this vector's x, y, z or w value is greater than the given vector's x, y, z or w 8749 * value, replace that value with the corresponding min value. 8750 * 8751 * @param {Vector4} v - The vector. 8752 * @return {Vector4} A reference to this vector. 8753 */ 8754 min( v ) { 8755 8756 this.x = Math.min( this.x, v.x ); 8757 this.y = Math.min( this.y, v.y ); 8758 this.z = Math.min( this.z, v.z ); 8759 this.w = Math.min( this.w, v.w ); 8760 8761 return this; 8762 8763 } 8764 8765 /** 8766 * If this vector's x, y, z or w value is less than the given vector's x, y, z or w 8767 * value, replace that value with the corresponding max value. 8768 * 8769 * @param {Vector4} v - The vector. 8770 * @return {Vector4} A reference to this vector. 8771 */ 8772 max( v ) { 8773 8774 this.x = Math.max( this.x, v.x ); 8775 this.y = Math.max( this.y, v.y ); 8776 this.z = Math.max( this.z, v.z ); 8777 this.w = Math.max( this.w, v.w ); 8778 8779 return this; 8780 8781 } 8782 8783 /** 8784 * If this vector's x, y, z or w value is greater than the max vector's x, y, z or w 8785 * value, it is replaced by the corresponding value. 8786 * If this vector's x, y, z or w value is less than the min vector's x, y, z or w value, 8787 * it is replaced by the corresponding value. 8788 * 8789 * @param {Vector4} min - The minimum x, y and z values. 8790 * @param {Vector4} max - The maximum x, y and z values in the desired range. 8791 * @return {Vector4} A reference to this vector. 8792 */ 8793 clamp( min, max ) { 8794 8795 // assumes min < max, componentwise 8796 8797 this.x = clamp( this.x, min.x, max.x ); 8798 this.y = clamp( this.y, min.y, max.y ); 8799 this.z = clamp( this.z, min.z, max.z ); 8800 this.w = clamp( this.w, min.w, max.w ); 8801 8802 return this; 8803 8804 } 8805 8806 /** 8807 * If this vector's x, y, z or w values are greater than the max value, they are 8808 * replaced by the max value. 8809 * If this vector's x, y, z or w values are less than the min value, they are 8810 * replaced by the min value. 8811 * 8812 * @param {number} minVal - The minimum value the components will be clamped to. 8813 * @param {number} maxVal - The maximum value the components will be clamped to. 8814 * @return {Vector4} A reference to this vector. 8815 */ 8816 clampScalar( minVal, maxVal ) { 8817 8818 this.x = clamp( this.x, minVal, maxVal ); 8819 this.y = clamp( this.y, minVal, maxVal ); 8820 this.z = clamp( this.z, minVal, maxVal ); 8821 this.w = clamp( this.w, minVal, maxVal ); 8822 8823 return this; 8824 8825 } 8826 8827 /** 8828 * If this vector's length is greater than the max value, it is replaced by 8829 * the max value. 8830 * If this vector's length is less than the min value, it is replaced by the 8831 * min value. 8832 * 8833 * @param {number} min - The minimum value the vector length will be clamped to. 8834 * @param {number} max - The maximum value the vector length will be clamped to. 8835 * @return {Vector4} A reference to this vector. 8836 */ 8837 clampLength( min, max ) { 8838 8839 const length = this.length(); 8840 8841 return this.divideScalar( length || 1 ).multiplyScalar( clamp( length, min, max ) ); 8842 8843 } 8844 8845 /** 8846 * The components of this vector are rounded down to the nearest integer value. 8847 * 8848 * @return {Vector4} A reference to this vector. 8849 */ 8850 floor() { 8851 8852 this.x = Math.floor( this.x ); 8853 this.y = Math.floor( this.y ); 8854 this.z = Math.floor( this.z ); 8855 this.w = Math.floor( this.w ); 8856 8857 return this; 8858 8859 } 8860 8861 /** 8862 * The components of this vector are rounded up to the nearest integer value. 8863 * 8864 * @return {Vector4} A reference to this vector. 8865 */ 8866 ceil() { 8867 8868 this.x = Math.ceil( this.x ); 8869 this.y = Math.ceil( this.y ); 8870 this.z = Math.ceil( this.z ); 8871 this.w = Math.ceil( this.w ); 8872 8873 return this; 8874 8875 } 8876 8877 /** 8878 * The components of this vector are rounded to the nearest integer value 8879 * 8880 * @return {Vector4} A reference to this vector. 8881 */ 8882 round() { 8883 8884 this.x = Math.round( this.x ); 8885 this.y = Math.round( this.y ); 8886 this.z = Math.round( this.z ); 8887 this.w = Math.round( this.w ); 8888 8889 return this; 8890 8891 } 8892 8893 /** 8894 * The components of this vector are rounded towards zero (up if negative, 8895 * down if positive) to an integer value. 8896 * 8897 * @return {Vector4} A reference to this vector. 8898 */ 8899 roundToZero() { 8900 8901 this.x = Math.trunc( this.x ); 8902 this.y = Math.trunc( this.y ); 8903 this.z = Math.trunc( this.z ); 8904 this.w = Math.trunc( this.w ); 8905 8906 return this; 8907 8908 } 8909 8910 /** 8911 * Inverts this vector - i.e. sets x = -x, y = -y, z = -z, w = -w. 8912 * 8913 * @return {Vector4} A reference to this vector. 8914 */ 8915 negate() { 8916 8917 this.x = - this.x; 8918 this.y = - this.y; 8919 this.z = - this.z; 8920 this.w = - this.w; 8921 8922 return this; 8923 8924 } 8925 8926 /** 8927 * Calculates the dot product of the given vector with this instance. 8928 * 8929 * @param {Vector4} v - The vector to compute the dot product with. 8930 * @return {number} The result of the dot product. 8931 */ 8932 dot( v ) { 8933 8934 return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; 8935 8936 } 8937 8938 /** 8939 * Computes the square of the Euclidean length (straight-line length) from 8940 * (0, 0, 0, 0) to (x, y, z, w). If you are comparing the lengths of vectors, you should 8941 * compare the length squared instead as it is slightly more efficient to calculate. 8942 * 8943 * @return {number} The square length of this vector. 8944 */ 8945 lengthSq() { 8946 8947 return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; 8948 8949 } 8950 8951 /** 8952 * Computes the Euclidean length (straight-line length) from (0, 0, 0, 0) to (x, y, z, w). 8953 * 8954 * @return {number} The length of this vector. 8955 */ 8956 length() { 8957 8958 return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); 8959 8960 } 8961 8962 /** 8963 * Computes the Manhattan length of this vector. 8964 * 8965 * @return {number} The length of this vector. 8966 */ 8967 manhattanLength() { 8968 8969 return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); 8970 8971 } 8972 8973 /** 8974 * Converts this vector to a unit vector - that is, sets it equal to a vector 8975 * with the same direction as this one, but with a vector length of `1`. 8976 * 8977 * @return {Vector4} A reference to this vector. 8978 */ 8979 normalize() { 8980 8981 return this.divideScalar( this.length() || 1 ); 8982 8983 } 8984 8985 /** 8986 * Sets this vector to a vector with the same direction as this one, but 8987 * with the specified length. 8988 * 8989 * @param {number} length - The new length of this vector. 8990 * @return {Vector4} A reference to this vector. 8991 */ 8992 setLength( length ) { 8993 8994 return this.normalize().multiplyScalar( length ); 8995 8996 } 8997 8998 /** 8999 * Linearly interpolates between the given vector and this instance, where 9000 * alpha is the percent distance along the line - alpha = 0 will be this 9001 * vector, and alpha = 1 will be the given one. 9002 * 9003 * @param {Vector4} v - The vector to interpolate towards. 9004 * @param {number} alpha - The interpolation factor, typically in the closed interval `[0, 1]`. 9005 * @return {Vector4} A reference to this vector. 9006 */ 9007 lerp( v, alpha ) { 9008 9009 this.x += ( v.x - this.x ) * alpha; 9010 this.y += ( v.y - this.y ) * alpha; 9011 this.z += ( v.z - this.z ) * alpha; 9012 this.w += ( v.w - this.w ) * alpha; 9013 9014 return this; 9015 9016 } 9017 9018 /** 9019 * Linearly interpolates between the given vectors, where alpha is the percent 9020 * distance along the line - alpha = 0 will be first vector, and alpha = 1 will 9021 * be the second one. The result is stored in this instance. 9022 * 9023 * @param {Vector4} v1 - The first vector. 9024 * @param {Vector4} v2 - The second vector. 9025 * @param {number} alpha - The interpolation factor, typically in the closed interval `[0, 1]`. 9026 * @return {Vector4} A reference to this vector. 9027 */ 9028 lerpVectors( v1, v2, alpha ) { 9029 9030 this.x = v1.x + ( v2.x - v1.x ) * alpha; 9031 this.y = v1.y + ( v2.y - v1.y ) * alpha; 9032 this.z = v1.z + ( v2.z - v1.z ) * alpha; 9033 this.w = v1.w + ( v2.w - v1.w ) * alpha; 9034 9035 return this; 9036 9037 } 9038 9039 /** 9040 * Returns `true` if this vector is equal with the given one. 9041 * 9042 * @param {Vector4} v - The vector to test for equality. 9043 * @return {boolean} Whether this vector is equal with the given one. 9044 */ 9045 equals( v ) { 9046 9047 return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); 9048 9049 } 9050 9051 /** 9052 * Sets this vector's x value to be `array[ offset ]`, y value to be `array[ offset + 1 ]`, 9053 * z value to be `array[ offset + 2 ]`, w value to be `array[ offset + 3 ]`. 9054 * 9055 * @param {Array<number>} array - An array holding the vector component values. 9056 * @param {number} [offset=0] - The offset into the array. 9057 * @return {Vector4} A reference to this vector. 9058 */ 9059 fromArray( array, offset = 0 ) { 9060 9061 this.x = array[ offset ]; 9062 this.y = array[ offset + 1 ]; 9063 this.z = array[ offset + 2 ]; 9064 this.w = array[ offset + 3 ]; 9065 9066 return this; 9067 9068 } 9069 9070 /** 9071 * Writes the components of this vector to the given array. If no array is provided, 9072 * the method returns a new instance. 9073 * 9074 * @param {Array<number>} [array=[]] - The target array holding the vector components. 9075 * @param {number} [offset=0] - Index of the first element in the array. 9076 * @return {Array<number>} The vector components. 9077 */ 9078 toArray( array = [], offset = 0 ) { 9079 9080 array[ offset ] = this.x; 9081 array[ offset + 1 ] = this.y; 9082 array[ offset + 2 ] = this.z; 9083 array[ offset + 3 ] = this.w; 9084 9085 return array; 9086 9087 } 9088 9089 /** 9090 * Sets the components of this vector from the given buffer attribute. 9091 * 9092 * @param {BufferAttribute} attribute - The buffer attribute holding vector data. 9093 * @param {number} index - The index into the attribute. 9094 * @return {Vector4} A reference to this vector. 9095 */ 9096 fromBufferAttribute( attribute, index ) { 9097 9098 this.x = attribute.getX( index ); 9099 this.y = attribute.getY( index ); 9100 this.z = attribute.getZ( index ); 9101 this.w = attribute.getW( index ); 9102 9103 return this; 9104 9105 } 9106 9107 /** 9108 * Sets each component of this vector to a pseudo-random value between `0` and 9109 * `1`, excluding `1`. 9110 * 9111 * @return {Vector4} A reference to this vector. 9112 */ 9113 random() { 9114 9115 this.x = Math.random(); 9116 this.y = Math.random(); 9117 this.z = Math.random(); 9118 this.w = Math.random(); 9119 9120 return this; 9121 9122 } 9123 9124 *[ Symbol.iterator ]() { 9125 9126 yield this.x; 9127 yield this.y; 9128 yield this.z; 9129 yield this.w; 9130 9131 } 9132 9133} 9134 9135/** 9136 * A render target is a buffer where the video card draws pixels for a scene 9137 * that is being rendered in the background. It is used in different effects, 9138 * such as applying postprocessing to a rendered image before displaying it 9139 * on the screen. 9140 * 9141 * @augments EventDispatcher 9142 */ 9143class RenderTarget extends EventDispatcher { 9144 9145 /** 9146 * Render target options. 9147 * 9148 * @typedef {Object} RenderTarget~Options 9149 * @property {boolean} [generateMipmaps=false] - Whether to generate mipmaps or not. 9150 * @property {number} [magFilter=LinearFilter] - The mag filter. 9151 * @property {number} [minFilter=LinearFilter] - The min filter. 9152 * @property {number} [format=RGBAFormat] - The texture format. 9153 * @property {number} [type=UnsignedByteType] - The texture type. 9154 * @property {?string} [internalFormat=null] - The texture's internal format. 9155 * @property {number} [wrapS=ClampToEdgeWrapping] - The texture's uv wrapping mode. 9156 * @property {number} [wrapT=ClampToEdgeWrapping] - The texture's uv wrapping mode. 9157 * @property {number} [anisotropy=1] - The texture's anisotropy value. 9158 * @property {string} [colorSpace=NoColorSpace] - The texture's color space. 9159 * @property {boolean} [depthBuffer=true] - Whether to allocate a depth buffer or not. 9160 * @property {boolean} [stencilBuffer=false] - Whether to allocate a stencil buffer or not. 9161 * @property {boolean} [resolveColorBuffer=true] - Whether to resolve the color buffer or not. Only relevant for multisampled render targets. 9162 * @property {boolean} [resolveDepthBuffer=true] - Whether to resolve the depth buffer or not. Only relevant for multisampled render targets. 9163 * @property {boolean} [resolveStencilBuffer=true] - Whether to resolve the stencil buffer or not. Only relevant for multisampled render targets. 9164 * @property {boolean} [storeMultisampledColorBuffer=true] - Whether to store the multisampled color buffer or not. Setting to `false` saves memory bandwidth when the multisampled data are not needed after a render pass. 9165 * @property {boolean} [storeMultisampledDepthBuffer=true] - Whether to store the multisampled depth buffer or not. Setting to `false` saves memory bandwidth when the multisampled data are not needed after a render pass. 9166 * @property {boolean} [storeMultisampledStencilBuffer=true] - Whether to store the multisampled stencil buffer or not. Setting to `false` saves memory bandwidth when the multisampled data are not needed after a render pass. 9167 * @property {?Texture} [depthTexture=null] - Reference to a depth texture. 9168 * @property {number} [samples=0] - The MSAA samples count. 9169 * @property {number} [count=1] - Defines the number of color attachments . Must be at least `1`. 9170 * @property {number} [depth=1] - The texture depth. 9171 * @property {boolean} [multiview=false] - Whether this target is used for multiview rendering (WebGL OVR_multiview2 extension). 9172 * @property {boolean} [useArrayDepthTexture=false] - Whether to create the depth texture as an array texture for per-layer depth testing. This is separate from multiview so layered render targets can use array depth without the multiview extension. 9173 */ 9174 9175 /** 9176 * Constructs a new render target. 9177 * 9178 * @param {number} [width=1] - The width of the render target. 9179 * @param {number} [height=1] - The height of the render target. 9180 * @param {RenderTarget~Options} [options] - The configuration object. 9181 */ 9182 constructor( width = 1, height = 1, options = {} ) { 9183 9184 super(); 9185 9186 options = Object.assign( { 9187 generateMipmaps: false, 9188 internalFormat: null, 9189 minFilter: LinearFilter, 9190 depthBuffer: true, 9191 stencilBuffer: false, 9192 resolveColorBuffer: true, 9193 resolveDepthBuffer: true, 9194 resolveStencilBuffer: true, 9195 storeMultisampledColorBuffer: true, 9196 storeMultisampledDepthBuffer: true, 9197 storeMultisampledStencilBuffer: true, 9198 depthTexture: null, 9199 samples: 0, 9200 count: 1, 9201 depth: 1, 9202 multiview: false, 9203 useArrayDepthTexture: false 9204 }, options ); 9205 9206 /** 9207 * This flag can be used for type testing. 9208 * 9209 * @type {boolean} 9210 * @readonly 9211 * @default true 9212 */ 9213 this.isRenderTarget = true; 9214 9215 /** 9216 * The width of the render target. 9217 * 9218 * @type {number} 9219 * @default 1 9220 */ 9221 this.width = width; 9222 9223 /** 9224 * The height of the render target. 9225 * 9226 * @type {number} 9227 * @default 1 9228 */ 9229 this.height = height; 9230 9231 /** 9232 * The depth of the render target. 9233 * 9234 * @type {number} 9235 * @default 1 9236 */ 9237 this.depth = options.depth; 9238 9239 /** 9240 * A rectangular area inside the render target's viewport. Fragments that are 9241 * outside the area will be discarded. 9242 * 9243 * @type {Vector4} 9244 * @default (0,0,width,height) 9245 */ 9246 this.scissor = new Vector4( 0, 0, width, height ); 9247 9248 /** 9249 * Indicates whether the scissor test should be enabled when rendering into 9250 * this render target or not. 9251 * 9252 * @type {boolean} 9253 * @default false 9254 */ 9255 this.scissorTest = false; 9256 9257 /** 9258 * A rectangular area representing the render target's viewport. 9259 * 9260 * @type {Vector4} 9261 * @default (0,0,width,height) 9262 */ 9263 this.viewport = new Vector4( 0, 0, width, height ); 9264 9265 /** 9266 * An array of textures. Each color attachment is represented as a separate texture. 9267 * Has at least a single entry for the default color attachment. 9268 * 9269 * @type {Array<Texture>} 9270 */ 9271 this.textures = []; 9272 9273 const image = { width: width, height: height, depth: options.depth }; 9274 const texture = new Texture( image ); 9275 9276 const count = options.count; 9277 for ( let i = 0; i < count; i ++ ) { 9278 9279 this.textures[ i ] = texture.clone(); 9280 this.textures[ i ].isRenderTargetTexture = true; 9281 this.textures[ i ].renderTarget = this; 9282 9283 } 9284 9285 this._setTextureOptions( options ); 9286 9287 /** 9288 * Whether to allocate a depth buffer or not. 9289 * 9290 * @type {boolean} 9291 * @default true 9292 */ 9293 this.depthBuffer = options.depthBuffer; 9294 9295 /** 9296 * Whether to allocate a stencil buffer or not. 9297 * 9298 * @type {boolean} 9299 * @default false 9300 */ 9301 this.stencilBuffer = options.stencilBuffer; 9302 9303 /** 9304 * Whether to resolve the color buffer or not. When set to `false`, the color 9305 * attachments do not receive the resolved (single-sampled) output of a render 9306 * pass and the render target's textures are left untouched. The rendered 9307 * content is then only accessible within the render pass itself. 9308 * 9309 * Only relevant for multisampled render targets. 9310 * 9311 * @type {boolean} 9312 * @default true 9313 */ 9314 this.resolveColorBuffer = options.resolveColorBuffer; 9315 9316 /** 9317 * Whether to resolve the depth buffer or not. When set to `false`, the depth 9318 * texture does not receive the resolved depth output of a render pass which 9319 * saves memory bandwidth. Use this setting when the depth data of a render 9320 * pass are not required afterwards. 9321 * 9322 * Only relevant for multisampled render targets in WebGL. WebGPU does not 9323 * support depth resolves; sampling the depth texture of a multisampled render 9324 * target accesses the multisampled data directly, see 9325 * {@link RenderTarget#storeMultisampledDepthBuffer}. 9326 * 9327 * @type {boolean} 9328 * @default true 9329 */ 9330 this.resolveDepthBuffer = options.resolveDepthBuffer; 9331 9332 /** 9333 * Whether to resolve the stencil buffer or not. Analogous to 9334 * {@link RenderTarget#resolveDepthBuffer} but for the stencil aspect. 9335 * 9336 * @type {boolean} 9337 * @default true 9338 */ 9339 this.resolveStencilBuffer = options.resolveStencilBuffer; 9340 9341 /** 9342 * Whether to store the multisampled color buffer or not. When set to `false`, 9343 * the multisampled data are discarded at the end of a render pass, right after 9344 * they have been resolved. This saves memory bandwidth, especially on tile-based 9345 * GPUs, and is the recommended setting for render targets that are fully redrawn 9346 * each frame and whose output is only accessed via the resolved textures (e.g. 9347 * scene passes in post-processing chains). 9348 * 9349 * Must be kept `true` when the multisampled data are needed after the render 9350 * pass ends, e.g. when rendering into the target without clearing or when the 9351 * scene contains transmissive objects which require a mid-pass framebuffer copy. 9352 * 9353 * @type {boolean} 9354 * @default true 9355 */ 9356 this.storeMultisampledColorBuffer = options.storeMultisampledColorBuffer; 9357 9358 /** 9359 * Whether to store the multisampled depth buffer or not. When set to `false`, 9360 * the multisampled depth data are discarded at the end of a render pass which 9361 * saves memory bandwidth. 9362 * 9363 * Must be kept `true` in WebGPU when the depth texture of a multisampled render 9364 * target is sampled (e.g. by depth-based post-processing effects) since depth 9365 * is read directly from the multisampled data. 9366 * 9367 * @type {boolean} 9368 * @default true 9369 */ 9370 this.storeMultisampledDepthBuffer = options.storeMultisampledDepthBuffer; 9371 9372 /** 9373 * Whether to store the multisampled stencil buffer or not. Analogous to 9374 * {@link RenderTarget#storeMultisampledDepthBuffer} but for the stencil aspect. 9375 * 9376 * @type {boolean} 9377 * @default true 9378 */ 9379 this.storeMultisampledStencilBuffer = options.storeMultisampledStencilBuffer; 9380 9381 this._depthTexture = null; 9382 this.depthTexture = options.depthTexture; 9383 9384 /** 9385 * The number of MSAA samples. 9386 * 9387 * A value of `0` disables MSAA. 9388 * 9389 * @type {number} 9390 * @default 0 9391 */ 9392 this.samples = options.samples; 9393 9394 /** 9395 * Whether to this target is used in multiview rendering. 9396 * 9397 * @type {boolean} 9398 * @default false 9399 */ 9400 this.multiview = options.multiview; 9401 9402 /** 9403 * Whether to create the depth texture as an array texture for per-layer depth testing. 9404 * This is separate from multiview so layered render targets can use array depth without 9405 * the multiview extension. 9406 * 9407 * @type {boolean} 9408 * @default false 9409 */ 9410 this.useArrayDepthTexture = options.useArrayDepthTexture; 9411 9412 } 9413 9414 _setTextureOptions( options = {} ) { 9415 9416 const values = { 9417 minFilter: LinearFilter, 9418 generateMipmaps: false, 9419 flipY: false, 9420 internalFormat: null 9421 }; 9422 9423 if ( options.mapping !== undefined ) values.mapping = options.mapping; 9424 if ( options.wrapS !== undefined ) values.wrapS = options.wrapS; 9425 if ( options.wrapT !== undefined ) values.wrapT = options.wrapT; 9426 if ( options.wrapR !== undefined ) values.wrapR = options.wrapR; 9427 if ( options.magFilter !== undefined ) values.magFilter = options.magFilter; 9428 if ( options.minFilter !== undefined ) values.minFilter = options.minFilter; 9429 if ( options.format !== undefined ) values.format = options.format; 9430 if ( options.type !== undefined ) values.type = options.type; 9431 if ( options.anisotropy !== undefined ) values.anisotropy = options.anisotropy; 9432 if ( options.colorSpace !== undefined ) values.colorSpace = options.colorSpace; 9433 if ( options.flipY !== undefined ) values.flipY = options.flipY; 9434 if ( options.generateMipmaps !== undefined ) values.generateMipmaps = options.generateMipmaps; 9435 if ( options.internalFormat !== undefined ) values.internalFormat = options.internalFormat; 9436 9437 for ( let i = 0; i < this.textures.length; i ++ ) { 9438 9439 const texture = this.textures[ i ]; 9440 texture.setValues( values ); 9441 9442 } 9443 9444 } 9445 9446 /** 9447 * The texture representing the default color attachment. 9448 * 9449 * @type {Texture} 9450 */ 9451 get texture() { 9452 9453 return this.textures[ 0 ]; 9454 9455 } 9456 9457 set texture( value ) { 9458 9459 this.textures[ 0 ] = value; 9460 9461 } 9462 9463 set depthTexture( current ) { 9464 9465 if ( this._depthTexture !== null && this._depthTexture.renderTarget === this ) this._depthTexture.renderTarget = null; 9466 if ( current !== null && current.renderTarget === null ) current.renderTarget = this; 9467 9468 this._depthTexture = current; 9469 9470 } 9471 9472 /** 9473 * Instead of saving the depth in a renderbuffer, a texture 9474 * can be used instead which is useful for further processing 9475 * e.g. in context of post-processing. 9476 * 9477 * @type {?DepthTexture} 9478 * @default null 9479 */ 9480 get depthTexture() { 9481 9482 return this._depthTexture; 9483 9484 } 9485 9486 /** 9487 * Sets the size of this render target. 9488 * 9489 * @param {number} width - The width. 9490 * @param {number} height - The height. 9491 * @param {number} [depth=1] - The depth. 9492 */ 9493 setSize( width, height, depth = 1 ) { 9494 9495 if ( this.width !== width || this.height !== height || this.depth !== depth ) { 9496 9497 this.width = width; 9498 this.height = height; 9499 this.depth = depth; 9500 9501 for ( let i = 0, il = this.textures.length; i < il; i ++ ) { 9502 9503 this.textures[ i ].image.width = width; 9504 this.textures[ i ].image.height = height; 9505 this.textures[ i ].image.depth = depth; 9506 9507 if ( this.textures[ i ].isData3DTexture !== true ) { // Fix for #31693 9508 9509 // TODO: Reconsider setting isArrayTexture flag here and in the ctor of Texture. 9510 // Maybe a method `isArrayTexture()` or just a getter could replace a flag since 9511 // both are evaluated on each call? 9512 9513 this.textures[ i ].isArrayTexture = this.textures[ i ].image.depth > 1; 9514 9515 } 9516 9517 } 9518 9519 this.dispose(); 9520 9521 } 9522 9523 this.viewport.set( 0, 0, width, height ); 9524 this.scissor.set( 0, 0, width, height ); 9525 9526 } 9527 9528 /** 9529 * Returns a new render target with copied values from this instance. 9530 * 9531 * @return {RenderTarget} A clone of this instance. 9532 */ 9533 clone() { 9534 9535 return new this.constructor().copy( this ); 9536 9537 } 9538 9539 /** 9540 * Copies the settings of the given render target. This is a structural copy so 9541 * no resources are shared between render targets after the copy. That includes 9542 * all MRT textures and the depth texture. 9543 * 9544 * @param {RenderTarget} source - The render target to copy. 9545 * @return {RenderTarget} A reference to this instance. 9546 */ 9547 copy( source ) { 9548 9549 this.width = source.width; 9550 this.height = source.height; 9551 this.depth = source.depth; 9552 9553 this.scissor.copy( source.scissor ); 9554 this.scissorTest = source.scissorTest; 9555 9556 this.viewport.copy( source.viewport ); 9557 9558 this.textures.length = 0; 9559 9560 for ( let i = 0, il = source.textures.length; i < il; i ++ ) { 9561 9562 this.textures[ i ] = source.textures[ i ].clone(); 9563 this.textures[ i ].isRenderTargetTexture = true; 9564 this.textures[ i ].renderTarget = this; 9565 9566 // ensure image object is not shared, see #20328 9567 9568 const image = Object.assign( {}, source.textures[ i ].image ); 9569 this.textures[ i ].source = new TextureSource( image ); 9570 9571 } 9572 9573 this.depthBuffer = source.depthBuffer; 9574 this.stencilBuffer = source.stencilBuffer; 9575 9576 this.resolveColorBuffer = source.resolveColorBuffer; 9577 this.resolveDepthBuffer = source.resolveDepthBuffer; 9578 this.resolveStencilBuffer = source.resolveStencilBuffer; 9579 9580 this.storeMultisampledColorBuffer = source.storeMultisampledColorBuffer; 9581 this.storeMultisampledDepthBuffer = source.storeMultisampledDepthBuffer; 9582 this.storeMultisampledStencilBuffer = source.storeMultisampledStencilBuffer; 9583 9584 if ( source.depthTexture !== null ) { 9585 9586 if ( source.depthTexture.renderTarget === source ) { 9587 9588 const depthTexture = source.depthTexture.clone(); 9589 depthTexture.renderTarget = null; 9590 9591 this.depthTexture = depthTexture; 9592 9593 } else { 9594 9595 this.depthTexture = source.depthTexture; 9596 9597 } 9598 9599 } 9600 9601 this.samples = source.samples; 9602 this.multiview = source.multiview; 9603 this.useArrayDepthTexture = source.useArrayDepthTexture; 9604 9605 return this; 9606 9607 } 9608 9609 /** 9610 * Frees the GPU-related resources allocated by this instance. Call this 9611 * method whenever this instance is no longer used in your app. 9612 * 9613 * @fires RenderTarget#dispose 9614 */ 9615 dispose() { 9616 9617 this.dispatchEvent( { type: 'dispose' } ); 9618 9619 } 9620 9621} 9622 9623/** 9624 * A render target used in context of {@link WebGLRenderer}. 9625 * 9626 * @augments RenderTarget 9627 */ 9628class WebGLRenderTarget extends RenderTarget { 9629 9630 /** 9631 * Constructs a new 3D render target. 9632 * 9633 * @param {number} [width=1] - The width of the render target. 9634 * @param {number} [height=1] - The height of the render target. 9635 * @param {RenderTarget~Options} [options] - The configuration object. 9636 */ 9637 constructor( width = 1, height = 1, options = {} ) { 9638 9639 super( width, height, options ); 9640 9641 /** 9642 * This flag can be used for type testing. 9643 * 9644 * @type {boolean} 9645 * @readonly 9646 * @default true 9647 */ 9648 this.isWebGLRenderTarget = true; 9649 9650 } 9651 9652} 9653 9654/** 9655 * Creates an array of textures directly from raw buffer data. 9656 * 9657 * @augments Texture 9658 */ 9659class DataArrayTexture extends Texture { 9660 9661 /** 9662 * Constructs a new data array texture. 9663 * 9664 * @param {?TypedArray} [data=null] - The buffer data. 9665 * @param {number} [width=1] - The width of the texture. 9666 * @param {number} [height=1] - The height of the texture. 9667 * @param {number} [depth=1] - The depth of the texture. 9668 */ 9669 constructor( data = null, width = 1, height = 1, depth = 1 ) { 9670 9671 super( null ); 9672 9673 /** 9674 * This flag can be used for type testing. 9675 * 9676 * @type {boolean} 9677 * @readonly 9678 * @default true 9679 */ 9680 this.isDataArrayTexture = true; 9681 9682 /** 9683 * The image definition of a data texture. 9684 * 9685 * @type {{data:TypedArray,width:number,height:number,depth:number}} 9686 */ 9687 this.image = { data, width, height, depth }; 9688 9689 /** 9690 * How the texture is sampled when a texel covers more than one pixel. 9691 * 9692 * Overwritten and set to `NearestFilter` by default. 9693 * 9694 * @type {(NearestFilter|NearestMipmapNearestFilter|NearestMipmapLinearFilter|LinearFilter|LinearMipmapNearestFilter|LinearMipmapLinearFilter)} 9695 * @default NearestFilter 9696 */ 9697 this.magFilter = NearestFilter; 9698 9699 /** 9700 * How the texture is sampled when a texel covers less than one pixel. 9701 * 9702 * Overwritten and set to `NearestFilter` by default. 9703 * 9704 * @type {(NearestFilter|NearestMipmapNearestFilter|NearestMipmapLinearFilter|LinearFilter|LinearMipmapNearestFilter|LinearMipmapLinearFilter)} 9705 * @default NearestFilter 9706 */ 9707 this.minFilter = NearestFilter; 9708 9709 /** 9710 * This defines how the texture is wrapped in the depth and corresponds to 9711 * *W* in UVW mapping. 9712 * 9713 * @type {(RepeatWrapping|ClampToEdgeWrapping|MirroredRepeatWrapping)} 9714 * @default ClampToEdgeWrapping 9715 */ 9716 this.wrapR = ClampToEdgeWrapping; 9717 9718 /** 9719 * Whether to generate mipmaps (if possible) for a texture. 9720 * 9721 * Overwritten and set to `false` by default. 9722 * 9723 * @type {boolean} 9724 * @default false 9725 */ 9726 this.generateMipmaps = false; 9727 9728 /** 9729 * If set to `true`, the texture is flipped along the vertical axis when 9730 * uploaded to the GPU. 9731 * 9732 * Overwritten and set to `false` by default. 9733 * 9734 * @type {boolean} 9735 * @default false 9736 */ 9737 this.flipY = false; 9738 9739 /** 9740 * Specifies the alignment requirements for the start of each pixel row in memory. 9741 * 9742 * Overwritten and set to `1` by default. 9743 * 9744 * @type {boolean} 9745 * @default 1 9746 */ 9747 this.unpackAlignment = 1; 9748 9749 /** 9750 * A set of all layers which need to be updated in the texture. 9751 * 9752 * @type {Set<number>} 9753 */ 9754 this.layerUpdates = new Set(); 9755 9756 } 9757 9758 /** 9759 * Copies the values of the given texture to this instance. 9760 * 9761 * @param {DataArrayTexture} source - The texture to copy. 9762 * @return {DataArrayTexture} A reference to this instance. 9763 */ 9764 copy( source ) { 9765 9766 super.copy( source ); 9767 9768 this.wrapR = source.wrapR; 9769 9770 return this; 9771 9772 } 9773 9774 /** 9775 * Describes that a specific layer of the texture needs to be updated. 9776 * Normally when {@link Texture#needsUpdate} is set to `true`, the 9777 * entire data texture array is sent to the GPU. Marking specific 9778 * layers will only transmit subsets of all mipmaps associated with a 9779 * specific depth in the array which is often much more performant. 9780 * 9781 * @param {number} layerIndex - The layer index that should be updated. 9782 */ 9783 addLayerUpdate( layerIndex ) { 9784 9785 this.layerUpdates.add( layerIndex ); 9786 9787 } 9788 9789 /** 9790 * Resets the layer updates registry. 9791 */ 9792 clearLayerUpdates() { 9793 9794 this.layerUpdates.clear(); 9795 9796 } 9797 9798} 9799 9800/** 9801 * An array render target used in context of {@link WebGLRenderer}. 9802 * 9803 * @augments WebGLRenderTarget 9804 */ 9805class WebGLArrayRenderTarget extends WebGLRenderTarget { 9806 9807 /** 9808 * Constructs a new array render target. 9809 * 9810 * @param {number} [width=1] - The width of the render target. 9811 * @param {number} [height=1] - The height of the render target. 9812 * @param {number} [depth=1] - The height of the render target. 9813 * @param {RenderTarget~Options} [options] - The configuration object. 9814 */ 9815 constructor( width = 1, height = 1, depth = 1, options = {} ) { 9816 9817 super( width, height, options ); 9818 9819 /** 9820 * This flag can be used for type testing. 9821 * 9822 * @type {boolean} 9823 * @readonly 9824 * @default true 9825 */ 9826 this.isWebGLArrayRenderTarget = true; 9827 9828 this.depth = depth; 9829 9830 /** 9831 * Overwritten with a different texture type. 9832 * 9833 * @type {DataArrayTexture} 9834 */ 9835 this.texture = new DataArrayTexture( null, width, height, depth ); 9836 this._setTextureOptions( options ); 9837 9838 this.texture.isRenderTargetTexture = true; 9839 9840 } 9841 9842} 9843 9844/** 9845 * Creates a three-dimensional texture from raw data, with parameters to 9846 * divide it into width, height, and depth. 9847 * 9848 * @augments Texture 9849 */ 9850class Data3DTexture extends Texture { 9851 9852 /** 9853 * Constructs a new data array texture. 9854 * 9855 * @param {?TypedArray} [data=null] - The buffer data. 9856 * @param {number} [width=1] - The width of the texture. 9857 * @param {number} [height=1] - The height of the texture. 9858 * @param {number} [depth=1] - The depth of the texture. 9859 */ 9860 constructor( data = null, width = 1, height = 1, depth = 1 ) { 9861 9862 // We're going to add .setXXX() methods for setting properties later. 9863 // Users can still set in Data3DTexture directly. 9864 // 9865 // const texture = new THREE.Data3DTexture( data, width, height, depth ); 9866 // texture.anisotropy = 16; 9867 // 9868 // See #14839 9869 9870 super( null ); 9871 9872 /** 9873 * This flag can be used for type testing. 9874 * 9875 * @type {boolean} 9876 * @readonly 9877 * @default true 9878 */ 9879 this.isData3DTexture = true; 9880 9881 /** 9882 * The image definition of a data texture. 9883 * 9884 * @type {{data:TypedArray,width:number,height:number,depth:number}} 9885 */ 9886 this.image = { data, width, height, depth }; 9887 9888 /** 9889 * How the texture is sampled when a texel covers more than one pixel. 9890 * 9891 * Overwritten and set to `NearestFilter` by default. 9892 * 9893 * @type {(NearestFilter|NearestMipmapNearestFilter|NearestMipmapLinearFilter|LinearFilter|LinearMipmapNearestFilter|LinearMipmapLinearFilter)} 9894 * @default NearestFilter 9895 */ 9896 this.magFilter = NearestFilter; 9897 9898 /** 9899 * How the texture is sampled when a texel covers less than one pixel. 9900 * 9901 * Overwritten and set to `NearestFilter` by default. 9902 * 9903 * @type {(NearestFilter|NearestMipmapNearestFilter|NearestMipmapLinearFilter|LinearFilter|LinearMipmapNearestFilter|LinearMipmapLinearFilter)} 9904 * @default NearestFilter 9905 */ 9906 this.minFilter = NearestFilter; 9907 9908 /** 9909 * This defines how the texture is wrapped in the depth and corresponds to 9910 * *W* in UVW mapping. 9911 * 9912 * @type {(RepeatWrapping|ClampToEdgeWrapping|MirroredRepeatWrapping)} 9913 * @default ClampToEdgeWrapping 9914 */ 9915 this.wrapR = ClampToEdgeWrapping; 9916 9917 /** 9918 * Whether to generate mipmaps (if possible) for a texture. 9919 * 9920 * Overwritten and set to `false` by default. 9921 * 9922 * @type {boolean} 9923 * @default false 9924 */ 9925 this.generateMipmaps = false; 9926 9927 /** 9928 * If set to `true`, the texture is flipped along the vertical axis when 9929 * uploaded to the GPU. 9930 * 9931 * Overwritten and set to `false` by default. 9932 * 9933 * @type {boolean} 9934 * @default false 9935 */ 9936 this.flipY = false; 9937 9938 /** 9939 * Specifies the alignment requirements for the start of each pixel row in memory. 9940 * 9941 * Overwritten and set to `1` by default. 9942 * 9943 * @type {boolean} 9944 * @default 1 9945 */ 9946 this.unpackAlignment = 1; 9947 9948 } 9949 9950 /** 9951 * Copies the values of the given texture to this instance. 9952 * 9953 * @param {Data3DTexture} source - The texture to copy. 9954 * @return {Data3DTexture} A reference to this instance. 9955 */ 9956 copy( source ) { 9957 9958 super.copy( source ); 9959 9960 this.wrapR = source.wrapR; 9961 9962 return this; 9963 9964 } 9965 9966} 9967 9968/** 9969 * A 3D render target used in context of {@link WebGLRenderer}. 9970 * 9971 * @augments WebGLRenderTarget 9972 */ 9973class WebGL3DRenderTarget extends WebGLRenderTarget { 9974 9975 /** 9976 * Constructs a new 3D render target. 9977 * 9978 * @param {number} [width=1] - The width of the render target. 9979 * @param {number} [height=1] - The height of the render target. 9980 * @param {number} [depth=1] - The height of the render target. 9981 * @param {RenderTarget~Options} [options] - The configuration object. 9982 */ 9983 constructor( width = 1, height = 1, depth = 1, options = {} ) { 9984 9985 super( width, height, options ); 9986 9987 /** 9988 * This flag can be used for type testing. 9989 * 9990 * @type {boolean} 9991 * @readonly 9992 * @default true 9993 */ 9994 this.isWebGL3DRenderTarget = true; 9995 9996 this.depth = depth; 9997 9998 /** 9999 * Overwritten with a different texture type. 10000 * 10001 * @type {Data3DTexture} 10002 */ 10003 this.texture = new Data3DTexture( null, width, height, depth ); 10004 this._setTextureOptions( options ); 10005 10006 this.texture.isRenderTargetTexture = true; 10007 10008 } 10009 10010} 10011 10012/** 10013 * Represents a 4x4 matrix. 10014 * 10015 * The most common use of a 4x4 matrix in 3D computer graphics is as a transformation matrix. 10016 * For an introduction to transformation matrices as used in WebGL, check out [this tutorial](https://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices) 10017 * 10018 * This allows a 3D vector representing a point in 3D space to undergo 10019 * transformations such as translation, rotation, shear, scale, reflection, 10020 * orthogonal or perspective projection and so on, by being multiplied by the 10021 * matrix. This is known as `applying` the matrix to the vector. 10022 * 10023 * A Note on Row-Major and Column-Major Ordering: 10024 * 10025 * The constructor and {@link Matrix3#set} method take arguments in 10026 * [row-major](https://en.wikipedia.org/wiki/Row-_and_column-major_order#Column-major_order) 10027 * order, while internally they are stored in the {@link Matrix3#elements} array in column-major order. 10028 * This means that calling: 10029 * ```js 10030 * const m = new THREE.Matrix4(); 10031 * m.set( 11, 12, 13, 14, 10032 * 21, 22, 23, 24, 10033 * 31, 32, 33, 34, 10034 * 41, 42, 43, 44 ); 10035 * ``` 10036 * will result in the elements array containing: 10037 * ```js 10038 * m.elements = [ 11, 21, 31, 41, 10039 * 12, 22, 32, 42, 10040 * 13, 23, 33, 43, 10041 * 14, 24, 34, 44 ]; 10042 * ``` 10043 * and internally all calculations are performed using column-major ordering. 10044 * However, as the actual ordering makes no difference mathematically and 10045 * most people are used to thinking about matrices in row-major order, the 10046 * three.js documentation shows matrices in row-major order. Just bear in 10047 * mind that if you are reading the source code, you'll have to take the 10048 * transpose of any matrices outlined here to make sense of the calculations. 10049 */ 10050class Matrix4 { 10051 10052 static { 10053 10054 /** 10055 * This flag can be used for type testing. 10056 * 10057 * @type {boolean} 10058 * @readonly 10059 * @default true 10060 */ 10061 Matrix4.prototype.isMatrix4 = true; 10062 10063 } 10064 10065 /** 10066 * Constructs a new 4x4 matrix. The arguments are supposed to be 10067 * in row-major order. If no arguments are provided, the constructor 10068 * initializes the matrix as an identity matrix. 10069 * 10070 * @param {number} [n11] - 1-1 matrix element. 10071 * @param {number} [n12] - 1-2 matrix element. 10072 * @param {number} [n13] - 1-3 matrix element. 10073 * @param {number} [n14] - 1-4 matrix element. 10074 * @param {number} [n21] - 2-1 matrix element. 10075 * @param {number} [n22] - 2-2 matrix element. 10076 * @param {number} [n23] - 2-3 matrix element. 10077 * @param {number} [n24] - 2-4 matrix element. 10078 * @param {number} [n31] - 3-1 matrix element. 10079 * @param {number} [n32] - 3-2 matrix element. 10080 * @param {number} [n33] - 3-3 matrix element. 10081 * @param {number} [n34] - 3-4 matrix element. 10082 * @param {number} [n41] - 4-1 matrix element. 10083 * @param {number} [n42] - 4-2 matrix element. 10084 * @param {number} [n43] - 4-3 matrix element. 10085 * @param {number} [n44] - 4-4 matrix element. 10086 */ 10087 constructor( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { 10088 10089 /** 10090 * A column-major list of matrix values. 10091 * 10092 * @type {Array<number>} 10093 */ 10094 this.elements = [ 10095 10096 1, 0, 0, 0, 10097 0, 1, 0, 0, 10098 0, 0, 1, 0, 10099 0, 0, 0, 1 10100 10101 ]; 10102 10103 if ( n11 !== undefined ) { 10104 10105 this.set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ); 10106 10107 } 10108 10109 } 10110 10111 /** 10112 * Sets the elements of the matrix.The arguments are supposed to be 10113 * in row-major order. 10114 * 10115 * @param {number} [n11] - 1-1 matrix element. 10116 * @param {number} [n12] - 1-2 matrix element. 10117 * @param {number} [n13] - 1-3 matrix element. 10118 * @param {number} [n14] - 1-4 matrix element. 10119 * @param {number} [n21] - 2-1 matrix element. 10120 * @param {number} [n22] - 2-2 matrix element. 10121 * @param {number} [n23] - 2-3 matrix element. 10122 * @param {number} [n24] - 2-4 matrix element. 10123 * @param {number} [n31] - 3-1 matrix element. 10124 * @param {number} [n32] - 3-2 matrix element. 10125 * @param {number} [n33] - 3-3 matrix element. 10126 * @param {number} [n34] - 3-4 matrix element. 10127 * @param {number} [n41] - 4-1 matrix element. 10128 * @param {number} [n42] - 4-2 matrix element. 10129 * @param {number} [n43] - 4-3 matrix element. 10130 * @param {number} [n44] - 4-4 matrix element. 10131 * @return {Matrix4} A reference to this matrix. 10132 */ 10133 set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { 10134 10135 const te = this.elements; 10136 10137 te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; 10138 te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; 10139 te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; 10140 te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; 10141 10142 return this; 10143 10144 } 10145 10146 /** 10147 * Sets this matrix to the 4x4 identity matrix. 10148 * 10149 * @return {Matrix4} A reference to this matrix. 10150 */ 10151 identity() { 10152 10153 this.set( 10154 10155 1, 0, 0, 0, 10156 0, 1, 0, 0, 10157 0, 0, 1, 0, 10158 0, 0, 0, 1 10159 10160 ); 10161 10162 return this; 10163 10164 } 10165 10166 /** 10167 * Returns a matrix with copied values from this instance. 10168 * 10169 * @return {Matrix4} A clone of this instance. 10170 */ 10171 clone() { 10172 10173 return new Matrix4().fromArray( this.elements ); 10174 10175 } 10176 10177 /** 10178 * Copies the values of the given matrix to this instance. 10179 * 10180 * @param {Matrix4} m - The matrix to copy. 10181 * @return {Matrix4} A reference to this matrix. 10182 */ 10183 copy( m ) { 10184 10185 const te = this.elements; 10186 const me = m.elements; 10187 10188 te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; 10189 te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; 10190 te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ]; 10191 te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ]; 10192 10193 return this; 10194 10195 } 10196 10197 /** 10198 * Copies the translation component of the given matrix 10199 * into this matrix's translation component. 10200 * 10201 * @param {Matrix4} m - The matrix to copy the translation component. 10202 * @return {Matrix4} A reference to this matrix. 10203 */ 10204 copyPosition( m ) { 10205 10206 const te = this.elements, me = m.elements; 10207 10208 te[ 12 ] = me[ 12 ]; 10209 te[ 13 ] = me[ 13 ]; 10210 te[ 14 ] = me[ 14 ]; 10211 10212 return this; 10213 10214 } 10215 10216 /** 10217 * Set the upper 3x3 elements of this matrix to the values of given 3x3 matrix. 10218 * 10219 * @param {Matrix3} m - The 3x3 matrix. 10220 * @return {Matrix4} A reference to this matrix. 10221 */ 10222 setFromMatrix3( m ) { 10223 10224 const me = m.elements; 10225 10226 this.set( 10227 10228 me[ 0 ], me[ 3 ], me[ 6 ], 0, 10229 me[ 1 ], me[ 4 ], me[ 7 ], 0, 10230 me[ 2 ], me[ 5 ], me[ 8 ], 0, 10231 0, 0, 0, 1 10232 10233 ); 10234 10235 return this; 10236 10237 } 10238 10239 /** 10240 * Extracts the basis vectors of this matrix into the three vectors provided. 10241 * 10242 * @param {Vector3} xAxis - The basis's x axis. 10243 * @param {Vector3} yAxis - The basis's y axis. 10244 * @param {Vector3} zAxis - The basis's z axis. 10245 * @return {Matrix4} A reference to this matrix. 10246 */ 10247 extractBasis( xAxis, yAxis, zAxis ) { 10248 10249 if ( this.determinantAffine() === 0 ) { 10250 10251 xAxis.set( 1, 0, 0 ); 10252 yAxis.set( 0, 1, 0 ); 10253 zAxis.set( 0, 0, 1 ); 10254 10255 return this; 10256 10257 } 10258 10259 xAxis.setFromMatrixColumn( this, 0 ); 10260 yAxis.setFromMatrixColumn( this, 1 ); 10261 zAxis.setFromMatrixColumn( this, 2 ); 10262 10263 return this; 10264 10265 } 10266 10267 /** 10268 * Sets the given basis vectors to this matrix. 10269 * 10270 * @param {Vector3} xAxis - The basis's x axis. 10271 * @param {Vector3} yAxis - The basis's y axis. 10272 * @param {Vector3} zAxis - The basis's z axis. 10273 * @return {Matrix4} A reference to this matrix. 10274 */ 10275 makeBasis( xAxis, yAxis, zAxis ) { 10276 10277 this.set( 10278 xAxis.x, yAxis.x, zAxis.x, 0, 10279 xAxis.y, yAxis.y, zAxis.y, 0, 10280 xAxis.z, yAxis.z, zAxis.z, 0, 10281 0, 0, 0, 1 10282 ); 10283 10284 return this; 10285 10286 } 10287 10288 /** 10289 * Extracts the rotation component of the given matrix 10290 * into this matrix's rotation component. 10291 * 10292 * Note: This method does not support reflection matrices. 10293 * 10294 * @param {Matrix4} m - The matrix. 10295 * @return {Matrix4} A reference to this matrix. 10296 */ 10297 extractRotation( m ) { 10298 10299 if ( m.determinantAffine() === 0 ) { 10300 10301 return this.identity(); 10302 10303 } 10304 10305 const te = this.elements; 10306 const me = m.elements; 10307 10308 const scaleX = 1 / _v1$7.setFromMatrixColumn( m, 0 ).length(); 10309 const scaleY = 1 / _v1$7.setFromMatrixColumn( m, 1 ).length(); 10310 const scaleZ = 1 / _v1$7.setFromMatrixColumn( m, 2 ).length(); 10311 10312 te[ 0 ] = me[ 0 ] * scaleX; 10313 te[ 1 ] = me[ 1 ] * scaleX; 10314 te[ 2 ] = me[ 2 ] * scaleX; 10315 te[ 3 ] = 0; 10316 10317 te[ 4 ] = me[ 4 ] * scaleY; 10318 te[ 5 ] = me[ 5 ] * scaleY; 10319 te[ 6 ] = me[ 6 ] * scaleY; 10320 te[ 7 ] = 0; 10321 10322 te[ 8 ] = me[ 8 ] * scaleZ; 10323 te[ 9 ] = me[ 9 ] * scaleZ; 10324 te[ 10 ] = me[ 10 ] * scaleZ; 10325 te[ 11 ] = 0; 10326 10327 te[ 12 ] = 0; 10328 te[ 13 ] = 0; 10329 te[ 14 ] = 0; 10330 te[ 15 ] = 1; 10331 10332 return this; 10333 10334 } 10335 10336 /** 10337 * Sets the rotation component (the upper left 3x3 matrix) of this matrix to 10338 * the rotation specified by the given Euler angles. The rest of 10339 * the matrix is set to the identity. Depending on the {@link Euler#order}, 10340 * there are six possible outcomes. See [this page](https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix) 10341 * for a complete list. 10342 * 10343 * @param {Euler} euler - The Euler angles. 10344 * @return {Matrix4} A reference to this matrix. 10345 */ 10346 makeRotationFromEuler( euler ) { 10347 10348 const te = this.elements; 10349 10350 const x = euler.x, y = euler.y, z = euler.z; 10351 const a = Math.cos( x ), b = Math.sin( x ); 10352 const c = Math.cos( y ), d = Math.sin( y ); 10353 const e = Math.cos( z ), f = Math.sin( z ); 10354 10355 if ( euler.order === 'XYZ' ) { 10356 10357 const ae = a * e, af = a * f, be = b * e, bf = b * f; 10358 10359 te[ 0 ] = c * e; 10360 te[ 4 ] = - c * f; 10361 te[ 8 ] = d; 10362 10363 te[ 1 ] = af + be * d; 10364 te[ 5 ] = ae - bf * d; 10365 te[ 9 ] = - b * c; 10366 10367 te[ 2 ] = bf - ae * d; 10368 te[ 6 ] = be + af * d; 10369 te[ 10 ] = a * c; 10370 10371 } else if ( euler.order === 'YXZ' ) { 10372 10373 const ce = c * e, cf = c * f, de = d * e, df = d * f; 10374 10375 te[ 0 ] = ce + df * b; 10376 te[ 4 ] = de * b - cf; 10377 te[ 8 ] = a * d; 10378 10379 te[ 1 ] = a * f; 10380 te[ 5 ] = a * e; 10381 te[ 9 ] = - b; 10382 10383 te[ 2 ] = cf * b - de; 10384 te[ 6 ] = df + ce * b; 10385 te[ 10 ] = a * c; 10386 10387 } else if ( euler.order === 'ZXY' ) { 10388 10389 const ce = c * e, cf = c * f, de = d * e, df = d * f; 10390 10391 te[ 0 ] = ce - df * b; 10392 te[ 4 ] = - a * f; 10393 te[ 8 ] = de + cf * b; 10394 10395 te[ 1 ] = cf + de * b; 10396 te[ 5 ] = a * e; 10397 te[ 9 ] = df - ce * b; 10398 10399 te[ 2 ] = - a * d; 10400 te[ 6 ] = b; 10401 te[ 10 ] = a * c; 10402 10403 } else if ( euler.order === 'ZYX' ) { 10404 10405 const ae = a * e, af = a * f, be = b * e, bf = b * f; 10406 10407 te[ 0 ] = c * e; 10408 te[ 4 ] = be * d - af; 10409 te[ 8 ] = ae * d + bf; 10410 10411 te[ 1 ] = c * f; 10412 te[ 5 ] = bf * d + ae; 10413 te[ 9 ] = af * d - be; 10414 10415 te[ 2 ] = - d; 10416 te[ 6 ] = b * c; 10417 te[ 10 ] = a * c; 10418 10419 } else if ( euler.order === 'YZX' ) { 10420 10421 const ac = a * c, ad = a * d, bc = b * c, bd = b * d; 10422 10423 te[ 0 ] = c * e; 10424 te[ 4 ] = bd - ac * f; 10425 te[ 8 ] = bc * f + ad; 10426 10427 te[ 1 ] = f; 10428 te[ 5 ] = a * e; 10429 te[ 9 ] = - b * e; 10430 10431 te[ 2 ] = - d * e; 10432 te[ 6 ] = ad * f + bc; 10433 te[ 10 ] = ac - bd * f; 10434 10435 } else if ( euler.order === 'XZY' ) { 10436 10437 const ac = a * c, ad = a * d, bc = b * c, bd = b * d; 10438 10439 te[ 0 ] = c * e; 10440 te[ 4 ] = - f; 10441 te[ 8 ] = d * e; 10442 10443 te[ 1 ] = ac * f + bd; 10444 te[ 5 ] = a * e; 10445 te[ 9 ] = ad * f - bc; 10446 10447 te[ 2 ] = bc * f - ad; 10448 te[ 6 ] = b * e; 10449 te[ 10 ] = bd * f + ac; 10450 10451 } 10452 10453 // bottom row 10454 te[ 3 ] = 0; 10455 te[ 7 ] = 0; 10456 te[ 11 ] = 0; 10457 10458 // last column 10459 te[ 12 ] = 0; 10460 te[ 13 ] = 0; 10461 te[ 14 ] = 0; 10462 te[ 15 ] = 1; 10463 10464 return this; 10465 10466 } 10467 10468 /** 10469 * Sets the rotation component of this matrix to the rotation specified by 10470 * the given Quaternion as outlined [here](https://en.wikipedia.org/wiki/Rotation_matrix#Quaternion) 10471 * The rest of the matrix is set to the identity. 10472 * 10473 * @param {Quaternion} q - The Quaternion. 10474 * @return {Matrix4} A reference to this matrix. 10475 */ 10476 makeRotationFromQuaternion( q ) { 10477 10478 return this.compose( _zero, q, _one ); 10479 10480 } 10481 10482 /** 10483 * Sets the rotation component of the transformation matrix, looking from `eye` towards 10484 * `target`, and oriented by the up-direction. 10485 * 10486 * @param {Vector3} eye - The eye vector. 10487 * @param {Vector3} target - The target vector. 10488 * @param {Vector3} up - The up vector. 10489 * @return {Matrix4} A reference to this matrix. 10490 */ 10491 lookAt( eye, target, up ) { 10492 10493 const te = this.elements; 10494 10495 _z.subVectors( eye, target ); 10496 10497 if ( _z.lengthSq() === 0 ) { 10498 10499 // eye and target are in the same position 10500 10501 _z.z = 1; 10502 10503 } 10504 10505 _z.normalize(); 10506 _x.crossVectors( up, _z ); 10507 10508 if ( _x.lengthSq() === 0 ) { 10509 10510 // up and z are parallel 10511 10512 if ( Math.abs( up.z ) === 1 ) { 10513 10514 _z.x += 0.0001; 10515 10516 } else { 10517 10518 _z.z += 0.0001; 10519 10520 } 10521 10522 _z.normalize(); 10523 _x.crossVectors( up, _z ); 10524 10525 } 10526 10527 _x.normalize(); 10528 _y.crossVectors( _z, _x ); 10529 10530 te[ 0 ] = _x.x; te[ 4 ] = _y.x; te[ 8 ] = _z.x; 10531 te[ 1 ] = _x.y; te[ 5 ] = _y.y; te[ 9 ] = _z.y; 10532 te[ 2 ] = _x.z; te[ 6 ] = _y.z; te[ 10 ] = _z.z; 10533 10534 return this; 10535 10536 } 10537 10538 /** 10539 * Post-multiplies this matrix by the given 4x4 matrix. 10540 * 10541 * @param {Matrix4} m - The matrix to multiply with. 10542 * @return {Matrix4} A reference to this matrix. 10543 */ 10544 multiply( m ) { 10545 10546 return this.multiplyMatrices( this, m ); 10547 10548 } 10549 10550 /** 10551 * Pre-multiplies this matrix by the given 4x4 matrix. 10552 * 10553 * @param {Matrix4} m - The matrix to multiply with. 10554 * @return {Matrix4} A reference to this matrix. 10555 */ 10556 premultiply( m ) { 10557 10558 return this.multiplyMatrices( m, this ); 10559 10560 } 10561 10562 /** 10563 * Multiples the given 4x4 matrices and stores the result 10564 * in this matrix. 10565 * 10566 * @param {Matrix4} a - The first matrix. 10567 * @param {Matrix4} b - The second matrix. 10568 * @return {Matrix4} A reference to this matrix. 10569 */ 10570 multiplyMatrices( a, b ) { 10571 10572 const ae = a.elements; 10573 const be = b.elements; 10574 const te = this.elements; 10575 10576 const a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; 10577 const a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; 10578 const a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; 10579 const a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; 10580 10581 const b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; 10582 const b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; 10583 const b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; 10584 const b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; 10585 10586 te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; 10587 te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; 10588 te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; 10589 te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; 10590 10591 te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; 10592 te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; 10593 te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; 10594 te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; 10595 10596 te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; 10597 te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; 10598 te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; 10599 te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; 10600 10601 te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; 10602 te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; 10603 te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; 10604 te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; 10605 10606 return this; 10607 10608 } 10609 10610 /** 10611 * Multiplies every component of the matrix by the given scalar. 10612 * 10613 * @param {number} s - The scalar. 10614 * @return {Matrix4} A reference to this matrix. 10615 */ 10616 multiplyScalar( s ) { 10617 10618 const te = this.elements; 10619 10620 te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; 10621 te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; 10622 te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; 10623 te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; 10624 10625 return this; 10626 10627 } 10628 10629 /** 10630 * Computes and returns the determinant of this matrix. 10631 * 10632 * @return {number} The determinant. 10633 */ 10634 determinant() { 10635 10636 const te = this.elements; 10637 10638 const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; 10639 const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; 10640 const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; 10641 const n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; 10642 10643 const t11 = n23 * n34 - n24 * n33; 10644 const t12 = n22 * n34 - n24 * n32; 10645 const t13 = n22 * n33 - n23 * n32; 10646 10647 const t21 = n21 * n34 - n24 * n31; 10648 const t22 = n21 * n33 - n23 * n31; 10649 const t23 = n21 * n32 - n22 * n31; 10650 10651 return n11 * ( n42 * t11 - n43 * t12 + n44 * t13 ) - 10652 n12 * ( n41 * t11 - n43 * t21 + n44 * t22 ) + 10653 n13 * ( n41 * t12 - n42 * t21 + n44 * t23 ) - 10654 n14 * ( n41 * t13 - n42 * t22 + n43 * t23 ); 10655 10656 } 10657 10658 /** 10659 * Computes and returns the determinant of the 4x4 matrix, but assumes the 10660 * matrix is affine, saving some computations. 10661 * 10662 * For affine matrices (like an object's world matrix), this value equals the 10663 * full 4x4 {@link Matrix4#determinant} but is cheaper to compute. 10664 * 10665 * Assumes the bottom row is [0, 0, 0, 1]. 10666 * 10667 * @return {number} The determinant of the matrix. 10668 */ 10669 determinantAffine() { 10670 10671 const te = this.elements; 10672 10673 const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ]; 10674 const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ]; 10675 const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ]; 10676 10677 return n11 * ( n22 * n33 - n23 * n32 ) - 10678 n12 * ( n21 * n33 - n23 * n31 ) + 10679 n13 * ( n21 * n32 - n22 * n31 ); 10680 10681 } 10682 10683 /** 10684 * Transposes this matrix in place. 10685 * 10686 * @return {Matrix4} A reference to this matrix. 10687 */ 10688 transpose() { 10689 10690 const te = this.elements; 10691 let tmp; 10692 10693 tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; 10694 tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; 10695 tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; 10696 10697 tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; 10698 tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; 10699 tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; 10700 10701 return this; 10702 10703 } 10704 10705 /** 10706 * Sets the position component for this matrix from the given vector, 10707 * without affecting the rest of the matrix. 10708 * 10709 * @param {number|Vector3} x - The x component of the vector or alternatively the vector object. 10710 * @param {number} y - The y component of the vector. 10711 * @param {number} z - The z component of the vector. 10712 * @return {Matrix4} A reference to this matrix. 10713 */ 10714 setPosition( x, y, z ) { 10715 10716 const te = this.elements; 10717 10718 if ( x.isVector3 ) { 10719 10720 te[ 12 ] = x.x; 10721 te[ 13 ] = x.y; 10722 te[ 14 ] = x.z; 10723 10724 } else { 10725 10726 te[ 12 ] = x; 10727 te[ 13 ] = y; 10728 te[ 14 ] = z; 10729 10730 } 10731 10732 return this; 10733 10734 } 10735 10736 /** 10737 * Inverts this matrix, using the [analytic method](https://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution). 10738 * You can not invert with a determinant of zero. If you attempt this, the method produces 10739 * a zero matrix instead. 10740 * 10741 * @return {Matrix4} A reference to this matrix. 10742 */ 10743 invert() { 10744 10745 // based on https://github.com/toji/gl-matrix 10746 const te = this.elements, 10747 10748 n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n41 = te[ 3 ], 10749 n12 = te[ 4 ], n22 = te[ 5 ], n32 = te[ 6 ], n42 = te[ 7 ], 10750 n13 = te[ 8 ], n23 = te[ 9 ], n33 = te[ 10 ], n43 = te[ 11 ], 10751 n14 = te[ 12 ], n24 = te[ 13 ], n34 = te[ 14 ], n44 = te[ 15 ], 10752 10753 t1 = n11 * n22 - n21 * n12, 10754 t2 = n11 * n32 - n31 * n12, 10755 t3 = n11 * n42 - n41 * n12, 10756 t4 = n21 * n32 - n31 * n22, 10757 t5 = n21 * n42 - n41 * n22, 10758 t6 = n31 * n42 - n41 * n32, 10759 t7 = n13 * n24 - n23 * n14, 10760 t8 = n13 * n34 - n33 * n14, 10761 t9 = n13 * n44 - n43 * n14, 10762 t10 = n23 * n34 - n33 * n24, 10763 t11 = n23 * n44 - n43 * n24, 10764 t12 = n33 * n44 - n43 * n34; 10765 10766 const det = t1 * t12 - t2 * t11 + t3 * t10 + t4 * t9 - t5 * t8 + t6 * t7; 10767 10768 if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); 10769 10770 const detInv = 1 / det; 10771 10772 te[ 0 ] = ( n22 * t12 - n32 * t11 + n42 * t10 ) * detInv; 10773 te[ 1 ] = ( n31 * t11 - n21 * t12 - n41 * t10 ) * detInv; 10774 te[ 2 ] = ( n24 * t6 - n34 * t5 + n44 * t4 ) * detInv; 10775 te[ 3 ] = ( n33 * t5 - n23 * t6 - n43 * t4 ) * detInv; 10776 10777 te[ 4 ] = ( n32 * t9 - n12 * t12 - n42 * t8 ) * detInv; 10778 te[ 5 ] = ( n11 * t12 - n31 * t9 + n41 * t8 ) * detInv; 10779 te[ 6 ] = ( n34 * t3 - n14 * t6 - n44 * t2 ) * detInv; 10780 te[ 7 ] = ( n13 * t6 - n33 * t3 + n43 * t2 ) * detInv; 10781 10782 te[ 8 ] = ( n12 * t11 - n22 * t9 + n42 * t7 ) * detInv; 10783 te[ 9 ] = ( n21 * t9 - n11 * t11 - n41 * t7 ) * detInv; 10784 te[ 10 ] = ( n14 * t5 - n24 * t3 + n44 * t1 ) * detInv; 10785 te[ 11 ] = ( n23 * t3 - n13 * t5 - n43 * t1 ) * detInv; 10786 10787 te[ 12 ] = ( n22 * t8 - n12 * t10 - n32 * t7 ) * detInv; 10788 te[ 13 ] = ( n11 * t10 - n21 * t8 + n31 * t7 ) * detInv; 10789 te[ 14 ] = ( n24 * t2 - n14 * t4 - n34 * t1 ) * detInv; 10790 te[ 15 ] = ( n13 * t4 - n23 * t2 + n33 * t1 ) * detInv; 10791 10792 return this; 10793 10794 } 10795 10796 /** 10797 * Scales each of the first three columns of this matrix by the corresponding component of the given vector. 10798 * 10799 * @param {Vector3} v - The scale vector. 10800 * @return {Matrix4} A reference to this matrix. 10801 */ 10802 scale( v ) { 10803 10804 const te = this.elements; 10805 const x = v.x, y = v.y, z = v.z; 10806 10807 te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; 10808 te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; 10809 te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; 10810 te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; 10811 10812 return this; 10813 10814 } 10815 10816 /** 10817 * Gets the maximum scale value of the three axes. 10818 * 10819 * @return {number} The maximum scale. 10820 */ 10821 getMaxScaleOnAxis() { 10822 10823 const te = this.elements; 10824 10825 const scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; 10826 const scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; 10827 const scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; 10828 10829 return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); 10830 10831 } 10832 10833 /** 10834 * Sets this matrix as a translation transform from the given vector. 10835 * 10836 * @param {number|Vector3} x - The amount to translate in the X axis or alternatively a translation vector. 10837 * @param {number} y - The amount to translate in the Y axis. 10838 * @param {number} z - The amount to translate in the z axis. 10839 * @return {Matrix4} A reference to this matrix. 10840 */ 10841 makeTranslation( x, y, z ) { 10842 10843 if ( x.isVector3 ) { 10844 10845 this.set( 10846 10847 1, 0, 0, x.x, 10848 0, 1, 0, x.y, 10849 0, 0, 1, x.z, 10850 0, 0, 0, 1 10851 10852 ); 10853 10854 } else { 10855 10856 this.set( 10857 10858 1, 0, 0, x, 10859 0, 1, 0, y, 10860 0, 0, 1, z, 10861 0, 0, 0, 1 10862 10863 ); 10864 10865 } 10866 10867 return this; 10868 10869 } 10870 10871 /** 10872 * Sets this matrix as a rotational transformation around the X axis by 10873 * the given angle. 10874 * 10875 * @param {number} theta - The rotation in radians. 10876 * @return {Matrix4} A reference to this matrix. 10877 */ 10878 makeRotationX( theta ) { 10879 10880 const c = Math.cos( theta ), s = Math.sin( theta ); 10881 10882 this.set( 10883 10884 1, 0, 0, 0, 10885 0, c, - s, 0, 10886 0, s, c, 0, 10887 0, 0, 0, 1 10888 10889 ); 10890 10891 return this; 10892 10893 } 10894 10895 /** 10896 * Sets this matrix as a rotational transformation around the Y axis by 10897 * the given angle. 10898 * 10899 * @param {number} theta - The rotation in radians. 10900 * @return {Matrix4} A reference to this matrix. 10901 */ 10902 makeRotationY( theta ) { 10903 10904 const c = Math.cos( theta ), s = Math.sin( theta ); 10905 10906 this.set( 10907 10908 c, 0, s, 0, 10909 0, 1, 0, 0, 10910 - s, 0, c, 0, 10911 0, 0, 0, 1 10912 10913 ); 10914 10915 return this; 10916 10917 } 10918 10919 /** 10920 * Sets this matrix as a rotational transformation around the Z axis by 10921 * the given angle. 10922 * 10923 * @param {number} theta - The rotation in radians. 10924 * @return {Matrix4} A reference to this matrix. 10925 */ 10926 makeRotationZ( theta ) { 10927 10928 const c = Math.cos( theta ), s = Math.sin( theta ); 10929 10930 this.set( 10931 10932 c, - s, 0, 0, 10933 s, c, 0, 0, 10934 0, 0, 1, 0, 10935 0, 0, 0, 1 10936 10937 ); 10938 10939 return this; 10940 10941 } 10942 10943 /** 10944 * Sets this matrix as a rotational transformation around the given axis by 10945 * the given angle. 10946 * 10947 * @param {Vector3} axis - The normalized rotation axis. 10948 * @param {number} angle - The rotation in radians. 10949 * @return {Matrix4} A reference to this matrix. 10950 */ 10951 makeRotationAxis( axis, angle ) { 10952 10953 const c = Math.cos( angle ); 10954 const s = Math.sin( angle ); 10955 const t = 1 - c; 10956 const x = axis.x, y = axis.y, z = axis.z; 10957 const tx = t * x, ty = t * y; 10958 10959 this.set( 10960 10961 tx * x + c, tx * y - s * z, tx * z + s * y, 0, 10962 tx * y + s * z, ty * y + c, ty * z - s * x, 0, 10963 tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 10964 0, 0, 0, 1 10965 10966 ); 10967 10968 return this; 10969 10970 } 10971 10972 /** 10973 * Sets this matrix as a scale transformation. 10974 * 10975 * @param {number} x - The amount to scale in the X axis. 10976 * @param {number} y - The amount to scale in the Y axis. 10977 * @param {number} z - The amount to scale in the Z axis. 10978 * @return {Matrix4} A reference to this matrix. 10979 */ 10980 makeScale( x, y, z ) { 10981 10982 this.set( 10983 10984 x, 0, 0, 0, 10985 0, y, 0, 0, 10986 0, 0, z, 0, 10987 0, 0, 0, 1 10988 10989 ); 10990 10991 return this; 10992 10993 } 10994 10995 /** 10996 * Sets this matrix as a shear transformation. 10997 * 10998 * @param {number} xy - The amount to shear X by Y. 10999 * @param {number} xz - The amount to shear X by Z. 11000 * @param {number} yx - The amount to shear Y by X. 11001 * @param {number} yz - The amount to shear Y by Z. 11002 * @param {number} zx - The amount to shear Z by X. 11003 * @param {number} zy - The amount to shear Z by Y. 11004 * @return {Matrix4} A reference to this matrix. 11005 */ 11006 makeShear( xy, xz, yx, yz, zx, zy ) { 11007 11008 this.set( 11009 11010 1, yx, zx, 0, 11011 xy, 1, zy, 0, 11012 xz, yz, 1, 0, 11013 0, 0, 0, 1 11014 11015 ); 11016 11017 return this; 11018 11019 } 11020 11021 /** 11022 * Sets this matrix to the transformation composed of the given position, 11023 * rotation (Quaternion) and scale. 11024 * 11025 * @param {Vector3} position - The position vector. 11026 * @param {Quaternion} quaternion - The rotation as a Quaternion. 11027 * @param {Vector3} scale - The scale vector. 11028 * @return {Matrix4} A reference to this matrix. 11029 */ 11030 compose( position, quaternion, scale ) { 11031 11032 const te = this.elements; 11033 11034 const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; 11035 const x2 = x + x, y2 = y + y, z2 = z + z; 11036 const xx = x * x2, xy = x * y2, xz = x * z2; 11037 const yy = y * y2, yz = y * z2, zz = z * z2; 11038 const wx = w * x2, wy = w * y2, wz = w * z2; 11039 11040 const sx = scale.x, sy = scale.y, sz = scale.z; 11041 11042 te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; 11043 te[ 1 ] = ( xy + wz ) * sx; 11044 te[ 2 ] = ( xz - wy ) * sx; 11045 te[ 3 ] = 0; 11046 11047 te[ 4 ] = ( xy - wz ) * sy; 11048 te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; 11049 te[ 6 ] = ( yz + wx ) * sy; 11050 te[ 7 ] = 0; 11051 11052 te[ 8 ] = ( xz + wy ) * sz; 11053 te[ 9 ] = ( yz - wx ) * sz; 11054 te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; 11055 te[ 11 ] = 0; 11056 11057 te[ 12 ] = position.x; 11058 te[ 13 ] = position.y; 11059 te[ 14 ] = position.z; 11060 te[ 15 ] = 1; 11061 11062 return this; 11063 11064 } 11065 11066 /** 11067 * Decomposes this matrix into its position, rotation and scale components 11068 * and provides the result in the given objects. 11069 * 11070 * Note: Not all matrices are decomposable in this way. For example, if an 11071 * object has a non-uniformly scaled parent, then the object's world matrix 11072 * may not be decomposable, and this method may not be appropriate. 11073 * 11074 * @param {Vector3} position - The position vector. 11075 * @param {Quaternion} quaternion - The rotation as a Quaternion. 11076 * @param {Vector3} scale - The scale vector. 11077 * @return {Matrix4} A reference to this matrix. 11078 */ 11079 decompose( position, quaternion, scale ) { 11080 11081 const te = this.elements; 11082 11083 position.x = te[ 12 ]; 11084 position.y = te[ 13 ]; 11085 position.z = te[ 14 ]; 11086 11087 const det = this.determinantAffine(); 11088 11089 if ( det === 0 ) { 11090 11091 scale.set( 1, 1, 1 ); 11092 quaternion.identity(); 11093 11094 return this; 11095 11096 } 11097 11098 let sx = _v1$7.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); 11099 const sy = _v1$7.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); 11100 const sz = _v1$7.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); 11101 11102 // if determinant is negative, we need to invert one scale 11103 if ( det < 0 ) sx = - sx; 11104 11105 // scale the rotation part 11106 _m1$2.copy( this ); 11107 11108 const invSX = 1 / sx; 11109 const invSY = 1 / sy; 11110 const invSZ = 1 / sz; 11111 11112 _m1$2.elements[ 0 ] *= invSX; 11113 _m1$2.elements[ 1 ] *= invSX; 11114 _m1$2.elements[ 2 ] *= invSX; 11115 11116 _m1$2.elements[ 4 ] *= invSY; 11117 _m1$2.elements[ 5 ] *= invSY; 11118 _m1$2.elements[ 6 ] *= invSY; 11119 11120 _m1$2.elements[ 8 ] *= invSZ; 11121 _m1$2.elements[ 9 ] *= invSZ; 11122 _m1$2.elements[ 10 ] *= invSZ; 11123 11124 quaternion.setFromRotationMatrix( _m1$2 ); 11125 11126 scale.x = sx; 11127 scale.y = sy; 11128 scale.z = sz; 11129 11130 return this; 11131 11132 } 11133 11134 /** 11135 * Creates a perspective projection matrix. This is used internally by 11136 * {@link PerspectiveCamera#updateProjectionMatrix}. 11137 11138 * @param {number} left - Left boundary of the viewing frustum at the near plane. 11139 * @param {number} right - Right boundary of the viewing frustum at the near plane. 11140 * @param {number} top - Top boundary of the viewing frustum at the near plane. 11141 * @param {number} bottom - Bottom boundary of the viewing frustum at the near plane. 11142 * @param {number} near - The distance from the camera to the near plane. 11143 * @param {number} far - The distance from the camera to the far plane. 11144 * @param {(WebGLCoordinateSystem|WebGPUCoordinateSystem)} [coordinateSystem=WebGLCoordinateSystem] - The coordinate system. 11145 * @param {boolean} [reversedDepth=false] - Whether to use a reversed depth. 11146 * @return {Matrix4} A reference to this matrix. 11147 */ 11148 makePerspective( left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem, reversedDepth = false ) { 11149 11150 const te = this.elements; 11151 11152 const x = 2 * near / ( right - left ); 11153 const y = 2 * near / ( top - bottom ); 11154 11155 const a = ( right + left ) / ( right - left ); 11156 const b = ( top + bottom ) / ( top - bottom ); 11157 11158 let c, d; 11159 11160 if ( reversedDepth ) { 11161 11162 c = near / ( far - near ); 11163 d = ( far * near ) / ( far - near ); 11164 11165 } else { 11166 11167 if ( coordinateSystem === WebGLCoordinateSystem ) { 11168 11169 c = - ( far + near ) / ( far - near ); 11170 d = ( -2 * far * near ) / ( far - near ); 11171 11172 } else if ( coordinateSystem === WebGPUCoordinateSystem ) { 11173 11174 c = - far / ( far - near ); 11175 d = ( - far * near ) / ( far - near ); 11176 11177 } else { 11178 11179 throw new Error( 'THREE.Matrix4.makePerspective(): Invalid coordinate system: ' + coordinateSystem ); 11180 11181 } 11182 11183 } 11184 11185 te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; 11186 te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; 11187 te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; 11188 te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = -1; te[ 15 ] = 0; 11189 11190 return this; 11191 11192 } 11193 11194 /** 11195 * Creates a orthographic projection matrix. This is used internally by 11196 * {@link OrthographicCamera#updateProjectionMatrix}. 11197 11198 * @param {number} left - Left boundary of the viewing frustum at the near plane. 11199 * @param {number} right - Right boundary of the viewing frustum at the near plane. 11200 * @param {number} top - Top boundary of the viewing frustum at the near plane. 11201 * @param {number} bottom - Bottom boundary of the viewing frustum at the near plane. 11202 * @param {number} near - The distance from the camera to the near plane. 11203 * @param {number} far - The distance from the camera to the far plane. 11204 * @param {(WebGLCoordinateSystem|WebGPUCoordinateSystem)} [coordinateSystem=WebGLCoordinateSystem] - The coordinate system. 11205 * @param {boolean} [reversedDepth=false] - Whether to use a reversed depth. 11206 * @return {Matrix4} A reference to this matrix. 11207 */ 11208 makeOrthographic( left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem, reversedDepth = false ) { 11209 11210 const te = this.elements; 11211 11212 const x = 2 / ( right - left ); 11213 const y = 2 / ( top - bottom ); 11214 11215 const a = - ( right + left ) / ( right - left ); 11216 const b = - ( top + bottom ) / ( top - bottom ); 11217 11218 let c, d; 11219 11220 if ( reversedDepth ) { 11221 11222 c = 1 / ( far - near ); 11223 d = far / ( far - near ); 11224 11225 } else { 11226 11227 if ( coordinateSystem === WebGLCoordinateSystem ) { 11228 11229 c = -2 / ( far - near ); 11230 d = - ( far + near ) / ( far - near ); 11231 11232 } else if ( coordinateSystem === WebGPUCoordinateSystem ) { 11233 11234 c = -1 / ( far - near ); 11235 d = - near / ( far - near ); 11236 11237 } else { 11238 11239 throw new Error( 'THREE.Matrix4.makeOrthographic(): Invalid coordinate system: ' + coordinateSystem ); 11240 11241 } 11242 11243 } 11244 11245 te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = a; 11246 te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = 0; te[ 13 ] = b; 11247 te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; 11248 te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; 11249 11250 return this; 11251 11252 } 11253 11254 /** 11255 * Returns `true` if this matrix is equal with the given one. 11256 * 11257 * @param {Matrix4} matrix - The matrix to test for equality. 11258 * @return {boolean} Whether this matrix is equal with the given one. 11259 */ 11260 equals( matrix ) { 11261 11262 const te = this.elements; 11263 const me = matrix.elements; 11264 11265 for ( let i = 0; i < 16; i ++ ) { 11266 11267 if ( te[ i ] !== me[ i ] ) return false; 11268 11269 } 11270 11271 return true; 11272 11273 } 11274 11275 /** 11276 * Sets the elements of the matrix from the given array. 11277 * 11278 * @param {Array<number>} array - The matrix elements in column-major order. 11279 * @param {number} [offset=0] - Index of the first element in the array. 11280 * @return {Matrix4} A reference to this matrix. 11281 */ 11282 fromArray( array, offset = 0 ) { 11283 11284 for ( let i = 0; i < 16; i ++ ) { 11285 11286 this.elements[ i ] = array[ i + offset ]; 11287 11288 } 11289 11290 return this; 11291 11292 } 11293 11294 /** 11295 * Writes the elements of this matrix to the given array. If no array is provided, 11296 * the method returns a new instance. 11297 * 11298 * @param {Array<number>} [array=[]] - The target array holding the matrix elements in column-major order. 11299 * @param {number} [offset=0] - Index of the first element in the array. 11300 * @return {Array<number>} The matrix elements in column-major order. 11301 */ 11302 toArray( array = [], offset = 0 ) { 11303 11304 const te = this.elements; 11305 11306 array[ offset ] = te[ 0 ]; 11307 array[ offset + 1 ] = te[ 1 ]; 11308 array[ offset + 2 ] = te[ 2 ]; 11309 array[ offset + 3 ] = te[ 3 ]; 11310 11311 array[ offset + 4 ] = te[ 4 ]; 11312 array[ offset + 5 ] = te[ 5 ]; 11313 array[ offset + 6 ] = te[ 6 ]; 11314 array[ offset + 7 ] = te[ 7 ]; 11315 11316 array[ offset + 8 ] = te[ 8 ]; 11317 array[ offset + 9 ] = te[ 9 ]; 11318 array[ offset + 10 ] = te[ 10 ]; 11319 array[ offset + 11 ] = te[ 11 ]; 11320 11321 array[ offset + 12 ] = te[ 12 ]; 11322 array[ offset + 13 ] = te[ 13 ]; 11323 array[ offset + 14 ] = te[ 14 ]; 11324 array[ offset + 15 ] = te[ 15 ]; 11325 11326 return array; 11327 11328 } 11329 11330} 11331 11332const _v1$7 = /*@__PURE__*/ new Vector3(); 11333const _m1$2 = /*@__PURE__*/ new Matrix4(); 11334const _zero = /*@__PURE__*/ new Vector3( 0, 0, 0 ); 11335const _one = /*@__PURE__*/ new Vector3( 1, 1, 1 ); 11336const _x = /*@__PURE__*/ new Vector3(); 11337const _y = /*@__PURE__*/ new Vector3(); 11338const _z = /*@__PURE__*/ new Vector3(); 11339 11340const _matrix$2 = /*@__PURE__*/ new Matrix4(); 11341const _quaternion$4 = /*@__PURE__*/ new Quaternion(); 11342 11343/** 11344 * A class representing Euler angles. 11345 * 11346 * Euler angles describe a rotational transformation by rotating an object on 11347 * its various axes in specified amounts per axis, and a specified axis 11348 * order. 11349 * 11350 * Iterating through an instance will yield its components (x, y, z, 11351 * order) in the corresponding order. 11352 * 11353 * ```js 11354 * const a = new THREE.Euler( 0, 1, 1.57, 'XYZ' ); 11355 * const b = new THREE.Vector3( 1, 0, 1 ); 11356 * b.applyEuler(a); 11357 * ``` 11358 */ 11359class Euler { 11360 11361 /** 11362 * Constructs a new euler instance. 11363 * 11364 * @param {number} [x=0] - The angle of the x axis in radians. 11365 * @param {number} [y=0] - The angle of the y axis in radians. 11366 * @param {number} [z=0] - The angle of the z axis in radians. 11367 * @param {string} [order=Euler.DEFAULT_ORDER] - A string representing the order that the rotations are applied. 11368 */ 11369 constructor( x = 0, y = 0, z = 0, order = Euler.DEFAULT_ORDER ) { 11370 11371 /** 11372 * This flag can be used for type testing. 11373 * 11374 * @type {boolean} 11375 * @readonly 11376 * @default true 11377 */ 11378 this.isEuler = true; 11379 11380 this._x = x; 11381 this._y = y; 11382 this._z = z; 11383 this._order = order; 11384 11385 } 11386 11387 /** 11388 * The angle of the x axis in radians. 11389 * 11390 * @type {number} 11391 * @default 0 11392 */ 11393 get x() { 11394 11395 return this._x; 11396 11397 } 11398 11399 set x( value ) { 11400 11401 this._x = value; 11402 this._onChangeCallback(); 11403 11404 } 11405 11406 /** 11407 * The angle of the y axis in radians. 11408 * 11409 * @type {number} 11410 * @default 0 11411 */ 11412 get y() { 11413 11414 return this._y; 11415 11416 } 11417 11418 set y( value ) { 11419 11420 this._y = value; 11421 this._onChangeCallback(); 11422 11423 } 11424 11425 /** 11426 * The angle of the z axis in radians. 11427 * 11428 * @type {number} 11429 * @default 0 11430 */ 11431 get z() { 11432 11433 return this._z; 11434 11435 } 11436 11437 set z( value ) { 11438 11439 this._z = value; 11440 this._onChangeCallback(); 11441 11442 } 11443 11444 /** 11445 * A string representing the order that the rotations are applied. 11446 * 11447 * @type {string} 11448 * @default 'XYZ' 11449 */ 11450 get order() { 11451 11452 return this._order; 11453 11454 } 11455 11456 set order( value ) { 11457 11458 this._order = value; 11459 this._onChangeCallback(); 11460 11461 } 11462 11463 /** 11464 * Sets the Euler components. 11465 * 11466 * @param {number} x - The angle of the x axis in radians. 11467 * @param {number} y - The angle of the y axis in radians. 11468 * @param {number} z - The angle of the z axis in radians. 11469 * @param {string} [order] - A string representing the order that the rotations are applied. 11470 * @return {Euler} A reference to this Euler instance. 11471 */ 11472 set( x, y, z, order = this._order ) { 11473 11474 this._x = x; 11475 this._y = y; 11476 this._z = z; 11477 this._order = order; 11478 11479 this._onChangeCallback(); 11480 11481 return this; 11482 11483 } 11484 11485 /** 11486 * Returns a new Euler instance with copied values from this instance. 11487 * 11488 * @return {Euler} A clone of this instance. 11489 */ 11490 clone() { 11491 11492 return new this.constructor( this._x, this._y, this._z, this._order ); 11493 11494 } 11495 11496 /** 11497 * Copies the values of the given Euler instance to this instance. 11498 * 11499 * @param {Euler} euler - The Euler instance to copy. 11500 * @return {Euler} A reference to this Euler instance. 11501 */ 11502 copy( euler ) { 11503 11504 this._x = euler._x; 11505 this._y = euler._y; 11506 this._z = euler._z; 11507 this._order = euler._order; 11508 11509 this._onChangeCallback(); 11510 11511 return this; 11512 11513 } 11514 11515 /** 11516 * Sets the angles of this Euler instance from a pure rotation matrix. 11517 * 11518 * @param {Matrix4} m - A 4x4 matrix of which the upper 3x3 of matrix is a pure rotation matrix (i.e. unscaled). 11519 * @param {string} [order] - A string representing the order that the rotations are applied. 11520 * @param {boolean} [update=true] - Whether the internal `onChange` callback should be executed or not. 11521 * @return {Euler} A reference to this Euler instance. 11522 */ 11523 setFromRotationMatrix( m, order = this._order, update = true ) { 11524 11525 const te = m.elements; 11526 const m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; 11527 const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; 11528 const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; 11529 11530 switch ( order ) { 11531 11532 case 'XYZ': 11533 11534 this._y = Math.asin( clamp( m13, -1, 1 ) ); 11535 11536 if ( Math.abs( m13 ) < 0.9999999 ) { 11537 11538 this._x = Math.atan2( - m23, m33 ); 11539 this._z = Math.atan2( - m12, m11 ); 11540 11541 } else { 11542 11543 this._x = Math.atan2( m32, m22 ); 11544 this._z = 0; 11545 11546 } 11547 11548 break; 11549 11550 case 'YXZ': 11551 11552 this._x = Math.asin( - clamp( m23, -1, 1 ) ); 11553 11554 if ( Math.abs( m23 ) < 0.9999999 ) { 11555 11556 this._y = Math.atan2( m13, m33 ); 11557 this._z = Math.atan2( m21, m22 ); 11558 11559 } else { 11560 11561 this._y = Math.atan2( - m31, m11 ); 11562 this._z = 0; 11563 11564 } 11565 11566 break; 11567 11568 case 'ZXY': 11569 11570 this._x = Math.asin( clamp( m32, -1, 1 ) ); 11571 11572 if ( Math.abs( m32 ) < 0.9999999 ) { 11573 11574 this._y = Math.atan2( - m31, m33 ); 11575 this._z = Math.atan2( - m12, m22 ); 11576 11577 } else { 11578 11579 this._y = 0; 11580 this._z = Math.atan2( m21, m11 ); 11581 11582 } 11583 11584 break; 11585 11586 case 'ZYX': 11587 11588 this._y = Math.asin( - clamp( m31, -1, 1 ) ); 11589 11590 if ( Math.abs( m31 ) < 0.9999999 ) { 11591 11592 this._x = Math.atan2( m32, m33 ); 11593 this._z = Math.atan2( m21, m11 ); 11594 11595 } else { 11596 11597 this._x = 0; 11598 this._z = Math.atan2( - m12, m22 ); 11599 11600 } 11601 11602 break; 11603 11604 case 'YZX': 11605 11606 this._z = Math.asin( clamp( m21, -1, 1 ) ); 11607 11608 if ( Math.abs( m21 ) < 0.9999999 ) { 11609 11610 this._x = Math.atan2( - m23, m22 ); 11611 this._y = Math.atan2( - m31, m11 ); 11612 11613 } else { 11614 11615 this._x = 0; 11616 this._y = Math.atan2( m13, m33 ); 11617 11618 } 11619 11620 break; 11621 11622 case 'XZY': 11623 11624 this._z = Math.asin( - clamp( m12, -1, 1 ) ); 11625 11626 if ( Math.abs( m12 ) < 0.9999999 ) { 11627 11628 this._x = Math.atan2( m32, m22 ); 11629 this._y = Math.atan2( m13, m11 ); 11630 11631 } else { 11632 11633 this._x = Math.atan2( - m23, m33 ); 11634 this._y = 0; 11635 11636 } 11637 11638 break; 11639 11640 default: 11641 11642 warn( 'Euler: .setFromRotationMatrix() encountered an unknown order: ' + order ); 11643 11644 } 11645 11646 this._order = order; 11647 11648 if ( update === true ) this._onChangeCallback(); 11649 11650 return this; 11651 11652 } 11653 11654 /** 11655 * Sets the angles of this Euler instance from a normalized quaternion. 11656 * 11657 * @param {Quaternion} q - A normalized Quaternion. 11658 * @param {string} [order] - A string representing the order that the rotations are applied. 11659 * @param {boolean} [update=true] - Whether the internal `onChange` callback should be executed or not. 11660 * @return {Euler} A reference to this Euler instance. 11661 */ 11662 setFromQuaternion( q, order, update ) { 11663 11664 _matrix$2.makeRotationFromQuaternion( q ); 11665 11666 return this.setFromRotationMatrix( _matrix$2, order, update ); 11667 11668 } 11669 11670 /** 11671 * Sets the angles of this Euler instance from the given vector. 11672 * 11673 * @param {Vector3} v - The vector. 11674 * @param {string} [order] - A string representing the order that the rotations are applied. 11675 * @return {Euler} A reference to this Euler instance. 11676 */ 11677 setFromVector3( v, order = this._order ) { 11678 11679 return this.set( v.x, v.y, v.z, order ); 11680 11681 } 11682 11683 /** 11684 * Resets the euler angle with a new order by creating a quaternion from this 11685 * euler angle and then setting this euler angle with the quaternion and the 11686 * new order. 11687 * 11688 * Warning: This discards revolution information. 11689 * 11690 * @param {string} [newOrder] - A string representing the new order that the rotations are applied. 11691 * @return {Euler} A reference to this Euler instance. 11692 */ 11693 reorder( newOrder ) { 11694 11695 _quaternion$4.setFromEuler( this ); 11696 11697 return this.setFromQuaternion( _quaternion$4, newOrder ); 11698 11699 } 11700 11701 /** 11702 * Returns `true` if this Euler instance is equal with the given one. 11703 * 11704 * @param {Euler} euler - The Euler instance to test for equality. 11705 * @return {boolean} Whether this Euler instance is equal with the given one. 11706 */ 11707 equals( euler ) { 11708 11709 return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); 11710 11711 } 11712 11713 /** 11714 * Sets this Euler instance's components to values from the given array. The first three 11715 * entries of the array are assign to the x,y and z components. An optional fourth entry 11716 * defines the Euler order. 11717 * 11718 * @param {Array<number,number,number,?string>} array - An array holding the Euler component values. 11719 * @return {Euler} A reference to this Euler instance. 11720 */ 11721 fromArray( array ) { 11722 11723 this._x = array[ 0 ]; 11724 this._y = array[ 1 ]; 11725 this._z = array[ 2 ]; 11726 if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; 11727 11728 this._onChangeCallback(); 11729 11730 return this; 11731 11732 } 11733 11734 /** 11735 * Writes the components of this Euler instance to the given array. If no array is provided, 11736 * the method returns a new instance. 11737 * 11738 * @param {Array<number,number,number,string>} [array=[]] - The target array holding the Euler components. 11739 * @param {number} [offset=0] - Index of the first element in the array. 11740 * @return {Array<number,number,number,string>} The Euler components. 11741 */ 11742 toArray( array = [], offset = 0 ) { 11743 11744 array[ offset ] = this._x; 11745 array[ offset + 1 ] = this._y; 11746 array[ offset + 2 ] = this._z; 11747 array[ offset + 3 ] = this._order; 11748 11749 return array; 11750 11751 } 11752 11753 _onChange( callback ) { 11754 11755 this._onChangeCallback = callback; 11756 11757 return this; 11758 11759 } 11760 11761 _onChangeCallback() {} 11762 11763 *[ Symbol.iterator ]() { 11764 11765 yield this._x; 11766 yield this._y; 11767 yield this._z; 11768 yield this._order; 11769 11770 } 11771 11772} 11773 11774/** 11775 * The default Euler angle order. 11776 * 11777 * @static 11778 * @type {string} 11779 * @default 'XYZ' 11780 */ 11781Euler.DEFAULT_ORDER = 'XYZ'; 11782 11783/** 11784 * A layers object assigns an 3D object to 1 or more of 32 11785 * layers numbered `0` to `31` - internally the layers are stored as a 11786 * bit mask], and by default all 3D objects are a member of layer `0`. 11787 * 11788 * This can be used to control visibility - an object must share a layer with 11789 * a camera to be visible when that camera's view is 11790 * rendered. 11791 * 11792 * All classes that inherit from {@link Object3D} have an `layers` property which 11793 * is an instance of this class. 11794 */ 11795class Layers { 11796 11797 /** 11798 * Constructs a new layers instance, with membership 11799 * initially set to layer `0`. 11800 */ 11801 constructor() { 11802 11803 /** 11804 * A bit mask storing which of the 32 layers this layers object is currently 11805 * a member of. 11806 * 11807 * @type {number} 11808 */ 11809 this.mask = 1 | 0; 11810 11811 } 11812 11813 /** 11814 * Sets membership to the given layer, and remove membership all other layers. 11815 * 11816 * @param {number} layer - The layer to set. 11817 */ 11818 set( layer ) { 11819 11820 this.mask = ( 1 << layer | 0 ) >>> 0; 11821 11822 } 11823 11824 /** 11825 * Adds membership of the given layer. 11826 * 11827 * @param {number} layer - The layer to enable. 11828 */ 11829 enable( layer ) { 11830 11831 this.mask |= 1 << layer | 0; 11832 11833 } 11834 11835 /** 11836 * Adds membership to all layers. 11837 */ 11838 enableAll() { 11839 11840 this.mask = 0xffffffff | 0; 11841 11842 } 11843 11844 /** 11845 * Toggles the membership of the given layer. 11846 * 11847 * @param {number} layer - The layer to toggle. 11848 */ 11849 toggle( layer ) { 11850 11851 this.mask ^= 1 << layer | 0; 11852 11853 } 11854 11855 /** 11856 * Removes membership of the given layer. 11857 * 11858 * @param {number} layer - The layer to enable. 11859 */ 11860 disable( layer ) { 11861 11862 this.mask &= ~ ( 1 << layer | 0 ); 11863 11864 } 11865 11866 /** 11867 * Removes the membership from all layers. 11868 */ 11869 disableAll() { 11870 11871 this.mask = 0; 11872 11873 } 11874 11875 /** 11876 * Returns `true` if this and the given layers object have at least one 11877 * layer in common. 11878 * 11879 * @param {Layers} layers - The layers to test. 11880 * @return {boolean } Whether this and the given layers object have at least one layer in common or not. 11881 */ 11882 test( layers ) { 11883 11884 return ( this.mask & layers.mask ) !== 0; 11885 11886 } 11887 11888 /** 11889 * Returns `true` if the given layer is enabled. 11890 * 11891 * @param {number} layer - The layer to test. 11892 * @return {boolean } Whether the given layer is enabled or not. 11893 */ 11894 isEnabled( layer ) { 11895 11896 return ( this.mask & ( 1 << layer | 0 ) ) !== 0; 11897 11898 } 11899 11900} 11901 11902let _object3DId = 0; 11903 11904const _v1$6 = /*@__PURE__*/ new Vector3(); 11905const _q1 = /*@__PURE__*/ new Quaternion(); 11906const _m1$1 = /*@__PURE__*/ new Matrix4(); 11907const _target = /*@__PURE__*/ new Vector3(); 11908 11909const _position$4 = /*@__PURE__*/ new Vector3(); 11910const _scale$3 = /*@__PURE__*/ new Vector3(); 11911const _quaternion$3 = /*@__PURE__*/ new Quaternion(); 11912 11913const _xAxis = /*@__PURE__*/ new Vector3( 1, 0, 0 ); 11914const _yAxis = /*@__PURE__*/ new Vector3( 0, 1, 0 ); 11915const _zAxis = /*@__PURE__*/ new Vector3( 0, 0, 1 ); 11916 11917/** 11918 * Fires when the object has been added to its parent object. 11919 * 11920 * @event Object3D#added 11921 * @type {Object} 11922 */ 11923const _addedEvent = { type: 'added' }; 11924 11925/** 11926 * Fires when the object has been removed from its parent object. 11927 * 11928 * @event Object3D#removed 11929 * @type {Object} 11930 */ 11931const _removedEvent = { type: 'removed' }; 11932 11933/** 11934 * Fires when a new child object has been added. 11935 * 11936 * @event Object3D#childadded 11937 * @type {Object} 11938 */ 11939const _childaddedEvent = { type: 'childadded', child: null }; 11940 11941/** 11942 * Fires when a child object has been removed. 11943 * 11944 * @event Object3D#childremoved 11945 * @type {Object} 11946 */ 11947const _childremovedEvent = { type: 'childremoved', child: null }; 11948 11949/** 11950 * This is the base class for most objects in three.js and provides a set of 11951 * properties and methods for manipulating objects in 3D space. 11952 * 11953 * @augments EventDispatcher 11954 */ 11955class Object3D extends EventDispatcher { 11956 11957 /** 11958 * Constructs a new 3D object. 11959 */ 11960 constructor() { 11961 11962 super(); 11963 11964 /** 11965 * This flag can be used for type testing. 11966 * 11967 * @type {boolean} 11968 * @readonly 11969 * @default true 11970 */ 11971 this.isObject3D = true; 11972 11973 /** 11974 * The ID of the 3D object. 11975 * 11976 * @name Object3D#id 11977 * @type {number} 11978 * @readonly 11979 */ 11980 Object.defineProperty( this, 'id', { value: _object3DId ++ } ); 11981 11982 /** 11983 * The UUID of the 3D object. 11984 * 11985 * @type {string} 11986 * @readonly 11987 */ 11988 this.uuid = generateUUID(); 11989 11990 /** 11991 * The name of the 3D object. 11992 * 11993 * @type {string} 11994 */ 11995 this.name = ''; 11996 11997 /** 11998 * The type property is used for detecting the object type 11999 * in context of serialization/deserialization. 12000 * 12001 * @type {string} 12002 * @readonly 12003 */ 12004 this.type = 'Object3D'; 12005 12006 /** 12007 * A reference to the parent object. 12008 * 12009 * @type {?Object3D} 12010 * @default null 12011 */ 12012 this.parent = null; 12013 12014 /** 12015 * An array holding the child 3D objects of this instance. 12016 * 12017 * @type {Array<Object3D>} 12018 */ 12019 this.children = []; 12020 12021 /** 12022 * Defines the `up` direction of the 3D object which influences 12023 * the orientation via methods like {@link Object3D#lookAt}. 12024 * 12025 * The default values for all 3D objects is defined by `Object3D.DEFAULT_UP`. 12026 * 12027 * @type {Vector3} 12028 */ 12029 this.up = Object3D.DEFAULT_UP.clone(); 12030 12031 const position = new Vector3(); 12032 const rotation = new Euler(); 12033 const quaternion = new Quaternion(); 12034 const scale = new Vector3( 1, 1, 1 ); 12035 12036 function onRotationChange() { 12037 12038 quaternion.setFromEuler( rotation, false ); 12039 12040 } 12041 12042 function onQuaternionChange() { 12043 12044 rotation.setFromQuaternion( quaternion, undefined, false ); 12045 12046 } 12047 12048 rotation._onChange( onRotationChange ); 12049 quaternion._onChange( onQuaternionChange ); 12050 12051 Object.defineProperties( this, { 12052 /** 12053 * Represents the object's local position. 12054 * 12055 * @name Object3D#position 12056 * @type {Vector3} 12057 * @default (0,0,0) 12058 */ 12059 position: { 12060 configurable: true, 12061 enumerable: true, 12062 value: position 12063 }, 12064 /** 12065 * Represents the object's local rotation as Euler angles, in radians. 12066 * 12067 * @name Object3D#rotation 12068 * @type {Euler} 12069 * @default (0,0,0) 12070 */ 12071 rotation: { 12072 configurable: true, 12073 enumerable: true, 12074 value: rotation 12075 }, 12076 /** 12077 * Represents the object's local rotation as Quaternions. 12078 * 12079 * @name Object3D#quaternion 12080 * @type {Quaternion} 12081 */ 12082 quaternion: { 12083 configurable: true, 12084 enumerable: true, 12085 value: quaternion 12086 }, 12087 /** 12088 * Represents the object's local scale. 12089 * 12090 * @name Object3D#scale 12091 * @type {Vector3} 12092 * @default (1,1,1) 12093 */ 12094 scale: { 12095 configurable: true, 12096 enumerable: true, 12097 value: scale 12098 }, 12099 /** 12100 * Represents the object's model-view matrix. 12101 * 12102 * @name Object3D#modelViewMatrix 12103 * @type {Matrix4} 12104 */ 12105 modelViewMatrix: { 12106 value: new Matrix4() 12107 }, 12108 /** 12109 * Represents the object's normal matrix. 12110 * 12111 * @name Object3D#normalMatrix 12112 * @type {Matrix3} 12113 */ 12114 normalMatrix: { 12115 value: new Matrix3() 12116 } 12117 } ); 12118 12119 /** 12120 * Represents the object's transformation matrix in local space. 12121 * 12122 * @type {Matrix4} 12123 */ 12124 this.matrix = new Matrix4(); 12125 12126 /** 12127 * Represents the object's transformation matrix in world space. 12128 * If the 3D object has no parent, then it's identical to the local transformation matrix 12129 * 12130 * @type {Matrix4} 12131 */ 12132 this.matrixWorld = new Matrix4(); 12133 12134 /** 12135 * When set to `true`, the engine automatically computes the local matrix from position, 12136 * rotation and scale every frame. If set to `false`, the app is responsible for recomputing 12137 * the local matrix by calling `updateMatrix()`. 12138 * 12139 * The default values for all 3D objects is defined by `Object3D.DEFAULT_MATRIX_AUTO_UPDATE`. 12140 * 12141 * @type {boolean} 12142 * @default true 12143 */ 12144 this.matrixAutoUpdate = Object3D.DEFAULT_MATRIX_AUTO_UPDATE; 12145 12146 /** 12147 * When set to `true`, the engine automatically computes the world matrix from the current local 12148 * matrix and the object's transformation hierarchy. If set to `false`, the app is responsible for 12149 * recomputing the world matrix by directly updating the `matrixWorld` property. 12150 * 12151 * The default values for all 3D objects is defined by `Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE`. 12152 * 12153 * @type {boolean} 12154 * @default true 12155 */ 12156 this.matrixWorldAutoUpdate = Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE; // checked by the renderer 12157 12158 /** 12159 * When set to `true`, it calculates the world matrix in that frame and resets this property 12160 * to `false`. 12161 * 12162 * @type {boolean} 12163 * @default false 12164 */ 12165 this.matrixWorldNeedsUpdate = false; 12166 12167 /** 12168 * The layer membership of the 3D object. The 3D object is only visible if it has 12169 * at least one layer in common with the camera in use. This property can also be 12170 * used to filter out unwanted objects in ray-intersection tests when using {@link Raycaster}. 12171 * 12172 * @type {Layers} 12173 */ 12174 this.layers = new Layers(); 12175 12176 /** 12177 * When set to `true`, the 3D object gets rendered. 12178 * 12179 * @type {boolean} 12180 * @default true 12181 */ 12182 this.visible = true; 12183 12184 /** 12185 * When set to `true`, the 3D object gets rendered into shadow maps. 12186 * 12187 * @type {boolean} 12188 * @default false 12189 */ 12190 this.castShadow = false; 12191 12192 /** 12193 * When set to `true`, the 3D object is affected by shadows in the scene. 12194 * 12195 * @type {boolean} 12196 * @default false 12197 */ 12198 this.receiveShadow = false; 12199 12200 /** 12201 * When set to `true`, the 3D object is honored by view frustum culling. 12202 * 12203 * @type {boolean} 12204 * @default true 12205 */ 12206 this.frustumCulled = true; 12207 12208 /** 12209 * This value allows the default rendering order of scene graph objects to be 12210 * overridden although opaque and transparent objects remain sorted independently. 12211 * When this property is set for an instance of {@link Group},all descendants 12212 * objects will be sorted and rendered together. Sorting is from lowest to highest 12213 * render order. 12214 * 12215 * @type {number} 12216 * @default 0 12217 */ 12218 this.renderOrder = 0; 12219 12220 /** 12221 * An array holding the animation clips of the 3D object. 12222 * 12223 * @type {Array<AnimationClip>} 12224 */ 12225 this.animations = []; 12226 12227 /** 12228 * Custom depth material to be used when rendering to the depth map. Can only be used 12229 * in context of meshes. When shadow-casting with a {@link DirectionalLight} or {@link SpotLight}, 12230 * if you are modifying vertex positions in the vertex shader you must specify a custom depth 12231 * material for proper shadows. 12232 * 12233 * Only relevant in context of {@link WebGLRenderer}. 12234 * 12235 * @type {(Material|undefined)} 12236 * @default undefined 12237 */ 12238 this.customDepthMaterial = undefined; 12239 12240 /** 12241 * Same as {@link Object3D#customDepthMaterial}, but used with {@link PointLight}. 12242 * 12243 * Only relevant in context of {@link WebGLRenderer}. 12244 * 12245 * @type {(Material|undefined)} 12246 * @default undefined 12247 */ 12248 this.customDistanceMaterial = undefined; 12249 12250 /** 12251 * Whether the 3D object is supposed to be static or not. If set to `true`, it means 12252 * the 3D object is not going to be changed after the initial renderer. This includes 12253 * geometry and material settings. A static 3D object can be processed by the renderer 12254 * slightly faster since certain state checks can be bypassed. 12255 * 12256 * Only relevant in context of {@link WebGPURenderer}. 12257 * 12258 * @type {boolean} 12259 * @default false 12260 */ 12261 this.static = false; 12262 12263 /** 12264 * An object that can be used to store custom data about the 3D object. It 12265 * should not hold references to functions as these will not be cloned. 12266 * 12267 * @type {Object} 12268 */ 12269 this.userData = {}; 12270 12271 /** 12272 * The pivot point for rotation and scale transformations. 12273 * When set, rotation and scale are applied around this point 12274 * instead of the object's origin. 12275 * 12276 * @type {?Vector3} 12277 * @default null 12278 */ 12279 this.pivot = null; 12280 12281 } 12282 12283 /** 12284 * A callback that is executed immediately before a 3D object is rendered to a shadow map. 12285 * 12286 * @param {Renderer|WebGLRenderer} renderer - The renderer. 12287 * @param {Object3D} object - The 3D object. 12288 * @param {Camera} camera - The camera that is used to render the scene. 12289 * @param {Camera} shadowCamera - The shadow camera. 12290 * @param {BufferGeometry} geometry - The 3D object's geometry. 12291 * @param {Material} depthMaterial - The depth material. 12292 * @param {Object} group - The geometry group data. 12293 */ 12294 onBeforeShadow( /* renderer, object, camera, shadowCamera, geometry, depthMaterial, group */ ) {} 12295 12296 /** 12297 * A callback that is executed immediately after a 3D object is rendered to a shadow map. 12298 * 12299 * @param {Renderer|WebGLRenderer} renderer - The renderer. 12300 * @param {Object3D} object - The 3D object. 12301 * @param {Camera} camera - The camera that is used to render the scene. 12302 * @param {Camera} shadowCamera - The shadow camera. 12303 * @param {BufferGeometry} geometry - The 3D object's geometry. 12304 * @param {Material} depthMaterial - The depth material. 12305 * @param {Object} group - The geometry group data. 12306 */ 12307 onAfterShadow( /* renderer, object, camera, shadowCamera, geometry, depthMaterial, group */ ) {} 12308 12309 /** 12310 * A callback that is executed immediately before a 3D object is rendered. 12311 * 12312 * @param {Renderer|WebGLRenderer} renderer - The renderer. 12313 * @param {Object3D} object - The 3D object. 12314 * @param {Camera} camera - The camera that is used to render the scene. 12315 * @param {BufferGeometry} geometry - The 3D object's geometry. 12316 * @param {Material} material - The 3D object's material. 12317 * @param {Object} group - The geometry group data. 12318 */ 12319 onBeforeRender( /* renderer, scene, camera, geometry, material, group */ ) {} 12320 12321 /** 12322 * A callback that is executed immediately after a 3D object is rendered. 12323 * 12324 * @param {Renderer|WebGLRenderer} renderer - The renderer. 12325 * @param {Object3D} object - The 3D object. 12326 * @param {Camera} camera - The camera that is used to render the scene. 12327 * @param {BufferGeometry} geometry - The 3D object's geometry. 12328 * @param {Material} material - The 3D object's material. 12329 * @param {Object} group - The geometry group data. 12330 */ 12331 onAfterRender( /* renderer, scene, camera, geometry, material, group */ ) {} 12332 12333 /** 12334 * Applies the given transformation matrix to the object and updates the object's position, 12335 * rotation and scale. 12336 * 12337 * @param {Matrix4} matrix - The transformation matrix. 12338 */ 12339 applyMatrix4( matrix ) { 12340 12341 if ( this.matrixAutoUpdate ) this.updateMatrix(); 12342 12343 this.matrix.premultiply( matrix ); 12344 12345 this.matrix.decompose( this.position, this.quaternion, this.scale ); 12346 12347 } 12348 12349 /** 12350 * Applies a rotation represented by given the quaternion to the 3D object. 12351 * 12352 * @param {Quaternion} q - The quaternion. 12353 * @return {Object3D} A reference to this instance. 12354 */ 12355 applyQuaternion( q ) { 12356 12357 this.quaternion.premultiply( q ); 12358 12359 return this; 12360 12361 } 12362 12363 /** 12364 * Sets the given rotation represented as an axis/angle couple to the 3D object. 12365 * 12366 * @param {Vector3} axis - The (normalized) axis vector. 12367 * @param {number} angle - The angle in radians. 12368 */ 12369 setRotationFromAxisAngle( axis, angle ) { 12370 12371 // assumes axis is normalized 12372 12373 this.quaternion.setFromAxisAngle( axis, angle ); 12374 12375 } 12376 12377 /** 12378 * Sets the given rotation represented as Euler angles to the 3D object. 12379 * 12380 * @param {Euler} euler - The Euler angles. 12381 */ 12382 setRotationFromEuler( euler ) { 12383 12384 this.quaternion.setFromEuler( euler, true ); 12385 12386 } 12387 12388 /** 12389 * Sets the given rotation represented as rotation matrix to the 3D object. 12390 * 12391 * @param {Matrix4} m - Although a 4x4 matrix is expected, the upper 3x3 portion must be 12392 * a pure rotation matrix (i.e, unscaled). 12393 */ 12394 setRotationFromMatrix( m ) { 12395 12396 // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) 12397 12398 this.quaternion.setFromRotationMatrix( m ); 12399 12400 } 12401 12402 /** 12403 * Sets the given rotation represented as a Quaternion to the 3D object. 12404 * 12405 * @param {Quaternion} q - The Quaternion 12406 */ 12407 setRotationFromQuaternion( q ) { 12408 12409 // assumes q is normalized 12410 12411 this.quaternion.copy( q ); 12412 12413 } 12414 12415 /** 12416 * Rotates the 3D object along an axis in local space. 12417 * 12418 * @param {Vector3} axis - The (normalized) axis vector. 12419 * @param {number} angle - The angle in radians. 12420 * @return {Object3D} A reference to this instance. 12421 */ 12422 rotateOnAxis( axis, angle ) { 12423 12424 // rotate object on axis in object space 12425 // axis is assumed to be normalized 12426 12427 _q1.setFromAxisAngle( axis, angle ); 12428 12429 this.quaternion.multiply( _q1 ); 12430 12431 return this; 12432 12433 } 12434 12435 /** 12436 * Rotates the 3D object along an axis in world space. 12437 * 12438 * @param {Vector3} axis - The (normalized) axis vector. 12439 * @param {number} angle - The angle in radians. 12440 * @return {Object3D} A reference to this instance. 12441 */ 12442 rotateOnWorldAxis( axis, angle ) { 12443 12444 // rotate object on axis in world space 12445 // axis is assumed to be normalized 12446 // method assumes no rotated parent 12447 12448 _q1.setFromAxisAngle( axis, angle ); 12449 12450 this.quaternion.premultiply( _q1 ); 12451 12452 return this; 12453 12454 } 12455 12456 /** 12457 * Rotates the 3D object around its X axis in local space. 12458 * 12459 * @param {number} angle - The angle in radians. 12460 * @return {Object3D} A reference to this instance. 12461 */ 12462 rotateX( angle ) { 12463 12464 return this.rotateOnAxis( _xAxis, angle ); 12465 12466 } 12467 12468 /** 12469 * Rotates the 3D object around its Y axis in local space. 12470 * 12471 * @param {number} angle - The angle in radians. 12472 * @return {Object3D} A reference to this instance. 12473 */ 12474 rotateY( angle ) { 12475 12476 return this.rotateOnAxis( _yAxis, angle ); 12477 12478 } 12479 12480 /** 12481 * Rotates the 3D object around its Z axis in local space. 12482 * 12483 * @param {number} angle - The angle in radians. 12484 * @return {Object3D} A reference to this instance. 12485 */ 12486 rotateZ( angle ) { 12487 12488 return this.rotateOnAxis( _zAxis, angle ); 12489 12490 } 12491 12492 /** 12493 * Translate the 3D object by a distance along the given axis in local space. 12494 * 12495 * @param {Vector3} axis - The (normalized) axis vector. 12496 * @param {number} distance - The distance in world units. 12497 * @return {Object3D} A reference to this instance. 12498 */ 12499 translateOnAxis( axis, distance ) { 12500 12501 // translate object by distance along axis in object space 12502 // axis is assumed to be normalized 12503 12504 _v1$6.copy( axis ).applyQuaternion( this.quaternion ); 12505 12506 this.position.add( _v1$6.multiplyScalar( distance ) ); 12507 12508 return this; 12509 12510 } 12511 12512 /** 12513 * Translate the 3D object by a distance along its X-axis in local space. 12514 * 12515 * @param {number} distance - The distance in world units. 12516 * @return {Object3D} A reference to this instance. 12517 */ 12518 translateX( distance ) { 12519 12520 return this.translateOnAxis( _xAxis, distance ); 12521 12522 } 12523 12524 /** 12525 * Translate the 3D object by a distance along its Y-axis in local space. 12526 * 12527 * @param {number} distance - The distance in world units. 12528 * @return {Object3D} A reference to this instance. 12529 */ 12530 translateY( distance ) { 12531 12532 return this.translateOnAxis( _yAxis, distance ); 12533 12534 } 12535 12536 /** 12537 * Translate the 3D object by a distance along its Z-axis in local space. 12538 * 12539 * @param {number} distance - The distance in world units. 12540 * @return {Object3D} A reference to this instance. 12541 */ 12542 translateZ( distance ) { 12543 12544 return this.translateOnAxis( _zAxis, distance ); 12545 12546 } 12547 12548 /** 12549 * Converts the given vector from this 3D object's local space to world space. 12550 * 12551 * @param {Vector3} vector - The vector to convert. 12552 * @return {Vector3} The converted vector. 12553 */ 12554 localToWorld( vector ) { 12555 12556 this.updateWorldMatrix( true, false ); 12557 12558 return vector.applyMatrix4( this.matrixWorld ); 12559 12560 } 12561 12562 /** 12563 * Converts the given vector from this 3D object's world space to local space. 12564 * 12565 * @param {Vector3} vector - The vector to convert. 12566 * @return {Vector3} The converted vector. 12567 */ 12568 worldToLocal( vector ) { 12569 12570 this.updateWorldMatrix( true, false ); 12571 12572 return vector.applyMatrix4( _m1$1.copy( this.matrixWorld ).invert() ); 12573 12574 } 12575 12576 /** 12577 * Rotates the object to face a point in world space. 12578 * 12579 * This method does not support objects having non-uniformly-scaled parent(s). 12580 * 12581 * @param {number|Vector3} x - The x coordinate in world space. Alternatively, a vector representing a position in world space 12582 * @param {number} [y] - The y coordinate in world space. 12583 * @param {number} [z] - The z coordinate in world space. 12584 */ 12585 lookAt( x, y, z ) { 12586 12587 // This method does not support objects having non-uniformly-scaled parent(s) 12588 12589 if ( x.isVector3 ) { 12590 12591 _target.copy( x ); 12592 12593 } else { 12594 12595 _target.set( x, y, z ); 12596 12597 } 12598 12599 const parent = this.parent; 12600 12601 this.updateWorldMatrix( true, false ); 12602 12603 _position$4.setFromMatrixPosition( this.matrixWorld ); 12604 12605 if ( this.isCamera || this.isLight ) { 12606 12607 _m1$1.lookAt( _position$4, _target, this.up ); 12608 12609 } else { 12610 12611 _m1$1.lookAt( _target, _position$4, this.up ); 12612 12613 } 12614 12615 this.quaternion.setFromRotationMatrix( _m1$1 ); 12616 12617 if ( parent ) { 12618 12619 _m1$1.extractRotation( parent.matrixWorld ); 12620 _q1.setFromRotationMatrix( _m1$1 ); 12621 this.quaternion.premultiply( _q1.invert() ); 12622 12623 } 12624 12625 } 12626 12627 /** 12628 * Adds the given 3D object as a child to this 3D object. An arbitrary number of 12629 * objects may be added. Any current parent on an object passed in here will be 12630 * removed, since an object can have at most one parent. 12631 * 12632 * @fires Object3D#added 12633 * @fires Object3D#childadded 12634 * @param {Object3D} object - The 3D object to add. 12635 * @return {Object3D} A reference to this instance. 12636 */ 12637 add( object ) { 12638 12639 if ( arguments.length > 1 ) { 12640 12641 for ( let i = 0; i < arguments.length; i ++ ) { 12642 12643 this.add( arguments[ i ] ); 12644 12645 } 12646 12647 return this; 12648 12649 } 12650 12651 if ( object === this ) { 12652 12653 error( 'Object3D.add: object can\'t be added as a child of itself.', object ); 12654 return this; 12655 12656 } 12657 12658 if ( object && object.isObject3D ) { 12659 12660 object.removeFromParent(); 12661 object.parent = this; 12662 this.children.push( object ); 12663 12664 object.dispatchEvent( _addedEvent ); 12665 12666 _childaddedEvent.child = object; 12667 this.dispatchEvent( _childaddedEvent ); 12668 _childaddedEvent.child = null; 12669 12670 } else { 12671 12672 error( 'Object3D.add: object not an instance of THREE.Object3D.', object ); 12673 12674 } 12675 12676 return this; 12677 12678 } 12679 12680 /** 12681 * Removes the given 3D object as child from this 3D object. 12682 * An arbitrary number of objects may be removed. 12683 * 12684 * @fires Object3D#removed 12685 * @fires Object3D#childremoved 12686 * @param {Object3D} object - The 3D object to remove. 12687 * @return {Object3D} A reference to this instance. 12688 */ 12689 remove( object ) { 12690 12691 if ( arguments.length > 1 ) { 12692 12693 for ( let i = 0; i < arguments.length; i ++ ) { 12694 12695 this.remove( arguments[ i ] ); 12696 12697 } 12698 12699 return this; 12700 12701 } 12702 12703 const index = this.children.indexOf( object ); 12704 12705 if ( index !== -1 ) { 12706 12707 object.parent = null; 12708 this.children.splice( index, 1 ); 12709 12710 object.dispatchEvent( _removedEvent ); 12711 12712 _childremovedEvent.child = object; 12713 this.dispatchEvent( _childremovedEvent ); 12714 _childremovedEvent.child = null; 12715 12716 } 12717 12718 return this; 12719 12720 } 12721 12722 /** 12723 * Removes this 3D object from its current parent. 12724 * 12725 * @fires Object3D#removed 12726 * @fires Object3D#childremoved 12727 * @return {Object3D} A reference to this instance. 12728 */ 12729 removeFromParent() { 12730 12731 const parent = this.parent; 12732 12733 if ( parent !== null ) { 12734 12735 parent.remove( this ); 12736 12737 } 12738 12739 return this; 12740 12741 } 12742 12743 /** 12744 * Removes all child objects. 12745 * 12746 * @fires Object3D#removed 12747 * @fires Object3D#childremoved 12748 * @return {Object3D} A reference to this instance. 12749 */ 12750 clear() { 12751 12752 return this.remove( ... this.children ); 12753 12754 } 12755 12756 /** 12757 * Adds the given 3D object as a child of this 3D object, while maintaining the object's world 12758 * transform. This method does not support scene graphs having non-uniformly-scaled nodes(s). 12759 * 12760 * @fires Object3D#added 12761 * @fires Object3D#childadded 12762 * @param {Object3D} object - The 3D object to attach. 12763 * @return {Object3D} A reference to this instance. 12764 */ 12765 attach( object ) { 12766 12767 // adds object as a child of this, while maintaining the object's world transform 12768 12769 // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) 12770 12771 this.updateWorldMatrix( true, false ); 12772 12773 _m1$1.copy( this.matrixWorld ).invert(); 12774 12775 if ( object.parent !== null ) { 12776 12777 object.parent.updateWorldMatrix( true, false ); 12778 12779 _m1$1.multiply( object.parent.matrixWorld ); 12780 12781 } 12782 12783 object.applyMatrix4( _m1$1 ); 12784 12785 object.removeFromParent(); 12786 object.parent = this; 12787 this.children.push( object ); 12788 12789 object.updateWorldMatrix( false, true ); 12790 12791 object.dispatchEvent( _addedEvent ); 12792 12793 _childaddedEvent.child = object; 12794 this.dispatchEvent( _childaddedEvent ); 12795 _childaddedEvent.child = null; 12796 12797 return this; 12798 12799 } 12800 12801 /** 12802 * Searches through the 3D object and its children, starting with the 3D object 12803 * itself, and returns the first with a matching ID. 12804 * 12805 * @param {number} id - The id. 12806 * @return {Object3D|undefined} The found 3D object. Returns `undefined` if no 3D object has been found. 12807 */ 12808 getObjectById( id ) { 12809 12810 return this.getObjectByProperty( 'id', id ); 12811 12812 } 12813 12814 /** 12815 * Searches through the 3D object and its children, starting with the 3D object 12816 * itself, and returns the first with a matching name. 12817 * 12818 * @param {string} name - The name. 12819 * @return {Object3D|undefined} The found 3D object. Returns `undefined` if no 3D object has been found. 12820 */ 12821 getObjectByName( name ) { 12822 12823 return this.getObjectByProperty( 'name', name ); 12824 12825 } 12826 12827 /** 12828 * Searches through the 3D object and its children, starting with the 3D object 12829 * itself, and returns the first with a matching property value. 12830 * 12831 * @param {string} name - The name of the property. 12832 * @param {any} value - The value. 12833 * @return {Object3D|undefined} The found 3D object. Returns `undefined` if no 3D object has been found. 12834 */ 12835 getObjectByProperty( name, value ) { 12836 12837 if ( this[ name ] === value ) return this; 12838 12839 for ( let i = 0, l = this.children.length; i < l; i ++ ) { 12840 12841 const child = this.children[ i ]; 12842 const object = child.getObjectByProperty( name, value ); 12843 12844 if ( object !== undefined ) { 12845 12846 return object; 12847 12848 } 12849 12850 } 12851 12852 return undefined; 12853 12854 } 12855 12856 /** 12857 * Searches through the 3D object and its children, starting with the 3D object 12858 * itself, and returns all 3D objects with a matching property value. 12859 * 12860 * @param {string} name - The name of the property. 12861 * @param {any} value - The value. 12862 * @param {Array<Object3D>} result - The method stores the result in this array. 12863 * @return {Array<Object3D>} The found 3D objects. 12864 */ 12865 getObjectsByProperty( name, value, result = [] ) { 12866 12867 if ( this[ name ] === value ) result.push( this ); 12868 12869 const children = this.children; 12870 12871 for ( let i = 0, l = children.length; i < l; i ++ ) { 12872 12873 children[ i ].getObjectsByProperty( name, value, result ); 12874 12875 } 12876 12877 return result; 12878 12879 } 12880 12881 /** 12882 * Returns a vector representing the position of the 3D object in world space. 12883 * 12884 * @param {Vector3} target - The target vector the result is stored to. 12885 * @return {Vector3} The 3D object's position in world space. 12886 */ 12887 getWorldPosition( target ) { 12888 12889 this.updateWorldMatrix( true, false ); 12890 12891 return target.setFromMatrixPosition( this.matrixWorld ); 12892 12893 } 12894 12895 /** 12896 * Returns a Quaternion representing the position of the 3D object in world space. 12897 * 12898 * @param {Quaternion} target - The target Quaternion the result is stored to. 12899 * @return {Quaternion} The 3D object's rotation in world space. 12900 */ 12901 getWorldQuaternion( target ) { 12902 12903 this.updateWorldMatrix( true, false ); 12904 12905 this.matrixWorld.decompose( _position$4, target, _scale$3 ); 12906 12907 return target; 12908 12909 } 12910 12911 /** 12912 * Returns a vector representing the scale of the 3D object in world space. 12913 * 12914 * @param {Vector3} target - The target vector the result is stored to. 12915 * @return {Vector3} The 3D object's scale in world space. 12916 */ 12917 getWorldScale( target ) { 12918 12919 this.updateWorldMatrix( true, false ); 12920 12921 this.matrixWorld.decompose( _position$4, _quaternion$3, target ); 12922 12923 return target; 12924 12925 } 12926 12927 /** 12928 * Returns a vector representing the ("look") direction of the 3D object in world space. 12929 * 12930 * @param {Vector3} target - The target vector the result is stored to. 12931 * @return {Vector3} The 3D object's direction in world space. 12932 */ 12933 getWorldDirection( target ) { 12934 12935 this.updateWorldMatrix( true, false ); 12936 12937 const e = this.matrixWorld.elements; 12938 12939 return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); 12940 12941 } 12942 12943 /** 12944 * Abstract method to get intersections between a casted ray and this 12945 * 3D object. Renderable 3D objects such as {@link Mesh}, {@link Line} or {@link Points} 12946 * implement this method in order to use raycasting. 12947 * 12948 * @abstract 12949 * @param {Raycaster} raycaster - The raycaster. 12950 * @param {Array<Object>} intersects - An array holding the result of the method. 12951 */ 12952 raycast( /* raycaster, intersects */ ) {} 12953 12954 /** 12955 * Abstract method to test whether this 3D object intersects the given frustum. 12956 * Renderable 3D objects such as {@link Mesh}, {@link Line} or {@link Points} 12957 * implement this method in order to use frustum culling. 12958 * 12959 * @abstract 12960 * @param {Frustum|FrustumArray} frustum - The frustum to test. 12961 * @return {boolean|undefined} Whether this 3D object intersects the given frustum or not. 12962 */ 12963 intersectsFrustum( /* frustum */ ) {} 12964 12965 /** 12966 * Executes the callback on this 3D object and all descendants. 12967 * 12968 * Note: Modifying the scene graph inside the callback is discouraged. 12969 * 12970 * @param {Function} callback - A callback function that allows to process the current 3D object. 12971 */ 12972 traverse( callback ) { 12973 12974 callback( this ); 12975 12976 const children = this.children; 12977 12978 for ( let i = 0, l = children.length; i < l; i ++ ) { 12979 12980 children[ i ].traverse( callback ); 12981 12982 } 12983 12984 } 12985 12986 /** 12987 * Like {@link Object3D#traverse}, but the callback will only be executed for visible 3D objects. 12988 * Descendants of invisible 3D objects are not traversed. 12989 * 12990 * Note: Modifying the scene graph inside the callback is discouraged. 12991 * 12992 * @param {Function} callback - A callback function that allows to process the current 3D object. 12993 */ 12994 traverseVisible( callback ) { 12995 12996 if ( this.visible === false ) return; 12997 12998 callback( this ); 12999 13000 const children = this.children; 13001 13002 for ( let i = 0, l = children.length; i < l; i ++ ) { 13003 13004 children[ i ].traverseVisible( callback ); 13005 13006 } 13007 13008 } 13009 13010 /** 13011 * Like {@link Object3D#traverse}, but the callback will only be executed for all ancestors. 13012 * 13013 * Note: Modifying the scene graph inside the callback is discouraged. 13014 * 13015 * @param {Function} callback - A callback function that allows to process the current 3D object. 13016 */ 13017 traverseAncestors( callback ) { 13018 13019 const parent = this.parent; 13020 13021 if ( parent !== null ) { 13022 13023 callback( parent ); 13024 13025 parent.traverseAncestors( callback ); 13026 13027 } 13028 13029 } 13030 13031 /** 13032 * Updates the transformation matrix in local space by computing it from the current 13033 * position, rotation and scale values. 13034 */ 13035 updateMatrix() { 13036 13037 this.matrix.compose( this.position, this.quaternion, this.scale ); 13038 13039 const pivot = this.pivot; 13040 13041 if ( pivot !== null ) { 13042 13043 const px = pivot.x, py = pivot.y, pz = pivot.z; 13044 const te = this.matrix.elements; 13045 13046 te[ 12 ] += px - te[ 0 ] * px - te[ 4 ] * py - te[ 8 ] * pz; 13047 te[ 13 ] += py - te[ 1 ] * px - te[ 5 ] * py - te[ 9 ] * pz; 13048 te[ 14 ] += pz - te[ 2 ] * px - te[ 6 ] * py - te[ 10 ] * pz; 13049 13050 } 13051 13052 this.matrixWorldNeedsUpdate = true; 13053 13054 } 13055 13056 /** 13057 * Updates the transformation matrix in world space of this 3D objects and its descendants. 13058 * 13059 * To ensure correct results, this method also recomputes the 3D object's transformation matrix in 13060 * local space. The computation of the local and world matrix can be controlled with the 13061 * {@link Object3D#matrixAutoUpdate} and {@link Object3D#matrixWorldAutoUpdate} flags which are both 13062 * `true` by default. Set these flags to `false` if you need more control over the update matrix process. 13063 * 13064 * @param {boolean} [force=false] - When set to `true`, a recomputation of world matrices is forced even 13065 * when {@link Object3D#matrixWorldNeedsUpdate} is `false`. 13066 */ 13067 updateMatrixWorld( force ) { 13068 13069 if ( this.matrixAutoUpdate ) this.updateMatrix(); 13070 13071 if ( this.matrixWorldNeedsUpdate || force ) { 13072 13073 if ( this.matrixWorldAutoUpdate === true ) { 13074 13075 if ( this.parent === null ) { 13076 13077 this.matrixWorld.copy( this.matrix ); 13078 13079 } else { 13080 13081 this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); 13082 13083 } 13084 13085 } 13086 13087 this.matrixWorldNeedsUpdate = false; 13088 13089 force = true; 13090 13091 } 13092 13093 // make sure descendants are updated if required 13094 13095 const children = this.children; 13096 13097 for ( let i = 0, l = children.length; i < l; i ++ ) { 13098 13099 const child = children[ i ]; 13100 13101 child.updateMatrixWorld( force ); 13102 13103 } 13104 13105 } 13106 13107 /** 13108 * An alternative version of {@link Object3D#updateMatrixWorld} with more control over the 13109 * update of ancestor and descendant nodes. 13110 * 13111 * @param {boolean} [updateParents=false] Whether ancestor nodes should be updated or not. 13112 * @param {boolean} [updateChildren=false] Whether descendant nodes should be updated or not. 13113 * @param {boolean} [force=false] - When set to `true`, a recomputation of world matrices is forced even 13114 * when {@link Object3D#matrixWorldNeedsUpdate} is `false`. 13115 */ 13116 updateWorldMatrix( updateParents, updateChildren, force = false ) { 13117 13118 const parent = this.parent; 13119 13120 if ( updateParents === true && parent !== null ) { 13121 13122 parent.updateWorldMatrix( true, false ); 13123 13124 } 13125 13126 if ( this.matrixAutoUpdate ) this.updateMatrix(); 13127 13128 if ( this.matrixWorldNeedsUpdate || force ) { 13129 13130 if ( this.matrixWorldAutoUpdate === true ) { 13131 13132 if ( this.parent === null ) { 13133 13134 this.matrixWorld.copy( this.matrix ); 13135 13136 } else { 13137 13138 this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); 13139 13140 } 13141 13142 } 13143 13144 this.matrixWorldNeedsUpdate = false; 13145 13146 force = true; 13147 13148 } 13149 13150 // make sure descendants are updated 13151 13152 if ( updateChildren === true ) { 13153 13154 const children = this.children; 13155 13156 for ( let i = 0, l = children.length; i < l; i ++ ) { 13157 13158 const child = children[ i ]; 13159 13160 child.updateWorldMatrix( false, true, force ); 13161 13162 } 13163 13164 } 13165 13166 } 13167 13168 /** 13169 * Serializes the 3D object into JSON. 13170 * 13171 * @param {?(Object|string)} meta - An optional value holding meta information about the serialization. 13172 * @return {Object} A JSON object representing the serialized 3D object. 13173 * @see {@link ObjectLoader#parse} 13174 */ 13175 toJSON( meta ) { 13176 13177 // meta is a string when called from JSON.stringify 13178 const isRootObject = ( meta === undefined || typeof meta === 'string' ); 13179 13180 const output = {}; 13181 13182 // meta is a hash used to collect geometries, materials. 13183 // not providing it implies that this is the root object 13184 // being serialized. 13185 if ( isRootObject ) { 13186 13187 // initialize meta obj 13188 meta = { 13189 geometries: {}, 13190 materials: {}, 13191 textures: {}, 13192 images: {}, 13193 shapes: {}, 13194 skeletons: {}, 13195 animations: {}, 13196 nodes: {} 13197 }; 13198 13199 output.metadata = { 13200 version: 4.7, 13201 type: 'Object', 13202 generator: 'Object3D.toJSON' 13203 }; 13204 13205 } 13206 13207 // standard Object3D serialization 13208 13209 const object = {}; 13210 13211 object.uuid = this.uuid; 13212 object.type = this.type; 13213 13214 object.name = this.name; 13215 object.castShadow = this.castShadow; 13216 object.receiveShadow = this.receiveShadow; 13217 object.visible = this.visible; 13218 object.frustumCulled = this.frustumCulled; 13219 object.renderOrder = this.renderOrder; 13220 object.static = this.static; 13221 object.matrixAutoUpdate = this.matrixAutoUpdate; 13222 13223 if ( Object.keys( this.userData ).length > 0 ) object.userData = this.userData; 13224 13225 object.layers = this.layers.mask; 13226 object.matrix = this.matrix.toArray(); 13227 object.up = this.up.toArray(); 13228 13229 if ( this.pivot !== null ) object.pivot = this.pivot.toArray(); 13230 13231 if ( this.morphTargetDictionary !== undefined ) object.morphTargetDictionary = Object.assign( {}, this.morphTargetDictionary ); 13232 if ( this.morphTargetInfluences !== undefined ) object.morphTargetInfluences = this.morphTargetInfluences.slice(); 13233 13234 // object specific properties 13235 13236 if ( this.isInstancedMesh ) { 13237 13238 object.type = 'InstancedMesh'; 13239 object.count = this.count; 13240 object.instanceMatrix = this.instanceMatrix.toJSON(); 13241 if ( this.instanceColor !== null ) object.instanceColor = this.instanceColor.toJSON(); 13242 13243 } 13244 13245 if ( this.isBatchedMesh ) { 13246 13247 object.type = 'BatchedMesh'; 13248 object.perObjectFrustumCulled = this.perObjectFrustumCulled; 13249 object.sortObjects = this.sortObjects; 13250 13251 object.drawRanges = this._drawRanges; 13252 object.reservedRanges = this._reservedRanges; 13253 13254 object.geometryInfo = this._geometryInfo.map( info => ( { 13255 ...info, 13256 boundingBox: info.boundingBox ? info.boundingBox.toJSON() : undefined, 13257 boundingSphere: info.boundingSphere ? info.boundingSphere.toJSON() : undefined 13258 } ) ); 13259 object.instanceInfo = this._instanceInfo.map( info => ( { ...info } ) ); 13260 13261 object.availableInstanceIds = this._availableInstanceIds.slice(); 13262 object.availableGeometryIds = this._availableGeometryIds.slice(); 13263 13264 object.nextIndexStart = this._nextIndexStart; 13265 object.nextVertexStart = this._nextVertexStart; 13266 object.geometryCount = this._geometryCount; 13267 13268 object.maxInstanceCount = this._maxInstanceCount; 13269 object.maxVertexCount = this._maxVertexCount; 13270 object.maxIndexCount = this._maxIndexCount; 13271 13272 object.geometryInitialized = this._geometryInitialized; 13273 13274 object.matricesTexture = this._matricesTexture.toJSON( meta ); 13275 13276 object.indirectTexture = this._indirectTexture.toJSON( meta ); 13277 13278 if ( this._colorsTexture !== null ) { 13279 13280 object.colorsTexture = this._colorsTexture.toJSON( meta ); 13281 13282 } 13283 13284 if ( this.boundingSphere !== null ) { 13285 13286 object.boundingSphere = this.boundingSphere.toJSON(); 13287 13288 } 13289 13290 if ( this.boundingBox !== null ) { 13291 13292 object.boundingBox = this.boundingBox.toJSON(); 13293 13294 } 13295 13296 } 13297 13298 // 13299 13300 function serialize( library, element ) { 13301 13302 if ( library[ element.uuid ] === undefined ) { 13303 13304 library[ element.uuid ] = element.toJSON( meta ); 13305 13306 } 13307 13308 return element.uuid; 13309 13310 } 13311 13312 if ( this.isScene ) { 13313 13314 if ( this.background ) { 13315 13316 if ( this.background.isColor ) { 13317 13318 object.background = this.background.toJSON(); 13319 13320 } else if ( this.background.isTexture ) { 13321 13322 object.background = this.background.toJSON( meta ).uuid; 13323 13324 } 13325 13326 } 13327 13328 if ( this.environment && this.environment.isTexture && this.environment.isRenderTargetTexture !== true ) { 13329 13330 object.environment = this.environment.toJSON( meta ).uuid; 13331 13332 } 13333 13334 } else if ( this.isMesh || this.isLine || this.isPoints ) { 13335 13336 object.geometry = serialize( meta.geometries, this.geometry ); 13337 13338 const parameters = this.geometry.parameters; 13339 13340 if ( parameters !== undefined && parameters.shapes !== undefined ) { 13341 13342 const shapes = parameters.shapes; 13343 13344 if ( Array.isArray( shapes ) ) { 13345 13346 for ( let i = 0, l = shapes.length; i < l; i ++ ) { 13347 13348 const shape = shapes[ i ]; 13349 13350 serialize( meta.shapes, shape ); 13351 13352 } 13353 13354 } else { 13355 13356 serialize( meta.shapes, shapes ); 13357 13358 } 13359 13360 } 13361 13362 } 13363 13364 if ( this.isSkinnedMesh ) { 13365 13366 object.bindMode = this.bindMode; 13367 object.bindMatrix = this.bindMatrix.toArray(); 13368 13369 if ( this.skeleton !== undefined ) { 13370 13371 serialize( meta.skeletons, this.skeleton ); 13372 13373 object.skeleton = this.skeleton.uuid; 13374 13375 } 13376 13377 } 13378 13379 if ( this.material !== undefined ) { 13380 13381 if ( Array.isArray( this.material ) ) { 13382 13383 const uuids = []; 13384 13385 for ( let i = 0, l = this.material.length; i < l; i ++ ) { 13386 13387 uuids.push( serialize( meta.materials, this.material[ i ] ) ); 13388 13389 } 13390 13391 object.material = uuids; 13392 13393 } else { 13394 13395 object.material = serialize( meta.materials, this.material ); 13396 13397 } 13398 13399 } 13400 13401 // 13402 13403 if ( this.children.length > 0 ) { 13404 13405 object.children = []; 13406 13407 for ( let i = 0; i < this.children.length; i ++ ) { 13408 13409 object.children.push( this.children[ i ].toJSON( meta ).object ); 13410 13411 } 13412 13413 } 13414 13415 // 13416 13417 if ( this.animations.length > 0 ) { 13418 13419 object.animations = []; 13420 13421 for ( let i = 0; i < this.animations.length; i ++ ) { 13422 13423 const animation = this.animations[ i ]; 13424 13425 object.animations.push( serialize( meta.animations, animation ) ); 13426 13427 } 13428 13429 } 13430 13431 if ( isRootObject ) { 13432 13433 const geometries = extractFromCache( meta.geometries ); 13434 const materials = extractFromCache( meta.materials ); 13435 const textures = extractFromCache( meta.textures ); 13436 const images = extractFromCache( meta.images ); 13437 const shapes = extractFromCache( meta.shapes ); 13438 const skeletons = extractFromCache( meta.skeletons ); 13439 const animations = extractFromCache( meta.animations ); 13440 const nodes = extractFromCache( meta.nodes ); 13441 13442 if ( geometries.length > 0 ) output.geometries = geometries; 13443 if ( materials.length > 0 ) output.materials = materials; 13444 if ( textures.length > 0 ) output.textures = textures; 13445 if ( images.length > 0 ) output.images = images; 13446 if ( shapes.length > 0 ) output.shapes = shapes; 13447 if ( skeletons.length > 0 ) output.skeletons = skeletons; 13448 if ( animations.length > 0 ) output.animations = animations; 13449 if ( nodes.length > 0 ) output.nodes = nodes; 13450 13451 } 13452 13453 output.object = object; 13454 13455 return output; 13456 13457 // extract data from the cache hash 13458 // remove metadata on each item 13459 // and return as array 13460 function extractFromCache( cache ) { 13461 13462 const values = []; 13463 for ( const key in cache ) { 13464 13465 const data = cache[ key ]; 13466 delete data.metadata; 13467 values.push( data ); 13468 13469 } 13470 13471 return values; 13472 13473 } 13474 13475 } 13476 13477 /** 13478 * Returns a new 3D object with copied values from this instance. 13479 * 13480 * @param {boolean} [recursive=true] - When set to `true`, descendants of the 3D object are also cloned. 13481 * @return {Object3D} A clone of this instance. 13482 */ 13483 clone( recursive ) { 13484 13485 return new this.constructor().copy( this, recursive ); 13486 13487 } 13488 13489 /** 13490 * Copies the values of the given 3D object to this instance. 13491 * 13492 * @param {Object3D} source - The 3D object to copy. 13493 * @param {boolean} [recursive=true] - When set to `true`, descendants of the 3D object are cloned. 13494 * @return {Object3D} A reference to this instance. 13495 */ 13496 copy( source, recursive = true ) { 13497 13498 this.name = source.name; 13499 13500 this.up.copy( source.up ); 13501 13502 this.position.copy( source.position ); 13503 this.rotation.order = source.rotation.order; 13504 this.quaternion.copy( source.quaternion ); 13505 this.scale.copy( source.scale ); 13506 13507 this.pivot = ( source.pivot !== null ) ? source.pivot.clone() : null; 13508 13509 this.matrix.copy( source.matrix ); 13510 this.matrixWorld.copy( source.matrixWorld ); 13511 13512 this.matrixAutoUpdate = source.matrixAutoUpdate; 13513 13514 this.matrixWorldAutoUpdate = source.matrixWorldAutoUpdate; 13515 this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; 13516 13517 this.layers.mask = source.layers.mask; 13518 this.visible = source.visible; 13519 13520 this.castShadow = source.castShadow; 13521 this.receiveShadow = source.receiveShadow; 13522 13523 this.frustumCulled = source.frustumCulled; 13524 this.renderOrder = source.renderOrder; 13525 13526 this.static = source.static; 13527 13528 this.animations = source.animations.slice(); 13529 13530 this.userData = JSON.parse( JSON.stringify( source.userData ) ); 13531 13532 if ( recursive === true ) { 13533 13534 for ( let i = 0; i < source.children.length; i ++ ) { 13535 13536 const child = source.children[ i ]; 13537 this.add( child.clone() ); 13538 13539 } 13540 13541 } 13542 13543 return this; 13544 13545 } 13546 13547 /** 13548 * Frees the GPU-related resources allocated by this instance. Call this 13549 * method whenever this instance is no longer used in your app. 13550 * 13551 * Geometries, materials and textures are potentially shared with other 13552 * 3D objects and must be disposed of separately. 13553 * 13554 * @fires Object3D#dispose 13555 */ 13556 dispose() { 13557 13558 /** 13559 * Fires when the 3D object has been disposed of. 13560 * 13561 * @event Object3D#dispose 13562 * @type {Object} 13563 */ 13564 this.dispatchEvent( { type: 'dispose' } ); 13565 13566 } 13567 13568} 13569 13570/** 13571 * The default up direction for objects, also used as the default 13572 * position for {@link DirectionalLight} and {@link HemisphereLight}. 13573 * 13574 * @static 13575 * @type {Vector3} 13576 * @default (0,1,0) 13577 */ 13578Object3D.DEFAULT_UP = /*@__PURE__*/ new Vector3( 0, 1, 0 ); 13579 13580/** 13581 * The default setting for {@link Object3D#matrixAutoUpdate} for 13582 * newly created 3D objects. 13583 * 13584 * @static 13585 * @type {boolean} 13586 * @default true 13587 */ 13588Object3D.DEFAULT_MATRIX_AUTO_UPDATE = true; 13589 13590/** 13591 * The default setting for {@link Object3D#matrixWorldAutoUpdate} for 13592 * newly created 3D objects. 13593 * 13594 * @static 13595 * @type {boolean} 13596 * @default true 13597 */ 13598Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE = true; 13599 13600/** 13601 * This is almost identical to an {@link Object3D}. Its purpose is to 13602 * make working with groups of objects syntactically clearer. 13603 * 13604 * ```js 13605 * // Create a group and add the two cubes. 13606 * // These cubes can now be rotated / scaled etc as a group. 13607 * const group = new THREE.Group(); 13608 * 13609 * group.add( meshA ); 13610 * group.add( meshB ); 13611 * 13612 * scene.add( group ); 13613 * ``` 13614 * 13615 * @augments Object3D 13616 */ 13617class Group extends Object3D { 13618 13619 constructor() { 13620 13621 super(); 13622 13623 /** 13624 * This flag can be used for type testing. 13625 * 13626 * @type {boolean} 13627 * @readonly 13628 * @default true 13629 */ 13630 this.isGroup = true; 13631 13632 this.type = 'Group'; 13633 13634 } 13635 13636} 13637 13638const _moveEvent = { type: 'move' }; 13639 13640/** 13641 * Class for representing a XR controller with its 13642 * different coordinate systems. 13643 * 13644 * @private 13645 */ 13646class WebXRController { 13647 13648 /** 13649 * Constructs a new XR controller. 13650 */ 13651 constructor() { 13652 13653 /** 13654 * A group representing the target ray space 13655 * of the XR controller. 13656 * 13657 * @private 13658 * @type {?Group} 13659 * @default null 13660 */ 13661 this._targetRay = null; 13662 13663 /** 13664 * A group representing the grip space 13665 * of the XR controller. 13666 * 13667 * @private 13668 * @type {?Group} 13669 * @default null 13670 */ 13671 this._grip = null; 13672 13673 /** 13674 * A group representing the hand space 13675 * of the XR controller. 13676 * 13677 * @private 13678 * @type {?Group} 13679 * @default null 13680 */ 13681 this._hand = null; 13682 13683 } 13684 13685 /** 13686 * Returns a group representing the hand space of the XR controller. 13687 * 13688 * @return {Group} A group representing the hand space of the XR controller. 13689 */ 13690 getHandSpace() { 13691 13692 if ( this._hand === null ) { 13693 13694 this._hand = new Group(); 13695 this._hand.matrixAutoUpdate = false; 13696 this._hand.visible = false; 13697 13698 this._hand.joints = {}; 13699 this._hand.inputState = { pinching: false }; 13700 13701 } 13702 13703 return this._hand; 13704 13705 } 13706 13707 /** 13708 * Returns a group representing the target ray space of the XR controller. 13709 * 13710 * @return {Group} A group representing the target ray space of the XR controller. 13711 */ 13712 getTargetRaySpace() { 13713 13714 if ( this._targetRay === null ) { 13715 13716 this._targetRay = new Group(); 13717 this._targetRay.matrixAutoUpdate = false; 13718 this._targetRay.visible = false; 13719 this._targetRay.hasLinearVelocity = false; 13720 this._targetRay.linearVelocity = new Vector3(); 13721 this._targetRay.hasAngularVelocity = false; 13722 this._targetRay.angularVelocity = new Vector3(); 13723 13724 } 13725 13726 return this._targetRay; 13727 13728 } 13729 13730 /** 13731 * Returns a group representing the grip space of the XR controller. 13732 * 13733 * @return {Group} A group representing the grip space of the XR controller. 13734 */ 13735 getGripSpace() { 13736 13737 if ( this._grip === null ) { 13738 13739 this._grip = new Group(); 13740 this._grip.matrixAutoUpdate = false; 13741 this._grip.visible = false; 13742 this._grip.hasLinearVelocity = false; 13743 this._grip.linearVelocity = new Vector3(); 13744 this._grip.hasAngularVelocity = false; 13745 this._grip.angularVelocity = new Vector3(); 13746 this._grip.eventsEnabled = false; 13747 13748 } 13749 13750 return this._grip; 13751 13752 } 13753 13754 /** 13755 * Dispatches the given event to the groups representing 13756 * the different coordinate spaces of the XR controller. 13757 * 13758 * @param {Object} event - The event to dispatch. 13759 * @return {WebXRController} A reference to this instance. 13760 */ 13761 dispatchEvent( event ) { 13762 13763 if ( this._targetRay !== null ) { 13764 13765 this._targetRay.dispatchEvent( event ); 13766 13767 } 13768 13769 if ( this._grip !== null ) { 13770 13771 this._grip.dispatchEvent( event ); 13772 13773 } 13774 13775 if ( this._hand !== null ) { 13776 13777 this._hand.dispatchEvent( event ); 13778 13779 } 13780 13781 return this; 13782 13783 } 13784 13785 /** 13786 * Connects the controller with the given XR input source. 13787 * 13788 * @param {XRInputSource} inputSource - The input source. 13789 * @return {WebXRController} A reference to this instance. 13790 */ 13791 connect( inputSource ) { 13792 13793 if ( inputSource && inputSource.hand ) { 13794 13795 const hand = this._hand; 13796 13797 if ( hand ) { 13798 13799 for ( const inputjoint of inputSource.hand.values() ) { 13800 13801 // Initialize hand with joints when connected 13802 this._getHandJoint( hand, inputjoint ); 13803 13804 } 13805 13806 } 13807 13808 } 13809 13810 this.dispatchEvent( { type: 'connected', data: inputSource } ); 13811 13812 return this; 13813 13814 } 13815 13816 /** 13817 * Disconnects the controller from the given XR input source. 13818 * 13819 * @param {XRInputSource} inputSource - The input source. 13820 * @return {WebXRController} A reference to this instance. 13821 */ 13822 disconnect( inputSource ) { 13823 13824 this.dispatchEvent( { type: 'disconnected', data: inputSource } ); 13825 13826 if ( this._targetRay !== null ) { 13827 13828 this._targetRay.visible = false; 13829 13830 } 13831 13832 if ( this._grip !== null ) { 13833 13834 this._grip.visible = false; 13835 13836 } 13837 13838 if ( this._hand !== null ) { 13839 13840 this._hand.visible = false; 13841 13842 } 13843 13844 return this; 13845 13846 } 13847 13848 /** 13849 * Updates the controller with the given input source, XR frame and reference space. 13850 * This updates the transformations of the groups that represent the different 13851 * coordinate systems of the controller. 13852 * 13853 * @param {XRInputSource} inputSource - The input source. 13854 * @param {XRFrame} frame - The XR frame. 13855 * @param {XRReferenceSpace} referenceSpace - The reference space. 13856 * @return {WebXRController} A reference to this instance. 13857 */ 13858 update( inputSource, frame, referenceSpace ) { 13859 13860 let inputPose = null; 13861 let gripPose = null; 13862 let handPose = null; 13863 13864 const targetRay = this._targetRay; 13865 const grip = this._grip; 13866 const hand = this._hand; 13867 13868 if ( inputSource && frame.session.visibilityState !== 'visible-blurred' ) { 13869 13870 if ( hand && inputSource.hand ) { 13871 13872 handPose = true; 13873 13874 for ( const inputjoint of inputSource.hand.values() ) { 13875 13876 // Update the joints groups with the XRJoint poses 13877 const jointPose = frame.getJointPose( inputjoint, referenceSpace ); 13878 13879 // The transform of this joint will be updated with the joint pose on each frame 13880 const joint = this._getHandJoint( hand, inputjoint ); 13881 13882 if ( jointPose !== null ) { 13883 13884 joint.matrix.fromArray( jointPose.transform.matrix ); 13885 joint.matrix.decompose( joint.position, joint.rotation, joint.scale ); 13886 joint.matrixWorldNeedsUpdate = true; 13887 joint.jointRadius = jointPose.radius; 13888 13889 } 13890 13891 joint.visible = jointPose !== null; 13892 13893 } 13894 13895 // Custom events 13896 13897 // Check pinchz 13898 const indexTip = hand.joints[ 'index-finger-tip' ]; 13899 const thumbTip = hand.joints[ 'thumb-tip' ]; 13900 const distance = indexTip.position.distanceTo( thumbTip.position ); 13901 13902 const distanceToPinch = 0.02; 13903 const threshold = 0.005; 13904 13905 if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) { 13906 13907 hand.inputState.pinching = false; 13908 this.dispatchEvent( { 13909 type: 'pinchend', 13910 handedness: inputSource.handedness, 13911 target: this 13912 } ); 13913 13914 } else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) { 13915 13916 hand.inputState.pinching = true; 13917 this.dispatchEvent( { 13918 type: 'pinchstart', 13919 handedness: inputSource.handedness, 13920 target: this 13921 } ); 13922 13923 } 13924 13925 } else { 13926 13927 if ( grip !== null && inputSource.gripSpace ) { 13928 13929 gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); 13930 13931 if ( gripPose !== null ) { 13932 13933 grip.matrix.fromArray( gripPose.transform.matrix ); 13934 grip.matrix.decompose( grip.position, grip.rotation, grip.scale ); 13935 grip.matrixWorldNeedsUpdate = true; 13936 13937 if ( gripPose.linearVelocity ) { 13938 13939 grip.hasLinearVelocity = true; 13940 grip.linearVelocity.copy( gripPose.linearVelocity ); 13941 13942 } else { 13943 13944 grip.hasLinearVelocity = false; 13945 13946 } 13947 13948 if ( gripPose.angularVelocity ) { 13949 13950 grip.hasAngularVelocity = true; 13951 grip.angularVelocity.copy( gripPose.angularVelocity ); 13952 13953 } else { 13954 13955 grip.hasAngularVelocity = false; 13956 13957 } 13958 13959 // grip update event if enabled 13960 if ( grip.eventsEnabled ) { 13961 13962 grip.dispatchEvent( { 13963 type: 'gripUpdated', 13964 data: inputSource, 13965 target: this 13966 } ); 13967 13968 } 13969 13970 } 13971 13972 } 13973 13974 } 13975 13976 if ( targetRay !== null ) { 13977 13978 inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); 13979 13980 // Some runtimes (namely Vive Cosmos with Vive OpenXR Runtime) have only grip space and ray space is equal to it 13981 if ( inputPose === null && gripPose !== null ) { 13982 13983 inputPose = gripPose; 13984 13985 } 13986 13987 if ( inputPose !== null ) { 13988 13989 targetRay.matrix.fromArray( inputPose.transform.matrix ); 13990 targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale ); 13991 targetRay.matrixWorldNeedsUpdate = true; 13992 13993 if ( inputPose.linearVelocity ) { 13994 13995 targetRay.hasLinearVelocity = true; 13996 targetRay.linearVelocity.copy( inputPose.linearVelocity ); 13997 13998 } else { 13999 14000 targetRay.hasLinearVelocity = false; 14001 14002 } 14003 14004 if ( inputPose.angularVelocity ) { 14005 14006 targetRay.hasAngularVelocity = true; 14007 targetRay.angularVelocity.copy( inputPose.angularVelocity ); 14008 14009 } else { 14010 14011 targetRay.hasAngularVelocity = false; 14012 14013 } 14014 14015 this.dispatchEvent( _moveEvent ); 14016 14017 } 14018 14019 } 14020 14021 14022 } 14023 14024 if ( targetRay !== null ) { 14025 14026 targetRay.visible = ( inputPose !== null ); 14027 14028 } 14029 14030 if ( grip !== null ) { 14031 14032 grip.visible = ( gripPose !== null ); 14033 14034 } 14035 14036 if ( hand !== null ) { 14037 14038 hand.visible = ( handPose !== null ); 14039 14040 } 14041 14042 return this; 14043 14044 } 14045 14046 /** 14047 * Returns a group representing the hand joint for the given input joint. 14048 * 14049 * @private 14050 * @param {Group} hand - The group representing the hand space. 14051 * @param {XRJointSpace} inputjoint - The hand joint data. 14052 * @return {Group} A group representing the hand joint for the given input joint. 14053 */ 14054 _getHandJoint( hand, inputjoint ) { 14055 14056 if ( hand.joints[ inputjoint.jointName ] === undefined ) { 14057 14058 const joint = new Group(); 14059 joint.matrixAutoUpdate = false; 14060 joint.visible = false; 14061 hand.joints[ inputjoint.jointName ] = joint; 14062 14063 hand.add( joint ); 14064 14065 } 14066 14067 return hand.joints[ inputjoint.jointName ]; 14068 14069 } 14070 14071} 14072 14073const _colorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF, 14074 'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2, 14075 'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50, 14076 'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B, 14077 'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B, 14078 'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F, 14079 'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3, 14080 'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222, 14081 'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700, 14082 'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4, 14083 'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00, 14084 'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3, 14085 'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA, 14086 'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32, 14087 'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3, 14088 'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC, 14089 'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD, 14090 'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6, 14091 'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9, 14092 'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F, 14093 'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE, 14094 'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA, 14095 'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0, 14096 'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 }; 14097 14098const _hslA = { h: 0, s: 0, l: 0 }; 14099const _hslB = { h: 0, s: 0, l: 0 }; 14100 14101function hue2rgb( p, q, t ) { 14102 14103 if ( t < 0 ) t += 1; 14104 if ( t > 1 ) t -= 1; 14105 if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; 14106 if ( t < 1 / 2 ) return q; 14107 if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); 14108 return p; 14109 14110} 14111 14112/** 14113 * A Color instance is represented by RGB components in the linear <i>working 14114 * color space</i>, which defaults to `LinearSRGBColorSpace`. Inputs 14115 * conventionally using `SRGBColorSpace` (such as hexadecimals and CSS 14116 * strings) are converted to the working color space automatically. 14117 * 14118 * ```js 14119 * // converted automatically from SRGBColorSpace to LinearSRGBColorSpace 14120 * const color = new THREE.Color().setHex( 0x112233 ); 14121 * ``` 14122 * Source color spaces may be specified explicitly, to ensure correct conversions. 14123 * ```js 14124 * // assumed already LinearSRGBColorSpace; no conversion 14125 * const color = new THREE.Color().setRGB( 0.5, 0.5, 0.5 ); 14126 * 14127 * // converted explicitly from SRGBColorSpace to LinearSRGBColorSpace 14128 * const color = new THREE.Color().setRGB( 0.5, 0.5, 0.5, SRGBColorSpace ); 14129 * ``` 14130 * If THREE.ColorManagement is disabled, no conversions occur. For details, 14131 * see <i>Color management</i>. Iterating through a Color instance will yield 14132 * its components (r, g, b) in the corresponding order. A Color can be initialised 14133 * in any of the following ways: 14134 * ```js 14135 * //empty constructor - will default white 14136 * const color1 = new THREE.Color(); 14137 * 14138 * //Hexadecimal color (recommended) 14139 * const color2 = new THREE.Color( 0xff0000 ); 14140 * 14141 * //RGB string 14142 * const color3 = new THREE.Color("rgb(255, 0, 0)"); 14143 * const color4 = new THREE.Color("rgb(100%, 0%, 0%)"); 14144 * 14145 * //X11 color name - all 140 color names are supported. 14146 * //Note the lack of CamelCase in the name 14147 * const color5 = new THREE.Color( 'skyblue' ); 14148 * //HSL string 14149 * const color6 = new THREE.Color("hsl(0, 100%, 50%)"); 14150 * 14151 * //Separate RGB values between 0 and 1 14152 * const color7 = new THREE.Color( 1, 0, 0 ); 14153 * ``` 14154 */ 14155class Color { 14156 14157 /** 14158 * Constructs a new color. 14159 * 14160 * Note that standard method of specifying color in three.js is with a hexadecimal triplet, 14161 * and that method is used throughout the rest of the documentation. 14162 * 14163 * @param {(number|string|Color)} [r] - The red component of the color. If `g` and `b` are 14164 * not provided, it can be hexadecimal triplet, a CSS-style string or another `Color` instance. 14165 * @param {number} [g] - The green component. 14166 * @param {number} [b] - The blue component. 14167 */ 14168 constructor( r, g, b ) { 14169 14170 /** 14171 * This flag can be used for type testing. 14172 * 14173 * @type {boolean} 14174 * @readonly 14175 * @default true 14176 */ 14177 this.isColor = true; 14178 14179 /** 14180 * The red component. 14181 * 14182 * @type {number} 14183 * @default 1 14184 */ 14185 this.r = 1; 14186 14187 /** 14188 * The green component. 14189 * 14190 * @type {number} 14191 * @default 1 14192 */ 14193 this.g = 1; 14194 14195 /** 14196 * The blue component. 14197 * 14198 * @type {number} 14199 * @default 1 14200 */ 14201 this.b = 1; 14202 14203 return this.set( r, g, b ); 14204 14205 } 14206 14207 /** 14208 * Sets the colors's components from the given values. 14209 * 14210 * @param {(number|string|Color)} [r] - The red component of the color. If `g` and `b` are 14211 * not provided, it can be hexadecimal triplet, a CSS-style string or another `Color` instance. 14212 * @param {number} [g] - The green component. 14213 * @param {number} [b] - The blue component. 14214 * @return {Color} A reference to this color. 14215 */ 14216 set( r, g, b ) { 14217 14218 if ( g === undefined && b === undefined ) { 14219 14220 // r is THREE.Color, hex or string 14221 14222 const value = r; 14223 14224 if ( value && value.isColor ) { 14225 14226 this.copy( value ); 14227 14228 } else if ( typeof value === 'number' ) { 14229 14230 this.setHex( value ); 14231 14232 } else if ( typeof value === 'string' ) { 14233 14234 this.setStyle( value ); 14235 14236 } 14237 14238 } else { 14239 14240 this.setRGB( r, g, b ); 14241 14242 } 14243 14244 return this; 14245 14246 } 14247 14248 /** 14249 * Sets the colors's components to the given scalar value. 14250 * 14251 * @param {number} scalar - The scalar value. 14252 * @return {Color} A reference to this color. 14253 */ 14254 setScalar( scalar ) { 14255 14256 this.r = scalar; 14257 this.g = scalar; 14258 this.b = scalar; 14259 14260 return this; 14261 14262 } 14263 14264 /** 14265 * Sets this color from a hexadecimal value. 14266 * 14267 * @param {number} hex - The hexadecimal value. 14268 * @param {string} [colorSpace=SRGBColorSpace] - The color space. 14269 * @return {Color} A reference to this color. 14270 */ 14271 setHex( hex, colorSpace = SRGBColorSpace ) { 14272 14273 hex = Math.floor( hex ); 14274 14275 this.r = ( hex >> 16 & 255 ) / 255; 14276 this.g = ( hex >> 8 & 255 ) / 255; 14277 this.b = ( hex & 255 ) / 255; 14278 14279 ColorManagement.colorSpaceToWorking( this, colorSpace ); 14280 14281 return this; 14282 14283 } 14284 14285 /** 14286 * Sets this color from RGB values. 14287 * 14288 * @param {number} r - Red channel value between `0.0` and `1.0`. 14289 * @param {number} g - Green channel value between `0.0` and `1.0`. 14290 * @param {number} b - Blue channel value between `0.0` and `1.0`. 14291 * @param {string} [colorSpace=ColorManagement.workingColorSpace] - The color space. 14292 * @return {Color} A reference to this color. 14293 */ 14294 setRGB( r, g, b, colorSpace = ColorManagement.workingColorSpace ) { 14295 14296 this.r = r; 14297 this.g = g; 14298 this.b = b; 14299 14300 ColorManagement.colorSpaceToWorking( this, colorSpace ); 14301 14302 return this; 14303 14304 } 14305 14306 /** 14307 * Sets this color from RGB values. 14308 * 14309 * @param {number} h - Hue value between `0.0` and `1.0`. 14310 * @param {number} s - Saturation value between `0.0` and `1.0`. 14311 * @param {number} l - Lightness value between `0.0` and `1.0`. 14312 * @param {string} [colorSpace=ColorManagement.workingColorSpace] - The color space. 14313 * @return {Color} A reference to this color. 14314 */ 14315 setHSL( h, s, l, colorSpace = ColorManagement.workingColorSpace ) { 14316 14317 // h,s,l ranges are in 0.0 - 1.0 14318 h = euclideanModulo( h, 1 ); 14319 s = clamp( s, 0, 1 ); 14320 l = clamp( l, 0, 1 ); 14321 14322 if ( s === 0 ) { 14323 14324 this.r = this.g = this.b = l; 14325 14326 } else { 14327 14328 const p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); 14329 const q = ( 2 * l ) - p; 14330 14331 this.r = hue2rgb( q, p, h + 1 / 3 ); 14332 this.g = hue2rgb( q, p, h ); 14333 this.b = hue2rgb( q, p, h - 1 / 3 ); 14334 14335 } 14336 14337 ColorManagement.colorSpaceToWorking( this, colorSpace ); 14338 14339 return this; 14340 14341 } 14342 14343 /** 14344 * Sets this color from a CSS-style string. For example, `rgb(250, 0,0)`, 14345 * `rgb(100%, 0%, 0%)`, `hsl(0, 100%, 50%)`, `#ff0000`, `#f00`, or `red` ( or 14346 * any [X11 color name](https://en.wikipedia.org/wiki/X11_color_names#Color_name_chart) - 14347 * all 140 color names are supported). 14348 * 14349 * @param {string} style - Color as a CSS-style string. 14350 * @param {string} [colorSpace=SRGBColorSpace] - The color space. 14351 * @return {Color} A reference to this color. 14352 */ 14353 setStyle( style, colorSpace = SRGBColorSpace ) { 14354 14355 function handleAlpha( string ) { 14356 14357 if ( string === undefined ) return; 14358 14359 if ( parseFloat( string ) < 1 ) { 14360 14361 warn( 'Color: Alpha component of ' + style + ' will be ignored.' ); 14362 14363 } 14364 14365 } 14366 14367 14368 let m; 14369 14370 if ( m = /^(\w+)\(([^\)]*)\)/.exec( style ) ) { 14371 14372 // rgb / hsl 14373 14374 let color; 14375 const name = m[ 1 ]; 14376 const components = m[ 2 ]; 14377 14378 switch ( name ) { 14379 14380 case 'rgb': 14381 case 'rgba': 14382 14383 if ( color = /^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { 14384 14385 // rgb(255,0,0) rgba(255,0,0,0.5) 14386 14387 handleAlpha( color[ 4 ] ); 14388 14389 return this.setRGB( 14390 Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255, 14391 Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255, 14392 Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255, 14393 colorSpace 14394 ); 14395 14396 } 14397 14398 if ( color = /^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { 14399 14400 // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) 14401 14402 handleAlpha( color[ 4 ] ); 14403 14404 return this.setRGB( 14405 Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100, 14406 Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100, 14407 Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100, 14408 colorSpace 14409 ); 14410 14411 } 14412 14413 break; 14414 14415 case 'hsl': 14416 case 'hsla': 14417 14418 if ( color = /^\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\%\s*,\s*(\d*\.?\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { 14419 14420 // hsl(120,50%,50%) hsla(120,50%,50%,0.5) 14421 14422 handleAlpha( color[ 4 ] ); 14423 14424 return this.setHSL( 14425 parseFloat( color[ 1 ] ) / 360, 14426 parseFloat( color[ 2 ] ) / 100, 14427 parseFloat( color[ 3 ] ) / 100, 14428 colorSpace 14429 ); 14430 14431 } 14432 14433 break; 14434 14435 default: 14436 14437 warn( 'Color: Unknown color model ' + style ); 14438 14439 } 14440 14441 } else if ( m = /^\#([A-Fa-f\d]+)$/.exec( style ) ) { 14442 14443 // hex color 14444 14445 const hex = m[ 1 ]; 14446 const size = hex.length; 14447 14448 if ( size === 3 ) { 14449 14450 // #ff0 14451 return this.setRGB( 14452 parseInt( hex.charAt( 0 ), 16 ) / 15, 14453 parseInt( hex.charAt( 1 ), 16 ) / 15, 14454 parseInt( hex.charAt( 2 ), 16 ) / 15, 14455 colorSpace 14456 ); 14457 14458 } else if ( size === 6 ) { 14459 14460 // #ff0000 14461 return this.setHex( parseInt( hex, 16 ), colorSpace ); 14462 14463 } else { 14464 14465 warn( 'Color: Invalid hex color ' + style ); 14466 14467 } 14468 14469 } else if ( style && style.length > 0 ) { 14470 14471 return this.setColorName( style, colorSpace ); 14472 14473 } 14474 14475 return this; 14476 14477 } 14478 14479 /** 14480 * Sets this color from a color name. Faster than {@link Color#setStyle} if 14481 * you don't need the other CSS-style formats. 14482 * 14483 * For convenience, the list of names is exposed in `Color.NAMES` as a hash. 14484 * ```js 14485 * Color.NAMES.aliceblue // returns 0xF0F8FF 14486 * ``` 14487 * 14488 * @param {string} style - The color name. 14489 * @param {string} [colorSpace=SRGBColorSpace] - The color space. 14490 * @return {Color} A reference to this color. 14491 */ 14492 setColorName( style, colorSpace = SRGBColorSpace ) { 14493 14494 // color keywords 14495 const hex = _colorKeywords[ style.toLowerCase() ]; 14496 14497 if ( hex !== undefined ) { 14498 14499 // red 14500 this.setHex( hex, colorSpace ); 14501 14502 } else { 14503 14504 // unknown color 14505 warn( 'Color: Unknown color ' + style ); 14506 14507 } 14508 14509 return this; 14510 14511 } 14512 14513 /** 14514 * Returns a new color with copied values from this instance. 14515 * 14516 * @return {Color} A clone of this instance. 14517 */ 14518 clone() { 14519 14520 return new this.constructor( this.r, this.g, this.b ); 14521 14522 } 14523 14524 /** 14525 * Copies the values of the given color to this instance. 14526 * 14527 * @param {Color} color - The color to copy. 14528 * @return {Color} A reference to this color. 14529 */ 14530 copy( color ) { 14531 14532 this.r = color.r; 14533 this.g = color.g; 14534 this.b = color.b; 14535 14536 return this; 14537 14538 } 14539 14540 /** 14541 * Copies the given color into this color, and then converts this color from 14542 * `SRGBColorSpace` to `LinearSRGBColorSpace`. 14543 * 14544 * @param {Color} color - The color to copy/convert. 14545 * @return {Color} A reference to this color. 14546 */ 14547 copySRGBToLinear( color ) { 14548 14549 this.r = SRGBToLinear( color.r ); 14550 this.g = SRGBToLinear( color.g ); 14551 this.b = SRGBToLinear( color.b ); 14552 14553 return this; 14554 14555 } 14556 14557 /** 14558 * Copies the given color into this color, and then converts this color from 14559 * `LinearSRGBColorSpace` to `SRGBColorSpace`. 14560 * 14561 * @param {Color} color - The color to copy/convert. 14562 * @return {Color} A reference to this color. 14563 */ 14564 copyLinearToSRGB( color ) { 14565 14566 this.r = LinearToSRGB( color.r ); 14567 this.g = LinearToSRGB( color.g ); 14568 this.b = LinearToSRGB( color.b ); 14569 14570 return this; 14571 14572 } 14573 14574 /** 14575 * Converts this color from `SRGBColorSpace` to `LinearSRGBColorSpace`. 14576 * 14577 * @return {Color} A reference to this color. 14578 */ 14579 convertSRGBToLinear() { 14580 14581 this.copySRGBToLinear( this ); 14582 14583 return this; 14584 14585 } 14586 14587 /** 14588 * Converts this color from `LinearSRGBColorSpace` to `SRGBColorSpace`. 14589 * 14590 * @return {Color} A reference to this color. 14591 */ 14592 convertLinearToSRGB() { 14593 14594 this.copyLinearToSRGB( this ); 14595 14596 return this; 14597 14598 } 14599 14600 /** 14601 * Returns the hexadecimal value of this color. 14602 * 14603 * @param {string} [colorSpace=SRGBColorSpace] - The color space. 14604 * @return {number} The hexadecimal value. 14605 */ 14606 getHex( colorSpace = SRGBColorSpace ) { 14607 14608 ColorManagement.workingToColorSpace( _color.copy( this ), colorSpace ); 14609 14610 return Math.round( clamp( _color.r * 255, 0, 255 ) ) * 65536 + Math.round( clamp( _color.g * 255, 0, 255 ) ) * 256 + Math.round( clamp( _color.b * 255, 0, 255 ) ); 14611 14612 } 14613 14614 /** 14615 * Returns the hexadecimal value of this color as a string (for example, 'FFFFFF'). 14616 * 14617 * @param {string} [colorSpace=SRGBColorSpace] - The color space. 14618 * @return {string} The hexadecimal value as a string. 14619 */ 14620 getHexString( colorSpace = SRGBColorSpace ) { 14621 14622 return ( '000000' + this.getHex( colorSpace ).toString( 16 ) ).slice( -6 ); 14623 14624 } 14625 14626 /** 14627 * Converts the colors RGB values into the HSL format and stores them into the 14628 * given target object. 14629 * 14630 * @param {{h:number,s:number,l:number}} target - The target object that is used to store the method's result. 14631 * @param {string} [colorSpace=ColorManagement.workingColorSpace] - The color space. 14632 * @return {{h:number,s:number,l:number}} The HSL representation of this color. 14633 */ 14634 getHSL( target, colorSpace = ColorManagement.workingColorSpace ) { 14635 14636 // h,s,l ranges are in 0.0 - 1.0 14637 14638 ColorManagement.workingToColorSpace( _color.copy( this ), colorSpace ); 14639 14640 const r = _color.r, g = _color.g, b = _color.b; 14641 14642 const max = Math.max( r, g, b ); 14643 const min = Math.min( r, g, b ); 14644 14645 let hue, saturation; 14646 const lightness = ( min + max ) / 2.0; 14647 14648 if ( min === max ) { 14649 14650 hue = 0; 14651 saturation = 0; 14652 14653 } else { 14654 14655 const delta = max - min; 14656 14657 saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); 14658 14659 switch ( max ) { 14660 14661 case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; 14662 case g: hue = ( b - r ) / delta + 2; break; 14663 case b: hue = ( r - g ) / delta + 4; break; 14664 14665 } 14666 14667 hue /= 6; 14668 14669 } 14670 14671 target.h = hue; 14672 target.s = saturation; 14673 target.l = lightness; 14674 14675 return target; 14676 14677 } 14678 14679 /** 14680 * Returns the RGB values of this color and stores them into the given target object. 14681 * 14682 * @param {Color} target - The target color that is used to store the method's result. 14683 * @param {string} [colorSpace=ColorManagement.workingColorSpace] - The color space. 14684 * @return {Color} The RGB representation of this color. 14685 */ 14686 getRGB( target, colorSpace = ColorManagement.workingColorSpace ) { 14687 14688 ColorManagement.workingToColorSpace( _color.copy( this ), colorSpace ); 14689 14690 target.r = _color.r; 14691 target.g = _color.g; 14692 target.b = _color.b; 14693 14694 return target; 14695 14696 } 14697 14698 /** 14699 * Returns the value of this color as a CSS style string. Example: `rgb(255,0,0)`. 14700 * 14701 * @param {string} [colorSpace=SRGBColorSpace] - The color space. 14702 * @return {string} The CSS representation of this color. 14703 */ 14704 getStyle( colorSpace = SRGBColorSpace ) { 14705 14706 ColorManagement.workingToColorSpace( _color.copy( this ), colorSpace ); 14707 14708 const r = _color.r, g = _color.g, b = _color.b; 14709 14710 if ( colorSpace !== SRGBColorSpace ) { 14711 14712 // Requires CSS Color Module Level 4 (https://www.w3.org/TR/css-color-4/). 14713 return `color(${ colorSpace } ${ r.toFixed( 3 ) } ${ g.toFixed( 3 ) } ${ b.toFixed( 3 ) })`; 14714 14715 } 14716 14717 return `rgb(${ Math.round( r * 255 ) },${ Math.round( g * 255 ) },${ Math.round( b * 255 ) })`; 14718 14719 } 14720 14721 /** 14722 * Adds the given HSL values to this color's values. 14723 * Internally, this converts the color's RGB values to HSL, adds HSL 14724 * and then converts the color back to RGB. 14725 * 14726 * @param {number} h - Hue value between `0.0` and `1.0`. 14727 * @param {number} s - Saturation value between `0.0` and `1.0`. 14728 * @param {number} l - Lightness value between `0.0` and `1.0`. 14729 * @return {Color} A reference to this color. 14730 */ 14731 offsetHSL( h, s, l ) { 14732 14733 this.getHSL( _hslA ); 14734 14735 return this.setHSL( _hslA.h + h, _hslA.s + s, _hslA.l + l ); 14736 14737 } 14738 14739 /** 14740 * Adds the RGB values of the given color to the RGB values of this color. 14741 * 14742 * @param {Color} color - The color to add. 14743 * @return {Color} A reference to this color. 14744 */ 14745 add( color ) { 14746 14747 this.r += color.r; 14748 this.g += color.g; 14749 this.b += color.b; 14750 14751 return this; 14752 14753 } 14754 14755 /** 14756 * Adds the RGB values of the given colors and stores the result in this instance. 14757 * 14758 * @param {Color} color1 - The first color. 14759 * @param {Color} color2 - The second color. 14760 * @return {Color} A reference to this color. 14761 */ 14762 addColors( color1, color2 ) { 14763 14764 this.r = color1.r + color2.r; 14765 this.g = color1.g + color2.g; 14766 this.b = color1.b + color2.b; 14767 14768 return this; 14769 14770 } 14771 14772 /** 14773 * Adds the given scalar value to the RGB values of this color. 14774 * 14775 * @param {number} s - The scalar to add. 14776 * @return {Color} A reference to this color. 14777 */ 14778 addScalar( s ) { 14779 14780 this.r += s; 14781 this.g += s; 14782 this.b += s; 14783 14784 return this; 14785 14786 } 14787 14788 /** 14789 * Subtracts the RGB values of the given color from the RGB values of this color. 14790 * 14791 * @param {Color} color - The color to subtract. 14792 * @return {Color} A reference to this color. 14793 */ 14794 sub( color ) { 14795 14796 this.r = Math.max( 0, this.r - color.r ); 14797 this.g = Math.max( 0, this.g - color.g ); 14798 this.b = Math.max( 0, this.b - color.b ); 14799 14800 return this; 14801 14802 } 14803 14804 /** 14805 * Multiplies the RGB values of the given color with the RGB values of this color. 14806 * 14807 * @param {Color} color - The color to multiply. 14808 * @return {Color} A reference to this color. 14809 */ 14810 multiply( color ) { 14811 14812 this.r *= color.r; 14813 this.g *= color.g; 14814 this.b *= color.b; 14815 14816 return this; 14817 14818 } 14819 14820 /** 14821 * Multiplies the given scalar value with the RGB values of this color. 14822 * 14823 * @param {number} s - The scalar to multiply. 14824 * @return {Color} A reference to this color. 14825 */ 14826 multiplyScalar( s ) { 14827 14828 this.r *= s; 14829 this.g *= s; 14830 this.b *= s; 14831 14832 return this; 14833 14834 } 14835 14836 /** 14837 * Linearly interpolates this color's RGB values toward the RGB values of the 14838 * given color. The alpha argument can be thought of as the ratio between 14839 * the two colors, where `0.0` is this color and `1.0` is the first argument. 14840 * 14841 * @param {Color} color - The color to converge on. 14842 * @param {number} alpha - The interpolation factor in the closed interval `[0,1]`. 14843 * @return {Color} A reference to this color. 14844 */ 14845 lerp( color, alpha ) { 14846 14847 this.r += ( color.r - this.r ) * alpha; 14848 this.g += ( color.g - this.g ) * alpha; 14849 this.b += ( color.b - this.b ) * alpha; 14850 14851 return this; 14852 14853 } 14854 14855 /** 14856 * Linearly interpolates between the given colors and stores the result in this instance. 14857 * The alpha argument can be thought of as the ratio between the two colors, where `0.0` 14858 * is the first and `1.0` is the second color. 14859 * 14860 * @param {Color} color1 - The first color. 14861 * @param {Color} color2 - The second color. 14862 * @param {number} alpha - The interpolation factor in the closed interval `[0,1]`. 14863 * @return {Color} A reference to this color. 14864 */ 14865 lerpColors( color1, color2, alpha ) { 14866 14867 this.r = color1.r + ( color2.r - color1.r ) * alpha; 14868 this.g = color1.g + ( color2.g - color1.g ) * alpha; 14869 this.b = color1.b + ( color2.b - color1.b ) * alpha; 14870 14871 return this; 14872 14873 } 14874 14875 /** 14876 * Linearly interpolates this color's HSL values toward the HSL values of the 14877 * given color. It differs from {@link Color#lerp} by not interpolating straight 14878 * from one color to the other, but instead going through all the hues in between 14879 * those two colors. The alpha argument can be thought of as the ratio between 14880 * the two colors, where 0.0 is this color and 1.0 is the first argument. 14881 * 14882 * @param {Color} color - The color to converge on. 14883 * @param {number} alpha - The interpolation factor in the closed interval `[0,1]`. 14884 * @return {Color} A reference to this color. 14885 */ 14886 lerpHSL( color, alpha ) { 14887 14888 this.getHSL( _hslA ); 14889 color.getHSL( _hslB ); 14890 14891 const h = lerp( _hslA.h, _hslB.h, alpha ); 14892 const s = lerp( _hslA.s, _hslB.s, alpha ); 14893 const l = lerp( _hslA.l, _hslB.l, alpha ); 14894 14895 this.setHSL( h, s, l ); 14896 14897 return this; 14898 14899 } 14900 14901 /** 14902 * Sets the color's RGB components from the given 3D vector. 14903 * 14904 * @param {Vector3} v - The vector to set. 14905 * @return {Color} A reference to this color. 14906 */ 14907 setFromVector3( v ) { 14908 14909 this.r = v.x; 14910 this.g = v.y; 14911 this.b = v.z; 14912 14913 return this; 14914 14915 } 14916 14917 /** 14918 * Transforms this color with the given 3x3 matrix. 14919 * 14920 * @param {Matrix3} m - The matrix. 14921 * @return {Color} A reference to this color. 14922 */ 14923 applyMatrix3( m ) { 14924 14925 const r = this.r, g = this.g, b = this.b; 14926 const e = m.elements; 14927 14928 this.r = e[ 0 ] * r + e[ 3 ] * g + e[ 6 ] * b; 14929 this.g = e[ 1 ] * r + e[ 4 ] * g + e[ 7 ] * b; 14930 this.b = e[ 2 ] * r + e[ 5 ] * g + e[ 8 ] * b; 14931 14932 return this; 14933 14934 } 14935 14936 /** 14937 * Returns `true` if this color is equal with the given one. 14938 * 14939 * @param {Color} c - The color to test for equality. 14940 * @return {boolean} Whether this bounding color is equal with the given one. 14941 */ 14942 equals( c ) { 14943 14944 return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); 14945 14946 } 14947 14948 /** 14949 * Sets this color's RGB components from the given array. 14950 * 14951 * @param {Array<number>} array - An array holding the RGB values. 14952 * @param {number} [offset=0] - The offset into the array. 14953 * @return {Color} A reference to this color. 14954 */ 14955 fromArray( array, offset = 0 ) { 14956 14957 this.r = array[ offset ]; 14958 this.g = array[ offset + 1 ]; 14959 this.b = array[ offset + 2 ]; 14960 14961 return this; 14962 14963 } 14964 14965 /** 14966 * Writes the RGB components of this color to the given array. If no array is provided, 14967 * the method returns a new instance. 14968 * 14969 * @param {Array<number>} [array=[]] - The target array holding the color components. 14970 * @param {number} [offset=0] - Index of the first element in the array. 14971 * @return {Array<number>} The color components. 14972 */ 14973 toArray( array = [], offset = 0 ) { 14974 14975 array[ offset ] = this.r; 14976 array[ offset + 1 ] = this.g; 14977 array[ offset + 2 ] = this.b; 14978 14979 return array; 14980 14981 } 14982 14983 /** 14984 * Sets the components of this color from the given buffer attribute. 14985 * 14986 * @param {BufferAttribute} attribute - The buffer attribute holding color data. 14987 * @param {number} index - The index into the attribute. 14988 * @return {Color} A reference to this color. 14989 */ 14990 fromBufferAttribute( attribute, index ) { 14991 14992 this.r = attribute.getX( index ); 14993 this.g = attribute.getY( index ); 14994 this.b = attribute.getZ( index ); 14995 14996 return this; 14997 14998 } 14999 15000 /** 15001 * This methods defines the serialization result of this class. Returns the color 15002 * as a hexadecimal value. 15003 * 15004 * @return {number} The hexadecimal value. 15005 */ 15006 toJSON() { 15007 15008 return this.getHex(); 15009 15010 } 15011 15012 *[ Symbol.iterator ]() { 15013 15014 yield this.r; 15015 yield this.g; 15016 yield this.b; 15017 15018 } 15019 15020} 15021 15022const _color = /*@__PURE__*/ new Color(); 15023 15024/** 15025 * A dictionary with X11 color names. 15026 * 15027 * Note that multiple words such as Dark Orange become the string 'darkorange'. 15028 * 15029 * @static 15030 * @type {Object} 15031 */ 15032Color.NAMES = _colorKeywords; 15033 15034/** 15035 * This class can be used to define an exponential squared fog, 15036 * which gives a clear view near the camera and a faster than exponentially 15037 * densening fog farther from the camera. 15038 * 15039 * ```js 15040 * const scene = new THREE.Scene(); 15041 * scene.fog = new THREE.FogExp2( 0xcccccc, 0.002 ); 15042 * ``` 15043 */ 15044class FogExp2 { 15045 15046 /** 15047 * Constructs a new fog. 15048 * 15049 * @param {number|Color} color - The fog's color. 15050 * @param {number} [density=0.00025] - Defines how fast the fog will grow dense. 15051 */ 15052 constructor( color, density = 0.00025 ) { 15053 15054 /** 15055 * This flag can be used for type testing. 15056 * 15057 * @type {boolean} 15058 * @readonly 15059 * @default true 15060 */ 15061 this.isFogExp2 = true; 15062 15063 /** 15064 * The name of the fog. 15065 * 15066 * @type {string} 15067 */ 15068 this.name = ''; 15069 15070 /** 15071 * The fog's color. 15072 * 15073 * @type {Color} 15074 */ 15075 this.color = new Color( color ); 15076 15077 /** 15078 * Defines how fast the fog will grow dense. 15079 * 15080 * @type {number} 15081 * @default 0.00025 15082 */ 15083 this.density = density; 15084 15085 } 15086 15087 /** 15088 * Returns a new fog with copied values from this instance. 15089 * 15090 * @return {FogExp2} A clone of this instance. 15091 */ 15092 clone() { 15093 15094 return new FogExp2( this.color, this.density ); 15095 15096 } 15097 15098 /** 15099 * Serializes the fog into JSON. 15100 * 15101 * @param {?(Object|string)} meta - An optional value holding meta information about the serialization. 15102 * @return {Object} A JSON object representing the serialized fog 15103 */ 15104 toJSON( /* meta */ ) { 15105 15106 return { 15107 type: 'FogExp2', 15108 name: this.name, 15109 color: this.color.getHex(), 15110 density: this.density 15111 }; 15112 15113 } 15114 15115} 15116 15117/** 15118 * This class can be used to define a linear fog that grows linearly denser 15119 * with the distance. 15120 * 15121 * ```js 15122 * const scene = new THREE.Scene(); 15123 * scene.fog = new THREE.Fog( 0xcccccc, 10, 15 ); 15124 * ``` 15125 */ 15126class Fog { 15127 15128 /** 15129 * Constructs a new fog. 15130 * 15131 * @param {number|Color} color - The fog's color. 15132 * @param {number} [near=1] - The minimum distance to start applying fog. 15133 * @param {number} [far=1000] - The maximum distance at which fog stops being calculated and applied. 15134 */ 15135 constructor( color, near = 1, far = 1000 ) { 15136 15137 /** 15138 * This flag can be used for type testing. 15139 * 15140 * @type {boolean} 15141 * @readonly 15142 * @default true 15143 */ 15144 this.isFog = true; 15145 15146 /** 15147 * The name of the fog. 15148 * 15149 * @type {string} 15150 */ 15151 this.name = ''; 15152 15153 /** 15154 * The fog's color. 15155 * 15156 * @type {Color} 15157 */ 15158 this.color = new Color( color ); 15159 15160 /** 15161 * The minimum distance to start applying fog. Objects that are less than 15162 * `near` units from the active camera won't be affected by fog. 15163 * 15164 * @type {number} 15165 * @default 1 15166 */ 15167 this.near = near; 15168 15169 /** 15170 * The maximum distance at which fog stops being calculated and applied. 15171 * Objects that are more than `far` units away from the active camera won't 15172 * be affected by fog. 15173 * 15174 * @type {number} 15175 * @default 1000 15176 */ 15177 this.far = far; 15178 15179 } 15180 15181 /** 15182 * Returns a new fog with copied values from this instance. 15183 * 15184 * @return {Fog} A clone of this instance. 15185 */ 15186 clone() { 15187 15188 return new Fog( this.color, this.near, this.far ); 15189 15190 } 15191 15192 /** 15193 * Serializes the fog into JSON. 15194 * 15195 * @param {?(Object|string)} meta - An optional value holding meta information about the serialization. 15196 * @return {Object} A JSON object representing the serialized fog 15197 */ 15198 toJSON( /* meta */ ) { 15199 15200 return { 15201 type: 'Fog', 15202 name: this.name, 15203 color: this.color.getHex(), 15204 near: this.near, 15205 far: this.far 15206 }; 15207 15208 } 15209 15210} 15211 15212/** 15213 * Scenes allow you to set up what is to be rendered and where by three.js. 15214 * This is where you place 3D objects like meshes, lines or lights. 15215 * 15216 * @augments Object3D 15217 */ 15218class Scene extends Object3D { 15219 15220 /** 15221 * Constructs a new scene. 15222 */ 15223 constructor() { 15224 15225 super(); 15226 15227 /** 15228 * This flag can be used for type testing. 15229 * 15230 * @type {boolean} 15231 * @readonly 15232 * @default true 15233 */ 15234 this.isScene = true; 15235 15236 this.type = 'Scene'; 15237 15238 /** 15239 * Defines the background of the scene. Valid inputs are: 15240 * 15241 * - A color for defining a uniform colored background. 15242 * - A texture for defining a (flat) textured background. 15243 * - Cube textures or equirectangular textures for defining a skybox. 15244 * 15245 * @type {?(Color|Texture)} 15246 * @default null 15247 */ 15248 this.background = null; 15249 15250 /** 15251 * Sets the environment map for all physical materials in the scene. However, 15252 * it's not possible to overwrite an existing texture assigned to the `envMap` 15253 * material property. 15254 * 15255 * @type {?Texture} 15256 * @default null 15257 */ 15258 this.environment = null; 15259 15260 /** 15261 * A fog instance defining the type of fog that affects everything 15262 * rendered in the scene. 15263 * 15264 * @type {?(Fog|FogExp2)} 15265 * @default null 15266 */ 15267 this.fog = null; 15268 15269 /** 15270 * Sets the blurriness of the background. Only influences environment maps 15271 * assigned to {@link Scene#background}. Valid input is a float between `0` 15272 * and `1`. 15273 * 15274 * @type {number} 15275 * @default 0 15276 */ 15277 this.backgroundBlurriness = 0; 15278 15279 /** 15280 * Attenuates the color of the background. Only applies to background textures. 15281 * 15282 * @type {number} 15283 * @default 1 15284 */ 15285 this.backgroundIntensity = 1; 15286 15287 /** 15288 * The rotation of the background in radians. Only influences environment maps 15289 * assigned to {@link Scene#background}. 15290 * 15291 * @type {Euler} 15292 * @default (0,0,0) 15293 */ 15294 this.backgroundRotation = new Euler(); 15295 15296 /** 15297 * Attenuates the color of the environment. Only influences environment maps 15298 * assigned to {@link Scene#environment}. 15299 * 15300 * @type {number} 15301 * @default 1 15302 */ 15303 this.environmentIntensity = 1; 15304 15305 /** 15306 * The rotation of the environment map in radians. Only influences physical materials 15307 * in the scene when {@link Scene#environment} is used. 15308 * 15309 * @type {Euler} 15310 * @default (0,0,0) 15311 */ 15312 this.environmentRotation = new Euler(); 15313 15314 /** 15315 * Forces everything in the scene to be rendered with the defined material. It is possible 15316 * to exclude materials from override by setting {@link Material#allowOverride} to `false`. 15317 * 15318 * @type {?Material} 15319 * @default null 15320 */ 15321 this.overrideMaterial = null; 15322 15323 if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { 15324 15325 __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); 15326 15327 } 15328 15329 } 15330 15331 copy( source, recursive ) { 15332 15333 super.copy( source, recursive ); 15334 15335 if ( source.background !== null ) this.background = source.background.clone(); 15336 if ( source.environment !== null ) this.environment = source.environment.clone(); 15337 if ( source.fog !== null ) this.fog = source.fog.clone(); 15338 15339 this.backgroundBlurriness = source.backgroundBlurriness; 15340 this.backgroundIntensity = source.backgroundIntensity; 15341 this.backgroundRotation.copy( source.backgroundRotation ); 15342 15343 this.environmentIntensity = source.environmentIntensity; 15344 this.environmentRotation.copy( source.environmentRotation ); 15345 15346 if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); 15347 15348 this.matrixAutoUpdate = source.matrixAutoUpdate; 15349 15350 return this; 15351 15352 } 15353 15354 toJSON( meta ) { 15355 15356 const data = super.toJSON( meta ); 15357 15358 if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); 15359 15360 data.object.backgroundBlurriness = this.backgroundBlurriness; 15361 data.object.backgroundIntensity = this.backgroundIntensity; 15362 data.object.backgroundRotation = this.backgroundRotation.toArray(); 15363 15364 data.object.environmentIntensity = this.environmentIntensity; 15365 data.object.environmentRotation = this.environmentRotation.toArray(); 15366 15367 return data; 15368 15369 } 15370 15371} 15372 15373const _v0$2 = /*@__PURE__*/ new Vector3(); 15374const _v1$5 = /*@__PURE__*/ new Vector3(); 15375const _v2$4 = /*@__PURE__*/ new Vector3(); 15376const _v3$2 = /*@__PURE__*/ new Vector3(); 15377 15378const _vab = /*@__PURE__*/ new Vector3(); 15379const _vac = /*@__PURE__*/ new Vector3(); 15380const _vbc = /*@__PURE__*/ new Vector3(); 15381const _vap = /*@__PURE__*/ new Vector3(); 15382const _vbp = /*@__PURE__*/ new Vector3(); 15383const _vcp = /*@__PURE__*/ new Vector3(); 15384 15385const _v40 = /*@__PURE__*/ new Vector4(); 15386const _v41 = /*@__PURE__*/ new Vector4(); 15387const _v42 = /*@__PURE__*/ new Vector4(); 15388 15389/** 15390 * A geometric triangle as defined by three vectors representing its three corners. 15391 */ 15392class Triangle { 15393 15394 /** 15395 * Constructs a new triangle. 15396 * 15397 * @param {Vector3} [a=(0,0,0)] - The first corner of the triangle. 15398 * @param {Vector3} [b=(0,0,0)] - The second corner of the triangle. 15399 * @param {Vector3} [c=(0,0,0)] - The third corner of the triangle. 15400 */ 15401 constructor( a = new Vector3(), b = new Vector3(), c = new Vector3() ) { 15402 15403 /** 15404 * The first corner of the triangle. 15405 * 15406 * @type {Vector3} 15407 */ 15408 this.a = a; 15409 15410 /** 15411 * The second corner of the triangle. 15412 * 15413 * @type {Vector3} 15414 */ 15415 this.b = b; 15416 15417 /** 15418 * The third corner of the triangle. 15419 * 15420 * @type {Vector3} 15421 */ 15422 this.c = c; 15423 15424 } 15425 15426 /** 15427 * Computes the normal vector of a triangle. 15428 * 15429 * @param {Vector3} a - The first corner of the triangle. 15430 * @param {Vector3} b - The second corner of the triangle. 15431 * @param {Vector3} c - The third corner of the triangle. 15432 * @param {Vector3} target - The target vector that is used to store the method's result. 15433 * @return {Vector3} The triangle's normal. 15434 */ 15435 static getNormal( a, b, c, target ) { 15436 15437 target.subVectors( c, b ); 15438 _v0$2.subVectors( a, b ); 15439 target.cross( _v0$2 ); 15440 15441 const targetLengthSq = target.lengthSq(); 15442 if ( targetLengthSq > 0 ) { 15443 15444 return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) ); 15445 15446 } 15447 15448 return target.set( 0, 0, 0 ); 15449 15450 } 15451 15452 /** 15453 * Computes a barycentric coordinates from the given vector. 15454 * Returns `null` if the triangle is degenerate. 15455 * 15456 * @param {Vector3} point - A point in 3D space. 15457 * @param {Vector3} a - The first corner of the triangle. 15458 * @param {Vector3} b - The second corner of the triangle. 15459 * @param {Vector3} c - The third corner of the triangle. 15460 * @param {Vector3} target - The target vector that is used to store the method's result. 15461 * @return {?Vector3} The barycentric coordinates for the given point 15462 */ 15463 static getBarycoord( point, a, b, c, target ) { 15464 15465 _v0$2.subVectors( c, a ); 15466 _v1$5.subVectors( b, a ); 15467 _v2$4.subVectors( point, a ); 15468 15469 const dot00 = _v0$2.dot( _v0$2 ); 15470 const dot01 = _v0$2.dot( _v1$5 ); 15471 const dot02 = _v0$2.dot( _v2$4 ); 15472 const dot11 = _v1$5.dot( _v1$5 ); 15473 const dot12 = _v1$5.dot( _v2$4 ); 15474 15475 const denom = ( dot00 * dot11 - dot01 * dot01 ); 15476 15477 // collinear or singular triangle 15478 if ( denom === 0 ) { 15479 15480 target.set( 0, 0, 0 ); 15481 return null; 15482 15483 } 15484 15485 const invDenom = 1 / denom; 15486 const u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; 15487 const v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; 15488 15489 // barycentric coordinates must always sum to 1 15490 return target.set( 1 - u - v, v, u ); 15491 15492 } 15493 15494 /** 15495 * Returns `true` if the given point, when projected onto the plane of the 15496 * triangle, lies within the triangle. 15497 * 15498 * @param {Vector3} point - The point in 3D space to test. 15499 * @param {Vector3} a - The first corner of the triangle. 15500 * @param {Vector3} b - The second corner of the triangle. 15501 * @param {Vector3} c - The third corner of the triangle. 15502 * @return {boolean} Whether the given point, when projected onto the plane of the 15503 * triangle, lies within the triangle or not. 15504 */ 15505 static containsPoint( point, a, b, c ) { 15506 15507 // if the triangle is degenerate then we can't contain a point 15508 if ( this.getBarycoord( point, a, b, c, _v3$2 ) === null ) { 15509 15510 return false; 15511 15512 } 15513 15514 return ( _v3$2.x >= 0 ) && ( _v3$2.y >= 0 ) && ( ( _v3$2.x + _v3$2.y ) <= 1 ); 15515 15516 } 15517 15518 /** 15519 * Computes the value barycentrically interpolated for the given point on the 15520 * triangle. Returns `null` if the triangle is degenerate. 15521 * 15522 * @param {Vector3} point - Position of interpolated point. 15523 * @param {Vector3} p1 - The first corner of the triangle. 15524 * @param {Vector3} p2 - The second corner of the triangle. 15525 * @param {Vector3} p3 - The third corner of the triangle. 15526 * @param {Vector3} v1 - Value to interpolate of first vertex. 15527 * @param {Vector3} v2 - Value to interpolate of second vertex. 15528 * @param {Vector3} v3 - Value to interpolate of third vertex. 15529 * @param {Vector3} target - The target vector that is used to store the method's result. 15530 * @return {?Vector3} The interpolated value. 15531 */ 15532 static getInterpolation( point, p1, p2, p3, v1, v2, v3, target ) { 15533 15534 if ( this.getBarycoord( point, p1, p2, p3, _v3$2 ) === null ) { 15535 15536 target.x = 0; 15537 target.y = 0; 15538 if ( 'z' in target ) target.z = 0; 15539 if ( 'w' in target ) target.w = 0; 15540 return null; 15541 15542 } 15543 15544 target.setScalar( 0 ); 15545 target.addScaledVector( v1, _v3$2.x ); 15546 target.addScaledVector( v2, _v3$2.y ); 15547 target.addScaledVector( v3, _v3$2.z ); 15548 15549 return target; 15550 15551 } 15552 15553 /** 15554 * Computes the value barycentrically interpolated for the given attribute and indices. 15555 * 15556 * @param {BufferAttribute} attr - The attribute to interpolate. 15557 * @param {number} i1 - Index of first vertex. 15558 * @param {number} i2 - Index of second vertex. 15559 * @param {number} i3 - Index of third vertex. 15560 * @param {Vector3} barycoord - The barycoordinate value to use to interpolate. 15561 * @param {Vector3} target - The target vector that is used to store the method's result. 15562 * @return {Vector3} The interpolated attribute value. 15563 */ 15564 static getInterpolatedAttribute( attr, i1, i2, i3, barycoord, target ) { 15565 15566 _v40.setScalar( 0 ); 15567 _v41.setScalar( 0 ); 15568 _v42.setScalar( 0 ); 15569 15570 _v40.fromBufferAttribute( attr, i1 ); 15571 _v41.fromBufferAttribute( attr, i2 ); 15572 _v42.fromBufferAttribute( attr, i3 ); 15573 15574 target.setScalar( 0 ); 15575 target.addScaledVector( _v40, barycoord.x ); 15576 target.addScaledVector( _v41, barycoord.y ); 15577 target.addScaledVector( _v42, barycoord.z ); 15578 15579 return target; 15580 15581 } 15582 15583 /** 15584 * Returns `true` if the triangle is oriented towards the given direction. 15585 * 15586 * @param {Vector3} a - The first corner of the triangle. 15587 * @param {Vector3} b - The second corner of the triangle. 15588 * @param {Vector3} c - The third corner of the triangle. 15589 * @param {Vector3} direction - The (normalized) direction vector. 15590 * @return {boolean} Whether the triangle is oriented towards the given direction or not. 15591 */ 15592 static isFrontFacing( a, b, c, direction ) { 15593 15594 _v0$2.subVectors( c, b ); 15595 _v1$5.subVectors( a, b ); 15596 15597 // strictly front facing 15598 return _v0$2.cross( _v1$5 ).dot( direction ) < 0; 15599 15600 } 15601 15602 /** 15603 * Sets the triangle's vertices by copying the given values. 15604 * 15605 * @param {Vector3} a - The first corner of the triangle. 15606 * @param {Vector3} b - The second corner of the triangle. 15607 * @param {Vector3} c - The third corner of the triangle. 15608 * @return {Triangle} A reference to this triangle. 15609 */ 15610 set( a, b, c ) { 15611 15612 this.a.copy( a ); 15613 this.b.copy( b ); 15614 this.c.copy( c ); 15615 15616 return this; 15617 15618 } 15619 15620 /** 15621 * Sets the triangle's vertices by copying the given array values. 15622 * 15623 * @param {Array<Vector3>} points - An array with 3D points. 15624 * @param {number} i0 - The array index representing the first corner of the triangle. 15625 * @param {number} i1 - The array index representing the second corner of the triangle. 15626 * @param {number} i2 - The array index representing the third corner of the triangle. 15627 * @return {Triangle} A reference to this triangle. 15628 */ 15629 setFromPointsAndIndices( points, i0, i1, i2 ) { 15630 15631 this.a.copy( points[ i0 ] ); 15632 this.b.copy( points[ i1 ] ); 15633 this.c.copy( points[ i2 ] ); 15634 15635 return this; 15636 15637 } 15638 15639 /** 15640 * Sets the triangle's vertices by copying the given attribute values. 15641 * 15642 * @param {BufferAttribute} attribute - A buffer attribute with 3D points data. 15643 * @param {number} i0 - The attribute index representing the first corner of the triangle. 15644 * @param {number} i1 - The attribute index representing the second corner of the triangle. 15645 * @param {number} i2 - The attribute index representing the third corner of the triangle. 15646 * @return {Triangle} A reference to this triangle. 15647 */ 15648 setFromAttributeAndIndices( attribute, i0, i1, i2 ) { 15649 15650 this.a.fromBufferAttribute( attribute, i0 ); 15651 this.b.fromBufferAttribute( attribute, i1 ); 15652 this.c.fromBufferAttribute( attribute, i2 ); 15653 15654 return this; 15655 15656 } 15657 15658 /** 15659 * Returns a new triangle with copied values from this instance. 15660 * 15661 * @return {Triangle} A clone of this instance. 15662 */ 15663 clone() { 15664 15665 return new this.constructor().copy( this ); 15666 15667 } 15668 15669 /** 15670 * Copies the values of the given triangle to this instance. 15671 * 15672 * @param {Triangle} triangle - The triangle to copy. 15673 * @return {Triangle} A reference to this triangle. 15674 */ 15675 copy( triangle ) { 15676 15677 this.a.copy( triangle.a ); 15678 this.b.copy( triangle.b ); 15679 this.c.copy( triangle.c ); 15680 15681 return this; 15682 15683 } 15684 15685 /** 15686 * Computes the area of the triangle. 15687 * 15688 * @return {number} The triangle's area. 15689 */ 15690 getArea() { 15691 15692 _v0$2.subVectors( this.c, this.b ); 15693 _v1$5.subVectors( this.a, this.b ); 15694 15695 return _v0$2.cross( _v1$5 ).length() * 0.5; 15696 15697 } 15698 15699 /** 15700 * Computes the midpoint of the triangle. 15701 * 15702 * @param {Vector3} target - The target vector that is used to store the method's result. 15703 * @return {Vector3} The triangle's midpoint. 15704 */ 15705 getMidpoint( target ) { 15706 15707 return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); 15708 15709 } 15710 15711 /** 15712 * Computes the normal of the triangle. 15713 * 15714 * @param {Vector3} target - The target vector that is used to store the method's result. 15715 * @return {Vector3} The triangle's normal. 15716 */ 15717 getNormal( target ) { 15718 15719 return Triangle.getNormal( this.a, this.b, this.c, target ); 15720 15721 } 15722 15723 /** 15724 * Computes a plane the triangle lies within. 15725 * 15726 * @param {Plane} target - The target vector that is used to store the method's result. 15727 * @return {Plane} The plane the triangle lies within. 15728 */ 15729 getPlane( target ) { 15730 15731 return target.setFromCoplanarPoints( this.a, this.b, this.c ); 15732 15733 } 15734 15735 /** 15736 * Computes a barycentric coordinates from the given vector. 15737 * Returns `null` if the triangle is degenerate. 15738 * 15739 * @param {Vector3} point - A point in 3D space. 15740 * @param {Vector3} target - The target vector that is used to store the method's result. 15741 * @return {?Vector3} The barycentric coordinates for the given point 15742 */ 15743 getBarycoord( point, target ) { 15744 15745 return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); 15746 15747 } 15748 15749 /** 15750 * Computes the value barycentrically interpolated for the given point on the 15751 * triangle. Returns `null` if the triangle is degenerate. 15752 * 15753 * @param {Vector3} point - Position of interpolated point. 15754 * @param {Vector3} v1 - Value to interpolate of first vertex. 15755 * @param {Vector3} v2 - Value to interpolate of second vertex. 15756 * @param {Vector3} v3 - Value to interpolate of third vertex. 15757 * @param {Vector3} target - The target vector that is used to store the method's result. 15758 * @return {?Vector3} The interpolated value. 15759 */ 15760 getInterpolation( point, v1, v2, v3, target ) { 15761 15762 return Triangle.getInterpolation( point, this.a, this.b, this.c, v1, v2, v3, target ); 15763 15764 } 15765 15766 /** 15767 * Returns `true` if the given point, when projected onto the plane of the 15768 * triangle, lies within the triangle. 15769 * 15770 * @param {Vector3} point - The point in 3D space to test. 15771 * @return {boolean} Whether the given point, when projected onto the plane of the 15772 * triangle, lies within the triangle or not. 15773 */ 15774 containsPoint( point ) { 15775 15776 return Triangle.containsPoint( point, this.a, this.b, this.c ); 15777 15778 } 15779 15780 /** 15781 * Returns `true` if the triangle is oriented towards the given direction. 15782 * 15783 * @param {Vector3} direction - The (normalized) direction vector. 15784 * @return {boolean} Whether the triangle is oriented towards the given direction or not. 15785 */ 15786 isFrontFacing( direction ) { 15787 15788 return Triangle.isFrontFacing( this.a, this.b, this.c, direction ); 15789 15790 } 15791 15792 /** 15793 * Returns `true` if this triangle intersects with the given box. 15794 * 15795 * @param {Box3} box - The box to intersect. 15796 * @return {boolean} Whether this triangle intersects with the given box or not. 15797 */ 15798 intersectsBox( box ) { 15799 15800 return box.intersectsTriangle( this ); 15801 15802 } 15803 15804 /** 15805 * Returns the closest point on the triangle to the given point. 15806 * 15807 * @param {Vector3} p - The point to compute the closest point for. 15808 * @param {Vector3} target - The target vector that is used to store the method's result. 15809 * @return {Vector3} The closest point on the triangle. 15810 */ 15811 closestPointToPoint( p, target ) { 15812 15813 const a = this.a, b = this.b, c = this.c; 15814 let v, w; 15815 15816 // algorithm thanks to Real-Time Collision Detection by Christer Ericson, 15817 // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., 15818 // under the accompanying license; see chapter 5.1.5 for detailed explanation. 15819 // basically, we're distinguishing which of the voronoi regions of the triangle 15820 // the point lies in with the minimum amount of redundant computation. 15821 15822 _vab.subVectors( b, a ); 15823 _vac.subVectors( c, a ); 15824 _vap.subVectors( p, a ); 15825 const d1 = _vab.dot( _vap ); 15826 const d2 = _vac.dot( _vap ); 15827 if ( d1 <= 0 && d2 <= 0 ) { 15828 15829 // vertex region of A; barycentric coords (1, 0, 0) 15830 return target.copy( a ); 15831 15832 } 15833 15834 _vbp.subVectors( p, b ); 15835 const d3 = _vab.dot( _vbp ); 15836 const d4 = _vac.dot( _vbp ); 15837 if ( d3 >= 0 && d4 <= d3 ) { 15838 15839 // vertex region of B; barycentric coords (0, 1, 0) 15840 return target.copy( b ); 15841 15842 } 15843 15844 const vc = d1 * d4 - d3 * d2; 15845 if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) { 15846 15847 v = d1 / ( d1 - d3 ); 15848 // edge region of AB; barycentric coords (1-v, v, 0) 15849 return target.copy( a ).addScaledVector( _vab, v ); 15850 15851 } 15852 15853 _vcp.subVectors( p, c ); 15854 const d5 = _vab.dot( _vcp ); 15855 const d6 = _vac.dot( _vcp ); 15856 if ( d6 >= 0 && d5 <= d6 ) { 15857 15858 // vertex region of C; barycentric coords (0, 0, 1) 15859 return target.copy( c ); 15860 15861 } 15862 15863 const vb = d5 * d2 - d1 * d6; 15864 if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) { 15865 15866 w = d2 / ( d2 - d6 ); 15867 // edge region of AC; barycentric coords (1-w, 0, w) 15868 return target.copy( a ).addScaledVector( _vac, w ); 15869 15870 } 15871 15872 const va = d3 * d6 - d5 * d4; 15873 if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) { 15874 15875 _vbc.subVectors( c, b ); 15876 w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) ); 15877 // edge region of BC; barycentric coords (0, 1-w, w) 15878 return target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC 15879 15880 } 15881 15882 // face region 15883 const denom = 1 / ( va + vb + vc ); 15884 // u = va * denom 15885 v = vb * denom; 15886 w = vc * denom; 15887 15888 return target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w ); 15889 15890 } 15891 15892 /** 15893 * Returns `true` if this triangle is equal with the given one. 15894 * 15895 * @param {Triangle} triangle - The triangle to test for equality. 15896 * @return {boolean} Whether this triangle is equal with the given one. 15897 */ 15898 equals( triangle ) { 15899 15900 return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); 15901 15902 } 15903 15904} 15905 15906/** 15907 * Represents an axis-aligned bounding box (AABB) in 3D space. 15908 */ 15909class Box3 { 15910 15911 /** 15912 * Constructs a new bounding box. 15913 * 15914 * @param {Vector3} [min=(Infinity,Infinity,Infinity)] - A vector representing the lower boundary of the box. 15915 * @param {Vector3} [max=(-Infinity,-Infinity,-Infinity)] - A vector representing the upper boundary of the box. 15916 */ 15917 constructor( min = new Vector3( + Infinity, + Infinity, + Infinity ), max = new Vector3( - Infinity, - Infinity, - Infinity ) ) { 15918 15919 /** 15920 * This flag can be used for type testing. 15921 * 15922 * @type {boolean} 15923 * @readonly 15924 * @default true 15925 */ 15926 this.isBox3 = true; 15927 15928 /** 15929 * The lower boundary of the box. 15930 * 15931 * @type {Vector3} 15932 */ 15933 this.min = min; 15934 15935 /** 15936 * The upper boundary of the box. 15937 * 15938 * @type {Vector3} 15939 */ 15940 this.max = max; 15941 15942 } 15943 15944 /** 15945 * Sets the lower and upper boundaries of this box. 15946 * Please note that this method only copies the values from the given objects. 15947 * 15948 * @param {Vector3} min - The lower boundary of the box. 15949 * @param {Vector3} max - The upper boundary of the box. 15950 * @return {Box3} A reference to this bounding box. 15951 */ 15952 set( min, max ) { 15953 15954 this.min.copy( min ); 15955 this.max.copy( max ); 15956 15957 return this; 15958 15959 } 15960 15961 /** 15962 * Sets the upper and lower bounds of this box so it encloses the position data 15963 * in the given array. 15964 * 15965 * @param {Array<number>} array - An array holding 3D position data. 15966 * @return {Box3} A reference to this bounding box. 15967 */ 15968 setFromArray( array ) { 15969 15970 this.makeEmpty(); 15971 15972 for ( let i = 0, il = array.length; i < il; i += 3 ) { 15973 15974 this.expandByPoint( _vector$b.fromArray( array, i ) ); 15975 15976 } 15977 15978 return this; 15979 15980 } 15981 15982 /** 15983 * Sets the upper and lower bounds of this box so it encloses the position data 15984 * in the given buffer attribute. 15985 * 15986 * @param {BufferAttribute} attribute - A buffer attribute holding 3D position data. 15987 * @return {Box3} A reference to this bounding box. 15988 */ 15989 setFromBufferAttribute( attribute ) { 15990 15991 this.makeEmpty(); 15992 15993 for ( let i = 0, il = attribute.count; i < il; i ++ ) { 15994 15995 this.expandByPoint( _vector$b.fromBufferAttribute( attribute, i ) ); 15996 15997 } 15998 15999 return this; 16000 16001 } 16002 16003 /** 16004 * Sets the upper and lower bounds of this box so it encloses the position data 16005 * in the given array. 16006 * 16007 * @param {Array<Vector3>} points - An array holding 3D position data as instances of {@link Vector3}. 16008 * @return {Box3} A reference to this bounding box. 16009 */ 16010 setFromPoints( points ) { 16011 16012 this.makeEmpty(); 16013 16014 for ( let i = 0, il = points.length; i < il; i ++ ) { 16015 16016 this.expandByPoint( points[ i ] ); 16017 16018 } 16019 16020 return this; 16021 16022 } 16023 16024 /** 16025 * Centers this box on the given center vector and sets this box's width, height and 16026 * depth to the given size values. 16027 * 16028 * @param {Vector3} center - The center of the box. 16029 * @param {Vector3} size - The x, y and z dimensions of the box. 16030 * @return {Box3} A reference to this bounding box. 16031 */ 16032 setFromCenterAndSize( center, size ) { 16033 16034 const halfSize = _vector$b.copy( size ).multiplyScalar( 0.5 ); 16035 16036 this.min.copy( center ).sub( halfSize ); 16037 this.max.copy( center ).add( halfSize ); 16038 16039 return this; 16040 16041 } 16042 16043 /** 16044 * Computes the world-axis-aligned bounding box for the given 3D object 16045 * (including its children), accounting for the object's, and children's, 16046 * world transforms. The function may result in a larger box than strictly necessary. 16047 * 16048 * Note: To compute the correct bounding box, make sure the given 3D object 16049 * has an up-to-date world matrix that reflects the current transformation of its 16050 * ancestor nodes. Call `object.updateWorldMatrix( true, false )` beforehand if 16051 * you're unsure. 16052 * 16053 * @param {Object3D} object - The 3D object to compute the bounding box for. 16054 * @param {boolean} [precise=false] - If set to `true`, the method computes the smallest 16055 * world-axis-aligned bounding box at the expense of more computation. 16056 * @return {Box3} A reference to this bounding box. 16057 */ 16058 setFromObject( object, precise = false ) { 16059 16060 this.makeEmpty(); 16061 16062 return this.expandByObject( object, precise ); 16063 16064 } 16065 16066 /** 16067 * Returns a new box with copied values from this instance. 16068 * 16069 * @return {Box3} A clone of this instance. 16070 */ 16071 clone() { 16072 16073 return new this.constructor().copy( this ); 16074 16075 } 16076 16077 /** 16078 * Copies the values of the given box to this instance. 16079 * 16080 * @param {Box3} box - The box to copy. 16081 * @return {Box3} A reference to this bounding box. 16082 */ 16083 copy( box ) { 16084 16085 this.min.copy( box.min ); 16086 this.max.copy( box.max ); 16087 16088 return this; 16089 16090 } 16091 16092 /** 16093 * Makes this box empty which means in encloses a zero space in 3D. 16094 * 16095 * @return {Box3} A reference to this bounding box. 16096 */ 16097 makeEmpty() { 16098 16099 this.min.x = this.min.y = this.min.z = + Infinity; 16100 this.max.x = this.max.y = this.max.z = - Infinity; 16101 16102 return this; 16103 16104 } 16105 16106 /** 16107 * Returns true if this box includes zero points within its bounds. 16108 * Note that a box with equal lower and upper bounds still includes one 16109 * point, the one both bounds share. 16110 * 16111 * @return {boolean} Whether this box is empty or not. 16112 */ 16113 isEmpty() { 16114 16115 // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes 16116 16117 return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); 16118 16119 } 16120 16121 /** 16122 * Returns the center point of this box. 16123 * 16124 * @param {Vector3} target - The target vector that is used to store the method's result. 16125 * @return {Vector3} The center point. 16126 */ 16127 getCenter( target ) { 16128 16129 return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); 16130 16131 } 16132 16133 /** 16134 * Returns the dimensions of this box. 16135 * 16136 * @param {Vector3} target - The target vector that is used to store the method's result. 16137 * @return {Vector3} The size. 16138 */ 16139 getSize( target ) { 16140 16141 return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); 16142 16143 } 16144 16145 /** 16146 * Expands the boundaries of this box to include the given point. 16147 * 16148 * @param {Vector3} point - The point that should be included by the bounding box. 16149 * @return {Box3} A reference to this bounding box. 16150 */ 16151 expandByPoint( point ) { 16152 16153 this.min.min( point ); 16154 this.max.max( point ); 16155 16156 return this; 16157 16158 } 16159 16160 /** 16161 * Expands this box equilaterally by the given vector. The width of this 16162 * box will be expanded by the x component of the vector in both 16163 * directions. The height of this box will be expanded by the y component of 16164 * the vector in both directions. The depth of this box will be 16165 * expanded by the z component of the vector in both directions. 16166 * 16167 * @param {Vector3} vector - The vector that should expand the bounding box. 16168 * @return {Box3} A reference to this bounding box. 16169 */ 16170 expandByVector( vector ) { 16171 16172 this.min.sub( vector ); 16173 this.max.add( vector ); 16174 16175 return this; 16176 16177 } 16178 16179 /** 16180 * Expands each dimension of the box by the given scalar. If negative, the 16181 * dimensions of the box will be contracted. 16182 * 16183 * @param {number} scalar - The scalar value that should expand the bounding box. 16184 * @return {Box3} A reference to this bounding box. 16185 */ 16186 expandByScalar( scalar ) { 16187 16188 this.min.addScalar( - scalar ); 16189 this.max.addScalar( scalar ); 16190 16191 return this; 16192 16193 } 16194 16195 /** 16196 * Expands the boundaries of this box to include the given 3D object and 16197 * its children, accounting for the object's, and children's, world 16198 * transforms. The function may result in a larger box than strictly 16199 * necessary (unless the precise parameter is set to true). 16200 * 16201 * @param {Object3D} object - The 3D object that should expand the bounding box. 16202 * @param {boolean} precise - If set to `true`, the method expands the bounding box 16203 * as little as necessary at the expense of more computation. 16204 * @return {Box3} A reference to this bounding box. 16205 */ 16206 expandByObject( object, precise = false ) { 16207 16208 // Computes the world-axis-aligned bounding box of an object (including its children), 16209 // accounting for both the object's, and children's, world transforms 16210 16211 object.updateWorldMatrix( false, false ); 16212 16213 const geometry = object.geometry; 16214 16215 if ( geometry !== undefined ) { 16216 16217 const positionAttribute = geometry.getAttribute( 'position' ); 16218 16219 // precise AABB computation based on vertex data requires at least a position attribute. 16220 // instancing isn't supported so far and uses the normal (conservative) code path. 16221 16222 if ( precise === true && positionAttribute !== undefined && object.isInstancedMesh !== true ) { 16223 16224 for ( let i = 0, l = positionAttribute.count; i < l; i ++ ) { 16225 16226 if ( object.isMesh === true ) { 16227 16228 object.getVertexPosition( i, _vector$b ); 16229 16230 } else { 16231 16232 _vector$b.fromBufferAttribute( positionAttribute, i ); 16233 16234 } 16235 16236 _vector$b.applyMatrix4( object.matrixWorld ); 16237 this.expandByPoint( _vector$b ); 16238 16239 } 16240 16241 } else { 16242 16243 if ( object.boundingBox !== undefined ) { 16244 16245 // object-level bounding box 16246 16247 if ( object.boundingBox === null ) { 16248 16249 object.computeBoundingBox(); 16250 16251 } 16252 16253 _box$4.copy( object.boundingBox ); 16254 16255 16256 } else { 16257 16258 // geometry-level bounding box 16259 16260 if ( geometry.boundingBox === null ) { 16261 16262 geometry.computeBoundingBox(); 16263 16264 } 16265 16266 _box$4.copy( geometry.boundingBox ); 16267 16268 } 16269 16270 _box$4.applyMatrix4( object.matrixWorld ); 16271 16272 this.union( _box$4 ); 16273 16274 } 16275 16276 } 16277 16278 const children = object.children; 16279 16280 for ( let i = 0, l = children.length; i < l; i ++ ) { 16281 16282 this.expandByObject( children[ i ], precise ); 16283 16284 } 16285 16286 return this; 16287 16288 } 16289 16290 /** 16291 * Returns `true` if the given point lies within or on the boundaries of this box. 16292 * 16293 * @param {Vector3} point - The point to test. 16294 * @return {boolean} Whether the bounding box contains the given point or not. 16295 */ 16296 containsPoint( point ) { 16297 16298 return point.x >= this.min.x && point.x <= this.max.x && 16299 point.y >= this.min.y && point.y <= this.max.y && 16300 point.z >= this.min.z && point.z <= this.max.z; 16301 16302 } 16303 16304 /** 16305 * Returns `true` if this bounding box includes the entirety of the given bounding box. 16306 * If this box and the given one are identical, this function also returns `true`. 16307 * 16308 * @param {Box3} box - The bounding box to test. 16309 * @return {boolean} Whether the bounding box contains the given bounding box or not. 16310 */ 16311 containsBox( box ) { 16312 16313 return this.min.x <= box.min.x && box.max.x <= this.max.x && 16314 this.min.y <= box.min.y && box.max.y <= this.max.y && 16315 this.min.z <= box.min.z && box.max.z <= this.max.z; 16316 16317 } 16318 16319 /** 16320 * Returns a point as a proportion of this box's width, height and depth. 16321 * 16322 * @param {Vector3} point - A point in 3D space. 16323 * @param {Vector3} target - The target vector that is used to store the method's result. 16324 * @return {Vector3} A point as a proportion of this box's width, height and depth. 16325 */ 16326 getParameter( point, target ) { 16327 16328 // This can potentially have a divide by zero if the box 16329 // has a size dimension of 0. 16330 16331 return target.set( 16332 ( point.x - this.min.x ) / ( this.max.x - this.min.x ), 16333 ( point.y - this.min.y ) / ( this.max.y - this.min.y ), 16334 ( point.z - this.min.z ) / ( this.max.z - this.min.z ) 16335 ); 16336 16337 } 16338 16339 /** 16340 * Returns `true` if the given bounding box intersects with this bounding box. 16341 * 16342 * @param {Box3} box - The bounding box to test. 16343 * @return {boolean} Whether the given bounding box intersects with this bounding box. 16344 */ 16345 intersectsBox( box ) { 16346 16347 // using 6 splitting planes to rule out intersections. 16348 return box.max.x >= this.min.x && box.min.x <= this.max.x && 16349 box.max.y >= this.min.y && box.min.y <= this.max.y && 16350 box.max.z >= this.min.z && box.min.z <= this.max.z; 16351 16352 } 16353 16354 /** 16355 * Returns `true` if the given bounding sphere intersects with this bounding box. 16356 * 16357 * @param {Sphere} sphere - The bounding sphere to test. 16358 * @return {boolean} Whether the given bounding sphere intersects with this bounding box. 16359 */ 16360 intersectsSphere( sphere ) { 16361 16362 // Find the point on the AABB closest to the sphere center. 16363 this.clampPoint( sphere.center, _vector$b ); 16364 16365 // If that point is inside the sphere, the AABB and sphere intersect. 16366 return _vector$b.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); 16367 16368 } 16369 16370 /** 16371 * Returns `true` if the given plane intersects with this bounding box. 16372 * 16373 * @param {Plane} plane - The plane to test. 16374 * @return {boolean} Whether the given plane intersects with this bounding box. 16375 */ 16376 intersectsPlane( plane ) { 16377 16378 // We compute the minimum and maximum dot product values. If those values 16379 // are on the same side (back or front) of the plane, then there is no intersection. 16380 16381 let min, max; 16382 16383 if ( plane.normal.x > 0 ) { 16384 16385 min = plane.normal.x * this.min.x; 16386 max = plane.normal.x * this.max.x; 16387 16388 } else { 16389 16390 min = plane.normal.x * this.max.x; 16391 max = plane.normal.x * this.min.x; 16392 16393 } 16394 16395 if ( plane.normal.y > 0 ) { 16396 16397 min += plane.normal.y * this.min.y; 16398 max += plane.normal.y * this.max.y; 16399 16400 } else { 16401 16402 min += plane.normal.y * this.max.y; 16403 max += plane.normal.y * this.min.y; 16404 16405 } 16406 16407 if ( plane.normal.z > 0 ) { 16408 16409 min += plane.normal.z * this.min.z; 16410 max += plane.normal.z * this.max.z; 16411 16412 } else { 16413 16414 min += plane.normal.z * this.max.z; 16415 max += plane.normal.z * this.min.z; 16416 16417 } 16418 16419 return ( min <= - plane.constant && max >= - plane.constant ); 16420 16421 } 16422 16423 /** 16424 * Returns `true` if the given triangle intersects with this bounding box. 16425 * 16426 * @param {Triangle} triangle - The triangle to test. 16427 * @return {boolean} Whether the given triangle intersects with this bounding box. 16428 */ 16429 intersectsTriangle( triangle ) { 16430 16431 if ( this.isEmpty() ) { 16432 16433 return false; 16434 16435 } 16436 16437 // compute box center and extents 16438 this.getCenter( _center ); 16439 _extents.subVectors( this.max, _center ); 16440 16441 // translate triangle to aabb origin 16442 _v0$1.subVectors( triangle.a, _center ); 16443 _v1$4.subVectors( triangle.b, _center ); 16444 _v2$3.subVectors( triangle.c, _center ); 16445 16446 // compute edge vectors for triangle 16447 _f0.subVectors( _v1$4, _v0$1 ); 16448 _f1.subVectors( _v2$3, _v1$4 ); 16449 _f2.subVectors( _v0$1, _v2$3 ); 16450 16451 // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb 16452 // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation 16453 // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) 16454 let axes = [ 16455 0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y, 16456 _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x, 16457 - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0 16458 ]; 16459 if ( ! satForAxes( axes, _v0$1, _v1$4, _v2$3, _extents ) ) { 16460 16461 return false; 16462 16463 } 16464 16465 // test 3 face normals from the aabb 16466 axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; 16467 if ( ! satForAxes( axes, _v0$1, _v1$4, _v2$3, _extents ) ) { 16468 16469 return false; 16470 16471 } 16472 16473 // finally testing the face normal of the triangle 16474 // use already existing triangle edge vectors here 16475 _triangleNormal.crossVectors( _f0, _f1 ); 16476 axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ]; 16477 16478 return satForAxes( axes, _v0$1, _v1$4, _v2$3, _extents ); 16479 16480 } 16481 16482 /** 16483 * Clamps the given point within the bounds of this box. 16484 * 16485 * @param {Vector3} point - The point to clamp. 16486 * @param {Vector3} target - The target vector that is used to store the method's result. 16487 * @return {Vector3} The clamped point. 16488 */ 16489 clampPoint( point, target ) { 16490 16491 return target.copy( point ).clamp( this.min, this.max ); 16492 16493 } 16494 16495 /** 16496 * Returns the euclidean distance from any edge of this box to the specified point. If 16497 * the given point lies inside of this box, the distance will be `0`. 16498 * 16499 * @param {Vector3} point - The point to compute the distance to. 16500 * @return {number} The euclidean distance. 16501 */ 16502 distanceToPoint( point ) { 16503 16504 return this.clampPoint( point, _vector$b ).distanceTo( point ); 16505 16506 } 16507 16508 /** 16509 * Returns a bounding sphere that encloses this bounding box. 16510 * 16511 * @param {Sphere} target - The target sphere that is used to store the method's result. 16512 * @return {Sphere} The bounding sphere that encloses this bounding box. 16513 */ 16514 getBoundingSphere( target ) { 16515 16516 if ( this.isEmpty() ) { 16517 16518 target.makeEmpty(); 16519 16520 } else { 16521 16522 this.getCenter( target.center ); 16523 16524 target.radius = this.getSize( _vector$b ).length() * 0.5; 16525 16526 } 16527 16528 return target; 16529 16530 } 16531 16532 /** 16533 * Computes the intersection of this bounding box and the given one, setting the upper 16534 * bound of this box to the lesser of the two boxes' upper bounds and the 16535 * lower bound of this box to the greater of the two boxes' lower bounds. If 16536 * there's no overlap, makes this box empty. 16537 * 16538 * @param {Box3} box - The bounding box to intersect with. 16539 * @return {Box3} A reference to this bounding box. 16540 */ 16541 intersect( box ) { 16542 16543 this.min.max( box.min ); 16544 this.max.min( box.max ); 16545 16546 // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. 16547 if ( this.isEmpty() ) this.makeEmpty(); 16548 16549 return this; 16550 16551 } 16552 16553 /** 16554 * Computes the union of this box and another and the given one, setting the upper 16555 * bound of this box to the greater of the two boxes' upper bounds and the 16556 * lower bound of this box to the lesser of the two boxes' lower bounds. 16557 * 16558 * @param {Box3} box - The bounding box that will be unioned with this instance. 16559 * @return {Box3} A reference to this bounding box. 16560 */ 16561 union( box ) { 16562 16563 this.min.min( box.min ); 16564 this.max.max( box.max ); 16565 16566 return this; 16567 16568 } 16569 16570 /** 16571 * Transforms this bounding box by the given 4x4 transformation matrix. 16572 * 16573 * @param {Matrix4} matrix - The transformation matrix. 16574 * @return {Box3} A reference to this bounding box. 16575 */ 16576 applyMatrix4( matrix ) { 16577 16578 // transform of empty box is an empty box. 16579 if ( this.isEmpty() ) return this; 16580 16581 // NOTE: I am using a binary pattern to specify all 2^3 combinations below 16582 _points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 16583 _points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 16584 _points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 16585 _points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 16586 _points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 16587 _points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 16588 _points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 16589 _points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 16590 16591 this.setFromPoints( _points ); 16592 16593 return this; 16594 16595 } 16596 16597 /** 16598 * Adds the given offset to both the upper and lower bounds of this bounding box, 16599 * effectively moving it in 3D space. 16600 * 16601 * @param {Vector3} offset - The offset that should be used to translate the bounding box. 16602 * @return {Box3} A reference to this bounding box. 16603 */ 16604 translate( offset ) { 16605 16606 this.min.add( offset ); 16607 this.max.add( offset ); 16608 16609 return this; 16610 16611 } 16612 16613 /** 16614 * Returns `true` if this bounding box is equal with the given one. 16615 * 16616 * @param {Box3} box - The box to test for equality. 16617 * @return {boolean} Whether this bounding box is equal with the given one. 16618 */ 16619 equals( box ) { 16620 16621 return box.min.equals( this.min ) && box.max.equals( this.max ); 16622 16623 } 16624 16625 /** 16626 * Returns a serialized structure of the bounding box. 16627 * 16628 * @return {Object} Serialized structure with fields representing the object state. 16629 */ 16630 toJSON() { 16631 16632 return { 16633 min: this.min.toArray(), 16634 max: this.max.toArray() 16635 }; 16636 16637 } 16638 16639 /** 16640 * Returns a serialized structure of the bounding box. 16641 * 16642 * @param {Object} json - The serialized json to set the box from. 16643 * @return {Box3} A reference to this bounding box. 16644 */ 16645 fromJSON( json ) { 16646 16647 this.min.fromArray( json.min ); 16648 this.max.fromArray( json.max ); 16649 return this; 16650 16651 } 16652 16653} 16654 16655const _points = [ 16656 /*@__PURE__*/ new Vector3(), 16657 /*@__PURE__*/ new Vector3(), 16658 /*@__PURE__*/ new Vector3(), 16659 /*@__PURE__*/ new Vector3(), 16660 /*@__PURE__*/ new Vector3(), 16661 /*@__PURE__*/ new Vector3(), 16662 /*@__PURE__*/ new Vector3(), 16663 /*@__PURE__*/ new Vector3() 16664]; 16665 16666const _vector$b = /*@__PURE__*/ new Vector3(); 16667 16668const _box$4 = /*@__PURE__*/ new Box3(); 16669 16670// triangle centered vertices 16671 16672const _v0$1 = /*@__PURE__*/ new Vector3(); 16673const _v1$4 = /*@__PURE__*/ new Vector3(); 16674const _v2$3 = /*@__PURE__*/ new Vector3(); 16675 16676// triangle edge vectors 16677 16678const _f0 = /*@__PURE__*/ new Vector3(); 16679const _f1 = /*@__PURE__*/ new Vector3(); 16680const _f2 = /*@__PURE__*/ new Vector3(); 16681 16682const _center = /*@__PURE__*/ new Vector3(); 16683const _extents = /*@__PURE__*/ new Vector3(); 16684const _triangleNormal = /*@__PURE__*/ new Vector3(); 16685const _testAxis = /*@__PURE__*/ new Vector3(); 16686 16687function satForAxes( axes, v0, v1, v2, extents ) { 16688 16689 for ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) { 16690 16691 _testAxis.fromArray( axes, i ); 16692 // project the aabb onto the separating axis 16693 const r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z ); 16694 // project all 3 vertices of the triangle onto the separating axis 16695 const p0 = v0.dot( _testAxis ); 16696 const p1 = v1.dot( _testAxis ); 16697 const p2 = v2.dot( _testAxis ); 16698 // actual test, basically see if either of the most extreme of the triangle points intersects r 16699 if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { 16700 16701 // points of the projected triangle are outside the projected half-length of the aabb 16702 // the axis is separating and we can exit 16703 return false; 16704 16705 } 16706 16707 } 16708 16709 return true; 16710 16711} 16712 16713// Fast Half Float Conversions, http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf 16714 16715const _tables = /*@__PURE__*/ _generateTables(); 16716 16717function _generateTables() { 16718 16719 // float32 to float16 helpers 16720 16721 const buffer = new ArrayBuffer( 4 ); 16722 const floatView = new Float32Array( buffer ); 16723 const uint32View = new Uint32Array( buffer ); 16724 16725 const baseTable = new Uint32Array( 512 ); 16726 const shiftTable = new Uint32Array( 512 ); 16727 16728 for ( let i = 0; i < 256; ++ i ) { 16729 16730 const e = i - 127; 16731 16732 // very small number (0, -0) 16733 16734 if ( e < -27 ) { 16735 16736 baseTable[ i ] = 0x0000; 16737 baseTable[ i | 0x100 ] = 0x8000; 16738 shiftTable[ i ] = 24; 16739 shiftTable[ i | 0x100 ] = 24; 16740 16741 // small number (denorm) 16742 16743 } else if ( e < -14 ) { 16744 16745 baseTable[ i ] = 0x0400 >> ( - e - 14 ); 16746 baseTable[ i | 0x100 ] = ( 0x0400 >> ( - e - 14 ) ) | 0x8000; 16747 shiftTable[ i ] = - e - 1; 16748 shiftTable[ i | 0x100 ] = - e - 1; 16749 16750 // normal number 16751 16752 } else if ( e <= 15 ) { 16753 16754 baseTable[ i ] = ( e + 15 ) << 10; 16755 baseTable[ i | 0x100 ] = ( ( e + 15 ) << 10 ) | 0x8000; 16756 shiftTable[ i ] = 13; 16757 shiftTable[ i | 0x100 ] = 13; 16758 16759 // large number (Infinity, -Infinity) 16760 16761 } else if ( e < 128 ) { 16762 16763 baseTable[ i ] = 0x7c00; 16764 baseTable[ i | 0x100 ] = 0xfc00; 16765 shiftTable[ i ] = 24; 16766 shiftTable[ i | 0x100 ] = 24; 16767 16768 // stay (NaN, Infinity, -Infinity) 16769 16770 } else { 16771 16772 baseTable[ i ] = 0x7c00; 16773 baseTable[ i | 0x100 ] = 0xfc00; 16774 shiftTable[ i ] = 13; 16775 shiftTable[ i | 0x100 ] = 13; 16776 16777 } 16778 16779 } 16780 16781 // float16 to float32 helpers 16782 16783 const mantissaTable = new Uint32Array( 2048 ); 16784 const exponentTable = new Uint32Array( 64 ); 16785 const offsetTable = new Uint32Array( 64 ); 16786 16787 for ( let i = 1; i < 1024; ++ i ) { 16788 16789 let m = i << 13; // zero pad mantissa bits 16790 let e = 0; // zero exponent 16791 16792 // normalized 16793 while ( ( m & 0x00800000 ) === 0 ) { 16794 16795 m <<= 1; 16796 e -= 0x00800000; // decrement exponent 16797 16798 } 16799 16800 m &= -8388609; // clear leading 1 bit 16801 e += 0x38800000; // adjust bias 16802 16803 mantissaTable[ i ] = m | e; 16804 16805 } 16806 16807 for ( let i = 1024; i < 2048; ++ i ) { 16808 16809 mantissaTable[ i ] = 0x38000000 + ( ( i - 1024 ) << 13 ); 16810 16811 } 16812 16813 for ( let i = 1; i < 31; ++ i ) { 16814 16815 exponentTable[ i ] = i << 23; 16816 16817 } 16818 16819 exponentTable[ 31 ] = 0x47800000; 16820 exponentTable[ 32 ] = 0x80000000; 16821 16822 for ( let i = 33; i < 63; ++ i ) { 16823 16824 exponentTable[ i ] = 0x80000000 + ( ( i - 32 ) << 23 ); 16825 16826 } 16827 16828 exponentTable[ 63 ] = 0xc7800000; 16829 16830 for ( let i = 1; i < 64; ++ i ) { 16831 16832 if ( i !== 32 ) { 16833 16834 offsetTable[ i ] = 1024; 16835 16836 } 16837 16838 } 16839 16840 return { 16841 floatView: floatView, 16842 uint32View: uint32View, 16843 baseTable: baseTable, 16844 shiftTable: shiftTable, 16845 mantissaTable: mantissaTable, 16846 exponentTable: exponentTable, 16847 offsetTable: offsetTable 16848 }; 16849 16850} 16851 16852/** 16853 * Returns a half precision floating point value (FP16) from the given single 16854 * precision floating point value (FP32). 16855 * 16856 * @param {number} val - A single precision floating point value. 16857 * @return {number} The FP16 value. 16858 */ 16859function toHalfFloat( val ) { 16860 16861 if ( Math.abs( val ) > 65504 ) warn( 'DataUtils.toHalfFloat(): Value out of range.' ); 16862 16863 val = clamp( val, -65504, 65504 ); 16864 16865 _tables.floatView[ 0 ] = val; 16866 const f = _tables.uint32View[ 0 ]; 16867 const e = ( f >> 23 ) & 0x1ff; 16868 return _tables.baseTable[ e ] + ( ( f & 0x007fffff ) >> _tables.shiftTable[ e ] ); 16869 16870} 16871 16872/** 16873 * Returns a single precision floating point value (FP32) from the given half 16874 * precision floating point value (FP16). 16875 * 16876 * @param {number} val - A half precision floating point value. 16877 * @return {number} The FP32 value. 16878 */ 16879function fromHalfFloat( val ) { 16880 16881 const m = val >> 10; 16882 _tables.uint32View[ 0 ] = _tables.mantissaTable[ _tables.offsetTable[ m ] + ( val & 0x3ff ) ] + _tables.exponentTable[ m ]; 16883 return _tables.floatView[ 0 ]; 16884 16885} 16886 16887/** 16888 * A class containing utility functions for data. 16889 * 16890 * @hideconstructor 16891 */ 16892class DataUtils { 16893 16894 /** 16895 * Returns a half precision floating point value (FP16) from the given single 16896 * precision floating point value (FP32). 16897 * 16898 * @param {number} val - A single precision floating point value. 16899 * @return {number} The FP16 value. 16900 */ 16901 static toHalfFloat( val ) { 16902 16903 return toHalfFloat( val ); 16904 16905 } 16906 16907 /** 16908 * Returns a single precision floating point value (FP32) from the given half 16909 * precision floating point value (FP16). 16910 * 16911 * @param {number} val - A half precision floating point value. 16912 * @return {number} The FP32 value. 16913 */ 16914 static fromHalfFloat( val ) { 16915 16916 return fromHalfFloat( val ); 16917 16918 } 16919 16920} 16921 16922const _vector$a = /*@__PURE__*/ new Vector3(); 16923const _vector2$1 = /*@__PURE__*/ new Vector2(); 16924 16925let _id$2 = 0; 16926 16927/** 16928 * This class stores data for an attribute (such as vertex positions, face 16929 * indices, normals, colors, UVs, and any custom attributes ) associated with 16930 * a geometry, which allows for more efficient passing of data to the GPU. 16931 * 16932 * When working with vector-like data, the `fromBufferAttribute( attribute, index )` 16933 * helper methods on vector and color class might be helpful. E.g. {@link Vector3#fromBufferAttribute}. 16934 */ 16935class BufferAttribute extends EventDispatcher { 16936 16937 /** 16938 * Constructs a new buffer attribute. 16939 * 16940 * @param {TypedArray} array - The array holding the attribute data. 16941 * @param {number} itemSize - The item size. 16942 * @param {boolean} [normalized=false] - Whether the data are normalized or not. 16943 */ 16944 constructor( array, itemSize, normalized = false ) { 16945 16946 super(); 16947 16948 if ( Array.isArray( array ) ) { 16949 16950 throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); 16951 16952 } 16953 16954 /** 16955 * This flag can be used for type testing. 16956 * 16957 * @type {boolean} 16958 * @readonly 16959 * @default true 16960 */ 16961 this.isBufferAttribute = true; 16962 16963 /** 16964 * The ID of the buffer attribute. 16965 * 16966 * @name BufferAttribute#id 16967 * @type {number} 16968 * @readonly 16969 */ 16970 Object.defineProperty( this, 'id', { value: _id$2 ++ } ); 16971 16972 /** 16973 * The name of the buffer attribute. 16974 * 16975 * @type {string} 16976 */ 16977 this.name = ''; 16978 16979 /** 16980 * The array holding the attribute data. It should have `itemSize * numVertices` 16981 * elements, where `numVertices` is the number of vertices in the associated geometry. 16982 * 16983 * @type {TypedArray} 16984 */ 16985 this.array = array; 16986 16987 /** 16988 * The number of values of the array that should be associated with a particular vertex. 16989 * For instance, if this attribute is storing a 3-component vector (such as a position, 16990 * normal, or color), then the value should be `3`. 16991 * 16992 * @type {number} 16993 */ 16994 this.itemSize = itemSize; 16995 16996 /** 16997 * Represents the number of items this buffer attribute stores. It is internally computed 16998 * by dividing the `array` length by the `itemSize`. 16999 * 17000 * @type {number} 17001 * @readonly 17002 */ 17003 this.count = array !== undefined ? array.length / itemSize : 0; 17004 17005 /** 17006 * Applies to integer data only. Indicates how the underlying data in the buffer maps to 17007 * the values in the GLSL code. For instance, if `array` is an instance of `UInt16Array`, 17008 * and `normalized` is `true`, the values `0 - +65535` in the array data will be mapped to 17009 * `0.0f - +1.0f` in the GLSL attribute. If `normalized` is `false`, the values will be converted 17010 * to floats unmodified, i.e. `65535` becomes `65535.0f`. 17011 * 17012 * @type {boolean} 17013 */ 17014 this.normalized = normalized; 17015 17016 /** 17017 * Defines the intended usage pattern of the data store for optimization purposes. 17018 * 17019 * Note: After the initial use of a buffer, its usage cannot be changed. Instead, 17020 * instantiate a new one and set the desired usage before the next render. 17021 * 17022 * @type {(StaticDrawUsage|DynamicDrawUsage|StreamDrawUsage|StaticReadUsage|DynamicReadUsage|StreamReadUsage|StaticCopyUsage|DynamicCopyUsage|StreamCopyUsage)} 17023 * @default StaticDrawUsage 17024 */ 17025 this.usage = StaticDrawUsage; 17026 17027 /** 17028 * This can be used to only update some components of stored vectors (for example, just the 17029 * component related to color). Use the `addUpdateRange()` function to add ranges to this array. 17030 * 17031 * @type {Array<Object>} 17032 */ 17033 this.updateRanges = []; 17034 17035 /** 17036 * Configures the bound GPU type for use in shaders. 17037 * 17038 * Note: this only has an effect for integer arrays and is not configurable for float arrays. 17039 * For lower precision float types, use `Float16BufferAttribute`. 17040 * 17041 * @type {(FloatType|IntType)} 17042 * @default FloatType 17043 */ 17044 this.gpuType = FloatType; 17045 17046 /** 17047 * A version number, incremented every time the `needsUpdate` is set to `true`. 17048 * 17049 * @type {number} 17050 */ 17051 this.version = 0; 17052 17053 } 17054 17055 /** 17056 * A callback function that is executed after the renderer has transferred the attribute 17057 * array data to the GPU. 17058 */ 17059 onUploadCallback() {} 17060 17061 /** 17062 * Flag to indicate that this attribute has changed and should be re-sent to 17063 * the GPU. Set this to `true` when you modify the value of the array. 17064 * 17065 * @type {number} 17066 * @default false 17067 * @param {boolean} value 17068 */ 17069 set needsUpdate( value ) { 17070 17071 if ( value === true ) this.version ++; 17072 17073 } 17074 17075 /** 17076 * Sets the usage of this buffer attribute. 17077 * 17078 * @param {(StaticDrawUsage|DynamicDrawUsage|StreamDrawUsage|StaticReadUsage|DynamicReadUsage|StreamReadUsage|StaticCopyUsage|DynamicCopyUsage|StreamCopyUsage)} value - The usage to set. 17079 * @return {BufferAttribute} A reference to this buffer attribute. 17080 */ 17081 setUsage( value ) { 17082 17083 this.usage = value; 17084 17085 return this; 17086 17087 } 17088 17089 /** 17090 * Adds a range of data in the data array to be updated on the GPU. 17091 * 17092 * @param {number} start - Position at which to start update. 17093 * @param {number} count - The number of components to update. 17094 */ 17095 addUpdateRange( start, count ) { 17096 17097 this.updateRanges.push( { start, count } ); 17098 17099 } 17100 17101 /** 17102 * Clears the update ranges. 17103 */ 17104 clearUpdateRanges() { 17105 17106 this.updateRanges.length = 0; 17107 17108 } 17109 17110 /** 17111 * Copies the values of the given buffer attribute to this instance. 17112 * 17113 * @param {BufferAttribute} source - The buffer attribute to copy. 17114 * @return {BufferAttribute} A reference to this instance. 17115 */ 17116 copy( source ) { 17117 17118 this.name = source.name; 17119 this.array = new source.array.constructor( source.array ); 17120 this.itemSize = source.itemSize; 17121 this.count = source.count; 17122 this.normalized = source.normalized; 17123 17124 this.usage = source.usage; 17125 this.gpuType = source.gpuType; 17126 17127 return this; 17128 17129 } 17130 17131 /** 17132 * Copies a vector from the given buffer attribute to this one. The start 17133 * and destination position in the attribute buffers are represented by the 17134 * given indices. 17135 * 17136 * @param {number} index1 - The destination index into this buffer attribute. 17137 * @param {BufferAttribute} attribute - The buffer attribute to copy from. 17138 * @param {number} index2 - The source index into the given buffer attribute. 17139 * @return {BufferAttribute} A reference to this instance. 17140 */ 17141 copyAt( index1, attribute, index2 ) { 17142 17143 index1 *= this.itemSize; 17144 index2 *= attribute.itemSize; 17145 17146 for ( let i = 0, l = this.itemSize; i < l; i ++ ) { 17147 17148 this.array[ index1 + i ] = attribute.array[ index2 + i ]; 17149 17150 } 17151 17152 return this; 17153 17154 } 17155 17156 /** 17157 * Copies the given array data into this buffer attribute. 17158 * 17159 * @param {(TypedArray|Array)} array - The array to copy. 17160 * @return {BufferAttribute} A reference to this instance. 17161 */ 17162 copyArray( array ) { 17163 17164 this.array.set( array ); 17165 17166 return this; 17167 17168 } 17169 17170 /** 17171 * Applies the given 3x3 matrix to the given attribute. Works with 17172 * item size `2` and `3`. 17173 * 17174 * @param {Matrix3} m - The matrix to apply. 17175 * @return {BufferAttribute} A reference to this instance. 17176 */ 17177 applyMatrix3( m ) { 17178 17179 if ( this.itemSize === 2 ) { 17180 17181 for ( let i = 0, l = this.count; i < l; i ++ ) { 17182 17183 _vector2$1.fromBufferAttribute( this, i ); 17184 _vector2$1.applyMatrix3( m ); 17185 17186 this.setXY( i, _vector2$1.x, _vector2$1.y ); 17187 17188 } 17189 17190 } else if ( this.itemSize === 3 ) { 17191 17192 for ( let i = 0, l = this.count; i < l; i ++ ) { 17193 17194 _vector$a.fromBufferAttribute( this, i ); 17195 _vector$a.applyMatrix3( m ); 17196 17197 this.setXYZ( i, _vector$a.x, _vector$a.y, _vector$a.z ); 17198 17199 } 17200 17201 } 17202 17203 return this; 17204 17205 } 17206 17207 /** 17208 * Applies the given 4x4 matrix to the given attribute. Only works with 17209 * item size `3`. 17210 * 17211 * @param {Matrix4} m - The matrix to apply. 17212 * @return {BufferAttribute} A reference to this instance. 17213 */ 17214 applyMatrix4( m ) { 17215 17216 for ( let i = 0, l = this.count; i < l; i ++ ) { 17217 17218 _vector$a.fromBufferAttribute( this, i ); 17219 17220 _vector$a.applyMatrix4( m ); 17221 17222 this.setXYZ( i, _vector$a.x, _vector$a.y, _vector$a.z ); 17223 17224 } 17225 17226 return this; 17227 17228 } 17229 17230 /** 17231 * Applies the given 3x3 normal matrix to the given attribute. Only works with 17232 * item size `3`. 17233 * 17234 * @param {Matrix3} m - The normal matrix to apply. 17235 * @return {BufferAttribute} A reference to this instance. 17236 */ 17237 applyNormalMatrix( m ) { 17238 17239 for ( let i = 0, l = this.count; i < l; i ++ ) { 17240 17241 _vector$a.fromBufferAttribute( this, i ); 17242 17243 _vector$a.applyNormalMatrix( m ); 17244 17245 this.setXYZ( i, _vector$a.x, _vector$a.y, _vector$a.z ); 17246 17247 } 17248 17249 return this; 17250 17251 } 17252 17253 /** 17254 * Applies the given 4x4 matrix to the given attribute. Only works with 17255 * item size `3` and with direction vectors. 17256 * 17257 * @param {Matrix4} m - The matrix to apply. 17258 * @return {BufferAttribute} A reference to this instance. 17259 */ 17260 transformDirection( m ) { 17261 17262 for ( let i = 0, l = this.count; i < l; i ++ ) { 17263 17264 _vector$a.fromBufferAttribute( this, i ); 17265 17266 _vector$a.transformDirection( m ); 17267 17268 this.setXYZ( i, _vector$a.x, _vector$a.y, _vector$a.z ); 17269 17270 } 17271 17272 return this; 17273 17274 } 17275 17276 /** 17277 * Sets the given array data in the buffer attribute. 17278 * 17279 * @param {(TypedArray|Array)} value - The array data to set. 17280 * @param {number} [offset=0] - The offset in this buffer attribute's array. 17281 * @return {BufferAttribute} A reference to this instance. 17282 */ 17283 set( value, offset = 0 ) { 17284 17285 // Matching BufferAttribute constructor, do not normalize the array. 17286 this.array.set( value, offset ); 17287 17288 return this; 17289 17290 } 17291 17292 /** 17293 * Returns the given component of the vector at the given index. 17294 * 17295 * @param {number} index - The index into the buffer attribute. 17296 * @param {number} component - The component index. 17297 * @return {number} The returned value. 17298 */ 17299 getComponent( index, component ) { 17300 17301 let value = this.array[ index * this.itemSize + component ]; 17302 17303 if ( this.normalized ) value = denormalize( value, this.array ); 17304 17305 return value; 17306 17307 } 17308 17309 /** 17310 * Sets the given value to the given component of the vector at the given index. 17311 * 17312 * @param {number} index - The index into the buffer attribute. 17313 * @param {number} component - The component index. 17314 * @param {number} value - The value to set. 17315 * @return {BufferAttribute} A reference to this instance. 17316 */ 17317 setComponent( index, component, value ) { 17318 17319 if ( this.normalized ) value = normalize( value, this.array ); 17320 17321 this.array[ index * this.itemSize + component ] = value; 17322 17323 return this; 17324 17325 } 17326 17327 /** 17328 * Returns the x component of the vector at the given index. 17329 * 17330 * @param {number} index - The index into the buffer attribute. 17331 * @return {number} The x component. 17332 */ 17333 getX( index ) { 17334 17335 let x = this.array[ index * this.itemSize ]; 17336 17337 if ( this.normalized ) x = denormalize( x, this.array ); 17338 17339 return x; 17340 17341 } 17342 17343 /** 17344 * Sets the x component of the vector at the given index. 17345 * 17346 * @param {number} index - The index into the buffer attribute. 17347 * @param {number} x - The value to set. 17348 * @return {BufferAttribute} A reference to this instance. 17349 */ 17350 setX( index, x ) { 17351 17352 if ( this.normalized ) x = normalize( x, this.array ); 17353 17354 this.array[ index * this.itemSize ] = x; 17355 17356 return this; 17357 17358 } 17359 17360 /** 17361 * Returns the y component of the vector at the given index. 17362 * 17363 * @param {number} index - The index into the buffer attribute. 17364 * @return {number} The y component. 17365 */ 17366 getY( index ) { 17367 17368 let y = this.array[ index * this.itemSize + 1 ]; 17369 17370 if ( this.normalized ) y = denormalize( y, this.array ); 17371 17372 return y; 17373 17374 } 17375 17376 /** 17377 * Sets the y component of the vector at the given index. 17378 * 17379 * @param {number} index - The index into the buffer attribute. 17380 * @param {number} y - The value to set. 17381 * @return {BufferAttribute} A reference to this instance. 17382 */ 17383 setY( index, y ) { 17384 17385 if ( this.normalized ) y = normalize( y, this.array ); 17386 17387 this.array[ index * this.itemSize + 1 ] = y; 17388 17389 return this; 17390 17391 } 17392 17393 /** 17394 * Returns the z component of the vector at the given index. 17395 * 17396 * @param {number} index - The index into the buffer attribute. 17397 * @return {number} The z component. 17398 */ 17399 getZ( index ) { 17400 17401 let z = this.array[ index * this.itemSize + 2 ]; 17402 17403 if ( this.normalized ) z = denormalize( z, this.array ); 17404 17405 return z; 17406 17407 } 17408 17409 /** 17410 * Sets the z component of the vector at the given index. 17411 * 17412 * @param {number} index - The index into the buffer attribute. 17413 * @param {number} z - The value to set. 17414 * @return {BufferAttribute} A reference to this instance. 17415 */ 17416 setZ( index, z ) { 17417 17418 if ( this.normalized ) z = normalize( z, this.array ); 17419 17420 this.array[ index * this.itemSize + 2 ] = z; 17421 17422 return this; 17423 17424 } 17425 17426 /** 17427 * Returns the w component of the vector at the given index. 17428 * 17429 * @param {number} index - The index into the buffer attribute. 17430 * @return {number} The w component. 17431 */ 17432 getW( index ) { 17433 17434 let w = this.array[ index * this.itemSize + 3 ]; 17435 17436 if ( this.normalized ) w = denormalize( w, this.array ); 17437 17438 return w; 17439 17440 } 17441 17442 /** 17443 * Sets the w component of the vector at the given index. 17444 * 17445 * @param {number} index - The index into the buffer attribute. 17446 * @param {number} w - The value to set. 17447 * @return {BufferAttribute} A reference to this instance. 17448 */ 17449 setW( index, w ) { 17450 17451 if ( this.normalized ) w = normalize( w, this.array ); 17452 17453 this.array[ index * this.itemSize + 3 ] = w; 17454 17455 return this; 17456 17457 } 17458 17459 /** 17460 * Sets the x and y component of the vector at the given index. 17461 * 17462 * @param {number} index - The index into the buffer attribute. 17463 * @param {number} x - The value for the x component to set. 17464 * @param {number} y - The value for the y component to set. 17465 * @return {BufferAttribute} A reference to this instance. 17466 */ 17467 setXY( index, x, y ) { 17468 17469 index *= this.itemSize; 17470 17471 if ( this.normalized ) { 17472 17473 x = normalize( x, this.array ); 17474 y = normalize( y, this.array ); 17475 17476 } 17477 17478 this.array[ index + 0 ] = x; 17479 this.array[ index + 1 ] = y; 17480 17481 return this; 17482 17483 } 17484 17485 /** 17486 * Sets the x, y and z component of the vector at the given index. 17487 * 17488 * @param {number} index - The index into the buffer attribute. 17489 * @param {number} x - The value for the x component to set. 17490 * @param {number} y - The value for the y component to set. 17491 * @param {number} z - The value for the z component to set. 17492 * @return {BufferAttribute} A reference to this instance. 17493 */ 17494 setXYZ( index, x, y, z ) { 17495 17496 index *= this.itemSize; 17497 17498 if ( this.normalized ) { 17499 17500 x = normalize( x, this.array ); 17501 y = normalize( y, this.array ); 17502 z = normalize( z, this.array ); 17503 17504 } 17505 17506 this.array[ index + 0 ] = x; 17507 this.array[ index + 1 ] = y; 17508 this.array[ index + 2 ] = z; 17509 17510 return this; 17511 17512 } 17513 17514 /** 17515 * Sets the x, y, z and w component of the vector at the given index. 17516 * 17517 * @param {number} index - The index into the buffer attribute. 17518 * @param {number} x - The value for the x component to set. 17519 * @param {number} y - The value for the y component to set. 17520 * @param {number} z - The value for the z component to set. 17521 * @param {number} w - The value for the w component to set. 17522 * @return {BufferAttribute} A reference to this instance. 17523 */ 17524 setXYZW( index, x, y, z, w ) { 17525 17526 index *= this.itemSize; 17527 17528 if ( this.normalized ) { 17529 17530 x = normalize( x, this.array ); 17531 y = normalize( y, this.array ); 17532 z = normalize( z, this.array ); 17533 w = normalize( w, this.array ); 17534 17535 } 17536 17537 this.array[ index + 0 ] = x; 17538 this.array[ index + 1 ] = y; 17539 this.array[ index + 2 ] = z; 17540 this.array[ index + 3 ] = w; 17541 17542 return this; 17543 17544 } 17545 17546 /** 17547 * Sets the given callback function that is executed after the Renderer has transferred 17548 * the attribute array data to the GPU. Can be used to perform clean-up operations after 17549 * the upload when attribute data are not needed anymore on the CPU side. 17550 * 17551 * @param {Function} callback - The `onUpload()` callback. 17552 * @return {BufferAttribute} A reference to this instance. 17553 */ 17554 onUpload( callback ) { 17555 17556 this.onUploadCallback = callback; 17557 17558 return this; 17559 17560 } 17561 17562 /** 17563 * Returns a new buffer attribute with copied values from this instance. 17564 * 17565 * @return {BufferAttribute} A clone of this instance. 17566 */ 17567 clone() { 17568 17569 return new this.constructor( this.array, this.itemSize ).copy( this ); 17570 17571 } 17572 17573 /** 17574 * Serializes the buffer attribute into JSON. 17575 * 17576 * @return {Object} A JSON object representing the serialized buffer attribute. 17577 */ 17578 toJSON() { 17579 17580 const data = { 17581 itemSize: this.itemSize, 17582 type: this.array.constructor.name, 17583 array: Array.from( this.array ), 17584 normalized: this.normalized 17585 }; 17586 17587 data.name = this.name; 17588 data.usage = this.usage; 17589 data.gpuType = this.gpuType; 17590 17591 return data; 17592 17593 } 17594 17595 /** 17596 * Disposes of the buffer attribute. Available only in {@link WebGPURenderer}. 17597 */ 17598 dispose() { 17599 17600 this.dispatchEvent( { type: 'dispose' } ); 17601 17602 } 17603 17604} 17605 17606/** 17607 * Convenient class that can be used when creating a `Int8` buffer attribute with 17608 * a plain `Array` instance. 17609 * 17610 * @augments BufferAttribute 17611 */ 17612class Int8BufferAttribute extends BufferAttribute { 17613 17614 /** 17615 * Constructs a new buffer attribute. 17616 * 17617 * @param {(Array<number>|Int8Array)} array - The array holding the attribute data. 17618 * @param {number} itemSize - The item size. 17619 * @param {boolean} [normalized=false] - Whether the data are normalized or not. 17620 */ 17621 constructor( array, itemSize, normalized ) { 17622 17623 super( new Int8Array( array ), itemSize, normalized ); 17624 17625 } 17626 17627} 17628 17629/** 17630 * Convenient class that can be used when creating a `UInt8` buffer attribute with 17631 * a plain `Array` instance. 17632 * 17633 * @augments BufferAttribute 17634 */ 17635class Uint8BufferAttribute extends BufferAttribute { 17636 17637 /** 17638 * Constructs a new buffer attribute. 17639 * 17640 * @param {(Array<number>|Uint8Array)} array - The array holding the attribute data. 17641 * @param {number} itemSize - The item size. 17642 * @param {boolean} [normalized=false] - Whether the data are normalized or not. 17643 */ 17644 constructor( array, itemSize, normalized ) { 17645 17646 super( new Uint8Array( array ), itemSize, normalized ); 17647 17648 } 17649 17650} 17651 17652/** 17653 * Convenient class that can be used when creating a `UInt8Clamped` buffer attribute with 17654 * a plain `Array` instance. 17655 * 17656 * @augments BufferAttribute 17657 */ 17658class Uint8ClampedBufferAttribute extends BufferAttribute { 17659 17660 /** 17661 * Constructs a new buffer attribute. 17662 * 17663 * @param {(Array<number>|Uint8ClampedArray)} array - The array holding the attribute data. 17664 * @param {number} itemSize - The item size. 17665 * @param {boolean} [normalized=false] - Whether the data are normalized or not. 17666 */ 17667 constructor( array, itemSize, normalized ) { 17668 17669 super( new Uint8ClampedArray( array ), itemSize, normalized ); 17670 17671 } 17672 17673} 17674 17675/** 17676 * Convenient class that can be used when creating a `Int16` buffer attribute with 17677 * a plain `Array` instance. 17678 * 17679 * @augments BufferAttribute 17680 */ 17681class Int16BufferAttribute extends BufferAttribute { 17682 17683 /** 17684 * Constructs a new buffer attribute. 17685 * 17686 * @param {(Array<number>|Int16Array)} array - The array holding the attribute data. 17687 * @param {number} itemSize - The item size. 17688 * @param {boolean} [normalized=false] - Whether the data are normalized or not. 17689 */ 17690 constructor( array, itemSize, normalized ) { 17691 17692 super( new Int16Array( array ), itemSize, normalized ); 17693 17694 } 17695 17696} 17697 17698/** 17699 * Convenient class that can be used when creating a `UInt16` buffer attribute with 17700 * a plain `Array` instance. 17701 * 17702 * @augments BufferAttribute 17703 */ 17704class Uint16BufferAttribute extends BufferAttribute { 17705 17706 /** 17707 * Constructs a new buffer attribute. 17708 * 17709 * @param {(Array<number>|Uint16Array)} array - The array holding the attribute data. 17710 * @param {number} itemSize - The item size. 17711 * @param {boolean} [normalized=false] - Whether the data are normalized or not. 17712 */ 17713 constructor( array, itemSize, normalized ) { 17714 17715 super( new Uint16Array( array ), itemSize, normalized ); 17716 17717 } 17718 17719} 17720 17721/** 17722 * Convenient class that can be used when creating a `Int32` buffer attribute with 17723 * a plain `Array` instance. 17724 * 17725 * @augments BufferAttribute 17726 */ 17727class Int32BufferAttribute extends BufferAttribute { 17728 17729 /** 17730 * Constructs a new buffer attribute. 17731 * 17732 * @param {(Array<number>|Int32Array)} array - The array holding the attribute data. 17733 * @param {number} itemSize - The item size. 17734 * @param {boolean} [normalized=false] - Whether the data are normalized or not. 17735 */ 17736 constructor( array, itemSize, normalized ) { 17737 17738 super( new Int32Array( array ), itemSize, normalized ); 17739 17740 } 17741 17742} 17743 17744/** 17745 * Convenient class that can be used when creating a `UInt32` buffer attribute with 17746 * a plain `Array` instance. 17747 * 17748 * @augments BufferAttribute 17749 */ 17750class Uint32BufferAttribute extends BufferAttribute { 17751 17752 /** 17753 * Constructs a new buffer attribute. 17754 * 17755 * @param {(Array<number>|Uint32Array)} array - The array holding the attribute data. 17756 * @param {number} itemSize - The item size. 17757 * @param {boolean} [normalized=false] - Whether the data are normalized or not. 17758 */ 17759 constructor( array, itemSize, normalized ) { 17760 17761 super( new Uint32Array( array ), itemSize, normalized ); 17762 17763 } 17764 17765} 17766 17767/** 17768 * Convenient class that can be used when creating a `Float16` buffer attribute with 17769 * a plain `Array` instance. 17770 * 17771 * This class automatically converts to and from FP16 via `Uint16Array` since `Float16Array` 17772 * browser support is still problematic. 17773 * 17774 * @augments BufferAttribute 17775 */ 17776class Float16BufferAttribute extends BufferAttribute { 17777 17778 /** 17779 * Constructs a new buffer attribute. 17780 * 17781 * @param {(Array<number>|Uint16Array)} array - The array holding the attribute data. 17782 * @param {number} itemSize - The item size. 17783 * @param {boolean} [normalized=false] - Whether the data are normalized or not. 17784 */ 17785 constructor( array, itemSize, normalized ) { 17786 17787 super( new Uint16Array( array ), itemSize, normalized ); 17788 17789 this.isFloat16BufferAttribute = true; 17790 17791 } 17792 17793 getX( index ) { 17794 17795 let x = fromHalfFloat( this.array[ index * this.itemSize ] ); 17796 17797 if ( this.normalized ) x = denormalize( x, this.array ); 17798 17799 return x; 17800 17801 } 17802 17803 setX( index, x ) { 17804 17805 if ( this.normalized ) x = normalize( x, this.array ); 17806 17807 this.array[ index * this.itemSize ] = toHalfFloat( x ); 17808 17809 return this; 17810 17811 } 17812 17813 getY( index ) { 17814 17815 let y = fromHalfFloat( this.array[ index * this.itemSize + 1 ] ); 17816 17817 if ( this.normalized ) y = denormalize( y, this.array ); 17818 17819 return y; 17820 17821 } 17822 17823 setY( index, y ) { 17824 17825 if ( this.normalized ) y = normalize( y, this.array ); 17826 17827 this.array[ index * this.itemSize + 1 ] = toHalfFloat( y ); 17828 17829 return this; 17830 17831 } 17832 17833 getZ( index ) { 17834 17835 let z = fromHalfFloat( this.array[ index * this.itemSize + 2 ] ); 17836 17837 if ( this.normalized ) z = denormalize( z, this.array ); 17838 17839 return z; 17840 17841 } 17842 17843 setZ( index, z ) { 17844 17845 if ( this.normalized ) z = normalize( z, this.array ); 17846 17847 this.array[ index * this.itemSize + 2 ] = toHalfFloat( z ); 17848 17849 return this; 17850 17851 } 17852 17853 getW( index ) { 17854 17855 let w = fromHalfFloat( this.array[ index * this.itemSize + 3 ] ); 17856 17857 if ( this.normalized ) w = denormalize( w, this.array ); 17858 17859 return w; 17860 17861 } 17862 17863 setW( index, w ) { 17864 17865 if ( this.normalized ) w = normalize( w, this.array ); 17866 17867 this.array[ index * this.itemSize + 3 ] = toHalfFloat( w ); 17868 17869 return this; 17870 17871 } 17872 17873 setXY( index, x, y ) { 17874 17875 index *= this.itemSize; 17876 17877 if ( this.normalized ) { 17878 17879 x = normalize( x, this.array ); 17880 y = normalize( y, this.array ); 17881 17882 } 17883 17884 this.array[ index + 0 ] = toHalfFloat( x ); 17885 this.array[ index + 1 ] = toHalfFloat( y ); 17886 17887 return this; 17888 17889 } 17890 17891 setXYZ( index, x, y, z ) { 17892 17893 index *= this.itemSize; 17894 17895 if ( this.normalized ) { 17896 17897 x = normalize( x, this.array ); 17898 y = normalize( y, this.array ); 17899 z = normalize( z, this.array ); 17900 17901 } 17902 17903 this.array[ index + 0 ] = toHalfFloat( x ); 17904 this.array[ index + 1 ] = toHalfFloat( y ); 17905 this.array[ index + 2 ] = toHalfFloat( z ); 17906 17907 return this; 17908 17909 } 17910 17911 setXYZW( index, x, y, z, w ) { 17912 17913 index *= this.itemSize; 17914 17915 if ( this.normalized ) { 17916 17917 x = normalize( x, this.array ); 17918 y = normalize( y, this.array ); 17919 z = normalize( z, this.array ); 17920 w = normalize( w, this.array ); 17921 17922 } 17923 17924 this.array[ index + 0 ] = toHalfFloat( x ); 17925 this.array[ index + 1 ] = toHalfFloat( y ); 17926 this.array[ index + 2 ] = toHalfFloat( z ); 17927 this.array[ index + 3 ] = toHalfFloat( w ); 17928 17929 return this; 17930 17931 } 17932 17933} 17934 17935/** 17936 * Convenient class that can be used when creating a `Float32` buffer attribute with 17937 * a plain `Array` instance. 17938 * 17939 * @augments BufferAttribute 17940 */ 17941class Float32BufferAttribute extends BufferAttribute { 17942 17943 /** 17944 * Constructs a new buffer attribute. 17945 * 17946 * @param {(Array<number>|Float32Array)} array - The array holding the attribute data. 17947 * @param {number} itemSize - The item size. 17948 * @param {boolean} [normalized=false] - Whether the data are normalized or not. 17949 */ 17950 constructor( array, itemSize, normalized ) { 17951 17952 super( new Float32Array( array ), itemSize, normalized ); 17953 17954 } 17955 17956} 17957 17958const _box$3 = /*@__PURE__*/ new Box3(); 17959const _v1$3 = /*@__PURE__*/ new Vector3(); 17960const _v2$2 = /*@__PURE__*/ new Vector3(); 17961 17962/** 17963 * An analytical 3D sphere defined by a center and radius. This class is mainly 17964 * used as a Bounding Sphere for 3D objects. 17965 */ 17966class Sphere { 17967 17968 /** 17969 * Constructs a new sphere. 17970 * 17971 * @param {Vector3} [center=(0,0,0)] - The center of the sphere 17972 * @param {number} [radius=-1] - The radius of the sphere. 17973 */ 17974 constructor( center = new Vector3(), radius = -1 ) { 17975 17976 /** 17977 * This flag can be used for type testing. 17978 * 17979 * @type {boolean} 17980 * @readonly 17981 * @default true 17982 */ 17983 this.isSphere = true; 17984 17985 /** 17986 * The center of the sphere 17987 * 17988 * @type {Vector3} 17989 */ 17990 this.center = center; 17991 17992 /** 17993 * The radius of the sphere. 17994 * 17995 * @type {number} 17996 */ 17997 this.radius = radius; 17998 17999 } 18000 18001 /** 18002 * Sets the sphere's components by copying the given values. 18003 * 18004 * @param {Vector3} center - The center. 18005 * @param {number} radius - The radius. 18006 * @return {Sphere} A reference to this sphere. 18007 */ 18008 set( center, radius ) { 18009 18010 this.center.copy( center ); 18011 this.radius = radius; 18012 18013 return this; 18014 18015 } 18016 18017 /** 18018 * Computes the minimum bounding sphere for list of points. 18019 * If the optional center point is given, it is used as the sphere's 18020 * center. Otherwise, the center of the axis-aligned bounding box 18021 * encompassing the points is calculated. 18022 * 18023 * @param {Array<Vector3>} points - A list of points in 3D space. 18024 * @param {Vector3} [optionalCenter] - The center of the sphere. 18025 * @return {Sphere} A reference to this sphere. 18026 */ 18027 setFromPoints( points, optionalCenter ) { 18028 18029 const center = this.center; 18030 18031 if ( optionalCenter !== undefined ) { 18032 18033 center.copy( optionalCenter ); 18034 18035 } else { 18036 18037 _box$3.setFromPoints( points ).getCenter( center ); 18038 18039 } 18040 18041 let maxRadiusSq = 0; 18042 18043 for ( let i = 0, il = points.length; i < il; i ++ ) { 18044 18045 maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); 18046 18047 } 18048 18049 this.radius = Math.sqrt( maxRadiusSq ); 18050 18051 return this; 18052 18053 } 18054 18055 /** 18056 * Copies the values of the given sphere to this instance. 18057 * 18058 * @param {Sphere} sphere - The sphere to copy. 18059 * @return {Sphere} A reference to this sphere. 18060 */ 18061 copy( sphere ) { 18062 18063 this.center.copy( sphere.center ); 18064 this.radius = sphere.radius; 18065 18066 return this; 18067 18068 } 18069 18070 /** 18071 * Returns `true` if the sphere is empty (the radius set to a negative number). 18072 * 18073 * Spheres with a radius of `0` contain only their center point and are not 18074 * considered to be empty. 18075 * 18076 * @return {boolean} Whether this sphere is empty or not. 18077 */ 18078 isEmpty() { 18079 18080 return ( this.radius < 0 ); 18081 18082 } 18083 18084 /** 18085 * Makes this sphere empty which means in encloses a zero space in 3D. 18086 * 18087 * @return {Sphere} A reference to this sphere. 18088 */ 18089 makeEmpty() { 18090 18091 this.center.set( 0, 0, 0 ); 18092 this.radius = -1; 18093 18094 return this; 18095 18096 } 18097 18098 /** 18099 * Returns `true` if this sphere contains the given point inclusive of 18100 * the surface of the sphere. 18101 * 18102 * @param {Vector3} point - The point to check. 18103 * @return {boolean} Whether this sphere contains the given point or not. 18104 */ 18105 containsPoint( point ) { 18106 18107 return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); 18108 18109 } 18110 18111 /** 18112 * Returns the closest distance from the boundary of the sphere to the 18113 * given point. If the sphere contains the point, the distance will 18114 * be negative. 18115 * 18116 * @param {Vector3} point - The point to compute the distance to. 18117 * @return {number} The distance to the point. 18118 */ 18119 distanceToPoint( point ) { 18120 18121 return ( point.distanceTo( this.center ) - this.radius ); 18122 18123 } 18124 18125 /** 18126 * Returns `true` if this sphere intersects with the given one. 18127 * 18128 * @param {Sphere} sphere - The sphere to test. 18129 * @return {boolean} Whether this sphere intersects with the given one or not. 18130 */ 18131 intersectsSphere( sphere ) { 18132 18133 const radiusSum = this.radius + sphere.radius; 18134 18135 return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); 18136 18137 } 18138 18139 /** 18140 * Returns `true` if this sphere intersects with the given box. 18141 * 18142 * @param {Box3} box - The box to test. 18143 * @return {boolean} Whether this sphere intersects with the given box or not. 18144 */ 18145 intersectsBox( box ) { 18146 18147 return box.intersectsSphere( this ); 18148 18149 } 18150 18151 /** 18152 * Returns `true` if this sphere intersects with the given plane. 18153 * 18154 * @param {Plane} plane - The plane to test. 18155 * @return {boolean} Whether this sphere intersects with the given plane or not. 18156 */ 18157 intersectsPlane( plane ) { 18158 18159 return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; 18160 18161 } 18162 18163 /** 18164 * Clamps a point within the sphere. If the point is outside the sphere, it 18165 * will clamp it to the closest point on the edge of the sphere. Points 18166 * already inside the sphere will not be affected. 18167 * 18168 * @param {Vector3} point - The plane to clamp. 18169 * @param {Vector3} target - The target vector that is used to store the method's result. 18170 * @return {Vector3} The clamped point. 18171 */ 18172 clampPoint( point, target ) { 18173 18174 const deltaLengthSq = this.center.distanceToSquared( point ); 18175 18176 target.copy( point ); 18177 18178 if ( deltaLengthSq > ( this.radius * this.radius ) ) { 18179 18180 target.sub( this.center ).normalize(); 18181 target.multiplyScalar( this.radius ).add( this.center ); 18182 18183 } 18184 18185 return target; 18186 18187 } 18188 18189 /** 18190 * Returns a bounding box that encloses this sphere. 18191 * 18192 * @param {Box3} target - The target box that is used to store the method's result. 18193 * @return {Box3} The bounding box that encloses this sphere. 18194 */ 18195 getBoundingBox( target ) { 18196 18197 if ( this.isEmpty() ) { 18198 18199 // Empty sphere produces empty bounding box 18200 target.makeEmpty(); 18201 return target; 18202 18203 } 18204 18205 target.set( this.center, this.center ); 18206 target.expandByScalar( this.radius ); 18207 18208 return target; 18209 18210 } 18211 18212 /** 18213 * Transforms this sphere with the given 4x4 transformation matrix. 18214 * 18215 * @param {Matrix4} matrix - The transformation matrix. 18216 * @return {Sphere} A reference to this sphere. 18217 */ 18218 applyMatrix4( matrix ) { 18219 18220 this.center.applyMatrix4( matrix ); 18221 this.radius = this.radius * matrix.getMaxScaleOnAxis(); 18222 18223 return this; 18224 18225 } 18226 18227 /** 18228 * Translates the sphere's center by the given offset. 18229 * 18230 * @param {Vector3} offset - The offset. 18231 * @return {Sphere} A reference to this sphere. 18232 */ 18233 translate( offset ) { 18234 18235 this.center.add( offset ); 18236 18237 return this; 18238 18239 } 18240 18241 /** 18242 * Expands the boundaries of this sphere to include the given point. 18243 * 18244 * @param {Vector3} point - The point to include. 18245 * @return {Sphere} A reference to this sphere. 18246 */ 18247 expandByPoint( point ) { 18248 18249 if ( this.isEmpty() ) { 18250 18251 this.center.copy( point ); 18252 18253 this.radius = 0; 18254 18255 return this; 18256 18257 } 18258 18259 _v1$3.subVectors( point, this.center ); 18260 18261 const lengthSq = _v1$3.lengthSq(); 18262 18263 if ( lengthSq > ( this.radius * this.radius ) ) { 18264 18265 // calculate the minimal sphere 18266 18267 const length = Math.sqrt( lengthSq ); 18268 18269 const delta = ( length - this.radius ) * 0.5; 18270 18271 this.center.addScaledVector( _v1$3, delta / length ); 18272 18273 this.radius += delta; 18274 18275 } 18276 18277 return this; 18278 18279 } 18280 18281 /** 18282 * Expands this sphere to enclose both the original sphere and the given sphere. 18283 * 18284 * @param {Sphere} sphere - The sphere to include. 18285 * @return {Sphere} A reference to this sphere. 18286 */ 18287 union( sphere ) { 18288 18289 if ( sphere.isEmpty() ) { 18290 18291 return this; 18292 18293 } 18294 18295 if ( this.isEmpty() ) { 18296 18297 this.copy( sphere ); 18298 18299 return this; 18300 18301 } 18302 18303 if ( this.center.equals( sphere.center ) === true ) { 18304 18305 this.radius = Math.max( this.radius, sphere.radius ); 18306 18307 } else { 18308 18309 _v2$2.subVectors( sphere.center, this.center ).setLength( sphere.radius ); 18310 18311 this.expandByPoint( _v1$3.copy( sphere.center ).add( _v2$2 ) ); 18312 18313 this.expandByPoint( _v1$3.copy( sphere.center ).sub( _v2$2 ) ); 18314 18315 } 18316 18317 return this; 18318 18319 } 18320 18321 /** 18322 * Returns `true` if this sphere is equal with the given one. 18323 * 18324 * @param {Sphere} sphere - The sphere to test for equality. 18325 * @return {boolean} Whether this bounding sphere is equal with the given one. 18326 */ 18327 equals( sphere ) { 18328 18329 return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); 18330 18331 } 18332 18333 /** 18334 * Returns a new sphere with copied values from this instance. 18335 * 18336 * @return {Sphere} A clone of this instance. 18337 */ 18338 clone() { 18339 18340 return new this.constructor().copy( this ); 18341 18342 } 18343 18344 /** 18345 * Returns a serialized structure of the bounding sphere. 18346 * 18347 * @return {Object} Serialized structure with fields representing the object state. 18348 */ 18349 toJSON() { 18350 18351 return { 18352 radius: this.radius, 18353 center: this.center.toArray() 18354 }; 18355 18356 } 18357 18358 /** 18359 * Returns a serialized structure of the bounding sphere. 18360 * 18361 * @param {Object} json - The serialized json to set the sphere from. 18362 * @return {Sphere} A reference to this bounding sphere. 18363 */ 18364 fromJSON( json ) { 18365 18366 this.radius = json.radius; 18367 this.center.fromArray( json.center ); 18368 return this; 18369 18370 } 18371 18372} 18373 18374let _id$1 = 0; 18375 18376const _m1 = /*@__PURE__*/ new Matrix4(); 18377const _obj = /*@__PURE__*/ new Object3D(); 18378const _offset = /*@__PURE__*/ new Vector3(); 18379const _box$2 = /*@__PURE__*/ new Box3(); 18380const _boxMorphTargets = /*@__PURE__*/ new Box3(); 18381const _vector$9 = /*@__PURE__*/ new Vector3(); 18382 18383/** 18384 * A representation of mesh, line, or point geometry. Includes vertex 18385 * positions, face indices, normals, colors, UVs, and custom attributes 18386 * within buffers, reducing the cost of passing all this data to the GPU. 18387 * 18388 * ```js 18389 * const geometry = new THREE.BufferGeometry(); 18390 * // create a simple square shape. We duplicate the top left and bottom right 18391 * // vertices because each vertex needs to appear once per triangle. 18392 * const vertices = new Float32Array( [ 18393 * -1.0, -1.0, 1.0, // v0 18394 * 1.0, -1.0, 1.0, // v1 18395 * 1.0, 1.0, 1.0, // v2 18396 * 18397 * 1.0, 1.0, 1.0, // v3 18398 * -1.0, 1.0, 1.0, // v4 18399 * -1.0, -1.0, 1.0 // v5 18400 * ] ); 18401 * // itemSize = 3 because there are 3 values (components) per vertex 18402 * geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); 18403 * const material = new THREE.MeshBasicMaterial( { color: 0xff0000 } ); 18404 * const mesh = new THREE.Mesh( geometry, material ); 18405 * ``` 18406 * 18407 * @augments EventDispatcher 18408 */ 18409class BufferGeometry extends EventDispatcher { 18410 18411 /** 18412 * Constructs a new geometry. 18413 */ 18414 constructor() { 18415 18416 super(); 18417 18418 /** 18419 * This flag can be used for type testing. 18420 * 18421 * @type {boolean} 18422 * @readonly 18423 * @default true 18424 */ 18425 this.isBufferGeometry = true; 18426 18427 /** 18428 * The ID of the geometry. 18429 * 18430 * @name BufferGeometry#id 18431 * @type {number} 18432 * @readonly 18433 */ 18434 Object.defineProperty( this, 'id', { value: _id$1 ++ } ); 18435 18436 /** 18437 * The UUID of the geometry. 18438 * 18439 * @type {string} 18440 * @readonly 18441 */ 18442 this.uuid = generateUUID(); 18443 18444 /** 18445 * The name of the geometry. 18446 * 18447 * @type {string} 18448 */ 18449 this.name = ''; 18450 this.type = 'BufferGeometry'; 18451 18452 /** 18453 * Allows for vertices to be re-used across multiple triangles; this is 18454 * called using "indexed triangles". Each triangle is associated with the 18455 * indices of three vertices. This attribute therefore stores the index of 18456 * each vertex for each triangular face. If this attribute is not set, the 18457 * renderer assumes that each three contiguous positions represent a single triangle. 18458 * 18459 * @type {?BufferAttribute} 18460 * @default null 18461 */ 18462 this.index = null; 18463 18464 /** 18465 * A (storage) buffer attribute which was generated with a compute shader and 18466 * now defines indirect draw calls. 18467 * 18468 * Can only be used with {@link WebGPURenderer} and a WebGPU backend. 18469 * 18470 * @type {?BufferAttribute} 18471 * @default null 18472 */ 18473 this.indirect = null; 18474 18475 /** 18476 * The offset, in bytes, into the indirect drawing buffer where the value data begins. If an array is provided, multiple indirect draw calls will be made for each offset. 18477 * 18478 * Can only be used with {@link WebGPURenderer} and a WebGPU backend. 18479 * 18480 * @type {number|Array<number>} 18481 * @default 0 18482 */ 18483 this.indirectOffset = 0; 18484 18485 /** 18486 * This dictionary has as id the name of the attribute to be set and as value 18487 * the buffer attribute to set it to. Rather than accessing this property directly, 18488 * use `setAttribute()` and `getAttribute()` to access attributes of this geometry. 18489 * 18490 * @type {Object<string,(BufferAttribute|InterleavedBufferAttribute)>} 18491 */ 18492 this.attributes = {}; 18493 18494 /** 18495 * This dictionary holds the morph targets of the geometry. 18496 * 18497 * Note: Once the geometry has been rendered, the morph attribute data cannot 18498 * be changed. You will have to call `dispose()`, and create a new geometry instance. 18499 * 18500 * @type {Object} 18501 */ 18502 this.morphAttributes = {}; 18503 18504 /** 18505 * Used to control the morph target behavior; when set to `true`, the morph 18506 * target data is treated as relative offsets, rather than as absolute 18507 * positions/normals. 18508 * 18509 * @type {boolean} 18510 * @default false 18511 */ 18512 this.morphTargetsRelative = false; 18513 18514 /** 18515 * Split the geometry into groups, each of which will be rendered in a 18516 * separate draw call. This allows an array of materials to be used with the geometry. 18517 * 18518 * Use `addGroup()` and `clearGroups()` to edit groups, rather than modifying this array directly. 18519 * 18520 * Every vertex and index must belong to exactly one group â groups must not share vertices or 18521 * indices, and must not leave vertices or indices unused. 18522 * 18523 * @type {Array<Object>} 18524 */ 18525 this.groups = []; 18526 18527 /** 18528 * Bounding box for the geometry which can be calculated with `computeBoundingBox()`. 18529 * 18530 * @type {?Box3} 18531 * @default null 18532 */ 18533 this.boundingBox = null; 18534 18535 /** 18536 * Bounding sphere for the geometry which can be calculated with `computeBoundingSphere()`. 18537 * 18538 * @type {?Sphere} 18539 * @default null 18540 */ 18541 this.boundingSphere = null; 18542 18543 /** 18544 * Determines the part of the geometry to render. This should not be set directly, 18545 * instead use `setDrawRange()`. 18546 * 18547 * @type {{start:number,count:number}} 18548 */ 18549 this.drawRange = { start: 0, count: Infinity }; 18550 18551 /** 18552 * An object that can be used to store custom data about the geometry. 18553 * It should not hold references to functions as these will not be cloned. 18554 * 18555 * @type {Object} 18556 */ 18557 this.userData = {}; 18558 18559 /** 18560 * `true` when the geometry has been transformed since construction 18561 * (e.g. via {@link BufferGeometry#applyMatrix4}). Only relevant for 18562 * geometry generators (subclasses that populate `parameters`): when set, 18563 * {@link BufferGeometry#toJSON} omits `parameters` since they no longer 18564 * describe the geometry. 18565 * 18566 * @private 18567 * @type {boolean} 18568 * @default false 18569 */ 18570 this._transformed = false; 18571 18572 } 18573 18574 /** 18575 * Returns the index of this geometry. 18576 * 18577 * @return {?BufferAttribute} The index. Returns `null` if no index is defined. 18578 */ 18579 getIndex() { 18580 18581 return this.index; 18582 18583 } 18584 18585 /** 18586 * Sets the given index to this geometry. 18587 * 18588 * @param {Array<number>|BufferAttribute} index - The index to set. 18589 * @return {BufferGeometry} A reference to this instance. 18590 */ 18591 setIndex( index ) { 18592 18593 if ( Array.isArray( index ) ) { 18594 18595 this.index = new ( arrayNeedsUint32( index ) ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); 18596 18597 } else { 18598 18599 this.index = index; 18600 18601 } 18602 18603 return this; 18604 18605 } 18606 18607 /** 18608 * Sets the given indirect attribute to this geometry. 18609 * 18610 * @param {BufferAttribute} indirect - The attribute holding indirect draw calls. 18611 * @param {number|Array<number>} [indirectOffset=0] - The offset, in bytes, into the indirect drawing buffer where the value data begins. If an array is provided, multiple indirect draw calls will be made for each offset. 18612 * @return {BufferGeometry} A reference to this instance. 18613 */ 18614 setIndirect( indirect, indirectOffset = 0 ) { 18615 18616 this.indirect = indirect; 18617 this.indirectOffset = indirectOffset; 18618 18619 return this; 18620 18621 } 18622 18623 /** 18624 * Returns the indirect attribute of this geometry. 18625 * 18626 * @return {?BufferAttribute} The indirect attribute. Returns `null` if no indirect attribute is defined. 18627 */ 18628 getIndirect() { 18629 18630 return this.indirect; 18631 18632 } 18633 18634 /** 18635 * Returns the buffer attribute for the given name. 18636 * 18637 * @param {string} name - The attribute name. 18638 * @return {BufferAttribute|InterleavedBufferAttribute|undefined} The buffer attribute. 18639 * Returns `undefined` if not attribute has been found. 18640 */ 18641 getAttribute( name ) { 18642 18643 return this.attributes[ name ]; 18644 18645 } 18646 18647 /** 18648 * Sets the given attribute for the given name. 18649 * 18650 * @param {string} name - The attribute name. 18651 * @param {BufferAttribute|InterleavedBufferAttribute} attribute - The attribute to set. 18652 * @return {BufferGeometry} A reference to this instance. 18653 */ 18654 setAttribute( name, attribute ) { 18655 18656 this.attributes[ name ] = attribute; 18657 18658 return this; 18659 18660 } 18661 18662 /** 18663 * Deletes the attribute for the given name. 18664 * 18665 * @param {string} name - The attribute name to delete. 18666 * @return {BufferGeometry} A reference to this instance. 18667 */ 18668 deleteAttribute( name ) { 18669 18670 delete this.attributes[ name ]; 18671 18672 return this; 18673 18674 } 18675 18676 /** 18677 * Returns `true` if this geometry has an attribute for the given name. 18678 * 18679 * @param {string} name - The attribute name. 18680 * @return {boolean} Whether this geometry has an attribute for the given name or not. 18681 */ 18682 hasAttribute( name ) { 18683 18684 return this.attributes[ name ] !== undefined; 18685 18686 } 18687 18688 /** 18689 * Adds a group to this geometry. 18690 * 18691 * @param {number} start - The first element in this draw call. That is the first 18692 * vertex for non-indexed geometry, otherwise the first triangle index. 18693 * @param {number} count - Specifies how many vertices (or indices) are part of this group. 18694 * @param {number} [materialIndex=0] - The material array index to use. 18695 */ 18696 addGroup( start, count, materialIndex = 0 ) { 18697 18698 this.groups.push( { 18699 18700 start: start, 18701 count: count, 18702 materialIndex: materialIndex 18703 18704 } ); 18705 18706 } 18707 18708 /** 18709 * Clears all groups. 18710 */ 18711 clearGroups() { 18712 18713 this.groups = []; 18714 18715 } 18716 18717 /** 18718 * Sets the draw range for this geometry. 18719 * 18720 * @param {number} start - The first vertex for non-indexed geometry, otherwise the first triangle index. 18721 * @param {number} count - For non-indexed BufferGeometry, `count` is the number of vertices to render. 18722 * For indexed BufferGeometry, `count` is the number of indices to render. 18723 */ 18724 setDrawRange( start, count ) { 18725 18726 this.drawRange.start = start; 18727 this.drawRange.count = count; 18728 18729 } 18730 18731 /** 18732 * Applies the given 4x4 transformation matrix to the geometry. 18733 * 18734 * @param {Matrix4} matrix - The matrix to apply. 18735 * @return {BufferGeometry} A reference to this instance. 18736 */ 18737 applyMatrix4( matrix ) { 18738 18739 const position = this.attributes.position; 18740 18741 if ( position !== undefined ) { 18742 18743 position.applyMatrix4( matrix ); 18744 18745 position.needsUpdate = true; 18746 18747 } 18748 18749 const normal = this.attributes.normal; 18750 18751 if ( normal !== undefined ) { 18752 18753 const normalMatrix = new Matrix3().getNormalMatrix( matrix ); 18754 18755 normal.applyNormalMatrix( normalMatrix ); 18756 18757 normal.needsUpdate = true; 18758 18759 } 18760 18761 const tangent = this.attributes.tangent; 18762 18763 if ( tangent !== undefined ) { 18764 18765 tangent.transformDirection( matrix ); 18766 18767 tangent.needsUpdate = true; 18768 18769 } 18770 18771 if ( this.boundingBox !== null ) { 18772 18773 this.computeBoundingBox(); 18774 18775 } 18776 18777 if ( this.boundingSphere !== null ) { 18778 18779 this.computeBoundingSphere(); 18780 18781 } 18782 18783 this._transformed = true; 18784 18785 return this; 18786 18787 } 18788 18789 /** 18790 * Applies the rotation represented by the Quaternion to the geometry. 18791 * 18792 * @param {Quaternion} q - The Quaternion to apply. 18793 * @return {BufferGeometry} A reference to this instance. 18794 */ 18795 applyQuaternion( q ) { 18796 18797 _m1.makeRotationFromQuaternion( q ); 18798 18799 this.applyMatrix4( _m1 ); 18800 18801 return this; 18802 18803 } 18804 18805 /** 18806 * Rotates the geometry about the X axis. This is typically done as a one time 18807 * operation, and not during a loop. Use {@link Object3D#rotation} for typical 18808 * real-time mesh rotation. 18809 * 18810 * @param {number} angle - The angle in radians. 18811 * @return {BufferGeometry} A reference to this instance. 18812 */ 18813 rotateX( angle ) { 18814 18815 // rotate geometry around world x-axis 18816 18817 _m1.makeRotationX( angle ); 18818 18819 this.applyMatrix4( _m1 ); 18820 18821 return this; 18822 18823 } 18824 18825 /** 18826 * Rotates the geometry about the Y axis. This is typically done as a one time 18827 * operation, and not during a loop. Use {@link Object3D#rotation} for typical 18828 * real-time mesh rotation. 18829 * 18830 * @param {number} angle - The angle in radians. 18831 * @return {BufferGeometry} A reference to this instance. 18832 */ 18833 rotateY( angle ) { 18834 18835 // rotate geometry around world y-axis 18836 18837 _m1.makeRotationY( angle ); 18838 18839 this.applyMatrix4( _m1 ); 18840 18841 return this; 18842 18843 } 18844 18845 /** 18846 * Rotates the geometry about the Z axis. This is typically done as a one time 18847 * operation, and not during a loop. Use {@link Object3D#rotation} for typical 18848 * real-time mesh rotation. 18849 * 18850 * @param {number} angle - The angle in radians. 18851 * @return {BufferGeometry} A reference to this instance. 18852 */ 18853 rotateZ( angle ) { 18854 18855 // rotate geometry around world z-axis 18856 18857 _m1.makeRotationZ( angle ); 18858 18859 this.applyMatrix4( _m1 ); 18860 18861 return this; 18862 18863 } 18864 18865 /** 18866 * Translates the geometry. This is typically done as a one time 18867 * operation, and not during a loop. Use {@link Object3D#position} for typical 18868 * real-time mesh rotation. 18869 * 18870 * @param {number} x - The x offset. 18871 * @param {number} y - The y offset. 18872 * @param {number} z - The z offset. 18873 * @return {BufferGeometry} A reference to this instance. 18874 */ 18875 translate( x, y, z ) { 18876 18877 // translate geometry 18878 18879 _m1.makeTranslation( x, y, z ); 18880 18881 this.applyMatrix4( _m1 ); 18882 18883 return this; 18884 18885 } 18886 18887 /** 18888 * Scales the geometry. This is typically done as a one time 18889 * operation, and not during a loop. Use {@link Object3D#scale} for typical 18890 * real-time mesh rotation. 18891 * 18892 * @param {number} x - The x scale. 18893 * @param {number} y - The y scale. 18894 * @param {number} z - The z scale. 18895 * @return {BufferGeometry} A reference to this instance. 18896 */ 18897 scale( x, y, z ) { 18898 18899 // scale geometry 18900 18901 _m1.makeScale( x, y, z ); 18902 18903 this.applyMatrix4( _m1 ); 18904 18905 return this; 18906 18907 } 18908 18909 /** 18910 * Rotates the geometry to face a point in 3D space. This is typically done as a one time 18911 * operation, and not during a loop. Use {@link Object3D#lookAt} for typical 18912 * real-time mesh rotation. 18913 * 18914 * @param {Vector3} vector - The target point. 18915 * @return {BufferGeometry} A reference to this instance. 18916 */ 18917 lookAt( vector ) { 18918 18919 _obj.lookAt( vector ); 18920 18921 _obj.updateMatrix(); 18922 18923 this.applyMatrix4( _obj.matrix ); 18924 18925 return this; 18926 18927 } 18928 18929 /** 18930 * Center the geometry based on its bounding box. 18931 * 18932 * @return {BufferGeometry} A reference to this instance. 18933 */ 18934 center() { 18935 18936 this.computeBoundingBox(); 18937 18938 this.boundingBox.getCenter( _offset ).negate(); 18939 18940 this.translate( _offset.x, _offset.y, _offset.z ); 18941 18942 return this; 18943 18944 } 18945 18946 /** 18947 * Defines a geometry by creating a `position` attribute based on the given array of points. The array 18948 * can hold 2D or 3D vectors. When using two-dimensional data, the `z` coordinate for all vertices is 18949 * set to `0`. 18950 * 18951 * If the method is used with an existing `position` attribute, the vertex data are overwritten with the 18952 * data from the array. The length of the array must match the vertex count. 18953 * 18954 * @param {Array<Vector2>|Array<Vector3>} points - The points. 18955 * @return {BufferGeometry} A reference to this instance. 18956 */ 18957 setFromPoints( points ) { 18958 18959 const positionAttribute = this.getAttribute( 'position' ); 18960 18961 if ( positionAttribute === undefined ) { 18962 18963 const position = []; 18964 18965 for ( let i = 0, l = points.length; i < l; i ++ ) { 18966 18967 const point = points[ i ]; 18968 position.push( point.x, point.y, point.z || 0 ); 18969 18970 } 18971 18972 this.setAttribute( 'position', new Float32BufferAttribute( position, 3 ) ); 18973 18974 } else { 18975 18976 const l = Math.min( points.length, positionAttribute.count ); // make sure data do not exceed buffer size 18977 18978 for ( let i = 0; i < l; i ++ ) { 18979 18980 const point = points[ i ]; 18981 positionAttribute.setXYZ( i, point.x, point.y, point.z || 0 ); 18982 18983 } 18984 18985 if ( points.length > positionAttribute.count ) { 18986 18987 warn( 'BufferGeometry: Buffer size too small for points data. Use .dispose() and create a new geometry.' ); 18988 18989 } 18990 18991 positionAttribute.needsUpdate = true; 18992 18993 } 18994 18995 return this; 18996 18997 } 18998 18999 /** 19000 * Computes the bounding box of the geometry, and updates the `boundingBox` member. 19001 * The bounding box is not computed by the engine; it must be computed by your app. 19002 * You may need to recompute the bounding box if the geometry vertices are modified. 19003 */ 19004 computeBoundingBox() { 19005 19006 if ( this.boundingBox === null ) { 19007 19008 this.boundingBox = new Box3(); 19009 19010 } 19011 19012 const position = this.attributes.position; 19013 const morphAttributesPosition = this.morphAttributes.position; 19014 19015 if ( position && position.isGLBufferAttribute ) { 19016 19017 error( 'BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box.', this ); 19018 19019 this.boundingBox.set( 19020 new Vector3( - Infinity, - Infinity, - Infinity ), 19021 new Vector3( + Infinity, + Infinity, + Infinity ) 19022 ); 19023 19024 return; 19025 19026 } 19027 19028 if ( position !== undefined ) { 19029 19030 this.boundingBox.setFromBufferAttribute( position ); 19031 19032 // process morph attributes if present 19033 19034 if ( morphAttributesPosition ) { 19035 19036 for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { 19037 19038 const morphAttribute = morphAttributesPosition[ i ]; 19039 _box$2.setFromBufferAttribute( morphAttribute ); 19040 19041 if ( this.morphTargetsRelative ) { 19042 19043 _vector$9.addVectors( this.boundingBox.min, _box$2.min ); 19044 this.boundingBox.expandByPoint( _vector$9 ); 19045 19046 _vector$9.addVectors( this.boundingBox.max, _box$2.max ); 19047 this.boundingBox.expandByPoint( _vector$9 ); 19048 19049 } else { 19050 19051 this.boundingBox.expandByPoint( _box$2.min ); 19052 this.boundingBox.expandByPoint( _box$2.max ); 19053 19054 } 19055 19056 } 19057 19058 } 19059 19060 } else { 19061 19062 this.boundingBox.makeEmpty(); 19063 19064 } 19065 19066 if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { 19067 19068 error( 'BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); 19069 19070 } 19071 19072 } 19073 19074 /** 19075 * Computes the bounding sphere of the geometry, and updates the `boundingSphere` member. 19076 * The engine automatically computes the bounding sphere when it is needed, e.g., for ray casting or view frustum culling. 19077 * You may need to recompute the bounding sphere if the geometry vertices are modified. 19078 */ 19079 computeBoundingSphere() { 19080 19081 if ( this.boundingSphere === null ) { 19082 19083 this.boundingSphere = new Sphere(); 19084 19085 } 19086 19087 const position = this.attributes.position; 19088 const morphAttributesPosition = this.morphAttributes.position; 19089 19090 if ( position && position.isGLBufferAttribute ) { 19091 19092 error( 'BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere.', this ); 19093 19094 this.boundingSphere.set( new Vector3(), Infinity ); 19095 19096 return; 19097 19098 } 19099 19100 if ( position ) { 19101 19102 // first, find the center of the bounding sphere 19103 19104 const center = this.boundingSphere.center; 19105 19106 _box$2.setFromBufferAttribute( position ); 19107 19108 // process morph attributes if present 19109 19110 if ( morphAttributesPosition ) { 19111 19112 for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { 19113 19114 const morphAttribute = morphAttributesPosition[ i ]; 19115 _boxMorphTargets.setFromBufferAttribute( morphAttribute ); 19116 19117 if ( this.morphTargetsRelative ) { 19118 19119 _vector$9.addVectors( _box$2.min, _boxMorphTargets.min ); 19120 _box$2.expandByPoint( _vector$9 ); 19121 19122 _vector$9.addVectors( _box$2.max, _boxMorphTargets.max ); 19123 _box$2.expandByPoint( _vector$9 ); 19124 19125 } else { 19126 19127 _box$2.expandByPoint( _boxMorphTargets.min ); 19128 _box$2.expandByPoint( _boxMorphTargets.max ); 19129 19130 } 19131 19132 } 19133 19134 } 19135 19136 _box$2.getCenter( center ); 19137 19138 // second, try to find a boundingSphere with a radius smaller than the 19139 // boundingSphere of the boundingBox: sqrt(3) smaller in the best case 19140 19141 let maxRadiusSq = 0; 19142 19143 for ( let i = 0, il = position.count; i < il; i ++ ) { 19144 19145 _vector$9.fromBufferAttribute( position, i ); 19146 19147 maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$9 ) ); 19148 19149 } 19150 19151 // process morph attributes if present 19152 19153 if ( morphAttributesPosition ) { 19154 19155 for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { 19156 19157 const morphAttribute = morphAttributesPosition[ i ]; 19158 const morphTargetsRelative = this.morphTargetsRelative; 19159 19160 for ( let j = 0, jl = morphAttribute.count; j < jl; j ++ ) { 19161 19162 _vector$9.fromBufferAttribute( morphAttribute, j ); 19163 19164 if ( morphTargetsRelative ) { 19165 19166 _offset.fromBufferAttribute( position, j ); 19167 _vector$9.add( _offset ); 19168 19169 } 19170 19171 maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$9 ) ); 19172 19173 } 19174 19175 } 19176 19177 } 19178 19179 this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); 19180 19181 if ( isNaN( this.boundingSphere.radius ) ) { 19182 19183 error( 'BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); 19184 19185 } 19186 19187 } 19188 19189 } 19190 19191 /** 19192 * Calculates and adds a tangent attribute to this geometry. 19193 * 19194 * The computation is only supported for indexed geometries and if position, normal, and uv attributes 19195 * are defined. When using a tangent space normal map, prefer the MikkTSpace algorithm provided by 19196 * {@link BufferGeometryUtils#computeMikkTSpaceTangents} instead. 19197 */ 19198 computeTangents() { 19199 19200 const index = this.index; 19201 const attributes = this.attributes; 19202 19203 // based on http://www.terathon.com/code/tangent.html 19204 // (per vertex tangents) 19205 19206 if ( index === null || 19207 attributes.position === undefined || 19208 attributes.normal === undefined || 19209 attributes.uv === undefined ) { 19210 19211 error( 'BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)' ); 19212 return; 19213 19214 } 19215 19216 const positionAttribute = attributes.position; 19217 const normalAttribute = attributes.normal; 19218 const uvAttribute = attributes.uv; 19219 19220 let tangentAttribute = this.getAttribute( 'tangent' ); 19221 19222 if ( tangentAttribute === undefined || tangentAttribute.count !== positionAttribute.count ) { 19223 19224 tangentAttribute = new BufferAttribute( new Float32Array( 4 * positionAttribute.count ), 4 ); 19225 this.setAttribute( 'tangent', tangentAttribute ); 19226 19227 } 19228 19229 const tan1 = [], tan2 = []; 19230 19231 for ( let i = 0; i < positionAttribute.count; i ++ ) { 19232 19233 tan1[ i ] = new Vector3(); 19234 tan2[ i ] = new Vector3(); 19235 19236 } 19237 19238 const vA = new Vector3(), 19239 vB = new Vector3(), 19240 vC = new Vector3(), 19241 19242 uvA = new Vector2(), 19243 uvB = new Vector2(), 19244 uvC = new Vector2(), 19245 19246 sdir = new Vector3(), 19247 tdir = new Vector3(); 19248 19249 function handleTriangle( a, b, c ) { 19250 19251 vA.fromBufferAttribute( positionAttribute, a ); 19252 vB.fromBufferAttribute( positionAttribute, b ); 19253 vC.fromBufferAttribute( positionAttribute, c ); 19254 19255 uvA.fromBufferAttribute( uvAttribute, a ); 19256 uvB.fromBufferAttribute( uvAttribute, b ); 19257 uvC.fromBufferAttribute( uvAttribute, c ); 19258 19259 vB.sub( vA ); 19260 vC.sub( vA ); 19261 19262 uvB.sub( uvA ); 19263 uvC.sub( uvA ); 19264 19265 const r = 1.0 / ( uvB.x * uvC.y - uvC.x * uvB.y ); 19266 19267 // silently ignore degenerate uv triangles having coincident or colinear vertices 19268 19269 if ( ! isFinite( r ) ) return; 19270 19271 sdir.copy( vB ).multiplyScalar( uvC.y ).addScaledVector( vC, - uvB.y ).multiplyScalar( r ); 19272 tdir.copy( vC ).multiplyScalar( uvB.x ).addScaledVector( vB, - uvC.x ).multiplyScalar( r ); 19273 19274 tan1[ a ].add( sdir ); 19275 tan1[ b ].add( sdir ); 19276 tan1[ c ].add( sdir ); 19277 19278 tan2[ a ].add( tdir ); 19279 tan2[ b ].add( tdir ); 19280 tan2[ c ].add( tdir ); 19281 19282 } 19283 19284 let groups = this.groups; 19285 19286 if ( groups.length === 0 ) { 19287 19288 groups = [ { 19289 start: 0, 19290 count: index.count 19291 } ]; 19292 19293 } 19294 19295 for ( let i = 0, il = groups.length; i < il; ++ i ) { 19296 19297 const group = groups[ i ]; 19298 19299 const start = group.start; 19300 const count = group.count; 19301 19302 for ( let j = start, jl = start + count; j < jl; j += 3 ) { 19303 19304 handleTriangle( 19305 index.getX( j + 0 ), 19306 index.getX( j + 1 ), 19307 index.getX( j + 2 ) 19308 ); 19309 19310 } 19311 19312 } 19313 19314 const tmp = new Vector3(), tmp2 = new Vector3(); 19315 const n = new Vector3(), n2 = new Vector3(); 19316 19317 function handleVertex( v ) { 19318 19319 n.fromBufferAttribute( normalAttribute, v ); 19320 n2.copy( n ); 19321 19322 const t = tan1[ v ]; 19323 19324 // Gram-Schmidt orthogonalize 19325 19326 tmp.copy( t ); 19327 tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); 19328 19329 // Calculate handedness 19330 19331 tmp2.crossVectors( n2, t ); 19332 const test = tmp2.dot( tan2[ v ] ); 19333 const w = ( test < 0.0 ) ? -1 : 1.0; 19334 19335 tangentAttribute.setXYZW( v, tmp.x, tmp.y, tmp.z, w ); 19336 19337 } 19338 19339 for ( let i = 0, il = groups.length; i < il; ++ i ) { 19340 19341 const group = groups[ i ]; 19342 19343 const start = group.start; 19344 const count = group.count; 19345 19346 for ( let j = start, jl = start + count; j < jl; j += 3 ) { 19347 19348 handleVertex( index.getX( j + 0 ) ); 19349 handleVertex( index.getX( j + 1 ) ); 19350 handleVertex( index.getX( j + 2 ) ); 19351 19352 } 19353 19354 } 19355 19356 this._transformed = true; 19357 19358 } 19359 19360 /** 19361 * Computes vertex normals for the given vertex data. For indexed geometries, the method sets 19362 * each vertex normal to be the average of the face normals of the faces that share that vertex. 19363 * For non-indexed geometries, vertices are not shared, and the method sets each vertex normal 19364 * to be the same as the face normal. 19365 */ 19366 computeVertexNormals() { 19367 19368 const index = this.index; 19369 const positionAttribute = this.getAttribute( 'position' ); 19370 19371 if ( positionAttribute !== undefined ) { 19372 19373 let normalAttribute = this.getAttribute( 'normal' ); 19374 19375 if ( normalAttribute === undefined || normalAttribute.count !== positionAttribute.count ) { 19376 19377 normalAttribute = new BufferAttribute( new Float32Array( positionAttribute.count * 3 ), 3 ); 19378 this.setAttribute( 'normal', normalAttribute ); 19379 19380 } else { 19381 19382 // reset existing normals to zero 19383 19384 for ( let i = 0, il = normalAttribute.count; i < il; i ++ ) { 19385 19386 normalAttribute.setXYZ( i, 0, 0, 0 ); 19387 19388 } 19389 19390 } 19391 19392 const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); 19393 const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); 19394 const cb = new Vector3(), ab = new Vector3(); 19395 19396 // indexed elements 19397 19398 if ( index ) { 19399 19400 for ( let i = 0, il = index.count; i < il; i += 3 ) { 19401 19402 const vA = index.getX( i + 0 ); 19403 const vB = index.getX( i + 1 ); 19404 const vC = index.getX( i + 2 ); 19405 19406 pA.fromBufferAttribute( positionAttribute, vA ); 19407 pB.fromBufferAttribute( positionAttribute, vB ); 19408 pC.fromBufferAttribute( positionAttribute, vC ); 19409 19410 cb.subVectors( pC, pB ); 19411 ab.subVectors( pA, pB ); 19412 cb.cross( ab ); 19413 19414 nA.fromBufferAttribute( normalAttribute, vA ); 19415 nB.fromBufferAttribute( normalAttribute, vB ); 19416 nC.fromBufferAttribute( normalAttribute, vC ); 19417 19418 nA.add( cb ); 19419 nB.add( cb ); 19420 nC.add( cb ); 19421 19422 normalAttribute.setXYZ( vA, nA.x, nA.y, nA.z ); 19423 normalAttribute.setXYZ( vB, nB.x, nB.y, nB.z ); 19424 normalAttribute.setXYZ( vC, nC.x, nC.y, nC.z ); 19425 19426 } 19427 19428 } else { 19429 19430 // non-indexed elements (unconnected triangle soup) 19431 19432 for ( let i = 0, il = positionAttribute.count; i < il; i += 3 ) { 19433 19434 pA.fromBufferAttribute( positionAttribute, i + 0 ); 19435 pB.fromBufferAttribute( positionAttribute, i + 1 ); 19436 pC.fromBufferAttribute( positionAttribute, i + 2 ); 19437 19438 cb.subVectors( pC, pB ); 19439 ab.subVectors( pA, pB ); 19440 cb.cross( ab ); 19441 19442 normalAttribute.setXYZ( i + 0, cb.x, cb.y, cb.z ); 19443 normalAttribute.setXYZ( i + 1, cb.x, cb.y, cb.z ); 19444 normalAttribute.setXYZ( i + 2, cb.x, cb.y, cb.z ); 19445 19446 } 19447 19448 } 19449 19450 this.normalizeNormals(); 19451 19452 normalAttribute.needsUpdate = true; 19453 19454 } 19455 19456 } 19457 19458 /** 19459 * Ensures every normal vector in a geometry will have a magnitude of `1`. This will 19460 * correct lighting on the geometry surfaces. 19461 */ 19462 normalizeNormals() { 19463 19464 const normals = this.attributes.normal; 19465 19466 for ( let i = 0, il = normals.count; i < il; i ++ ) { 19467 19468 _vector$9.fromBufferAttribute( normals, i ); 19469 19470 _vector$9.normalize(); 19471 19472 normals.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); 19473 19474 } 19475 19476 } 19477 19478 /** 19479 * Return a new non-index version of this indexed geometry. If the geometry 19480 * is already non-indexed, the method is a NOOP. 19481 * 19482 * @return {BufferGeometry} The non-indexed version of this indexed geometry. 19483 */ 19484 toNonIndexed() { 19485 19486 function convertBufferAttribute( attribute, indices ) { 19487 19488 const array = attribute.array; 19489 const itemSize = attribute.itemSize; 19490 const normalized = attribute.normalized; 19491 19492 const array2 = new array.constructor( indices.length * itemSize ); 19493 19494 let index = 0, index2 = 0; 19495 19496 for ( let i = 0, l = indices.length; i < l; i ++ ) { 19497 19498 if ( attribute.isInterleavedBufferAttribute ) { 19499 19500 index = indices[ i ] * attribute.data.stride + attribute.offset; 19501 19502 } else { 19503 19504 index = indices[ i ] * itemSize; 19505 19506 } 19507 19508 for ( let j = 0; j < itemSize; j ++ ) { 19509 19510 array2[ index2 ++ ] = array[ index ++ ]; 19511 19512 } 19513 19514 } 19515 19516 return new BufferAttribute( array2, itemSize, normalized ); 19517 19518 } 19519 19520 // 19521 19522 if ( this.index === null ) { 19523 19524 warn( 'BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed.' ); 19525 return this; 19526 19527 } 19528 19529 const geometry2 = new BufferGeometry(); 19530 19531 const indices = this.index.array; 19532 const attributes = this.attributes; 19533 19534 // attributes 19535 19536 for ( const name in attributes ) { 19537 19538 const attribute = attributes[ name ]; 19539 19540 const newAttribute = convertBufferAttribute( attribute, indices ); 19541 19542 geometry2.setAttribute( name, newAttribute ); 19543 19544 } 19545 19546 // morph attributes 19547 19548 const morphAttributes = this.morphAttributes; 19549 19550 for ( const name in morphAttributes ) { 19551 19552 const morphArray = []; 19553 const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes 19554 19555 for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) { 19556 19557 const attribute = morphAttribute[ i ]; 19558 19559 const newAttribute = convertBufferAttribute( attribute, indices ); 19560 19561 morphArray.push( newAttribute ); 19562 19563 } 19564 19565 geometry2.morphAttributes[ name ] = morphArray; 19566 19567 } 19568 19569 geometry2.morphTargetsRelative = this.morphTargetsRelative; 19570 19571 // groups 19572 19573 const groups = this.groups; 19574 19575 for ( let i = 0, l = groups.length; i < l; i ++ ) { 19576 19577 const group = groups[ i ]; 19578 geometry2.addGroup( group.start, group.count, group.materialIndex ); 19579 19580 } 19581 19582 return geometry2; 19583 19584 } 19585 19586 /** 19587 * Serializes the geometry into JSON. 19588 * 19589 * @return {Object} A JSON object representing the serialized geometry. 19590 */ 19591 toJSON() { 19592 19593 const data = { 19594 metadata: { 19595 version: 4.7, 19596 type: 'BufferGeometry', 19597 generator: 'BufferGeometry.toJSON' 19598 } 19599 }; 19600 19601 // standard BufferGeometry serialization 19602 19603 data.uuid = this.uuid; 19604 data.type = ( this.parameters !== undefined && this._transformed === true ) ? 'BufferGeometry' : this.type; 19605 data.name = this.name; 19606 if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; 19607 19608 if ( this.parameters !== undefined && this._transformed !== true ) { 19609 19610 const parameters = this.parameters; 19611 19612 for ( const key in parameters ) { 19613 19614 if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; 19615 19616 } 19617 19618 return data; 19619 19620 } 19621 19622 // for simplicity the code assumes attributes are not shared across geometries, see #15811 19623 19624 data.data = { attributes: {} }; 19625 19626 const index = this.index; 19627 19628 if ( index !== null ) { 19629 19630 data.data.index = { 19631 type: index.array.constructor.name, 19632 array: Array.prototype.slice.call( index.array ) 19633 }; 19634 19635 } 19636 19637 const attributes = this.attributes; 19638 19639 for ( const key in attributes ) { 19640 19641 const attribute = attributes[ key ]; 19642 19643 data.data.attributes[ key ] = attribute.toJSON( data.data ); 19644 19645 } 19646 19647 const morphAttributes = {}; 19648 let hasMorphAttributes = false; 19649 19650 for ( const key in this.morphAttributes ) { 19651 19652 const attributeArray = this.morphAttributes[ key ]; 19653 19654 const array = []; 19655 19656 for ( let i = 0, il = attributeArray.length; i < il; i ++ ) { 19657 19658 const attribute = attributeArray[ i ]; 19659 19660 array.push( attribute.toJSON( data.data ) ); 19661 19662 } 19663 19664 if ( array.length > 0 ) { 19665 19666 morphAttributes[ key ] = array; 19667 19668 hasMorphAttributes = true; 19669 19670 } 19671 19672 } 19673 19674 if ( hasMorphAttributes ) { 19675 19676 data.data.morphAttributes = morphAttributes; 19677 data.data.morphTargetsRelative = this.morphTargetsRelative; 19678 19679 } 19680 19681 const groups = this.groups; 19682 19683 if ( groups.length > 0 ) { 19684 19685 data.data.groups = JSON.parse( JSON.stringify( groups ) ); 19686 19687 } 19688 19689 const boundingSphere = this.boundingSphere; 19690 19691 if ( boundingSphere !== null ) { 19692 19693 data.data.boundingSphere = boundingSphere.toJSON(); 19694 19695 } 19696 19697 return data; 19698 19699 } 19700 19701 /** 19702 * Returns a new geometry with copied values from this instance. 19703 * 19704 * @return {BufferGeometry} A clone of this instance. 19705 */ 19706 clone() { 19707 19708 return new this.constructor().copy( this ); 19709 19710 } 19711 19712 /** 19713 * Copies the values of the given geometry to this instance. 19714 * 19715 * @param {BufferGeometry} source - The geometry to copy. 19716 * @return {BufferGeometry} A reference to this instance. 19717 */ 19718 copy( source ) { 19719 19720 // reset 19721 19722 this.index = null; 19723 this.attributes = {}; 19724 this.morphAttributes = {}; 19725 this.groups = []; 19726 this.boundingBox = null; 19727 this.boundingSphere = null; 19728 19729 // used for storing cloned, shared data 19730 19731 const data = {}; 19732 19733 // name 19734 19735 this.name = source.name; 19736 19737 // index 19738 19739 const index = source.index; 19740 19741 if ( index !== null ) { 19742 19743 this.setIndex( index.clone() ); 19744 19745 } 19746 19747 // attributes 19748 19749 const attributes = source.attributes; 19750 19751 for ( const name in attributes ) { 19752 19753 const attribute = attributes[ name ]; 19754 this.setAttribute( name, attribute.clone( data ) ); 19755 19756 } 19757 19758 // morph attributes 19759 19760 const morphAttributes = source.morphAttributes; 19761 19762 for ( const name in morphAttributes ) { 19763 19764 const array = []; 19765 const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes 19766 19767 for ( let i = 0, l = morphAttribute.length; i < l; i ++ ) { 19768 19769 array.push( morphAttribute[ i ].clone( data ) ); 19770 19771 } 19772 19773 this.morphAttributes[ name ] = array; 19774 19775 } 19776 19777 this.morphTargetsRelative = source.morphTargetsRelative; 19778 19779 // groups 19780 19781 const groups = source.groups; 19782 19783 for ( let i = 0, l = groups.length; i < l; i ++ ) { 19784 19785 const group = groups[ i ]; 19786 this.addGroup( group.start, group.count, group.materialIndex ); 19787 19788 } 19789 19790 // bounding box 19791 19792 const boundingBox = source.boundingBox; 19793 19794 if ( boundingBox !== null ) { 19795 19796 this.boundingBox = boundingBox.clone(); 19797 19798 } 19799 19800 // bounding sphere 19801 19802 const boundingSphere = source.boundingSphere; 19803 19804 if ( boundingSphere !== null ) { 19805 19806 this.boundingSphere = boundingSphere.clone(); 19807 19808 } 19809 19810 // draw range 19811 19812 this.drawRange.start = source.drawRange.start; 19813 this.drawRange.count = source.drawRange.count; 19814 19815 // user data 19816 19817 this.userData = source.userData; 19818 19819 // transformed flag 19820 19821 this._transformed = source._transformed; 19822 19823 return this; 19824 19825 } 19826 19827 /** 19828 * Frees the GPU-related resources allocated by this instance. Call this 19829 * method whenever this instance is no longer used in your app. 19830 * 19831 * @fires BufferGeometry#dispose 19832 */ 19833 dispose() { 19834 19835 this.dispatchEvent( { type: 'dispose' } ); 19836 19837 } 19838 19839} 19840 19841/** 19842 * "Interleaved" means that multiple attributes, possibly of different types, 19843 * (e.g., position, normal, uv, color) are packed into a single array buffer. 19844 * 19845 * An introduction into interleaved arrays can be found here: [Interleaved array basics](https://blog.tojicode.com/2011/05/interleaved-array-basics.html) 19846 */ 19847class InterleavedBuffer { 19848 19849 /** 19850 * Constructs a new interleaved buffer. 19851 * 19852 * @param {TypedArray} array - A typed array with a shared buffer storing attribute data. 19853 * @param {number} stride - The number of typed-array elements per vertex. 19854 */ 19855 constructor( array, stride ) { 19856 19857 /** 19858 * This flag can be used for type testing. 19859 * 19860 * @type {boolean} 19861 * @readonly 19862 * @default true 19863 */ 19864 this.isInterleavedBuffer = true; 19865 19866 /** 19867 * A typed array with a shared buffer storing attribute data. 19868 * 19869 * @type {TypedArray} 19870 */ 19871 this.array = array; 19872 19873 /** 19874 * The number of typed-array elements per vertex. 19875 * 19876 * @type {number} 19877 */ 19878 this.stride = stride; 19879 19880 /** 19881 * The total number of elements in the array 19882 * 19883 * @type {number} 19884 * @readonly 19885 */ 19886 this.count = array !== undefined ? array.length / stride : 0; 19887 19888 /** 19889 * Defines the intended usage pattern of the data store for optimization purposes. 19890 * 19891 * Note: After the initial use of a buffer, its usage cannot be changed. Instead, 19892 * instantiate a new one and set the desired usage before the next render. 19893 * 19894 * @type {(StaticDrawUsage|DynamicDrawUsage|StreamDrawUsage|StaticReadUsage|DynamicReadUsage|StreamReadUsage|StaticCopyUsage|DynamicCopyUsage|StreamCopyUsage)} 19895 * @default StaticDrawUsage 19896 */ 19897 this.usage = StaticDrawUsage; 19898 19899 /** 19900 * This can be used to only update some components of stored vectors (for example, just the 19901 * component related to color). Use the `addUpdateRange()` function to add ranges to this array. 19902 * 19903 * @type {Array<Object>} 19904 */ 19905 this.updateRanges = []; 19906 19907 /** 19908 * A version number, incremented every time the `needsUpdate` is set to `true`. 19909 * 19910 * @type {number} 19911 */ 19912 this.version = 0; 19913 19914 /** 19915 * The UUID of the interleaved buffer. 19916 * 19917 * @type {string} 19918 * @readonly 19919 */ 19920 this.uuid = generateUUID(); 19921 19922 } 19923 19924 /** 19925 * A callback function that is executed after the renderer has transferred the attribute array 19926 * data to the GPU. 19927 */ 19928 onUploadCallback() {} 19929 19930 /** 19931 * Flag to indicate that this attribute has changed and should be re-sent to 19932 * the GPU. Set this to `true` when you modify the value of the array. 19933 * 19934 * @type {number} 19935 * @default false 19936 * @param {boolean} value 19937 */ 19938 set needsUpdate( value ) { 19939 19940 if ( value === true ) this.version ++; 19941 19942 } 19943 19944 /** 19945 * Sets the usage of this interleaved buffer. 19946 * 19947 * @param {(StaticDrawUsage|DynamicDrawUsage|StreamDrawUsage|StaticReadUsage|DynamicReadUsage|StreamReadUsage|StaticCopyUsage|DynamicCopyUsage|StreamCopyUsage)} value - The usage to set. 19948 * @return {InterleavedBuffer} A reference to this interleaved buffer. 19949 */ 19950 setUsage( value ) { 19951 19952 this.usage = value; 19953 19954 return this; 19955 19956 } 19957 19958 /** 19959 * Adds a range of data in the data array to be updated on the GPU. 19960 * 19961 * @param {number} start - Position at which to start update. 19962 * @param {number} count - The number of components to update. 19963 */ 19964 addUpdateRange( start, count ) { 19965 19966 this.updateRanges.push( { start, count } ); 19967 19968 } 19969 19970 /** 19971 * Clears the update ranges. 19972 */ 19973 clearUpdateRanges() { 19974 19975 this.updateRanges.length = 0; 19976 19977 } 19978 19979 /** 19980 * Copies the values of the given interleaved buffer to this instance. 19981 * 19982 * @param {InterleavedBuffer} source - The interleaved buffer to copy. 19983 * @return {InterleavedBuffer} A reference to this instance. 19984 */ 19985 copy( source ) { 19986 19987 this.array = new source.array.constructor( source.array ); 19988 this.count = source.count; 19989 this.stride = source.stride; 19990 this.usage = source.usage; 19991 19992 return this; 19993 19994 } 19995 19996 /** 19997 * Copies a vector from the given interleaved buffer to this one. The start 19998 * and destination position in the attribute buffers are represented by the 19999 * given indices. 20000 * 20001 * @param {number} index1 - The destination index into this interleaved buffer. 20002 * @param {InterleavedBuffer} interleavedBuffer - The interleaved buffer to copy from. 20003 * @param {number} index2 - The source index into the given interleaved buffer. 20004 * @return {InterleavedBuffer} A reference to this instance. 20005 */ 20006 copyAt( index1, interleavedBuffer, index2 ) { 20007 20008 index1 *= this.stride; 20009 index2 *= interleavedBuffer.stride; 20010 20011 for ( let i = 0, l = this.stride; i < l; i ++ ) { 20012 20013 this.array[ index1 + i ] = interleavedBuffer.array[ index2 + i ]; 20014 20015 } 20016 20017 return this; 20018 20019 } 20020 20021 /** 20022 * Sets the given array data in the interleaved buffer. 20023 * 20024 * @param {(TypedArray|Array)} value - The array data to set. 20025 * @param {number} [offset=0] - The offset in this interleaved buffer's array. 20026 * @return {InterleavedBuffer} A reference to this instance. 20027 */ 20028 set( value, offset = 0 ) { 20029 20030 this.array.set( value, offset ); 20031 20032 return this; 20033 20034 } 20035 20036 /** 20037 * Returns a new interleaved buffer with copied values from this instance. 20038 * 20039 * @param {Object} [data] - An object with shared array buffers that allows to retain shared structures. 20040 * @return {InterleavedBuffer} A clone of this instance. 20041 */ 20042 clone( data ) { 20043 20044 if ( data.arrayBuffers === undefined ) { 20045 20046 data.arrayBuffers = {}; 20047 20048 } 20049 20050 if ( this.array.buffer._uuid === undefined ) { 20051 20052 this.array.buffer._uuid = generateUUID(); 20053 20054 } 20055 20056 if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { 20057 20058 data.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer; 20059 20060 } 20061 20062 const array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] ); 20063 20064 const ib = new this.constructor( array, this.stride ); 20065 ib.setUsage( this.usage ); 20066 20067 return ib; 20068 20069 } 20070 20071 /** 20072 * Sets the given callback function that is executed after the Renderer has transferred 20073 * the array data to the GPU. Can be used to perform clean-up operations after 20074 * the upload when data are not needed anymore on the CPU side. 20075 * 20076 * @param {Function} callback - The `onUpload()` callback. 20077 * @return {InterleavedBuffer} A reference to this instance. 20078 */ 20079 onUpload( callback ) { 20080 20081 this.onUploadCallback = callback; 20082 20083 return this; 20084 20085 } 20086 20087 /** 20088 * Serializes the interleaved buffer into JSON. 20089 * 20090 * @param {Object} [data] - An optional value holding meta information about the serialization. 20091 * @return {Object} A JSON object representing the serialized interleaved buffer. 20092 */ 20093 toJSON( data ) { 20094 20095 if ( data.arrayBuffers === undefined ) { 20096 20097 data.arrayBuffers = {}; 20098 20099 } 20100 20101 // generate UUID for array buffer if necessary 20102 20103 if ( this.array.buffer._uuid === undefined ) { 20104 20105 this.array.buffer._uuid = generateUUID(); 20106 20107 } 20108 20109 if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { 20110 20111 data.arrayBuffers[ this.array.buffer._uuid ] = Array.from( new Uint32Array( this.array.buffer ) ); 20112 20113 } 20114 20115 // 20116 20117 const json = { 20118 uuid: this.uuid, 20119 buffer: this.array.buffer._uuid, 20120 type: this.array.constructor.name, 20121 stride: this.stride 20122 }; 20123 20124 json.usage = this.usage; 20125 20126 return json; 20127 20128 } 20129 20130} 20131 20132const _vector$8 = /*@__PURE__*/ new Vector3(); 20133 20134/** 20135 * An alternative version of a buffer attribute with interleaved data. Interleaved 20136 * attributes share a common interleaved data storage ({@link InterleavedBuffer}) and refer with 20137 * different offsets into the buffer. 20138 */ 20139class InterleavedBufferAttribute { 20140 20141 /** 20142 * Constructs a new interleaved buffer attribute. 20143 * 20144 * @param {InterleavedBuffer} interleavedBuffer - The buffer holding the interleaved data. 20145 * @param {number} itemSize - The item size. 20146 * @param {number} offset - The attribute offset into the buffer. 20147 * @param {boolean} [normalized=false] - Whether the data are normalized or not. 20148 */ 20149 constructor( interleavedBuffer, itemSize, offset, normalized = false ) { 20150 20151 /** 20152 * This flag can be used for type testing. 20153 * 20154 * @type {boolean} 20155 * @readonly 20156 * @default true 20157 */ 20158 this.isInterleavedBufferAttribute = true; 20159 20160 /** 20161 * The name of the buffer attribute. 20162 * 20163 * @type {string} 20164 */ 20165 this.name = ''; 20166 20167 /** 20168 * The buffer holding the interleaved data. 20169 * 20170 * @type {InterleavedBuffer} 20171 */ 20172 this.data = interleavedBuffer; 20173 20174 /** 20175 * The item size, see {@link BufferAttribute#itemSize}. 20176 * 20177 * @type {number} 20178 */ 20179 this.itemSize = itemSize; 20180 20181 /** 20182 * The attribute offset into the buffer. 20183 * 20184 * @type {number} 20185 */ 20186 this.offset = offset; 20187 20188 /** 20189 * Whether the data are normalized or not, see {@link BufferAttribute#normalized} 20190 * 20191 * @type {InterleavedBuffer} 20192 */ 20193 this.normalized = normalized; 20194 20195 } 20196 20197 /** 20198 * The item count of this buffer attribute. 20199 * 20200 * @type {number} 20201 * @readonly 20202 */ 20203 get count() { 20204 20205 return this.data.count; 20206 20207 } 20208 20209 /** 20210 * The array holding the interleaved buffer attribute data. 20211 * 20212 * @type {TypedArray} 20213 */ 20214 get array() { 20215 20216 return this.data.array; 20217 20218 } 20219 20220 /** 20221 * Flag to indicate that this attribute has changed and should be re-sent to 20222 * the GPU. Set this to `true` when you modify the value of the array. 20223 * 20224 * @type {number} 20225 * @default false 20226 * @param {boolean} value 20227 */ 20228 set needsUpdate( value ) { 20229 20230 this.data.needsUpdate = value; 20231 20232 } 20233 20234 /** 20235 * Applies the given 4x4 matrix to the given attribute. Only works with 20236 * item size `3`. 20237 * 20238 * @param {Matrix4} m - The matrix to apply. 20239 * @return {InterleavedBufferAttribute} A reference to this instance. 20240 */ 20241 applyMatrix4( m ) { 20242 20243 for ( let i = 0, l = this.data.count; i < l; i ++ ) { 20244 20245 _vector$8.fromBufferAttribute( this, i ); 20246 20247 _vector$8.applyMatrix4( m ); 20248 20249 this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); 20250 20251 } 20252 20253 return this; 20254 20255 } 20256 20257 /** 20258 * Applies the given 3x3 normal matrix to the given attribute. Only works with 20259 * item size `3`. 20260 * 20261 * @param {Matrix3} m - The normal matrix to apply. 20262 * @return {InterleavedBufferAttribute} A reference to this instance. 20263 */ 20264 applyNormalMatrix( m ) { 20265 20266 for ( let i = 0, l = this.count; i < l; i ++ ) { 20267 20268 _vector$8.fromBufferAttribute( this, i ); 20269 20270 _vector$8.applyNormalMatrix( m ); 20271 20272 this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); 20273 20274 } 20275 20276 return this; 20277 20278 } 20279 20280 /** 20281 * Applies the given 4x4 matrix to the given attribute. Only works with 20282 * item size `3` and with direction vectors. 20283 * 20284 * @param {Matrix4} m - The matrix to apply. 20285 * @return {InterleavedBufferAttribute} A reference to this instance. 20286 */ 20287 transformDirection( m ) { 20288 20289 for ( let i = 0, l = this.count; i < l; i ++ ) { 20290 20291 _vector$8.fromBufferAttribute( this, i ); 20292 20293 _vector$8.transformDirection( m ); 20294 20295 this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); 20296 20297 } 20298 20299 return this; 20300 20301 } 20302 20303 /** 20304 * Returns the given component of the vector at the given index. 20305 * 20306 * @param {number} index - The index into the buffer attribute. 20307 * @param {number} component - The component index. 20308 * @return {number} The returned value. 20309 */ 20310 getComponent( index, component ) { 20311 20312 let value = this.array[ index * this.data.stride + this.offset + component ]; 20313 20314 if ( this.normalized ) value = denormalize( value, this.array ); 20315 20316 return value; 20317 20318 } 20319 20320 /** 20321 * Sets the given value to the given component of the vector at the given index. 20322 * 20323 * @param {number} index - The index into the buffer attribute. 20324 * @param {number} component - The component index. 20325 * @param {number} value - The value to set. 20326 * @return {InterleavedBufferAttribute} A reference to this instance. 20327 */ 20328 setComponent( index, component, value ) { 20329 20330 if ( this.normalized ) value = normalize( value, this.array ); 20331 20332 this.data.array[ index * this.data.stride + this.offset + component ] = value; 20333 20334 return this; 20335 20336 } 20337 20338 /** 20339 * Sets the x component of the vector at the given index. 20340 * 20341 * @param {number} index - The index into the buffer attribute. 20342 * @param {number} x - The value to set. 20343 * @return {InterleavedBufferAttribute} A reference to this instance. 20344 */ 20345 setX( index, x ) { 20346 20347 if ( this.normalized ) x = normalize( x, this.array ); 20348 20349 this.data.array[ index * this.data.stride + this.offset ] = x; 20350 20351 return this; 20352 20353 } 20354 20355 /** 20356 * Sets the y component of the vector at the given index. 20357 * 20358 * @param {number} index - The index into the buffer attribute. 20359 * @param {number} y - The value to set. 20360 * @return {InterleavedBufferAttribute} A reference to this instance. 20361 */ 20362 setY( index, y ) { 20363 20364 if ( this.normalized ) y = normalize( y, this.array ); 20365 20366 this.data.array[ index * this.data.stride + this.offset + 1 ] = y; 20367 20368 return this; 20369 20370 } 20371 20372 /** 20373 * Sets the z component of the vector at the given index. 20374 * 20375 * @param {number} index - The index into the buffer attribute. 20376 * @param {number} z - The value to set. 20377 * @return {InterleavedBufferAttribute} A reference to this instance. 20378 */ 20379 setZ( index, z ) { 20380 20381 if ( this.normalized ) z = normalize( z, this.array ); 20382 20383 this.data.array[ index * this.data.stride + this.offset + 2 ] = z; 20384 20385 return this; 20386 20387 } 20388 20389 /** 20390 * Sets the w component of the vector at the given index. 20391 * 20392 * @param {number} index - The index into the buffer attribute. 20393 * @param {number} w - The value to set. 20394 * @return {InterleavedBufferAttribute} A reference to this instance. 20395 */ 20396 setW( index, w ) { 20397 20398 if ( this.normalized ) w = normalize( w, this.array ); 20399 20400 this.data.array[ index * this.data.stride + this.offset + 3 ] = w; 20401 20402 return this; 20403 20404 } 20405 20406 /** 20407 * Returns the x component of the vector at the given index. 20408 * 20409 * @param {number} index - The index into the buffer attribute. 20410 * @return {number} The x component. 20411 */ 20412 getX( index ) { 20413 20414 let x = this.data.array[ index * this.data.stride + this.offset ]; 20415 20416 if ( this.normalized ) x = denormalize( x, this.array ); 20417 20418 return x; 20419 20420 } 20421 20422 /** 20423 * Returns the y component of the vector at the given index. 20424 * 20425 * @param {number} index - The index into the buffer attribute. 20426 * @return {number} The y component. 20427 */ 20428 getY( index ) { 20429 20430 let y = this.data.array[ index * this.data.stride + this.offset + 1 ]; 20431 20432 if ( this.normalized ) y = denormalize( y, this.array ); 20433 20434 return y; 20435 20436 } 20437 20438 /** 20439 * Returns the z component of the vector at the given index. 20440 * 20441 * @param {number} index - The index into the buffer attribute. 20442 * @return {number} The z component. 20443 */ 20444 getZ( index ) { 20445 20446 let z = this.data.array[ index * this.data.stride + this.offset + 2 ]; 20447 20448 if ( this.normalized ) z = denormalize( z, this.array ); 20449 20450 return z; 20451 20452 } 20453 20454 /** 20455 * Returns the w component of the vector at the given index. 20456 * 20457 * @param {number} index - The index into the buffer attribute. 20458 * @return {number} The w component. 20459 */ 20460 getW( index ) { 20461 20462 let w = this.data.array[ index * this.data.stride + this.offset + 3 ]; 20463 20464 if ( this.normalized ) w = denormalize( w, this.array ); 20465 20466 return w; 20467 20468 } 20469 20470 /** 20471 * Sets the x and y component of the vector at the given index. 20472 * 20473 * @param {number} index - The index into the buffer attribute. 20474 * @param {number} x - The value for the x component to set. 20475 * @param {number} y - The value for the y component to set. 20476 * @return {InterleavedBufferAttribute} A reference to this instance. 20477 */ 20478 setXY( index, x, y ) { 20479 20480 index = index * this.data.stride + this.offset; 20481 20482 if ( this.normalized ) { 20483 20484 x = normalize( x, this.array ); 20485 y = normalize( y, this.array ); 20486 20487 } 20488 20489 this.data.array[ index + 0 ] = x; 20490 this.data.array[ index + 1 ] = y; 20491 20492 return this; 20493 20494 } 20495 20496 /** 20497 * Sets the x, y and z component of the vector at the given index. 20498 * 20499 * @param {number} index - The index into the buffer attribute. 20500 * @param {number} x - The value for the x component to set. 20501 * @param {number} y - The value for the y component to set. 20502 * @param {number} z - The value for the z component to set. 20503 * @return {InterleavedBufferAttribute} A reference to this instance. 20504 */ 20505 setXYZ( index, x, y, z ) { 20506 20507 index = index * this.data.stride + this.offset; 20508 20509 if ( this.normalized ) { 20510 20511 x = normalize( x, this.array ); 20512 y = normalize( y, this.array ); 20513 z = normalize( z, this.array ); 20514 20515 } 20516 20517 this.data.array[ index + 0 ] = x; 20518 this.data.array[ index + 1 ] = y; 20519 this.data.array[ index + 2 ] = z; 20520 20521 return this; 20522 20523 } 20524 20525 /** 20526 * Sets the x, y, z and w component of the vector at the given index. 20527 * 20528 * @param {number} index - The index into the buffer attribute. 20529 * @param {number} x - The value for the x component to set. 20530 * @param {number} y - The value for the y component to set. 20531 * @param {number} z - The value for the z component to set. 20532 * @param {number} w - The value for the w component to set. 20533 * @return {InterleavedBufferAttribute} A reference to this instance. 20534 */ 20535 setXYZW( index, x, y, z, w ) { 20536 20537 index = index * this.data.stride + this.offset; 20538 20539 if ( this.normalized ) { 20540 20541 x = normalize( x, this.array ); 20542 y = normalize( y, this.array ); 20543 z = normalize( z, this.array ); 20544 w = normalize( w, this.array ); 20545 20546 } 20547 20548 this.data.array[ index + 0 ] = x; 20549 this.data.array[ index + 1 ] = y; 20550 this.data.array[ index + 2 ] = z; 20551 this.data.array[ index + 3 ] = w; 20552 20553 return this; 20554 20555 } 20556 20557 /** 20558 * Returns a new buffer attribute with copied values from this instance. 20559 * 20560 * If no parameter is provided, cloning an interleaved buffer attribute will de-interleave buffer data. 20561 * 20562 * @param {Object} [data] - An object with interleaved buffers that allows to retain the interleaved property. 20563 * @return {BufferAttribute|InterleavedBufferAttribute} A clone of this instance. 20564 */ 20565 clone( data ) { 20566 20567 if ( data === undefined ) { 20568 20569 log( 'InterleavedBufferAttribute.clone(): Cloning an interleaved buffer attribute will de-interleave buffer data.' ); 20570 20571 const array = []; 20572 20573 for ( let i = 0; i < this.count; i ++ ) { 20574 20575 const index = i * this.data.stride + this.offset; 20576 20577 for ( let j = 0; j < this.itemSize; j ++ ) { 20578 20579 array.push( this.data.array[ index + j ] ); 20580 20581 } 20582 20583 } 20584 20585 return new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized ); 20586 20587 } else { 20588 20589 if ( data.interleavedBuffers === undefined ) { 20590 20591 data.interleavedBuffers = {}; 20592 20593 } 20594 20595 if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { 20596 20597 data.interleavedBuffers[ this.data.uuid ] = this.data.clone( data ); 20598 20599 } 20600 20601 return new InterleavedBufferAttribute( data.interleavedBuffers[ this.data.uuid ], this.itemSize, this.offset, this.normalized ); 20602 20603 } 20604 20605 } 20606 20607 /** 20608 * Serializes the buffer attribute into JSON. 20609 * 20610 * If no parameter is provided, cloning an interleaved buffer attribute will de-interleave buffer data. 20611 * 20612 * @param {Object} [data] - An optional value holding meta information about the serialization. 20613 * @return {Object} A JSON object representing the serialized buffer attribute. 20614 */ 20615 toJSON( data ) { 20616 20617 if ( data === undefined ) { 20618 20619 log( 'InterleavedBufferAttribute.toJSON(): Serializing an interleaved buffer attribute will de-interleave buffer data.' ); 20620 20621 const array = []; 20622 20623 for ( let i = 0; i < this.count; i ++ ) { 20624 20625 const index = i * this.data.stride + this.offset; 20626 20627 for ( let j = 0; j < this.itemSize; j ++ ) { 20628 20629 array.push( this.data.array[ index + j ] ); 20630 20631 } 20632 20633 } 20634 20635 // de-interleave data and save it as an ordinary buffer attribute for now 20636 20637 return { 20638 itemSize: this.itemSize, 20639 type: this.array.constructor.name, 20640 array: array, 20641 normalized: this.normalized 20642 }; 20643 20644 } else { 20645 20646 // save as true interleaved attribute 20647 20648 if ( data.interleavedBuffers === undefined ) { 20649 20650 data.interleavedBuffers = {}; 20651 20652 } 20653 20654 if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { 20655 20656 data.interleavedBuffers[ this.data.uuid ] = this.data.toJSON( data ); 20657 20658 } 20659 20660 return { 20661 isInterleavedBufferAttribute: true, 20662 itemSize: this.itemSize, 20663 data: this.data.uuid, 20664 offset: this.offset, 20665 normalized: this.normalized 20666 }; 20667 20668 } 20669 20670 } 20671 20672} 20673 20674const _vector1 = /*@__PURE__*/ new Vector3(); 20675const _vector2 = /*@__PURE__*/ new Vector3(); 20676const _normalMatrix = /*@__PURE__*/ new Matrix3(); 20677 20678/** 20679 * A two dimensional surface that extends infinitely in 3D space, represented 20680 * in [Hessian normal form](https://mathworld.wolfram.com/HessianNormalForm.html) 20681 * by a unit length normal vector and a constant. 20682 */ 20683class Plane { 20684 20685 /** 20686 * Constructs a new plane. 20687 * 20688 * @param {Vector3} [normal=(1,0,0)] - A unit length vector defining the normal of the plane. 20689 * @param {number} [constant=0] - The signed distance from the origin to the plane. 20690 */ 20691 constructor( normal = new Vector3( 1, 0, 0 ), constant = 0 ) { 20692 20693 /** 20694 * This flag can be used for type testing. 20695 * 20696 * @type {boolean} 20697 * @readonly 20698 * @default true 20699 */ 20700 this.isPlane = true; 20701 20702 /** 20703 * A unit length vector defining the normal of the plane. 20704 * 20705 * @type {Vector3} 20706 */ 20707 this.normal = normal; 20708 20709 /** 20710 * The signed distance from the origin to the plane. 20711 * 20712 * @type {number} 20713 * @default 0 20714 */ 20715 this.constant = constant; 20716 20717 } 20718 20719 /** 20720 * Sets the plane components by copying the given values. 20721 * 20722 * @param {Vector3} normal - The normal. 20723 * @param {number} constant - The constant. 20724 * @return {Plane} A reference to this plane. 20725 */ 20726 set( normal, constant ) { 20727 20728 this.normal.copy( normal ); 20729 this.constant = constant; 20730 20731 return this; 20732 20733 } 20734 20735 /** 20736 * Sets the plane components by defining `x`, `y`, `z` as the 20737 * plane normal and `w` as the constant. 20738 * 20739 * @param {number} x - The value for the normal's x component. 20740 * @param {number} y - The value for the normal's y component. 20741 * @param {number} z - The value for the normal's z component. 20742 * @param {number} w - The constant value. 20743 * @return {Plane} A reference to this plane. 20744 */ 20745 setComponents( x, y, z, w ) { 20746 20747 this.normal.set( x, y, z ); 20748 this.constant = w; 20749 20750 return this; 20751 20752 } 20753 20754 /** 20755 * Sets the plane from the given normal and coplanar point (that is a point 20756 * that lies onto the plane). 20757 * 20758 * @param {Vector3} normal - The normal. 20759 * @param {Vector3} point - A coplanar point. 20760 * @return {Plane} A reference to this plane. 20761 */ 20762 setFromNormalAndCoplanarPoint( normal, point ) { 20763 20764 this.normal.copy( normal ); 20765 this.constant = - point.dot( this.normal ); 20766 20767 return this; 20768 20769 } 20770 20771 /** 20772 * Sets the plane from three coplanar points. The winding order is 20773 * assumed to be counter-clockwise, and determines the direction of 20774 * the plane normal. 20775 * 20776 * @param {Vector3} a - The first coplanar point. 20777 * @param {Vector3} b - The second coplanar point. 20778 * @param {Vector3} c - The third coplanar point. 20779 * @return {Plane} A reference to this plane. 20780 */ 20781 setFromCoplanarPoints( a, b, c ) { 20782 20783 const normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize(); 20784 20785 // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? 20786 20787 this.setFromNormalAndCoplanarPoint( normal, a ); 20788 20789 return this; 20790 20791 } 20792 20793 /** 20794 * Copies the values of the given plane to this instance. 20795 * 20796 * @param {Plane} plane - The plane to copy. 20797 * @return {Plane} A reference to this plane. 20798 */ 20799 copy( plane ) { 20800 20801 this.normal.copy( plane.normal ); 20802 this.constant = plane.constant; 20803 20804 return this; 20805 20806 } 20807 20808 /** 20809 * Normalizes the plane normal and adjusts the constant accordingly. 20810 * 20811 * @return {Plane} A reference to this plane. 20812 */ 20813 normalize() { 20814 20815 // Note: will lead to a divide by zero if the plane is invalid. 20816 20817 const inverseNormalLength = 1.0 / this.normal.length(); 20818 this.normal.multiplyScalar( inverseNormalLength ); 20819 this.constant *= inverseNormalLength; 20820 20821 return this; 20822 20823 } 20824 20825 /** 20826 * Negates both the plane normal and the constant. 20827 * 20828 * @return {Plane} A reference to this plane. 20829 */ 20830 negate() { 20831 20832 this.constant *= -1; 20833 this.normal.negate(); 20834 20835 return this; 20836 20837 } 20838 20839 /** 20840 * Returns the signed distance from the given point to this plane. 20841 * 20842 * @param {Vector3} point - The point to compute the distance for. 20843 * @return {number} The signed distance. 20844 */ 20845 distanceToPoint( point ) { 20846 20847 return this.normal.dot( point ) + this.constant; 20848 20849 } 20850 20851 /** 20852 * Returns the signed distance from the given sphere to this plane. 20853 * 20854 * @param {Sphere} sphere - The sphere to compute the distance for. 20855 * @return {number} The signed distance. 20856 */ 20857 distanceToSphere( sphere ) { 20858 20859 return this.distanceToPoint( sphere.center ) - sphere.radius; 20860 20861 } 20862 20863 /** 20864 * Projects a the given point onto the plane. 20865 * 20866 * @param {Vector3} point - The point to project. 20867 * @param {Vector3} target - The target vector that is used to store the method's result. 20868 * @return {Vector3} The projected point on the plane. 20869 */ 20870 projectPoint( point, target ) { 20871 20872 return target.copy( point ).addScaledVector( this.normal, - this.distanceToPoint( point ) ); 20873 20874 } 20875 20876 /** 20877 * Returns the intersection point of the passed line and the plane. Returns 20878 * `null` if the line does not intersect. Returns the line's starting point if 20879 * the line is coplanar with the plane. 20880 * 20881 * @param {Line3} line - The line to compute the intersection for. 20882 * @param {Vector3} target - The target vector that is used to store the method's result. 20883 * @param {boolean} [clampToLine=true] - Whether to clamp the intersection to the line segment. 20884 * @return {?Vector3} The intersection point. Returns `null` if no intersection is detected. 20885 */ 20886 intersectLine( line, target, clampToLine = true ) { 20887 20888 const direction = line.delta( _vector1 ); 20889 20890 const denominator = this.normal.dot( direction ); 20891 20892 if ( denominator === 0 ) { 20893 20894 // line is coplanar, return origin 20895 if ( this.distanceToPoint( line.start ) === 0 ) { 20896 20897 return target.copy( line.start ); 20898 20899 } 20900 20901 // Unsure if this is the correct method to handle this case. 20902 return null; 20903 20904 } 20905 20906 const t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; 20907 20908 if ( ( clampToLine === true ) && ( t < 0 || t > 1 ) ) { 20909 20910 return null; 20911 20912 } 20913 20914 return target.copy( line.start ).addScaledVector( direction, t ); 20915 20916 } 20917 20918 /** 20919 * Returns `true` if the given line segment intersects with (passes through) the plane. 20920 * 20921 * @param {Line3} line - The line to test. 20922 * @return {boolean} Whether the given line segment intersects with the plane or not. 20923 */ 20924 intersectsLine( line ) { 20925 20926 // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. 20927 20928 const startSign = this.distanceToPoint( line.start ); 20929 const endSign = this.distanceToPoint( line.end ); 20930 20931 return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); 20932 20933 } 20934 20935 /** 20936 * Returns `true` if the given bounding box intersects with the plane. 20937 * 20938 * @param {Box3} box - The bounding box to test. 20939 * @return {boolean} Whether the given bounding box intersects with the plane or not. 20940 */ 20941 intersectsBox( box ) { 20942 20943 return box.intersectsPlane( this ); 20944 20945 } 20946 20947 /** 20948 * Returns `true` if the given bounding sphere intersects with the plane. 20949 * 20950 * @param {Sphere} sphere - The bounding sphere to test. 20951 * @return {boolean} Whether the given bounding sphere intersects with the plane or not. 20952 */ 20953 intersectsSphere( sphere ) { 20954 20955 return sphere.intersectsPlane( this ); 20956 20957 } 20958 20959 /** 20960 * Returns a coplanar vector to the plane, by calculating the 20961 * projection of the normal at the origin onto the plane. 20962 * 20963 * @param {Vector3} target - The target vector that is used to store the method's result. 20964 * @return {Vector3} The coplanar point. 20965 */ 20966 coplanarPoint( target ) { 20967 20968 return target.copy( this.normal ).multiplyScalar( - this.constant ); 20969 20970 } 20971 20972 /** 20973 * Apply a 4x4 matrix to the plane. The matrix must be an affine, homogeneous transform. 20974 * 20975 * The optional normal matrix can be pre-computed like so: 20976 * ```js 20977 * const optionalNormalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); 20978 * ``` 20979 * 20980 * @param {Matrix4} matrix - The transformation matrix. 20981 * @param {Matrix4} [optionalNormalMatrix] - A pre-computed normal matrix. 20982 * @return {Plane} A reference to this plane. 20983 */ 20984 applyMatrix4( matrix, optionalNormalMatrix ) { 20985 20986 const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix ); 20987 20988 const referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix ); 20989 20990 const normal = this.normal.applyMatrix3( normalMatrix ).normalize(); 20991 20992 this.constant = - referencePoint.dot( normal ); 20993 20994 return this; 20995 20996 } 20997 20998 /** 20999 * Translates the plane by the distance defined by the given offset vector. 21000 * Note that this only affects the plane constant and will not affect the normal vector. 21001 * 21002 * @param {Vector3} offset - The offset vector. 21003 * @return {Plane} A reference to this plane. 21004 */ 21005 translate( offset ) { 21006 21007 this.constant -= offset.dot( this.normal ); 21008 21009 return this; 21010 21011 } 21012 21013 /** 21014 * Returns `true` if this plane is equal with the given one. 21015 * 21016 * @param {Plane} plane - The plane to test for equality. 21017 * @return {boolean} Whether this plane is equal with the given one. 21018 */ 21019 equals( plane ) { 21020 21021 return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); 21022 21023 } 21024 21025 /** 21026 * Returns a new plane with copied values from this instance. 21027 * 21028 * @return {Plane} A clone of this instance. 21029 */ 21030 clone() { 21031 21032 return new this.constructor().copy( this ); 21033 21034 } 21035 21036 /** 21037 * Returns a serialized structure of the plane. 21038 * 21039 * @return {Object} Serialized structure with fields representing the object state. 21040 */ 21041 toJSON() { 21042 21043 return { 21044 normal: this.normal.toArray(), 21045 constant: this.constant 21046 }; 21047 21048 } 21049 21050 /** 21051 * Sets the plane properties from the given JSON. 21052 * 21053 * @param {Object} json - The serialized json to set the plane from. 21054 * @return {Plane} A reference to this plane. 21055 */ 21056 fromJSON( json ) { 21057 21058 this.normal.fromArray( json.normal ); 21059 this.constant = json.constant; 21060 21061 return this; 21062 21063 } 21064 21065} 21066 21067let _materialId = 0; 21068 21069/** 21070 * Abstract base class for materials. 21071 * 21072 * Materials define the appearance of renderable 3D objects. 21073 * 21074 * @abstract 21075 * @augments EventDispatcher 21076 */ 21077class Material extends EventDispatcher { 21078 21079 /** 21080 * Constructs a new material. 21081 */ 21082 constructor() { 21083 21084 super(); 21085 21086 /** 21087 * This flag can be used for type testing. 21088 * 21089 * @type {boolean} 21090 * @readonly 21091 * @default true 21092 */ 21093 this.isMaterial = true; 21094 21095 /** 21096 * The ID of the material. 21097 * 21098 * @name Material#id 21099 * @type {number} 21100 * @readonly 21101 */ 21102 Object.defineProperty( this, 'id', { value: _materialId ++ } ); 21103 21104 /** 21105 * The UUID of the material. 21106 * 21107 * @type {string} 21108 * @readonly 21109 */ 21110 this.uuid = generateUUID(); 21111 21112 /** 21113 * The name of the material. 21114 * 21115 * @type {string} 21116 */ 21117 this.name = ''; 21118 21119 /** 21120 * The type property is used for detecting the object type 21121 * in context of serialization/deserialization. 21122 * 21123 * @type {string} 21124 * @readonly 21125 */ 21126 this.type = 'Material'; 21127 21128 /** 21129 * Defines the blending type of the material. 21130 * 21131 * It must be set to `CustomBlending` if custom blending properties like 21132 * {@link Material#blendSrc}, {@link Material#blendDst} or {@link Material#blendEquation} 21133 * should have any effect. 21134 * 21135 * @type {(NoBlending|NormalBlending|AdditiveBlending|SubtractiveBlending|MultiplyBlending|CustomBlending)} 21136 * @default NormalBlending 21137 */ 21138 this.blending = NormalBlending; 21139 21140 /** 21141 * Defines which side of faces will be rendered - front, back or both. 21142 * 21143 * @type {(FrontSide|BackSide|DoubleSide)} 21144 * @default FrontSide 21145 */ 21146 this.side = FrontSide; 21147 21148 /** 21149 * If set to `true`, vertex colors should be used. 21150 * 21151 * The engine supports RGB and RGBA vertex colors depending on whether a three (RGB) or 21152 * four (RGBA) component color buffer attribute is used. 21153 * 21154 * @type {boolean} 21155 * @default false 21156 */ 21157 this.vertexColors = false; 21158 21159 /** 21160 * Defines how transparent the material is. 21161 * A value of `0.0` indicates fully transparent, `1.0` is fully opaque. 21162 * 21163 * If the {@link Material#transparent} is not set to `true`, 21164 * the material will remain fully opaque and this value will only affect its color. 21165 * 21166 * @type {number} 21167 * @default 1 21168 */ 21169 this.opacity = 1; 21170 21171 /** 21172 * Defines whether this material is transparent. This has an effect on 21173 * rendering as transparent objects need special treatment and are rendered 21174 * after non-transparent objects. 21175 * 21176 * When set to true, the extent to which the material is transparent is 21177 * controlled by {@link Material#opacity}. 21178 * 21179 * @type {boolean} 21180 * @default false 21181 */ 21182 this.transparent = false; 21183 21184 /** 21185 * Enables alpha hashed transparency, an alternative to {@link Material#transparent} or 21186 * {@link Material#alphaTest}. The material will not be rendered if opacity is lower than 21187 * a random threshold. Randomization introduces some grain or noise, but approximates alpha 21188 * blending without the associated problems of sorting. Using TAA can reduce the resulting noise. 21189 * 21190 * @type {boolean} 21191 * @default false 21192 */ 21193 this.alphaHash = false; 21194 21195 /** 21196 * Defines the blending source factor. 21197 * 21198 * @type {(ZeroFactor|OneFactor|SrcColorFactor|OneMinusSrcColorFactor|SrcAlphaFactor|OneMinusSrcAlphaFactor|DstAlphaFactor|OneMinusDstAlphaFactor|DstColorFactor|OneMinusDstColorFactor|SrcAlphaSaturateFactor|ConstantColorFactor|OneMinusConstantColorFactor|ConstantAlphaFactor|OneMinusConstantAlphaFactor)} 21199 * @default SrcAlphaFactor 21200 */ 21201 this.blendSrc = SrcAlphaFactor; 21202 21203 /** 21204 * Defines the blending destination factor. 21205 * 21206 * @type {(ZeroFactor|OneFactor|SrcColorFactor|OneMinusSrcColorFactor|SrcAlphaFactor|OneMinusSrcAlphaFactor|DstAlphaFactor|OneMinusDstAlphaFactor|DstColorFactor|OneMinusDstColorFactor|SrcAlphaSaturateFactor|ConstantColorFactor|OneMinusConstantColorFactor|ConstantAlphaFactor|OneMinusConstantAlphaFactor)} 21207 * @default OneMinusSrcAlphaFactor 21208 */ 21209 this.blendDst = OneMinusSrcAlphaFactor; 21210 21211 /** 21212 * Defines the blending equation. 21213 * 21214 * @type {(AddEquation|SubtractEquation|ReverseSubtractEquation|MinEquation|MaxEquation)} 21215 * @default AddEquation 21216 */ 21217 this.blendEquation = AddEquation; 21218 21219 /** 21220 * Defines the blending source alpha factor. 21221 * 21222 * @type {?(ZeroFactor|OneFactor|SrcColorFactor|OneMinusSrcColorFactor|SrcAlphaFactor|OneMinusSrcAlphaFactor|DstAlphaFactor|OneMinusDstAlphaFactor|DstColorFactor|OneMinusDstColorFactor|SrcAlphaSaturateFactor|ConstantColorFactor|OneMinusConstantColorFactor|ConstantAlphaFactor|OneMinusConstantAlphaFactor)} 21223 * @default null 21224 */ 21225 this.blendSrcAlpha = null; 21226 21227 /** 21228 * Defines the blending destination alpha factor. 21229 * 21230 * @type {?(ZeroFactor|OneFactor|SrcColorFactor|OneMinusSrcColorFactor|SrcAlphaFactor|OneMinusSrcAlphaFactor|DstAlphaFactor|OneMinusDstAlphaFactor|DstColorFactor|OneMinusDstColorFactor|SrcAlphaSaturateFactor|ConstantColorFactor|OneMinusConstantColorFactor|ConstantAlphaFactor|OneMinusConstantAlphaFactor)} 21231 * @default null 21232 */ 21233 this.blendDstAlpha = null; 21234 21235 /** 21236 * Defines the blending equation of the alpha channel. 21237 * 21238 * @type {?(AddEquation|SubtractEquation|ReverseSubtractEquation|MinEquation|MaxEquation)} 21239 * @default null 21240 */ 21241 this.blendEquationAlpha = null; 21242 21243 /** 21244 * Represents the RGB values of the constant blend color. 21245 * 21246 * This property has only an effect when using custom blending with `ConstantColor` or `OneMinusConstantColor`. 21247 * 21248 * @type {Color} 21249 * @default (0,0,0) 21250 */ 21251 this.blendColor = new Color( 0, 0, 0 ); 21252 21253 /** 21254 * Represents the alpha value of the constant blend color. 21255 * 21256 * This property has only an effect when using custom blending with `ConstantAlpha` or `OneMinusConstantAlpha`. 21257 * 21258 * @type {number} 21259 * @default 0 21260 */ 21261 this.blendAlpha = 0; 21262 21263 /** 21264 * Defines the depth function. 21265 * 21266 * @type {(NeverDepth|AlwaysDepth|LessDepth|LessEqualDepth|EqualDepth|GreaterEqualDepth|GreaterDepth|NotEqualDepth)} 21267 * @default LessEqualDepth 21268 */ 21269 this.depthFunc = LessEqualDepth; 21270 21271 /** 21272 * Whether to have depth test enabled when rendering this material. 21273 * When the depth test is disabled, the depth write will also be implicitly disabled. 21274 * 21275 * @type {boolean} 21276 * @default true 21277 */ 21278 this.depthTest = true; 21279 21280 /** 21281 * Whether rendering this material has any effect on the depth buffer. 21282 * 21283 * When drawing 2D overlays it can be useful to disable the depth writing in 21284 * order to layer several things together without creating z-index artifacts. 21285 * 21286 * @type {boolean} 21287 * @default true 21288 */ 21289 this.depthWrite = true; 21290 21291 /** 21292 * The bit mask to use when writing to the stencil buffer. 21293 * 21294 * @type {number} 21295 * @default 0xff 21296 */ 21297 this.stencilWriteMask = 0xff; 21298 21299 /** 21300 * The stencil comparison function to use. 21301 * 21302 * @type {NeverStencilFunc|LessStencilFunc|EqualStencilFunc|LessEqualStencilFunc|GreaterStencilFunc|NotEqualStencilFunc|GreaterEqualStencilFunc|AlwaysStencilFunc} 21303 * @default AlwaysStencilFunc 21304 */ 21305 this.stencilFunc = AlwaysStencilFunc; 21306 21307 /** 21308 * The value to use when performing stencil comparisons or stencil operations. 21309 * 21310 * @type {number} 21311 * @default 0 21312 */ 21313 this.stencilRef = 0; 21314 21315 /** 21316 * The bit mask to use when comparing against the stencil buffer. 21317 * 21318 * @type {number} 21319 * @default 0xff 21320 */ 21321 this.stencilFuncMask = 0xff; 21322 21323 /** 21324 * Which stencil operation to perform when the comparison function returns `false`. 21325 * 21326 * @type {ZeroStencilOp|KeepStencilOp|ReplaceStencilOp|IncrementStencilOp|DecrementStencilOp|IncrementWrapStencilOp|DecrementWrapStencilOp|InvertStencilOp} 21327 * @default KeepStencilOp 21328 */ 21329 this.stencilFail = KeepStencilOp; 21330 21331 /** 21332 * Which stencil operation to perform when the comparison function returns 21333 * `true` but the depth test fails. 21334 * 21335 * @type {ZeroStencilOp|KeepStencilOp|ReplaceStencilOp|IncrementStencilOp|DecrementStencilOp|IncrementWrapStencilOp|DecrementWrapStencilOp|InvertStencilOp} 21336 * @default KeepStencilOp 21337 */ 21338 this.stencilZFail = KeepStencilOp; 21339 21340 /** 21341 * Which stencil operation to perform when the comparison function returns 21342 * `true` and the depth test passes. 21343 * 21344 * @type {ZeroStencilOp|KeepStencilOp|ReplaceStencilOp|IncrementStencilOp|DecrementStencilOp|IncrementWrapStencilOp|DecrementWrapStencilOp|InvertStencilOp} 21345 * @default KeepStencilOp 21346 */ 21347 this.stencilZPass = KeepStencilOp; 21348 21349 /** 21350 * Whether stencil operations are performed against the stencil buffer. In 21351 * order to perform writes or comparisons against the stencil buffer this 21352 * value must be `true`. 21353 * 21354 * @type {boolean} 21355 * @default false 21356 */ 21357 this.stencilWrite = false; 21358 21359 /** 21360 * User-defined clipping planes specified as THREE.Plane objects in world 21361 * space. These planes apply to the objects this material is attached to. 21362 * Points in space whose signed distance to the plane is negative are clipped 21363 * (not rendered). This requires {@link WebGLRenderer#localClippingEnabled} to 21364 * be `true`. 21365 * 21366 * @type {?Array<Plane>} 21367 * @default null 21368 */ 21369 this.clippingPlanes = null; 21370 21371 /** 21372 * Changes the behavior of clipping planes so that only their intersection is 21373 * clipped, rather than their union. 21374 * 21375 * @type {boolean} 21376 * @default false 21377 */ 21378 this.clipIntersection = false; 21379 21380 /** 21381 * Defines whether to clip shadows according to the clipping planes specified 21382 * on this material. 21383 * 21384 * @type {boolean} 21385 * @default false 21386 */ 21387 this.clipShadows = false; 21388 21389 /** 21390 * Defines which side of faces cast shadows. If `null`, the side casting shadows 21391 * is determined as follows: 21392 * 21393 * - When {@link Material#side} is set to `FrontSide`, the back side cast shadows. 21394 * - When {@link Material#side} is set to `BackSide`, the front side cast shadows. 21395 * - When {@link Material#side} is set to `DoubleSide`, both sides cast shadows. 21396 * 21397 * @type {?(FrontSide|BackSide|DoubleSide)} 21398 * @default null 21399 */ 21400 this.shadowSide = null; 21401 21402 /** 21403 * Whether to render the material's color. 21404 * 21405 * This can be used in conjunction with {@link Object3D#renderOder} to create invisible 21406 * objects that occlude other objects. 21407 * 21408 * @type {boolean} 21409 * @default true 21410 */ 21411 this.colorWrite = true; 21412 21413 /** 21414 * Override the renderer's default precision for this material. 21415 * 21416 * @type {?('highp'|'mediump'|'lowp')} 21417 * @default null 21418 */ 21419 this.precision = null; 21420 21421 /** 21422 * Whether to use polygon offset or not. When enabled, each fragment's depth value will 21423 * be offset after it is interpolated from the depth values of the appropriate vertices. 21424 * The offset is added before the depth test is performed and before the value is written 21425 * into the depth buffer. 21426 * 21427 * Can be useful for rendering hidden-line images, for applying decals to surfaces, and for 21428 * rendering solids with highlighted edges. 21429 * 21430 * @type {boolean} 21431 * @default false 21432 */ 21433 this.polygonOffset = false; 21434 21435 /** 21436 * Specifies a scale factor that is used to create a variable depth offset for each polygon. 21437 * 21438 * @type {number} 21439 * @default 0 21440 */ 21441 this.polygonOffsetFactor = 0; 21442 21443 /** 21444 * Is multiplied by an implementation-specific value to create a constant depth offset. 21445 * 21446 * @type {number} 21447 * @default 0 21448 */ 21449 this.polygonOffsetUnits = 0; 21450 21451 /** 21452 * Whether to apply dithering to the color to remove the appearance of banding. 21453 * 21454 * @type {boolean} 21455 * @default false 21456 */ 21457 this.dithering = false; 21458 21459 /** 21460 * Whether alpha to coverage should be enabled or not. Can only be used with MSAA-enabled contexts 21461 * (meaning when the renderer was created with *antialias* parameter set to `true`). Enabling this 21462 * will smooth aliasing on clip plane edges and alphaTest-clipped edges. 21463 * 21464 * @type {boolean} 21465 * @default false 21466 */ 21467 this.alphaToCoverage = false; 21468 21469 /** 21470 * Whether to premultiply the alpha (transparency) value. 21471 * 21472 * @type {boolean} 21473 * @default false 21474 */ 21475 this.premultipliedAlpha = false; 21476 21477 /** 21478 * Whether double-sided, transparent objects should be rendered with a single pass or not. 21479 * 21480 * The engine renders double-sided, transparent objects with two draw calls (back faces first, 21481 * then front faces) to mitigate transparency artifacts. There are scenarios however where this 21482 * approach produces no quality gains but still doubles draw calls e.g. when rendering flat 21483 * vegetation like grass sprites. In these cases, set the `forceSinglePass` flag to `true` to 21484 * disable the two pass rendering to avoid performance issues. 21485 * 21486 * @type {boolean} 21487 * @default false 21488 */ 21489 this.forceSinglePass = false; 21490 21491 /** 21492 * Whether it's possible to override the material with {@link Scene#overrideMaterial} or not. 21493 * 21494 * @type {boolean} 21495 * @default true 21496 */ 21497 this.allowOverride = true; 21498 21499 /** 21500 * Defines whether 3D objects using this material are visible. 21501 * 21502 * @type {boolean} 21503 * @default true 21504 */ 21505 this.visible = true; 21506 21507 /** 21508 * Defines whether this material is tone mapped according to the renderer's tone mapping setting. 21509 * 21510 * It is ignored when rendering to a render target or using post processing or when using 21511 * `WebGPURenderer`. In all these cases, all materials are honored by tone mapping. 21512 * 21513 * @type {boolean} 21514 * @default true 21515 */ 21516 this.toneMapped = true; 21517 21518 /** 21519 * An object that can be used to store custom data about the Material. It 21520 * should not hold references to functions as these will not be cloned. 21521 * 21522 * @type {Object} 21523 */ 21524 this.userData = {}; 21525 21526 /** 21527 * This starts at `0` and counts how many times {@link Material#needsUpdate} is set to `true`. 21528 * 21529 * @type {number} 21530 * @readonly 21531 * @default 0 21532 */ 21533 this.version = 0; 21534 21535 this._alphaTest = 0; 21536 21537 } 21538 21539 /** 21540 * Sets the alpha value to be used when running an alpha test. The material 21541 * will not be rendered if the opacity is lower than this value. 21542 * 21543 * @type {number} 21544 * @readonly 21545 * @default 0 21546 */ 21547 get alphaTest() { 21548 21549 return this._alphaTest; 21550 21551 } 21552 21553 set alphaTest( value ) { 21554 21555 if ( this._alphaTest > 0 !== value > 0 ) { 21556 21557 this.version ++; 21558 21559 } 21560 21561 this._alphaTest = value; 21562 21563 } 21564 21565 /** 21566 * An optional callback that is executed immediately before the material is used to render a 3D object. 21567 * 21568 * This method can only be used when rendering with {@link WebGLRenderer}. 21569 * 21570 * @param {WebGLRenderer} renderer - The renderer. 21571 * @param {Scene} scene - The scene. 21572 * @param {Camera} camera - The camera that is used to render the scene. 21573 * @param {BufferGeometry} geometry - The 3D object's geometry. 21574 * @param {Object3D} object - The 3D object. 21575 * @param {Object} group - The geometry group data. 21576 */ 21577 onBeforeRender( /* renderer, scene, camera, geometry, object, group */ ) {} 21578 21579 /** 21580 * An optional callback that is executed immediately before the shader 21581 * program is compiled. This function is called with the shader source code 21582 * as a parameter. Useful for the modification of built-in materials. 21583 * 21584 * This method can only be used when rendering with {@link WebGLRenderer}. The 21585 * recommended approach when customizing materials is to use `WebGPURenderer` with the new 21586 * Node Material system and [TSL](https://github.com/mrdoob/three.js/wiki/Three.js-Shading-Language). 21587 * 21588 * @param {{vertexShader:string,fragmentShader:string,uniforms:Object}} shaderobject - The object holds the uniforms and the vertex and fragment shader source. 21589 * @param {WebGLRenderer} renderer - A reference to the renderer. 21590 */ 21591 onBeforeCompile( /* shaderobject, renderer */ ) {} 21592 21593 /** 21594 * In case {@link Material#onBeforeCompile} is used, this callback can be used to identify 21595 * values of settings used in `onBeforeCompile()`, so three.js can reuse a cached 21596 * shader or recompile the shader for this material as needed. 21597 * 21598 * This method can only be used when rendering with {@link WebGLRenderer}. 21599 * 21600 * @return {string} The custom program cache key. 21601 */ 21602 customProgramCacheKey() { 21603 21604 return this.onBeforeCompile.toString(); 21605 21606 } 21607 21608 /** 21609 * This method can be used to set default values from parameter objects. 21610 * It is a generic implementation so it can be used with different types 21611 * of materials. 21612 * 21613 * @param {Object} [values] - The material values to set. 21614 */ 21615 setValues( values ) { 21616 21617 if ( values === undefined ) return; 21618 21619 for ( const key in values ) { 21620 21621 const newValue = values[ key ]; 21622 21623 if ( newValue === undefined ) { 21624 21625 warn( `Material: parameter '${ key }' has value of undefined.` ); 21626 continue; 21627 21628 } 21629 21630 const currentValue = this[ key ]; 21631 21632 if ( currentValue === undefined ) { 21633 21634 warn( `Material: '${ key }' is not a property of THREE.${ this.type }.` ); 21635 continue; 21636 21637 } 21638 21639 if ( currentValue && currentValue.isColor ) { 21640 21641 currentValue.set( newValue ); 21642 21643 } else if ( 21644 ( ( currentValue && currentValue.isVector2 ) && ( newValue && newValue.isVector2 ) ) || 21645 ( ( currentValue && currentValue.isEuler ) && ( newValue && newValue.isEuler ) ) || 21646 ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) 21647 ) { 21648 21649 currentValue.copy( newValue ); 21650 21651 } else { 21652 21653 this[ key ] = newValue; 21654 21655 } 21656 21657 } 21658 21659 } 21660 21661 /** 21662 * Serializes the material into JSON. 21663 * 21664 * @param {?(Object|string)} meta - An optional value holding meta information about the serialization. 21665 * @return {Object} A JSON object representing the serialized material. 21666 * @see {@link ObjectLoader#parse} 21667 */ 21668 toJSON( meta ) { 21669 21670 const isRootObject = ( meta === undefined || typeof meta === 'string' ); 21671 21672 if ( isRootObject ) { 21673 21674 meta = { 21675 textures: {}, 21676 images: {} 21677 }; 21678 21679 } 21680 21681 const data = { 21682 metadata: { 21683 version: 4.7, 21684 type: 'Material', 21685 generator: 'Material.toJSON' 21686 } 21687 }; 21688 21689 // standard Material serialization 21690 21691 data.uuid = this.uuid; 21692 data.type = this.type; 21693 21694 data.blending = this.blending; 21695 data.side = this.side; 21696 data.shadowSide = this.shadowSide; 21697 data.vertexColors = this.vertexColors; 21698 21699 data.opacity = this.opacity; 21700 data.transparent = this.transparent; 21701 21702 data.blendSrc = this.blendSrc; 21703 data.blendDst = this.blendDst; 21704 data.blendEquation = this.blendEquation; 21705 data.blendSrcAlpha = this.blendSrcAlpha; 21706 data.blendDstAlpha = this.blendDstAlpha; 21707 data.blendEquationAlpha = this.blendEquationAlpha; 21708 data.blendColor = this.blendColor.getHex(); 21709 data.blendAlpha = this.blendAlpha; 21710 21711 data.depthFunc = this.depthFunc; 21712 data.depthTest = this.depthTest; 21713 data.depthWrite = this.depthWrite; 21714 data.colorWrite = this.colorWrite; 21715 21716 data.clipIntersection = this.clipIntersection; 21717 data.clipShadows = this.clipShadows; 21718 21719 data.stencilWriteMask = this.stencilWriteMask; 21720 data.stencilFunc = this.stencilFunc; 21721 data.stencilRef = this.stencilRef; 21722 data.stencilFuncMask = this.stencilFuncMask; 21723 data.stencilFail = this.stencilFail; 21724 data.stencilZFail = this.stencilZFail; 21725 data.stencilZPass = this.stencilZPass; 21726 data.stencilWrite = this.stencilWrite; 21727 21728 data.polygonOffset = this.polygonOffset; 21729 data.polygonOffsetFactor = this.polygonOffsetFactor; 21730 data.polygonOffsetUnits = this.polygonOffsetUnits; 21731 21732 data.dithering = this.dithering; 21733 21734 data.alphaTest = this.alphaTest; 21735 data.alphaHash = this.alphaHash; 21736 data.alphaToCoverage = this.alphaToCoverage; 21737 data.premultipliedAlpha = this.premultipliedAlpha; 21738 data.forceSinglePass = this.forceSinglePass; 21739 data.allowOverride = this.allowOverride; 21740 21741 data.visible = this.visible; 21742 data.toneMapped = this.toneMapped; 21743 21744 data.name = this.name; 21745 21746 if ( this.color && this.color.isColor ) data.color = this.color.getHex(); 21747 21748 if ( this.roughness !== undefined ) data.roughness = this.roughness; 21749 if ( this.metalness !== undefined ) data.metalness = this.metalness; 21750 21751 if ( this.sheen !== undefined ) data.sheen = this.sheen; 21752 if ( this.sheenColor && this.sheenColor.isColor ) data.sheenColor = this.sheenColor.getHex(); 21753 if ( this.sheenRoughness !== undefined ) data.sheenRoughness = this.sheenRoughness; 21754 if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); 21755 if ( this.emissiveIntensity !== undefined ) data.emissiveIntensity = this.emissiveIntensity; 21756 21757 if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); 21758 if ( this.specularIntensity !== undefined ) data.specularIntensity = this.specularIntensity; 21759 if ( this.specularColor && this.specularColor.isColor ) data.specularColor = this.specularColor.getHex(); 21760 if ( this.shininess !== undefined ) data.shininess = this.shininess; 21761 if ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat; 21762 if ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness; 21763 21764 if ( this.clearcoatMap && this.clearcoatMap.isTexture ) { 21765 21766 data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid; 21767 21768 } 21769 21770 if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) { 21771 21772 data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid; 21773 21774 } 21775 21776 if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) { 21777 21778 data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid; 21779 data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); 21780 21781 } 21782 21783 if ( this.sheenColorMap && this.sheenColorMap.isTexture ) { 21784 21785 data.sheenColorMap = this.sheenColorMap.toJSON( meta ).uuid; 21786 21787 } 21788 21789 if ( this.sheenRoughnessMap && this.sheenRoughnessMap.isTexture ) { 21790 21791 data.sheenRoughnessMap = this.sheenRoughnessMap.toJSON( meta ).uuid; 21792 21793 } 21794 21795 if ( this.dispersion !== undefined ) data.dispersion = this.dispersion; 21796 if ( this.retroreflectivity !== undefined ) data.retroreflectivity = this.retroreflectivity; 21797 21798 if ( this.iridescence !== undefined ) data.iridescence = this.iridescence; 21799 if ( this.iridescenceIOR !== undefined ) data.iridescenceIOR = this.iridescenceIOR; 21800 if ( this.iridescenceThicknessRange !== undefined ) data.iridescenceThicknessRange = this.iridescenceThicknessRange; 21801 21802 if ( this.iridescenceMap && this.iridescenceMap.isTexture ) { 21803 21804 data.iridescenceMap = this.iridescenceMap.toJSON( meta ).uuid; 21805 21806 } 21807 21808 if ( this.iridescenceThicknessMap && this.iridescenceThicknessMap.isTexture ) { 21809 21810 data.iridescenceThicknessMap = this.iridescenceThicknessMap.toJSON( meta ).uuid; 21811 21812 } 21813 21814 if ( this.anisotropy !== undefined ) data.anisotropy = this.anisotropy; 21815 if ( this.anisotropyRotation !== undefined ) data.anisotropyRotation = this.anisotropyRotation; 21816 21817 if ( this.anisotropyMap && this.anisotropyMap.isTexture ) { 21818 21819 data.anisotropyMap = this.anisotropyMap.toJSON( meta ).uuid; 21820 21821 } 21822 21823 if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; 21824 if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid; 21825 if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; 21826 21827 if ( this.lightMap && this.lightMap.isTexture ) { 21828 21829 data.lightMap = this.lightMap.toJSON( meta ).uuid; 21830 data.lightMapIntensity = this.lightMapIntensity; 21831 21832 } 21833 21834 if ( this.aoMap && this.aoMap.isTexture ) { 21835 21836 data.aoMap = this.aoMap.toJSON( meta ).uuid; 21837 data.aoMapIntensity = this.aoMapIntensity; 21838 21839 } 21840 21841 if ( this.bumpMap && this.bumpMap.isTexture ) { 21842 21843 data.bumpMap = this.bumpMap.toJSON( meta ).uuid; 21844 data.bumpScale = this.bumpScale; 21845 21846 } 21847 21848 if ( this.normalMap && this.normalMap.isTexture ) { 21849 21850 data.normalMap = this.normalMap.toJSON( meta ).uuid; 21851 data.normalMapType = this.normalMapType; 21852 data.normalScale = this.normalScale.toArray(); 21853 21854 } 21855 21856 if ( this.displacementMap && this.displacementMap.isTexture ) { 21857 21858 data.displacementMap = this.displacementMap.toJSON( meta ).uuid; 21859 data.displacementScale = this.displacementScale; 21860 data.displacementBias = this.displacementBias; 21861 21862 } 21863 21864 if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; 21865 if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; 21866 21867 if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; 21868 if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; 21869 if ( this.specularIntensityMap && this.specularIntensityMap.isTexture ) data.specularIntensityMap = this.specularIntensityMap.toJSON( meta ).uuid; 21870 if ( this.specularColorMap && this.specularColorMap.isTexture ) data.specularColorMap = this.specularColorMap.toJSON( meta ).uuid; 21871 21872 if ( this.envMap && this.envMap.isTexture ) { 21873 21874 data.envMap = this.envMap.toJSON( meta ).uuid; 21875 21876 if ( this.combine !== undefined ) data.combine = this.combine; 21877 21878 } 21879 21880 if ( this.envMapRotation !== undefined ) data.envMapRotation = this.envMapRotation.toArray(); 21881 if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity; 21882 if ( this.reflectivity !== undefined ) data.reflectivity = this.reflectivity; 21883 if ( this.refractionRatio !== undefined ) data.refractionRatio = this.refractionRatio; 21884 21885 if ( this.gradientMap && this.gradientMap.isTexture ) { 21886 21887 data.gradientMap = this.gradientMap.toJSON( meta ).uuid; 21888 21889 } 21890 21891 if ( this.transmission !== undefined ) data.transmission = this.transmission; 21892 if ( this.transmissionMap && this.transmissionMap.isTexture ) data.transmissionMap = this.transmissionMap.toJSON( meta ).uuid; 21893 if ( this.thickness !== undefined ) data.thickness = this.thickness; 21894 if ( this.thicknessMap && this.thicknessMap.isTexture ) data.thicknessMap = this.thicknessMap.toJSON( meta ).uuid; 21895 if ( this.attenuationDistance !== undefined ) data.attenuationDistance = this.attenuationDistance; 21896 if ( this.attenuationColor !== undefined ) data.attenuationColor = this.attenuationColor.getHex(); 21897 21898 if ( this.size !== undefined ) data.size = this.size; 21899 if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; 21900 21901 if ( Array.isArray( this.clippingPlanes ) && this.clippingPlanes.length > 0 ) { 21902 21903 data.clippingPlanes = this.clippingPlanes.map( plane => plane.toJSON() ); 21904 21905 } 21906 21907 // rotation (SpriteMaterial) 21908 if ( this.rotation !== undefined ) data.rotation = this.rotation; 21909 21910 // depthPacking (MeshDepthMaterial) 21911 if ( this.depthPacking !== undefined ) data.depthPacking = this.depthPacking; 21912 21913 if ( this.linewidth !== undefined ) data.linewidth = this.linewidth; 21914 if ( this.linecap !== undefined ) data.linecap = this.linecap; 21915 if ( this.linejoin !== undefined ) data.linejoin = this.linejoin; 21916 if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; 21917 if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; 21918 if ( this.scale !== undefined ) data.scale = this.scale; 21919 21920 if ( this.wireframe !== undefined ) data.wireframe = this.wireframe; 21921 if ( this.wireframeLinewidth !== undefined ) data.wireframeLinewidth = this.wireframeLinewidth; 21922 if ( this.wireframeLinecap !== undefined ) data.wireframeLinecap = this.wireframeLinecap; 21923 if ( this.wireframeLinejoin !== undefined ) data.wireframeLinejoin = this.wireframeLinejoin; 21924 21925 if ( this.flatShading !== undefined ) data.flatShading = this.flatShading; 21926 21927 if ( this.fog !== undefined ) data.fog = this.fog; 21928 21929 if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; 21930 21931 // TODO: Copied from Object3D.toJSON 21932 21933 function extractFromCache( cache ) { 21934 21935 const values = []; 21936 21937 for ( const key in cache ) { 21938 21939 const data = cache[ key ]; 21940 delete data.metadata; 21941 values.push( data ); 21942 21943 } 21944 21945 return values; 21946 21947 } 21948 21949 if ( isRootObject ) { 21950 21951 const textures = extractFromCache( meta.textures ); 21952 const images = extractFromCache( meta.images ); 21953 21954 if ( textures.length > 0 ) data.textures = textures; 21955 if ( images.length > 0 ) data.images = images; 21956 21957 } 21958 21959 return data; 21960 21961 } 21962 21963 /** 21964 * Deserializes the material from the given JSON. 21965 * 21966 * @param {Object} json - The JSON holding the serialized material. 21967 * @param {Object<string,Texture>} textures - A dictionary holding textures referenced by the material. 21968 * @return {Material} A reference to this material. 21969 */ 21970 fromJSON( json, textures ) { 21971 21972 if ( json.uuid !== undefined ) this.uuid = json.uuid; 21973 if ( json.name !== undefined ) this.name = json.name; 21974 if ( json.color !== undefined && this.color !== undefined ) this.color.setHex( json.color ); 21975 if ( json.roughness !== undefined ) this.roughness = json.roughness; 21976 if ( json.metalness !== undefined ) this.metalness = json.metalness; 21977 if ( json.sheen !== undefined ) this.sheen = json.sheen; 21978 if ( json.sheenColor !== undefined ) this.sheenColor = new Color().setHex( json.sheenColor ); 21979 if ( json.sheenRoughness !== undefined ) this.sheenRoughness = json.sheenRoughness; 21980 if ( json.emissive !== undefined && this.emissive !== undefined ) this.emissive.setHex( json.emissive ); 21981 if ( json.specular !== undefined && this.specular !== undefined ) this.specular.setHex( json.specular ); 21982 if ( json.specularIntensity !== undefined ) this.specularIntensity = json.specularIntensity; 21983 if ( json.specularColor !== undefined && this.specularColor !== undefined ) this.specularColor.setHex( json.specularColor ); 21984 if ( json.shininess !== undefined ) this.shininess = json.shininess; 21985 if ( json.clearcoat !== undefined ) this.clearcoat = json.clearcoat; 21986 if ( json.clearcoatRoughness !== undefined ) this.clearcoatRoughness = json.clearcoatRoughness; 21987 if ( json.dispersion !== undefined ) this.dispersion = json.dispersion; 21988 if ( json.retroreflectivity !== undefined ) this.retroreflectivity = json.retroreflectivity; 21989 if ( json.iridescence !== undefined ) this.iridescence = json.iridescence; 21990 if ( json.iridescenceIOR !== undefined ) this.iridescenceIOR = json.iridescenceIOR; 21991 if ( json.iridescenceThicknessRange !== undefined ) this.iridescenceThicknessRange = json.iridescenceThicknessRange; 21992 if ( json.transmission !== undefined ) this.transmission = json.transmission; 21993 if ( json.thickness !== undefined ) this.thickness = json.thickness; 21994 if ( json.attenuationDistance !== undefined ) this.attenuationDistance = json.attenuationDistance; 21995 if ( json.attenuationColor !== undefined && this.attenuationColor !== undefined ) this.attenuationColor.setHex( json.attenuationColor ); 21996 if ( json.anisotropy !== undefined ) this.anisotropy = json.anisotropy; 21997 if ( json.anisotropyRotation !== undefined ) this.anisotropyRotation = json.anisotropyRotation; 21998 if ( json.fog !== undefined ) this.fog = json.fog; 21999 if ( json.flatShading !== undefined ) this.flatShading = json.flatShading; 22000 if ( json.blending !== undefined ) this.blending = json.blending; 22001 if ( json.combine !== undefined ) this.combine = json.combine; 22002 if ( json.side !== undefined ) this.side = json.side; 22003 if ( json.shadowSide !== undefined ) this.shadowSide = json.shadowSide; 22004 if ( json.opacity !== undefined ) this.opacity = json.opacity; 22005 if ( json.transparent !== undefined ) this.transparent = json.transparent; 22006 if ( json.alphaTest !== undefined ) this.alphaTest = json.alphaTest; 22007 if ( json.alphaHash !== undefined ) this.alphaHash = json.alphaHash; 22008 if ( json.depthFunc !== undefined ) this.depthFunc = json.depthFunc; 22009 if ( json.depthTest !== undefined ) this.depthTest = json.depthTest; 22010 if ( json.depthWrite !== undefined ) this.depthWrite = json.depthWrite; 22011 if ( json.colorWrite !== undefined ) this.colorWrite = json.colorWrite; 22012 if ( json.clippingPlanes !== undefined ) this.clippingPlanes = json.clippingPlanes.map( plane => new Plane().fromJSON( plane ) ); 22013 if ( json.clipIntersection !== undefined ) this.clipIntersection = json.clipIntersection; 22014 if ( json.clipShadows !== undefined ) this.clipShadows = json.clipShadows; 22015 if ( json.depthPacking !== undefined ) this.depthPacking = json.depthPacking; 22016 if ( json.blendSrc !== undefined ) this.blendSrc = json.blendSrc; 22017 if ( json.blendDst !== undefined ) this.blendDst = json.blendDst; 22018 if ( json.blendEquation !== undefined ) this.blendEquation = json.blendEquation; 22019 if ( json.blendSrcAlpha !== undefined ) this.blendSrcAlpha = json.blendSrcAlpha; 22020 if ( json.blendDstAlpha !== undefined ) this.blendDstAlpha = json.blendDstAlpha; 22021 if ( json.blendEquationAlpha !== undefined ) this.blendEquationAlpha = json.blendEquationAlpha; 22022 if ( json.blendColor !== undefined && this.blendColor !== undefined ) this.blendColor.setHex( json.blendColor ); 22023 if ( json.blendAlpha !== undefined ) this.blendAlpha = json.blendAlpha; 22024 if ( json.stencilWriteMask !== undefined ) this.stencilWriteMask = json.stencilWriteMask; 22025 if ( json.stencilFunc !== undefined ) this.stencilFunc = json.stencilFunc; 22026 if ( json.stencilRef !== undefined ) this.stencilRef = json.stencilRef; 22027 if ( json.stencilFuncMask !== undefined ) this.stencilFuncMask = json.stencilFuncMask; 22028 if ( json.stencilFail !== undefined ) this.stencilFail = json.stencilFail; 22029 if ( json.stencilZFail !== undefined ) this.stencilZFail = json.stencilZFail; 22030 if ( json.stencilZPass !== undefined ) this.stencilZPass = json.stencilZPass; 22031 if ( json.stencilWrite !== undefined ) this.stencilWrite = json.stencilWrite; 22032 22033 if ( json.wireframe !== undefined ) this.wireframe = json.wireframe; 22034 if ( json.wireframeLinewidth !== undefined ) this.wireframeLinewidth = json.wireframeLinewidth; 22035 if ( json.wireframeLinecap !== undefined ) this.wireframeLinecap = json.wireframeLinecap; 22036 if ( json.wireframeLinejoin !== undefined ) this.wireframeLinejoin = json.wireframeLinejoin; 22037 22038 if ( json.rotation !== undefined ) this.rotation = json.rotation; 22039 22040 if ( json.linewidth !== undefined ) this.linewidth = json.linewidth; 22041 if ( json.linecap !== undefined ) this.linecap = json.linecap; 22042 if ( json.linejoin !== undefined ) this.linejoin = json.linejoin; 22043 if ( json.dashSize !== undefined ) this.dashSize = json.dashSize; 22044 if ( json.gapSize !== undefined ) this.gapSize = json.gapSize; 22045 if ( json.scale !== undefined ) this.scale = json.scale; 22046 22047 if ( json.polygonOffset !== undefined ) this.polygonOffset = json.polygonOffset; 22048 if ( json.polygonOffsetFactor !== undefined ) this.polygonOffsetFactor = json.polygonOffsetFactor; 22049 if ( json.polygonOffsetUnits !== undefined ) this.polygonOffsetUnits = json.polygonOffsetUnits; 22050 22051 if ( json.dithering !== undefined ) this.dithering = json.dithering; 22052 22053 if ( json.alphaToCoverage !== undefined ) this.alphaToCoverage = json.alphaToCoverage; 22054 if ( json.premultipliedAlpha !== undefined ) this.premultipliedAlpha = json.premultipliedAlpha; 22055 if ( json.forceSinglePass !== undefined ) this.forceSinglePass = json.forceSinglePass; 22056 if ( json.allowOverride !== undefined ) this.allowOverride = json.allowOverride; 22057 22058 if ( json.visible !== undefined ) this.visible = json.visible; 22059 22060 if ( json.toneMapped !== undefined ) this.toneMapped = json.toneMapped; 22061 22062 if ( json.userData !== undefined ) this.userData = json.userData; 22063 22064 if ( json.vertexColors !== undefined ) { 22065 22066 if ( typeof json.vertexColors === 'number' ) { 22067 22068 this.vertexColors = json.vertexColors > 0; 22069 22070 } else { 22071 22072 this.vertexColors = json.vertexColors; 22073 22074 } 22075 22076 } 22077 22078 // for PointsMaterial 22079 22080 if ( json.size !== undefined ) this.size = json.size; 22081 if ( json.sizeAttenuation !== undefined ) this.sizeAttenuation = json.sizeAttenuation; 22082 22083 // maps 22084 22085 if ( json.map !== undefined ) this.map = textures[ json.map ] || null; 22086 if ( json.matcap !== undefined ) this.matcap = textures[ json.matcap ] || null; 22087 22088 if ( json.alphaMap !== undefined ) this.alphaMap = textures[ json.alphaMap ] || null; 22089 22090 if ( json.bumpMap !== undefined ) this.bumpMap = textures[ json.bumpMap ] || null; 22091 if ( json.bumpScale !== undefined ) this.bumpScale = json.bumpScale; 22092 22093 if ( json.normalMap !== undefined ) this.normalMap = textures[ json.normalMap ] || null; 22094 if ( json.normalMapType !== undefined ) this.normalMapType = json.normalMapType; 22095 if ( json.normalScale !== undefined ) { 22096 22097 let normalScale = json.normalScale; 22098 22099 if ( Array.isArray( normalScale ) === false ) { 22100 22101 // Blender exporter used to export a scalar. See #7459 22102 22103 normalScale = [ normalScale, normalScale ]; 22104 22105 } 22106 22107 this.normalScale = new Vector2().fromArray( normalScale ); 22108 22109 } 22110 22111 if ( json.displacementMap !== undefined ) this.displacementMap = textures[ json.displacementMap ] || null; 22112 if ( json.displacementScale !== undefined ) this.displacementScale = json.displacementScale; 22113 if ( json.displacementBias !== undefined ) this.displacementBias = json.displacementBias; 22114 22115 if ( json.roughnessMap !== undefined ) this.roughnessMap = textures[ json.roughnessMap ] || null; 22116 if ( json.metalnessMap !== undefined ) this.metalnessMap = textures[ json.metalnessMap ] || null; 22117 22118 if ( json.emissiveMap !== undefined ) this.emissiveMap = textures[ json.emissiveMap ] || null; 22119 if ( json.emissiveIntensity !== undefined ) this.emissiveIntensity = json.emissiveIntensity; 22120 22121 if ( json.specularMap !== undefined ) this.specularMap = textures[ json.specularMap ] || null; 22122 if ( json.specularIntensityMap !== undefined ) this.specularIntensityMap = textures[ json.specularIntensityMap ] || null; 22123 if ( json.specularColorMap !== undefined ) this.specularColorMap = textures[ json.specularColorMap ] || null; 22124 22125 if ( json.envMap !== undefined ) this.envMap = textures[ json.envMap ] || null; 22126 if ( json.envMapRotation !== undefined ) this.envMapRotation.fromArray( json.envMapRotation ); 22127 if ( json.envMapIntensity !== undefined ) this.envMapIntensity = json.envMapIntensity; 22128 22129 if ( json.reflectivity !== undefined ) this.reflectivity = json.reflectivity; 22130 if ( json.refractionRatio !== undefined ) this.refractionRatio = json.refractionRatio; 22131 22132 if ( json.lightMap !== undefined ) this.lightMap = textures[ json.lightMap ] || null; 22133 if ( json.lightMapIntensity !== undefined ) this.lightMapIntensity = json.lightMapIntensity; 22134 22135 if ( json.aoMap !== undefined ) this.aoMap = textures[ json.aoMap ] || null; 22136 if ( json.aoMapIntensity !== undefined ) this.aoMapIntensity = json.aoMapIntensity; 22137 22138 if ( json.gradientMap !== undefined ) this.gradientMap = textures[ json.gradientMap ] || null; 22139 22140 if ( json.clearcoatMap !== undefined ) this.clearcoatMap = textures[ json.clearcoatMap ] || null; 22141 if ( json.clearcoatRoughnessMap !== undefined ) this.clearcoatRoughnessMap = textures[ json.clearcoatRoughnessMap ] || null; 22142 if ( json.clearcoatNormalMap !== undefined ) this.clearcoatNormalMap = textures[ json.clearcoatNormalMap ] || null; 22143 if ( json.clearcoatNormalScale !== undefined ) this.clearcoatNormalScale = new Vector2().fromArray( json.clearcoatNormalScale ); 22144 22145 if ( json.iridescenceMap !== undefined ) this.iridescenceMap = textures[ json.iridescenceMap ] || null; 22146 if ( json.iridescenceThicknessMap !== undefined ) this.iridescenceThicknessMap = textures[ json.iridescenceThicknessMap ] || null; 22147 22148 if ( json.transmissionMap !== undefined ) this.transmissionMap = textures[ json.transmissionMap ] || null; 22149 if ( json.thicknessMap !== undefined ) this.thicknessMap = textures[ json.thicknessMap ] || null; 22150 22151 if ( json.anisotropyMap !== undefined ) this.anisotropyMap = textures[ json.anisotropyMap ] || null; 22152 22153 if ( json.sheenColorMap !== undefined ) this.sheenColorMap = textures[ json.sheenColorMap ] || null; 22154 if ( json.sheenRoughnessMap !== undefined ) this.sheenRoughnessMap = textures[ json.sheenRoughnessMap ] || null; 22155 22156 return this; 22157 22158 } 22159 22160 /** 22161 * Returns a new material with copied values from this instance. 22162 * 22163 * @return {Material} A clone of this instance. 22164 */ 22165 clone() { 22166 22167 return new this.constructor().copy( this ); 22168 22169 } 22170 22171 /** 22172 * Copies the values of the given material to this instance. 22173 * 22174 * @param {Material} source - The material to copy. 22175 * @return {Material} A reference to this instance. 22176 */ 22177 copy( source ) { 22178 22179 this.name = source.name; 22180 22181 this.blending = source.blending; 22182 this.side = source.side; 22183 this.vertexColors = source.vertexColors; 22184 22185 this.opacity = source.opacity; 22186 this.transparent = source.transparent; 22187 22188 this.blendSrc = source.blendSrc; 22189 this.blendDst = source.blendDst; 22190 this.blendEquation = source.blendEquation; 22191 this.blendSrcAlpha = source.blendSrcAlpha; 22192 this.blendDstAlpha = source.blendDstAlpha; 22193 this.blendEquationAlpha = source.blendEquationAlpha; 22194 this.blendColor.copy( source.blendColor ); 22195 this.blendAlpha = source.blendAlpha; 22196 22197 this.depthFunc = source.depthFunc; 22198 this.depthTest = source.depthTest; 22199 this.depthWrite = source.depthWrite; 22200 22201 this.stencilWriteMask = source.stencilWriteMask; 22202 this.stencilFunc = source.stencilFunc; 22203 this.stencilRef = source.stencilRef; 22204 this.stencilFuncMask = source.stencilFuncMask; 22205 this.stencilFail = source.stencilFail; 22206 this.stencilZFail = source.stencilZFail; 22207 this.stencilZPass = source.stencilZPass; 22208 this.stencilWrite = source.stencilWrite; 22209 22210 const srcPlanes = source.clippingPlanes; 22211 let dstPlanes = null; 22212 22213 if ( srcPlanes !== null ) { 22214 22215 const n = srcPlanes.length; 22216 dstPlanes = new Array( n ); 22217 22218 for ( let i = 0; i !== n; ++ i ) { 22219 22220 dstPlanes[ i ] = srcPlanes[ i ].clone(); 22221 22222 } 22223 22224 } 22225 22226 this.clippingPlanes = dstPlanes; 22227 this.clipIntersection = source.clipIntersection; 22228 this.clipShadows = source.clipShadows; 22229 22230 this.shadowSide = source.shadowSide; 22231 22232 this.colorWrite = source.colorWrite; 22233 22234 this.precision = source.precision; 22235 22236 this.polygonOffset = source.polygonOffset; 22237 this.polygonOffsetFactor = source.polygonOffsetFactor; 22238 this.polygonOffsetUnits = source.polygonOffsetUnits; 22239 22240 this.dithering = source.dithering; 22241 22242 this.alphaTest = source.alphaTest; 22243 this.alphaHash = source.alphaHash; 22244 this.alphaToCoverage = source.alphaToCoverage; 22245 this.premultipliedAlpha = source.premultipliedAlpha; 22246 this.forceSinglePass = source.forceSinglePass; 22247 this.allowOverride = source.allowOverride; 22248 22249 this.visible = source.visible; 22250 22251 this.toneMapped = source.toneMapped; 22252 22253 this.userData = JSON.parse( JSON.stringify( source.userData ) ); 22254 22255 return this; 22256 22257 } 22258 22259 /** 22260 * Frees the GPU-related resources allocated by this instance. Call this 22261 * method whenever this instance is no longer used in your app. 22262 * 22263 * @fires Material#dispose 22264 */ 22265 dispose() { 22266 22267 /** 22268 * Fires when the material has been disposed of. 22269 * 22270 * @event Material#dispose 22271 * @type {Object} 22272 */ 22273 this.dispatchEvent( { type: 'dispose' } ); 22274 22275 } 22276 22277 /** 22278 * Setting this property to `true` indicates the engine the material 22279 * needs to be recompiled. 22280 * 22281 * @type {boolean} 22282 * @default false 22283 * @param {boolean} value 22284 */ 22285 set needsUpdate( value ) { 22286 22287 if ( value === true ) this.version ++; 22288 22289 } 22290 22291} 22292 22293/** 22294 * A material for rendering instances of {@link Sprite}. 22295 * 22296 * ```js 22297 * const map = new THREE.TextureLoader().load( 'textures/sprite.png' ); 22298 * const material = new THREE.SpriteMaterial( { map: map, color: 0xffffff } ); 22299 * 22300 * const sprite = new THREE.Sprite( material ); 22301 * sprite.scale.set(200, 200, 1) 22302 * scene.add( sprite ); 22303 * ``` 22304 * 22305 * @augments Material 22306 */ 22307class SpriteMaterial extends Material { 22308 22309 /** 22310 * Constructs a new sprite material. 22311 * 22312 * @param {Object} [parameters] - An object with one or more properties 22313 * defining the material's appearance. Any property of the material 22314 * (including any property from inherited materials) can be passed 22315 * in here. Color values can be passed any type of value accepted 22316 * by {@link Color#set}. 22317 */ 22318 constructor( parameters ) { 22319 22320 super(); 22321 22322 /** 22323 * This flag can be used for type testing. 22324 * 22325 * @type {boolean} 22326 * @readonly 22327 * @default true 22328 */ 22329 this.isSpriteMaterial = true; 22330 22331 this.type = 'SpriteMaterial'; 22332 22333 /** 22334 * Color of the material. 22335 * 22336 * @type {Color} 22337 * @default (1,1,1) 22338 */ 22339 this.color = new Color( 0xffffff ); 22340 22341 /** 22342 * The color map. May optionally include an alpha channel, typically combined 22343 * with {@link Material#transparent} or {@link Material#alphaTest}. The texture map 22344 * color is modulated by the diffuse `color`. 22345 * 22346 * `map` represents color data, and the texture must be assigned a 22347 * {@link Texture#colorSpace}. Most `map` textures set 22348 * `texture.colorSpace = SRGBColorSpace`. 22349 * 22350 * @type {?Texture} 22351 * @default null 22352 */ 22353 this.map = null; 22354 22355 /** 22356 * The alpha map is a grayscale texture that controls the opacity across the 22357 * surface (black: fully transparent; white: fully opaque). 22358 * 22359 * Only the color of the texture is used, ignoring the alpha channel if one 22360 * exists. For RGB and RGBA textures, the renderer will use the green channel 22361 * when sampling this texture due to the extra bit of precision provided for 22362 * green in DXT-compressed and uncompressed RGB 565 formats. Luminance-only and 22363 * luminance/alpha textures will also still work as expected. 22364 * 22365 * `alphaMap` represents non-color data. Any texture assigned must have 22366 * `texture.colorSpace = NoColorSpace` (default). 22367 * 22368 * @type {?Texture} 22369 * @default null 22370 */ 22371 this.alphaMap = null; 22372 22373 /** 22374 * The rotation of the sprite in radians. 22375 * 22376 * @type {number} 22377 * @default 0 22378 */ 22379 this.rotation = 0; 22380 22381 /** 22382 * Specifies whether size of the sprite is attenuated by the camera depth (perspective camera only). 22383 * 22384 * @type {boolean} 22385 * @default true 22386 */ 22387 this.sizeAttenuation = true; 22388 22389 /** 22390 * Overwritten since sprite materials are transparent 22391 * by default. 22392 * 22393 * @type {boolean} 22394 * @default true 22395 */ 22396 this.transparent = true; 22397 22398 /** 22399 * Whether the material is affected by fog or not. 22400 * 22401 * @type {boolean} 22402 * @default true 22403 */ 22404 this.fog = true; 22405 22406 this.setValues( parameters ); 22407 22408 } 22409 22410 copy( source ) { 22411 22412 super.copy( source ); 22413 22414 this.color.copy( source.color ); 22415 22416 this.map = source.map; 22417 22418 this.alphaMap = source.alphaMap; 22419 22420 this.rotation = source.rotation; 22421 22422 this.sizeAttenuation = source.sizeAttenuation; 22423 22424 this.fog = source.fog; 22425 22426 return this; 22427 22428 } 22429 22430} 22431 22432let _geometry; 22433 22434const _intersectPoint = /*@__PURE__*/ new Vector3(); 22435const _worldScale = /*@__PURE__*/ new Vector3(); 22436const _mvPosition = /*@__PURE__*/ new Vector3(); 22437 22438const _alignedPosition = /*@__PURE__*/ new Vector2(); 22439const _rotatedPosition = /*@__PURE__*/ new Vector2(); 22440const _viewWorldMatrix = /*@__PURE__*/ new Matrix4(); 22441 22442const _vA$1 = /*@__PURE__*/ new Vector3(); 22443const _vB$1 = /*@__PURE__*/ new Vector3(); 22444const _vC$1 = /*@__PURE__*/ new Vector3(); 22445 22446const _uvA = /*@__PURE__*/ new Vector2(); 22447const _uvB = /*@__PURE__*/ new Vector2(); 22448const _uvC = /*@__PURE__*/ new Vector2(); 22449 22450/** 22451 * A sprite is a plane that always faces towards the camera, generally with a 22452 * partially transparent texture applied. 22453 * 22454 * Sprites do not cast shadows, setting {@link Object3D#castShadow} to `true` will 22455 * have no effect. 22456 * 22457 * ```js 22458 * const map = new THREE.TextureLoader().load( 'sprite.png' ); 22459 * const material = new THREE.SpriteMaterial( { map: map } ); 22460 * 22461 * const sprite = new THREE.Sprite( material ); 22462 * scene.add( sprite ); 22463 * ``` 22464 * 22465 * @augments Object3D 22466 */ 22467class Sprite extends Object3D { 22468 22469 /** 22470 * Constructs a new sprite. 22471 * 22472 * @param {(SpriteMaterial|SpriteNodeMaterial)} [material] - The sprite material. 22473 */ 22474 constructor( material = new SpriteMaterial() ) { 22475 22476 super(); 22477 22478 /** 22479 * This flag can be used for type testing. 22480 * 22481 * @type {boolean} 22482 * @readonly 22483 * @default true 22484 */ 22485 this.isSprite = true; 22486 22487 this.type = 'Sprite'; 22488 22489 if ( _geometry === undefined ) { 22490 22491 _geometry = new BufferGeometry(); 22492 22493 const float32Array = new Float32Array( [ 22494 -0.5, -0.5, 0, 0, 0, 22495 0.5, -0.5, 0, 1, 0, 22496 0.5, 0.5, 0, 1, 1, 22497 -0.5, 0.5, 0, 0, 1 22498 ] ); 22499 22500 const interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); 22501 22502 _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); 22503 _geometry.setAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); 22504 _geometry.setAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); 22505 22506 } 22507 22508 /** 22509 * The sprite geometry. 22510 * 22511 * @type {BufferGeometry} 22512 */ 22513 this.geometry = _geometry; 22514 22515 /** 22516 * The sprite material. 22517 * 22518 * @type {(SpriteMaterial|SpriteNodeMaterial)} 22519 */ 22520 this.material = material; 22521 22522 /** 22523 * The sprite's anchor point, and the point around which the sprite rotates. 22524 * A value of `(0.5, 0.5)` corresponds to the midpoint of the sprite. A value 22525 * of `(0, 0)` corresponds to the lower left corner of the sprite. 22526 * 22527 * @type {Vector2} 22528 * @default (0.5,0.5) 22529 */ 22530 this.center = new Vector2( 0.5, 0.5 ); 22531 22532 /** 22533 * The number of instances of this sprite. 22534 * Can only be used with {@link WebGPURenderer}. 22535 * 22536 * @type {number} 22537 * @default 1 22538 */ 22539 this.count = 1; 22540 22541 } 22542 22543 /** 22544 * Returns `true` if this sprite intersects the given frustum. 22545 * 22546 * @param {Frustum|FrustumArray} frustum - The frustum to test. 22547 * @return {boolean} Whether this sprite intersects the given frustum or not. 22548 */ 22549 intersectsFrustum( frustum ) { 22550 22551 return frustum.intersectsSprite( this ); 22552 22553 } 22554 22555 /** 22556 * Computes intersection points between a casted ray and this sprite. 22557 * 22558 * @param {Raycaster} raycaster - The raycaster. 22559 * @param {Array<Object>} intersects - The target array that holds the intersection points. 22560 */ 22561 raycast( raycaster, intersects ) { 22562 22563 if ( raycaster.camera === null ) { 22564 22565 error( 'Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.' ); 22566 22567 } 22568 22569 _worldScale.setFromMatrixScale( this.matrixWorld ); 22570 22571 _viewWorldMatrix.copy( raycaster.camera.matrixWorld ); 22572 this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld ); 22573 22574 _mvPosition.setFromMatrixPosition( this.modelViewMatrix ); 22575 22576 if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { 22577 22578 _worldScale.multiplyScalar( - _mvPosition.z ); 22579 22580 } 22581 22582 const rotation = this.material.rotation; 22583 let sin, cos; 22584 22585 if ( rotation !== 0 ) { 22586 22587 cos = Math.cos( rotation ); 22588 sin = Math.sin( rotation ); 22589 22590 } 22591 22592 const center = this.center; 22593 22594 transformVertex( _vA$1.set( -0.5, -0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); 22595 transformVertex( _vB$1.set( 0.5, -0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); 22596 transformVertex( _vC$1.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); 22597 22598 _uvA.set( 0, 0 ); 22599 _uvB.set( 1, 0 ); 22600 _uvC.set( 1, 1 ); 22601 22602 // check first triangle 22603 let intersect = raycaster.ray.intersectTriangle( _vA$1, _vB$1, _vC$1, false, _intersectPoint ); 22604 22605 if ( intersect === null ) { 22606 22607 // check second triangle 22608 transformVertex( _vB$1.set( -0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); 22609 _uvB.set( 0, 1 ); 22610 22611 intersect = raycaster.ray.intersectTriangle( _vA$1, _vC$1, _vB$1, false, _intersectPoint ); 22612 if ( intersect === null ) { 22613 22614 return; 22615 22616 } 22617 22618 } 22619 22620 const distance = raycaster.ray.origin.distanceTo( _intersectPoint ); 22621 22622 if ( distance < raycaster.near || distance > raycaster.far ) return; 22623 22624 intersects.push( { 22625 22626 distance: distance, 22627 point: _intersectPoint.clone(), 22628 uv: Triangle.getInterpolation( _intersectPoint, _vA$1, _vB$1, _vC$1, _uvA, _uvB, _uvC, new Vector2() ), 22629 face: null, 22630 object: this 22631 22632 } ); 22633 22634 } 22635 22636 copy( source, recursive ) { 22637 22638 super.copy( source, recursive ); 22639 22640 if ( source.center !== undefined ) this.center.copy( source.center ); 22641 22642 this.material = source.material; 22643 22644 return this; 22645 22646 } 22647 22648} 22649 22650function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { 22651 22652 // compute position in camera space 22653 _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); 22654 22655 // to check if rotation is not zero 22656 if ( sin !== undefined ) { 22657 22658 _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y ); 22659 _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y ); 22660 22661 } else { 22662 22663 _rotatedPosition.copy( _alignedPosition ); 22664 22665 } 22666 22667 22668 vertexPosition.copy( mvPosition ); 22669 vertexPosition.x += _rotatedPosition.x; 22670 vertexPosition.y += _rotatedPosition.y; 22671 22672 // transform to world space 22673 vertexPosition.applyMatrix4( _viewWorldMatrix ); 22674 22675} 22676 22677const _v1$2 = /*@__PURE__*/ new Vector3(); 22678const _v2$1 = /*@__PURE__*/ new Vector3(); 22679 22680/** 22681 * A component for providing a basic Level of Detail (LOD) mechanism. 22682 * 22683 * Every LOD level is associated with an object, and rendering can be switched 22684 * between them at the distances specified. Typically you would create, say, 22685 * three meshes, one for far away (low detail), one for mid range (medium 22686 * detail) and one for close up (high detail). 22687 * 22688 * ```js 22689 * const lod = new THREE.LOD(); 22690 * const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); 22691 * 22692 * //Create spheres with 3 levels of detail and create new LOD levels for them 22693 * for( let i = 0; i < 3; i++ ) { 22694 * 22695 * const geometry = new THREE.IcosahedronGeometry( 10, 3 - i ); 22696 * const mesh = new THREE.Mesh( geometry, material ); 22697 * lod.addLevel( mesh, i * 75 ); 22698 * 22699 * } 22700 * 22701 * scene.add( lod ); 22702 * ``` 22703 * 22704 * @augments Object3D 22705 */ 22706class LOD extends Object3D { 22707 22708 /** 22709 * Constructs a new LOD. 22710 */ 22711 constructor() { 22712 22713 super(); 22714 22715 /** 22716 * This flag can be used for type testing. 22717 * 22718 * @type {boolean} 22719 * @readonly 22720 * @default true 22721 */ 22722 this.isLOD = true; 22723 22724 /** 22725 * The current LOD index. 22726 * 22727 * @private 22728 * @type {number} 22729 * @default 0 22730 */ 22731 this._currentLevel = 0; 22732 22733 this.type = 'LOD'; 22734 22735 Object.defineProperties( this, { 22736 /** 22737 * This array holds the LOD levels. 22738 * 22739 * @name LOD#levels 22740 * @type {Array<{object:Object3D,distance:number,hysteresis:number}>} 22741 */ 22742 levels: { 22743 enumerable: true, 22744 value: [] 22745 } 22746 } ); 22747 22748 /** 22749 * Whether the LOD object is updated automatically by the renderer per frame 22750 * or not. If set to `false`, you have to call {@link LOD#update} in the 22751 * render loop by yourself. 22752 * 22753 * @type {boolean} 22754 * @default true 22755 */ 22756 this.autoUpdate = true; 22757 22758 } 22759 22760 copy( source ) { 22761 22762 super.copy( source, false ); 22763 22764 const levels = source.levels; 22765 22766 for ( let i = 0, l = levels.length; i < l; i ++ ) { 22767 22768 const level = levels[ i ]; 22769 22770 this.addLevel( level.object.clone(), level.distance, level.hysteresis ); 22771 22772 } 22773 22774 this.autoUpdate = source.autoUpdate; 22775 22776 return this; 22777 22778 } 22779 22780 /** 22781 * Adds a mesh that will display at a certain distance and greater. Typically 22782 * the further away the distance, the lower the detail on the mesh. 22783 * 22784 * @param {Object3D} object - The 3D object to display at this level. 22785 * @param {number} [distance=0] - The distance at which to display this level of detail. 22786 * @param {number} [hysteresis=0] - Threshold used to avoid flickering at LOD boundaries, as a fraction of distance. 22787 * @return {LOD} A reference to this instance. 22788 */ 22789 addLevel( object, distance = 0, hysteresis = 0 ) { 22790 22791 distance = Math.abs( distance ); 22792 22793 const levels = this.levels; 22794 22795 let l; 22796 22797 for ( l = 0; l < levels.length; l ++ ) { 22798 22799 if ( distance < levels[ l ].distance ) { 22800 22801 break; 22802 22803 } 22804 22805 } 22806 22807 levels.splice( l, 0, { distance: distance, hysteresis: hysteresis, object: object } ); 22808 22809 this.add( object ); 22810 22811 return this; 22812 22813 } 22814 22815 /** 22816 * Removes an existing level, based on the distance from the camera. 22817 * Returns `true` when the level has been removed. Otherwise `false`. 22818 * 22819 * @param {number} distance - Distance of the level to remove. 22820 * @return {boolean} Whether the level has been removed or not. 22821 */ 22822 removeLevel( distance ) { 22823 22824 const levels = this.levels; 22825 22826 for ( let i = 0; i < levels.length; i ++ ) { 22827 22828 if ( levels[ i ].distance === distance ) { 22829 22830 const removedElements = levels.splice( i, 1 ); 22831 this.remove( removedElements[ 0 ].object ); 22832 22833 return true; 22834 22835 } 22836 22837 } 22838 22839 return false; 22840 22841 } 22842 22843 /** 22844 * Returns the currently active LOD level index. 22845 * 22846 * @return {number} The current active LOD level index. 22847 */ 22848 getCurrentLevel() { 22849 22850 return this._currentLevel; 22851 22852 } 22853 22854 /** 22855 * Returns a reference to the first 3D object that is greater than 22856 * the given distance. 22857 * 22858 * @param {number} distance - The LOD distance. 22859 * @return {?Object3D} The found 3D object. `null` if no 3D object has been found. 22860 */ 22861 getObjectForDistance( distance ) { 22862 22863 const levels = this.levels; 22864 22865 if ( levels.length > 0 ) { 22866 22867 let i, l; 22868 22869 for ( i = 1, l = levels.length; i < l; i ++ ) { 22870 22871 let levelDistance = levels[ i ].distance; 22872 22873 if ( levels[ i ].object.visible ) { 22874 22875 levelDistance -= levelDistance * levels[ i ].hysteresis; 22876 22877 } 22878 22879 if ( distance < levelDistance ) { 22880 22881 break; 22882 22883 } 22884 22885 } 22886 22887 return levels[ i - 1 ].object; 22888 22889 } 22890 22891 return null; 22892 22893 } 22894 22895 /** 22896 * Computes intersection points between a casted ray and this LOD. 22897 * 22898 * @param {Raycaster} raycaster - The raycaster. 22899 * @param {Array<Object>} intersects - The target array that holds the intersection points. 22900 */ 22901 raycast( raycaster, intersects ) { 22902 22903 const levels = this.levels; 22904 22905 if ( levels.length > 0 ) { 22906 22907 _v1$2.setFromMatrixPosition( this.matrixWorld ); 22908 22909 const distance = raycaster.ray.origin.distanceTo( _v1$2 ); 22910 22911 this.getObjectForDistance( distance ).raycast( raycaster, intersects ); 22912 22913 } 22914 22915 } 22916 22917 /** 22918 * Updates the LOD by computing which LOD level should be visible according 22919 * to the current distance of the given camera. 22920 * 22921 * @param {Camera} camera - The camera the scene is rendered with. 22922 */ 22923 update( camera ) { 22924 22925 const levels = this.levels; 22926 22927 if ( levels.length > 1 ) { 22928 22929 _v1$2.setFromMatrixPosition( camera.matrixWorld ); 22930 _v2$1.setFromMatrixPosition( this.matrixWorld ); 22931 22932 const distance = _v1$2.distanceTo( _v2$1 ) / camera.zoom; 22933 22934 levels[ 0 ].object.visible = true; 22935 22936 let i, l; 22937 22938 for ( i = 1, l = levels.length; i < l; i ++ ) { 22939 22940 let levelDistance = levels[ i ].distance; 22941 22942 if ( levels[ i ].object.visible ) { 22943 22944 levelDistance -= levelDistance * levels[ i ].hysteresis; 22945 22946 } 22947 22948 if ( distance >= levelDistance ) { 22949 22950 levels[ i - 1 ].object.visible = false; 22951 levels[ i ].object.visible = true; 22952 22953 } else { 22954 22955 break; 22956 22957 } 22958 22959 } 22960 22961 this._currentLevel = i - 1; 22962 22963 for ( ; i < l; i ++ ) { 22964 22965 levels[ i ].object.visible = false; 22966 22967 } 22968 22969 } 22970 22971 } 22972 22973 toJSON( meta ) { 22974 22975 const data = super.toJSON( meta ); 22976 22977 data.object.autoUpdate = this.autoUpdate; 22978 22979 data.object.levels = []; 22980 22981 const levels = this.levels; 22982 22983 for ( let i = 0, l = levels.length; i < l; i ++ ) { 22984 22985 const level = levels[ i ]; 22986 22987 data.object.levels.push( { 22988 object: level.object.uuid, 22989 distance: level.distance, 22990 hysteresis: level.hysteresis 22991 } ); 22992 22993 } 22994 22995 return data; 22996 22997 } 22998 22999} 23000 23001const _vector$7 = /*@__PURE__*/ new Vector3(); 23002const _segCenter = /*@__PURE__*/ new Vector3(); 23003const _segDir = /*@__PURE__*/ new Vector3(); 23004const _diff = /*@__PURE__*/ new Vector3(); 23005 23006/** 23007 * A ray that emits from an origin in a certain direction. The class is used by 23008 * {@link Raycaster} to assist with raycasting. Raycasting is used for 23009 * mouse picking (working out what objects in the 3D space the mouse is over) 23010 * amongst other things. 23011 */ 23012class Ray { 23013 23014 /** 23015 * Constructs a new ray. 23016 * 23017 * @param {Vector3} [origin=(0,0,0)] - The origin of the ray. 23018 * @param {Vector3} [direction=(0,0,-1)] - The (normalized) direction of the ray. 23019 */ 23020 constructor( origin = new Vector3(), direction = new Vector3( 0, 0, -1 ) ) { 23021 23022 /** 23023 * The origin of the ray. 23024 * 23025 * @type {Vector3} 23026 */ 23027 this.origin = origin; 23028 23029 /** 23030 * The (normalized) direction of the ray. 23031 * 23032 * @type {Vector3} 23033 */ 23034 this.direction = direction; 23035 23036 } 23037 23038 /** 23039 * Sets the ray's components by copying the given values. 23040 * 23041 * @param {Vector3} origin - The origin. 23042 * @param {Vector3} direction - The direction. 23043 * @return {Ray} A reference to this ray. 23044 */ 23045 set( origin, direction ) { 23046 23047 this.origin.copy( origin ); 23048 this.direction.copy( direction ); 23049 23050 return this; 23051 23052 } 23053 23054 /** 23055 * Copies the values of the given ray to this instance. 23056 * 23057 * @param {Ray} ray - The ray to copy. 23058 * @return {Ray} A reference to this ray. 23059 */ 23060 copy( ray ) { 23061 23062 this.origin.copy( ray.origin ); 23063 this.direction.copy( ray.direction ); 23064 23065 return this; 23066 23067 } 23068 23069 /** 23070 * Returns a vector that is located at a given distance along this ray. 23071 * 23072 * @param {number} t - The distance along the ray to retrieve a position for. 23073 * @param {Vector3} target - The target vector that is used to store the method's result. 23074 * @return {Vector3} A position on the ray. 23075 */ 23076 at( t, target ) { 23077 23078 return target.copy( this.origin ).addScaledVector( this.direction, t ); 23079 23080 } 23081 23082 /** 23083 * Adjusts the direction of the ray to point at the given vector in world space. 23084 * 23085 * @param {Vector3} v - The target position. 23086 * @return {Ray} A reference to this ray. 23087 */ 23088 lookAt( v ) { 23089 23090 this.direction.copy( v ).sub( this.origin ).normalize(); 23091 23092 return this; 23093 23094 } 23095 23096 /** 23097 * Shift the origin of this ray along its direction by the given distance. 23098 * 23099 * @param {number} t - The distance along the ray to interpolate. 23100 * @return {Ray} A reference to this ray. 23101 */ 23102 recast( t ) { 23103 23104 this.origin.copy( this.at( t, _vector$7 ) ); 23105 23106 return this; 23107 23108 } 23109 23110 /** 23111 * Returns the point along this ray that is closest to the given point. 23112 * 23113 * @param {Vector3} point - A point in 3D space to get the closet location on the ray for. 23114 * @param {Vector3} target - The target vector that is used to store the method's result. 23115 * @return {Vector3} The closest point on this ray. 23116 */ 23117 closestPointToPoint( point, target ) { 23118 23119 target.subVectors( point, this.origin ); 23120 23121 const directionDistance = target.dot( this.direction ); 23122 23123 if ( directionDistance < 0 ) { 23124 23125 return target.copy( this.origin ); 23126 23127 } 23128 23129 return target.copy( this.origin ).addScaledVector( this.direction, directionDistance ); 23130 23131 } 23132 23133 /** 23134 * Returns the distance of the closest approach between this ray and the given point. 23135 * 23136 * @param {Vector3} point - A point in 3D space to compute the distance to. 23137 * @return {number} The distance. 23138 */ 23139 distanceToPoint( point ) { 23140 23141 return Math.sqrt( this.distanceSqToPoint( point ) ); 23142 23143 } 23144 23145 /** 23146 * Returns the squared distance of the closest approach between this ray and the given point. 23147 * 23148 * @param {Vector3} point - A point in 3D space to compute the distance to. 23149 * @return {number} The squared distance. 23150 */ 23151 distanceSqToPoint( point ) { 23152 23153 const directionDistance = _vector$7.subVectors( point, this.origin ).dot( this.direction ); 23154 23155 // point behind the ray 23156 23157 if ( directionDistance < 0 ) { 23158 23159 return this.origin.distanceToSquared( point ); 23160 23161 } 23162 23163 _vector$7.copy( this.origin ).addScaledVector( this.direction, directionDistance ); 23164 23165 return _vector$7.distanceToSquared( point ); 23166 23167 } 23168 23169 /** 23170 * Returns the squared distance between this ray and the given line segment. 23171 * 23172 * @param {Vector3} v0 - The start point of the line segment. 23173 * @param {Vector3} v1 - The end point of the line segment. 23174 * @param {Vector3} [optionalPointOnRay] - When provided, it receives the point on this ray that is closest to the segment. 23175 * @param {Vector3} [optionalPointOnSegment] - When provided, it receives the point on the line segment that is closest to this ray. 23176 * @return {number} The squared distance. 23177 */ 23178 distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { 23179 23180 // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h 23181 // It returns the min distance between the ray and the segment 23182 // defined by v0 and v1 23183 // It can also set two optional targets : 23184 // - The closest point on the ray 23185 // - The closest point on the segment 23186 23187 _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); 23188 _segDir.copy( v1 ).sub( v0 ).normalize(); 23189 _diff.copy( this.origin ).sub( _segCenter ); 23190 23191 const segExtent = v0.distanceTo( v1 ) * 0.5; 23192 const a01 = - this.direction.dot( _segDir ); 23193 const b0 = _diff.dot( this.direction ); 23194 const b1 = - _diff.dot( _segDir ); 23195 const c = _diff.lengthSq(); 23196 const det = Math.abs( 1 - a01 * a01 ); 23197 let s0, s1, sqrDist, extDet; 23198 23199 if ( det > 0 ) { 23200 23201 // The ray and segment are not parallel. 23202 23203 s0 = a01 * b1 - b0; 23204 s1 = a01 * b0 - b1; 23205 extDet = segExtent * det; 23206 23207 if ( s0 >= 0 ) { 23208 23209 if ( s1 >= - extDet ) { 23210 23211 if ( s1 <= extDet ) { 23212 23213 // region 0 23214 // Minimum at interior points of ray and segment. 23215 23216 const invDet = 1 / det; 23217 s0 *= invDet; 23218 s1 *= invDet; 23219 sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; 23220 23221 } else { 23222 23223 // region 1 23224 23225 s1 = segExtent; 23226 s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); 23227 sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; 23228 23229 } 23230 23231 } else { 23232 23233 // region 5 23234 23235 s1 = - segExtent; 23236 s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); 23237 sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; 23238 23239 } 23240 23241 } else { 23242 23243 if ( s1 <= - extDet ) { 23244 23245 // region 4 23246 23247 s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); 23248 s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); 23249 sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; 23250 23251 } else if ( s1 <= extDet ) { 23252 23253 // region 3 23254 23255 s0 = 0; 23256 s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); 23257 sqrDist = s1 * ( s1 + 2 * b1 ) + c; 23258 23259 } else { 23260 23261 // region 2 23262 23263 s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); 23264 s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); 23265 sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; 23266 23267 } 23268 23269 } 23270 23271 } else { 23272 23273 // Ray and segment are parallel. 23274 23275 s1 = ( a01 > 0 ) ? - segExtent : segExtent; 23276 s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); 23277 sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; 23278 23279 } 23280 23281 if ( optionalPointOnRay ) { 23282 23283 optionalPointOnRay.copy( this.origin ).addScaledVector( this.direction, s0 ); 23284 23285 } 23286 23287 if ( optionalPointOnSegment ) { 23288 23289 optionalPointOnSegment.copy( _segCenter ).addScaledVector( _segDir, s1 ); 23290 23291 } 23292 23293 return sqrDist; 23294 23295 } 23296 23297 /** 23298 * Intersects this ray with the given sphere, returning the intersection 23299 * point or `null` if there is no intersection. 23300 * 23301 * @param {Sphere} sphere - The sphere to intersect. 23302 * @param {Vector3} target - The target vector that is used to store the method's result. 23303 * @return {?Vector3} The intersection point. 23304 */ 23305 intersectSphere( sphere, target ) { 23306 23307 if ( sphere.radius < 0 ) return null; // handle empty spheres, see #31187 23308 23309 _vector$7.subVectors( sphere.center, this.origin ); 23310 const tca = _vector$7.dot( this.direction ); 23311 const d2 = _vector$7.dot( _vector$7 ) - tca * tca; 23312 const radius2 = sphere.radius * sphere.radius; 23313 23314 if ( d2 > radius2 ) return null; 23315 23316 const thc = Math.sqrt( radius2 - d2 ); 23317 23318 // t0 = first intersect point - entrance on front of sphere 23319 const t0 = tca - thc; 23320 23321 // t1 = second intersect point - exit point on back of sphere 23322 const t1 = tca + thc; 23323 23324 // test to see if t1 is behind the ray - if so, return null 23325 if ( t1 < 0 ) return null; 23326 23327 // test to see if t0 is behind the ray: 23328 // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, 23329 // in order to always return an intersect point that is in front of the ray. 23330 if ( t0 < 0 ) return this.at( t1, target ); 23331 23332 // else t0 is in front of the ray, so return the first collision point scaled by t0 23333 return this.at( t0, target ); 23334 23335 } 23336 23337 /** 23338 * Returns `true` if this ray intersects with the given sphere. 23339 * 23340 * @param {Sphere} sphere - The sphere to intersect. 23341 * @return {boolean} Whether this ray intersects with the given sphere or not. 23342 */ 23343 intersectsSphere( sphere ) { 23344 23345 if ( sphere.radius < 0 ) return false; // handle empty spheres, see #31187 23346 23347 return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius ); 23348 23349 } 23350 23351 /** 23352 * Computes the distance from the ray's origin to the given plane. Returns `null` if the ray 23353 * does not intersect with the plane. 23354 * 23355 * @param {Plane} plane - The plane to compute the distance to. 23356 * @return {?number} Whether this ray intersects with the given sphere or not. 23357 */ 23358 distanceToPlane( plane ) { 23359 23360 const denominator = plane.normal.dot( this.direction ); 23361 23362 if ( denominator === 0 ) { 23363 23364 // line is coplanar, return origin 23365 if ( plane.distanceToPoint( this.origin ) === 0 ) { 23366 23367 return 0; 23368 23369 } 23370 23371 // Null is preferable to undefined since undefined means.... it is undefined 23372 23373 return null; 23374 23375 } 23376 23377 const t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; 23378 23379 // Return if the ray never intersects the plane 23380 23381 return t >= 0 ? t : null; 23382 23383 } 23384 23385 /** 23386 * Intersects this ray with the given plane, returning the intersection 23387 * point or `null` if there is no intersection. 23388 * 23389 * @param {Plane} plane - The plane to intersect. 23390 * @param {Vector3} target - The target vector that is used to store the method's result. 23391 * @return {?Vector3} The intersection point. 23392 */ 23393 intersectPlane( plane, target ) { 23394 23395 const t = this.distanceToPlane( plane ); 23396 23397 if ( t === null ) { 23398 23399 return null; 23400 23401 } 23402 23403 return this.at( t, target ); 23404 23405 } 23406 23407 /** 23408 * Returns `true` if this ray intersects with the given plane. 23409 * 23410 * @param {Plane} plane - The plane to intersect. 23411 * @return {boolean} Whether this ray intersects with the given plane or not. 23412 */ 23413 intersectsPlane( plane ) { 23414 23415 // check if the ray lies on the plane first 23416 23417 const distToPoint = plane.distanceToPoint( this.origin ); 23418 23419 if ( distToPoint === 0 ) { 23420 23421 return true; 23422 23423 } 23424 23425 const denominator = plane.normal.dot( this.direction ); 23426 23427 if ( denominator * distToPoint < 0 ) { 23428 23429 return true; 23430 23431 } 23432 23433 // ray origin is behind the plane (and is pointing behind it) 23434 23435 return false; 23436 23437 } 23438 23439 /** 23440 * Intersects this ray with the given bounding box, returning the intersection 23441 * point or `null` if there is no intersection. 23442 * 23443 * @param {Box3} box - The box to intersect. 23444 * @param {Vector3} target - The target vector that is used to store the method's result. 23445 * @return {?Vector3} The intersection point. 23446 */ 23447 intersectBox( box, target ) { 23448 23449 let tmin, tmax, tymin, tymax, tzmin, tzmax; 23450 23451 const invdirx = 1 / this.direction.x, 23452 invdiry = 1 / this.direction.y, 23453 invdirz = 1 / this.direction.z; 23454 23455 const origin = this.origin; 23456 23457 if ( invdirx >= 0 ) { 23458 23459 tmin = ( box.min.x - origin.x ) * invdirx; 23460 tmax = ( box.max.x - origin.x ) * invdirx; 23461 23462 } else { 23463 23464 tmin = ( box.max.x - origin.x ) * invdirx; 23465 tmax = ( box.min.x - origin.x ) * invdirx; 23466 23467 } 23468 23469 if ( invdiry >= 0 ) { 23470 23471 tymin = ( box.min.y - origin.y ) * invdiry; 23472 tymax = ( box.max.y - origin.y ) * invdiry; 23473 23474 } else { 23475 23476 tymin = ( box.max.y - origin.y ) * invdiry; 23477 tymax = ( box.min.y - origin.y ) * invdiry; 23478 23479 } 23480 23481 if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; 23482 23483 if ( tymin > tmin || isNaN( tmin ) ) tmin = tymin; 23484 23485 if ( tymax < tmax || isNaN( tmax ) ) tmax = tymax; 23486 23487 if ( invdirz >= 0 ) { 23488 23489 tzmin = ( box.min.z - origin.z ) * invdirz; 23490 tzmax = ( box.max.z - origin.z ) * invdirz; 23491 23492 } else { 23493 23494 tzmin = ( box.max.z - origin.z ) * invdirz; 23495 tzmax = ( box.min.z - origin.z ) * invdirz; 23496 23497 } 23498 23499 if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; 23500 23501 if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; 23502 23503 if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; 23504 23505 //return point closest to the ray (positive side) 23506 23507 if ( tmax < 0 ) return null; 23508 23509 return this.at( tmin >= 0 ? tmin : tmax, target ); 23510 23511 } 23512 23513 /** 23514 * Returns `true` if this ray intersects with the given box. 23515 * 23516 * @param {Box3} box - The box to intersect. 23517 * @return {boolean} Whether this ray intersects with the given box or not. 23518 */ 23519 intersectsBox( box ) { 23520 23521 return this.intersectBox( box, _vector$7 ) !== null; 23522 23523 } 23524 23525 /** 23526 * Intersects this ray with the given triangle, returning the intersection 23527 * point or `null` if there is no intersection. 23528 * 23529 * @param {Vector3} a - The first vertex of the triangle. 23530 * @param {Vector3} b - The second vertex of the triangle. 23531 * @param {Vector3} c - The third vertex of the triangle. 23532 * @param {boolean} backfaceCulling - Whether to use backface culling or not. 23533 * @param {Vector3} target - The target vector that is used to store the method's result. 23534 * @return {?Vector3} The intersection point. 23535 */ 23536 intersectTriangle( a, b, c, backfaceCulling, target ) { 23537 23538 // Watertight ray/triangle intersection. Reference: Woop, Benthin, Wald, 23539 // "Watertight Ray/Triangle Intersection", JCGT vol. 2 no. 1 (2013), Appendix A. 23540 // https://jcgt.org/published/0002/01/05/ 23541 23542 const origin = this.origin; 23543 const direction = this.direction; 23544 23545 const dx = direction.x; 23546 const dy = direction.y; 23547 const dz = direction.z; 23548 23549 // triangle vertices relative to the ray origin 23550 23551 const aox = a.x - origin.x, aoy = a.y - origin.y, aoz = a.z - origin.z; 23552 const box = b.x - origin.x, boy = b.y - origin.y, boz = b.z - origin.z; 23553 const cox = c.x - origin.x, coy = c.y - origin.y, coz = c.z - origin.z; 23554 23555 // Use the dimension where the ray direction is maximal as the projection 23556 // axis (kz) and read every component already permuted into (kx, ky, kz). 23557 // kx and ky are swapped when the direction's kz component is negative, to 23558 // preserve the winding order of triangles. 23559 23560 const adx = Math.abs( dx ), ady = Math.abs( dy ), adz = Math.abs( dz ); 23561 23562 let dkx, dky, dkz; 23563 let akx, aky, akz, bkx, bky, bkz, ckx, cky, ckz; 23564 23565 if ( adx >= ady && adx >= adz ) { 23566 23567 dkz = dx; akz = aox; bkz = box; ckz = cox; 23568 23569 if ( dx >= 0 ) { 23570 23571 dkx = dy; dky = dz; 23572 akx = aoy; aky = aoz; bkx = boy; bky = boz; ckx = coy; cky = coz; 23573 23574 } else { 23575 23576 dkx = dz; dky = dy; 23577 akx = aoz; aky = aoy; bkx = boz; bky = boy; ckx = coz; cky = coy; 23578 23579 } 23580 23581 } else if ( ady >= adz ) { 23582 23583 dkz = dy; akz = aoy; bkz = boy; ckz = coy; 23584 23585 if ( dy >= 0 ) { 23586 23587 dkx = dz; dky = dx; 23588 akx = aoz; aky = aox; bkx = boz; bky = box; ckx = coz; cky = cox; 23589 23590 } else { 23591 23592 dkx = dx; dky = dz; 23593 akx = aox; aky = aoz; bkx = box; bky = boz; ckx = cox; cky = coz; 23594 23595 } 23596 23597 } else { 23598 23599 dkz = dz; akz = aoz; bkz = boz; ckz = coz; 23600 23601 if ( dz >= 0 ) { 23602 23603 dkx = dx; dky = dy; 23604 akx = aox; aky = aoy; bkx = box; bky = boy; ckx = cox; cky = coy; 23605 23606 } else { 23607 23608 dkx = dy; dky = dx; 23609 akx = aoy; aky = aox; bkx = boy; bky = box; ckx = coy; cky = cox; 23610 23611 } 23612 23613 } 23614 23615 // a zero direction has no maximal axis and cannot intersect 23616 23617 if ( dkz === 0 ) return null; 23618 23619 // shear constants that align the ray with the +kz axis 23620 23621 const sx = dkx / dkz, sy = dky / dkz, sz = 1 / dkz; 23622 23623 // sheared and scaled vertices 23624 23625 const ax = akx - sx * akz, ay = aky - sy * akz; 23626 const bx = bkx - sx * bkz, by = bky - sy * bkz; 23627 const cx = ckx - sx * ckz, cy = cky - sy * ckz; 23628 23629 // scaled barycentric coordinates (signed edge functions); the shear makes a 23630 // shared edge evaluate identically for both adjacent triangles, so the ray 23631 // can never fall between them 23632 23633 const u = cx * by - cy * bx; 23634 const v = ax * cy - ay * cx; 23635 const w = bx * ay - by * ax; 23636 23637 if ( backfaceCulling ) { 23638 23639 if ( u < 0 || v < 0 || w < 0 ) return null; 23640 23641 } else { 23642 23643 if ( ( u < 0 || v < 0 || w < 0 ) && ( u > 0 || v > 0 || w > 0 ) ) return null; 23644 23645 } 23646 23647 const det = u + v + w; 23648 23649 // ray is co-planar with the triangle 23650 23651 if ( det === 0 ) return null; 23652 23653 // scaled hit distance; t = tScaled / det must lie in front of the origin 23654 23655 const tScaled = sz * ( u * akz + v * bkz + w * ckz ); 23656 23657 if ( det > 0 ? tScaled < 0 : tScaled > 0 ) return null; 23658 23659 return this.at( tScaled / det, target ); 23660 23661 } 23662 23663 /** 23664 * Transforms this ray with the given 4x4 transformation matrix. 23665 * 23666 * @param {Matrix4} matrix4 - The transformation matrix. 23667 * @return {Ray} A reference to this ray. 23668 */ 23669 applyMatrix4( matrix4 ) { 23670 23671 this.origin.applyMatrix4( matrix4 ); 23672 this.direction.transformDirection( matrix4 ); 23673 23674 return this; 23675 23676 } 23677 23678 /** 23679 * Returns `true` if this ray is equal with the given one. 23680 * 23681 * @param {Ray} ray - The ray to test for equality. 23682 * @return {boolean} Whether this ray is equal with the given one. 23683 */ 23684 equals( ray ) { 23685 23686 return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); 23687 23688 } 23689 23690 /** 23691 * Returns a new ray with copied values from this instance. 23692 * 23693 * @return {Ray} A clone of this instance. 23694 */ 23695 clone() { 23696 23697 return new this.constructor().copy( this ); 23698 23699 } 23700 23701} 23702 23703/** 23704 * A material for drawing geometries in a simple shaded (flat or wireframe) way. 23705 * 23706 * This material is not affected by lights. 23707 * 23708 * @augments Material 23709 * @demo scenes/material-browser.html#MeshBasicMaterial 23710 */ 23711class MeshBasicMaterial extends Material { 23712 23713 /** 23714 * Constructs a new mesh basic material. 23715 * 23716 * @param {Object} [parameters] - An object with one or more properties 23717 * defining the material's appearance. Any property of the material 23718 * (including any property from inherited materials) can be passed 23719 * in here. Color values can be passed any type of value accepted 23720 * by {@link Color#set}. 23721 */ 23722 constructor( parameters ) { 23723 23724 super(); 23725 23726 /** 23727 * This flag can be used for type testing. 23728 * 23729 * @type {boolean} 23730 * @readonly 23731 * @default true 23732 */ 23733 this.isMeshBasicMaterial = true; 23734 23735 this.type = 'MeshBasicMaterial'; 23736 23737 /** 23738 * Color of the material. 23739 * 23740 * @type {Color} 23741 * @default (1,1,1) 23742 */ 23743 this.color = new Color( 0xffffff ); // diffuse 23744 23745 /** 23746 * The color map. May optionally include an alpha channel, typically combined 23747 * with {@link Material#transparent} or {@link Material#alphaTest}. The texture map 23748 * color is modulated by the diffuse `color`. 23749 * 23750 * `map` represents color data, and the texture must be assigned a 23751 * {@link Texture#colorSpace}. Most `map` textures set 23752 * `texture.colorSpace = SRGBColorSpace`. 23753 * 23754 * @type {?Texture} 23755 * @default null 23756 */ 23757 this.map = null; 23758 23759 /** 23760 * The light map. Requires a second set of UVs. 23761 * 23762 * `lightMap` represents pre-baked illuminance data, and the texture must be assigned 23763 * a {@link Texture#colorSpace}. Most `lightMap` textures set 23764 * `texture.colorSpace = LinearSRGBColorSpace` and use float-type formats 23765 * such as `.exr` or `.hdr`. 23766 * 23767 * @type {?Texture} 23768 * @default null 23769 */ 23770 this.lightMap = null; 23771 23772 /** 23773 * Intensity of the baked light. 23774 * 23775 * @type {number} 23776 * @default 1 23777 */ 23778 this.lightMapIntensity = 1.0; 23779 23780 /** 23781 * The red channel of this texture is used as the ambient occlusion map. 23782 * Requires a second set of UVs. 23783 * 23784 * `aoMap` represents non-color data. Any texture assigned must have 23785 * `texture.colorSpace = NoColorSpace` (default). 23786 * 23787 * @type {?Texture} 23788 * @default null 23789 */ 23790 this.aoMap = null; 23791 23792 /** 23793 * Intensity of the ambient occlusion effect. Range is `[0,1]`, where `0` 23794 * disables ambient occlusion. Where intensity is `1` and the AO map's 23795 * red channel is also `1`, ambient light is fully occluded on a surface. 23796 * 23797 * @type {number} 23798 * @default 1 23799 */ 23800 this.aoMapIntensity = 1.0; 23801 23802 /** 23803 * Specular map used by the material. 23804 * 23805 * `specularMap` represents color data, and the texture must be assigned a 23806 * {@link Texture#colorSpace}. Most `specularMap` textures set 23807 * `texture.colorSpace = SRGBColorSpace`. 23808 * 23809 * @type {?Texture} 23810 * @default null 23811 */ 23812 this.specularMap = null; 23813 23814 /** 23815 * The alpha map is a grayscale texture that controls the opacity across the 23816 * surface (black: fully transparent; white: fully opaque). 23817 * 23818 * Only the color of the texture is used, ignoring the alpha channel if one 23819 * exists. For RGB and RGBA textures, the renderer will use the green channel 23820 * when sampling this texture due to the extra bit of precision provided for 23821 * green in DXT-compressed and uncompressed RGB 565 formats. Luminance-only and 23822 * luminance/alpha textures will also still work as expected. 23823 * 23824 * `alphaMap` represents non-color data. Any texture assigned must have 23825 * `texture.colorSpace = NoColorSpace` (default). 23826 * 23827 * @type {?Texture} 23828 * @default null 23829 */ 23830 this.alphaMap = null; 23831 23832 /** 23833 * The environment map. 23834 * 23835 * `envMap` represents luminance data, and the texture must be assigned 23836 * a {@link Texture#colorSpace}. Most `envMap` textures set 23837 * `texture.colorSpace = LinearSRGBColorSpace` and use float-type formats 23838 * such as `.exr` or `.hdr`. 23839 * 23840 * @type {?Texture} 23841 * @default null 23842 */ 23843 this.envMap = null; 23844 23845 /** 23846 * The rotation of the environment map in radians. 23847 * 23848 * @type {Euler} 23849 * @default (0,0,0) 23850 */ 23851 this.envMapRotation = new Euler(); 23852 23853 /** 23854 * How to combine the result of the surface's color with the environment map, if any. 23855 * 23856 * When set to `MixOperation`, the {@link MeshBasicMaterial#reflectivity} is used to 23857 * blend between the two colors. 23858 * 23859 * @type {(MultiplyOperation|MixOperation|AddOperation)} 23860 * @default MultiplyOperation 23861 */ 23862 this.combine = MultiplyOperation; 23863 23864 /** 23865 * How much the environment map affects the surface. 23866 * The valid range is between `0` (no reflections) and `1` (full reflections). 23867 * 23868 * @type {number} 23869 * @default 1 23870 */ 23871 this.reflectivity = 1; 23872 23873 /** 23874 * The index of refraction (IOR) of air (approximately 1) divided by the 23875 * index of refraction of the material. It is used with environment mapping 23876 * modes {@link CubeRefractionMapping} and {@link EquirectangularRefractionMapping}. 23877 * The refraction ratio should not exceed `1`. 23878 * 23879 * @type {number} 23880 * @default 0.98 23881 */ 23882 this.refractionRatio = 0.98; 23883 23884 /** 23885 * Renders the geometry as a wireframe. 23886 * 23887 * @type {boolean} 23888 * @default false 23889 */ 23890 this.wireframe = false; 23891 23892 /** 23893 * Controls the thickness of the wireframe. 23894 * 23895 * Can only be used with {@link SVGRenderer}. 23896 * 23897 * @type {number} 23898 * @default 1 23899 */ 23900 this.wireframeLinewidth = 1; 23901 23902 /** 23903 * Defines appearance of wireframe ends. 23904 * 23905 * Can only be used with {@link SVGRenderer}. 23906 * 23907 * @type {('round'|'bevel'|'miter')} 23908 * @default 'round' 23909 */ 23910 this.wireframeLinecap = 'round'; 23911 23912 /** 23913 * Defines appearance of wireframe joints. 23914 * 23915 * Can only be used with {@link SVGRenderer}. 23916 * 23917 * @type {('round'|'bevel'|'miter')} 23918 * @default 'round' 23919 */ 23920 this.wireframeLinejoin = 'round'; 23921 23922 /** 23923 * Whether the material is affected by fog or not. 23924 * 23925 * @type {boolean} 23926 * @default true 23927 */ 23928 this.fog = true; 23929 23930 this.setValues( parameters ); 23931 23932 } 23933 23934 copy( source ) { 23935 23936 super.copy( source ); 23937 23938 this.color.copy( source.color ); 23939 23940 this.map = source.map; 23941 23942 this.lightMap = source.lightMap; 23943 this.lightMapIntensity = source.lightMapIntensity; 23944 23945 this.aoMap = source.aoMap; 23946 this.aoMapIntensity = source.aoMapIntensity; 23947 23948 this.specularMap = source.specularMap; 23949 23950 this.alphaMap = source.alphaMap; 23951 23952 this.envMap = source.envMap; 23953 this.envMapRotation.copy( source.envMapRotation ); 23954 this.combine = source.combine; 23955 this.reflectivity = source.reflectivity; 23956 this.refractionRatio = source.refractionRatio; 23957 23958 this.wireframe = source.wireframe; 23959 this.wireframeLinewidth = source.wireframeLinewidth; 23960 this.wireframeLinecap = source.wireframeLinecap; 23961 this.wireframeLinejoin = source.wireframeLinejoin; 23962 23963 this.fog = source.fog; 23964 23965 return this; 23966 23967 } 23968 23969} 23970 23971const _inverseMatrix$3 = /*@__PURE__*/ new Matrix4(); 23972const _ray$3 = /*@__PURE__*/ new Ray(); 23973const _sphere$6 = /*@__PURE__*/ new Sphere(); 23974const _sphereHitAt = /*@__PURE__*/ new Vector3(); 23975 23976const _vA = /*@__PURE__*/ new Vector3(); 23977const _vB = /*@__PURE__*/ new Vector3(); 23978const _vC = /*@__PURE__*/ new Vector3(); 23979 23980const _tempA = /*@__PURE__*/ new Vector3(); 23981const _morphA = /*@__PURE__*/ new Vector3(); 23982 23983const _intersectionPoint = /*@__PURE__*/ new Vector3(); 23984const _intersectionPointWorld = /*@__PURE__*/ new Vector3(); 23985 23986/** 23987 * Class representing triangular polygon mesh based objects. 23988 * 23989 * ```js 23990 * const geometry = new THREE.BoxGeometry( 1, 1, 1 ); 23991 * const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); 23992 * const mesh = new THREE.Mesh( geometry, material ); 23993 * scene.add( mesh ); 23994 * ``` 23995 * 23996 * @augments Object3D 23997 */ 23998class Mesh extends Object3D { 23999 24000 /** 24001 * Constructs a new mesh. 24002 * 24003 * @param {BufferGeometry} [geometry] - The mesh geometry. 24004 * @param {Material|Array<Material>} [material] - The mesh material. 24005 */ 24006 constructor( geometry = new BufferGeometry(), material = new MeshBasicMaterial() ) { 24007 24008 super(); 24009 24010 /** 24011 * This flag can be used for type testing. 24012 * 24013 * @type {boolean} 24014 * @readonly 24015 * @default true 24016 */ 24017 this.isMesh = true; 24018 24019 this.type = 'Mesh'; 24020 24021 /** 24022 * The mesh geometry. 24023 * 24024 * @type {BufferGeometry} 24025 */ 24026 this.geometry = geometry; 24027 24028 /** 24029 * The mesh material. 24030 * 24031 * @type {Material|Array<Material>} 24032 * @default MeshBasicMaterial 24033 */ 24034 this.material = material; 24035 24036 /** 24037 * A dictionary representing the morph targets in the geometry. The key is the 24038 * morph targets name, the value its attribute index. This member is `undefined` 24039 * by default and only set when morph targets are detected in the geometry. 24040 * 24041 * @type {Object<string,number>|undefined} 24042 * @default undefined 24043 */ 24044 this.morphTargetDictionary = undefined; 24045 24046 /** 24047 * An array of weights typically in the range `[0,1]` that specify how much of the morph 24048 * is applied. This member is `undefined` by default and only set when morph targets are 24049 * detected in the geometry. 24050 * 24051 * @type {Array<number>|undefined} 24052 * @default undefined 24053 */ 24054 this.morphTargetInfluences = undefined; 24055 24056 /** 24057 * The number of instances of this mesh. 24058 * Can only be used with {@link WebGPURenderer}. 24059 * 24060 * @type {number} 24061 * @default 1 24062 */ 24063 this.count = 1; 24064 24065 this.updateMorphTargets(); 24066 24067 } 24068 24069 copy( source, recursive ) { 24070 24071 super.copy( source, recursive ); 24072 24073 if ( source.morphTargetInfluences !== undefined ) { 24074 24075 this.morphTargetInfluences = source.morphTargetInfluences.slice(); 24076 24077 } 24078 24079 if ( source.morphTargetDictionary !== undefined ) { 24080 24081 this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary ); 24082 24083 } 24084 24085 this.material = Array.isArray( source.material ) ? source.material.slice() : source.material; 24086 this.geometry = source.geometry; 24087 24088 return this; 24089 24090 } 24091 24092 /** 24093 * Sets the values of {@link Mesh#morphTargetDictionary} and {@link Mesh#morphTargetInfluences} 24094 * to make sure existing morph targets can influence this 3D object. 24095 */ 24096 updateMorphTargets() { 24097 24098 const geometry = this.geometry; 24099 24100 const morphAttributes = geometry.morphAttributes; 24101 const keys = Object.keys( morphAttributes ); 24102 24103 if ( keys.length > 0 ) { 24104 24105 const morphAttribute = morphAttributes[ keys[ 0 ] ]; 24106 24107 if ( morphAttribute !== undefined ) { 24108 24109 this.morphTargetInfluences = []; 24110 this.morphTargetDictionary = {}; 24111 24112 for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { 24113 24114 const name = morphAttribute[ m ].name || String( m ); 24115 24116 this.morphTargetInfluences.push( 0 ); 24117 this.morphTargetDictionary[ name ] = m; 24118 24119 } 24120 24121 } 24122 24123 } 24124 24125 } 24126 24127 /** 24128 * Returns the local-space position of the vertex at the given index, taking into 24129 * account the current animation state of both morph targets and skinning. 24130 * 24131 * @param {number} index - The vertex index. 24132 * @param {Vector3} target - The target object that is used to store the method's result. 24133 * @return {Vector3} The vertex position in local space. 24134 */ 24135 getVertexPosition( index, target ) { 24136 24137 const geometry = this.geometry; 24138 const position = geometry.attributes.position; 24139 const morphPosition = geometry.morphAttributes.position; 24140 const morphTargetsRelative = geometry.morphTargetsRelative; 24141 24142 target.fromBufferAttribute( position, index ); 24143 24144 const morphInfluences = this.morphTargetInfluences; 24145 24146 if ( morphPosition && morphInfluences ) { 24147 24148 _morphA.set( 0, 0, 0 ); 24149 24150 for ( let i = 0, il = morphPosition.length; i < il; i ++ ) { 24151 24152 const influence = morphInfluences[ i ]; 24153 const morphAttribute = morphPosition[ i ]; 24154 24155 if ( influence === 0 ) continue; 24156 24157 _tempA.fromBufferAttribute( morphAttribute, index ); 24158 24159 if ( morphTargetsRelative ) { 24160 24161 _morphA.addScaledVector( _tempA, influence ); 24162 24163 } else { 24164 24165 _morphA.addScaledVector( _tempA.sub( target ), influence ); 24166 24167 } 24168 24169 } 24170 24171 target.add( _morphA ); 24172 24173 } 24174 24175 return target; 24176 24177 } 24178 24179 /** 24180 * Returns `true` if this mesh intersects the given frustum. 24181 * 24182 * @param {Frustum|FrustumArray} frustum - The frustum to test. 24183 * @return {boolean} Whether this mesh intersects the given frustum or not. 24184 */ 24185 intersectsFrustum( frustum ) { 24186 24187 return frustum.intersectsObject( this ); 24188 24189 } 24190 24191 /** 24192 * Computes intersection points between a casted ray and this line. 24193 * 24194 * @param {Raycaster} raycaster - The raycaster. 24195 * @param {Array<Object>} intersects - The target array that holds the intersection points. 24196 */ 24197 raycast( raycaster, intersects ) { 24198 24199 const geometry = this.geometry; 24200 const material = this.material; 24201 const matrixWorld = this.matrixWorld; 24202 24203 if ( material === undefined ) return; 24204 24205 // test with bounding sphere in world space 24206 24207 if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); 24208 24209 _sphere$6.copy( geometry.boundingSphere ); 24210 _sphere$6.applyMatrix4( matrixWorld ); 24211 24212 // check distance from ray origin to bounding sphere 24213 24214 _ray$3.copy( raycaster.ray ).recast( raycaster.near ); 24215 24216 if ( _sphere$6.containsPoint( _ray$3.origin ) === false ) { 24217 24218 if ( _ray$3.intersectSphere( _sphere$6, _sphereHitAt ) === null ) return; 24219 24220 if ( _ray$3.origin.distanceToSquared( _sphereHitAt ) > ( raycaster.far - raycaster.near ) ** 2 ) return; 24221 24222 } 24223 24224 // convert ray to local space of mesh 24225 24226 _inverseMatrix$3.copy( matrixWorld ).invert(); 24227 _ray$3.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$3 ); 24228 24229 // test with bounding box in local space 24230 24231 if ( geometry.boundingBox !== null ) { 24232 24233 if ( _ray$3.intersectsBox( geometry.boundingBox ) === false ) return; 24234 24235 } 24236 24237 // test for intersections with geometry 24238 24239 this._computeIntersections( raycaster, intersects, _ray$3 ); 24240 24241 } 24242 24243 _computeIntersections( raycaster, intersects, rayLocalSpace ) { 24244 24245 let intersection; 24246 24247 const geometry = this.geometry; 24248 const material = this.material; 24249 24250 const index = geometry.index; 24251 const position = geometry.attributes.position; 24252 const uv = geometry.attributes.uv; 24253 const uv1 = geometry.attributes.uv1; 24254 const normal = geometry.attributes.normal; 24255 const groups = geometry.groups; 24256 const drawRange = geometry.drawRange; 24257 24258 if ( index !== null ) { 24259 24260 // indexed buffer geometry 24261 24262 if ( Array.isArray( material ) ) { 24263 24264 for ( let i = 0, il = groups.length; i < il; i ++ ) { 24265 24266 const group = groups[ i ]; 24267 const groupMaterial = material[ group.materialIndex ]; 24268 24269 const start = Math.max( group.start, drawRange.start ); 24270 const end = Math.min( index.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); 24271 24272 for ( let j = start, jl = end; j < jl; j += 3 ) { 24273 24274 const a = index.getX( j ); 24275 const b = index.getX( j + 1 ); 24276 const c = index.getX( j + 2 ); 24277 24278 intersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c ); 24279 24280 if ( intersection ) { 24281 24282 intersection.faceIndex = Math.floor( j / 3 ); // triangle number in indexed buffer semantics 24283 intersection.face.materialIndex = group.materialIndex; 24284 intersects.push( intersection ); 24285 24286 } 24287 24288 } 24289 24290 } 24291 24292 } else { 24293 24294 const start = Math.max( 0, drawRange.start ); 24295 const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); 24296 24297 for ( let i = start, il = end; i < il; i += 3 ) { 24298 24299 const a = index.getX( i ); 24300 const b = index.getX( i + 1 ); 24301 const c = index.getX( i + 2 ); 24302 24303 intersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c ); 24304 24305 if ( intersection ) { 24306 24307 intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics 24308 intersects.push( intersection ); 24309 24310 } 24311 24312 } 24313 24314 } 24315 24316 } else if ( position !== undefined ) { 24317 24318 // non-indexed buffer geometry 24319 24320 if ( Array.isArray( material ) ) { 24321 24322 for ( let i = 0, il = groups.length; i < il; i ++ ) { 24323 24324 const group = groups[ i ]; 24325 const groupMaterial = material[ group.materialIndex ]; 24326 24327 const start = Math.max( group.start, drawRange.start ); 24328 const end = Math.min( position.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); 24329 24330 for ( let j = start, jl = end; j < jl; j += 3 ) { 24331 24332 const a = j; 24333 const b = j + 1; 24334 const c = j + 2; 24335 24336 intersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c ); 24337 24338 if ( intersection ) { 24339 24340 intersection.faceIndex = Math.floor( j / 3 ); // triangle number in non-indexed buffer semantics 24341 intersection.face.materialIndex = group.materialIndex; 24342 intersects.push( intersection ); 24343 24344 } 24345 24346 } 24347 24348 } 24349 24350 } else { 24351 24352 const start = Math.max( 0, drawRange.start ); 24353 const end = Math.min( position.count, ( drawRange.start + drawRange.count ) ); 24354 24355 for ( let i = start, il = end; i < il; i += 3 ) { 24356 24357 const a = i; 24358 const b = i + 1; 24359 const c = i + 2; 24360 24361 intersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c ); 24362 24363 if ( intersection ) { 24364 24365 intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics 24366 intersects.push( intersection ); 24367 24368 } 24369 24370 } 24371 24372 } 24373 24374 } 24375 24376 } 24377 24378} 24379 24380function checkIntersection$1( object, material, raycaster, ray, pA, pB, pC, point ) { 24381 24382 let intersect; 24383 24384 if ( material.side === BackSide ) { 24385 24386 intersect = ray.intersectTriangle( pC, pB, pA, true, point ); 24387 24388 } else { 24389 24390 intersect = ray.intersectTriangle( pA, pB, pC, ( material.side === FrontSide ), point ); 24391 24392 } 24393 24394 if ( intersect === null ) return null; 24395 24396 _intersectionPointWorld.copy( point ); 24397 _intersectionPointWorld.applyMatrix4( object.matrixWorld ); 24398 24399 const distance = raycaster.ray.origin.distanceTo( _intersectionPointWorld ); 24400 24401 if ( distance < raycaster.near || distance > raycaster.far ) return null; 24402 24403 return { 24404 distance: distance, 24405 point: _intersectionPointWorld.clone(), 24406 object: object 24407 }; 24408 24409} 24410 24411function checkGeometryIntersection( object, material, raycaster, ray, uv, uv1, normal, a, b, c ) { 24412 24413 object.getVertexPosition( a, _vA ); 24414 object.getVertexPosition( b, _vB ); 24415 object.getVertexPosition( c, _vC ); 24416 24417 const intersection = checkIntersection$1( object, material, raycaster, ray, _vA, _vB, _vC, _intersectionPoint ); 24418 24419 if ( intersection ) { 24420 24421 const barycoord = new Vector3(); 24422 Triangle.getBarycoord( _intersectionPoint, _vA, _vB, _vC, barycoord ); 24423 24424 if ( uv ) { 24425 24426 intersection.uv = Triangle.getInterpolatedAttribute( uv, a, b, c, barycoord, new Vector2() ); 24427 24428 } 24429 24430 if ( uv1 ) { 24431 24432 intersection.uv1 = Triangle.getInterpolatedAttribute( uv1, a, b, c, barycoord, new Vector2() ); 24433 24434 } 24435 24436 if ( normal ) { 24437 24438 intersection.normal = Triangle.getInterpolatedAttribute( normal, a, b, c, barycoord, new Vector3() ); 24439 24440 if ( intersection.normal.dot( ray.direction ) > 0 ) { 24441 24442 intersection.normal.multiplyScalar( -1 ); 24443 24444 } 24445 24446 } 24447 24448 const face = { 24449 a: a, 24450 b: b, 24451 c: c, 24452 normal: new Vector3(), 24453 materialIndex: 0 24454 }; 24455 24456 Triangle.getNormal( _vA, _vB, _vC, face.normal ); 24457 24458 intersection.face = face; 24459 intersection.barycoord = barycoord; 24460 24461 } 24462 24463 return intersection; 24464 24465} 24466 24467const _baseVector = /*@__PURE__*/ new Vector4(); 24468 24469const _skinIndex = /*@__PURE__*/ new Vector4(); 24470const _skinWeight = /*@__PURE__*/ new Vector4(); 24471 24472const _vector4 = /*@__PURE__*/ new Vector4(); 24473const _matrix4 = /*@__PURE__*/ new Matrix4(); 24474const _vertex = /*@__PURE__*/ new Vector3(); 24475 24476const _sphere$5 = /*@__PURE__*/ new Sphere(); 24477const _inverseMatrix$2 = /*@__PURE__*/ new Matrix4(); 24478const _ray$2 = /*@__PURE__*/ new Ray(); 24479 24480/** 24481 * A mesh that has a {@link Skeleton} that can then be used to animate the 24482 * vertices of the geometry with skinning/skeleton animation. 24483 * 24484 * Next to a valid skeleton, the skinned mesh requires skin indices and weights 24485 * as buffer attributes in its geometry. These attribute define which bones affect a single 24486 * vertex to a certain extend. 24487 * 24488 * Typically skinned meshes are not created manually but loaders like {@link GLTFLoader} 24489 * or {@link FBXLoader } import respective models. 24490 * 24491 * @augments Mesh 24492 * @demo scenes/bones-browser.html 24493 */ 24494class SkinnedMesh extends Mesh { 24495 24496 /** 24497 * Constructs a new skinned mesh. 24498 * 24499 * @param {BufferGeometry} [geometry] - The mesh geometry. 24500 * @param {Material|Array<Material>} [material] - The mesh material. 24501 */ 24502 constructor( geometry, material ) { 24503 24504 super( geometry, material ); 24505 24506 /** 24507 * This flag can be used for type testing. 24508 * 24509 * @type {boolean} 24510 * @readonly 24511 * @default true 24512 */ 24513 this.isSkinnedMesh = true; 24514 24515 this.type = 'SkinnedMesh'; 24516 24517 /** 24518 * `AttachedBindMode` means the skinned mesh shares the same world space as the skeleton. 24519 * This is not true when using `DetachedBindMode` which is useful when sharing a skeleton 24520 * across multiple skinned meshes. 24521 * 24522 * @type {(AttachedBindMode|DetachedBindMode)} 24523 * @default AttachedBindMode 24524 */ 24525 this.bindMode = AttachedBindMode; 24526 24527 /** 24528 * The base matrix that is used for the bound bone transforms. 24529 * 24530 * @type {Matrix4} 24531 */ 24532 this.bindMatrix = new Matrix4(); 24533 24534 /** 24535 * The base matrix that is used for resetting the bound bone transforms. 24536 * 24537 * @type {Matrix4} 24538 */ 24539 this.bindMatrixInverse = new Matrix4(); 24540 24541 /** 24542 * The bounding box of the skinned mesh. Can be computed via {@link SkinnedMesh#computeBoundingBox}. 24543 * 24544 * @type {?Box3} 24545 * @default null 24546 */ 24547 this.boundingBox = null; 24548 24549 /** 24550 * The bounding sphere of the skinned mesh. Can be computed via {@link SkinnedMesh#computeBoundingSphere}. 24551 * 24552 * @type {?Sphere} 24553 * @default null 24554 */ 24555 this.boundingSphere = null; 24556 24557 } 24558 24559 /** 24560 * Computes the bounding box of the skinned mesh, and updates {@link SkinnedMesh#boundingBox}. 24561 * The bounding box is not automatically computed by the engine; this method must be called by your app. 24562 * If the skinned mesh is animated, the bounding box should be recomputed per frame in order to reflect 24563 * the current animation state. 24564 */ 24565 computeBoundingBox() { 24566 24567 const geometry = this.geometry; 24568 24569 if ( this.boundingBox === null ) { 24570 24571 this.boundingBox = new Box3(); 24572 24573 } 24574 24575 this.boundingBox.makeEmpty(); 24576 24577 const positionAttribute = geometry.getAttribute( 'position' ); 24578 24579 for ( let i = 0; i < positionAttribute.count; i ++ ) { 24580 24581 this.getVertexPosition( i, _vertex ); 24582 this.boundingBox.expandByPoint( _vertex ); 24583 24584 } 24585 24586 } 24587 24588 /** 24589 * Computes the bounding sphere of the skinned mesh, and updates {@link SkinnedMesh#boundingSphere}. 24590 * The bounding sphere is automatically computed by the engine once when it is needed, e.g., for ray casting 24591 * and view frustum culling. If the skinned mesh is animated, the bounding sphere should be recomputed 24592 * per frame in order to reflect the current animation state. 24593 */ 24594 computeBoundingSphere() { 24595 24596 const geometry = this.geometry; 24597 24598 if ( this.boundingSphere === null ) { 24599 24600 this.boundingSphere = new Sphere(); 24601 24602 } 24603 24604 this.boundingSphere.makeEmpty(); 24605 24606 const positionAttribute = geometry.getAttribute( 'position' ); 24607 24608 for ( let i = 0; i < positionAttribute.count; i ++ ) { 24609 24610 this.getVertexPosition( i, _vertex ); 24611 this.boundingSphere.expandByPoint( _vertex ); 24612 24613 } 24614 24615 } 24616 24617 copy( source, recursive ) { 24618 24619 super.copy( source, recursive ); 24620 24621 this.bindMode = source.bindMode; 24622 this.bindMatrix.copy( source.bindMatrix ); 24623 this.bindMatrixInverse.copy( source.bindMatrixInverse ); 24624 24625 this.skeleton = source.skeleton; 24626 24627 if ( source.boundingBox !== null ) this.boundingBox = source.boundingBox.clone(); 24628 if ( source.boundingSphere !== null ) this.boundingSphere = source.boundingSphere.clone(); 24629 24630 return this; 24631 24632 } 24633 24634 raycast( raycaster, intersects ) { 24635 24636 const material = this.material; 24637 const matrixWorld = this.matrixWorld; 24638 24639 if ( material === undefined ) return; 24640 24641 // test with bounding sphere in world space 24642 24643 if ( this.boundingSphere === null ) this.computeBoundingSphere(); 24644 24645 _sphere$5.copy( this.boundingSphere ); 24646 _sphere$5.applyMatrix4( matrixWorld ); 24647 24648 if ( raycaster.ray.intersectsSphere( _sphere$5 ) === false ) return; 24649 24650 // convert ray to local space of skinned mesh 24651 24652 _inverseMatrix$2.copy( matrixWorld ).invert(); 24653 _ray$2.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$2 ); 24654 24655 // test with bounding box in local space 24656 24657 if ( this.boundingBox !== null ) { 24658 24659 if ( _ray$2.intersectsBox( this.boundingBox ) === false ) return; 24660 24661 } 24662 24663 // test for intersections with geometry 24664 24665 this._computeIntersections( raycaster, intersects, _ray$2 ); 24666 24667 } 24668 24669 getVertexPosition( index, target ) { 24670 24671 super.getVertexPosition( index, target ); 24672 24673 this.applyBoneTransform( index, target ); 24674 24675 return target; 24676 24677 } 24678 24679 /** 24680 * Binds the given skeleton to the skinned mesh. 24681 * 24682 * @param {Skeleton} skeleton - The skeleton to bind. 24683 * @param {Matrix4} [bindMatrix] - The bind matrix. If no bind matrix is provided, 24684 * the skinned mesh's world matrix will be used instead. 24685 */ 24686 bind( skeleton, bindMatrix ) { 24687 24688 this.skeleton = skeleton; 24689 24690 if ( bindMatrix === undefined ) { 24691 24692 this.updateMatrixWorld( true ); 24693 24694 this.skeleton.calculateInverses(); 24695 24696 bindMatrix = this.matrixWorld; 24697 24698 } 24699 24700 this.bindMatrix.copy( bindMatrix ); 24701 this.bindMatrixInverse.copy( bindMatrix ).invert(); 24702 24703 } 24704 24705 /** 24706 * This method sets the skinned mesh in the rest pose). 24707 */ 24708 pose() { 24709 24710 this.skeleton.pose(); 24711 24712 } 24713 24714 /** 24715 * Normalizes the skin weights which are defined as a buffer attribute 24716 * in the skinned mesh's geometry. 24717 */ 24718 normalizeSkinWeights() { 24719 24720 const vector = new Vector4(); 24721 24722 const skinWeight = this.geometry.attributes.skinWeight; 24723 24724 for ( let i = 0, l = skinWeight.count; i < l; i ++ ) { 24725 24726 vector.fromBufferAttribute( skinWeight, i ); 24727 24728 const scale = 1.0 / vector.manhattanLength(); 24729 24730 if ( scale !== Infinity ) { 24731 24732 vector.multiplyScalar( scale ); 24733 24734 } else { 24735 24736 vector.set( 1, 0, 0, 0 ); // do something reasonable 24737 24738 } 24739 24740 skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w ); 24741 24742 } 24743 24744 } 24745 24746 updateMatrixWorld( force ) { 24747 24748 super.updateMatrixWorld( force ); 24749 24750 if ( this.bindMode === AttachedBindMode ) { 24751 24752 this.bindMatrixInverse.copy( this.matrixWorld ).invert(); 24753 24754 } else if ( this.bindMode === DetachedBindMode ) { 24755 24756 this.bindMatrixInverse.copy( this.bindMatrix ).invert(); 24757 24758 } else { 24759 24760 warn( 'SkinnedMesh: Unrecognized bindMode: ' + this.bindMode ); 24761 24762 } 24763 24764 } 24765 24766 /** 24767 * Applies the bone transform associated with the given index to the given 24768 * vector. Can be used to transform positions or direction vectors by providing 24769 * a Vector4 with 1 or 0 in the w component respectively. Returns the updated vector. 24770 * 24771 * @param {number} index - The vertex index. 24772 * @param {Vector3|Vector4} target - The target object that is used to store the method's result. 24773 * @return {Vector3|Vector4} The updated vertex attribute data. 24774 */ 24775 applyBoneTransform( index, target ) { 24776 24777 const skeleton = this.skeleton; 24778 const geometry = this.geometry; 24779 24780 _skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index ); 24781 _skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index ); 24782 24783 if ( target.isVector4 ) { 24784 24785 _baseVector.copy( target ); 24786 target.set( 0, 0, 0, 0 ); 24787 24788 } else { 24789 24790 _baseVector.set( ...target, 1 ); 24791 target.set( 0, 0, 0 ); 24792 24793 } 24794 24795 _baseVector.applyMatrix4( this.bindMatrix ); 24796 24797 for ( let i = 0; i < 4; i ++ ) { 24798 24799 const weight = _skinWeight.getComponent( i ); 24800 24801 if ( weight !== 0 ) { 24802 24803 const boneIndex = _skinIndex.getComponent( i ); 24804 24805 _matrix4.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] ); 24806 24807 target.addScaledVector( _vector4.copy( _baseVector ).applyMatrix4( _matrix4 ), weight ); 24808 24809 } 24810 24811 } 24812 24813 if ( target.isVector4 ) { 24814 24815 // ensure the homogenous coordinate remains unchanged after vector operations 24816 target.w = _baseVector.w; 24817 24818 } 24819 24820 return target.applyMatrix4( this.bindMatrixInverse ); 24821 24822 } 24823 24824} 24825 24826/** 24827 * A bone which is part of a {@link Skeleton}. The skeleton in turn is used by 24828 * the {@link SkinnedMesh}. 24829 * 24830 * ```js 24831 * const root = new THREE.Bone(); 24832 * const child = new THREE.Bone(); 24833 * 24834 * root.add( child ); 24835 * child.position.y = 5; 24836 * ``` 24837 * 24838 * @augments Object3D 24839 */ 24840class Bone extends Object3D { 24841 24842 /** 24843 * Constructs a new bone. 24844 */ 24845 constructor() { 24846 24847 super(); 24848 24849 /** 24850 * This flag can be used for type testing. 24851 * 24852 * @type {boolean} 24853 * @readonly 24854 * @default true 24855 */ 24856 this.isBone = true; 24857 24858 this.type = 'Bone'; 24859 24860 } 24861 24862} 24863 24864/** 24865 * Creates a texture directly from raw buffer data. 24866 * 24867 * The interpretation of the data depends on type and format: If the type is 24868 * `UnsignedByteType`, a `Uint8Array` will be useful for addressing the 24869 * texel data. If the format is `RGBAFormat`, data needs four values for 24870 * one texel; Red, Green, Blue and Alpha (typically the opacity). 24871 * 24872 * @augments Texture 24873 */ 24874class DataTexture extends Texture { 24875 24876 /** 24877 * Constructs a new data texture. 24878 * 24879 * @param {?TypedArray} [data=null] - The buffer data. 24880 * @param {number} [width=1] - The width of the texture. 24881 * @param {number} [height=1] - The height of the texture. 24882 * @param {number} [format=RGBAFormat] - The texture format. 24883 * @param {number} [type=UnsignedByteType] - The texture type. 24884 * @param {number} [mapping=Texture.DEFAULT_MAPPING] - The texture mapping. 24885 * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value. 24886 * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value. 24887 * @param {number} [magFilter=NearestFilter] - The mag filter value. 24888 * @param {number} [minFilter=NearestFilter] - The min filter value. 24889 * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value. 24890 * @param {string} [colorSpace=NoColorSpace] - The color space. 24891 */ 24892 constructor( data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, colorSpace ) { 24893 24894 super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ); 24895 24896 /** 24897 * This flag can be used for type testing. 24898 * 24899 * @type {boolean} 24900 * @readonly 24901 * @default true 24902 */ 24903 this.isDataTexture = true; 24904 24905 /** 24906 * The image definition of a data texture. 24907 * 24908 * @type {{data:TypedArray,width:number,height:number}} 24909 */ 24910 this.image = { data: data, width: width, height: height }; 24911 24912 /** 24913 * Whether to generate mipmaps (if possible) for a texture. 24914 * 24915 * Overwritten and set to `false` by default. 24916 * 24917 * @type {boolean} 24918 * @default false 24919 */ 24920 this.generateMipmaps = false; 24921 24922 /** 24923 * If set to `true`, the texture is flipped along the vertical axis when 24924 * uploaded to the GPU. 24925 * 24926 * Overwritten and set to `false` by default. 24927 * 24928 * @type {boolean} 24929 * @default false 24930 */ 24931 this.flipY = false; 24932 24933 /** 24934 * Specifies the alignment requirements for the start of each pixel row in memory. 24935 * 24936 * Overwritten and set to `1` by default. 24937 * 24938 * @type {boolean} 24939 * @default 1 24940 */ 24941 this.unpackAlignment = 1; 24942 24943 } 24944 24945} 24946 24947const _offsetMatrix = /*@__PURE__*/ new Matrix4(); 24948const _identityMatrix = /*@__PURE__*/ new Matrix4(); 24949 24950/** 24951 * Class for representing the armatures in `three.js`. The skeleton 24952 * is defined by a hierarchy of bones. 24953 * 24954 * ```js 24955 * const bones = []; 24956 * 24957 * const shoulder = new THREE.Bone(); 24958 * const elbow = new THREE.Bone(); 24959 * const hand = new THREE.Bone(); 24960 * 24961 * shoulder.add( elbow ); 24962 * elbow.add( hand ); 24963 * 24964 * bones.push( shoulder , elbow, hand); 24965 * 24966 * shoulder.position.y = -5; 24967 * elbow.position.y = 0; 24968 * hand.position.y = 5; 24969 * 24970 * const armSkeleton = new THREE.Skeleton( bones ); 24971 * ``` 24972 */ 24973class Skeleton { 24974 24975 /** 24976 * Constructs a new skeleton. 24977 * 24978 * @param {Array<Bone>} [bones] - An array of bones. 24979 * @param {Array<Matrix4>} [boneInverses] - An array of bone inverse matrices. 24980 * If not provided, these matrices will be computed automatically via {@link Skeleton#calculateInverses}. 24981 */ 24982 constructor( bones = [], boneInverses = [] ) { 24983 24984 this.uuid = generateUUID(); 24985 24986 /** 24987 * An array of bones defining the skeleton. 24988 * 24989 * @type {Array<Bone>} 24990 */ 24991 this.bones = bones.slice( 0 ); 24992 24993 /** 24994 * An array of bone inverse matrices. 24995 * 24996 * @type {Array<Matrix4>} 24997 */ 24998 this.boneInverses = boneInverses; 24999 25000 /** 25001 * An array buffer holding the bone data. 25002 * Input data for {@link Skeleton#boneTexture}. 25003 * 25004 * @type {?Float32Array} 25005 * @default null 25006 */ 25007 this.boneMatrices = null; 25008 25009 /** 25010 * A texture holding the bone data for use 25011 * in the vertex shader. 25012 * 25013 * @type {?DataTexture} 25014 * @default null 25015 */ 25016 this.boneTexture = null; 25017 25018 this.init(); 25019 25020 } 25021 25022 /** 25023 * Initializes the skeleton. This method gets automatically called by the constructor 25024 * but depending on how the skeleton is created it might be necessary to call this method 25025 * manually. 25026 */ 25027 init() { 25028 25029 const bones = this.bones; 25030 const boneInverses = this.boneInverses; 25031 25032 this.boneMatrices = new Float32Array( bones.length * 16 ); 25033 25034 // calculate inverse bone matrices if necessary 25035 25036 if ( boneInverses.length === 0 ) { 25037 25038 this.calculateInverses(); 25039 25040 } else { 25041 25042 // handle special case 25043 25044 if ( bones.length !== boneInverses.length ) { 25045 25046 warn( 'Skeleton: Number of inverse bone matrices does not match amount of bones.' ); 25047 25048 this.boneInverses = []; 25049 25050 for ( let i = 0, il = this.bones.length; i < il; i ++ ) { 25051 25052 this.boneInverses.push( new Matrix4() ); 25053 25054 } 25055 25056 } 25057 25058 } 25059 25060 } 25061 25062 /** 25063 * Computes the bone inverse matrices. This method resets {@link Skeleton#boneInverses} 25064 * and fills it with new matrices. 25065 */ 25066 calculateInverses() { 25067 25068 this.boneInverses.length = 0; 25069 25070 for ( let i = 0, il = this.bones.length; i < il; i ++ ) { 25071 25072 const inverse = new Matrix4(); 25073 25074 if ( this.bones[ i ] ) { 25075 25076 inverse.copy( this.bones[ i ].matrixWorld ).invert(); 25077 25078 } 25079 25080 this.boneInverses.push( inverse ); 25081 25082 } 25083 25084 } 25085 25086 /** 25087 * Resets the skeleton to the base pose. 25088 */ 25089 pose() { 25090 25091 // recover the bind-time world matrices 25092 25093 for ( let i = 0, il = this.bones.length; i < il; i ++ ) { 25094 25095 const bone = this.bones[ i ]; 25096 25097 if ( bone ) { 25098 25099 bone.matrixWorld.copy( this.boneInverses[ i ] ).invert(); 25100 25101 } 25102 25103 } 25104 25105 // compute the local matrices, positions, rotations and scales 25106 25107 for ( let i = 0, il = this.bones.length; i < il; i ++ ) { 25108 25109 const bone = this.bones[ i ]; 25110 25111 if ( bone ) { 25112 25113 if ( bone.parent && bone.parent.isBone ) { 25114 25115 bone.matrix.copy( bone.parent.matrixWorld ).invert(); 25116 bone.matrix.multiply( bone.matrixWorld ); 25117 25118 } else { 25119 25120 bone.matrix.copy( bone.matrixWorld ); 25121 25122 } 25123 25124 bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); 25125 25126 } 25127 25128 } 25129 25130 } 25131 25132 /** 25133 * Resets the skeleton to the base pose. 25134 */ 25135 update() { 25136 25137 const bones = this.bones; 25138 const boneInverses = this.boneInverses; 25139 const boneMatrices = this.boneMatrices; 25140 const boneTexture = this.boneTexture; 25141 25142 // flatten bone matrices to array 25143 25144 for ( let i = 0, il = bones.length; i < il; i ++ ) { 25145 25146 // compute the offset between the current and the original transform 25147 25148 const matrix = bones[ i ] ? bones[ i ].matrixWorld : _identityMatrix; 25149 25150 _offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); 25151 _offsetMatrix.toArray( boneMatrices, i * 16 ); 25152 25153 } 25154 25155 if ( boneTexture !== null ) { 25156 25157 boneTexture.needsUpdate = true; 25158 25159 } 25160 25161 } 25162 25163 /** 25164 * Returns a new skeleton with copied values from this instance. 25165 * 25166 * @return {Skeleton} A clone of this instance. 25167 */ 25168 clone() { 25169 25170 return new Skeleton( this.bones, this.boneInverses ); 25171 25172 } 25173 25174 /** 25175 * Computes a data texture for passing bone data to the vertex shader. 25176 * 25177 * @return {Skeleton} A reference of this instance. 25178 */ 25179 computeBoneTexture() { 25180 25181 // layout (1 matrix = 4 pixels) 25182 // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) 25183 // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) 25184 // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) 25185 // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) 25186 // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) 25187 25188 let size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix 25189 size = Math.ceil( size / 4 ) * 4; 25190 size = Math.max( size, 4 ); 25191 25192 const boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel 25193 boneMatrices.set( this.boneMatrices ); // copy current values 25194 25195 const boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); 25196 boneTexture.needsUpdate = true; 25197 25198 this.boneMatrices = boneMatrices; 25199 this.boneTexture = boneTexture; 25200 25201 return this; 25202 25203 } 25204 25205 /** 25206 * Searches through the skeleton's bone array and returns the first with a 25207 * matching name. 25208 * 25209 * @param {string} name - The name of the bone. 25210 * @return {Bone|undefined} The found bone. `undefined` if no bone has been found. 25211 */ 25212 getBoneByName( name ) { 25213 25214 for ( let i = 0, il = this.bones.length; i < il; i ++ ) { 25215 25216 const bone = this.bones[ i ]; 25217 25218 if ( bone.name === name ) { 25219 25220 return bone; 25221 25222 } 25223 25224 } 25225 25226 return undefined; 25227 25228 } 25229 25230 /** 25231 * Frees the GPU-related resources allocated by this instance. Call this 25232 * method whenever this instance is no longer used in your app. 25233 */ 25234 dispose( ) { 25235 25236 if ( this.boneTexture !== null ) { 25237 25238 this.boneTexture.dispose(); 25239 25240 this.boneTexture = null; 25241 25242 } 25243 25244 } 25245 25246 /** 25247 * Setups the skeleton by the given JSON and bones. 25248 * 25249 * @param {Object} json - The skeleton as serialized JSON. 25250 * @param {Object<string, Bone>} bones - An array of bones. 25251 * @return {Skeleton} A reference of this instance. 25252 */ 25253 fromJSON( json, bones ) { 25254 25255 this.uuid = json.uuid; 25256 25257 for ( let i = 0, l = json.bones.length; i < l; i ++ ) { 25258 25259 const uuid = json.bones[ i ]; 25260 let bone = bones[ uuid ]; 25261 25262 if ( bone === undefined ) { 25263 25264 warn( 'Skeleton: No bone found with UUID:', uuid ); 25265 bone = new Bone(); 25266 25267 } 25268 25269 this.bones.push( bone ); 25270 this.boneInverses.push( new Matrix4().fromArray( json.boneInverses[ i ] ) ); 25271 25272 } 25273 25274 this.init(); 25275 25276 return this; 25277 25278 } 25279 25280 /** 25281 * Serializes the skeleton into JSON. 25282 * 25283 * @return {Object} A JSON object representing the serialized skeleton. 25284 * @see {@link ObjectLoader#parse} 25285 */ 25286 toJSON() { 25287 25288 const data = { 25289 metadata: { 25290 version: 4.7, 25291 type: 'Skeleton', 25292 generator: 'Skeleton.toJSON' 25293 }, 25294 bones: [], 25295 boneInverses: [] 25296 }; 25297 25298 data.uuid = this.uuid; 25299 25300 const bones = this.bones; 25301 const boneInverses = this.boneInverses; 25302 25303 for ( let i = 0, l = bones.length; i < l; i ++ ) { 25304 25305 const bone = bones[ i ]; 25306 data.bones.push( bone.uuid ); 25307 25308 const boneInverse = boneInverses[ i ]; 25309 data.boneInverses.push( boneInverse.toArray() ); 25310 25311 } 25312 25313 return data; 25314 25315 } 25316 25317} 25318 25319/** 25320 * An instanced version of a buffer attribute. 25321 * 25322 * @augments BufferAttribute 25323 */ 25324class InstancedBufferAttribute extends BufferAttribute { 25325 25326 /** 25327 * Constructs a new instanced buffer attribute. 25328 * 25329 * @param {TypedArray} array - The array holding the attribute data. 25330 * @param {number} itemSize - The item size. 25331 * @param {boolean} [normalized=false] - Whether the data are normalized or not. 25332 * @param {number} [meshPerAttribute=1] - How often a value of this buffer attribute should be repeated. 25333 */ 25334 constructor( array, itemSize, normalized, meshPerAttribute = 1 ) { 25335 25336 super( array, itemSize, normalized ); 25337 25338 /** 25339 * This flag can be used for type testing. 25340 * 25341 * @type {boolean} 25342 * @readonly 25343 * @default true 25344 */ 25345 this.isInstancedBufferAttribute = true; 25346 25347 /** 25348 * Defines how often a value of this buffer attribute should be repeated. A 25349 * value of one means that each value of the instanced attribute is used for 25350 * a single instance. A value of two means that each value is used for two 25351 * consecutive instances (and so on). 25352 * 25353 * @type {number} 25354 * @default 1 25355 */ 25356 this.meshPerAttribute = meshPerAttribute; 25357 25358 } 25359 25360 copy( source ) { 25361 25362 super.copy( source ); 25363 25364 this.meshPerAttribute = source.meshPerAttribute; 25365 25366 return this; 25367 25368 } 25369 25370 toJSON() { 25371 25372 const data = super.toJSON(); 25373 25374 data.meshPerAttribute = this.meshPerAttribute; 25375 25376 data.isInstancedBufferAttribute = true; 25377 25378 return data; 25379 25380 } 25381 25382} 25383 25384const _instanceLocalMatrix = /*@__PURE__*/ new Matrix4(); 25385const _instanceWorldMatrix = /*@__PURE__*/ new Matrix4(); 25386 25387const _instanceIntersects = []; 25388 25389const _box3 = /*@__PURE__*/ new Box3(); 25390const _identity = /*@__PURE__*/ new Matrix4(); 25391const _mesh$1 = /*@__PURE__*/ new Mesh(); 25392const _sphere$4 = /*@__PURE__*/ new Sphere(); 25393 25394/** 25395 * A special version of a mesh with instanced rendering support. Use 25396 * this class if you have to render a large number of objects with the same 25397 * geometry and material(s) but with different world transformations. The usage 25398 * of 'InstancedMesh' will help you to reduce the number of draw calls and thus 25399 * improve the overall rendering performance in your application. 25400 * 25401 * @augments Mesh 25402 */ 25403class InstancedMesh extends Mesh { 25404 25405 /** 25406 * Constructs a new instanced mesh. 25407 * 25408 * @param {BufferGeometry} [geometry] - The mesh geometry. 25409 * @param {Material|Array<Material>} [material] - The mesh material. 25410 * @param {number} count - The number of instances. 25411 */ 25412 constructor( geometry, material, count ) { 25413 25414 super( geometry, material ); 25415 25416 /** 25417 * This flag can be used for type testing. 25418 * 25419 * @type {boolean} 25420 * @readonly 25421 * @default true 25422 */ 25423 this.isInstancedMesh = true; 25424 25425 /** 25426 * Represents the local transformation of all instances. You have to set its 25427 * {@link BufferAttribute#needsUpdate} flag to true if you modify instanced data 25428 * via {@link InstancedMesh#setMatrixAt}. 25429 * 25430 * @type {InstancedBufferAttribute} 25431 */ 25432 this.instanceMatrix = new InstancedBufferAttribute( new Float32Array( count * 16 ), 16 ); 25433 25434 /** 25435 * Represents the color of all instances. You have to set its 25436 * {@link BufferAttribute#needsUpdate} flag to true if you modify instanced data 25437 * via {@link InstancedMesh#setColorAt}. 25438 * 25439 * @type {?InstancedBufferAttribute} 25440 * @default null 25441 */ 25442 this.instanceColor = null; 25443 25444 /** 25445 * Represents the morph target weights of all instances. You have to set its 25446 * {@link Texture#needsUpdate} flag to true if you modify instanced data 25447 * via {@link InstancedMesh#setMorphAt}. 25448 * 25449 * @type {?DataTexture} 25450 * @default null 25451 */ 25452 this.morphTexture = null; 25453 25454 /** 25455 * The number of instances. 25456 * 25457 * @type {number} 25458 */ 25459 this.count = count; 25460 25461 /** 25462 * The bounding box of the instanced mesh. Can be computed via {@link InstancedMesh#computeBoundingBox}. 25463 * 25464 * @type {?Box3} 25465 * @default null 25466 */ 25467 this.boundingBox = null; 25468 25469 /** 25470 * The bounding sphere of the instanced mesh. Can be computed via {@link InstancedMesh#computeBoundingSphere}. 25471 * 25472 * @type {?Sphere} 25473 * @default null 25474 */ 25475 this.boundingSphere = null; 25476 25477 for ( let i = 0; i < count; i ++ ) { 25478 25479 this.setMatrixAt( i, _identity ); 25480 25481 } 25482 25483 } 25484 25485 /** 25486 * Computes the bounding box of the instanced mesh, and updates {@link InstancedMesh#boundingBox}. 25487 * The bounding box is not automatically computed by the engine; this method must be called by your app. 25488 * You may need to recompute the bounding box if an instance is transformed via {@link InstancedMesh#setMatrixAt}. 25489 */ 25490 computeBoundingBox() { 25491 25492 const geometry = this.geometry; 25493 const count = this.count; 25494 25495 if ( this.boundingBox === null ) { 25496 25497 this.boundingBox = new Box3(); 25498 25499 } 25500 25501 if ( geometry.boundingBox === null ) { 25502 25503 geometry.computeBoundingBox(); 25504 25505 } 25506 25507 this.boundingBox.makeEmpty(); 25508 25509 for ( let i = 0; i < count; i ++ ) { 25510 25511 this.getMatrixAt( i, _instanceLocalMatrix ); 25512 25513 _box3.copy( geometry.boundingBox ).applyMatrix4( _instanceLocalMatrix ); 25514 25515 this.boundingBox.union( _box3 ); 25516 25517 } 25518 25519 } 25520 25521 /** 25522 * Computes the bounding sphere of the instanced mesh, and updates {@link InstancedMesh#boundingSphere} 25523 * The engine automatically computes the bounding sphere when it is needed, e.g., for ray casting or view frustum culling. 25524 * You may need to recompute the bounding sphere if an instance is transformed via {@link InstancedMesh#setMatrixAt}. 25525 */ 25526 computeBoundingSphere() { 25527 25528 const geometry = this.geometry; 25529 const count = this.count; 25530 25531 if ( this.boundingSphere === null ) { 25532 25533 this.boundingSphere = new Sphere(); 25534 25535 } 25536 25537 if ( geometry.boundingSphere === null ) { 25538 25539 geometry.computeBoundingSphere(); 25540 25541 } 25542 25543 this.boundingSphere.makeEmpty(); 25544 25545 for ( let i = 0; i < count; i ++ ) { 25546 25547 this.getMatrixAt( i, _instanceLocalMatrix ); 25548 25549 _sphere$4.copy( geometry.boundingSphere ).applyMatrix4( _instanceLocalMatrix ); 25550 25551 this.boundingSphere.union( _sphere$4 ); 25552 25553 } 25554 25555 } 25556 25557 copy( source, recursive ) { 25558 25559 super.copy( source, recursive ); 25560 25561 this.instanceMatrix.copy( source.instanceMatrix ); 25562 25563 if ( source.morphTexture !== null ) this.morphTexture = source.morphTexture.clone(); 25564 if ( source.instanceColor !== null ) this.instanceColor = source.instanceColor.clone(); 25565 25566 this.count = source.count; 25567 25568 if ( source.boundingBox !== null ) this.boundingBox = source.boundingBox.clone(); 25569 if ( source.boundingSphere !== null ) this.boundingSphere = source.boundingSphere.clone(); 25570 25571 return this; 25572 25573 } 25574 25575 /** 25576 * Gets the color of the defined instance. 25577 * 25578 * @param {number} index - The instance index. 25579 * @param {Color} color - The target object that is used to store the method's result. 25580 * @return {Color} A reference to the target color. 25581 */ 25582 getColorAt( index, color ) { 25583 25584 if ( this.instanceColor === null ) { 25585 25586 return color.setRGB( 1, 1, 1 ); 25587 25588 } else { 25589 25590 return color.fromArray( this.instanceColor.array, index * 3 ); 25591 25592 } 25593 25594 } 25595 25596 /** 25597 * Gets the local transformation matrix of the defined instance. 25598 * 25599 * @param {number} index - The instance index. 25600 * @param {Matrix4} matrix - The target object that is used to store the method's result. 25601 * @return {Matrix4} A reference to the target matrix. 25602 */ 25603 getMatrixAt( index, matrix ) { 25604 25605 return matrix.fromArray( this.instanceMatrix.array, index * 16 ); 25606 25607 } 25608 25609 /** 25610 * Gets the morph target weights of the defined instance. 25611 * 25612 * @param {number} index - The instance index. 25613 * @param {Mesh} object - The target object that is used to store the method's result. 25614 */ 25615 getMorphAt( index, object ) { 25616 25617 const objectInfluences = object.morphTargetInfluences; 25618 25619 const array = this.morphTexture.source.data.data; 25620 25621 const len = objectInfluences.length + 1; // All influences + the baseInfluenceSum 25622 25623 const dataIndex = index * len + 1; // Skip the baseInfluenceSum at the beginning 25624 25625 for ( let i = 0; i < objectInfluences.length; i ++ ) { 25626 25627 objectInfluences[ i ] = array[ dataIndex + i ]; 25628 25629 } 25630 25631 } 25632 25633 raycast( raycaster, intersects ) { 25634 25635 const matrixWorld = this.matrixWorld; 25636 const raycastTimes = this.count; 25637 25638 _mesh$1.geometry = this.geometry; 25639 _mesh$1.material = this.material; 25640 25641 if ( _mesh$1.material === undefined ) return; 25642 25643 // test with bounding sphere first 25644 25645 if ( this.boundingSphere === null ) this.computeBoundingSphere(); 25646 25647 _sphere$4.copy( this.boundingSphere ); 25648 _sphere$4.applyMatrix4( matrixWorld ); 25649 25650 if ( raycaster.ray.intersectsSphere( _sphere$4 ) === false ) return; 25651 25652 // now test each instance 25653 25654 for ( let instanceId = 0; instanceId < raycastTimes; instanceId ++ ) { 25655 25656 // calculate the world matrix for each instance 25657 25658 this.getMatrixAt( instanceId, _instanceLocalMatrix ); 25659 25660 _instanceWorldMatrix.multiplyMatrices( matrixWorld, _instanceLocalMatrix ); 25661 25662 // the mesh represents this single instance 25663 25664 _mesh$1.matrixWorld = _instanceWorldMatrix; 25665 25666 _mesh$1.raycast( raycaster, _instanceIntersects ); 25667 25668 // process the result of raycast 25669 25670 for ( let i = 0, l = _instanceIntersects.length; i < l; i ++ ) { 25671 25672 const intersect = _instanceIntersects[ i ]; 25673 intersect.instanceId = instanceId; 25674 intersect.object = this; 25675 intersects.push( intersect ); 25676 25677 } 25678 25679 _instanceIntersects.length = 0; 25680 25681 } 25682 25683 } 25684 25685 /** 25686 * Sets the given color to the defined instance. Make sure you set the `needsUpdate` flag of 25687 * {@link InstancedMesh#instanceColor} to `true` after updating all the colors. 25688 * 25689 * @param {number} index - The instance index. 25690 * @param {Color} color - The instance color. 25691 * @return {InstancedMesh} A reference to this instanced mesh. 25692 */ 25693 setColorAt( index, color ) { 25694 25695 if ( this.instanceColor === null ) { 25696 25697 this.instanceColor = new InstancedBufferAttribute( new Float32Array( this.instanceMatrix.count * 3 ).fill( 1 ), 3 ); 25698 25699 } 25700 25701 color.toArray( this.instanceColor.array, index * 3 ); 25702 return this; 25703 25704 } 25705 25706 /** 25707 * Sets the given local transformation matrix to the defined instance. Make sure you set the `needsUpdate` flag of 25708 * {@link InstancedMesh#instanceMatrix} to `true` after updating all the matrices. 25709 * 25710 * @param {number} index - The instance index. 25711 * @param {Matrix4} matrix - The local transformation. 25712 * @return {InstancedMesh} A reference to this instanced mesh. 25713 */ 25714 setMatrixAt( index, matrix ) { 25715 25716 matrix.toArray( this.instanceMatrix.array, index * 16 ); 25717 return this; 25718 25719 } 25720 25721 /** 25722 * Sets the morph target weights to the defined instance. Make sure you set the `needsUpdate` flag of 25723 * {@link InstancedMesh#morphTexture} to `true` after updating all the influences. 25724 * 25725 * @param {number} index - The instance index. 25726 * @param {Mesh} object - A mesh which `morphTargetInfluences` property containing the morph target weights 25727 * of a single instance. 25728 * @return {InstancedMesh} A reference to this instanced mesh. 25729 */ 25730 setMorphAt( index, object ) { 25731 25732 const objectInfluences = object.morphTargetInfluences; 25733 25734 const len = objectInfluences.length + 1; // morphBaseInfluence + all influences 25735 25736 if ( this.morphTexture === null ) { 25737 25738 this.morphTexture = new DataTexture( new Float32Array( len * this.count ), len, this.count, RedFormat, FloatType ); 25739 25740 } 25741 25742 const array = this.morphTexture.source.data.data; 25743 25744 let morphInfluencesSum = 0; 25745 25746 for ( let i = 0; i < objectInfluences.length; i ++ ) { 25747 25748 morphInfluencesSum += objectInfluences[ i ]; 25749 25750 } 25751 25752 const morphBaseInfluence = this.geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; 25753 25754 const dataIndex = len * index; 25755 25756 array[ dataIndex ] = morphBaseInfluence; 25757 25758 array.set( objectInfluences, dataIndex + 1 ); 25759 return this; 25760 25761 } 25762 25763 updateMorphTargets() { 25764 25765 } 25766 25767 /** 25768 * Frees the GPU-related resources allocated by this instance. Call this 25769 * method whenever this instance is no longer used in your app. 25770 */ 25771 dispose() { 25772 25773 super.dispose(); 25774 25775 if ( this.morphTexture !== null ) { 25776 25777 this.morphTexture.dispose(); 25778 this.morphTexture = null; 25779 25780 } 25781 25782 } 25783 25784} 25785 25786const _sphere$3 = /*@__PURE__*/ new Sphere(); 25787const _defaultSpriteCenter = /*@__PURE__*/ new Vector2( 0.5, 0.5 ); 25788const _vector$6 = /*@__PURE__*/ new Vector3(); 25789 25790/** 25791 * Frustums are used to determine what is inside the camera's field of view. 25792 * They help speed up the rendering process - objects which lie outside a camera's 25793 * frustum can safely be excluded from rendering. 25794 * 25795 * This class is mainly intended for use internally by a renderer. 25796 */ 25797class Frustum { 25798 25799 /** 25800 * Constructs a new frustum. 25801 * 25802 * @param {Plane} [p0] - The first plane that encloses the frustum. 25803 * @param {Plane} [p1] - The second plane that encloses the frustum. 25804 * @param {Plane} [p2] - The third plane that encloses the frustum. 25805 * @param {Plane} [p3] - The fourth plane that encloses the frustum. 25806 * @param {Plane} [p4] - The fifth plane that encloses the frustum. 25807 * @param {Plane} [p5] - The sixth plane that encloses the frustum. 25808 */ 25809 constructor( p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane() ) { 25810 25811 /** 25812 * This array holds the planes that enclose the frustum. 25813 * 25814 * @type {Array<Plane>} 25815 */ 25816 this.planes = [ p0, p1, p2, p3, p4, p5 ]; 25817 25818 } 25819 25820 /** 25821 * Sets the frustum planes by copying the given planes. 25822 * 25823 * @param {Plane} [p0] - The first plane that encloses the frustum. 25824 * @param {Plane} [p1] - The second plane that encloses the frustum. 25825 * @param {Plane} [p2] - The third plane that encloses the frustum. 25826 * @param {Plane} [p3] - The fourth plane that encloses the frustum. 25827 * @param {Plane} [p4] - The fifth plane that encloses the frustum. 25828 * @param {Plane} [p5] - The sixth plane that encloses the frustum. 25829 * @return {Frustum} A reference to this frustum. 25830 */ 25831 set( p0, p1, p2, p3, p4, p5 ) { 25832 25833 const planes = this.planes; 25834 25835 planes[ 0 ].copy( p0 ); 25836 planes[ 1 ].copy( p1 ); 25837 planes[ 2 ].copy( p2 ); 25838 planes[ 3 ].copy( p3 ); 25839 planes[ 4 ].copy( p4 ); 25840 planes[ 5 ].copy( p5 ); 25841 25842 return this; 25843 25844 } 25845 25846 /** 25847 * Copies the values of the given frustum to this instance. 25848 * 25849 * @param {Frustum} frustum - The frustum to copy. 25850 * @return {Frustum} A reference to this frustum. 25851 */ 25852 copy( frustum ) { 25853 25854 const planes = this.planes; 25855 25856 for ( let i = 0; i < 6; i ++ ) { 25857 25858 planes[ i ].copy( frustum.planes[ i ] ); 25859 25860 } 25861 25862 return this; 25863 25864 } 25865 25866 /** 25867 * Sets the frustum planes from the given projection matrix. 25868 * 25869 * @param {Matrix4} m - The projection matrix. 25870 * @param {(WebGLCoordinateSystem|WebGPUCoordinateSystem)} coordinateSystem - The coordinate system. 25871 * @param {boolean} [reversedDepth=false] - Whether to use a reversed depth. 25872 * @return {Frustum} A reference to this frustum. 25873 */ 25874 setFromProjectionMatrix( m, coordinateSystem = WebGLCoordinateSystem, reversedDepth = false ) { 25875 25876 const planes = this.planes; 25877 const me = m.elements; 25878 const me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; 25879 const me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; 25880 const me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; 25881 const me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; 25882 25883 planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); 25884 planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); 25885 planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); 25886 planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); 25887 25888 if ( reversedDepth ) { 25889 25890 planes[ 4 ].setComponents( me2, me6, me10, me14 ).normalize(); // far 25891 planes[ 5 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); // near 25892 25893 } else { 25894 25895 planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); // far 25896 25897 if ( coordinateSystem === WebGLCoordinateSystem ) { 25898 25899 planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); // near 25900 25901 } else if ( coordinateSystem === WebGPUCoordinateSystem ) { 25902 25903 planes[ 5 ].setComponents( me2, me6, me10, me14 ).normalize(); // near 25904 25905 } else { 25906 25907 throw new Error( 'THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: ' + coordinateSystem ); 25908 25909 } 25910 25911 } 25912 25913 return this; 25914 25915 } 25916 25917 /** 25918 * Returns `true` if the 3D object's bounding sphere is intersecting this frustum. 25919 * 25920 * Note that the 3D object must have a geometry so that the bounding sphere can be calculated. 25921 * 25922 * @param {Object3D} object - The 3D object to test. 25923 * @return {boolean} Whether the 3D object's bounding sphere is intersecting this frustum or not. 25924 */ 25925 intersectsObject( object ) { 25926 25927 if ( object.boundingSphere !== undefined ) { 25928 25929 if ( object.boundingSphere === null ) object.computeBoundingSphere(); 25930 25931 _sphere$3.copy( object.boundingSphere ).applyMatrix4( object.matrixWorld ); 25932 25933 } else { 25934 25935 const geometry = object.geometry; 25936 25937 if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); 25938 25939 _sphere$3.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld ); 25940 25941 } 25942 25943 return this.intersectsSphere( _sphere$3 ); 25944 25945 } 25946 25947 /** 25948 * Returns `true` if the given sprite is intersecting this frustum. 25949 * 25950 * @param {Sprite} sprite - The sprite to test. 25951 * @return {boolean} Whether the sprite is intersecting this frustum or not. 25952 */ 25953 intersectsSprite( sprite ) { 25954 25955 _sphere$3.center.set( 0, 0, 0 ); 25956 25957 const offset = _defaultSpriteCenter.distanceTo( sprite.center ); 25958 25959 _sphere$3.radius = 0.7071067811865476 + offset; 25960 _sphere$3.applyMatrix4( sprite.matrixWorld ); 25961 25962 return this.intersectsSphere( _sphere$3 ); 25963 25964 } 25965 25966 /** 25967 * Returns `true` if the given bounding sphere is intersecting this frustum. 25968 * 25969 * This is a fast, conservative test that favors performance over precision. It can 25970 * report false positives for spheres that lie outside the frustum but are not separated 25971 * by a single frustum plane. It never reports false negatives, so it is safe for culling. 25972 * 25973 * @param {Sphere} sphere - The bounding sphere to test. 25974 * @return {boolean} Whether the bounding sphere is intersecting this frustum or not. 25975 */ 25976 intersectsSphere( sphere ) { 25977 25978 const planes = this.planes; 25979 const center = sphere.center; 25980 const negRadius = - sphere.radius; 25981 25982 for ( let i = 0; i < 6; i ++ ) { 25983 25984 const distance = planes[ i ].distanceToPoint( center ); 25985 25986 if ( distance < negRadius ) { 25987 25988 return false; 25989 25990 } 25991 25992 } 25993 25994 return true; 25995 25996 } 25997 25998 /** 25999 * Returns `true` if the given bounding box is intersecting this frustum. 26000 * 26001 * This is a fast, conservative test that favors performance over precision. It can 26002 * report false positives for large boxes that lie outside the frustum but are not 26003 * separated by a single frustum plane. It never reports false negatives, so it is 26004 * safe for culling. 26005 * 26006 * @param {Box3} box - The bounding box to test. 26007 * @return {boolean} Whether the bounding box is intersecting this frustum or not. 26008 */ 26009 intersectsBox( box ) { 26010 26011 const planes = this.planes; 26012 26013 for ( let i = 0; i < 6; i ++ ) { 26014 26015 const plane = planes[ i ]; 26016 26017 // corner at max distance 26018 26019 _vector$6.x = plane.normal.x > 0 ? box.max.x : box.min.x; 26020 _vector$6.y = plane.normal.y > 0 ? box.max.y : box.min.y; 26021 _vector$6.z = plane.normal.z > 0 ? box.max.z : box.min.z; 26022 26023 if ( plane.distanceToPoint( _vector$6 ) < 0 ) { 26024 26025 return false; 26026 26027 } 26028 26029 } 26030 26031 return true; 26032 26033 } 26034 26035 /** 26036 * Returns `true` if the given point lies within the frustum. 26037 * 26038 * @param {Vector3} point - The point to test. 26039 * @return {boolean} Whether the point lies within this frustum or not. 26040 */ 26041 containsPoint( point ) { 26042 26043 const planes = this.planes; 26044 26045 for ( let i = 0; i < 6; i ++ ) { 26046 26047 if ( planes[ i ].distanceToPoint( point ) < 0 ) { 26048 26049 return false; 26050 26051 } 26052 26053 } 26054 26055 return true; 26056 26057 } 26058 26059 /** 26060 * Returns a new frustum with copied values from this instance. 26061 * 26062 * @return {Frustum} A clone of this instance. 26063 */ 26064 clone() { 26065 26066 return new this.constructor().copy( this ); 26067 26068 } 26069 26070} 26071 26072const _projScreenMatrix$1 = /*@__PURE__*/ new Matrix4(); 26073 26074/** 26075 * FrustumArray is used to determine if an object is visible in at least one camera 26076 * from an array of cameras. This is particularly useful for multi-view renderers. 26077*/ 26078class FrustumArray { 26079 26080 /** 26081 * Constructs a new frustum array. 26082 * 26083 */ 26084 constructor() { 26085 26086 /** 26087 * The coordinate system to use. 26088 * 26089 * @type {WebGLCoordinateSystem|WebGPUCoordinateSystem} 26090 * @default WebGLCoordinateSystem 26091 */ 26092 this.coordinateSystem = WebGLCoordinateSystem; 26093 26094 /** 26095 * A pool of frustum instances. It may hold more entries than are 26096 * currently in use; surplus instances are kept for reuse to avoid 26097 * reallocating when array cameras of different lengths are rendered. 26098 * 26099 * @private 26100 * @type {Array<Frustum>} 26101 */ 26102 this._frustums = []; 26103 26104 /** 26105 * The number of frustums in {@link FrustumArray#_frustums} that are currently 26106 * in use. 26107 * 26108 * @private 26109 * @type {number} 26110 * @default 0 26111 */ 26112 this._count = 0; 26113 26114 } 26115 26116 /** 26117 * Computes and caches a frustum for each camera of the given array camera. 26118 * 26119 * @param {ArrayCamera} cameraArray - The array camera whose sub-cameras define the frustums. 26120 * @return {FrustumArray} A reference to this frustum array. 26121 */ 26122 setFromArrayCamera( cameraArray ) { 26123 26124 const cameras = cameraArray.cameras; 26125 const frustums = this._frustums; 26126 26127 for ( let i = 0; i < cameras.length; i ++ ) { 26128 26129 const camera = cameras[ i ]; 26130 26131 _projScreenMatrix$1.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); 26132 26133 if ( frustums[ i ] === undefined ) frustums[ i ] = new Frustum(); 26134 26135 frustums[ i ].setFromProjectionMatrix( _projScreenMatrix$1, camera.coordinateSystem, camera.reversedDepth ); 26136 26137 } 26138 26139 this._count = cameras.length; 26140 26141 return this; 26142 26143 } 26144 26145 /** 26146 * Returns `true` if the 3D object's bounding sphere is intersecting any cached frustum. 26147 * 26148 * {@link FrustumArray#setFromArrayCamera} must be called once per render before this method. 26149 * 26150 * @param {Object3D} object - The 3D object to test. 26151 * @return {boolean} Whether the 3D object is visible in any camera. 26152 */ 26153 intersectsObject( object ) { 26154 26155 const frustums = this._frustums; 26156 26157 for ( let i = 0; i < this._count; i ++ ) { 26158 26159 if ( frustums[ i ].intersectsObject( object ) ) return true; 26160 26161 } 26162 26163 return false; 26164 26165 } 26166 26167 /** 26168 * Returns `true` if the given sprite is intersecting any cached frustum. 26169 * 26170 * {@link FrustumArray#setFromArrayCamera} must be called once per render before this method. 26171 * 26172 * @param {Sprite} sprite - The sprite to test. 26173 * @return {boolean} Whether the sprite is visible in any camera. 26174 */ 26175 intersectsSprite( sprite ) { 26176 26177 const frustums = this._frustums; 26178 26179 for ( let i = 0; i < this._count; i ++ ) { 26180 26181 if ( frustums[ i ].intersectsSprite( sprite ) ) return true; 26182 26183 } 26184 26185 return false; 26186 26187 } 26188 26189 /** 26190 * Returns `true` if the given bounding sphere is intersecting any cached frustum. 26191 * 26192 * {@link FrustumArray#setFromArrayCamera} must be called once per render before this method. 26193 * 26194 * @param {Sphere} sphere - The bounding sphere to test. 26195 * @return {boolean} Whether the sphere is visible in any camera. 26196 */ 26197 intersectsSphere( sphere ) { 26198 26199 const frustums = this._frustums; 26200 26201 for ( let i = 0; i < this._count; i ++ ) { 26202 26203 if ( frustums[ i ].intersectsSphere( sphere ) ) return true; 26204 26205 } 26206 26207 return false; 26208 26209 } 26210 26211 /** 26212 * Returns `true` if the given bounding box is intersecting any cached frustum. 26213 * 26214 * {@link FrustumArray#setFromArrayCamera} must be called once per render before this method. 26215 * 26216 * @param {Box3} box - The bounding box to test. 26217 * @return {boolean} Whether the box is visible in any camera. 26218 */ 26219 intersectsBox( box ) { 26220 26221 const frustums = this._frustums; 26222 26223 for ( let i = 0; i < this._count; i ++ ) { 26224 26225 if ( frustums[ i ].intersectsBox( box ) ) return true; 26226 26227 } 26228 26229 return false; 26230 26231 } 26232 26233 /** 26234 * Returns `true` if the given point lies within any cached frustum. 26235 * 26236 * {@link FrustumArray#setFromArrayCamera} must be called once per render before this method. 26237 * 26238 * @param {Vector3} point - The point to test. 26239 * @return {boolean} Whether the point is visible in any camera. 26240 */ 26241 containsPoint( point ) { 26242 26243 const frustums = this._frustums; 26244 26245 for ( let i = 0; i < this._count; i ++ ) { 26246 26247 if ( frustums[ i ].containsPoint( point ) ) return true; 26248 26249 } 26250 26251 return false; 26252 26253 } 26254 26255 /** 26256 * Copies the values of the given frustum array to this instance. 26257 * 26258 * @param {FrustumArray} source - The frustum array to copy. 26259 * @return {FrustumArray} A reference to this frustum array. 26260 */ 26261 copy( source ) { 26262 26263 this.coordinateSystem = source.coordinateSystem; 26264 26265 const frustums = this._frustums; 26266 const sourceFrustums = source._frustums; 26267 26268 for ( let i = 0; i < source._count; i ++ ) { 26269 26270 if ( frustums[ i ] === undefined ) frustums[ i ] = new Frustum(); 26271 26272 frustums[ i ].copy( sourceFrustums[ i ] ); 26273 26274 } 26275 26276 this._count = source._count; 26277 26278 return this; 26279 26280 } 26281 26282 /** 26283 * Returns a new frustum array with copied values from this instance. 26284 * 26285 * @return {FrustumArray} A clone of this instance. 26286 */ 26287 clone() { 26288 26289 return new FrustumArray().copy( this ); 26290 26291 } 26292 26293} 26294 26295function ascIdSort( a, b ) { 26296 26297 return a - b; 26298 26299} 26300 26301function sortOpaque( a, b ) { 26302 26303 return a.z - b.z; 26304 26305} 26306 26307function sortTransparent( a, b ) { 26308 26309 return b.z - a.z; 26310 26311} 26312 26313class MultiDrawRenderList { 26314 26315 constructor() { 26316 26317 this.index = 0; 26318 this.pool = []; 26319 this.list = []; 26320 26321 } 26322 26323 push( start, count, z, index ) { 26324 26325 const pool = this.pool; 26326 const list = this.list; 26327 if ( this.index >= pool.length ) { 26328 26329 pool.push( { 26330 26331 start: -1, 26332 count: -1, 26333 z: -1, 26334 index: -1, 26335 26336 } ); 26337 26338 } 26339 26340 const item = pool[ this.index ]; 26341 list.push( item ); 26342 this.index ++; 26343 26344 item.start = start; 26345 item.count = count; 26346 item.z = z; 26347 item.index = index; 26348 26349 } 26350 26351 reset() { 26352 26353 this.list.length = 0; 26354 this.index = 0; 26355 26356 } 26357 26358} 26359 26360const _matrix$1 = /*@__PURE__*/ new Matrix4(); 26361const _whiteColor = /*@__PURE__*/ new Color( 1, 1, 1 ); 26362const _frustum = /*@__PURE__*/ new Frustum(); 26363const _frustumArray = /*@__PURE__*/ new FrustumArray(); 26364const _box$1 = /*@__PURE__*/ new Box3(); 26365const _sphere$2 = /*@__PURE__*/ new Sphere(); 26366const _vector$5 = /*@__PURE__*/ new Vector3(); 26367const _forward$1 = /*@__PURE__*/ new Vector3(); 26368const _temp = /*@__PURE__*/ new Vector3(); 26369const _renderList = /*@__PURE__*/ new MultiDrawRenderList(); 26370const _mesh = /*@__PURE__*/ new Mesh(); 26371const _batchIntersects = []; 26372 26373// copies data from attribute "src" into "target" starting at "targetOffset" 26374function copyAttributeData( src, target, targetOffset = 0 ) { 26375 26376 const itemSize = target.itemSize; 26377 if ( src.isInterleavedBufferAttribute || src.array.constructor !== target.array.constructor ) { 26378 26379 // use the component getters and setters if the array data cannot 26380 // be copied directly 26381 const vertexCount = src.count; 26382 for ( let i = 0; i < vertexCount; i ++ ) { 26383 26384 for ( let c = 0; c < itemSize; c ++ ) { 26385 26386 target.setComponent( i + targetOffset, c, src.getComponent( i, c ) ); 26387 26388 } 26389 26390 } 26391 26392 } else { 26393 26394 // faster copy approach using typed array set function 26395 target.array.set( src.array, targetOffset * itemSize ); 26396 26397 } 26398 26399 target.needsUpdate = true; 26400 26401} 26402 26403// safely copies array contents to a potentially smaller array 26404function copyArrayContents( src, target ) { 26405 26406 if ( src.constructor !== target.constructor ) { 26407 26408 // if arrays are of a different type (eg due to index size increasing) then data must be per-element copied 26409 const len = Math.min( src.length, target.length ); 26410 for ( let i = 0; i < len; i ++ ) { 26411 26412 target[ i ] = src[ i ]; 26413 26414 } 26415 26416 } else { 26417 26418 // if the arrays use the same data layout we can use a fast block copy 26419 const len = Math.min( src.length, target.length ); 26420 target.set( new src.constructor( src.buffer, 0, len ) ); 26421 26422 } 26423 26424} 26425 26426/** 26427 * A special version of a mesh with multi draw batch rendering support. Use 26428 * this class if you have to render a large number of objects with the same 26429 * material but with different geometries or world transformations. The usage of 26430 * `BatchedMesh` will help you to reduce the number of draw calls and thus improve the overall 26431 * rendering performance in your application. 26432 * 26433 * ```js 26434 * const box = new THREE.BoxGeometry( 1, 1, 1 ); 26435 * const sphere = new THREE.SphereGeometry( 1, 12, 12 ); 26436 * const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); 26437 * 26438 * // initialize and add geometries into the batched mesh 26439 * const batchedMesh = new BatchedMesh( 10, 5000, 10000, material ); 26440 * const boxGeometryId = batchedMesh.addGeometry( box ); 26441 * const sphereGeometryId = batchedMesh.addGeometry( sphere ); 26442 * 26443 * // create instances of those geometries 26444 * const boxInstancedId1 = batchedMesh.addInstance( boxGeometryId ); 26445 * const boxInstancedId2 = batchedMesh.addInstance( boxGeometryId ); 26446 * 26447 * const sphereInstancedId1 = batchedMesh.addInstance( sphereGeometryId ); 26448 * const sphereInstancedId2 = batchedMesh.addInstance( sphereGeometryId ); 26449 * 26450 * // position the geometries 26451 * batchedMesh.setMatrixAt( boxInstancedId1, boxMatrix1 ); 26452 * batchedMesh.setMatrixAt( boxInstancedId2, boxMatrix2 ); 26453 * 26454 * batchedMesh.setMatrixAt( sphereInstancedId1, sphereMatrix1 ); 26455 * batchedMesh.setMatrixAt( sphereInstancedId2, sphereMatrix2 ); 26456 * 26457 * scene.add( batchedMesh ); 26458 * ``` 26459 * 26460 * @augments Mesh 26461 */ 26462class BatchedMesh extends Mesh { 26463 26464 /** 26465 * Constructs a new batched mesh. 26466 * 26467 * @param {number} maxInstanceCount - The maximum number of individual instances planned to be added and rendered. 26468 * @param {number} maxVertexCount - The maximum number of vertices to be used by all unique geometries. 26469 * @param {number} [maxIndexCount=maxVertexCount*2] - The maximum number of indices to be used by all unique geometries 26470 * @param {Material|Array<Material>} [material] - The mesh material. 26471 */ 26472 constructor( maxInstanceCount, maxVertexCount, maxIndexCount = maxVertexCount * 2, material ) { 26473 26474 super( new BufferGeometry(), material ); 26475 26476 /** 26477 * This flag can be used for type testing. 26478 * 26479 * @type {boolean} 26480 * @readonly 26481 * @default true 26482 */ 26483 this.isBatchedMesh = true; 26484 26485 /** 26486 * When set ot `true`, the individual objects of a batch are frustum culled. 26487 * 26488 * @type {boolean} 26489 * @default true 26490 */ 26491 this.perObjectFrustumCulled = true; 26492 26493 /** 26494 * When set to `true`, the individual objects of a batch are sorted to improve overdraw-related artifacts. 26495 * If the material is marked as "transparent" objects are rendered back to front and if not then they are 26496 * rendered front to back. 26497 * 26498 * @type {boolean} 26499 * @default true 26500 */ 26501 this.sortObjects = true; 26502 26503 /** 26504 * The bounding box of the batched mesh. Can be computed via {@link BatchedMesh#computeBoundingBox}. 26505 * 26506 * @type {?Box3} 26507 * @default null 26508 */ 26509 this.boundingBox = null; 26510 26511 /** 26512 * The bounding sphere of the batched mesh. Can be computed via {@link BatchedMesh#computeBoundingSphere}. 26513 * 26514 * @type {?Sphere} 26515 * @default null 26516 */ 26517 this.boundingSphere = null; 26518 26519 /** 26520 * Takes a sort a function that is run before render. The function takes a list of instances to 26521 * sort and a camera. The objects in the list include a "z" field to perform a depth-ordered 26522 * sort with. 26523 * 26524 * @type {?Function} 26525 * @default null 26526 */ 26527 this.customSort = null; 26528 26529 // stores visible, active, and geometry id per instance and reserved buffer ranges for geometries 26530 this._instanceInfo = []; 26531 this._geometryInfo = []; 26532 26533 // instance, geometry ids that have been set as inactive, and are available to be overwritten 26534 this._availableInstanceIds = []; 26535 this._availableGeometryIds = []; 26536 26537 // used to track where the next point is that geometry should be inserted 26538 this._nextIndexStart = 0; 26539 this._nextVertexStart = 0; 26540 this._geometryCount = 0; 26541 26542 // flags 26543 this._visibilityChanged = true; 26544 this._geometryInitialized = false; 26545 26546 // cached user options 26547 this._maxInstanceCount = maxInstanceCount; 26548 this._maxVertexCount = maxVertexCount; 26549 this._maxIndexCount = maxIndexCount; 26550 26551 // buffers for multi draw 26552 this._multiDrawCounts = new Int32Array( maxInstanceCount ); 26553 this._multiDrawStarts = new Int32Array( maxInstanceCount ); 26554 this._multiDrawCount = 0; 26555 this._multiDrawBytesPerElement = 1; 26556 26557 // Local matrix per geometry by using data texture 26558 this._matricesTexture = null; 26559 this._indirectTexture = null; 26560 this._colorsTexture = null; 26561 26562 this._initMatricesTexture(); 26563 this._initIndirectTexture(); 26564 26565 } 26566 26567 /** 26568 * The maximum number of individual instances that can be stored in the batch. 26569 * 26570 * @type {number} 26571 * @readonly 26572 */ 26573 get maxInstanceCount() { 26574 26575 return this._maxInstanceCount; 26576 26577 } 26578 26579 /** 26580 * The instance count. 26581 * 26582 * @type {number} 26583 * @readonly 26584 */ 26585 get instanceCount() { 26586 26587 return this._instanceInfo.length - this._availableInstanceIds.length; 26588 26589 } 26590 26591 /** 26592 * The number of unused vertices. 26593 * 26594 * @type {number} 26595 * @readonly 26596 */ 26597 get unusedVertexCount() { 26598 26599 return this._maxVertexCount - this._nextVertexStart; 26600 26601 } 26602 26603 /** 26604 * The number of unused indices. 26605 * 26606 * @type {number} 26607 * @readonly 26608 */ 26609 get unusedIndexCount() { 26610 26611 return this._maxIndexCount - this._nextIndexStart; 26612 26613 } 26614 26615 _initMatricesTexture() { 26616 26617 // layout (1 matrix = 4 pixels) 26618 // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) 26619 // with 8x8 pixel texture max 16 matrices * 4 pixels = (8 * 8) 26620 // 16x16 pixel texture max 64 matrices * 4 pixels = (16 * 16) 26621 // 32x32 pixel texture max 256 matrices * 4 pixels = (32 * 32) 26622 // 64x64 pixel texture max 1024 matrices * 4 pixels = (64 * 64) 26623 26624 let size = Math.sqrt( this._maxInstanceCount * 4 ); // 4 pixels needed for 1 matrix 26625 size = Math.ceil( size / 4 ) * 4; 26626 size = Math.max( size, 4 ); 26627 26628 const matricesArray = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel 26629 const matricesTexture = new DataTexture( matricesArray, size, size, RGBAFormat, FloatType ); 26630 26631 this._matricesTexture = matricesTexture; 26632 26633 } 26634 26635 _initIndirectTexture() { 26636 26637 let size = Math.sqrt( this._maxInstanceCount ); 26638 size = Math.ceil( size ); 26639 26640 const indirectArray = new Uint32Array( size * size ); 26641 const indirectTexture = new DataTexture( indirectArray, size, size, RedIntegerFormat, UnsignedIntType ); 26642 26643 this._indirectTexture = indirectTexture; 26644 26645 } 26646 26647 _initColorsTexture() { 26648 26649 let size = Math.sqrt( this._maxInstanceCount ); 26650 size = Math.ceil( size ); 26651 26652 // 4 floats per RGBA pixel initialized to white 26653 const colorsArray = new Float32Array( size * size * 4 ).fill( 1 ); 26654 const colorsTexture = new DataTexture( colorsArray, size, size, RGBAFormat, FloatType ); 26655 colorsTexture.colorSpace = ColorManagement.workingColorSpace; 26656 26657 this._colorsTexture = colorsTexture; 26658 26659 } 26660 26661 _initializeGeometry( reference ) { 26662 26663 const geometry = this.geometry; 26664 const maxVertexCount = this._maxVertexCount; 26665 const maxIndexCount = this._maxIndexCount; 26666 if ( this._geometryInitialized === false ) { 26667 26668 for ( const attributeName in reference.attributes ) { 26669 26670 const srcAttribute = reference.getAttribute( attributeName ); 26671 const { array, itemSize, normalized } = srcAttribute; 26672 26673 const dstArray = new array.constructor( maxVertexCount * itemSize ); 26674 const dstAttribute = new BufferAttribute( dstArray, itemSize, normalized ); 26675 26676 geometry.setAttribute( attributeName, dstAttribute ); 26677 26678 } 26679 26680 if ( reference.getIndex() !== null ) { 26681 26682 // Reserve last u16 index for primitive restart. 26683 const indexArray = maxVertexCount > 65535 26684 ? new Uint32Array( maxIndexCount ) 26685 : new Uint16Array( maxIndexCount ); 26686 26687 geometry.setIndex( new BufferAttribute( indexArray, 1 ) ); 26688 26689 } 26690 26691 this._geometryInitialized = true; 26692 26693 } 26694 26695 } 26696 26697 // Make sure the geometry is compatible with the existing combined geometry attributes 26698 _validateGeometry( geometry ) { 26699 26700 // check to ensure the geometries are using consistent attributes and indices 26701 const batchGeometry = this.geometry; 26702 if ( Boolean( geometry.getIndex() ) !== Boolean( batchGeometry.getIndex() ) ) { 26703 26704 throw new Error( 'THREE.BatchedMesh: All geometries must consistently have "index".' ); 26705 26706 } 26707 26708 for ( const attributeName in batchGeometry.attributes ) { 26709 26710 if ( ! geometry.hasAttribute( attributeName ) ) { 26711 26712 throw new Error( `THREE.BatchedMesh: Added geometry missing "${ attributeName }". All geometries must have consistent attributes.` ); 26713 26714 } 26715 26716 const srcAttribute = geometry.getAttribute( attributeName ); 26717 const dstAttribute = batchGeometry.getAttribute( attributeName ); 26718 if ( srcAttribute.itemSize !== dstAttribute.itemSize || srcAttribute.normalized !== dstAttribute.normalized ) { 26719 26720 throw new Error( 'THREE.BatchedMesh: All attributes must have a consistent itemSize and normalized value.' ); 26721 26722 } 26723 26724 } 26725 26726 } 26727 26728 /** 26729 * Validates the instance defined by the given ID. 26730 * 26731 * @param {number} instanceId - The instance to validate. 26732 */ 26733 validateInstanceId( instanceId ) { 26734 26735 const instanceInfo = this._instanceInfo; 26736 if ( instanceId < 0 || instanceId >= instanceInfo.length || instanceInfo[ instanceId ].active === false ) { 26737 26738 throw new Error( `THREE.BatchedMesh: Invalid instanceId ${instanceId}. Instance is either out of range or has been deleted.` ); 26739 26740 } 26741 26742 } 26743 26744 /** 26745 * Validates the geometry defined by the given ID. 26746 * 26747 * @param {number} geometryId - The geometry to validate. 26748 */ 26749 validateGeometryId( geometryId ) { 26750 26751 const geometryInfoList = this._geometryInfo; 26752 if ( geometryId < 0 || geometryId >= geometryInfoList.length || geometryInfoList[ geometryId ].active === false ) { 26753 26754 throw new Error( `THREE.BatchedMesh: Invalid geometryId ${geometryId}. Geometry is either out of range or has been deleted.` ); 26755 26756 } 26757 26758 } 26759 26760 /** 26761 * Takes a sort a function that is run before render. The function takes a list of instances to 26762 * sort and a camera. The objects in the list include a "z" field to perform a depth-ordered sort with. 26763 * 26764 * @param {Function} func - The custom sort function. 26765 * @return {BatchedMesh} A reference to this batched mesh. 26766 */ 26767 setCustomSort( func ) { 26768 26769 this.customSort = func; 26770 return this; 26771 26772 } 26773 26774 /** 26775 * Computes the bounding box, updating {@link BatchedMesh#boundingBox}. 26776 * Bounding boxes aren't computed by default. They need to be explicitly computed, 26777 * otherwise they are `null`. 26778 */ 26779 computeBoundingBox() { 26780 26781 if ( this.boundingBox === null ) { 26782 26783 this.boundingBox = new Box3(); 26784 26785 } 26786 26787 const boundingBox = this.boundingBox; 26788 const instanceInfo = this._instanceInfo; 26789 26790 boundingBox.makeEmpty(); 26791 for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) { 26792 26793 if ( instanceInfo[ i ].active === false ) continue; 26794 26795 const geometryId = instanceInfo[ i ].geometryIndex; 26796 this.getMatrixAt( i, _matrix$1 ); 26797 this.getBoundingBoxAt( geometryId, _box$1 ).applyMatrix4( _matrix$1 ); 26798 boundingBox.union( _box$1 ); 26799 26800 } 26801 26802 } 26803 26804 /** 26805 * Computes the bounding sphere, updating {@link BatchedMesh#boundingSphere}. 26806 * Bounding spheres aren't computed by default. They need to be explicitly computed, 26807 * otherwise they are `null`. 26808 */ 26809 computeBoundingSphere() { 26810 26811 if ( this.boundingSphere === null ) { 26812 26813 this.boundingSphere = new Sphere(); 26814 26815 } 26816 26817 const boundingSphere = this.boundingSphere; 26818 const instanceInfo = this._instanceInfo; 26819 26820 boundingSphere.makeEmpty(); 26821 for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) { 26822 26823 if ( instanceInfo[ i ].active === false ) continue; 26824 26825 const geometryId = instanceInfo[ i ].geometryIndex; 26826 this.getMatrixAt( i, _matrix$1 ); 26827 this.getBoundingSphereAt( geometryId, _sphere$2 ).applyMatrix4( _matrix$1 ); 26828 boundingSphere.union( _sphere$2 ); 26829 26830 } 26831 26832 } 26833 26834 /** 26835 * Adds a new instance to the batch using the geometry of the given ID and returns 26836 * a new id referring to the new instance to be used by other functions. 26837 * 26838 * @param {number} geometryId - The ID of a previously added geometry via {@link BatchedMesh#addGeometry}. 26839 * @return {number} The instance ID. 26840 */ 26841 addInstance( geometryId ) { 26842 26843 const atCapacity = this._instanceInfo.length >= this.maxInstanceCount; 26844 26845 // ensure we're not over geometry 26846 if ( atCapacity && this._availableInstanceIds.length === 0 ) { 26847 26848 throw new Error( 'THREE.BatchedMesh: Maximum item count reached.' ); 26849 26850 } 26851 26852 const instanceInfo = { 26853 visible: true, 26854 active: true, 26855 geometryIndex: geometryId, 26856 }; 26857 26858 let drawId = null; 26859 26860 // Prioritize using previously freed instance ids 26861 if ( this._availableInstanceIds.length > 0 ) { 26862 26863 this._availableInstanceIds.sort( ascIdSort ); 26864 26865 drawId = this._availableInstanceIds.shift(); 26866 this._instanceInfo[ drawId ] = instanceInfo; 26867 26868 } else { 26869 26870 drawId = this._instanceInfo.length; 26871 this._instanceInfo.push( instanceInfo ); 26872 26873 } 26874 26875 const matricesTexture = this._matricesTexture; 26876 _matrix$1.identity().toArray( matricesTexture.image.data, drawId * 16 ); 26877 matricesTexture.needsUpdate = true; 26878 26879 const colorsTexture = this._colorsTexture; 26880 if ( colorsTexture ) { 26881 26882 _whiteColor.toArray( colorsTexture.image.data, drawId * 4 ); 26883 colorsTexture.needsUpdate = true; 26884 26885 } 26886 26887 this._visibilityChanged = true; 26888 return drawId; 26889 26890 } 26891 26892 /** 26893 * Adds the given geometry to the batch and returns the associated 26894 * geometry id referring to it to be used in other functions. 26895 * 26896 * @param {BufferGeometry} geometry - The geometry to add. 26897 * @param {number} [reservedVertexCount=-1] - Optional parameter specifying the amount of 26898 * vertex buffer space to reserve for the added geometry. This is necessary if it is planned 26899 * to set a new geometry at this index at a later time that is larger than the original geometry. 26900 * Defaults to the length of the given geometry vertex buffer. 26901 * @param {number} [reservedIndexCount=-1] - Optional parameter specifying the amount of index 26902 * buffer space to reserve for the added geometry. This is necessary if it is planned to set a 26903 * new geometry at this index at a later time that is larger than the original geometry. Defaults to 26904 * the length of the given geometry index buffer. 26905 * @return {number} The geometry ID. 26906 */ 26907 addGeometry( geometry, reservedVertexCount = -1, reservedIndexCount = -1 ) { 26908 26909 this._initializeGeometry( geometry ); 26910 26911 this._validateGeometry( geometry ); 26912 26913 const geometryInfo = { 26914 // geometry information 26915 vertexStart: -1, 26916 vertexCount: -1, 26917 reservedVertexCount: -1, 26918 26919 indexStart: -1, 26920 indexCount: -1, 26921 reservedIndexCount: -1, 26922 26923 // draw range information 26924 start: -1, 26925 count: -1, 26926 26927 // state 26928 boundingBox: null, 26929 boundingSphere: null, 26930 active: true, 26931 }; 26932 26933 const geometryInfoList = this._geometryInfo; 26934 geometryInfo.vertexStart = this._nextVertexStart; 26935 geometryInfo.reservedVertexCount = reservedVertexCount === -1 ? geometry.getAttribute( 'position' ).count : reservedVertexCount; 26936 26937 const index = geometry.getIndex(); 26938 const hasIndex = index !== null; 26939 if ( hasIndex ) { 26940 26941 geometryInfo.indexStart = this._nextIndexStart; 26942 geometryInfo.reservedIndexCount = reservedIndexCount === -1 ? index.count : reservedIndexCount; 26943 26944 } 26945 26946 if ( 26947 geometryInfo.indexStart !== -1 && 26948 geometryInfo.indexStart + geometryInfo.reservedIndexCount > this._maxIndexCount || 26949 geometryInfo.vertexStart + geometryInfo.reservedVertexCount > this._maxVertexCount 26950 ) { 26951 26952 throw new Error( 'THREE.BatchedMesh: Reserved space request exceeds the maximum buffer size.' ); 26953 26954 } 26955 26956 // update id 26957 let geometryId; 26958 if ( this._availableGeometryIds.length > 0 ) { 26959 26960 this._availableGeometryIds.sort( ascIdSort ); 26961 26962 geometryId = this._availableGeometryIds.shift(); 26963 geometryInfoList[ geometryId ] = geometryInfo; 26964 26965 26966 } else { 26967 26968 geometryId = this._geometryCount; 26969 this._geometryCount ++; 26970 geometryInfoList.push( geometryInfo ); 26971 26972 } 26973 26974 // update the geometry 26975 this.setGeometryAt( geometryId, geometry ); 26976 26977 // increment the next geometry position 26978 this._nextIndexStart = geometryInfo.indexStart + geometryInfo.reservedIndexCount; 26979 this._nextVertexStart = geometryInfo.vertexStart + geometryInfo.reservedVertexCount; 26980 26981 return geometryId; 26982 26983 } 26984 26985 /** 26986 * Replaces the geometry at the given ID with the provided geometry. Throws an error if there 26987 * is not enough space reserved for geometry. Calling this will change all instances that are 26988 * rendering that geometry. 26989 * 26990 * @param {number} geometryId - The ID of the geometry that should be replaced with the given geometry. 26991 * @param {BufferGeometry} geometry - The new geometry. 26992 * @return {number} The geometry ID. 26993 */ 26994 setGeometryAt( geometryId, geometry ) { 26995 26996 if ( geometryId >= this._geometryCount ) { 26997 26998 throw new Error( 'THREE.BatchedMesh: Maximum geometry count reached.' ); 26999 27000 } 27001 27002 this._validateGeometry( geometry ); 27003 27004 const batchGeometry = this.geometry; 27005 const hasIndex = batchGeometry.getIndex() !== null; 27006 const dstIndex = batchGeometry.getIndex(); 27007 const srcIndex = geometry.getIndex(); 27008 const geometryInfo = this._geometryInfo[ geometryId ]; 27009 if ( 27010 hasIndex && 27011 srcIndex.count > geometryInfo.reservedIndexCount || 27012 geometry.attributes.position.count > geometryInfo.reservedVertexCount 27013 ) { 27014 27015 throw new Error( 'THREE.BatchedMesh: Reserved space not large enough for provided geometry.' ); 27016 27017 } 27018 27019 // copy geometry buffer data over 27020 const vertexStart = geometryInfo.vertexStart; 27021 const reservedVertexCount = geometryInfo.reservedVertexCount; 27022 geometryInfo.vertexCount = geometry.getAttribute( 'position' ).count; 27023 27024 for ( const attributeName in batchGeometry.attributes ) { 27025 27026 // copy attribute data 27027 const srcAttribute = geometry.getAttribute( attributeName ); 27028 const dstAttribute = batchGeometry.getAttribute( attributeName ); 27029 copyAttributeData( srcAttribute, dstAttribute, vertexStart ); 27030 27031 // fill the rest in with zeroes 27032 const itemSize = srcAttribute.itemSize; 27033 for ( let i = srcAttribute.count, l = reservedVertexCount; i < l; i ++ ) { 27034 27035 const index = vertexStart + i; 27036 for ( let c = 0; c < itemSize; c ++ ) { 27037 27038 dstAttribute.setComponent( index, c, 0 ); 27039 27040 } 27041 27042 } 27043 27044 dstAttribute.needsUpdate = true; 27045 dstAttribute.addUpdateRange( vertexStart * itemSize, reservedVertexCount * itemSize ); 27046 27047 } 27048 27049 // copy index 27050 if ( hasIndex ) { 27051 27052 const indexStart = geometryInfo.indexStart; 27053 const reservedIndexCount = geometryInfo.reservedIndexCount; 27054 geometryInfo.indexCount = geometry.getIndex().count; 27055 27056 // copy index data over 27057 for ( let i = 0; i < srcIndex.count; i ++ ) { 27058 27059 dstIndex.setX( indexStart + i, vertexStart + srcIndex.getX( i ) ); 27060 27061 } 27062 27063 // fill the rest in with zeroes 27064 for ( let i = srcIndex.count, l = reservedIndexCount; i < l; i ++ ) { 27065 27066 dstIndex.setX( indexStart + i, vertexStart ); 27067 27068 } 27069 27070 dstIndex.needsUpdate = true; 27071 dstIndex.addUpdateRange( indexStart, geometryInfo.reservedIndexCount ); 27072 27073 } 27074 27075 // update the draw range 27076 geometryInfo.start = hasIndex ? geometryInfo.indexStart : geometryInfo.vertexStart; 27077 geometryInfo.count = hasIndex ? geometryInfo.indexCount : geometryInfo.vertexCount; 27078 27079 // store the bounding boxes 27080 geometryInfo.boundingBox = null; 27081 if ( geometry.boundingBox !== null ) { 27082 27083 geometryInfo.boundingBox = geometry.boundingBox.clone(); 27084 27085 } 27086 27087 geometryInfo.boundingSphere = null; 27088 if ( geometry.boundingSphere !== null ) { 27089 27090 geometryInfo.boundingSphere = geometry.boundingSphere.clone(); 27091 27092 } 27093 27094 this._visibilityChanged = true; 27095 return geometryId; 27096 27097 } 27098 27099 /** 27100 * Deletes the geometry defined by the given ID from this batch. Any instances referencing 27101 * this geometry will also be removed as a side effect. 27102 * 27103 * @param {number} geometryId - The ID of the geometry to remove from the batch. 27104 * @return {BatchedMesh} A reference to this batched mesh. 27105 */ 27106 deleteGeometry( geometryId ) { 27107 27108 const geometryInfoList = this._geometryInfo; 27109 if ( geometryId >= geometryInfoList.length || geometryInfoList[ geometryId ].active === false ) { 27110 27111 return this; 27112 27113 } 27114 27115 // delete any instances associated with this geometry 27116 const instanceInfo = this._instanceInfo; 27117 for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) { 27118 27119 if ( instanceInfo[ i ].active && instanceInfo[ i ].geometryIndex === geometryId ) { 27120 27121 this.deleteInstance( i ); 27122 27123 } 27124 27125 } 27126 27127 geometryInfoList[ geometryId ].active = false; 27128 this._availableGeometryIds.push( geometryId ); 27129 this._visibilityChanged = true; 27130 27131 return this; 27132 27133 } 27134 27135 /** 27136 * Deletes an existing instance from the batch using the given ID. 27137 * 27138 * @param {number} instanceId - The ID of the instance to remove from the batch. 27139 * @return {BatchedMesh} A reference to this batched mesh. 27140 */ 27141 deleteInstance( instanceId ) { 27142 27143 this.validateInstanceId( instanceId ); 27144 27145 this._instanceInfo[ instanceId ].active = false; 27146 this._availableInstanceIds.push( instanceId ); 27147 this._visibilityChanged = true; 27148 27149 return this; 27150 27151 } 27152 27153 /** 27154 * Repacks the sub geometries in BatchedMesh to remove any unused space remaining from 27155 * previously deleted geometry, freeing up space to add new geometry. 27156 * 27157 * @return {BatchedMesh} A reference to this batched mesh. 27158 */ 27159 optimize() { 27160 27161 // track the next indices to copy data to 27162 let nextVertexStart = 0; 27163 let nextIndexStart = 0; 27164 27165 // Iterate over all geometry ranges in order sorted from earliest in the geometry buffer to latest 27166 // in the geometry buffer. Because draw range objects can be reused there is no guarantee of their order. 27167 const geometryInfoList = this._geometryInfo; 27168 const indices = geometryInfoList 27169 .map( ( e, i ) => i ) 27170 .sort( ( a, b ) => { 27171 27172 return geometryInfoList[ a ].vertexStart - geometryInfoList[ b ].vertexStart; 27173 27174 } ); 27175 27176 const geometry = this.geometry; 27177 for ( let i = 0, l = geometryInfoList.length; i < l; i ++ ) { 27178 27179 // if a geometry range is inactive then don't copy anything 27180 const index = indices[ i ]; 27181 const geometryInfo = geometryInfoList[ index ]; 27182 if ( geometryInfo.active === false ) { 27183 27184 continue; 27185 27186 } 27187 27188 // if a geometry contains an index buffer then shift it, as well 27189 if ( geometry.index !== null ) { 27190 27191 if ( geometryInfo.indexStart !== nextIndexStart ) { 27192 27193 const { indexStart, vertexStart, reservedIndexCount } = geometryInfo; 27194 const index = geometry.index; 27195 const array = index.array; 27196 27197 // shift the index pointers based on how the vertex data will shift 27198 // adjusting the index must happen first so the original vertex start value is available 27199 const elementDelta = nextVertexStart - vertexStart; 27200 for ( let j = indexStart; j < indexStart + reservedIndexCount; j ++ ) { 27201 27202 array[ j ] = array[ j ] + elementDelta; 27203 27204 } 27205 27206 index.array.copyWithin( nextIndexStart, indexStart, indexStart + reservedIndexCount ); 27207 index.addUpdateRange( nextIndexStart, reservedIndexCount ); 27208 index.needsUpdate = true; 27209 27210 geometryInfo.indexStart = nextIndexStart; 27211 27212 } 27213 27214 nextIndexStart += geometryInfo.reservedIndexCount; 27215 27216 } 27217 27218 // if a geometry needs to be moved then copy attribute data to overwrite unused space 27219 if ( geometryInfo.vertexStart !== nextVertexStart ) { 27220 27221 const { vertexStart, reservedVertexCount } = geometryInfo; 27222 const attributes = geometry.attributes; 27223 for ( const key in attributes ) { 27224 27225 const attribute = attributes[ key ]; 27226 const { array, itemSize } = attribute; 27227 array.copyWithin( nextVertexStart * itemSize, vertexStart * itemSize, ( vertexStart + reservedVertexCount ) * itemSize ); 27228 attribute.addUpdateRange( nextVertexStart * itemSize, reservedVertexCount * itemSize ); 27229 attribute.needsUpdate = true; 27230 27231 } 27232 27233 geometryInfo.vertexStart = nextVertexStart; 27234 27235 } 27236 27237 nextVertexStart += geometryInfo.reservedVertexCount; 27238 geometryInfo.start = geometry.index ? geometryInfo.indexStart : geometryInfo.vertexStart; 27239 27240 } 27241 27242 this._nextIndexStart = nextIndexStart; 27243 this._nextVertexStart = nextVertexStart; 27244 this._visibilityChanged = true; 27245 27246 return this; 27247 27248 } 27249 27250 /** 27251 * Returns the bounding box for the given geometry. 27252 * 27253 * @param {number} geometryId - The ID of the geometry to return the bounding box for. 27254 * @param {Box3} target - The target object that is used to store the method's result. 27255 * @return {?Box3} The geometry's bounding box. Returns `null` if no geometry has been found for the given ID. 27256 */ 27257 getBoundingBoxAt( geometryId, target ) { 27258 27259 if ( geometryId >= this._geometryCount ) { 27260 27261 return null; 27262 27263 } 27264 27265 // compute bounding box 27266 const geometry = this.geometry; 27267 const geometryInfo = this._geometryInfo[ geometryId ]; 27268 if ( geometryInfo.boundingBox === null ) { 27269 27270 const box = new Box3(); 27271 const index = geometry.index; 27272 const position = geometry.attributes.position; 27273 for ( let i = geometryInfo.start, l = geometryInfo.start + geometryInfo.count; i < l; i ++ ) { 27274 27275 let iv = i; 27276 if ( index ) { 27277 27278 iv = index.getX( iv ); 27279 27280 } 27281 27282 box.expandByPoint( _vector$5.fromBufferAttribute( position, iv ) ); 27283 27284 } 27285 27286 geometryInfo.boundingBox = box; 27287 27288 } 27289 27290 target.copy( geometryInfo.boundingBox ); 27291 return target; 27292 27293 } 27294 27295 /** 27296 * Returns the bounding sphere for the given geometry. 27297 * 27298 * @param {number} geometryId - The ID of the geometry to return the bounding sphere for. 27299 * @param {Sphere} target - The target object that is used to store the method's result. 27300 * @return {?Sphere} The geometry's bounding sphere. Returns `null` if no geometry has been found for the given ID. 27301 */ 27302 getBoundingSphereAt( geometryId, target ) { 27303 27304 if ( geometryId >= this._geometryCount ) { 27305 27306 return null; 27307 27308 } 27309 27310 // compute bounding sphere 27311 const geometry = this.geometry; 27312 const geometryInfo = this._geometryInfo[ geometryId ]; 27313 if ( geometryInfo.boundingSphere === null ) { 27314 27315 const sphere = new Sphere(); 27316 this.getBoundingBoxAt( geometryId, _box$1 ); 27317 _box$1.getCenter( sphere.center ); 27318 27319 const index = geometry.index; 27320 const position = geometry.attributes.position; 27321 27322 let maxRadiusSq = 0; 27323 for ( let i = geometryInfo.start, l = geometryInfo.start + geometryInfo.count; i < l; i ++ ) { 27324 27325 let iv = i; 27326 if ( index ) { 27327 27328 iv = index.getX( iv ); 27329 27330 } 27331 27332 _vector$5.fromBufferAttribute( position, iv ); 27333 maxRadiusSq = Math.max( maxRadiusSq, sphere.center.distanceToSquared( _vector$5 ) ); 27334 27335 } 27336 27337 sphere.radius = Math.sqrt( maxRadiusSq ); 27338 geometryInfo.boundingSphere = sphere; 27339 27340 } 27341 27342 target.copy( geometryInfo.boundingSphere ); 27343 return target; 27344 27345 } 27346 27347 /** 27348 * Sets the given local transformation matrix to the defined instance. 27349 * Negatively scaled matrices are not supported. 27350 * 27351 * @param {number} instanceId - The ID of an instance to set the matrix of. 27352 * @param {Matrix4} matrix - A 4x4 matrix representing the local transformation of a single instance. 27353 * @return {BatchedMesh} A reference to this batched mesh. 27354 */ 27355 setMatrixAt( instanceId, matrix ) { 27356 27357 this.validateInstanceId( instanceId ); 27358 27359 const matricesTexture = this._matricesTexture; 27360 const matricesArray = this._matricesTexture.image.data; 27361 matrix.toArray( matricesArray, instanceId * 16 ); 27362 matricesTexture.needsUpdate = true; 27363 27364 return this; 27365 27366 } 27367 27368 /** 27369 * Returns the local transformation matrix of the defined instance. 27370 * 27371 * @param {number} instanceId - The ID of an instance to get the matrix of. 27372 * @param {Matrix4} matrix - The target object that is used to store the method's result. 27373 * @return {Matrix4} The instance's local transformation matrix. 27374 */ 27375 getMatrixAt( instanceId, matrix ) { 27376 27377 this.validateInstanceId( instanceId ); 27378 return matrix.fromArray( this._matricesTexture.image.data, instanceId * 16 ); 27379 27380 } 27381 27382 /** 27383 * Sets the given color to the defined instance. 27384 * 27385 * @param {number} instanceId - The ID of an instance to set the color of. 27386 * @param {Color|Vector4} color - The color to set the instance to. Use a `Vector4` to also define alpha. 27387 * @return {BatchedMesh} A reference to this batched mesh. 27388 */ 27389 setColorAt( instanceId, color ) { 27390 27391 this.validateInstanceId( instanceId ); 27392 27393 if ( this._colorsTexture === null ) { 27394 27395 this._initColorsTexture(); 27396 27397 } 27398 27399 color.toArray( this._colorsTexture.image.data, instanceId * 4 ); 27400 this._colorsTexture.needsUpdate = true; 27401 27402 return this; 27403 27404 } 27405 27406 /** 27407 * Returns the color of the defined instance. 27408 * 27409 * @param {number} instanceId - The ID of an instance to get the color of. 27410 * @param {Color|Vector4} color - The target object that is used to store the method's result. 27411 * @return {Color|Vector4} The instance's color. Use a `Vector4` to also retrieve alpha. 27412 */ 27413 getColorAt( instanceId, color ) { 27414 27415 this.validateInstanceId( instanceId ); 27416 if ( this._colorsTexture === null ) { 27417 27418 if ( color.isVector4 ) { 27419 27420 return color.set( 1, 1, 1, 1 ); 27421 27422 } else { 27423 27424 return color.setRGB( 1, 1, 1 ); 27425 27426 } 27427 27428 } else { 27429 27430 return color.fromArray( this._colorsTexture.image.data, instanceId * 4 ); 27431 27432 } 27433 27434 } 27435 27436 /** 27437 * Sets the visibility of the instance. 27438 * 27439 * @param {number} instanceId - The id of the instance to set the visibility of. 27440 * @param {boolean} visible - Whether the instance is visible or not. 27441 * @return {BatchedMesh} A reference to this batched mesh. 27442 */ 27443 setVisibleAt( instanceId, visible ) { 27444 27445 this.validateInstanceId( instanceId ); 27446 27447 if ( this._instanceInfo[ instanceId ].visible === visible ) { 27448 27449 return this; 27450 27451 } 27452 27453 this._instanceInfo[ instanceId ].visible = visible; 27454 this._visibilityChanged = true; 27455 27456 return this; 27457 27458 } 27459 27460 /** 27461 * Returns the visibility state of the defined instance. 27462 * 27463 * @param {number} instanceId - The ID of an instance to get the visibility state of. 27464 * @return {boolean} Whether the instance is visible or not. 27465 */ 27466 getVisibleAt( instanceId ) { 27467 27468 this.validateInstanceId( instanceId ); 27469 27470 return this._instanceInfo[ instanceId ].visible; 27471 27472 } 27473 27474 /** 27475 * Sets the geometry ID of the instance at the given index. 27476 * 27477 * @param {number} instanceId - The ID of the instance to set the geometry ID of. 27478 * @param {number} geometryId - The geometry ID to be use by the instance. 27479 * @return {BatchedMesh} A reference to this batched mesh. 27480 */ 27481 setGeometryIdAt( instanceId, geometryId ) { 27482 27483 this.validateInstanceId( instanceId ); 27484 this.validateGeometryId( geometryId ); 27485 27486 this._instanceInfo[ instanceId ].geometryIndex = geometryId; 27487 this._visibilityChanged = true; 27488 27489 return this; 27490 27491 } 27492 27493 /** 27494 * Returns the geometry ID of the defined instance. 27495 * 27496 * @param {number} instanceId - The ID of an instance to get the geometry ID of. 27497 * @return {number} The instance's geometry ID. 27498 */ 27499 getGeometryIdAt( instanceId ) { 27500 27501 this.validateInstanceId( instanceId ); 27502 27503 return this._instanceInfo[ instanceId ].geometryIndex; 27504 27505 } 27506 27507 /** 27508 * Get the range representing the subset of triangles related to the attached geometry, 27509 * indicating the starting offset and count, or `null` if invalid. 27510 * 27511 * @param {number} geometryId - The id of the geometry to get the range of. 27512 * @param {Object} [target] - The target object that is used to store the method's result. 27513 * @return {{ 27514 * vertexStart:number,vertexCount:number,reservedVertexCount:number, 27515 * indexStart:number,indexCount:number,reservedIndexCount:number, 27516 * start:number,count:number 27517 * }} The result object with range data. 27518 */ 27519 getGeometryRangeAt( geometryId, target = {} ) { 27520 27521 this.validateGeometryId( geometryId ); 27522 27523 const geometryInfo = this._geometryInfo[ geometryId ]; 27524 target.vertexStart = geometryInfo.vertexStart; 27525 target.vertexCount = geometryInfo.vertexCount; 27526 target.reservedVertexCount = geometryInfo.reservedVertexCount; 27527 27528 target.indexStart = geometryInfo.indexStart; 27529 target.indexCount = geometryInfo.indexCount; 27530 target.reservedIndexCount = geometryInfo.reservedIndexCount; 27531 27532 target.start = geometryInfo.start; 27533 target.count = geometryInfo.count; 27534 27535 return target; 27536 27537 } 27538 27539 /** 27540 * Resizes the necessary buffers to support the provided number of instances. 27541 * If the provided arguments shrink the number of instances but there are not enough 27542 * unused Ids at the end of the list then an error is thrown. 27543 * 27544 * @param {number} maxInstanceCount - The max number of individual instances that can be added and rendered by the batch. 27545 */ 27546 setInstanceCount( maxInstanceCount ) { 27547 27548 // shrink the available instances as much as possible 27549 const availableInstanceIds = this._availableInstanceIds; 27550 const instanceInfo = this._instanceInfo; 27551 availableInstanceIds.sort( ascIdSort ); 27552 while ( availableInstanceIds[ availableInstanceIds.length - 1 ] === instanceInfo.length - 1 ) { 27553 27554 instanceInfo.pop(); 27555 availableInstanceIds.pop(); 27556 27557 } 27558 27559 // throw an error if it can't be shrunk to the desired size 27560 if ( maxInstanceCount < instanceInfo.length ) { 27561 27562 throw new Error( `THREE.BatchedMesh: Instance ids outside the range ${ maxInstanceCount } are being used. Cannot shrink instance count.` ); 27563 27564 } 27565 27566 // copy the multi draw counts 27567 const multiDrawCounts = new Int32Array( maxInstanceCount ); 27568 const multiDrawStarts = new Int32Array( maxInstanceCount ); 27569 copyArrayContents( this._multiDrawCounts, multiDrawCounts ); 27570 copyArrayContents( this._multiDrawStarts, multiDrawStarts ); 27571 27572 this._multiDrawCounts = multiDrawCounts; 27573 this._multiDrawStarts = multiDrawStarts; 27574 this._maxInstanceCount = maxInstanceCount; 27575 27576 // update texture data for instance sampling 27577 const indirectTexture = this._indirectTexture; 27578 const matricesTexture = this._matricesTexture; 27579 const colorsTexture = this._colorsTexture; 27580 27581 indirectTexture.dispose(); 27582 this._initIndirectTexture(); 27583 copyArrayContents( indirectTexture.image.data, this._indirectTexture.image.data ); 27584 27585 matricesTexture.dispose(); 27586 this._initMatricesTexture(); 27587 copyArrayContents( matricesTexture.image.data, this._matricesTexture.image.data ); 27588 27589 if ( colorsTexture ) { 27590 27591 colorsTexture.dispose(); 27592 this._initColorsTexture(); 27593 copyArrayContents( colorsTexture.image.data, this._colorsTexture.image.data ); 27594 27595 } 27596 27597 } 27598 27599 /** 27600 * Resizes the available space in the batch's vertex and index buffer attributes to the provided sizes. 27601 * If the provided arguments shrink the geometry buffers but there is not enough unused space at the 27602 * end of the geometry attributes then an error is thrown. 27603 * 27604 * @param {number} maxVertexCount - The maximum number of vertices to be used by all unique geometries to resize to. 27605 * @param {number} maxIndexCount - The maximum number of indices to be used by all unique geometries to resize to. 27606 */ 27607 setGeometrySize( maxVertexCount, maxIndexCount ) { 27608 27609 // Check if we can shrink to the requested vertex attribute size 27610 const validRanges = [ ...this._geometryInfo ].filter( info => info.active ); 27611 const requiredVertexLength = Math.max( ...validRanges.map( range => range.vertexStart + range.reservedVertexCount ) ); 27612 if ( requiredVertexLength > maxVertexCount ) { 27613 27614 throw new Error( `THREE.BatchedMesh: Geometry vertex values are being used outside the range ${ maxIndexCount }. Cannot shrink further.` ); 27615 27616 } 27617 27618 // Check if we can shrink to the requested index attribute size 27619 if ( this.geometry.index ) { 27620 27621 const requiredIndexLength = Math.max( ...validRanges.map( range => range.indexStart + range.reservedIndexCount ) ); 27622 if ( requiredIndexLength > maxIndexCount ) { 27623 27624 throw new Error( `THREE.BatchedMesh: Geometry index values are being used outside the range ${ maxIndexCount }. Cannot shrink further.` ); 27625 27626 } 27627 27628 } 27629 27630 // 27631 27632 // dispose of the previous geometry 27633 const oldGeometry = this.geometry; 27634 oldGeometry.dispose(); 27635 27636 // recreate the geometry needed based on the previous variant 27637 this._maxVertexCount = maxVertexCount; 27638 this._maxIndexCount = maxIndexCount; 27639 27640 if ( this._geometryInitialized ) { 27641 27642 this._geometryInitialized = false; 27643 this.geometry = new BufferGeometry(); 27644 this._initializeGeometry( oldGeometry ); 27645 27646 } 27647 27648 // copy data from the previous geometry 27649 const geometry = this.geometry; 27650 if ( oldGeometry.index ) { 27651 27652 copyArrayContents( oldGeometry.index.array, geometry.index.array ); 27653 27654 } 27655 27656 for ( const key in oldGeometry.attributes ) { 27657 27658 copyArrayContents( oldGeometry.attributes[ key ].array, geometry.attributes[ key ].array ); 27659 27660 } 27661 27662 } 27663 27664 raycast( raycaster, intersects ) { 27665 27666 const instanceInfo = this._instanceInfo; 27667 const geometryInfoList = this._geometryInfo; 27668 const matrixWorld = this.matrixWorld; 27669 const batchGeometry = this.geometry; 27670 27671 // iterate over each geometry 27672 _mesh.material = this.material; 27673 _mesh.geometry.index = batchGeometry.index; 27674 _mesh.geometry.attributes = batchGeometry.attributes; 27675 if ( _mesh.geometry.boundingBox === null ) { 27676 27677 _mesh.geometry.boundingBox = new Box3(); 27678 27679 } 27680 27681 if ( _mesh.geometry.boundingSphere === null ) { 27682 27683 _mesh.geometry.boundingSphere = new Sphere(); 27684 27685 } 27686 27687 for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) { 27688 27689 if ( ! instanceInfo[ i ].visible || ! instanceInfo[ i ].active ) { 27690 27691 continue; 27692 27693 } 27694 27695 const geometryId = instanceInfo[ i ].geometryIndex; 27696 const geometryInfo = geometryInfoList[ geometryId ]; 27697 _mesh.geometry.setDrawRange( geometryInfo.start, geometryInfo.count ); 27698 27699 // get the intersects 27700 this.getMatrixAt( i, _mesh.matrixWorld ).premultiply( matrixWorld ); 27701 this.getBoundingBoxAt( geometryId, _mesh.geometry.boundingBox ); 27702 this.getBoundingSphereAt( geometryId, _mesh.geometry.boundingSphere ); 27703 _mesh.raycast( raycaster, _batchIntersects ); 27704 27705 // add batch id to the intersects 27706 for ( let j = 0, l = _batchIntersects.length; j < l; j ++ ) { 27707 27708 const intersect = _batchIntersects[ j ]; 27709 intersect.object = this; 27710 intersect.batchId = i; 27711 intersects.push( intersect ); 27712 27713 } 27714 27715 _batchIntersects.length = 0; 27716 27717 } 27718 27719 _mesh.material = null; 27720 _mesh.geometry.index = null; 27721 _mesh.geometry.attributes = {}; 27722 _mesh.geometry.setDrawRange( 0, Infinity ); 27723 27724 } 27725 27726 copy( source ) { 27727 27728 super.copy( source ); 27729 27730 this.geometry = source.geometry.clone(); 27731 this.perObjectFrustumCulled = source.perObjectFrustumCulled; 27732 this.sortObjects = source.sortObjects; 27733 this.boundingBox = source.boundingBox !== null ? source.boundingBox.clone() : null; 27734 this.boundingSphere = source.boundingSphere !== null ? source.boundingSphere.clone() : null; 27735 27736 this._geometryInfo = source._geometryInfo.map( info => ( { 27737 ...info, 27738 27739 boundingBox: info.boundingBox !== null ? info.boundingBox.clone() : null, 27740 boundingSphere: info.boundingSphere !== null ? info.boundingSphere.clone() : null, 27741 } ) ); 27742 this._instanceInfo = source._instanceInfo.map( info => ( { ...info } ) ); 27743 27744 this._availableInstanceIds = source._availableInstanceIds.slice(); 27745 this._availableGeometryIds = source._availableGeometryIds.slice(); 27746 27747 this._nextIndexStart = source._nextIndexStart; 27748 this._nextVertexStart = source._nextVertexStart; 27749 this._geometryCount = source._geometryCount; 27750 27751 this._maxInstanceCount = source._maxInstanceCount; 27752 this._maxVertexCount = source._maxVertexCount; 27753 this._maxIndexCount = source._maxIndexCount; 27754 27755 this._geometryInitialized = source._geometryInitialized; 27756 this._multiDrawCounts = source._multiDrawCounts.slice(); 27757 this._multiDrawStarts = source._multiDrawStarts.slice(); 27758 this._multiDrawBytesPerElement = source._multiDrawBytesPerElement; 27759 27760 this._indirectTexture = source._indirectTexture.clone(); 27761 this._indirectTexture.image.data = this._indirectTexture.image.data.slice(); 27762 27763 this._matricesTexture = source._matricesTexture.clone(); 27764 this._matricesTexture.image.data = this._matricesTexture.image.data.slice(); 27765 27766 if ( this._colorsTexture !== null ) { 27767 27768 this._colorsTexture = source._colorsTexture.clone(); 27769 this._colorsTexture.image.data = this._colorsTexture.image.data.slice(); 27770 27771 } 27772 27773 return this; 27774 27775 } 27776 27777 /** 27778 * Frees the GPU-related resources allocated by this instance. Call this 27779 * method whenever this instance is no longer used in your app. 27780 */ 27781 dispose() { 27782 27783 super.dispose(); 27784 27785 // Assuming the geometry is not shared with other meshes 27786 this.geometry.dispose(); 27787 27788 this._matricesTexture.dispose(); 27789 this._matricesTexture = null; 27790 27791 this._indirectTexture.dispose(); 27792 this._indirectTexture = null; 27793 27794 if ( this._colorsTexture !== null ) { 27795 27796 this._colorsTexture.dispose(); 27797 this._colorsTexture = null; 27798 27799 } 27800 27801 } 27802 27803 onBeforeRender( renderer, scene, camera, geometry, material/*, _group*/ ) { 27804 27805 // if visibility has not changed and frustum culling and object sorting is not required 27806 // then skip iterating over all items 27807 if ( ! this._visibilityChanged && ! this.perObjectFrustumCulled && ! this.sortObjects ) { 27808 27809 return; 27810 27811 } 27812 27813 // the indexed version of the multi draw function requires specifying the start 27814 // offset in bytes. 27815 const index = geometry.getIndex(); 27816 let bytesPerElement = index === null ? 1 : index.array.BYTES_PER_ELEMENT; 27817 27818 27819 // the "wireframe" attribute implicitly creates a line attribute in the renderer, which is double 27820 // the vertices to draw (3 lines per triangle) so we multiply the draw counts / starts and make 27821 // assumptions about the index buffer byte size. 27822 let multiDrawMultiplier = 1; 27823 if ( material.wireframe ) { 27824 27825 multiDrawMultiplier = 2; 27826 bytesPerElement = geometry.attributes.position.count > 65535 ? 4 : 2; 27827 27828 } 27829 27830 const instanceInfo = this._instanceInfo; 27831 const multiDrawStarts = this._multiDrawStarts; 27832 const multiDrawCounts = this._multiDrawCounts; 27833 const geometryInfoList = this._geometryInfo; 27834 const perObjectFrustumCulled = this.perObjectFrustumCulled; 27835 const indirectTexture = this._indirectTexture; 27836 const indirectArray = indirectTexture.image.data; 27837 27838 const frustum = camera.isArrayCamera ? _frustumArray : _frustum; 27839 // prepare the frustum in the local frame 27840 if ( perObjectFrustumCulled ) { 27841 27842 if ( camera.isArrayCamera ) { 27843 27844 frustum.setFromArrayCamera( camera ); 27845 27846 } else { 27847 27848 _matrix$1 27849 .multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ) 27850 .multiply( this.matrixWorld ); 27851 27852 frustum.setFromProjectionMatrix( 27853 _matrix$1, 27854 camera.coordinateSystem, 27855 camera.reversedDepth 27856 ); 27857 27858 } 27859 27860 } 27861 27862 let multiDrawCount = 0; 27863 if ( this.sortObjects ) { 27864 27865 // get the camera position in the local frame 27866 _matrix$1.copy( this.matrixWorld ).invert(); 27867 _vector$5.setFromMatrixPosition( camera.matrixWorld ).applyMatrix4( _matrix$1 ); 27868 _forward$1.set( 0, 0, -1 ).transformDirection( camera.matrixWorld ).transformDirection( _matrix$1 ); 27869 27870 for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) { 27871 27872 if ( instanceInfo[ i ].visible && instanceInfo[ i ].active ) { 27873 27874 const geometryId = instanceInfo[ i ].geometryIndex; 27875 27876 // get the bounds in world space 27877 this.getMatrixAt( i, _matrix$1 ); 27878 this.getBoundingSphereAt( geometryId, _sphere$2 ).applyMatrix4( _matrix$1 ); 27879 27880 // determine whether the batched geometry is within the frustum 27881 let culled = false; 27882 if ( perObjectFrustumCulled ) { 27883 27884 culled = ! frustum.intersectsSphere( _sphere$2 ); 27885 27886 } 27887 27888 if ( ! culled ) { 27889 27890 // get the distance from camera used for sorting 27891 const geometryInfo = geometryInfoList[ geometryId ]; 27892 const z = _temp.subVectors( _sphere$2.center, _vector$5 ).dot( _forward$1 ); 27893 _renderList.push( geometryInfo.start, geometryInfo.count, z, i ); 27894 27895 } 27896 27897 } 27898 27899 } 27900 27901 // Sort the draw ranges and prep for rendering 27902 const list = _renderList.list; 27903 const customSort = this.customSort; 27904 if ( customSort === null ) { 27905 27906 list.sort( material.transparent ? sortTransparent : sortOpaque ); 27907 27908 } else { 27909 27910 customSort.call( this, list, camera ); 27911 27912 } 27913 27914 for ( let i = 0, l = list.length; i < l; i ++ ) { 27915 27916 const item = list[ i ]; 27917 multiDrawStarts[ multiDrawCount ] = item.start * bytesPerElement * multiDrawMultiplier; 27918 multiDrawCounts[ multiDrawCount ] = item.count * multiDrawMultiplier; 27919 indirectArray[ multiDrawCount ] = item.index; 27920 multiDrawCount ++; 27921 27922 } 27923 27924 _renderList.reset(); 27925 27926 } else { 27927 27928 for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) { 27929 27930 if ( instanceInfo[ i ].visible && instanceInfo[ i ].active ) { 27931 27932 const geometryId = instanceInfo[ i ].geometryIndex; 27933 27934 // determine whether the batched geometry is within the frustum 27935 let culled = false; 27936 if ( perObjectFrustumCulled ) { 27937 27938 // get the bounds in world space 27939 this.getMatrixAt( i, _matrix$1 ); 27940 this.getBoundingSphereAt( geometryId, _sphere$2 ).applyMatrix4( _matrix$1 ); 27941 culled = ! frustum.intersectsSphere( _sphere$2 ); 27942 27943 } 27944 27945 if ( ! culled ) { 27946 27947 const geometryInfo = geometryInfoList[ geometryId ]; 27948 multiDrawStarts[ multiDrawCount ] = geometryInfo.start * bytesPerElement * multiDrawMultiplier; 27949 multiDrawCounts[ multiDrawCount ] = geometryInfo.count * multiDrawMultiplier; 27950 indirectArray[ multiDrawCount ] = i; 27951 multiDrawCount ++; 27952 27953 } 27954 27955 } 27956 27957 } 27958 27959 } 27960 27961 indirectTexture.needsUpdate = true; 27962 this._multiDrawCount = multiDrawCount; 27963 this._multiDrawBytesPerElement = bytesPerElement; 27964 this._visibilityChanged = false; 27965 27966 } 27967 27968 onBeforeShadow( renderer, object, camera, shadowCamera, geometry, depthMaterial/* , group */ ) { 27969 27970 this.onBeforeRender( renderer, null, shadowCamera, geometry, depthMaterial ); 27971 27972 } 27973 27974} 27975 27976/** 27977 * A material for rendering line primitives. 27978 * 27979 * Materials define the appearance of renderable 3D objects. 27980 * 27981 * ```js 27982 * const material = new THREE.LineBasicMaterial( { color: 0xffffff } ); 27983 * ``` 27984 * 27985 * @augments Material 27986 */ 27987class LineBasicMaterial extends Material { 27988 27989 /** 27990 * Constructs a new line basic material. 27991 * 27992 * @param {Object} [parameters] - An object with one or more properties 27993 * defining the material's appearance. Any property of the material 27994 * (including any property from inherited materials) can be passed 27995 * in here. Color values can be passed any type of value accepted 27996 * by {@link Color#set}. 27997 */ 27998 constructor( parameters ) { 27999 28000 super(); 28001 28002 /** 28003 * This flag can be used for type testing. 28004 * 28005 * @type {boolean} 28006 * @readonly 28007 * @default true 28008 */ 28009 this.isLineBasicMaterial = true; 28010 28011 this.type = 'LineBasicMaterial'; 28012 28013 /** 28014 * Color of the material. 28015 * 28016 * @type {Color} 28017 * @default (1,1,1) 28018 */ 28019 this.color = new Color( 0xffffff ); 28020 28021 /** 28022 * Sets the color of the lines using data from a texture. The texture map 28023 * color is modulated by the diffuse `color`. 28024 * 28025 * `map` represents color data, and the texture must be assigned a 28026 * {@link Texture#colorSpace}. Most `map` textures set 28027 * `texture.colorSpace = SRGBColorSpace`. 28028 * 28029 * @type {?Texture} 28030 * @default null 28031 */ 28032 this.map = null; 28033 28034 /** 28035 * Controls line thickness or lines. 28036 * 28037 * Can only be used with {@link SVGRenderer}. WebGL and WebGPU 28038 * ignore this setting and always render line primitives with a 28039 * width of one pixel. 28040 * 28041 * @type {number} 28042 * @default 1 28043 */ 28044 this.linewidth = 1; 28045 28046 /** 28047 * Defines appearance of line ends. 28048 * 28049 * Can only be used with {@link SVGRenderer}. 28050 * 28051 * @type {('butt'|'round'|'square')} 28052 * @default 'round' 28053 */ 28054 this.linecap = 'round'; 28055 28056 /** 28057 * Defines appearance of line joints. 28058 * 28059 * Can only be used with {@link SVGRenderer}. 28060 * 28061 * @type {('round'|'bevel'|'miter')} 28062 * @default 'round' 28063 */ 28064 this.linejoin = 'round'; 28065 28066 /** 28067 * Whether the material is affected by fog or not. 28068 * 28069 * @type {boolean} 28070 * @default true 28071 */ 28072 this.fog = true; 28073 28074 this.setValues( parameters ); 28075 28076 } 28077 28078 copy( source ) { 28079 28080 super.copy( source ); 28081 28082 this.color.copy( source.color ); 28083 28084 this.map = source.map; 28085 28086 this.linewidth = source.linewidth; 28087 this.linecap = source.linecap; 28088 this.linejoin = source.linejoin; 28089 28090 this.fog = source.fog; 28091 28092 return this; 28093 28094 } 28095 28096} 28097 28098const _vStart = /*@__PURE__*/ new Vector3(); 28099const _vEnd = /*@__PURE__*/ new Vector3(); 28100 28101const _inverseMatrix$1 = /*@__PURE__*/ new Matrix4(); 28102const _ray$1 = /*@__PURE__*/ new Ray(); 28103const _sphere$1 = /*@__PURE__*/ new Sphere(); 28104 28105const _intersectPointOnRay = /*@__PURE__*/ new Vector3(); 28106const _intersectPointOnSegment = /*@__PURE__*/ new Vector3(); 28107 28108/** 28109 * A continuous line. The line are rendered by connecting consecutive 28110 * vertices with straight lines. 28111 * 28112 * ```js 28113 * const material = new THREE.LineBasicMaterial( { color: 0x0000ff } ); 28114 * 28115 * const points = []; 28116 * points.push( new THREE.Vector3( - 10, 0, 0 ) ); 28117 * points.push( new THREE.Vector3( 0, 10, 0 ) ); 28118 * points.push( new THREE.Vector3( 10, 0, 0 ) ); 28119 * 28120 * const geometry = new THREE.BufferGeometry().setFromPoints( points ); 28121 * 28122 * const line = new THREE.Line( geometry, material ); 28123 * scene.add( line ); 28124 * ``` 28125 * 28126 * @augments Object3D 28127 */ 28128class Line extends Object3D { 28129 28130 /** 28131 * Constructs a new line. 28132 * 28133 * @param {BufferGeometry} [geometry] - The line geometry. 28134 * @param {Material|Array<Material>} [material] - The line material. 28135 */ 28136 constructor( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) { 28137 28138 super(); 28139 28140 /** 28141 * This flag can be used for type testing. 28142 * 28143 * @type {boolean} 28144 * @readonly 28145 * @default true 28146 */ 28147 this.isLine = true; 28148 28149 this.type = 'Line'; 28150 28151 /** 28152 * The line geometry. 28153 * 28154 * @type {BufferGeometry} 28155 */ 28156 this.geometry = geometry; 28157 28158 /** 28159 * The line material. 28160 * 28161 * @type {Material|Array<Material>} 28162 * @default LineBasicMaterial 28163 */ 28164 this.material = material; 28165 28166 /** 28167 * A dictionary representing the morph targets in the geometry. The key is the 28168 * morph targets name, the value its attribute index. This member is `undefined` 28169 * by default and only set when morph targets are detected in the geometry. 28170 * 28171 * @type {Object<string,number>|undefined} 28172 * @default undefined 28173 */ 28174 this.morphTargetDictionary = undefined; 28175 28176 /** 28177 * An array of weights typically in the range `[0,1]` that specify how much of the morph 28178 * is applied. This member is `undefined` by default and only set when morph targets are 28179 * detected in the geometry. 28180 * 28181 * @type {Array<number>|undefined} 28182 * @default undefined 28183 */ 28184 this.morphTargetInfluences = undefined; 28185 28186 this.updateMorphTargets(); 28187 28188 } 28189 28190 copy( source, recursive ) { 28191 28192 super.copy( source, recursive ); 28193 28194 this.material = Array.isArray( source.material ) ? source.material.slice() : source.material; 28195 this.geometry = source.geometry; 28196 28197 return this; 28198 28199 } 28200 28201 /** 28202 * Computes an array of distance values which are necessary for rendering dashed lines. 28203 * For each vertex in the geometry, the method calculates the cumulative length from the 28204 * current point to the very beginning of the line. 28205 * 28206 * @return {Line} A reference to this line. 28207 */ 28208 computeLineDistances() { 28209 28210 const geometry = this.geometry; 28211 28212 // we assume non-indexed geometry 28213 28214 if ( geometry.index === null ) { 28215 28216 const positionAttribute = geometry.attributes.position; 28217 const lineDistances = [ 0 ]; 28218 28219 for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) { 28220 28221 _vStart.fromBufferAttribute( positionAttribute, i - 1 ); 28222 _vEnd.fromBufferAttribute( positionAttribute, i ); 28223 28224 lineDistances[ i ] = lineDistances[ i - 1 ]; 28225 lineDistances[ i ] += _vStart.distanceTo( _vEnd ); 28226 28227 } 28228 28229 geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); 28230 28231 } else { 28232 28233 warn( 'Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); 28234 28235 } 28236 28237 return this; 28238 28239 } 28240 28241 /** 28242 * Returns `true` if this line intersects the given frustum. 28243 * 28244 * @param {Frustum|FrustumArray} frustum - The frustum to test. 28245 * @return {boolean} Whether this line intersects the given frustum or not. 28246 */ 28247 intersectsFrustum( frustum ) { 28248 28249 return frustum.intersectsObject( this ); 28250 28251 } 28252 28253 /** 28254 * Computes intersection points between a casted ray and this line. 28255 * 28256 * @param {Raycaster} raycaster - The raycaster. 28257 * @param {Array<Object>} intersects - The target array that holds the intersection points. 28258 */ 28259 raycast( raycaster, intersects ) { 28260 28261 const geometry = this.geometry; 28262 const matrixWorld = this.matrixWorld; 28263 const threshold = raycaster.params.Line.threshold; 28264 const drawRange = geometry.drawRange; 28265 28266 // Checking boundingSphere distance to ray 28267 28268 if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); 28269 28270 _sphere$1.copy( geometry.boundingSphere ); 28271 _sphere$1.applyMatrix4( matrixWorld ); 28272 _sphere$1.radius += threshold; 28273 28274 if ( raycaster.ray.intersectsSphere( _sphere$1 ) === false ) return; 28275 28276 // 28277 28278 _inverseMatrix$1.copy( matrixWorld ).invert(); 28279 _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 ); 28280 28281 const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); 28282 const localThresholdSq = localThreshold * localThreshold; 28283 28284 const step = this.isLineSegments ? 2 : 1; 28285 28286 const index = geometry.index; 28287 const attributes = geometry.attributes; 28288 const positionAttribute = attributes.position; 28289 28290 if ( index !== null ) { 28291 28292 const start = Math.max( 0, drawRange.start ); 28293 const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); 28294 28295 for ( let i = start, l = end - 1; i < l; i += step ) { 28296 28297 const a = index.getX( i ); 28298 const b = index.getX( i + 1 ); 28299 28300 const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, a, b, i ); 28301 28302 if ( intersect ) { 28303 28304 intersects.push( intersect ); 28305 28306 } 28307 28308 } 28309 28310 if ( this.isLineLoop ) { 28311 28312 const a = index.getX( end - 1 ); 28313 const b = index.getX( start ); 28314 28315 const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, a, b, end - 1 ); 28316 28317 if ( intersect ) { 28318 28319 intersects.push( intersect ); 28320 28321 } 28322 28323 } 28324 28325 } else { 28326 28327 const start = Math.max( 0, drawRange.start ); 28328 const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); 28329 28330 for ( let i = start, l = end - 1; i < l; i += step ) { 28331 28332 const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, i, i + 1, i ); 28333 28334 if ( intersect ) { 28335 28336 intersects.push( intersect ); 28337 28338 } 28339 28340 } 28341 28342 if ( this.isLineLoop ) { 28343 28344 const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, end - 1, start, end - 1 ); 28345 28346 if ( intersect ) { 28347 28348 intersects.push( intersect ); 28349 28350 } 28351 28352 } 28353 28354 } 28355 28356 } 28357 28358 /** 28359 * Sets the values of {@link Line#morphTargetDictionary} and {@link Line#morphTargetInfluences} 28360 * to make sure existing morph targets can influence this 3D object. 28361 */ 28362 updateMorphTargets() { 28363 28364 const geometry = this.geometry; 28365 28366 const morphAttributes = geometry.morphAttributes; 28367 const keys = Object.keys( morphAttributes ); 28368 28369 if ( keys.length > 0 ) { 28370 28371 const morphAttribute = morphAttributes[ keys[ 0 ] ]; 28372 28373 if ( morphAttribute !== undefined ) { 28374 28375 this.morphTargetInfluences = []; 28376 this.morphTargetDictionary = {}; 28377 28378 for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { 28379 28380 const name = morphAttribute[ m ].name || String( m ); 28381 28382 this.morphTargetInfluences.push( 0 ); 28383 this.morphTargetDictionary[ name ] = m; 28384 28385 } 28386 28387 } 28388 28389 } 28390 28391 } 28392 28393} 28394 28395function checkIntersection( object, raycaster, ray, thresholdSq, a, b, i ) { 28396 28397 const positionAttribute = object.geometry.attributes.position; 28398 28399 _vStart.fromBufferAttribute( positionAttribute, a ); 28400 _vEnd.fromBufferAttribute( positionAttribute, b ); 28401 28402 const distSq = ray.distanceSqToSegment( _vStart, _vEnd, _intersectPointOnRay, _intersectPointOnSegment ); 28403 28404 if ( distSq > thresholdSq ) return; 28405 28406 _intersectPointOnRay.applyMatrix4( object.matrixWorld ); // Move back to world space for distance calculation 28407 28408 const distance = raycaster.ray.origin.distanceTo( _intersectPointOnRay ); 28409 28410 if ( distance < raycaster.near || distance > raycaster.far ) return; 28411 28412 return { 28413 28414 distance: distance, 28415 // What do we want? intersection point on the ray or on the segment?? 28416 // point: raycaster.ray.at( distance ), 28417 point: _intersectPointOnSegment.clone().applyMatrix4( object.matrixWorld ), 28418 index: i, 28419 face: null, 28420 faceIndex: null, 28421 barycoord: null, 28422 object: object 28423 28424 }; 28425 28426} 28427 28428const _start = /*@__PURE__*/ new Vector3(); 28429const _end = /*@__PURE__*/ new Vector3(); 28430 28431/** 28432 * A series of lines drawn between pairs of vertices. 28433 * 28434 * @augments Line 28435 */ 28436class LineSegments extends Line { 28437 28438 /** 28439 * Constructs a new line segments. 28440 * 28441 * @param {BufferGeometry} [geometry] - The line geometry. 28442 * @param {Material|Array<Material>} [material] - The line material. 28443 */ 28444 constructor( geometry, material ) { 28445 28446 super( geometry, material ); 28447 28448 /** 28449 * This flag can be used for type testing. 28450 * 28451 * @type {boolean} 28452 * @readonly 28453 * @default true 28454 */ 28455 this.isLineSegments = true; 28456 28457 this.type = 'LineSegments'; 28458 28459 } 28460 28461 computeLineDistances() { 28462 28463 const geometry = this.geometry; 28464 28465 // we assume non-indexed geometry 28466 28467 if ( geometry.index === null ) { 28468 28469 const positionAttribute = geometry.attributes.position; 28470 const lineDistances = []; 28471 28472 for ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) { 28473 28474 _start.fromBufferAttribute( positionAttribute, i ); 28475 _end.fromBufferAttribute( positionAttribute, i + 1 ); 28476 28477 lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; 28478 lineDistances[ i + 1 ] = lineDistances[ i ] + _start.distanceTo( _end ); 28479 28480 } 28481 28482 geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); 28483 28484 } else { 28485 28486 warn( 'LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); 28487 28488 } 28489 28490 return this; 28491 28492 } 28493 28494} 28495 28496/** 28497 * A continuous line. This is nearly the same as {@link Line} the only difference 28498 * is that the last vertex is connected with the first vertex in order to close 28499 * the line to form a loop. 28500 * 28501 * @augments Line 28502 */ 28503class LineLoop extends Line { 28504 28505 /** 28506 * Constructs a new line loop. 28507 * 28508 * @param {BufferGeometry} [geometry] - The line geometry. 28509 * @param {Material|Array<Material>} [material] - The line material. 28510 */ 28511 constructor( geometry, material ) { 28512 28513 super( geometry, material ); 28514 28515 /** 28516 * This flag can be used for type testing. 28517 * 28518 * @type {boolean} 28519 * @readonly 28520 * @default true 28521 */ 28522 this.isLineLoop = true; 28523 28524 this.type = 'LineLoop'; 28525 28526 } 28527 28528} 28529 28530/** 28531 * A material for rendering point primitives. 28532 * 28533 * Materials define the appearance of renderable 3D objects. 28534 * 28535 * ```js 28536 * const vertices = []; 28537 * 28538 * for ( let i = 0; i < 10000; i ++ ) { 28539 * const x = THREE.MathUtils.randFloatSpread( 2000 ); 28540 * const y = THREE.MathUtils.randFloatSpread( 2000 ); 28541 * const z = THREE.MathUtils.randFloatSpread( 2000 ); 28542 * 28543 * vertices.push( x, y, z ); 28544 * } 28545 * 28546 * const geometry = new THREE.BufferGeometry(); 28547 * geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) ); 28548 * const material = new THREE.PointsMaterial( { color: 0x888888 } ); 28549 * const points = new THREE.Points( geometry, material ); 28550 * scene.add( points ); 28551 * ``` 28552 * 28553 * @augments Material 28554 */ 28555class PointsMaterial extends Material { 28556 28557 /** 28558 * Constructs a new points material. 28559 * 28560 * @param {Object} [parameters] - An object with one or more properties 28561 * defining the material's appearance. Any property of the material 28562 * (including any property from inherited materials) can be passed 28563 * in here. Color values can be passed any type of value accepted 28564 * by {@link Color#set}. 28565 */ 28566 constructor( parameters ) { 28567 28568 super(); 28569 28570 /** 28571 * This flag can be used for type testing. 28572 * 28573 * @type {boolean} 28574 * @readonly 28575 * @default true 28576 */ 28577 this.isPointsMaterial = true; 28578 28579 this.type = 'PointsMaterial'; 28580 28581 /** 28582 * Color of the material. 28583 * 28584 * @type {Color} 28585 * @default (1,1,1) 28586 */ 28587 this.color = new Color( 0xffffff ); 28588 28589 /** 28590 * The color map. May optionally include an alpha channel, typically combined 28591 * with {@link Material#transparent} or {@link Material#alphaTest}. The texture map 28592 * color is modulated by the diffuse `color`. 28593 * 28594 * `map` represents color data, and the texture must be assigned a 28595 * {@link Texture#colorSpace}. Most `map` textures set 28596 * `texture.colorSpace = SRGBColorSpace`. 28597 * 28598 * @type {?Texture} 28599 * @default null 28600 */ 28601 this.map = null; 28602 28603 /** 28604 * The alpha map is a grayscale texture that controls the opacity across the 28605 * surface (black: fully transparent; white: fully opaque). 28606 * 28607 * Only the color of the texture is used, ignoring the alpha channel if one 28608 * exists. For RGB and RGBA textures, the renderer will use the green channel 28609 * when sampling this texture due to the extra bit of precision provided for 28610 * green in DXT-compressed and uncompressed RGB 565 formats. Luminance-only and 28611 * luminance/alpha textures will also still work as expected. 28612 * 28613 * `alphaMap` represents non-color data. Any texture assigned must have 28614 * `texture.colorSpace = NoColorSpace` (default). 28615 * 28616 * @type {?Texture} 28617 * @default null 28618 */ 28619 this.alphaMap = null; 28620 28621 /** 28622 * Defines the size of the points in pixels. 28623 * 28624 * Might be capped if the value exceeds hardware dependent parameters like [gl.ALIASED_POINT_SIZE_RANGE](https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/getParamete). 28625 * 28626 * @type {number} 28627 * @default 1 28628 */ 28629 this.size = 1; 28630 28631 /** 28632 * Specifies whether size of individual points is attenuated by the camera depth (perspective camera only). 28633 * 28634 * @type {boolean} 28635 * @default true 28636 */ 28637 this.sizeAttenuation = true; 28638 28639 /** 28640 * Whether the material is affected by fog or not. 28641 * 28642 * @type {boolean} 28643 * @default true 28644 */ 28645 this.fog = true; 28646 28647 this.setValues( parameters ); 28648 28649 } 28650 28651 copy( source ) { 28652 28653 super.copy( source ); 28654 28655 this.color.copy( source.color ); 28656 28657 this.map = source.map; 28658 28659 this.alphaMap = source.alphaMap; 28660 28661 this.size = source.size; 28662 this.sizeAttenuation = source.sizeAttenuation; 28663 28664 this.fog = source.fog; 28665 28666 return this; 28667 28668 } 28669 28670} 28671 28672const _inverseMatrix = /*@__PURE__*/ new Matrix4(); 28673const _ray = /*@__PURE__*/ new Ray(); 28674const _sphere = /*@__PURE__*/ new Sphere(); 28675const _position$3 = /*@__PURE__*/ new Vector3(); 28676 28677/** 28678 * A class for displaying points or point clouds. 28679 * 28680 * @augments Object3D 28681 */ 28682class Points extends Object3D { 28683 28684 /** 28685 * Constructs a new point cloud. 28686 * 28687 * @param {BufferGeometry} [geometry] - The points geometry. 28688 * @param {Material|Array<Material>} [material] - The points material. 28689 */ 28690 constructor( geometry = new BufferGeometry(), material = new PointsMaterial() ) { 28691 28692 super(); 28693 28694 /** 28695 * This flag can be used for type testing. 28696 * 28697 * @type {boolean} 28698 * @readonly 28699 * @default true 28700 */ 28701 this.isPoints = true; 28702 28703 this.type = 'Points'; 28704 28705 /** 28706 * The points geometry. 28707 * 28708 * @type {BufferGeometry} 28709 */ 28710 this.geometry = geometry; 28711 28712 /** 28713 * The line material. 28714 * 28715 * @type {Material|Array<Material>} 28716 * @default PointsMaterial 28717 */ 28718 this.material = material; 28719 28720 /** 28721 * A dictionary representing the morph targets in the geometry. The key is the 28722 * morph targets name, the value its attribute index. This member is `undefined` 28723 * by default and only set when morph targets are detected in the geometry. 28724 * 28725 * @type {Object<string,number>|undefined} 28726 * @default undefined 28727 */ 28728 this.morphTargetDictionary = undefined; 28729 28730 /** 28731 * An array of weights typically in the range `[0,1]` that specify how much of the morph 28732 * is applied. This member is `undefined` by default and only set when morph targets are 28733 * detected in the geometry. 28734 * 28735 * @type {Array<number>|undefined} 28736 * @default undefined 28737 */ 28738 this.morphTargetInfluences = undefined; 28739 28740 this.updateMorphTargets(); 28741 28742 } 28743 28744 copy( source, recursive ) { 28745 28746 super.copy( source, recursive ); 28747 28748 this.material = Array.isArray( source.material ) ? source.material.slice() : source.material; 28749 this.geometry = source.geometry; 28750 28751 return this; 28752 28753 } 28754 28755 /** 28756 * Returns `true` if this point cloud intersects the given frustum. 28757 * 28758 * @param {Frustum|FrustumArray} frustum - The frustum to test. 28759 * @return {boolean} Whether this point cloud intersects the given frustum or not. 28760 */ 28761 intersectsFrustum( frustum ) { 28762 28763 return frustum.intersectsObject( this ); 28764 28765 } 28766 28767 /** 28768 * Computes intersection points between a casted ray and this point cloud. 28769 * 28770 * @param {Raycaster} raycaster - The raycaster. 28771 * @param {Array<Object>} intersects - The target array that holds the intersection points. 28772 */ 28773 raycast( raycaster, intersects ) { 28774 28775 const geometry = this.geometry; 28776 const matrixWorld = this.matrixWorld; 28777 const threshold = raycaster.params.Points.threshold; 28778 const drawRange = geometry.drawRange; 28779 28780 // Checking boundingSphere distance to ray 28781 28782 if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); 28783 28784 _sphere.copy( geometry.boundingSphere ); 28785 _sphere.applyMatrix4( matrixWorld ); 28786 _sphere.radius += threshold; 28787 28788 if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return; 28789 28790 // 28791 28792 _inverseMatrix.copy( matrixWorld ).invert(); 28793 _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); 28794 28795 const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); 28796 const localThresholdSq = localThreshold * localThreshold; 28797 28798 const index = geometry.index; 28799 const attributes = geometry.attributes; 28800 const positionAttribute = attributes.position; 28801 28802 if ( index !== null ) { 28803 28804 const start = Math.max( 0, drawRange.start ); 28805 const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); 28806 28807 for ( let i = start, il = end; i < il; i ++ ) { 28808 28809 const a = index.getX( i ); 28810 28811 _position$3.fromBufferAttribute( positionAttribute, a ); 28812 28813 testPoint( _position$3, a, localThresholdSq, matrixWorld, raycaster, intersects, this ); 28814 28815 } 28816 28817 } else { 28818 28819 const start = Math.max( 0, drawRange.start ); 28820 const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); 28821 28822 for ( let i = start, l = end; i < l; i ++ ) { 28823 28824 _position$3.fromBufferAttribute( positionAttribute, i ); 28825 28826 testPoint( _position$3, i, localThresholdSq, matrixWorld, raycaster, intersects, this ); 28827 28828 } 28829 28830 } 28831 28832 } 28833 28834 /** 28835 * Sets the values of {@link Points#morphTargetDictionary} and {@link Points#morphTargetInfluences} 28836 * to make sure existing morph targets can influence this 3D object. 28837 */ 28838 updateMorphTargets() { 28839 28840 const geometry = this.geometry; 28841 28842 const morphAttributes = geometry.morphAttributes; 28843 const keys = Object.keys( morphAttributes ); 28844 28845 if ( keys.length > 0 ) { 28846 28847 const morphAttribute = morphAttributes[ keys[ 0 ] ]; 28848 28849 if ( morphAttribute !== undefined ) { 28850 28851 this.morphTargetInfluences = []; 28852 this.morphTargetDictionary = {}; 28853 28854 for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { 28855 28856 const name = morphAttribute[ m ].name || String( m ); 28857 28858 this.morphTargetInfluences.push( 0 ); 28859 this.morphTargetDictionary[ name ] = m; 28860 28861 } 28862 28863 } 28864 28865 } 28866 28867 } 28868 28869} 28870 28871function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) { 28872 28873 const rayPointDistanceSq = _ray.distanceSqToPoint( point ); 28874 28875 if ( rayPointDistanceSq < localThresholdSq ) { 28876 28877 const intersectPoint = new Vector3(); 28878 28879 _ray.closestPointToPoint( point, intersectPoint ); 28880 intersectPoint.applyMatrix4( matrixWorld ); 28881 28882 const distance = raycaster.ray.origin.distanceTo( intersectPoint ); 28883 28884 if ( distance < raycaster.near || distance > raycaster.far ) return; 28885 28886 intersects.push( { 28887 28888 distance: distance, 28889 distanceToRay: Math.sqrt( rayPointDistanceSq ), 28890 point: intersectPoint, 28891 index: index, 28892 face: null, 28893 faceIndex: null, 28894 barycoord: null, 28895 object: object 28896 28897 } ); 28898 28899 } 28900 28901} 28902 28903/** 28904 * A texture for use with a video. 28905 * 28906 * ```js 28907 * // assuming you have created a HTML video element with id="video" 28908 * const video = document.getElementById( 'video' ); 28909 * const texture = new THREE.VideoTexture( video ); 28910 * ``` 28911 * 28912 * Note: When using video textures with {@link WebGPURenderer}, {@link Texture#colorSpace} must be 28913 * set to THREE.SRGBColorSpace. 28914 * 28915 * Note: After the initial use of a texture, its dimensions, format, and type 28916 * cannot be changed. Instead, call {@link Texture#dispose} on the texture and instantiate a new one. 28917 * 28918 * @augments Texture 28919 */ 28920class VideoTexture extends Texture { 28921 28922 /** 28923 * Constructs a new video texture. 28924 * 28925 * @param {HTMLVideoElement} video - The video element to use as a data source for the texture. 28926 * @param {number} [mapping=Texture.DEFAULT_MAPPING] - The texture mapping. 28927 * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value. 28928 * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value. 28929 * @param {number} [magFilter=LinearFilter] - The mag filter value. 28930 * @param {number} [minFilter=LinearFilter] - The min filter value. 28931 * @param {number} [format=RGBAFormat] - The texture format. 28932 * @param {number} [type=UnsignedByteType] - The texture type. 28933 * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value. 28934 */ 28935 constructor( video, mapping, wrapS, wrapT, magFilter = LinearFilter, minFilter = LinearFilter, format, type, anisotropy ) { 28936 28937 super( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); 28938 28939 /** 28940 * This flag can be used for type testing. 28941 * 28942 * @type {boolean} 28943 * @readonly 28944 * @default true 28945 */ 28946 this.isVideoTexture = true; 28947 28948 /** 28949 * Whether to generate mipmaps (if possible) for a texture. 28950 * 28951 * Overwritten and set to `false` by default. 28952 * 28953 * @type {boolean} 28954 * @default false 28955 */ 28956 this.generateMipmaps = false; 28957 28958 /** 28959 * The video frame request callback identifier, which is a positive integer. 28960 * 28961 * Value of 0 represents no scheduled rVFC. 28962 * 28963 * @private 28964 * @type {number} 28965 */ 28966 this._requestVideoFrameCallbackId = 0; 28967 28968 const scope = this; 28969 28970 function updateVideo() { 28971 28972 scope.needsUpdate = true; 28973 scope._requestVideoFrameCallbackId = video.requestVideoFrameCallback( updateVideo ); 28974 28975 } 28976 28977 if ( 'requestVideoFrameCallback' in video ) { 28978 28979 this._requestVideoFrameCallbackId = video.requestVideoFrameCallback( updateVideo ); 28980 28981 } 28982 28983 } 28984 28985 clone() { 28986 28987 return new this.constructor( this.image ).copy( this ); 28988 28989 } 28990 28991 /** 28992 * This method is called automatically by the renderer and sets {@link Texture#needsUpdate} 28993 * to `true` every time a new frame is available. 28994 * 28995 * Only relevant if `requestVideoFrameCallback` is not supported in the browser. 28996 */ 28997 update() { 28998 28999 const video = this.image; 29000 const hasVideoFrameCallback = 'requestVideoFrameCallback' in video; 29001 29002 if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) { 29003 29004 this.needsUpdate = true; 29005 29006 } 29007 29008 } 29009 29010 dispose() { 29011 29012 if ( this._requestVideoFrameCallbackId !== 0 ) { 29013 29014 this.source.data.cancelVideoFrameCallback( this._requestVideoFrameCallbackId ); 29015 29016 this._requestVideoFrameCallbackId = 0; 29017 29018 } 29019 29020 super.dispose(); 29021 29022 } 29023 29024} 29025 29026/** 29027 * This class can be used as an alternative way to define video data. Instead of using 29028 * an instance of `HTMLVideoElement` like with `VideoTexture`, `VideoFrameTexture` expects each frame is 29029 * defined manually via {@link VideoFrameTexture#setFrame}. A typical use case for this module is when 29030 * video frames are decoded with the WebCodecs API. 29031 * 29032 * ```js 29033 * const texture = new THREE.VideoFrameTexture(); 29034 * texture.setFrame( frame ); 29035 * ``` 29036 * 29037 * @augments VideoTexture 29038 */ 29039class VideoFrameTexture extends VideoTexture { 29040 29041 /** 29042 * Constructs a new video frame texture. 29043 * 29044 * @param {number} [mapping=Texture.DEFAULT_MAPPING] - The texture mapping. 29045 * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value. 29046 * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value. 29047 * @param {number} [magFilter=LinearFilter] - The mag filter value. 29048 * @param {number} [minFilter=LinearFilter] - The min filter value. 29049 * @param {number} [format=RGBAFormat] - The texture format. 29050 * @param {number} [type=UnsignedByteType] - The texture type. 29051 * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value. 29052 */ 29053 constructor( mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { 29054 29055 super( {}, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); 29056 29057 /** 29058 * This flag can be used for type testing. 29059 * 29060 * @type {boolean} 29061 * @readonly 29062 * @default true 29063 */ 29064 this.isVideoFrameTexture = true; 29065 29066 } 29067 29068 /** 29069 * This method overwritten with an empty implementation since 29070 * this type of texture is updated via `setFrame()`. 29071 */ 29072 update() {} 29073 29074 clone() { 29075 29076 return new this.constructor().copy( this ); // restoring Texture.clone() 29077 29078 } 29079 29080 /** 29081 * Sets the current frame of the video. This will automatically update the texture 29082 * so the data can be used for rendering. 29083 * 29084 * @param {VideoFrame} frame - The video frame. 29085 */ 29086 setFrame( frame ) { 29087 29088 this.image = frame; 29089 this.needsUpdate = true; 29090 29091 } 29092 29093} 29094 29095/** 29096 * This class can only be used in combination with `copyFramebufferToTexture()` methods 29097 * of renderers. It extracts the contents of the current bound framebuffer and provides it 29098 * as a texture for further usage. 29099 * 29100 * ```js 29101 * const pixelRatio = window.devicePixelRatio; 29102 * const textureSize = 128 * pixelRatio; 29103 * 29104 * const frameTexture = new FramebufferTexture( textureSize, textureSize ); 29105 * 29106 * // calculate start position for copying part of the frame data 29107 * const vector = new Vector2(); 29108 * vector.x = ( window.innerWidth * pixelRatio / 2 ) - ( textureSize / 2 ); 29109 * vector.y = ( window.innerHeight * pixelRatio / 2 ) - ( textureSize / 2 ); 29110 * 29111 * renderer.render( scene, camera ); 29112 * 29113 * // copy part of the rendered frame into the framebuffer texture 29114 * renderer.copyFramebufferToTexture( frameTexture, vector ); 29115 * ``` 29116 * 29117 * @augments Texture 29118 */ 29119class FramebufferTexture extends Texture { 29120 29121 /** 29122 * Constructs a new framebuffer texture. 29123 * 29124 * @param {number} [width] - The width of the texture. 29125 * @param {number} [height] - The height of the texture. 29126 */ 29127 constructor( width, height ) { 29128 29129 super( { width, height } ); 29130 29131 /** 29132 * This flag can be used for type testing. 29133 * 29134 * @type {boolean} 29135 * @readonly 29136 * @default true 29137 */ 29138 this.isFramebufferTexture = true; 29139 29140 /** 29141 * How the texture is sampled when a texel covers more than one pixel. 29142 * 29143 * Overwritten and set to `NearestFilter` by default to disable filtering. 29144 * 29145 * @type {(NearestFilter|NearestMipmapNearestFilter|NearestMipmapLinearFilter|LinearFilter|LinearMipmapNearestFilter|LinearMipmapLinearFilter)} 29146 * @default NearestFilter 29147 */ 29148 this.magFilter = NearestFilter; 29149 29150 /** 29151 * How the texture is sampled when a texel covers less than one pixel. 29152 * 29153 * Overwritten and set to `NearestFilter` by default to disable filtering. 29154 * 29155 * @type {(NearestFilter|NearestMipmapNearestFilter|NearestMipmapLinearFilter|LinearFilter|LinearMipmapNearestFilter|LinearMipmapLinearFilter)} 29156 * @default NearestFilter 29157 */ 29158 this.minFilter = NearestFilter; 29159 29160 /** 29161 * Whether to generate mipmaps (if possible) for a texture. 29162 * 29163 * Overwritten and set to `false` by default. 29164 * 29165 * @type {boolean} 29166 * @default false 29167 */ 29168 this.generateMipmaps = false; 29169 29170 this.needsUpdate = true; 29171 29172 } 29173 29174} 29175 29176/** 29177 * Creates a texture based on data in compressed form. 29178 * 29179 * These texture are usually loaded with {@link CompressedTextureLoader}. 29180 * 29181 * @augments Texture 29182 */ 29183class CompressedTexture extends Texture { 29184 29185 /** 29186 * Constructs a new compressed texture. 29187 * 29188 * @param {Array<Object>} mipmaps - This array holds for all mipmaps (including the bases mip) 29189 * the data and dimensions. 29190 * @param {number} width - The width of the texture. 29191 * @param {number} height - The height of the texture. 29192 * @param {number} [format=RGBAFormat] - The texture format. 29193 * @param {number} [type=UnsignedByteType] - The texture type. 29194 * @param {number} [mapping=Texture.DEFAULT_MAPPING] - The texture mapping. 29195 * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value. 29196 * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value. 29197 * @param {number} [magFilter=LinearFilter] - The mag filter value. 29198 * @param {number} [minFilter=LinearMipmapLinearFilter] - The min filter value. 29199 * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value. 29200 * @param {string} [colorSpace=NoColorSpace] - The color space. 29201 */ 29202 constructor( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, colorSpace ) { 29203 29204 super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ); 29205 29206 /** 29207 * This flag can be used for type testing. 29208 * 29209 * @type {boolean} 29210 * @readonly 29211 * @default true 29212 */ 29213 this.isCompressedTexture = true; 29214 29215 /** 29216 * The image property of a compressed texture just defines its dimensions. 29217 * 29218 * @type {{width:number,height:number}} 29219 */ 29220 this.image = { width: width, height: height }; 29221 29222 /** 29223 * This array holds for all mipmaps (including the bases mip) the data and dimensions. 29224 * 29225 * @type {Array<Object>} 29226 */ 29227 this.mipmaps = mipmaps; 29228 29229 /** 29230 * If set to `true`, the texture is flipped along the vertical axis when 29231 * uploaded to the GPU. 29232 * 29233 * Overwritten and set to `false` by default since it is not possible to 29234 * flip compressed textures. 29235 * 29236 * @type {boolean} 29237 * @default false 29238 * @readonly 29239 */ 29240 this.flipY = false; 29241 29242 /** 29243 * Whether to generate mipmaps (if possible) for a texture. 29244 * 29245 * Overwritten and set to `false` by default since it is not 29246 * possible to generate mipmaps for compressed data. Mipmaps 29247 * must be embedded in the compressed texture file. 29248 * 29249 * @type {boolean} 29250 * @default false 29251 * @readonly 29252 */ 29253 this.generateMipmaps = false; 29254 29255 } 29256 29257} 29258 29259/** 29260 * Creates a texture 2D array based on data in compressed form. 29261 * 29262 * These texture are usually loaded with {@link CompressedTextureLoader}. 29263 * 29264 * @augments CompressedTexture 29265 */ 29266class CompressedArrayTexture extends CompressedTexture { 29267 29268 /** 29269 * Constructs a new compressed array texture. 29270 * 29271 * @param {Array<Object>} mipmaps - This array holds for all mipmaps (including the bases mip) 29272 * the data and dimensions. 29273 * @param {number} width - The width of the texture. 29274 * @param {number} height - The height of the texture. 29275 * @param {number} depth - The depth of the texture. 29276 * @param {number} [format=RGBAFormat] - The min filter value. 29277 * @param {number} [type=UnsignedByteType] - The min filter value. 29278 */ 29279 constructor( mipmaps, width, height, depth, format, type ) { 29280 29281 super( mipmaps, width, height, format, type ); 29282 29283 /** 29284 * This flag can be used for type testing. 29285 * 29286 * @type {boolean} 29287 * @readonly 29288 * @default true 29289 */ 29290 this.isCompressedArrayTexture = true; 29291 29292 /** 29293 * The image property of a compressed texture just defines its dimensions. 29294 * 29295 * @name CompressedArrayTexture#image 29296 * @type {{width:number,height:number,depth:number}} 29297 */ 29298 this.image.depth = depth; 29299 29300 /** 29301 * This defines how the texture is wrapped in the depth and corresponds to 29302 * *W* in UVW mapping. 29303 * 29304 * @type {(RepeatWrapping|ClampToEdgeWrapping|MirroredRepeatWrapping)} 29305 * @default ClampToEdgeWrapping 29306 */ 29307 this.wrapR = ClampToEdgeWrapping; 29308 29309 /** 29310 * A set of all layers which need to be updated in the texture. 29311 * 29312 * @type {Set<number>} 29313 */ 29314 this.layerUpdates = new Set(); 29315 29316 } 29317 29318 /** 29319 * Copies the values of the given texture to this instance. 29320 * 29321 * @param {CompressedArrayTexture} source - The texture to copy. 29322 * @return {CompressedArrayTexture} A reference to this instance. 29323 */ 29324 copy( source ) { 29325 29326 super.copy( source ); 29327 29328 this.wrapR = source.wrapR; 29329 29330 return this; 29331 29332 } 29333 29334 /** 29335 * Describes that a specific layer of the texture needs to be updated. 29336 * Normally when {@link Texture#needsUpdate} is set to `true`, the 29337 * entire compressed texture array is sent to the GPU. Marking specific 29338 * layers will only transmit subsets of all mipmaps associated with a 29339 * specific depth in the array which is often much more performant. 29340 * 29341 * @param {number} layerIndex - The layer index that should be updated. 29342 */ 29343 addLayerUpdate( layerIndex ) { 29344 29345 this.layerUpdates.add( layerIndex ); 29346 29347 } 29348 29349 /** 29350 * Resets the layer updates registry. 29351 */ 29352 clearLayerUpdates() { 29353 29354 this.layerUpdates.clear(); 29355 29356 } 29357 29358} 29359 29360/** 29361 * Creates a cube texture based on data in compressed form. 29362 * 29363 * These texture are usually loaded with {@link CompressedTextureLoader}. 29364 * 29365 * @augments CompressedTexture 29366 */ 29367class CompressedCubeTexture extends CompressedTexture { 29368 29369 /** 29370 * Constructs a new compressed texture. 29371 * 29372 * @param {Array<CompressedTexture>} images - An array of compressed textures. 29373 * @param {number} [format=RGBAFormat] - The texture format. 29374 * @param {number} [type=UnsignedByteType] - The texture type. 29375 */ 29376 constructor( images, format, type ) { 29377 29378 super( undefined, images[ 0 ].width, images[ 0 ].height, format, type, CubeReflectionMapping ); 29379 29380 /** 29381 * This flag can be used for type testing. 29382 * 29383 * @type {boolean} 29384 * @readonly 29385 * @default true 29386 */ 29387 this.isCompressedCubeTexture = true; 29388 29389 /** 29390 * This flag can be used for type testing. 29391 * 29392 * @type {boolean} 29393 * @readonly 29394 * @default true 29395 */ 29396 this.isCubeTexture = true; 29397 29398 this.image = images; 29399 29400 } 29401 29402} 29403 29404/** 29405 * Creates a cube texture made up of six images. 29406 * 29407 * ```js 29408 * const loader = new THREE.CubeTextureLoader(); 29409 * loader.setPath( 'textures/cube/pisa/' ); 29410 * 29411 * const textureCube = loader.load( [ 29412 * 'px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png' 29413 * ] ); 29414 * 29415 * const material = new THREE.MeshBasicMaterial( { color: 0xffffff, envMap: textureCube } ); 29416 * ``` 29417 * 29418 * @augments Texture 29419 */ 29420class CubeTexture extends Texture { 29421 29422 /** 29423 * Constructs a new cube texture. 29424 * 29425 * @param {Array<Image>} [images=[]] - An array holding a image for each side of a cube. 29426 * @param {number} [mapping=CubeReflectionMapping] - The texture mapping. 29427 * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value. 29428 * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value. 29429 * @param {number} [magFilter=LinearFilter] - The mag filter value. 29430 * @param {number} [minFilter=LinearMipmapLinearFilter] - The min filter value. 29431 * @param {number} [format=RGBAFormat] - The texture format. 29432 * @param {number} [type=UnsignedByteType] - The texture type. 29433 * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value. 29434 * @param {string} [colorSpace=NoColorSpace] - The color space value. 29435 */ 29436 constructor( images = [], mapping = CubeReflectionMapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ) { 29437 29438 super( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ); 29439 29440 /** 29441 * This flag can be used for type testing. 29442 * 29443 * @type {boolean} 29444 * @readonly 29445 * @default true 29446 */ 29447 this.isCubeTexture = true; 29448 29449 /** 29450 * If set to `true`, the texture is flipped along the vertical axis when 29451 * uploaded to the GPU. 29452 * 29453 * Overwritten and set to `false` by default. 29454 * 29455 * @type {boolean} 29456 * @default false 29457 */ 29458 this.flipY = false; 29459 29460 } 29461 29462 /** 29463 * Alias for {@link CubeTexture#image}. 29464 * 29465 * @type {Array<Image>} 29466 */ 29467 get images() { 29468 29469 return this.image; 29470 29471 } 29472 29473 set images( value ) { 29474 29475 this.image = value; 29476 29477 } 29478 29479} 29480 29481/** 29482 * Creates a texture from a canvas element. 29483 * 29484 * This is almost the same as the base texture class, except that it sets {@link Texture#needsUpdate} 29485 * to `true` immediately since a canvas can directly be used for rendering. 29486 * 29487 * @augments Texture 29488 */ 29489class CanvasTexture extends Texture { 29490 29491 /** 29492 * Constructs a new texture. 29493 * 29494 * @param {HTMLCanvasElement} [canvas] - The HTML canvas element. 29495 * @param {number} [mapping=Texture.DEFAULT_MAPPING] - The texture mapping. 29496 * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value. 29497 * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value. 29498 * @param {number} [magFilter=LinearFilter] - The mag filter value. 29499 * @param {number} [minFilter=LinearMipmapLinearFilter] - The min filter value. 29500 * @param {number} [format=RGBAFormat] - The texture format. 29501 * @param {number} [type=UnsignedByteType] - The texture type. 29502 * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value. 29503 */ 29504 constructor( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { 29505 29506 super( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); 29507 29508 /** 29509 * This flag can be used for type testing. 29510 * 29511 * @type {boolean} 29512 * @readonly 29513 * @default true 29514 */ 29515 this.isCanvasTexture = true; 29516 29517 this.needsUpdate = true; 29518 29519 } 29520 29521} 29522 29523/** 29524 * Creates a texture from an HTML element. 29525 * 29526 * This is almost the same as the base texture class, except that it sets {@link Texture#needsUpdate} 29527 * to `true` immediately and listens for the parent canvas's paint events to trigger updates. 29528 * 29529 * @augments Texture 29530 */ 29531class HTMLTexture extends Texture { 29532 29533 /** 29534 * Constructs a new texture. 29535 * 29536 * @param {HTMLElement} [element] - The HTML element. 29537 * @param {number} [mapping=Texture.DEFAULT_MAPPING] - The texture mapping. 29538 * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value. 29539 * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value. 29540 * @param {number} [magFilter=LinearFilter] - The mag filter value. 29541 * @param {number} [minFilter=LinearMipmapLinearFilter] - The min filter value. 29542 * @param {number} [format=RGBAFormat] - The texture format. 29543 * @param {number} [type=UnsignedByteType] - The texture type. 29544 * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value. 29545 */ 29546 constructor( element, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { 29547 29548 super( element, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); 29549 29550 /** 29551 * This flag can be used for type testing. 29552 * 29553 * @type {boolean} 29554 * @readonly 29555 * @default true 29556 */ 29557 this.isHTMLTexture = true; 29558 this.generateMipmaps = false; 29559 29560 this.needsUpdate = true; 29561 29562 const parent = element ? element.parentNode : null; 29563 29564 if ( parent !== null && 'requestPaint' in parent ) { 29565 29566 parent.onpaint = () => { 29567 29568 this.needsUpdate = true; 29569 29570 }; 29571 29572 parent.requestPaint(); 29573 29574 } 29575 29576 } 29577 29578 dispose() { 29579 29580 const parent = this.image ? this.image.parentNode : null; 29581 29582 if ( parent !== null && 'onpaint' in parent ) { 29583 29584 parent.onpaint = null; 29585 29586 } 29587 29588 super.dispose(); 29589 29590 } 29591 29592} 29593 29594/** 29595 * This class can be used to automatically save the depth information of a 29596 * rendering into a texture. 29597 * 29598 * @augments Texture 29599 */ 29600class DepthTexture extends Texture { 29601 29602 /** 29603 * Constructs a new depth texture. 29604 * 29605 * @param {number} width - The width of the texture. 29606 * @param {number} height - The height of the texture. 29607 * @param {number} [type=UnsignedIntType] - The texture type. 29608 * @param {number} [mapping=Texture.DEFAULT_MAPPING] - The texture mapping. 29609 * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value. 29610 * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value. 29611 * @param {number} [magFilter=LinearFilter] - The mag filter value. 29612 * @param {number} [minFilter=LinearFilter] - The min filter value. 29613 * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value. 29614 * @param {number} [format=DepthFormat] - The texture format. 29615 * @param {number} [depth=1] - The depth of the texture. 29616 */ 29617 constructor( width, height, type = UnsignedIntType, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, format = DepthFormat, depth = 1 ) { 29618 29619 if ( format !== DepthFormat && format !== DepthStencilFormat ) { 29620 29621 throw new Error( 'THREE.DepthTexture: format must be either THREE.DepthFormat or THREE.DepthStencilFormat' ); 29622 29623 } 29624 29625 const image = { width: width, height: height, depth: depth }; 29626 29627 super( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); 29628 29629 /** 29630 * This flag can be used for type testing. 29631 * 29632 * @type {boolean} 29633 * @readonly 29634 * @default true 29635 */ 29636 this.isDepthTexture = true; 29637 29638 /** 29639 * If set to `true`, the texture is flipped along the vertical axis when 29640 * uploaded to the GPU. 29641 * 29642 * Overwritten and set to `false` by default. 29643 * 29644 * @type {boolean} 29645 * @default false 29646 */ 29647 this.flipY = false; 29648 29649 /** 29650 * Whether to generate mipmaps (if possible) for a texture. 29651 * 29652 * Overwritten and set to `false` by default. 29653 * 29654 * @type {boolean} 29655 * @default false 29656 */ 29657 this.generateMipmaps = false; 29658 29659 /** 29660 * Code corresponding to the depth compare function. 29661 * 29662 * @type {?(NeverCompare|LessCompare|EqualCompare|LessEqualCompare|GreaterCompare|NotEqualCompare|GreaterEqualCompare|AlwaysCompare)} 29663 * @default null 29664 */ 29665 this.compareFunction = null; 29666 29667 } 29668 29669 29670 copy( source ) { 29671 29672 super.copy( source ); 29673 29674 this.source = new TextureSource( Object.assign( {}, source.image ) ); // see #30540 29675 this.compareFunction = source.compareFunction; 29676 29677 return this; 29678 29679 } 29680 29681 toJSON( meta ) { 29682 29683 const data = super.toJSON( meta ); 29684 29685 data.compareFunction = this.compareFunction; 29686 29687 return data; 29688 29689 } 29690 29691} 29692 29693/** 29694 * This class can be used to automatically save the depth information of a 29695 * cube rendering into a cube texture with depth format. Used for PointLight shadows. 29696 * 29697 * @augments DepthTexture 29698 */ 29699class CubeDepthTexture extends DepthTexture { 29700 29701 /** 29702 * Constructs a new cube depth texture. 29703 * 29704 * @param {number} size - The size (width and height) of each cube face. 29705 * @param {number} [type=UnsignedIntType] - The texture type. 29706 * @param {number} [mapping=CubeReflectionMapping] - The texture mapping. 29707 * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value. 29708 * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value. 29709 * @param {number} [magFilter=NearestFilter] - The mag filter value. 29710 * @param {number} [minFilter=NearestFilter] - The min filter value. 29711 * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value. 29712 * @param {number} [format=DepthFormat] - The texture format. 29713 */ 29714 constructor( size, type = UnsignedIntType, mapping = CubeReflectionMapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, format = DepthFormat ) { 29715 29716 // Create 6 identical image descriptors for the cube faces 29717 const image = { width: size, height: size, depth: 1 }; 29718 const images = [ image, image, image, image, image, image ]; 29719 29720 // Call DepthTexture constructor with width, height 29721 super( size, size, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ); 29722 29723 // Replace the single image with the array of 6 images 29724 this.image = images; 29725 29726 /** 29727 * This flag can be used for type testing. 29728 * 29729 * @type {boolean} 29730 * @readonly 29731 * @default true 29732 */ 29733 this.isCubeDepthTexture = true; 29734 29735 /** 29736 * Set to true for cube texture handling in WebGLTextures. 29737 * 29738 * @type {boolean} 29739 * @readonly 29740 * @default true 29741 */ 29742 this.isCubeTexture = true; 29743 29744 } 29745 29746 /** 29747 * Alias for {@link CubeDepthTexture#image}. 29748 * 29749 * @type {Array<Image>} 29750 */ 29751 get images() { 29752 29753 return this.image; 29754 29755 } 29756 29757 set images( value ) { 29758 29759 this.image = value; 29760 29761 } 29762 29763} 29764 29765/** 29766 * Represents a texture created externally with the same renderer context. 29767 * 29768 * This may be a texture from a protected media stream, device camera feed, 29769 * or other data feeds like a depth sensor. 29770 * 29771 * @augments Texture 29772 */ 29773class ExternalTexture extends Texture { 29774 29775 /** 29776 * Creates a new raw texture. 29777 * 29778 * @param {?(WebGLTexture|GPUTexture)} [sourceTexture=null] - The external texture. 29779 */ 29780 constructor( sourceTexture = null ) { 29781 29782 super(); 29783 29784 /** 29785 * The external source texture. 29786 * 29787 * @type {?(WebGLTexture|GPUTexture)} 29788 * @default null 29789 */ 29790 this.sourceTexture = sourceTexture; 29791 29792 /** 29793 * This flag can be used for type testing. 29794 * 29795 * @type {boolean} 29796 * @readonly 29797 * @default true 29798 */ 29799 this.isExternalTexture = true; 29800 29801 } 29802 29803 copy( source ) { 29804 29805 super.copy( source ); 29806 29807 this.sourceTexture = source.sourceTexture; 29808 29809 return this; 29810 29811 } 29812 29813} 29814 29815/** 29816 * A geometry class for a rectangular cuboid with a given width, height, and depth. 29817 * On creation, the cuboid is centred on the origin, with each edge parallel to one 29818 * of the axes. 29819 * 29820 * ```js 29821 * const geometry = new THREE.BoxGeometry( 1, 1, 1 ); 29822 * const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); 29823 * const cube = new THREE.Mesh( geometry, material ); 29824 * scene.add( cube ); 29825 * ``` 29826 * 29827 * @augments BufferGeometry 29828 * @demo scenes/geometry-browser.html#BoxGeometry 29829 */ 29830class BoxGeometry extends BufferGeometry { 29831 29832 /** 29833 * Constructs a new box geometry. 29834 * 29835 * @param {number} [width=1] - The width. That is, the length of the edges parallel to the X axis. 29836 * @param {number} [height=1] - The height. That is, the length of the edges parallel to the Y axis. 29837 * @param {number} [depth=1] - The depth. That is, the length of the edges parallel to the Z axis. 29838 * @param {number} [widthSegments=1] - Number of segmented rectangular faces along the width of the sides. 29839 * @param {number} [heightSegments=1] - Number of segmented rectangular faces along the height of the sides. 29840 * @param {number} [depthSegments=1] - Number of segmented rectangular faces along the depth of the sides. 29841 */ 29842 constructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1 ) { 29843 29844 super(); 29845 29846 this.type = 'BoxGeometry'; 29847 29848 /** 29849 * Holds the constructor parameters that have been 29850 * used to generate the geometry. Any modification 29851 * after instantiation does not change the geometry. 29852 * 29853 * @type {Object} 29854 */ 29855 this.parameters = { 29856 width: width, 29857 height: height, 29858 depth: depth, 29859 widthSegments: widthSegments, 29860 heightSegments: heightSegments, 29861 depthSegments: depthSegments 29862 }; 29863 29864 const scope = this; 29865 29866 // segments 29867 29868 widthSegments = Math.floor( widthSegments ); 29869 heightSegments = Math.floor( heightSegments ); 29870 depthSegments = Math.floor( depthSegments ); 29871 29872 // buffers 29873 29874 const indices = []; 29875 const vertices = []; 29876 const normals = []; 29877 const uvs = []; 29878 29879 // helper variables 29880 29881 let numberOfVertices = 0; 29882 let groupStart = 0; 29883 29884 // build each side of the box geometry 29885 29886 buildPlane( 'z', 'y', 'x', -1, -1, depth, height, width, depthSegments, heightSegments, 0 ); // px 29887 buildPlane( 'z', 'y', 'x', 1, -1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx 29888 buildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py 29889 buildPlane( 'x', 'z', 'y', 1, -1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny 29890 buildPlane( 'x', 'y', 'z', 1, -1, width, height, depth, widthSegments, heightSegments, 4 ); // pz 29891 buildPlane( 'x', 'y', 'z', -1, -1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz 29892 29893 // build geometry 29894 29895 this.setIndex( indices ); 29896 this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 29897 this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); 29898 this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); 29899 29900 function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { 29901 29902 const segmentWidth = width / gridX; 29903 const segmentHeight = height / gridY; 29904 29905 const widthHalf = width / 2; 29906 const heightHalf = height / 2; 29907 const depthHalf = depth / 2; 29908 29909 const gridX1 = gridX + 1; 29910 const gridY1 = gridY + 1; 29911 29912 let vertexCounter = 0; 29913 let groupCount = 0; 29914 29915 const vector = new Vector3(); 29916 29917 // generate vertices, normals and uvs 29918 29919 for ( let iy = 0; iy < gridY1; iy ++ ) { 29920 29921 const y = iy * segmentHeight - heightHalf; 29922 29923 for ( let ix = 0; ix < gridX1; ix ++ ) { 29924 29925 const x = ix * segmentWidth - widthHalf; 29926 29927 // set values to correct vector component 29928 29929 vector[ u ] = x * udir; 29930 vector[ v ] = y * vdir; 29931 vector[ w ] = depthHalf; 29932 29933 // now apply vector to vertex buffer 29934 29935 vertices.push( vector.x, vector.y, vector.z ); 29936 29937 // set values to correct vector component 29938 29939 vector[ u ] = 0; 29940 vector[ v ] = 0; 29941 vector[ w ] = depth > 0 ? 1 : -1; 29942 29943 // now apply vector to normal buffer 29944 29945 normals.push( vector.x, vector.y, vector.z ); 29946 29947 // uvs 29948 29949 uvs.push( ix / gridX ); 29950 uvs.push( 1 - ( iy / gridY ) ); 29951 29952 // counters 29953 29954 vertexCounter += 1; 29955 29956 } 29957 29958 } 29959 29960 // indices 29961 29962 // 1. you need three indices to draw a single face 29963 // 2. a single segment consists of two faces 29964 // 3. so we need to generate six (2*3) indices per segment 29965 29966 for ( let iy = 0; iy < gridY; iy ++ ) { 29967 29968 for ( let ix = 0; ix < gridX; ix ++ ) { 29969 29970 const a = numberOfVertices + ix + gridX1 * iy; 29971 const b = numberOfVertices + ix + gridX1 * ( iy + 1 ); 29972 const c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 ); 29973 const d = numberOfVertices + ( ix + 1 ) + gridX1 * iy; 29974 29975 // faces 29976 29977 indices.push( a, b, d ); 29978 indices.push( b, c, d ); 29979 29980 // increase counter 29981 29982 groupCount += 6; 29983 29984 } 29985 29986 } 29987 29988 // add a group to the geometry. this will ensure multi material support 29989 29990 scope.addGroup( groupStart, groupCount, materialIndex ); 29991 29992 // calculate new start value for groups 29993 29994 groupStart += groupCount; 29995 29996 // update total number of vertices 29997 29998 numberOfVertices += vertexCounter; 29999 30000 } 30001 30002 } 30003 30004 copy( source ) { 30005 30006 super.copy( source ); 30007 30008 this.parameters = Object.assign( {}, source.parameters ); 30009 30010 return this; 30011 30012 } 30013 30014 /** 30015 * Factory method for creating an instance of this class from the given 30016 * JSON object. 30017 * 30018 * @param {Object} data - A JSON object representing the serialized geometry. 30019 * @return {BoxGeometry} A new instance. 30020 */ 30021 static fromJSON( data ) { 30022 30023 return new BoxGeometry( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments ); 30024 30025 } 30026 30027} 30028 30029/** 30030 * A geometry class for representing a capsule. 30031 * 30032 * ```js 30033 * const geometry = new THREE.CapsuleGeometry( 1, 1, 4, 8, 1 ); 30034 * const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); 30035 * const capsule = new THREE.Mesh( geometry, material ); 30036 * scene.add( capsule ); 30037 * ``` 30038 * 30039 * @augments BufferGeometry 30040 * @demo scenes/geometry-browser.html#CapsuleGeometry 30041 */ 30042class CapsuleGeometry extends BufferGeometry { 30043 30044 /** 30045 * Constructs a new capsule geometry. 30046 * 30047 * @param {number} [radius=1] - Radius of the capsule. 30048 * @param {number} [height=1] - Height of the middle section. 30049 * @param {number} [capSegments=4] - Number of curve segments used to build each cap. 30050 * @param {number} [radialSegments=8] - Number of segmented faces around the circumference of the capsule. Must be an integer >= 3. 30051 * @param {number} [heightSegments=1] - Number of rows of faces along the height of the middle section. Must be an integer >= 1. 30052 */ 30053 constructor( radius = 1, height = 1, capSegments = 4, radialSegments = 8, heightSegments = 1 ) { 30054 30055 super(); 30056 30057 this.type = 'CapsuleGeometry'; 30058 30059 /** 30060 * Holds the constructor parameters that have been 30061 * used to generate the geometry. Any modification 30062 * after instantiation does not change the geometry. 30063 * 30064 * @type {Object} 30065 */ 30066 this.parameters = { 30067 radius: radius, 30068 height: height, 30069 capSegments: capSegments, 30070 radialSegments: radialSegments, 30071 heightSegments: heightSegments, 30072 }; 30073 30074 height = Math.max( 0, height ); 30075 capSegments = Math.max( 1, Math.floor( capSegments ) ); 30076 radialSegments = Math.max( 3, Math.floor( radialSegments ) ); 30077 heightSegments = Math.max( 1, Math.floor( heightSegments ) ); 30078 30079 // buffers 30080 30081 const indices = []; 30082 const vertices = []; 30083 const normals = []; 30084 const uvs = []; 30085 30086 // helper variables 30087 30088 const halfHeight = height / 2; 30089 const capArcLength = ( Math.PI / 2 ) * radius; 30090 const cylinderPartLength = height; 30091 const totalArcLength = 2 * capArcLength + cylinderPartLength; 30092 30093 const numVerticalSegments = capSegments * 2 + heightSegments; 30094 const verticesPerRow = radialSegments + 1; 30095 30096 const normal = new Vector3(); 30097 const vertex = new Vector3(); 30098 30099 // generate vertices, normals, and uvs 30100 30101 for ( let iy = 0; iy <= numVerticalSegments; iy ++ ) { 30102 30103 let currentArcLength = 0; 30104 let profileY = 0; 30105 let profileRadius = 0; 30106 let normalYComponent = 0; 30107 30108 if ( iy <= capSegments ) { 30109 30110 // bottom cap 30111 const segmentProgress = iy / capSegments; 30112 const angle = ( segmentProgress * Math.PI ) / 2; 30113 profileY = - halfHeight - radius * Math.cos( angle ); 30114 profileRadius = radius * Math.sin( angle ); 30115 normalYComponent = - radius * Math.cos( angle ); 30116 currentArcLength = segmentProgress * capArcLength; 30117 30118 } else if ( iy <= capSegments + heightSegments ) { 30119 30120 // middle section 30121 const segmentProgress = ( iy - capSegments ) / heightSegments; 30122 profileY = - halfHeight + segmentProgress * height; 30123 profileRadius = radius; 30124 normalYComponent = 0; 30125 currentArcLength = capArcLength + segmentProgress * cylinderPartLength; 30126 30127 } else { 30128 30129 // top cap 30130 const segmentProgress = 30131 ( iy - capSegments - heightSegments ) / capSegments; 30132 const angle = ( segmentProgress * Math.PI ) / 2; 30133 profileY = halfHeight + radius * Math.sin( angle ); 30134 profileRadius = radius * Math.cos( angle ); 30135 normalYComponent = radius * Math.sin( angle ); 30136 currentArcLength = 30137 capArcLength + cylinderPartLength + segmentProgress * capArcLength; 30138 30139 } 30140 30141 const v = Math.max( 0, Math.min( 1, currentArcLength / totalArcLength ) ); 30142 30143 30144 // special case for the poles 30145 30146 let uOffset = 0; 30147 30148 if ( iy === 0 ) { 30149 30150 uOffset = 0.5 / radialSegments; 30151 30152 } else if ( iy === numVerticalSegments ) { 30153 30154 uOffset = -0.5 / radialSegments; 30155 30156 } 30157 30158 for ( let ix = 0; ix <= radialSegments; ix ++ ) { 30159 30160 const u = ix / radialSegments; 30161 const theta = u * Math.PI * 2; 30162 30163 const sinTheta = Math.sin( theta ); 30164 const cosTheta = Math.cos( theta ); 30165 30166 // vertex 30167 30168 vertex.x = - profileRadius * cosTheta; 30169 vertex.y = profileY; 30170 vertex.z = profileRadius * sinTheta; 30171 vertices.push( vertex.x, vertex.y, vertex.z ); 30172 30173 // normal 30174 30175 normal.set( 30176 - profileRadius * cosTheta, 30177 normalYComponent, 30178 profileRadius * sinTheta 30179 ); 30180 normal.normalize(); 30181 normals.push( normal.x, normal.y, normal.z ); 30182 30183 // uv 30184 30185 uvs.push( u + uOffset, v ); 30186 30187 } 30188 30189 if ( iy > 0 ) { 30190 30191 const prevIndexRow = ( iy - 1 ) * verticesPerRow; 30192 for ( let ix = 0; ix < radialSegments; ix ++ ) { 30193 30194 const i1 = prevIndexRow + ix; 30195 const i2 = prevIndexRow + ix + 1; 30196 const i3 = iy * verticesPerRow + ix; 30197 const i4 = iy * verticesPerRow + ix + 1; 30198 30199 indices.push( i1, i2, i3 ); 30200 indices.push( i2, i4, i3 ); 30201 30202 } 30203 30204 } 30205 30206 } 30207 30208 // build geometry 30209 30210 this.setIndex( indices ); 30211 this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 30212 this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); 30213 this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); 30214 30215 } 30216 30217 copy( source ) { 30218 30219 super.copy( source ); 30220 30221 this.parameters = Object.assign( {}, source.parameters ); 30222 30223 return this; 30224 30225 } 30226 30227 /** 30228 * Factory method for creating an instance of this class from the given 30229 * JSON object. 30230 * 30231 * @param {Object} data - A JSON object representing the serialized geometry. 30232 * @return {CapsuleGeometry} A new instance. 30233 */ 30234 static fromJSON( data ) { 30235 30236 return new CapsuleGeometry( data.radius, data.height, data.capSegments, data.radialSegments, data.heightSegments ); 30237 30238 } 30239 30240} 30241 30242/** 30243 * A simple shape of Euclidean geometry. It is constructed from a 30244 * number of triangular segments that are oriented around a central point and 30245 * extend as far out as a given radius. It is built counter-clockwise from a 30246 * start angle and a given central angle. It can also be used to create 30247 * regular polygons, where the number of segments determines the number of 30248 * sides. 30249 * 30250 * ```js 30251 * const geometry = new THREE.CircleGeometry( 5, 32 ); 30252 * const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); 30253 * const circle = new THREE.Mesh( geometry, material ); 30254 * scene.add( circle ) 30255 * ``` 30256 * 30257 * @augments BufferGeometry 30258 * @demo scenes/geometry-browser.html#CircleGeometry 30259 */ 30260class CircleGeometry extends BufferGeometry { 30261 30262 /** 30263 * Constructs a new circle geometry. 30264 * 30265 * @param {number} [radius=1] - Radius of the circle. 30266 * @param {number} [segments=32] - Number of segments (triangles), minimum = `3`. 30267 * @param {number} [thetaStart=0] - Start angle for first segment in radians. 30268 * @param {number} [thetaLength=Math.PI*2] - The central angle, often called theta, 30269 * of the circular sector in radians. The default value results in a complete circle. 30270 */ 30271 constructor( radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2 ) { 30272 30273 super(); 30274 30275 this.type = 'CircleGeometry'; 30276 30277 /** 30278 * Holds the constructor parameters that have been 30279 * used to generate the geometry. Any modification 30280 * after instantiation does not change the geometry. 30281 * 30282 * @type {Object} 30283 */ 30284 this.parameters = { 30285 radius: radius, 30286 segments: segments, 30287 thetaStart: thetaStart, 30288 thetaLength: thetaLength 30289 }; 30290 30291 segments = Math.max( 3, segments ); 30292 30293 // buffers 30294 30295 const indices = []; 30296 const vertices = []; 30297 const normals = []; 30298 const uvs = []; 30299 30300 // helper variables 30301 30302 const vertex = new Vector3(); 30303 const uv = new Vector2(); 30304 30305 // center point 30306 30307 vertices.push( 0, 0, 0 ); 30308 normals.push( 0, 0, 1 ); 30309 uvs.push( 0.5, 0.5 ); 30310 30311 for ( let s = 0, i = 3; s <= segments; s ++, i += 3 ) { 30312 30313 const segment = thetaStart + s / segments * thetaLength; 30314 30315 // vertex 30316 30317 vertex.x = radius * Math.cos( segment ); 30318 vertex.y = radius * Math.sin( segment ); 30319 30320 vertices.push( vertex.x, vertex.y, vertex.z ); 30321 30322 // normal 30323 30324 normals.push( 0, 0, 1 ); 30325 30326 // uvs 30327 30328 uv.x = ( vertices[ i ] / radius + 1 ) / 2; 30329 uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2; 30330 30331 uvs.push( uv.x, uv.y ); 30332 30333 } 30334 30335 // indices 30336 30337 for ( let i = 1; i <= segments; i ++ ) { 30338 30339 indices.push( i, i + 1, 0 ); 30340 30341 } 30342 30343 // build geometry 30344 30345 this.setIndex( indices ); 30346 this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 30347 this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); 30348 this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); 30349 30350 } 30351 30352 copy( source ) { 30353 30354 super.copy( source ); 30355 30356 this.parameters = Object.assign( {}, source.parameters ); 30357 30358 return this; 30359 30360 } 30361 30362 /** 30363 * Factory method for creating an instance of this class from the given 30364 * JSON object. 30365 * 30366 * @param {Object} data - A JSON object representing the serialized geometry. 30367 * @return {CircleGeometry} A new instance. 30368 */ 30369 static fromJSON( data ) { 30370 30371 return new CircleGeometry( data.radius, data.segments, data.thetaStart, data.thetaLength ); 30372 30373 } 30374 30375} 30376 30377/** 30378 * A geometry class for representing a cylinder. 30379 * 30380 * ```js 30381 * const geometry = new THREE.CylinderGeometry( 5, 5, 20, 32 ); 30382 * const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); 30383 * const cylinder = new THREE.Mesh( geometry, material ); 30384 * scene.add( cylinder ); 30385 * ``` 30386 * 30387 * @augments BufferGeometry 30388 * @demo scenes/geometry-browser.html#CylinderGeometry 30389 */ 30390class CylinderGeometry extends BufferGeometry { 30391 30392 /** 30393 * Constructs a new cylinder geometry. 30394 * 30395 * @param {number} [radiusTop=1] - Radius of the cylinder at the top. 30396 * @param {number} [radiusBottom=1] - Radius of the cylinder at the bottom. 30397 * @param {number} [height=1] - Height of the cylinder. 30398 * @param {number} [radialSegments=32] - Number of segmented faces around the circumference of the cylinder. 30399 * @param {number} [heightSegments=1] - Number of rows of faces along the height of the cylinder. 30400 * @param {boolean} [openEnded=false] - Whether the base of the cylinder is open or capped. 30401 * @param {number} [thetaStart=0] - Start angle for first segment, in radians. 30402 * @param {number} [thetaLength=Math.PI*2] - The central angle, often called theta, of the circular sector, in radians. 30403 * The default value results in a complete cylinder. 30404 */ 30405 constructor( radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) { 30406 30407 super(); 30408 30409 this.type = 'CylinderGeometry'; 30410 30411 /** 30412 * Holds the constructor parameters that have been 30413 * used to generate the geometry. Any modification 30414 * after instantiation does not change the geometry. 30415 * 30416 * @type {Object} 30417 */ 30418 this.parameters = { 30419 radiusTop: radiusTop, 30420 radiusBottom: radiusBottom, 30421 height: height, 30422 radialSegments: radialSegments, 30423 heightSegments: heightSegments, 30424 openEnded: openEnded, 30425 thetaStart: thetaStart, 30426 thetaLength: thetaLength 30427 }; 30428 30429 const scope = this; 30430 30431 radialSegments = Math.floor( radialSegments ); 30432 heightSegments = Math.floor( heightSegments ); 30433 30434 // buffers 30435 30436 const indices = []; 30437 const vertices = []; 30438 const normals = []; 30439 const uvs = []; 30440 30441 // helper variables 30442 30443 let index = 0; 30444 const indexArray = []; 30445 const halfHeight = height / 2; 30446 let groupStart = 0; 30447 30448 // generate geometry 30449 30450 generateTorso(); 30451 30452 if ( openEnded === false ) { 30453 30454 if ( radiusTop > 0 ) generateCap( true ); 30455 if ( radiusBottom > 0 ) generateCap( false ); 30456 30457 } 30458 30459 // build geometry 30460 30461 this.setIndex( indices ); 30462 this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 30463 this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); 30464 this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); 30465 30466 function generateTorso() { 30467 30468 const normal = new Vector3(); 30469 const vertex = new Vector3(); 30470 30471 let groupCount = 0; 30472 30473 // this will be used to calculate the normal 30474 const slope = ( radiusBottom - radiusTop ) / height; 30475 30476 // generate vertices, normals and uvs 30477 30478 for ( let y = 0; y <= heightSegments; y ++ ) { 30479 30480 const indexRow = []; 30481 30482 const v = y / heightSegments; 30483 30484 // calculate the radius of the current row 30485 30486 const radius = v * ( radiusBottom - radiusTop ) + radiusTop; 30487 30488 for ( let x = 0; x <= radialSegments; x ++ ) { 30489 30490 const u = x / radialSegments; 30491 30492 const theta = u * thetaLength + thetaStart; 30493 30494 const sinTheta = Math.sin( theta ); 30495 const cosTheta = Math.cos( theta ); 30496 30497 // vertex 30498 30499 vertex.x = radius * sinTheta; 30500 vertex.y = - v * height + halfHeight; 30501 vertex.z = radius * cosTheta; 30502 vertices.push( vertex.x, vertex.y, vertex.z ); 30503 30504 // normal 30505 30506 normal.set( sinTheta, slope, cosTheta ).normalize(); 30507 normals.push( normal.x, normal.y, normal.z ); 30508 30509 // uv 30510 30511 uvs.push( u, 1 - v ); 30512 30513 // save index of vertex in respective row 30514 30515 indexRow.push( index ++ ); 30516 30517 } 30518 30519 // now save vertices of the row in our index array 30520 30521 indexArray.push( indexRow ); 30522 30523 } 30524 30525 // generate indices 30526 30527 for ( let x = 0; x < radialSegments; x ++ ) { 30528 30529 for ( let y = 0; y < heightSegments; y ++ ) { 30530 30531 // we use the index array to access the correct indices 30532 30533 const a = indexArray[ y ][ x ]; 30534 const b = indexArray[ y + 1 ][ x ]; 30535 const c = indexArray[ y + 1 ][ x + 1 ]; 30536 const d = indexArray[ y ][ x + 1 ]; 30537 30538 // faces 30539 30540 if ( radiusTop > 0 || y !== 0 ) { 30541 30542 indices.push( a, b, d ); 30543 groupCount += 3; 30544 30545 } 30546 30547 if ( radiusBottom > 0 || y !== heightSegments - 1 ) { 30548 30549 indices.push( b, c, d ); 30550 groupCount += 3; 30551 30552 } 30553 30554 } 30555 30556 } 30557 30558 // add a group to the geometry. this will ensure multi material support 30559 30560 scope.addGroup( groupStart, groupCount, 0 ); 30561 30562 // calculate new start value for groups 30563 30564 groupStart += groupCount; 30565 30566 } 30567 30568 function generateCap( top ) { 30569 30570 // save the index of the first center vertex 30571 const centerIndexStart = index; 30572 30573 const uv = new Vector2(); 30574 const vertex = new Vector3(); 30575 30576 let groupCount = 0; 30577 30578 const radius = ( top === true ) ? radiusTop : radiusBottom; 30579 const sign = ( top === true ) ? 1 : -1; 30580 30581 // first we generate the center vertex data of the cap. 30582 // because the geometry needs one set of uvs per face, 30583 // we must generate a center vertex per face/segment 30584 30585 for ( let x = 1; x <= radialSegments; x ++ ) { 30586 30587 // vertex 30588 30589 vertices.push( 0, halfHeight * sign, 0 ); 30590 30591 // normal 30592 30593 normals.push( 0, sign, 0 ); 30594 30595 // uv 30596 30597 uvs.push( 0.5, 0.5 ); 30598 30599 // increase index 30600 30601 index ++; 30602 30603 } 30604 30605 // save the index of the last center vertex 30606 const centerIndexEnd = index; 30607 30608 // now we generate the surrounding vertices, normals and uvs 30609 30610 for ( let x = 0; x <= radialSegments; x ++ ) { 30611 30612 const u = x / radialSegments; 30613 const theta = u * thetaLength + thetaStart; 30614 30615 const cosTheta = Math.cos( theta ); 30616 const sinTheta = Math.sin( theta ); 30617 30618 // vertex 30619 30620 vertex.x = radius * sinTheta; 30621 vertex.y = halfHeight * sign; 30622 vertex.z = radius * cosTheta; 30623 vertices.push( vertex.x, vertex.y, vertex.z ); 30624 30625 // normal 30626 30627 normals.push( 0, sign, 0 ); 30628 30629 // uv 30630 30631 uv.x = ( cosTheta * 0.5 ) + 0.5; 30632 uv.y = ( sinTheta * 0.5 * sign ) + 0.5; 30633 uvs.push( uv.x, uv.y ); 30634 30635 // increase index 30636 30637 index ++; 30638 30639 } 30640 30641 // generate indices 30642 30643 for ( let x = 0; x < radialSegments; x ++ ) { 30644 30645 const c = centerIndexStart + x; 30646 const i = centerIndexEnd + x; 30647 30648 if ( top === true ) { 30649 30650 // face top 30651 30652 indices.push( i, i + 1, c ); 30653 30654 } else { 30655 30656 // face bottom 30657 30658 indices.push( i + 1, i, c ); 30659 30660 } 30661 30662 groupCount += 3; 30663 30664 } 30665 30666 // add a group to the geometry. this will ensure multi material support 30667 30668 scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 ); 30669 30670 // calculate new start value for groups 30671 30672 groupStart += groupCount; 30673 30674 } 30675 30676 } 30677 30678 copy( source ) { 30679 30680 super.copy( source ); 30681 30682 this.parameters = Object.assign( {}, source.parameters ); 30683 30684 return this; 30685 30686 } 30687 30688 /** 30689 * Factory method for creating an instance of this class from the given 30690 * JSON object. 30691 * 30692 * @param {Object} data - A JSON object representing the serialized geometry. 30693 * @return {CylinderGeometry} A new instance. 30694 */ 30695 static fromJSON( data ) { 30696 30697 return new CylinderGeometry( data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength ); 30698 30699 } 30700 30701} 30702 30703/** 30704 * A geometry class for representing a cone. 30705 * 30706 * ```js 30707 * const geometry = new THREE.ConeGeometry( 5, 20, 32 ); 30708 * const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); 30709 * const cone = new THREE.Mesh(geometry, material ); 30710 * scene.add( cone ); 30711 * ``` 30712 * 30713 * @augments CylinderGeometry 30714 * @demo scenes/geometry-browser.html#ConeGeometry 30715 */ 30716class ConeGeometry extends CylinderGeometry { 30717 30718 /** 30719 * Constructs a new cone geometry. 30720 * 30721 * @param {number} [radius=1] - Radius of the cone base. 30722 * @param {number} [height=1] - Height of the cone. 30723 * @param {number} [radialSegments=32] - Number of segmented faces around the circumference of the cone. 30724 * @param {number} [heightSegments=1] - Number of rows of faces along the height of the cone. 30725 * @param {boolean} [openEnded=false] - Whether the base of the cone is open or capped. 30726 * @param {number} [thetaStart=0] - Start angle for first segment, in radians. 30727 * @param {number} [thetaLength=Math.PI*2] - The central angle, often called theta, of the circular sector, in radians. 30728 * The default value results in a complete cone. 30729 */ 30730 constructor( radius = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) { 30731 30732 super( 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); 30733 30734 this.type = 'ConeGeometry'; 30735 30736 /** 30737 * Holds the constructor parameters that have been 30738 * used to generate the geometry. Any modification 30739 * after instantiation does not change the geometry. 30740 * 30741 * @type {Object} 30742 */ 30743 this.parameters = { 30744 radius: radius, 30745 height: height, 30746 radialSegments: radialSegments, 30747 heightSegments: heightSegments, 30748 openEnded: openEnded, 30749 thetaStart: thetaStart, 30750 thetaLength: thetaLength 30751 }; 30752 30753 } 30754 30755 /** 30756 * Factory method for creating an instance of this class from the given 30757 * JSON object. 30758 * 30759 * @param {Object} data - A JSON object representing the serialized geometry. 30760 * @return {ConeGeometry} A new instance. 30761 */ 30762 static fromJSON( data ) { 30763 30764 return new ConeGeometry( data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength ); 30765 30766 } 30767 30768} 30769 30770/** 30771 * A polyhedron is a solid in three dimensions with flat faces. This class 30772 * will take an array of vertices, project them onto a sphere, and then 30773 * divide them up to the desired level of detail. 30774 * 30775 * @augments BufferGeometry 30776 */ 30777class PolyhedronGeometry extends BufferGeometry { 30778 30779 /** 30780 * Constructs a new polyhedron geometry. 30781 * 30782 * @param {Array<number>} [vertices] - A flat array of vertices describing the base shape. 30783 * @param {Array<number>} [indices] - A flat array of indices describing the base shape. 30784 * @param {number} [radius=1] - The radius of the shape. 30785 * @param {number} [detail=0] - How many levels to subdivide the geometry. The more detail, the smoother the shape. 30786 */ 30787 constructor( vertices = [], indices = [], radius = 1, detail = 0 ) { 30788 30789 super(); 30790 30791 this.type = 'PolyhedronGeometry'; 30792 30793 /** 30794 * Holds the constructor parameters that have been 30795 * used to generate the geometry. Any modification 30796 * after instantiation does not change the geometry. 30797 * 30798 * @type {Object} 30799 */ 30800 this.parameters = { 30801 vertices: vertices, 30802 indices: indices, 30803 radius: radius, 30804 detail: detail 30805 }; 30806 30807 // default buffer data 30808 30809 const vertexBuffer = []; 30810 const uvBuffer = []; 30811 30812 // the subdivision creates the vertex buffer data 30813 30814 subdivide( detail ); 30815 30816 // all vertices should lie on a conceptual sphere with a given radius 30817 30818 applyRadius( radius ); 30819 30820 // finally, create the uv data 30821 30822 generateUVs(); 30823 30824 // build non-indexed geometry 30825 30826 this.setAttribute( 'position', new Float32BufferAttribute( vertexBuffer, 3 ) ); 30827 this.setAttribute( 'normal', new Float32BufferAttribute( vertexBuffer.slice(), 3 ) ); 30828 this.setAttribute( 'uv', new Float32BufferAttribute( uvBuffer, 2 ) ); 30829 30830 if ( detail === 0 ) { 30831 30832 this.computeVertexNormals(); // flat normals 30833 30834 } else { 30835 30836 this.normalizeNormals(); // smooth normals 30837 30838 } 30839 30840 // helper functions 30841 30842 function subdivide( detail ) { 30843 30844 const a = new Vector3(); 30845 const b = new Vector3(); 30846 const c = new Vector3(); 30847 30848 // iterate over all faces and apply a subdivision with the given detail value 30849 30850 for ( let i = 0; i < indices.length; i += 3 ) { 30851 30852 // get the vertices of the face 30853 30854 getVertexByIndex( indices[ i + 0 ], a ); 30855 getVertexByIndex( indices[ i + 1 ], b ); 30856 getVertexByIndex( indices[ i + 2 ], c ); 30857 30858 // perform subdivision 30859 30860 subdivideFace( a, b, c, detail ); 30861 30862 } 30863 30864 } 30865 30866 function subdivideFace( a, b, c, detail ) { 30867 30868 const cols = detail + 1; 30869 30870 // we use this multidimensional array as a data structure for creating the subdivision 30871 30872 const v = []; 30873 30874 // construct all of the vertices for this subdivision 30875 30876 for ( let i = 0; i <= cols; i ++ ) { 30877 30878 v[ i ] = []; 30879 30880 const aj = a.clone().lerp( c, i / cols ); 30881 const bj = b.clone().lerp( c, i / cols ); 30882 30883 const rows = cols - i; 30884 30885 for ( let j = 0; j <= rows; j ++ ) { 30886 30887 if ( j === 0 && i === cols ) { 30888 30889 v[ i ][ j ] = aj; 30890 30891 } else { 30892 30893 v[ i ][ j ] = aj.clone().lerp( bj, j / rows ); 30894 30895 } 30896 30897 } 30898 30899 } 30900 30901 // construct all of the faces 30902 30903 for ( let i = 0; i < cols; i ++ ) { 30904 30905 for ( let j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { 30906 30907 const k = Math.floor( j / 2 ); 30908 30909 if ( j % 2 === 0 ) { 30910 30911 pushVertex( v[ i ][ k + 1 ] ); 30912 pushVertex( v[ i + 1 ][ k ] ); 30913 pushVertex( v[ i ][ k ] ); 30914 30915 } else { 30916 30917 pushVertex( v[ i ][ k + 1 ] ); 30918 pushVertex( v[ i + 1 ][ k + 1 ] ); 30919 pushVertex( v[ i + 1 ][ k ] ); 30920 30921 } 30922 30923 } 30924 30925 } 30926 30927 } 30928 30929 function applyRadius( radius ) { 30930 30931 const vertex = new Vector3(); 30932 30933 // iterate over the entire buffer and apply the radius to each vertex 30934 30935 for ( let i = 0; i < vertexBuffer.length; i += 3 ) { 30936 30937 vertex.x = vertexBuffer[ i + 0 ]; 30938 vertex.y = vertexBuffer[ i + 1 ]; 30939 vertex.z = vertexBuffer[ i + 2 ]; 30940 30941 vertex.normalize().multiplyScalar( radius ); 30942 30943 vertexBuffer[ i + 0 ] = vertex.x; 30944 vertexBuffer[ i + 1 ] = vertex.y; 30945 vertexBuffer[ i + 2 ] = vertex.z; 30946 30947 } 30948 30949 } 30950 30951 function generateUVs() { 30952 30953 const vertex = new Vector3(); 30954 30955 for ( let i = 0; i < vertexBuffer.length; i += 3 ) { 30956 30957 vertex.x = vertexBuffer[ i + 0 ]; 30958 vertex.y = vertexBuffer[ i + 1 ]; 30959 vertex.z = vertexBuffer[ i + 2 ]; 30960 30961 const u = azimuth( vertex ) / 2 / Math.PI + 0.5; 30962 const v = inclination( vertex ) / Math.PI + 0.5; 30963 uvBuffer.push( u, 1 - v ); 30964 30965 } 30966 30967 correctUVs(); 30968 30969 correctSeam(); 30970 30971 } 30972 30973 function correctSeam() { 30974 30975 // handle case when face straddles the seam, see #3269 30976 30977 for ( let i = 0; i < uvBuffer.length; i += 6 ) { 30978 30979 // uv data of a single face 30980 30981 const x0 = uvBuffer[ i + 0 ]; 30982 const x1 = uvBuffer[ i + 2 ]; 30983 const x2 = uvBuffer[ i + 4 ]; 30984 30985 const max = Math.max( x0, x1, x2 ); 30986 const min = Math.min( x0, x1, x2 ); 30987 30988 // 0.9 is somewhat arbitrary 30989 30990 if ( max > 0.9 && min < 0.1 ) { 30991 30992 if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1; 30993 if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1; 30994 if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1; 30995 30996 } 30997 30998 } 30999 31000 } 31001 31002 function pushVertex( vertex ) { 31003 31004 vertexBuffer.push( vertex.x, vertex.y, vertex.z ); 31005 31006 } 31007 31008 function getVertexByIndex( index, vertex ) { 31009 31010 const stride = index * 3; 31011 31012 vertex.x = vertices[ stride + 0 ]; 31013 vertex.y = vertices[ stride + 1 ]; 31014 vertex.z = vertices[ stride + 2 ]; 31015 31016 } 31017 31018 function correctUVs() { 31019 31020 const a = new Vector3(); 31021 const b = new Vector3(); 31022 const c = new Vector3(); 31023 31024 const centroid = new Vector3(); 31025 31026 const uvA = new Vector2(); 31027 const uvB = new Vector2(); 31028 const uvC = new Vector2(); 31029 31030 for ( let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) { 31031 31032 a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] ); 31033 b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] ); 31034 c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] ); 31035 31036 uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] ); 31037 uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] ); 31038 uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] ); 31039 31040 centroid.copy( a ).add( b ).add( c ).divideScalar( 3 ); 31041 31042 const azi = azimuth( centroid ); 31043 31044 correctUV( uvA, j + 0, a, azi ); 31045 correctUV( uvB, j + 2, b, azi ); 31046 correctUV( uvC, j + 4, c, azi ); 31047 31048 } 31049 31050 } 31051 31052 function correctUV( uv, stride, vector, azimuth ) { 31053 31054 if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) { 31055 31056 uvBuffer[ stride ] = uv.x - 1; 31057 31058 } 31059 31060 if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) { 31061 31062 uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5; 31063 31064 } 31065 31066 } 31067 31068 // Angle around the Y axis, counter-clockwise when looking from above. 31069 31070 function azimuth( vector ) { 31071 31072 return Math.atan2( vector.z, - vector.x ); 31073 31074 } 31075 31076 31077 // Angle above the XZ plane. 31078 31079 function inclination( vector ) { 31080 31081 return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); 31082 31083 } 31084 31085 } 31086 31087 copy( source ) { 31088 31089 super.copy( source ); 31090 31091 this.parameters = Object.assign( {}, source.parameters ); 31092 31093 return this; 31094 31095 } 31096 31097 /** 31098 * Factory method for creating an instance of this class from the given 31099 * JSON object. 31100 * 31101 * @param {Object} data - A JSON object representing the serialized geometry. 31102 * @return {PolyhedronGeometry} A new instance. 31103 */ 31104 static fromJSON( data ) { 31105 31106 return new PolyhedronGeometry( data.vertices, data.indices, data.radius, data.detail ); 31107 31108 } 31109 31110} 31111 31112/** 31113 * A geometry class for representing a dodecahedron. 31114 * 31115 * ```js 31116 * const geometry = new THREE.DodecahedronGeometry(); 31117 * const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); 31118 * const dodecahedron = new THREE.Mesh( geometry, material ); 31119 * scene.add( dodecahedron ); 31120 * ``` 31121 * 31122 * @augments PolyhedronGeometry 31123 * @demo scenes/geometry-browser.html#DodecahedronGeometry 31124 */ 31125class DodecahedronGeometry extends PolyhedronGeometry { 31126 31127 /** 31128 * Constructs a new dodecahedron geometry. 31129 * 31130 * @param {number} [radius=1] - Radius of the dodecahedron. 31131 * @param {number} [detail=0] - Setting this to a value greater than `0` adds vertices making it no longer a dodecahedron. 31132 */ 31133 constructor( radius = 1, detail = 0 ) { 31134 31135 const t = ( 1 + Math.sqrt( 5 ) ) / 2; 31136 const r = 1 / t; 31137 31138 const vertices = [ 31139 31140 // (±1, ±1, ±1) 31141 -1, -1, -1, -1, -1, 1, 31142 -1, 1, -1, -1, 1, 1, 31143 1, -1, -1, 1, -1, 1, 31144 1, 1, -1, 1, 1, 1, 31145 31146 // (0, ±1/Ï, ±Ï) 31147 0, - r, - t, 0, - r, t, 31148 0, r, - t, 0, r, t, 31149 31150 // (±1/Ï, ±Ï, 0) 31151 - r, - t, 0, - r, t, 0, 31152 r, - t, 0, r, t, 0, 31153 31154 // (±Ï, 0, ±1/Ï) 31155 - t, 0, - r, t, 0, - r, 31156 - t, 0, r, t, 0, r 31157 ]; 31158 31159 const indices = [ 31160 3, 11, 7, 3, 7, 15, 3, 15, 13, 31161 7, 19, 17, 7, 17, 6, 7, 6, 15, 31162 17, 4, 8, 17, 8, 10, 17, 10, 6, 31163 8, 0, 16, 8, 16, 2, 8, 2, 10, 31164 0, 12, 1, 0, 1, 18, 0, 18, 16, 31165 6, 10, 2, 6, 2, 13, 6, 13, 15, 31166 2, 16, 18, 2, 18, 3, 2, 3, 13, 31167 18, 1, 9, 18, 9, 11, 18, 11, 3, 31168 4, 14, 12, 4, 12, 0, 4, 0, 8, 31169 11, 9, 5, 11, 5, 19, 11, 19, 7, 31170 19, 5, 14, 19, 14, 4, 19, 4, 17, 31171 1, 12, 14, 1, 14, 5, 1, 5, 9 31172 ]; 31173 31174 super( vertices, indices, radius, detail ); 31175 31176 this.type = 'DodecahedronGeometry'; 31177 31178 /** 31179 * Holds the constructor parameters that have been 31180 * used to generate the geometry. Any modification 31181 * after instantiation does not change the geometry. 31182 * 31183 * @type {Object} 31184 */ 31185 this.parameters = { 31186 radius: radius, 31187 detail: detail 31188 }; 31189 31190 } 31191 31192 /** 31193 * Factory method for creating an instance of this class from the given 31194 * JSON object. 31195 * 31196 * @param {Object} data - A JSON object representing the serialized geometry. 31197 * @return {DodecahedronGeometry} A new instance. 31198 */ 31199 static fromJSON( data ) { 31200 31201 return new DodecahedronGeometry( data.radius, data.detail ); 31202 31203 } 31204 31205} 31206 31207const _v0 = /*@__PURE__*/ new Vector3(); 31208const _v1$1 = /*@__PURE__*/ new Vector3(); 31209const _normal = /*@__PURE__*/ new Vector3(); 31210const _triangle = /*@__PURE__*/ new Triangle(); 31211 31212/** 31213 * Can be used as a helper object to view the edges of a geometry. 31214 * 31215 * ```js 31216 * const geometry = new THREE.BoxGeometry(); 31217 * const edges = new THREE.EdgesGeometry( geometry ); 31218 * const line = new THREE.LineSegments( edges ); 31219 * scene.add( line ); 31220 * ``` 31221 * 31222 * Note: It is not yet possible to serialize/deserialize instances of this class. 31223 * 31224 * @augments BufferGeometry 31225 */ 31226class EdgesGeometry extends BufferGeometry { 31227 31228 /** 31229 * Constructs a new edges geometry. 31230 * 31231 * @param {?BufferGeometry} [geometry=null] - The geometry. 31232 * @param {number} [thresholdAngle=1] - An edge is only rendered if the angle (in degrees) 31233 * between the face normals of the adjoining faces exceeds this value. 31234 */ 31235 constructor( geometry = null, thresholdAngle = 1 ) { 31236 31237 super(); 31238 31239 this.type = 'EdgesGeometry'; 31240 31241 /** 31242 * Holds the constructor parameters that have been 31243 * used to generate the geometry. Any modification 31244 * after instantiation does not change the geometry. 31245 * 31246 * @type {Object} 31247 */ 31248 this.parameters = { 31249 geometry: geometry, 31250 thresholdAngle: thresholdAngle 31251 }; 31252 31253 if ( geometry !== null ) { 31254 31255 const precisionPoints = 4; 31256 const precision = Math.pow( 10, precisionPoints ); 31257 const thresholdDot = Math.cos( DEG2RAD * thresholdAngle ); 31258 31259 const indexAttr = geometry.getIndex(); 31260 const positionAttr = geometry.getAttribute( 'position' ); 31261 const indexCount = indexAttr ? indexAttr.count : positionAttr.count; 31262 31263 const indexArr = [ 0, 0, 0 ]; 31264 const vertKeys = [ 'a', 'b', 'c' ]; 31265 const hashes = new Array( 3 ); 31266 31267 const edgeData = {}; 31268 const vertices = []; 31269 for ( let i = 0; i < indexCount; i += 3 ) { 31270 31271 if ( indexAttr ) { 31272 31273 indexArr[ 0 ] = indexAttr.getX( i ); 31274 indexArr[ 1 ] = indexAttr.getX( i + 1 ); 31275 indexArr[ 2 ] = indexAttr.getX( i + 2 ); 31276 31277 } else { 31278 31279 indexArr[ 0 ] = i; 31280 indexArr[ 1 ] = i + 1; 31281 indexArr[ 2 ] = i + 2; 31282 31283 } 31284 31285 const { a, b, c } = _triangle; 31286 a.fromBufferAttribute( positionAttr, indexArr[ 0 ] ); 31287 b.fromBufferAttribute( positionAttr, indexArr[ 1 ] ); 31288 c.fromBufferAttribute( positionAttr, indexArr[ 2 ] ); 31289 _triangle.getNormal( _normal ); 31290 31291 // create hashes for the edge from the vertices 31292 hashes[ 0 ] = `${ Math.round( a.x * precision ) },${ Math.round( a.y * precision ) },${ Math.round( a.z * precision ) }`; 31293 hashes[ 1 ] = `${ Math.round( b.x * precision ) },${ Math.round( b.y * precision ) },${ Math.round( b.z * precision ) }`; 31294 hashes[ 2 ] = `${ Math.round( c.x * precision ) },${ Math.round( c.y * precision ) },${ Math.round( c.z * precision ) }`; 31295 31296 // skip degenerate triangles 31297 if ( hashes[ 0 ] === hashes[ 1 ] || hashes[ 1 ] === hashes[ 2 ] || hashes[ 2 ] === hashes[ 0 ] ) { 31298 31299 continue; 31300 31301 } 31302 31303 // iterate over every edge 31304 for ( let j = 0; j < 3; j ++ ) { 31305 31306 // get the first and next vertex making up the edge 31307 const jNext = ( j + 1 ) % 3; 31308 const vecHash0 = hashes[ j ]; 31309 const vecHash1 = hashes[ jNext ]; 31310 const v0 = _triangle[ vertKeys[ j ] ]; 31311 const v1 = _triangle[ vertKeys[ jNext ] ]; 31312 31313 const hash = `${ vecHash0 }_${ vecHash1 }`; 31314 const reverseHash = `${ vecHash1 }_${ vecHash0 }`; 31315 31316 if ( reverseHash in edgeData && edgeData[ reverseHash ] ) { 31317 31318 // if we found a sibling edge add it into the vertex array if 31319 // it meets the angle threshold and delete the edge from the map. 31320 if ( _normal.dot( edgeData[ reverseHash ].normal ) <= thresholdDot ) { 31321 31322 vertices.push( v0.x, v0.y, v0.z ); 31323 vertices.push( v1.x, v1.y, v1.z ); 31324 31325 } 31326 31327 edgeData[ reverseHash ] = null; 31328 31329 } else if ( ! ( hash in edgeData ) ) { 31330 31331 // if we've already got an edge here then skip adding a new one 31332 edgeData[ hash ] = { 31333 31334 index0: indexArr[ j ], 31335 index1: indexArr[ jNext ], 31336 normal: _normal.clone(), 31337 31338 }; 31339 31340 } 31341 31342 } 31343 31344 } 31345 31346 // iterate over all remaining, unmatched edges and add them to the vertex array 31347 for ( const key in edgeData ) { 31348 31349 if ( edgeData[ key ] ) { 31350 31351 const { index0, index1 } = edgeData[ key ]; 31352 _v0.fromBufferAttribute( positionAttr, index0 ); 31353 _v1$1.fromBufferAttribute( positionAttr, index1 ); 31354 31355 vertices.push( _v0.x, _v0.y, _v0.z ); 31356 vertices.push( _v1$1.x, _v1$1.y, _v1$1.z ); 31357 31358 } 31359 31360 } 31361 31362 this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 31363 31364 } 31365 31366 } 31367 31368 copy( source ) { 31369 31370 super.copy( source ); 31371 31372 this.parameters = Object.assign( {}, source.parameters ); 31373 31374 return this; 31375 31376 } 31377 31378} 31379 31380/** 31381 * An abstract base class for creating an analytic curve object that contains methods 31382 * for interpolation. 31383 * 31384 * @abstract 31385 */ 31386class Curve { 31387 31388 /** 31389 * Constructs a new curve. 31390 */ 31391 constructor() { 31392 31393 /** 31394 * The type property is used for detecting the object type 31395 * in context of serialization/deserialization. 31396 * 31397 * @type {string} 31398 * @readonly 31399 */ 31400 this.type = 'Curve'; 31401 31402 /** 31403 * This value determines the amount of divisions when calculating the 31404 * cumulative segment lengths of a curve via {@link Curve#getLengths}. To ensure 31405 * precision when using methods like {@link Curve#getSpacedPoints}, it is 31406 * recommended to increase the value of this property if the curve is very large. 31407 * 31408 * @type {number} 31409 * @default 200 31410 */ 31411 this.arcLengthDivisions = 200; 31412 31413 /** 31414 * Must be set to `true` if the curve parameters have changed. 31415 * 31416 * @type {boolean} 31417 * @default false 31418 */ 31419 this.needsUpdate = false; 31420 31421 /** 31422 * An internal cache that holds precomputed curve length values. 31423 * 31424 * @private 31425 * @type {?Array<number>} 31426 * @default null 31427 */ 31428 this.cacheArcLengths = null; 31429 31430 } 31431 31432 /** 31433 * This method returns a vector in 2D or 3D space (depending on the curve definition) 31434 * for the given interpolation factor. 31435 * 31436 * @abstract 31437 * @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`. 31438 * @param {(Vector2|Vector3)} [optionalTarget] - The optional target vector the result is written to. 31439 * @return {(Vector2|Vector3)} The position on the curve. It can be a 2D or 3D vector depending on the curve definition. 31440 */ 31441 getPoint( /* t, optionalTarget */ ) { 31442 31443 warn( 'Curve: .getPoint() not implemented.' ); 31444 31445 } 31446 31447 /** 31448 * This method returns a vector in 2D or 3D space (depending on the curve definition) 31449 * for the given interpolation factor. Unlike {@link Curve#getPoint}, this method honors the length 31450 * of the curve which equidistant samples. 31451 * 31452 * @param {number} u - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`. 31453 * @param {(Vector2|Vector3)} [optionalTarget] - The optional target vector the result is written to. 31454 * @return {(Vector2|Vector3)} The position on the curve. It can be a 2D or 3D vector depending on the curve definition. 31455 */ 31456 getPointAt( u, optionalTarget ) { 31457 31458 const t = this.getUtoTmapping( u ); 31459 return this.getPoint( t, optionalTarget ); 31460 31461 } 31462 31463 /** 31464 * This method samples the curve via {@link Curve#getPoint} and returns an array of points representing 31465 * the curve shape. 31466 * 31467 * @param {number} [divisions=5] - The number of divisions. 31468 * @return {Array<(Vector2|Vector3)>} An array holding the sampled curve values. The number of points is `divisions + 1`. 31469 */ 31470 getPoints( divisions = 5 ) { 31471 31472 const points = []; 31473 31474 for ( let d = 0; d <= divisions; d ++ ) { 31475 31476 points.push( this.getPoint( d / divisions ) ); 31477 31478 } 31479 31480 return points; 31481 31482 } 31483 31484 // Get sequence of points using getPointAt( u ) 31485 31486 /** 31487 * This method samples the curve via {@link Curve#getPointAt} and returns an array of points representing 31488 * the curve shape. Unlike {@link Curve#getPoints}, this method returns equi-spaced points across the entire 31489 * curve. 31490 * 31491 * @param {number} [divisions=5] - The number of divisions. 31492 * @return {Array<(Vector2|Vector3)>} An array holding the sampled curve values. The number of points is `divisions + 1`. 31493 */ 31494 getSpacedPoints( divisions = 5 ) { 31495 31496 const points = []; 31497 31498 for ( let d = 0; d <= divisions; d ++ ) { 31499 31500 points.push( this.getPointAt( d / divisions ) ); 31501 31502 } 31503 31504 return points; 31505 31506 } 31507 31508 /** 31509 * Returns the total arc length of the curve. 31510 * 31511 * @return {number} The length of the curve. 31512 */ 31513 getLength() { 31514 31515 const lengths = this.getLengths(); 31516 return lengths[ lengths.length - 1 ]; 31517 31518 } 31519 31520 /** 31521 * Returns an array of cumulative segment lengths of the curve. 31522 * 31523 * @param {number} [divisions=this.arcLengthDivisions] - The number of divisions. 31524 * @return {Array<number>} An array holding the cumulative segment lengths. 31525 */ 31526 getLengths( divisions = this.arcLengthDivisions ) { 31527 31528 if ( this.cacheArcLengths && 31529 ( this.cacheArcLengths.length === divisions + 1 ) && 31530 ! this.needsUpdate ) { 31531 31532 return this.cacheArcLengths; 31533 31534 } 31535 31536 this.needsUpdate = false; 31537 31538 const cache = []; 31539 let current, last = this.getPoint( 0 ); 31540 let sum = 0; 31541 31542 cache.push( 0 ); 31543 31544 for ( let p = 1; p <= divisions; p ++ ) { 31545 31546 current = this.getPoint( p / divisions ); 31547 sum += current.distanceTo( last ); 31548 cache.push( sum ); 31549 last = current; 31550 31551 } 31552 31553 this.cacheArcLengths = cache; 31554 31555 return cache; // { sums: cache, sum: sum }; Sum is in the last element. 31556 31557 } 31558 31559 /** 31560 * Update the cumulative segment distance cache. The method must be called 31561 * every time curve parameters are changed. If an updated curve is part of a 31562 * composed curve like {@link CurvePath}, this method must be called on the 31563 * composed curve, too. 31564 */ 31565 updateArcLengths() { 31566 31567 this.needsUpdate = true; 31568 this.getLengths(); 31569 31570 } 31571 31572 /** 31573 * Given an interpolation factor in the range `[0,1]`, this method returns an updated 31574 * interpolation factor in the same range that can be ued to sample equidistant points 31575 * from a curve. 31576 * 31577 * @param {number} u - The interpolation factor. 31578 * @param {?number} distance - An optional distance on the curve. 31579 * @return {number} The updated interpolation factor. 31580 */ 31581 getUtoTmapping( u, distance = null ) { 31582 31583 const arcLengths = this.getLengths(); 31584 31585 let i = 0; 31586 const il = arcLengths.length; 31587 31588 let targetArcLength; // The targeted u distance value to get 31589 31590 if ( distance ) { 31591 31592 targetArcLength = distance; 31593 31594 } else { 31595 31596 targetArcLength = u * arcLengths[ il - 1 ]; 31597 31598 } 31599 31600 // binary search for the index with largest value smaller than target u distance 31601 31602 let low = 0, high = il - 1, comparison; 31603 31604 while ( low <= high ) { 31605 31606 i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats 31607 31608 comparison = arcLengths[ i ] - targetArcLength; 31609 31610 if ( comparison < 0 ) { 31611 31612 low = i + 1; 31613 31614 } else if ( comparison > 0 ) { 31615 31616 high = i - 1; 31617 31618 } else { 31619 31620 high = i; 31621 break; 31622 31623 // DONE 31624 31625 } 31626 31627 } 31628 31629 i = high; 31630 31631 if ( arcLengths[ i ] === targetArcLength ) { 31632 31633 return i / ( il - 1 ); 31634 31635 } 31636 31637 // we could get finer grain at lengths, or use simple interpolation between two points 31638 31639 const lengthBefore = arcLengths[ i ]; 31640 const lengthAfter = arcLengths[ i + 1 ]; 31641 31642 const segmentLength = lengthAfter - lengthBefore; 31643 31644 // determine where we are between the 'before' and 'after' points 31645 31646 const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; 31647 31648 // add that fractional amount to t 31649 31650 const t = ( i + segmentFraction ) / ( il - 1 ); 31651 31652 return t; 31653 31654 } 31655 31656 /** 31657 * Returns a unit vector tangent for the given interpolation factor. 31658 * If the derived curve does not implement its tangent derivation, 31659 * two points a small delta apart will be used to find its gradient 31660 * which seems to give a reasonable approximation. 31661 * 31662 * @param {number} t - The interpolation factor. 31663 * @param {(Vector2|Vector3)} [optionalTarget] - The optional target vector the result is written to. 31664 * @return {(Vector2|Vector3)} The tangent vector. 31665 */ 31666 getTangent( t, optionalTarget ) { 31667 31668 const delta = 0.0001; 31669 let t1 = t - delta; 31670 let t2 = t + delta; 31671 31672 // Capping in case of danger 31673 31674 if ( t1 < 0 ) t1 = 0; 31675 if ( t2 > 1 ) t2 = 1; 31676 31677 const pt1 = this.getPoint( t1 ); 31678 const pt2 = this.getPoint( t2 ); 31679 31680 const tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() ); 31681 31682 tangent.copy( pt2 ).sub( pt1 ).normalize(); 31683 31684 return tangent; 31685 31686 } 31687 31688 /** 31689 * Same as {@link Curve#getTangent} but with equidistant samples. 31690 * 31691 * @param {number} u - The interpolation factor. 31692 * @param {(Vector2|Vector3)} [optionalTarget] - The optional target vector the result is written to. 31693 * @return {(Vector2|Vector3)} The tangent vector. 31694 * @see {@link Curve#getPointAt} 31695 */ 31696 getTangentAt( u, optionalTarget ) { 31697 31698 const t = this.getUtoTmapping( u ); 31699 return this.getTangent( t, optionalTarget ); 31700 31701 } 31702 31703 /** 31704 * Generates the Frenet Frames. Requires a curve definition in 3D space. Used 31705 * in geometries like {@link TubeGeometry} or {@link ExtrudeGeometry}. 31706 * 31707 * @param {number} segments - The number of segments. 31708 * @param {boolean} [closed=false] - Whether the curve is closed or not. 31709 * @return {{tangents: Array<Vector3>, normals: Array<Vector3>, binormals: Array<Vector3>}} The Frenet Frames. 31710 */ 31711 computeFrenetFrames( segments, closed = false ) { 31712 31713 // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf 31714 31715 const normal = new Vector3(); 31716 31717 const tangents = []; 31718 const normals = []; 31719 const binormals = []; 31720 31721 const vec = new Vector3(); 31722 const mat = new Matrix4(); 31723 31724 // compute the tangent vectors for each segment on the curve 31725 31726 for ( let i = 0; i <= segments; i ++ ) { 31727 31728 const u = i / segments; 31729 31730 tangents[ i ] = this.getTangentAt( u, new Vector3() ); 31731 31732 } 31733 31734 // select an initial normal vector perpendicular to the first tangent vector, 31735 // and in the direction of the minimum tangent xyz component 31736 31737 normals[ 0 ] = new Vector3(); 31738 binormals[ 0 ] = new Vector3(); 31739 let min = Number.MAX_VALUE; 31740 const tx = Math.abs( tangents[ 0 ].x ); 31741 const ty = Math.abs( tangents[ 0 ].y ); 31742 const tz = Math.abs( tangents[ 0 ].z ); 31743 31744 if ( tx <= min ) { 31745 31746 min = tx; 31747 normal.set( 1, 0, 0 ); 31748 31749 } 31750 31751 if ( ty <= min ) { 31752 31753 min = ty; 31754 normal.set( 0, 1, 0 ); 31755 31756 } 31757 31758 if ( tz <= min ) { 31759 31760 normal.set( 0, 0, 1 ); 31761 31762 } 31763 31764 vec.crossVectors( tangents[ 0 ], normal ).normalize(); 31765 31766 normals[ 0 ].crossVectors( tangents[ 0 ], vec ); 31767 binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); 31768 31769 31770 // compute the slowly-varying normal and binormal vectors for each segment on the curve 31771 31772 for ( let i = 1; i <= segments; i ++ ) { 31773 31774 normals[ i ] = normals[ i - 1 ].clone(); 31775 31776 binormals[ i ] = binormals[ i - 1 ].clone(); 31777 31778 vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); 31779 31780 if ( vec.length() > Number.EPSILON ) { 31781 31782 vec.normalize(); 31783 31784 const theta = Math.acos( clamp( tangents[ i - 1 ].dot( tangents[ i ] ), -1, 1 ) ); // clamp for floating pt errors 31785 31786 normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); 31787 31788 } 31789 31790 binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); 31791 31792 } 31793 31794 // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same 31795 31796 if ( closed === true ) { 31797 31798 let theta = Math.acos( clamp( normals[ 0 ].dot( normals[ segments ] ), -1, 1 ) ); 31799 theta /= segments; 31800 31801 if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { 31802 31803 theta = - theta; 31804 31805 } 31806 31807 for ( let i = 1; i <= segments; i ++ ) { 31808 31809 // twist a little... 31810 normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); 31811 binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); 31812 31813 } 31814 31815 } 31816 31817 return { 31818 tangents: tangents, 31819 normals: normals, 31820 binormals: binormals 31821 }; 31822 31823 } 31824 31825 /** 31826 * Returns a new curve with copied values from this instance. 31827 * 31828 * @return {Curve} A clone of this instance. 31829 */ 31830 clone() { 31831 31832 return new this.constructor().copy( this ); 31833 31834 } 31835 31836 /** 31837 * Copies the values of the given curve to this instance. 31838 * 31839 * @param {Curve} source - The curve to copy. 31840 * @return {Curve} A reference to this curve. 31841 */ 31842 copy( source ) { 31843 31844 this.arcLengthDivisions = source.arcLengthDivisions; 31845 31846 return this; 31847 31848 } 31849 31850 /** 31851 * Serializes the curve into JSON. 31852 * 31853 * @return {Object} A JSON object representing the serialized curve. 31854 * @see {@link ObjectLoader#parse} 31855 */ 31856 toJSON() { 31857 31858 const data = { 31859 metadata: { 31860 version: 4.7, 31861 type: 'Curve', 31862 generator: 'Curve.toJSON' 31863 } 31864 }; 31865 31866 data.arcLengthDivisions = this.arcLengthDivisions; 31867 data.type = this.type; 31868 31869 return data; 31870 31871 } 31872 31873 /** 31874 * Deserializes the curve from the given JSON. 31875 * 31876 * @param {Object} json - The JSON holding the serialized curve. 31877 * @return {Curve} A reference to this curve. 31878 */ 31879 fromJSON( json ) { 31880 31881 this.arcLengthDivisions = json.arcLengthDivisions; 31882 31883 return this; 31884 31885 } 31886 31887} 31888 31889/** 31890 * A curve representing an ellipse. 31891 * 31892 * ```js 31893 * const curve = new THREE.EllipseCurve( 31894 * 0, 0, 31895 * 10, 10, 31896 * 0, 2 * Math.PI, 31897 * false, 31898 * 0 31899 * ); 31900 * 31901 * const points = curve.getPoints( 50 ); 31902 * const geometry = new THREE.BufferGeometry().setFromPoints( points ); 31903 * 31904 * const material = new THREE.LineBasicMaterial( { color: 0xff0000 } ); 31905 * 31906 * // Create the final object to add to the scene 31907 * const ellipse = new THREE.Line( geometry, material ); 31908 * ``` 31909 * 31910 * @augments Curve 31911 */ 31912class EllipseCurve extends Curve { 31913 31914 /** 31915 * Constructs a new ellipse curve. 31916 * 31917 * @param {number} [aX=0] - The X center of the ellipse. 31918 * @param {number} [aY=0] - The Y center of the ellipse. 31919 * @param {number} [xRadius=1] - The radius of the ellipse in the x direction. 31920 * @param {number} [yRadius=1] - The radius of the ellipse in the y direction. 31921 * @param {number} [aStartAngle=0] - The start angle of the curve in radians starting from the positive X axis. 31922 * @param {number} [aEndAngle=Math.PI*2] - The end angle of the curve in radians starting from the positive X axis. 31923 * @param {boolean} [aClockwise=false] - Whether the ellipse is drawn clockwise or not. 31924 * @param {number} [aRotation=0] - The rotation angle of the ellipse in radians, counterclockwise from the positive X axis. 31925 */ 31926 constructor( aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0 ) { 31927 31928 super(); 31929 31930 /** 31931 * This flag can be used for type testing. 31932 * 31933 * @type {boolean} 31934 * @readonly 31935 * @default true 31936 */ 31937 this.isEllipseCurve = true; 31938 31939 this.type = 'EllipseCurve'; 31940 31941 /** 31942 * The X center of the ellipse. 31943 * 31944 * @type {number} 31945 * @default 0 31946 */ 31947 this.aX = aX; 31948 31949 /** 31950 * The Y center of the ellipse. 31951 * 31952 * @type {number} 31953 * @default 0 31954 */ 31955 this.aY = aY; 31956 31957 /** 31958 * The radius of the ellipse in the x direction. 31959 * Setting the this value equal to the {@link EllipseCurve#yRadius} will result in a circle. 31960 * 31961 * @type {number} 31962 * @default 1 31963 */ 31964 this.xRadius = xRadius; 31965 31966 /** 31967 * The radius of the ellipse in the y direction. 31968 * Setting the this value equal to the {@link EllipseCurve#xRadius} will result in a circle. 31969 * 31970 * @type {number} 31971 * @default 1 31972 */ 31973 this.yRadius = yRadius; 31974 31975 /** 31976 * The start angle of the curve in radians starting from the positive X axis. 31977 * 31978 * @type {number} 31979 * @default 0 31980 */ 31981 this.aStartAngle = aStartAngle; 31982 31983 /** 31984 * The end angle of the curve in radians starting from the positive X axis. 31985 * 31986 * @type {number} 31987 * @default Math.PI*2 31988 */ 31989 this.aEndAngle = aEndAngle; 31990 31991 /** 31992 * Whether the ellipse is drawn clockwise or not. 31993 * 31994 * @type {boolean} 31995 * @default false 31996 */ 31997 this.aClockwise = aClockwise; 31998 31999 /** 32000 * The rotation angle of the ellipse in radians, counterclockwise from the positive X axis. 32001 * 32002 * @type {number} 32003 * @default 0 32004 */ 32005 this.aRotation = aRotation; 32006 32007 } 32008 32009 /** 32010 * Returns a point on the curve. 32011 * 32012 * @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`. 32013 * @param {Vector2} [optionalTarget] - The optional target vector the result is written to. 32014 * @return {Vector2} The position on the curve. 32015 */ 32016 getPoint( t, optionalTarget = new Vector2() ) { 32017 32018 const point = optionalTarget; 32019 32020 const twoPi = Math.PI * 2; 32021 let deltaAngle = this.aEndAngle - this.aStartAngle; 32022 const samePoints = Math.abs( deltaAngle ) < Number.EPSILON; 32023 32024 // ensures that deltaAngle is 0 .. 2 PI 32025 while ( deltaAngle < 0 ) deltaAngle += twoPi; 32026 while ( deltaAngle > twoPi ) deltaAngle -= twoPi; 32027 32028 if ( deltaAngle < Number.EPSILON ) { 32029 32030 if ( samePoints ) { 32031 32032 deltaAngle = 0; 32033 32034 } else { 32035 32036 deltaAngle = twoPi; 32037 32038 } 32039 32040 } 32041 32042 if ( this.aClockwise === true && ! samePoints ) { 32043 32044 if ( deltaAngle === twoPi ) { 32045 32046 deltaAngle = - twoPi; 32047 32048 } else { 32049 32050 deltaAngle = deltaAngle - twoPi; 32051 32052 } 32053 32054 } 32055 32056 const angle = this.aStartAngle + t * deltaAngle; 32057 let x = this.aX + this.xRadius * Math.cos( angle ); 32058 let y = this.aY + this.yRadius * Math.sin( angle ); 32059 32060 if ( this.aRotation !== 0 ) { 32061 32062 const cos = Math.cos( this.aRotation ); 32063 const sin = Math.sin( this.aRotation ); 32064 32065 const tx = x - this.aX; 32066 const ty = y - this.aY; 32067 32068 // Rotate the point about the center of the ellipse. 32069 x = tx * cos - ty * sin + this.aX; 32070 y = tx * sin + ty * cos + this.aY; 32071 32072 } 32073 32074 return point.set( x, y ); 32075 32076 } 32077 32078 copy( source ) { 32079 32080 super.copy( source ); 32081 32082 this.aX = source.aX; 32083 this.aY = source.aY; 32084 32085 this.xRadius = source.xRadius; 32086 this.yRadius = source.yRadius; 32087 32088 this.aStartAngle = source.aStartAngle; 32089 this.aEndAngle = source.aEndAngle; 32090 32091 this.aClockwise = source.aClockwise; 32092 32093 this.aRotation = source.aRotation; 32094 32095 return this; 32096 32097 } 32098 32099 toJSON() { 32100 32101 const data = super.toJSON(); 32102 32103 data.aX = this.aX; 32104 data.aY = this.aY; 32105 32106 data.xRadius = this.xRadius; 32107 data.yRadius = this.yRadius; 32108 32109 data.aStartAngle = this.aStartAngle; 32110 data.aEndAngle = this.aEndAngle; 32111 32112 data.aClockwise = this.aClockwise; 32113 32114 data.aRotation = this.aRotation; 32115 32116 return data; 32117 32118 } 32119 32120 fromJSON( json ) { 32121 32122 super.fromJSON( json ); 32123 32124 this.aX = json.aX; 32125 this.aY = json.aY; 32126 32127 this.xRadius = json.xRadius; 32128 this.yRadius = json.yRadius; 32129 32130 this.aStartAngle = json.aStartAngle; 32131 this.aEndAngle = json.aEndAngle; 32132 32133 this.aClockwise = json.aClockwise; 32134 32135 this.aRotation = json.aRotation; 32136 32137 return this; 32138 32139 } 32140 32141} 32142 32143/** 32144 * A curve representing an arc. 32145 * 32146 * @augments EllipseCurve 32147 */ 32148class ArcCurve extends EllipseCurve { 32149 32150 /** 32151 * Constructs a new arc curve. 32152 * 32153 * @param {number} [aX=0] - The X center of the ellipse. 32154 * @param {number} [aY=0] - The Y center of the ellipse. 32155 * @param {number} [aRadius=1] - The radius of the ellipse in the x direction. 32156 * @param {number} [aStartAngle=0] - The start angle of the curve in radians starting from the positive X axis. 32157 * @param {number} [aEndAngle=Math.PI*2] - The end angle of the curve in radians starting from the positive X axis. 32158 * @param {boolean} [aClockwise=false] - Whether the ellipse is drawn clockwise or not. 32159 */ 32160 constructor( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { 32161 32162 super( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); 32163 32164 /** 32165 * This flag can be used for type testing. 32166 * 32167 * @type {boolean} 32168 * @readonly 32169 * @default true 32170 */ 32171 this.isArcCurve = true; 32172 32173 this.type = 'ArcCurve'; 32174 32175 } 32176 32177} 32178 32179function CubicPoly() { 32180 32181 /** 32182 * Centripetal CatmullRom Curve - which is useful for avoiding 32183 * cusps and self-intersections in non-uniform catmull rom curves. 32184 * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf 32185 * 32186 * curve.type accepts centripetal(default), chordal and catmullrom 32187 * curve.tension is used for catmullrom which defaults to 0.5 32188 */ 32189 32190 /* 32191 Based on an optimized c++ solution in 32192 - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ 32193 - http://ideone.com/NoEbVM 32194 32195 This CubicPoly class could be used for reusing some variables and calculations, 32196 but for three.js curve use, it could be possible inlined and flatten into a single function call 32197 which can be placed in CurveUtils. 32198 */ 32199 32200 let c0 = 0, c1 = 0, c2 = 0, c3 = 0; 32201 32202 /* 32203 * Compute coefficients for a cubic polynomial 32204 * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 32205 * such that 32206 * p(0) = x0, p(1) = x1 32207 * and 32208 * p'(0) = t0, p'(1) = t1. 32209 */ 32210 function init( x0, x1, t0, t1 ) { 32211 32212 c0 = x0; 32213 c1 = t0; 32214 c2 = -3 * x0 + 3 * x1 - 2 * t0 - t1; 32215 c3 = 2 * x0 - 2 * x1 + t0 + t1; 32216 32217 } 32218 32219 return { 32220 32221 initCatmullRom: function ( x0, x1, x2, x3, tension ) { 32222 32223 init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); 32224 32225 }, 32226 32227 initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { 32228 32229 // compute tangents when parameterized in [t1,t2] 32230 let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; 32231 let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; 32232 32233 // rescale tangents for parametrization in [0,1] 32234 t1 *= dt1; 32235 t2 *= dt1; 32236 32237 init( x1, x2, t1, t2 ); 32238 32239 }, 32240 32241 calc: function ( t ) { 32242 32243 const t2 = t * t; 32244 const t3 = t2 * t; 32245 return c0 + c1 * t + c2 * t2 + c3 * t3; 32246 32247 } 32248 32249 }; 32250 32251} 32252 32253// 32254 32255const tmp = /*@__PURE__*/ new Vector3(); 32256const tmp2 = /*@__PURE__*/ new Vector3(); 32257const px = /*@__PURE__*/ new CubicPoly(); 32258const py = /*@__PURE__*/ new CubicPoly(); 32259const pz = /*@__PURE__*/ new CubicPoly(); 32260 32261/** 32262 * A curve representing a Catmull-Rom spline. 32263 * 32264 * ```js 32265 * //Create a closed wavey loop 32266 * const curve = new THREE.CatmullRomCurve3( [ 32267 * new THREE.Vector3( -10, 0, 10 ), 32268 * new THREE.Vector3( -5, 5, 5 ), 32269 * new THREE.Vector3( 0, 0, 0 ), 32270 * new THREE.Vector3( 5, -5, 5 ), 32271 * new THREE.Vector3( 10, 0, 10 ) 32272 * ] ); 32273 * 32274 * const points = curve.getPoints( 50 ); 32275 * const geometry = new THREE.BufferGeometry().setFromPoints( points ); 32276 * 32277 * const material = new THREE.LineBasicMaterial( { color: 0xff0000 } ); 32278 * 32279 * // Create the final object to add to the scene 32280 * const curveObject = new THREE.Line( geometry, material ); 32281 * ``` 32282 * 32283 * @augments Curve 32284 */ 32285class CatmullRomCurve3 extends Curve { 32286 32287 /** 32288 * Constructs a new Catmull-Rom curve. 32289 * 32290 * @param {Array<Vector3>} [points] - An array of 3D points defining the curve. 32291 * @param {boolean} [closed=false] - Whether the curve is closed or not. 32292 * @param {('centripetal'|'chordal'|'catmullrom')} [curveType='centripetal'] - The curve type. 32293 * @param {number} [tension=0.5] - Tension of the curve. 32294 */ 32295 constructor( points = [], closed = false, curveType = 'centripetal', tension = 0.5 ) { 32296 32297 super(); 32298 32299 /** 32300 * This flag can be used for type testing. 32301 * 32302 * @type {boolean} 32303 * @readonly 32304 * @default true 32305 */ 32306 this.isCatmullRomCurve3 = true; 32307 32308 this.type = 'CatmullRomCurve3'; 32309 32310 /** 32311 * An array of 3D points defining the curve. 32312 * 32313 * @type {Array<Vector3>} 32314 */ 32315 this.points = points; 32316 32317 /** 32318 * Whether the curve is closed or not. 32319 * 32320 * @type {boolean} 32321 * @default false 32322 */ 32323 this.closed = closed; 32324 32325 /** 32326 * The curve type. 32327 * 32328 * @type {('centripetal'|'chordal'|'catmullrom')} 32329 * @default 'centripetal' 32330 */ 32331 this.curveType = curveType; 32332 32333 /** 32334 * Tension of the curve. 32335 * 32336 * @type {number} 32337 * @default 0.5 32338 */ 32339 this.tension = tension; 32340 32341 } 32342 32343 /** 32344 * Returns a point on the curve. 32345 * 32346 * @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`. 32347 * @param {Vector3} [optionalTarget] - The optional target vector the result is written to. 32348 * @return {Vector3} The position on the curve. 32349 */ 32350 getPoint( t, optionalTarget = new Vector3() ) { 32351 32352 const point = optionalTarget; 32353 32354 const points = this.points; 32355 const l = points.length; 32356 32357 const p = ( l - ( this.closed ? 0 : 1 ) ) * t; 32358 let intPoint = Math.floor( p ); 32359 let weight = p - intPoint; 32360 32361 if ( this.closed ) { 32362 32363 intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; 32364 32365 } else if ( weight === 0 && intPoint === l - 1 ) { 32366 32367 intPoint = l - 2; 32368 weight = 1; 32369 32370 } 32371 32372 let p0, p3; // 4 points (p1 & p2 defined below) 32373 32374 if ( this.closed || intPoint > 0 ) { 32375 32376 p0 = points[ ( intPoint - 1 ) % l ]; 32377 32378 } else { 32379 32380 // extrapolate first point 32381 tmp2.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); 32382 p0 = tmp2; 32383 32384 } 32385 32386 const p1 = points[ intPoint % l ]; 32387 const p2 = points[ ( intPoint + 1 ) % l ]; 32388 32389 if ( this.closed || intPoint + 2 < l ) { 32390 32391 p3 = points[ ( intPoint + 2 ) % l ]; 32392 32393 } else { 32394 32395 // extrapolate last point 32396 tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); 32397 p3 = tmp; 32398 32399 } 32400 32401 if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) { 32402 32403 // init Centripetal / Chordal Catmull-Rom 32404 const pow = this.curveType === 'chordal' ? 0.5 : 0.25; 32405 let dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); 32406 let dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); 32407 let dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); 32408 32409 // safety check for repeated points 32410 if ( dt1 < 1e-4 ) dt1 = 1.0; 32411 if ( dt0 < 1e-4 ) dt0 = dt1; 32412 if ( dt2 < 1e-4 ) dt2 = dt1; 32413 32414 px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); 32415 py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); 32416 pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); 32417 32418 } else if ( this.curveType === 'catmullrom' ) { 32419 32420 px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); 32421 py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); 32422 pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); 32423 32424 } 32425 32426 point.set( 32427 px.calc( weight ), 32428 py.calc( weight ), 32429 pz.calc( weight ) 32430 ); 32431 32432 return point; 32433 32434 } 32435 32436 copy( source ) { 32437 32438 super.copy( source ); 32439 32440 this.points = []; 32441 32442 for ( let i = 0, l = source.points.length; i < l; i ++ ) { 32443 32444 const point = source.points[ i ]; 32445 32446 this.points.push( point.clone() ); 32447 32448 } 32449 32450 this.closed = source.closed; 32451 this.curveType = source.curveType; 32452 this.tension = source.tension; 32453 32454 return this; 32455 32456 } 32457 32458 toJSON() { 32459 32460 const data = super.toJSON(); 32461 32462 data.points = []; 32463 32464 for ( let i = 0, l = this.points.length; i < l; i ++ ) { 32465 32466 const point = this.points[ i ]; 32467 data.points.push( point.toArray() ); 32468 32469 } 32470 32471 data.closed = this.closed; 32472 data.curveType = this.curveType; 32473 data.tension = this.tension; 32474 32475 return data; 32476 32477 } 32478 32479 fromJSON( json ) { 32480 32481 super.fromJSON( json ); 32482 32483 this.points = []; 32484 32485 for ( let i = 0, l = json.points.length; i < l; i ++ ) { 32486 32487 const point = json.points[ i ]; 32488 this.points.push( new Vector3().fromArray( point ) ); 32489 32490 } 32491 32492 this.closed = json.closed; 32493 this.curveType = json.curveType; 32494 this.tension = json.tension; 32495 32496 return this; 32497 32498 } 32499 32500} 32501 32502/** 32503 * Interpolations contains spline and Bézier functions internally used by concrete curve classes. 32504 * 32505 * Bezier Curves formulas obtained from: https://en.wikipedia.org/wiki/B%C3%A9zier_curve 32506 * 32507 * @module Interpolations 32508 */ 32509 32510/** 32511 * Computes a point on a Catmull-Rom spline. 32512 * 32513 * @param {number} t - The interpolation factor. 32514 * @param {number} p0 - The first control point. 32515 * @param {number} p1 - The second control point. 32516 * @param {number} p2 - The third control point. 32517 * @param {number} p3 - The fourth control point. 32518 * @return {number} The calculated point on a Catmull-Rom spline. 32519 */ 32520function CatmullRom( t, p0, p1, p2, p3 ) { 32521 32522 const v0 = ( p2 - p0 ) * 0.5; 32523 const v1 = ( p3 - p1 ) * 0.5; 32524 const t2 = t * t; 32525 const t3 = t * t2; 32526 return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( -3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; 32527 32528} 32529 32530// 32531 32532function QuadraticBezierP0( t, p ) { 32533 32534 const k = 1 - t; 32535 return k * k * p; 32536 32537} 32538 32539function QuadraticBezierP1( t, p ) { 32540 32541 return 2 * ( 1 - t ) * t * p; 32542 32543} 32544 32545function QuadraticBezierP2( t, p ) { 32546 32547 return t * t * p; 32548 32549} 32550 32551/** 32552 * Computes a point on a Quadratic Bezier curve. 32553 * 32554 * @param {number} t - The interpolation factor. 32555 * @param {number} p0 - The first control point. 32556 * @param {number} p1 - The second control point. 32557 * @param {number} p2 - The third control point. 32558 * @return {number} The calculated point on a Quadratic Bezier curve. 32559 */ 32560function QuadraticBezier( t, p0, p1, p2 ) { 32561 32562 return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + 32563 QuadraticBezierP2( t, p2 ); 32564 32565} 32566 32567// 32568 32569function CubicBezierP0( t, p ) { 32570 32571 const k = 1 - t; 32572 return k * k * k * p; 32573 32574} 32575 32576function CubicBezierP1( t, p ) { 32577 32578 const k = 1 - t; 32579 return 3 * k * k * t * p; 32580 32581} 32582 32583function CubicBezierP2( t, p ) { 32584 32585 return 3 * ( 1 - t ) * t * t * p; 32586 32587} 32588 32589function CubicBezierP3( t, p ) { 32590 32591 return t * t * t * p; 32592 32593} 32594 32595/** 32596 * Computes a point on a Cubic Bezier curve. 32597 * 32598 * @param {number} t - The interpolation factor. 32599 * @param {number} p0 - The first control point. 32600 * @param {number} p1 - The second control point. 32601 * @param {number} p2 - The third control point. 32602 * @param {number} p3 - The fourth control point. 32603 * @return {number} The calculated point on a Cubic Bezier curve. 32604 */ 32605function CubicBezier( t, p0, p1, p2, p3 ) { 32606 32607 return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + 32608 CubicBezierP3( t, p3 ); 32609 32610} 32611 32612/** 32613 * A curve representing a 2D Cubic Bezier curve. 32614 * 32615 * ```js 32616 * const curve = new THREE.CubicBezierCurve( 32617 * new THREE.Vector2( - 0, 0 ), 32618 * new THREE.Vector2( - 5, 15 ), 32619 * new THREE.Vector2( 20, 15 ), 32620 * new THREE.Vector2( 10, 0 ) 32621 * ); 32622 * 32623 * const points = curve.getPoints( 50 ); 32624 * const geometry = new THREE.BufferGeometry().setFromPoints( points ); 32625 * 32626 * const material = new THREE.LineBasicMaterial( { color: 0xff0000 } ); 32627 * 32628 * // Create the final object to add to the scene 32629 * const curveObject = new THREE.Line( geometry, material ); 32630 * ``` 32631 * 32632 * @augments Curve 32633 */ 32634class CubicBezierCurve extends Curve { 32635 32636 /** 32637 * Constructs a new Cubic Bezier curve. 32638 * 32639 * @param {Vector2} [v0] - The start point. 32640 * @param {Vector2} [v1] - The first control point. 32641 * @param {Vector2} [v2] - The second control point. 32642 * @param {Vector2} [v3] - The end point. 32643 */ 32644 constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2() ) { 32645 32646 super(); 32647 32648 /** 32649 * This flag can be used for type testing. 32650 * 32651 * @type {boolean} 32652 * @readonly 32653 * @default true 32654 */ 32655 this.isCubicBezierCurve = true; 32656 32657 this.type = 'CubicBezierCurve'; 32658 32659 /** 32660 * The start point. 32661 * 32662 * @type {Vector2} 32663 */ 32664 this.v0 = v0; 32665 32666 /** 32667 * The first control point. 32668 * 32669 * @type {Vector2} 32670 */ 32671 this.v1 = v1; 32672 32673 /** 32674 * The second control point. 32675 * 32676 * @type {Vector2} 32677 */ 32678 this.v2 = v2; 32679 32680 /** 32681 * The end point. 32682 * 32683 * @type {Vector2} 32684 */ 32685 this.v3 = v3; 32686 32687 } 32688 32689 /** 32690 * Returns a point on the curve. 32691 * 32692 * @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`. 32693 * @param {Vector2} [optionalTarget] - The optional target vector the result is written to. 32694 * @return {Vector2} The position on the curve. 32695 */ 32696 getPoint( t, optionalTarget = new Vector2() ) { 32697 32698 const point = optionalTarget; 32699 32700 const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; 32701 32702 point.set( 32703 CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), 32704 CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) 32705 ); 32706 32707 return point; 32708 32709 } 32710 32711 copy( source ) { 32712 32713 super.copy( source ); 32714 32715 this.v0.copy( source.v0 ); 32716 this.v1.copy( source.v1 ); 32717 this.v2.copy( source.v2 ); 32718 this.v3.copy( source.v3 ); 32719 32720 return this; 32721 32722 } 32723 32724 toJSON() { 32725 32726 const data = super.toJSON(); 32727 32728 data.v0 = this.v0.toArray(); 32729 data.v1 = this.v1.toArray(); 32730 data.v2 = this.v2.toArray(); 32731 data.v3 = this.v3.toArray(); 32732 32733 return data; 32734 32735 } 32736 32737 fromJSON( json ) { 32738 32739 super.fromJSON( json ); 32740 32741 this.v0.fromArray( json.v0 ); 32742 this.v1.fromArray( json.v1 ); 32743 this.v2.fromArray( json.v2 ); 32744 this.v3.fromArray( json.v3 ); 32745 32746 return this; 32747 32748 } 32749 32750} 32751 32752/** 32753 * A curve representing a 3D Cubic Bezier curve. 32754 * 32755 * @augments Curve 32756 */ 32757class CubicBezierCurve3 extends Curve { 32758 32759 /** 32760 * Constructs a new Cubic Bezier curve. 32761 * 32762 * @param {Vector3} [v0] - The start point. 32763 * @param {Vector3} [v1] - The first control point. 32764 * @param {Vector3} [v2] - The second control point. 32765 * @param {Vector3} [v3] - The end point. 32766 */ 32767 constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3() ) { 32768 32769 super(); 32770 32771 /** 32772 * This flag can be used for type testing. 32773 * 32774 * @type {boolean} 32775 * @readonly 32776 * @default true 32777 */ 32778 this.isCubicBezierCurve3 = true; 32779 32780 this.type = 'CubicBezierCurve3'; 32781 32782 /** 32783 * The start point. 32784 * 32785 * @type {Vector3} 32786 */ 32787 this.v0 = v0; 32788 32789 /** 32790 * The first control point. 32791 * 32792 * @type {Vector3} 32793 */ 32794 this.v1 = v1; 32795 32796 /** 32797 * The second control point. 32798 * 32799 * @type {Vector3} 32800 */ 32801 this.v2 = v2; 32802 32803 /** 32804 * The end point. 32805 * 32806 * @type {Vector3} 32807 */ 32808 this.v3 = v3; 32809 32810 } 32811 32812 /** 32813 * Returns a point on the curve. 32814 * 32815 * @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`. 32816 * @param {Vector3} [optionalTarget] - The optional target vector the result is written to. 32817 * @return {Vector3} The position on the curve. 32818 */ 32819 getPoint( t, optionalTarget = new Vector3() ) { 32820 32821 const point = optionalTarget; 32822 32823 const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; 32824 32825 point.set( 32826 CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), 32827 CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), 32828 CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) 32829 ); 32830 32831 return point; 32832 32833 } 32834 32835 copy( source ) { 32836 32837 super.copy( source ); 32838 32839 this.v0.copy( source.v0 ); 32840 this.v1.copy( source.v1 ); 32841 this.v2.copy( source.v2 ); 32842 this.v3.copy( source.v3 ); 32843 32844 return this; 32845 32846 } 32847 32848 toJSON() { 32849 32850 const data = super.toJSON(); 32851 32852 data.v0 = this.v0.toArray(); 32853 data.v1 = this.v1.toArray(); 32854 data.v2 = this.v2.toArray(); 32855 data.v3 = this.v3.toArray(); 32856 32857 return data; 32858 32859 } 32860 32861 fromJSON( json ) { 32862 32863 super.fromJSON( json ); 32864 32865 this.v0.fromArray( json.v0 ); 32866 this.v1.fromArray( json.v1 ); 32867 this.v2.fromArray( json.v2 ); 32868 this.v3.fromArray( json.v3 ); 32869 32870 return this; 32871 32872 } 32873 32874} 32875 32876/** 32877 * A curve representing a 2D line segment. 32878 * 32879 * @augments Curve 32880 */ 32881class LineCurve extends Curve { 32882 32883 /** 32884 * Constructs a new line curve. 32885 * 32886 * @param {Vector2} [v1] - The start point. 32887 * @param {Vector2} [v2] - The end point. 32888 */ 32889 constructor( v1 = new Vector2(), v2 = new Vector2() ) { 32890 32891 super(); 32892 32893 /** 32894 * This flag can be used for type testing. 32895 * 32896 * @type {boolean} 32897 * @readonly 32898 * @default true 32899 */ 32900 this.isLineCurve = true; 32901 32902 this.type = 'LineCurve'; 32903 32904 /** 32905 * The start point. 32906 * 32907 * @type {Vector2} 32908 */ 32909 this.v1 = v1; 32910 32911 /** 32912 * The end point. 32913 * 32914 * @type {Vector2} 32915 */ 32916 this.v2 = v2; 32917 32918 } 32919 32920 /** 32921 * Returns a point on the line. 32922 * 32923 * @param {number} t - A interpolation factor representing a position on the line. Must be in the range `[0,1]`. 32924 * @param {Vector2} [optionalTarget] - The optional target vector the result is written to. 32925 * @return {Vector2} The position on the line. 32926 */ 32927 getPoint( t, optionalTarget = new Vector2() ) { 32928 32929 const point = optionalTarget; 32930 32931 if ( t === 1 ) { 32932 32933 point.copy( this.v2 ); 32934 32935 } else { 32936 32937 point.copy( this.v2 ).sub( this.v1 ); 32938 point.multiplyScalar( t ).add( this.v1 ); 32939 32940 } 32941 32942 return point; 32943 32944 } 32945 32946 // Line curve is linear, so we can overwrite default getPointAt 32947 getPointAt( u, optionalTarget ) { 32948 32949 return this.getPoint( u, optionalTarget ); 32950 32951 } 32952 32953 getTangent( t, optionalTarget = new Vector2() ) { 32954 32955 return optionalTarget.subVectors( this.v2, this.v1 ).normalize(); 32956 32957 } 32958 32959 getTangentAt( u, optionalTarget ) { 32960 32961 return this.getTangent( u, optionalTarget ); 32962 32963 } 32964 32965 copy( source ) { 32966 32967 super.copy( source ); 32968 32969 this.v1.copy( source.v1 ); 32970 this.v2.copy( source.v2 ); 32971 32972 return this; 32973 32974 } 32975 32976 toJSON() { 32977 32978 const data = super.toJSON(); 32979 32980 data.v1 = this.v1.toArray(); 32981 data.v2 = this.v2.toArray(); 32982 32983 return data; 32984 32985 } 32986 32987 fromJSON( json ) { 32988 32989 super.fromJSON( json ); 32990 32991 this.v1.fromArray( json.v1 ); 32992 this.v2.fromArray( json.v2 ); 32993 32994 return this; 32995 32996 } 32997 32998} 32999 33000/** 33001 * A curve representing a 3D line segment. 33002 * 33003 * @augments Curve 33004 */ 33005class LineCurve3 extends Curve { 33006 33007 /** 33008 * Constructs a new line curve. 33009 * 33010 * @param {Vector3} [v1] - The start point. 33011 * @param {Vector3} [v2] - The end point. 33012 */ 33013 constructor( v1 = new Vector3(), v2 = new Vector3() ) { 33014 33015 super(); 33016 33017 /** 33018 * This flag can be used for type testing. 33019 * 33020 * @type {boolean} 33021 * @readonly 33022 * @default true 33023 */ 33024 this.isLineCurve3 = true; 33025 33026 this.type = 'LineCurve3'; 33027 33028 /** 33029 * The start point. 33030 * 33031 * @type {Vector3} 33032 */ 33033 this.v1 = v1; 33034 33035 /** 33036 * The end point. 33037 * 33038 * @type {Vector2} 33039 */ 33040 this.v2 = v2; 33041 33042 } 33043 33044 /** 33045 * Returns a point on the line. 33046 * 33047 * @param {number} t - A interpolation factor representing a position on the line. Must be in the range `[0,1]`. 33048 * @param {Vector3} [optionalTarget] - The optional target vector the result is written to. 33049 * @return {Vector3} The position on the line. 33050 */ 33051 getPoint( t, optionalTarget = new Vector3() ) { 33052 33053 const point = optionalTarget; 33054 33055 if ( t === 1 ) { 33056 33057 point.copy( this.v2 ); 33058 33059 } else { 33060 33061 point.copy( this.v2 ).sub( this.v1 ); 33062 point.multiplyScalar( t ).add( this.v1 ); 33063 33064 } 33065 33066 return point; 33067 33068 } 33069 33070 // Line curve is linear, so we can overwrite default getPointAt 33071 getPointAt( u, optionalTarget ) { 33072 33073 return this.getPoint( u, optionalTarget ); 33074 33075 } 33076 33077 getTangent( t, optionalTarget = new Vector3() ) { 33078 33079 return optionalTarget.subVectors( this.v2, this.v1 ).normalize(); 33080 33081 } 33082 33083 getTangentAt( u, optionalTarget ) { 33084 33085 return this.getTangent( u, optionalTarget ); 33086 33087 } 33088 33089 copy( source ) { 33090 33091 super.copy( source ); 33092 33093 this.v1.copy( source.v1 ); 33094 this.v2.copy( source.v2 ); 33095 33096 return this; 33097 33098 } 33099 33100 toJSON() { 33101 33102 const data = super.toJSON(); 33103 33104 data.v1 = this.v1.toArray(); 33105 data.v2 = this.v2.toArray(); 33106 33107 return data; 33108 33109 } 33110 33111 fromJSON( json ) { 33112 33113 super.fromJSON( json ); 33114 33115 this.v1.fromArray( json.v1 ); 33116 this.v2.fromArray( json.v2 ); 33117 33118 return this; 33119 33120 } 33121 33122} 33123 33124/** 33125 * A curve representing a 2D Quadratic Bezier curve. 33126 * 33127 * ```js 33128 * const curve = new THREE.QuadraticBezierCurve( 33129 * new THREE.Vector2( - 10, 0 ), 33130 * new THREE.Vector2( 20, 15 ), 33131 * new THREE.Vector2( 10, 0 ) 33132 * ) 33133 * 33134 * const points = curve.getPoints( 50 ); 33135 * const geometry = new THREE.BufferGeometry().setFromPoints( points ); 33136 * 33137 * const material = new THREE.LineBasicMaterial( { color: 0xff0000 } ); 33138 * 33139 * // Create the final object to add to the scene 33140 * const curveObject = new THREE.Line( geometry, material ); 33141 * ``` 33142 * 33143 * @augments Curve 33144 */ 33145class QuadraticBezierCurve extends Curve { 33146 33147 /** 33148 * Constructs a new Quadratic Bezier curve. 33149 * 33150 * @param {Vector2} [v0] - The start point. 33151 * @param {Vector2} [v1] - The control point. 33152 * @param {Vector2} [v2] - The end point. 33153 */ 33154 constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2() ) { 33155 33156 super(); 33157 33158 /** 33159 * This flag can be used for type testing. 33160 * 33161 * @type {boolean} 33162 * @readonly 33163 * @default true 33164 */ 33165 this.isQuadraticBezierCurve = true; 33166 33167 this.type = 'QuadraticBezierCurve'; 33168 33169 /** 33170 * The start point. 33171 * 33172 * @type {Vector2} 33173 */ 33174 this.v0 = v0; 33175 33176 /** 33177 * The control point. 33178 * 33179 * @type {Vector2} 33180 */ 33181 this.v1 = v1; 33182 33183 /** 33184 * The end point. 33185 * 33186 * @type {Vector2} 33187 */ 33188 this.v2 = v2; 33189 33190 } 33191 33192 /** 33193 * Returns a point on the curve. 33194 * 33195 * @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`. 33196 * @param {Vector2} [optionalTarget] - The optional target vector the result is written to. 33197 * @return {Vector2} The position on the curve. 33198 */ 33199 getPoint( t, optionalTarget = new Vector2() ) { 33200 33201 const point = optionalTarget; 33202 33203 const v0 = this.v0, v1 = this.v1, v2 = this.v2; 33204 33205 point.set( 33206 QuadraticBezier( t, v0.x, v1.x, v2.x ), 33207 QuadraticBezier( t, v0.y, v1.y, v2.y ) 33208 ); 33209 33210 return point; 33211 33212 } 33213 33214 copy( source ) { 33215 33216 super.copy( source ); 33217 33218 this.v0.copy( source.v0 ); 33219 this.v1.copy( source.v1 ); 33220 this.v2.copy( source.v2 ); 33221 33222 return this; 33223 33224 } 33225 33226 toJSON() { 33227 33228 const data = super.toJSON(); 33229 33230 data.v0 = this.v0.toArray(); 33231 data.v1 = this.v1.toArray(); 33232 data.v2 = this.v2.toArray(); 33233 33234 return data; 33235 33236 } 33237 33238 fromJSON( json ) { 33239 33240 super.fromJSON( json ); 33241 33242 this.v0.fromArray( json.v0 ); 33243 this.v1.fromArray( json.v1 ); 33244 this.v2.fromArray( json.v2 ); 33245 33246 return this; 33247 33248 } 33249 33250} 33251 33252/** 33253 * A curve representing a 3D Quadratic Bezier curve. 33254 * 33255 * @augments Curve 33256 */ 33257class QuadraticBezierCurve3 extends Curve { 33258 33259 /** 33260 * Constructs a new Quadratic Bezier curve. 33261 * 33262 * @param {Vector3} [v0] - The start point. 33263 * @param {Vector3} [v1] - The control point. 33264 * @param {Vector3} [v2] - The end point. 33265 */ 33266 constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3() ) { 33267 33268 super(); 33269 33270 /** 33271 * This flag can be used for type testing. 33272 * 33273 * @type {boolean} 33274 * @readonly 33275 * @default true 33276 */ 33277 this.isQuadraticBezierCurve3 = true; 33278 33279 this.type = 'QuadraticBezierCurve3'; 33280 33281 /** 33282 * The start point. 33283 * 33284 * @type {Vector3} 33285 */ 33286 this.v0 = v0; 33287 33288 /** 33289 * The control point. 33290 * 33291 * @type {Vector3} 33292 */ 33293 this.v1 = v1; 33294 33295 /** 33296 * The end point. 33297 * 33298 * @type {Vector3} 33299 */ 33300 this.v2 = v2; 33301 33302 } 33303 33304 /** 33305 * Returns a point on the curve. 33306 * 33307 * @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`. 33308 * @param {Vector3} [optionalTarget] - The optional target vector the result is written to. 33309 * @return {Vector3} The position on the curve. 33310 */ 33311 getPoint( t, optionalTarget = new Vector3() ) { 33312 33313 const point = optionalTarget; 33314 33315 const v0 = this.v0, v1 = this.v1, v2 = this.v2; 33316 33317 point.set( 33318 QuadraticBezier( t, v0.x, v1.x, v2.x ), 33319 QuadraticBezier( t, v0.y, v1.y, v2.y ), 33320 QuadraticBezier( t, v0.z, v1.z, v2.z ) 33321 ); 33322 33323 return point; 33324 33325 } 33326 33327 copy( source ) { 33328 33329 super.copy( source ); 33330 33331 this.v0.copy( source.v0 ); 33332 this.v1.copy( source.v1 ); 33333 this.v2.copy( source.v2 ); 33334 33335 return this; 33336 33337 } 33338 33339 toJSON() { 33340 33341 const data = super.toJSON(); 33342 33343 data.v0 = this.v0.toArray(); 33344 data.v1 = this.v1.toArray(); 33345 data.v2 = this.v2.toArray(); 33346 33347 return data; 33348 33349 } 33350 33351 fromJSON( json ) { 33352 33353 super.fromJSON( json ); 33354 33355 this.v0.fromArray( json.v0 ); 33356 this.v1.fromArray( json.v1 ); 33357 this.v2.fromArray( json.v2 ); 33358 33359 return this; 33360 33361 } 33362 33363} 33364 33365/** 33366 * A curve representing a 2D spline curve. 33367 * 33368 * ```js 33369 * // Create a sine-like wave 33370 * const curve = new THREE.SplineCurve( [ 33371 * new THREE.Vector2( -10, 0 ), 33372 * new THREE.Vector2( -5, 5 ), 33373 * new THREE.Vector2( 0, 0 ), 33374 * new THREE.Vector2( 5, -5 ), 33375 * new THREE.Vector2( 10, 0 ) 33376 * ] ); 33377 * 33378 * const points = curve.getPoints( 50 ); 33379 * const geometry = new THREE.BufferGeometry().setFromPoints( points ); 33380 * 33381 * const material = new THREE.LineBasicMaterial( { color: 0xff0000 } ); 33382 * 33383 * // Create the final object to add to the scene 33384 * const splineObject = new THREE.Line( geometry, material ); 33385 * ``` 33386 * 33387 * @augments Curve 33388 */ 33389class SplineCurve extends Curve { 33390 33391 /** 33392 * Constructs a new 2D spline curve. 33393 * 33394 * @param {Array<Vector2>} [points] - An array of 2D points defining the curve. 33395 */ 33396 constructor( points = [] ) { 33397 33398 super(); 33399 33400 /** 33401 * This flag can be used for type testing. 33402 * 33403 * @type {boolean} 33404 * @readonly 33405 * @default true 33406 */ 33407 this.isSplineCurve = true; 33408 33409 this.type = 'SplineCurve'; 33410 33411 /** 33412 * An array of 2D points defining the curve. 33413 * 33414 * @type {Array<Vector2>} 33415 */ 33416 this.points = points; 33417 33418 } 33419 33420 /** 33421 * Returns a point on the curve. 33422 * 33423 * @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`. 33424 * @param {Vector2} [optionalTarget] - The optional target vector the result is written to. 33425 * @return {Vector2} The position on the curve. 33426 */ 33427 getPoint( t, optionalTarget = new Vector2() ) { 33428 33429 const point = optionalTarget; 33430 33431 const points = this.points; 33432 const p = ( points.length - 1 ) * t; 33433 33434 const intPoint = Math.floor( p ); 33435 const weight = p - intPoint; 33436 33437 const p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; 33438 const p1 = points[ intPoint ]; 33439 const p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; 33440 const p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; 33441 33442 point.set( 33443 CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), 33444 CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) 33445 ); 33446 33447 return point; 33448 33449 } 33450 33451 copy( source ) { 33452 33453 super.copy( source ); 33454 33455 this.points = []; 33456 33457 for ( let i = 0, l = source.points.length; i < l; i ++ ) { 33458 33459 const point = source.points[ i ]; 33460 33461 this.points.push( point.clone() ); 33462 33463 } 33464 33465 return this; 33466 33467 } 33468 33469 toJSON() { 33470 33471 const data = super.toJSON(); 33472 33473 data.points = []; 33474 33475 for ( let i = 0, l = this.points.length; i < l; i ++ ) { 33476 33477 const point = this.points[ i ]; 33478 data.points.push( point.toArray() ); 33479 33480 } 33481 33482 return data; 33483 33484 } 33485 33486 fromJSON( json ) { 33487 33488 super.fromJSON( json ); 33489 33490 this.points = []; 33491 33492 for ( let i = 0, l = json.points.length; i < l; i ++ ) { 33493 33494 const point = json.points[ i ]; 33495 this.points.push( new Vector2().fromArray( point ) ); 33496 33497 } 33498 33499 return this; 33500 33501 } 33502 33503} 33504 33505var Curves = /*#__PURE__*/Object.freeze({ 33506 __proto__: null, 33507 ArcCurve: ArcCurve, 33508 CatmullRomCurve3: CatmullRomCurve3, 33509 CubicBezierCurve: CubicBezierCurve, 33510 CubicBezierCurve3: CubicBezierCurve3, 33511 EllipseCurve: EllipseCurve, 33512 LineCurve: LineCurve, 33513 LineCurve3: LineCurve3, 33514 QuadraticBezierCurve: QuadraticBezierCurve, 33515 QuadraticBezierCurve3: QuadraticBezierCurve3, 33516 SplineCurve: SplineCurve 33517}); 33518 33519/** 33520 * A base class extending {@link Curve}. `CurvePath` is simply an 33521 * array of connected curves, but retains the API of a curve. 33522 * 33523 * @augments Curve 33524 */ 33525class CurvePath extends Curve { 33526 33527 /** 33528 * Constructs a new curve path. 33529 */ 33530 constructor() { 33531 33532 super(); 33533 33534 this.type = 'CurvePath'; 33535 33536 /** 33537 * An array of curves defining the 33538 * path. 33539 * 33540 * @type {Array<Curve>} 33541 */ 33542 this.curves = []; 33543 33544 /** 33545 * Whether the path should automatically be closed 33546 * by a line curve. 33547 * 33548 * @type {boolean} 33549 * @default false 33550 */ 33551 this.autoClose = false; 33552 33553 } 33554 33555 /** 33556 * Adds a curve to this curve path. 33557 * 33558 * @param {Curve} curve - The curve to add. 33559 */ 33560 add( curve ) { 33561 33562 this.curves.push( curve ); 33563 33564 } 33565 33566 /** 33567 * Adds a line curve to close the path. 33568 * 33569 * @return {CurvePath} A reference to this curve path. 33570 */ 33571 closePath() { 33572 33573 // Add a line curve if start and end of lines are not connected 33574 const startPoint = this.curves[ 0 ].getPoint( 0 ); 33575 const endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); 33576 33577 if ( ! startPoint.equals( endPoint ) ) { 33578 33579 const lineType = ( startPoint.isVector2 === true ) ? 'LineCurve' : 'LineCurve3'; 33580 this.curves.push( new Curves[ lineType ]( endPoint, startPoint ) ); 33581 33582 } 33583 33584 return this; 33585 33586 } 33587 33588 /** 33589 * This method returns a vector in 2D or 3D space (depending on the curve definitions) 33590 * for the given interpolation factor. 33591 * 33592 * @param {number} t - A interpolation factor representing a position on the curve. Must be in the range `[0,1]`. 33593 * @param {(Vector2|Vector3)} [optionalTarget] - The optional target vector the result is written to. 33594 * @return {?(Vector2|Vector3)} The position on the curve. It can be a 2D or 3D vector depending on the curve definition. 33595 */ 33596 getPoint( t, optionalTarget ) { 33597 33598 // To get accurate point with reference to 33599 // entire path distance at time t, 33600 // following has to be done: 33601 33602 // 1. Length of each sub path have to be known 33603 // 2. Locate and identify type of curve 33604 // 3. Get t for the curve 33605 // 4. Return curve.getPointAt(t') 33606 33607 const d = t * this.getLength(); 33608 const curveLengths = this.getCurveLengths(); 33609 let i = 0; 33610 33611 // To think about boundaries points. 33612 33613 while ( i < curveLengths.length ) { 33614 33615 if ( curveLengths[ i ] >= d ) { 33616 33617 const diff = curveLengths[ i ] - d; 33618 const curve = this.curves[ i ]; 33619 33620 const segmentLength = curve.getLength(); 33621 const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; 33622 33623 return curve.getPointAt( u, optionalTarget ); 33624 33625 } 33626 33627 i ++; 33628 33629 } 33630 33631 return null; 33632 33633 // loop where sum != 0, sum > d , sum+1 <d 33634 33635 } 33636 33637 getLength() { 33638 33639 // We cannot use the default THREE.Curve getPoint() with getLength() because in 33640 // THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath 33641 // getPoint() depends on getLength 33642 33643 const lens = this.getCurveLengths(); 33644 return lens[ lens.length - 1 ]; 33645 33646 } 33647 33648 updateArcLengths() { 33649 33650 // cacheLengths must be recalculated. 33651 33652 this.needsUpdate = true; 33653 this.cacheLengths = null; 33654 this.getCurveLengths(); 33655 33656 } 33657 33658 /** 33659 * Returns list of cumulative curve lengths of the defined curves. 33660 * 33661 * @return {Array<number>} The curve lengths. 33662 */ 33663 getCurveLengths() { 33664 33665 // Compute lengths and cache them 33666 // We cannot overwrite getLengths() because UtoT mapping uses it. 33667 // We use cache values if curves and cache array are same length 33668 33669 if ( this.cacheLengths && this.cacheLengths.length === this.curves.length ) { 33670 33671 return this.cacheLengths; 33672 33673 } 33674 33675 // Get length of sub-curve 33676 // Push sums into cached array 33677 33678 const lengths = []; 33679 let sums = 0; 33680 33681 for ( let i = 0, l = this.curves.length; i < l; i ++ ) { 33682 33683 sums += this.curves[ i ].getLength(); 33684 lengths.push( sums ); 33685 33686 } 33687 33688 this.cacheLengths = lengths; 33689 33690 return lengths; 33691 33692 } 33693 33694 getSpacedPoints( divisions = 40 ) { 33695 33696 const points = []; 33697 33698 for ( let i = 0; i <= divisions; i ++ ) { 33699 33700 points.push( this.getPoint( i / divisions ) ); 33701 33702 } 33703 33704 if ( this.autoClose ) { 33705 33706 points.push( points[ 0 ] ); 33707 33708 } 33709 33710 return points; 33711 33712 } 33713 33714 getPoints( divisions = 12 ) { 33715 33716 const points = []; 33717 let last; 33718 33719 for ( let i = 0, curves = this.curves; i < curves.length; i ++ ) { 33720 33721 const curve = curves[ i ]; 33722 const resolution = curve.isEllipseCurve ? divisions * 2 33723 : ( curve.isLineCurve || curve.isLineCurve3 ) ? 1 33724 : curve.isSplineCurve ? divisions * curve.points.length 33725 : divisions; 33726 33727 const pts = curve.getPoints( resolution ); 33728 33729 for ( let j = 0; j < pts.length; j ++ ) { 33730 33731 const point = pts[ j ]; 33732 33733 if ( last && last.equals( point ) ) continue; // ensures no consecutive points are duplicates 33734 33735 points.push( point ); 33736 last = point; 33737 33738 } 33739 33740 } 33741 33742 if ( this.autoClose && points.length > 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { 33743 33744 points.push( points[ 0 ] ); 33745 33746 } 33747 33748 return points; 33749 33750 } 33751 33752 copy( source ) { 33753 33754 super.copy( source ); 33755 33756 this.curves = []; 33757 33758 for ( let i = 0, l = source.curves.length; i < l; i ++ ) { 33759 33760 const curve = source.curves[ i ]; 33761 33762 this.curves.push( curve.clone() ); 33763 33764 } 33765 33766 this.autoClose = source.autoClose; 33767 33768 return this; 33769 33770 } 33771 33772 toJSON() { 33773 33774 const data = super.toJSON(); 33775 33776 data.autoClose = this.autoClose; 33777 data.curves = []; 33778 33779 for ( let i = 0, l = this.curves.length; i < l; i ++ ) { 33780 33781 const curve = this.curves[ i ]; 33782 data.curves.push( curve.toJSON() ); 33783 33784 } 33785 33786 return data; 33787 33788 } 33789 33790 fromJSON( json ) { 33791 33792 super.fromJSON( json ); 33793 33794 this.autoClose = json.autoClose; 33795 this.curves = []; 33796 33797 for ( let i = 0, l = json.curves.length; i < l; i ++ ) { 33798 33799 const curve = json.curves[ i ]; 33800 this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); 33801 33802 } 33803 33804 return this; 33805 33806 } 33807 33808} 33809 33810/** 33811 * A 2D path representation. The class provides methods for creating paths 33812 * and contours of 2D shapes similar to the 2D Canvas API. 33813 * 33814 * ```js 33815 * const path = new THREE.Path(); 33816 * 33817 * path.lineTo( 0, 0.8 ); 33818 * path.quadraticCurveTo( 0, 1, 0.2, 1 ); 33819 * path.lineTo( 1, 1 ); 33820 * 33821 * const points = path.getPoints(); 33822 * 33823 * const geometry = new THREE.BufferGeometry().setFromPoints( points ); 33824 * const material = new THREE.LineBasicMaterial( { color: 0xffffff } ); 33825 * 33826 * const line = new THREE.Line( geometry, material ); 33827 * scene.add( line ); 33828 * ``` 33829 * 33830 * @augments CurvePath 33831 */ 33832class Path extends CurvePath { 33833 33834 /** 33835 * Constructs a new path. 33836 * 33837 * @param {Array<Vector2>} [points] - An array of 2D points defining the path. 33838 */ 33839 constructor( points ) { 33840 33841 super(); 33842 33843 this.type = 'Path'; 33844 33845 /** 33846 * The current offset of the path. Any new curve added will start here. 33847 * 33848 * @type {Vector2} 33849 */ 33850 this.currentPoint = new Vector2(); 33851 33852 if ( points ) { 33853 33854 this.setFromPoints( points ); 33855 33856 } 33857 33858 } 33859 33860 /** 33861 * Creates a path from the given list of points. The points are added 33862 * to the path as instances of {@link LineCurve}. 33863 * 33864 * @param {Array<Vector2>} points - An array of 2D points. 33865 * @return {Path} A reference to this path. 33866 */ 33867 setFromPoints( points ) { 33868 33869 this.moveTo( points[ 0 ].x, points[ 0 ].y ); 33870 33871 for ( let i = 1, l = points.length; i < l; i ++ ) { 33872 33873 this.lineTo( points[ i ].x, points[ i ].y ); 33874 33875 } 33876 33877 return this; 33878 33879 } 33880 33881 /** 33882 * Moves {@link Path#currentPoint} to the given point. 33883 * 33884 * @param {number} x - The x coordinate. 33885 * @param {number} y - The y coordinate. 33886 * @return {Path} A reference to this path. 33887 */ 33888 moveTo( x, y ) { 33889 33890 this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? 33891 33892 return this; 33893 33894 } 33895 33896 /** 33897 * Adds an instance of {@link LineCurve} to the path by connecting 33898 * the current point with the given one. 33899 * 33900 * @param {number} x - The x coordinate of the end point. 33901 * @param {number} y - The y coordinate of the end point. 33902 * @return {Path} A reference to this path. 33903 */ 33904 lineTo( x, y ) { 33905 33906 const curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); 33907 this.curves.push( curve ); 33908 33909 this.currentPoint.set( x, y ); 33910 33911 return this; 33912 33913 } 33914 33915 /** 33916 * Adds an instance of {@link QuadraticBezierCurve} to the path by connecting 33917 * the current point with the given one. 33918 * 33919 * @param {number} aCPx - The x coordinate of the control point. 33920 * @param {number} aCPy - The y coordinate of the control point. 33921 * @param {number} aX - The x coordinate of the end point. 33922 * @param {number} aY - The y coordinate of the end point. 33923 * @return {Path} A reference to this path. 33924 */ 33925 quadraticCurveTo( aCPx, aCPy, aX, aY ) { 33926 33927 const curve = new QuadraticBezierCurve( 33928 this.currentPoint.clone(), 33929 new Vector2( aCPx, aCPy ), 33930 new Vector2( aX, aY ) 33931 ); 33932 33933 this.curves.push( curve ); 33934 33935 this.currentPoint.set( aX, aY ); 33936 33937 return this; 33938 33939 } 33940 33941 /** 33942 * Adds an instance of {@link CubicBezierCurve} to the path by connecting 33943 * the current point with the given one. 33944 * 33945 * @param {number} aCP1x - The x coordinate of the first control point. 33946 * @param {number} aCP1y - The y coordinate of the first control point. 33947 * @param {number} aCP2x - The x coordinate of the second control point. 33948 * @param {number} aCP2y - The y coordinate of the second control point. 33949 * @param {number} aX - The x coordinate of the end point. 33950 * @param {number} aY - The y coordinate of the end point. 33951 * @return {Path} A reference to this path. 33952 */ 33953 bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { 33954 33955 const curve = new CubicBezierCurve( 33956 this.currentPoint.clone(), 33957 new Vector2( aCP1x, aCP1y ), 33958 new Vector2( aCP2x, aCP2y ), 33959 new Vector2( aX, aY ) 33960 ); 33961 33962 this.curves.push( curve ); 33963 33964 this.currentPoint.set( aX, aY ); 33965 33966 return this; 33967 33968 } 33969 33970 /** 33971 * Adds an instance of {@link SplineCurve} to the path by connecting 33972 * the current point with the given list of points. 33973 * 33974 * @param {Array<Vector2>} pts - An array of points in 2D space. 33975 * @return {Path} A reference to this path. 33976 */ 33977 splineThru( pts ) { 33978 33979 const npts = [ this.currentPoint.clone() ].concat( pts ); 33980 33981 const curve = new SplineCurve( npts ); 33982 this.curves.push( curve ); 33983 33984 this.currentPoint.copy( pts[ pts.length - 1 ] ); 33985 33986 return this; 33987 33988 } 33989 33990 /** 33991 * Adds an arc as an instance of {@link EllipseCurve} to the path, positioned relative 33992 * to the current point. 33993 * 33994 * @param {number} [aX=0] - The x coordinate of the center of the arc offsetted from the previous curve. 33995 * @param {number} [aY=0] - The y coordinate of the center of the arc offsetted from the previous curve. 33996 * @param {number} [aRadius=1] - The radius of the arc. 33997 * @param {number} [aStartAngle=0] - The start angle in radians. 33998 * @param {number} [aEndAngle=Math.PI*2] - The end angle in radians. 33999 * @param {boolean} [aClockwise=false] - Whether to sweep the arc clockwise or not. 34000 * @return {Path} A reference to this path. 34001 */ 34002 arc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { 34003 34004 const x0 = this.currentPoint.x; 34005 const y0 = this.currentPoint.y; 34006 34007 this.absarc( aX + x0, aY + y0, aRadius, 34008 aStartAngle, aEndAngle, aClockwise ); 34009 34010 return this; 34011 34012 } 34013 34014 /** 34015 * Adds an absolutely positioned arc as an instance of {@link EllipseCurve} to the path. 34016 * 34017 * @param {number} [aX=0] - The x coordinate of the center of the arc. 34018 * @param {number} [aY=0] - The y coordinate of the center of the arc. 34019 * @param {number} [aRadius=1] - The radius of the arc. 34020 * @param {number} [aStartAngle=0] - The start angle in radians. 34021 * @param {number} [aEndAngle=Math.PI*2] - The end angle in radians. 34022 * @param {boolean} [aClockwise=false] - Whether to sweep the arc clockwise or not. 34023 * @return {Path} A reference to this path. 34024 */ 34025 absarc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { 34026 34027 this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); 34028 34029 return this; 34030 34031 } 34032 34033 /** 34034 * Adds an ellipse as an instance of {@link EllipseCurve} to the path, positioned relative 34035 * to the current point 34036 * 34037 * @param {number} [aX=0] - The x coordinate of the center of the ellipse offsetted from the previous curve. 34038 * @param {number} [aY=0] - The y coordinate of the center of the ellipse offsetted from the previous curve. 34039 * @param {number} [xRadius=1] - The radius of the ellipse in the x axis. 34040 * @param {number} [yRadius=1] - The radius of the ellipse in the y axis. 34041 * @param {number} [aStartAngle=0] - The start angle in radians. 34042 * @param {number} [aEndAngle=Math.PI*2] - The end angle in radians. 34043 * @param {boolean} [aClockwise=false] - Whether to sweep the ellipse clockwise or not. 34044 * @param {number} [aRotation=0] - The rotation angle of the ellipse in radians, counterclockwise from the positive X axis. 34045 * @return {Path} A reference to this path. 34046 */ 34047 ellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { 34048 34049 const x0 = this.currentPoint.x; 34050 const y0 = this.currentPoint.y; 34051 34052 this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); 34053 34054 return this; 34055 34056 } 34057 34058 /** 34059 * Adds an absolutely positioned ellipse as an instance of {@link EllipseCurve} to the path. 34060 * 34061 * @param {number} [aX=0] - The x coordinate of the absolute center of the ellipse. 34062 * @param {number} [aY=0] - The y coordinate of the absolute center of the ellipse. 34063 * @param {number} [xRadius=1] - The radius of the ellipse in the x axis. 34064 * @param {number} [yRadius=1] - The radius of the ellipse in the y axis. 34065 * @param {number} [aStartAngle=0] - The start angle in radians. 34066 * @param {number} [aEndAngle=Math.PI*2] - The end angle in radians. 34067 * @param {boolean} [aClockwise=false] - Whether to sweep the ellipse clockwise or not. 34068 * @param {number} [aRotation=0] - The rotation angle of the ellipse in radians, counterclockwise from the positive X axis. 34069 * @return {Path} A reference to this path. 34070 */ 34071 absellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { 34072 34073 const curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); 34074 34075 if ( this.curves.length > 0 ) { 34076 34077 // if a previous curve is present, attempt to join 34078 const firstPoint = curve.getPoint( 0 ); 34079 34080 if ( ! firstPoint.equals( this.currentPoint ) ) { 34081 34082 this.lineTo( firstPoint.x, firstPoint.y ); 34083 34084 } 34085 34086 } 34087 34088 this.curves.push( curve ); 34089 34090 const lastPoint = curve.getPoint( 1 ); 34091 this.currentPoint.copy( lastPoint ); 34092 34093 return this; 34094 34095 } 34096 34097 copy( source ) { 34098 34099 super.copy( source ); 34100 34101 this.currentPoint.copy( source.currentPoint ); 34102 34103 return this; 34104 34105 } 34106 34107 toJSON() { 34108 34109 const data = super.toJSON(); 34110 34111 data.currentPoint = this.currentPoint.toArray(); 34112 34113 return data; 34114 34115 } 34116 34117 fromJSON( json ) { 34118 34119 super.fromJSON( json ); 34120 34121 this.currentPoint.fromArray( json.currentPoint ); 34122 34123 return this; 34124 34125 } 34126 34127} 34128 34129/** 34130 * Defines an arbitrary 2d shape plane using paths with optional holes. It 34131 * can be used with {@link ExtrudeGeometry}, {@link ShapeGeometry}, to get 34132 * points, or to get triangulated faces. 34133 * 34134 * ```js 34135 * const heartShape = new THREE.Shape(); 34136 * 34137 * heartShape.moveTo( 25, 25 ); 34138 * heartShape.bezierCurveTo( 25, 25, 20, 0, 0, 0 ); 34139 * heartShape.bezierCurveTo( - 30, 0, - 30, 35, - 30, 35 ); 34140 * heartShape.bezierCurveTo( - 30, 55, - 10, 77, 25, 95 ); 34141 * heartShape.bezierCurveTo( 60, 77, 80, 55, 80, 35 ); 34142 * heartShape.bezierCurveTo( 80, 35, 80, 0, 50, 0 ); 34143 * heartShape.bezierCurveTo( 35, 0, 25, 25, 25, 25 ); 34144 * 34145 * const extrudeSettings = { 34146 * depth: 8, 34147 * bevelEnabled: true, 34148 * bevelSegments: 2, 34149 * steps: 2, 34150 * bevelSize: 1, 34151 * bevelThickness: 1 34152 * }; 34153 * 34154 * const geometry = new THREE.ExtrudeGeometry( heartShape, extrudeSettings ); 34155 * const mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial() ); 34156 * ``` 34157 * 34158 * @augments Path 34159 */ 34160class Shape extends Path { 34161 34162 /** 34163 * Constructs a new shape. 34164 * 34165 * @param {Array<Vector2>} [points] - An array of 2D points defining the shape. 34166 */ 34167 constructor( points ) { 34168 34169 super( points ); 34170 34171 /** 34172 * The UUID of the shape. 34173 * 34174 * @type {string} 34175 * @readonly 34176 */ 34177 this.uuid = generateUUID(); 34178 34179 this.type = 'Shape'; 34180 34181 /** 34182 * Defines the holes in the shape. Hole definitions must use the 34183 * opposite winding order (CW/CCW) than the outer shape. 34184 * 34185 * @type {Array<Path>} 34186 * @readonly 34187 */ 34188 this.holes = []; 34189 34190 } 34191 34192 /** 34193 * Returns an array representing each contour of the holes 34194 * as a list of 2D points. 34195 * 34196 * @param {number} divisions - The fineness of the result. 34197 * @return {Array<Array<Vector2>>} The holes as a series of 2D points. 34198 */ 34199 getPointsHoles( divisions ) { 34200 34201 const holesPts = []; 34202 34203 for ( let i = 0, l = this.holes.length; i < l; i ++ ) { 34204 34205 holesPts[ i ] = this.holes[ i ].getPoints( divisions ); 34206 34207 } 34208 34209 return holesPts; 34210 34211 } 34212 34213 // get points of shape and holes (keypoints based on segments parameter) 34214 34215 /** 34216 * Returns an object that holds contour data for the shape and its holes as 34217 * arrays of 2D points. 34218 * 34219 * @param {number} divisions - The fineness of the result. 34220 * @return {{shape:Array<Vector2>,holes:Array<Array<Vector2>>}} An object with contour data. 34221 */ 34222 extractPoints( divisions ) { 34223 34224 return { 34225 34226 shape: this.getPoints( divisions ), 34227 holes: this.getPointsHoles( divisions ) 34228 34229 }; 34230 34231 } 34232 34233 copy( source ) { 34234 34235 super.copy( source ); 34236 34237 this.holes = []; 34238 34239 for ( let i = 0, l = source.holes.length; i < l; i ++ ) { 34240 34241 const hole = source.holes[ i ]; 34242 34243 this.holes.push( hole.clone() ); 34244 34245 } 34246 34247 return this; 34248 34249 } 34250 34251 toJSON() { 34252 34253 const data = super.toJSON(); 34254 34255 data.uuid = this.uuid; 34256 data.holes = []; 34257 34258 for ( let i = 0, l = this.holes.length; i < l; i ++ ) { 34259 34260 const hole = this.holes[ i ]; 34261 data.holes.push( hole.toJSON() ); 34262 34263 } 34264 34265 return data; 34266 34267 } 34268 34269 fromJSON( json ) { 34270 34271 super.fromJSON( json ); 34272 34273 this.uuid = json.uuid; 34274 this.holes = []; 34275 34276 for ( let i = 0, l = json.holes.length; i < l; i ++ ) { 34277 34278 const hole = json.holes[ i ]; 34279 this.holes.push( new Path().fromJSON( hole ) ); 34280 34281 } 34282 34283 return this; 34284 34285 } 34286 34287} 34288 34289/* eslint-disable */ 34290// copy of mapbox/earcut version 3.0.2 34291// https://github.com/mapbox/earcut/tree/v3.0.2 34292 34293function earcut(data, holeIndices, dim = 2) { 34294 34295 const hasHoles = holeIndices && holeIndices.length; 34296 const outerLen = hasHoles ? holeIndices[0] * dim : data.length; 34297 let outerNode = linkedList(data, 0, outerLen, dim, true); 34298 const triangles = []; 34299 34300 if (!outerNode || outerNode.next === outerNode.prev) return triangles; 34301 34302 let minX, minY, invSize; 34303 34304 if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); 34305 34306 // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox 34307 if (data.length > 80 * dim) { 34308 minX = data[0]; 34309 minY = data[1]; 34310 let maxX = minX; 34311 let maxY = minY; 34312 34313 for (let i = dim; i < outerLen; i += dim) { 34314 const x = data[i]; 34315 const y = data[i + 1]; 34316 if (x < minX) minX = x; 34317 if (y < minY) minY = y; 34318 if (x > maxX) maxX = x; 34319 if (y > maxY) maxY = y; 34320 } 34321 34322 // minX, minY and invSize are later used to transform coords into integers for z-order calculation 34323 invSize = Math.max(maxX - minX, maxY - minY); 34324 invSize = invSize !== 0 ? 32767 / invSize : 0; 34325 } 34326 34327 earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0); 34328 34329 return triangles; 34330} 34331 34332// create a circular doubly linked list from polygon points in the specified winding order 34333function linkedList(data, start, end, dim, clockwise) { 34334 let last; 34335 34336 if (clockwise === (signedArea(data, start, end, dim) > 0)) { 34337 for (let i = start; i < end; i += dim) last = insertNode(i / dim | 0, data[i], data[i + 1], last); 34338 } else { 34339 for (let i = end - dim; i >= start; i -= dim) last = insertNode(i / dim | 0, data[i], data[i + 1], last); 34340 } 34341 34342 if (last && equals(last, last.next)) { 34343 removeNode(last); 34344 last = last.next; 34345 } 34346 34347 return last; 34348} 34349 34350// eliminate colinear or duplicate points 34351function filterPoints(start, end) { 34352 if (!start) return start; 34353 if (!end) end = start; 34354 34355 let p = start, 34356 again; 34357 do { 34358 again = false; 34359 34360 if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) { 34361 removeNode(p); 34362 p = end = p.prev; 34363 if (p === p.next) break; 34364 again = true; 34365 34366 } else { 34367 p = p.next; 34368 } 34369 } while (again || p !== end); 34370 34371 return end; 34372} 34373 34374// main ear slicing loop which triangulates a polygon (given as a linked list) 34375function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) { 34376 if (!ear) return; 34377 34378 // interlink polygon nodes in z-order 34379 if (!pass && invSize) indexCurve(ear, minX, minY, invSize); 34380 34381 let stop = ear; 34382 34383 // iterate through ears, slicing them one by one 34384 while (ear.prev !== ear.next) { 34385 const prev = ear.prev; 34386 const next = ear.next; 34387 34388 if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) { 34389 triangles.push(prev.i, ear.i, next.i); // cut off the triangle 34390 34391 removeNode(ear); 34392 34393 // skipping the next vertex leads to less sliver triangles 34394 ear = next.next; 34395 stop = next.next; 34396 34397 continue; 34398 } 34399 34400 ear = next; 34401 34402 // if we looped through the whole remaining polygon and can't find any more ears 34403 if (ear === stop) { 34404 // try filtering points and slicing again 34405 if (!pass) { 34406 earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); 34407 34408 // if this didn't work, try curing all small self-intersections locally 34409 } else if (pass === 1) { 34410 ear = cureLocalIntersections(filterPoints(ear), triangles); 34411 earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); 34412 34413 // as a last resort, try splitting the remaining polygon into two 34414 } else if (pass === 2) { 34415 splitEarcut(ear, triangles, dim, minX, minY, invSize); 34416 } 34417 34418 break; 34419 } 34420 } 34421} 34422 34423// check whether a polygon node forms a valid ear with adjacent nodes 34424function isEar(ear) { 34425 const a = ear.prev, 34426 b = ear, 34427 c = ear.next; 34428 34429 if (area(a, b, c) >= 0) return false; // reflex, can't be an ear 34430 34431 // now make sure we don't have other points inside the potential ear 34432 const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; 34433 34434 // triangle bbox 34435 const x0 = Math.min(ax, bx, cx), 34436 y0 = Math.min(ay, by, cy), 34437 x1 = Math.max(ax, bx, cx), 34438 y1 = Math.max(ay, by, cy); 34439 34440 let p = c.next; 34441 while (p !== a) { 34442 if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && 34443 pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && 34444 area(p.prev, p, p.next) >= 0) return false; 34445 p = p.next; 34446 } 34447 34448 return true; 34449} 34450 34451function isEarHashed(ear, minX, minY, invSize) { 34452 const a = ear.prev, 34453 b = ear, 34454 c = ear.next; 34455 34456 if (area(a, b, c) >= 0) return false; // reflex, can't be an ear 34457 34458 const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; 34459 34460 // triangle bbox 34461 const x0 = Math.min(ax, bx, cx), 34462 y0 = Math.min(ay, by, cy), 34463 x1 = Math.max(ax, bx, cx), 34464 y1 = Math.max(ay, by, cy); 34465 34466 // z-order range for the current triangle bbox; 34467 const minZ = zOrder(x0, y0, minX, minY, invSize), 34468 maxZ = zOrder(x1, y1, minX, minY, invSize); 34469 34470 let p = ear.prevZ, 34471 n = ear.nextZ; 34472 34473 // look for points inside the triangle in both directions 34474 while (p && p.z >= minZ && n && n.z <= maxZ) { 34475 if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && 34476 pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; 34477 p = p.prevZ; 34478 34479 if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && 34480 pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; 34481 n = n.nextZ; 34482 } 34483 34484 // look for remaining points in decreasing z-order 34485 while (p && p.z >= minZ) { 34486 if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && 34487 pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; 34488 p = p.prevZ; 34489 } 34490 34491 // look for remaining points in increasing z-order 34492 while (n && n.z <= maxZ) { 34493 if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && 34494 pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; 34495 n = n.nextZ; 34496 } 34497 34498 return true; 34499} 34500 34501// go through all polygon nodes and cure small local self-intersections 34502function cureLocalIntersections(start, triangles) { 34503 let p = start; 34504 do { 34505 const a = p.prev, 34506 b = p.next.next; 34507 34508 if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) { 34509 34510 triangles.push(a.i, p.i, b.i); 34511 34512 // remove two nodes involved 34513 removeNode(p); 34514 removeNode(p.next); 34515 34516 p = start = b; 34517 } 34518 p = p.next; 34519 } while (p !== start); 34520 34521 return filterPoints(p); 34522} 34523 34524// try splitting polygon into two and triangulate them independently 34525function splitEarcut(start, triangles, dim, minX, minY, invSize) { 34526 // look for a valid diagonal that divides the polygon into two 34527 let a = start; 34528 do { 34529 let b = a.next.next; 34530 while (b !== a.prev) { 34531 if (a.i !== b.i && isValidDiagonal(a, b)) { 34532 // split the polygon in two by the diagonal 34533 let c = splitPolygon(a, b); 34534 34535 // filter colinear points around the cuts 34536 a = filterPoints(a, a.next); 34537 c = filterPoints(c, c.next); 34538 34539 // run earcut on each half 34540 earcutLinked(a, triangles, dim, minX, minY, invSize, 0); 34541 earcutLinked(c, triangles, dim, minX, minY, invSize, 0); 34542 return; 34543 } 34544 b = b.next; 34545 } 34546 a = a.next; 34547 } while (a !== start); 34548} 34549 34550// link every hole into the outer loop, producing a single-ring polygon without holes 34551function eliminateHoles(data, holeIndices, outerNode, dim) { 34552 const queue = []; 34553 34554 for (let i = 0, len = holeIndices.length; i < len; i++) { 34555 const start = holeIndices[i] * dim; 34556 const end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; 34557 const list = linkedList(data, start, end, dim, false); 34558 if (list === list.next) list.steiner = true; 34559 queue.push(getLeftmost(list)); 34560 } 34561 34562 queue.sort(compareXYSlope); 34563 34564 // process holes from left to right 34565 for (let i = 0; i < queue.length; i++) { 34566 outerNode = eliminateHole(queue[i], outerNode); 34567 } 34568 34569 return outerNode; 34570} 34571 34572function compareXYSlope(a, b) { 34573 let result = a.x - b.x; 34574 // when the left-most point of 2 holes meet at a vertex, sort the holes counterclockwise so that when we find 34575 // the bridge to the outer shell is always the point that they meet at. 34576 if (result === 0) { 34577 result = a.y - b.y; 34578 if (result === 0) { 34579 const aSlope = (a.next.y - a.y) / (a.next.x - a.x); 34580 const bSlope = (b.next.y - b.y) / (b.next.x - b.x); 34581 result = aSlope - bSlope; 34582 } 34583 } 34584 return result; 34585} 34586 34587// find a bridge between vertices that connects hole with an outer ring and link it 34588function eliminateHole(hole, outerNode) { 34589 const bridge = findHoleBridge(hole, outerNode); 34590 if (!bridge) { 34591 return outerNode; 34592 } 34593 34594 const bridgeReverse = splitPolygon(bridge, hole); 34595 34596 // filter collinear points around the cuts 34597 filterPoints(bridgeReverse, bridgeReverse.next); 34598 return filterPoints(bridge, bridge.next); 34599} 34600 34601// David Eberly's algorithm for finding a bridge between hole and outer polygon 34602function findHoleBridge(hole, outerNode) { 34603 let p = outerNode; 34604 const hx = hole.x; 34605 const hy = hole.y; 34606 let qx = -Infinity; 34607 let m; 34608 34609 // find a segment intersected by a ray from the hole's leftmost point to the left; 34610 // segment's endpoint with lesser x will be potential connection point 34611 // unless they intersect at a vertex, then choose the vertex 34612 if (equals(hole, p)) return p; 34613 do { 34614 if (equals(hole, p.next)) return p.next; 34615 else if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) { 34616 const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y); 34617 if (x <= hx && x > qx) { 34618 qx = x; 34619 m = p.x < p.next.x ? p : p.next; 34620 if (x === hx) return m; // hole touches outer segment; pick leftmost endpoint 34621 } 34622 } 34623 p = p.next; 34624 } while (p !== outerNode); 34625 34626 if (!m) return null; 34627 34628 // look for points inside the triangle of hole point, segment intersection and endpoint; 34629 // if there are no points found, we have a valid connection; 34630 // otherwise choose the point of the minimum angle with the ray as connection point 34631 34632 const stop = m; 34633 const mx = m.x; 34634 const my = m.y; 34635 let tanMin = Infinity; 34636 34637 p = m; 34638 34639 do { 34640 if (hx >= p.x && p.x >= mx && hx !== p.x && 34641 pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) { 34642 34643 const tan = Math.abs(hy - p.y) / (hx - p.x); // tangential 34644 34645 if (locallyInside(p, hole) && 34646 (tan < tanMin || (tan === tanMin && (p.x > m.x || (p.x === m.x && sectorContainsSector(m, p)))))) { 34647 m = p; 34648 tanMin = tan; 34649 } 34650 } 34651 34652 p = p.next; 34653 } while (p !== stop); 34654 34655 return m; 34656} 34657 34658// whether sector in vertex m contains sector in vertex p in the same coordinates 34659function sectorContainsSector(m, p) { 34660 return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0; 34661} 34662 34663// interlink polygon nodes in z-order 34664function indexCurve(start, minX, minY, invSize) { 34665 let p = start; 34666 do { 34667 if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize); 34668 p.prevZ = p.prev; 34669 p.nextZ = p.next; 34670 p = p.next; 34671 } while (p !== start); 34672 34673 p.prevZ.nextZ = null; 34674 p.prevZ = null; 34675 34676 sortLinked(p); 34677} 34678 34679// Simon Tatham's linked list merge sort algorithm 34680// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html 34681function sortLinked(list) { 34682 let numMerges; 34683 let inSize = 1; 34684 34685 do { 34686 let p = list; 34687 let e; 34688 list = null; 34689 let tail = null; 34690 numMerges = 0; 34691 34692 while (p) { 34693 numMerges++; 34694 let q = p; 34695 let pSize = 0; 34696 for (let i = 0; i < inSize; i++) { 34697 pSize++; 34698 q = q.nextZ; 34699 if (!q) break; 34700 } 34701 let qSize = inSize; 34702 34703 while (pSize > 0 || (qSize > 0 && q)) { 34704 34705 if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) { 34706 e = p; 34707 p = p.nextZ; 34708 pSize--; 34709 } else { 34710 e = q; 34711 q = q.nextZ; 34712 qSize--; 34713 } 34714 34715 if (tail) tail.nextZ = e; 34716 else list = e; 34717 34718 e.prevZ = tail; 34719 tail = e; 34720 } 34721 34722 p = q; 34723 } 34724 34725 tail.nextZ = null; 34726 inSize *= 2; 34727 34728 } while (numMerges > 1); 34729 34730 return list; 34731} 34732 34733// z-order of a point given coords and inverse of the longer side of data bbox 34734function zOrder(x, y, minX, minY, invSize) { 34735 // coords are transformed into non-negative 15-bit integer range 34736 x = (x - minX) * invSize | 0; 34737 y = (y - minY) * invSize | 0; 34738 34739 x = (x | (x << 8)) & 0x00FF00FF; 34740 x = (x | (x << 4)) & 0x0F0F0F0F; 34741 x = (x | (x << 2)) & 0x33333333; 34742 x = (x | (x << 1)) & 0x55555555; 34743 34744 y = (y | (y << 8)) & 0x00FF00FF; 34745 y = (y | (y << 4)) & 0x0F0F0F0F; 34746 y = (y | (y << 2)) & 0x33333333; 34747 y = (y | (y << 1)) & 0x55555555; 34748 34749 return x | (y << 1); 34750} 34751 34752// find the leftmost node of a polygon ring 34753function getLeftmost(start) { 34754 let p = start, 34755 leftmost = start; 34756 do { 34757 if (p.x < leftmost.x || (p.x === leftmost.x && p.y < leftmost.y)) leftmost = p; 34758 p = p.next; 34759 } while (p !== start); 34760 34761 return leftmost; 34762} 34763 34764// check if a point lies within a convex triangle 34765function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { 34766 return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && 34767 (ax - px) * (by - py) >= (bx - px) * (ay - py) && 34768 (bx - px) * (cy - py) >= (cx - px) * (by - py); 34769} 34770 34771// check if a point lies within a convex triangle but false if its equal to the first point of the triangle 34772function pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, px, py) { 34773 return !(ax === px && ay === py) && pointInTriangle(ax, ay, bx, by, cx, cy, px, py); 34774} 34775 34776// check if a diagonal between two polygon nodes is valid (lies in polygon interior) 34777function isValidDiagonal(a, b) { 34778 return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // doesn't intersect other edges 34779 (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible 34780 (area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors 34781 equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case 34782} 34783 34784// signed area of a triangle 34785function area(p, q, r) { 34786 return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); 34787} 34788 34789// check if two points are equal 34790function equals(p1, p2) { 34791 return p1.x === p2.x && p1.y === p2.y; 34792} 34793 34794// check if two segments intersect 34795function intersects(p1, q1, p2, q2) { 34796 const o1 = sign(area(p1, q1, p2)); 34797 const o2 = sign(area(p1, q1, q2)); 34798 const o3 = sign(area(p2, q2, p1)); 34799 const o4 = sign(area(p2, q2, q1)); 34800 34801 if (o1 !== o2 && o3 !== o4) return true; // general case 34802 34803 if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 34804 if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 34805 if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 34806 if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 34807 34808 return false; 34809} 34810 34811// for collinear points p, q, r, check if point q lies on segment pr 34812function onSegment(p, q, r) { 34813 return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y); 34814} 34815 34816function sign(num) { 34817 return num > 0 ? 1 : num < 0 ? -1 : 0; 34818} 34819 34820// check if a polygon diagonal intersects any polygon segments 34821function intersectsPolygon(a, b) { 34822 let p = a; 34823 do { 34824 if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && 34825 intersects(p, p.next, a, b)) return true; 34826 p = p.next; 34827 } while (p !== a); 34828 34829 return false; 34830} 34831 34832// check if a polygon diagonal is locally inside the polygon 34833function locallyInside(a, b) { 34834 return area(a.prev, a, a.next) < 0 ? 34835 area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : 34836 area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; 34837} 34838 34839// check if the middle point of a polygon diagonal is inside the polygon 34840function middleInside(a, b) { 34841 let p = a; 34842 let inside = false; 34843 const px = (a.x + b.x) / 2; 34844 const py = (a.y + b.y) / 2; 34845 do { 34846 if (((p.y > py) !== (p.next.y > py)) && p.next.y !== p.y && 34847 (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x)) 34848 inside = !inside; 34849 p = p.next; 34850 } while (p !== a); 34851 34852 return inside; 34853} 34854 34855// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; 34856// if one belongs to the outer ring and another to a hole, it merges it into a single ring 34857function splitPolygon(a, b) { 34858 const a2 = createNode(a.i, a.x, a.y), 34859 b2 = createNode(b.i, b.x, b.y), 34860 an = a.next, 34861 bp = b.prev; 34862 34863 a.next = b; 34864 b.prev = a; 34865 34866 a2.next = an; 34867 an.prev = a2; 34868 34869 b2.next = a2; 34870 a2.prev = b2; 34871 34872 bp.next = b2; 34873 b2.prev = bp; 34874 34875 return b2; 34876} 34877 34878// create a node and optionally link it with previous one (in a circular doubly linked list) 34879function insertNode(i, x, y, last) { 34880 const p = createNode(i, x, y); 34881 34882 if (!last) { 34883 p.prev = p; 34884 p.next = p; 34885 34886 } else { 34887 p.next = last.next; 34888 p.prev = last; 34889 last.next.prev = p; 34890 last.next = p; 34891 } 34892 return p; 34893} 34894 34895function removeNode(p) { 34896 p.next.prev = p.prev; 34897 p.prev.next = p.next; 34898 34899 if (p.prevZ) p.prevZ.nextZ = p.nextZ; 34900 if (p.nextZ) p.nextZ.prevZ = p.prevZ; 34901} 34902 34903function createNode(i, x, y) { 34904 return { 34905 i, // vertex index in coordinates array 34906 x, y, // vertex coordinates 34907 prev: null, // previous and next vertex nodes in a polygon ring 34908 next: null, 34909 z: 0, // z-order curve value 34910 prevZ: null, // previous and next nodes in z-order 34911 nextZ: null, 34912 steiner: false // indicates whether this is a steiner point 34913 }; 34914} 34915 34916function signedArea(data, start, end, dim) { 34917 let sum = 0; 34918 for (let i = start, j = end - dim; i < end; i += dim) { 34919 sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]); 34920 j = i; 34921 } 34922 return sum; 34923} 34924 34925/** 34926 * An implementation of the earcut polygon triangulation algorithm. 34927 * The code is a port of [mapbox/earcut](https://github.com/mapbox/earcut). 34928 * 34929 * @see https://github.com/mapbox/earcut 34930 */ 34931class Earcut { 34932 34933 /** 34934 * Triangulates the given shape definition by returning an array of triangles. 34935 * 34936 * @param {Array<number>} data - An array with 2D points. 34937 * @param {Array<number>} holeIndices - An array with indices defining holes. 34938 * @param {number} [dim=2] - The number of coordinates per vertex in the input array. 34939 * @return {Array<number>} An array representing the triangulated faces. Each face is defined by three consecutive numbers 34940 * representing vertex indices. 34941 */ 34942 static triangulate( data, holeIndices, dim = 2 ) { 34943 34944 return earcut( data, holeIndices, dim ); 34945 34946 } 34947 34948} 34949 34950/** 34951 * A class containing utility functions for shapes. 34952 * 34953 * @hideconstructor 34954 */ 34955class ShapeUtils { 34956 34957 /** 34958 * Calculate area of a ( 2D ) contour polygon. 34959 * 34960 * @param {Array<Vector2>} contour - An array of 2D points. 34961 * @return {number} The area. 34962 */ 34963 static area( contour ) { 34964 34965 const n = contour.length; 34966 let a = 0.0; 34967 34968 for ( let p = n - 1, q = 0; q < n; p = q ++ ) { 34969 34970 a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; 34971 34972 } 34973 34974 return a * 0.5; 34975 34976 } 34977 34978 /** 34979 * Returns `true` if the given contour uses a clockwise winding order. 34980 * 34981 * @param {Array<Vector2>} pts - An array of 2D points defining a polygon. 34982 * @return {boolean} Whether the given contour uses a clockwise winding order or not. 34983 */ 34984 static isClockWise( pts ) { 34985 34986 return ShapeUtils.area( pts ) < 0; 34987 34988 } 34989 34990 /** 34991 * Triangulates the given shape definition. 34992 * 34993 * @param {Array<Vector2>} contour - An array of 2D points defining the contour. 34994 * @param {Array<Array<Vector2>>} holes - An array that holds arrays of 2D points defining the holes. 34995 * @return {Array<Array<number>>} An array that holds for each face definition an array with three indices. 34996 */ 34997 static triangulateShape( contour, holes ) { 34998 34999 const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] 35000 const holeIndices = []; // array of hole indices 35001 const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] 35002 35003 removeDupEndPts( contour ); 35004 addContour( vertices, contour ); 35005 35006 // 35007 35008 let holeIndex = contour.length; 35009 35010 holes.forEach( removeDupEndPts ); 35011 35012 for ( let i = 0; i < holes.length; i ++ ) { 35013 35014 holeIndices.push( holeIndex ); 35015 holeIndex += holes[ i ].length; 35016 addContour( vertices, holes[ i ] ); 35017 35018 } 35019 35020 // 35021 35022 const triangles = Earcut.triangulate( vertices, holeIndices ); 35023 35024 // 35025 35026 for ( let i = 0; i < triangles.length; i += 3 ) { 35027 35028 faces.push( triangles.slice( i, i + 3 ) ); 35029 35030 } 35031 35032 return faces; 35033 35034 } 35035 35036} 35037 35038function removeDupEndPts( points ) { 35039 35040 const l = points.length; 35041 35042 if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { 35043 35044 points.pop(); 35045 35046 } 35047 35048} 35049 35050function addContour( vertices, contour ) { 35051 35052 for ( let i = 0; i < contour.length; i ++ ) { 35053 35054 vertices.push( contour[ i ].x ); 35055 vertices.push( contour[ i ].y ); 35056 35057 } 35058 35059} 35060 35061/** 35062 * Creates extruded geometry from a path shape. 35063 * 35064 * ```js 35065 * const length = 12, width = 8; 35066 * 35067 * const shape = new THREE.Shape(); 35068 * shape.moveTo( 0,0 ); 35069 * shape.lineTo( 0, width ); 35070 * shape.lineTo( length, width ); 35071 * shape.lineTo( length, 0 ); 35072 * shape.lineTo( 0, 0 ); 35073 * 35074 * const geometry = new THREE.ExtrudeGeometry( shape ); 35075 * const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); 35076 * const mesh = new THREE.Mesh( geometry, material ) ; 35077 * scene.add( mesh ); 35078 * ``` 35079 * 35080 * @augments BufferGeometry 35081 * @demo scenes/geometry-browser.html#ExtrudeGeometry 35082 */ 35083class ExtrudeGeometry extends BufferGeometry { 35084 35085 /** 35086 * Constructs a new extrude geometry. 35087 * 35088 * @param {Shape|Array<Shape>} [shapes] - A shape or an array of shapes. 35089 * @param {ExtrudeGeometry~Options} [options] - The extrude settings. 35090 */ 35091 constructor( shapes = new Shape( [ new Vector2( 0.5, 0.5 ), new Vector2( -0.5, 0.5 ), new Vector2( -0.5, -0.5 ), new Vector2( 0.5, -0.5 ) ] ), options = {} ) { 35092 35093 super(); 35094 35095 this.type = 'ExtrudeGeometry'; 35096 35097 /** 35098 * Holds the constructor parameters that have been 35099 * used to generate the geometry. Any modification 35100 * after instantiation does not change the geometry. 35101 * 35102 * @type {Object} 35103 */ 35104 this.parameters = { 35105 shapes: shapes, 35106 options: options 35107 }; 35108 35109 shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; 35110 35111 const scope = this; 35112 35113 const verticesArray = []; 35114 const uvArray = []; 35115 35116 for ( let i = 0, l = shapes.length; i < l; i ++ ) { 35117 35118 const shape = shapes[ i ]; 35119 addShape( shape ); 35120 35121 } 35122 35123 // build geometry 35124 35125 this.setAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) ); 35126 this.setAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) ); 35127 35128 this.computeVertexNormals(); 35129 35130 // functions 35131 35132 function addShape( shape ) { 35133 35134 const placeholder = []; 35135 35136 // options 35137 35138 const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; 35139 const steps = options.steps !== undefined ? options.steps : 1; 35140 const depth = options.depth !== undefined ? options.depth : 1; 35141 35142 let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; 35143 let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 0.2; 35144 let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 0.1; 35145 let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; 35146 let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; 35147 35148 const extrudePath = options.extrudePath; 35149 35150 const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; 35151 35152 // 35153 35154 let extrudePts, extrudeByPath = false; 35155 let splineTube, binormal, normal, position2; 35156 35157 if ( extrudePath ) { 35158 35159 extrudePts = extrudePath.getSpacedPoints( steps ); 35160 35161 extrudeByPath = true; 35162 bevelEnabled = false; // bevels not supported for path extrusion 35163 35164 // SETUP TNB variables 35165 35166 const isClosed = extrudePath.isCatmullRomCurve3 ? extrudePath.closed : false; 35167 35168 splineTube = extrudePath.computeFrenetFrames( steps, isClosed ); 35169 35170 // log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); 35171 35172 binormal = new Vector3(); 35173 normal = new Vector3(); 35174 position2 = new Vector3(); 35175 35176 } 35177 35178 // Safeguards if bevels are not enabled 35179 35180 if ( ! bevelEnabled ) { 35181 35182 bevelSegments = 0; 35183 bevelThickness = 0; 35184 bevelSize = 0; 35185 bevelOffset = 0; 35186 35187 } 35188 35189 // Variables initialization 35190 35191 const shapePoints = shape.extractPoints( curveSegments ); 35192 35193 let vertices = shapePoints.shape; 35194 const holes = shapePoints.holes; 35195 35196 const reverse = ! ShapeUtils.isClockWise( vertices ); 35197 35198 if ( reverse ) { 35199 35200 vertices = vertices.reverse(); 35201 35202 // Maybe we should also check if holes are in the opposite direction, just to be safe ... 35203 35204 for ( let h = 0, hl = holes.length; h < hl; h ++ ) { 35205 35206 const ahole = holes[ h ]; 35207 35208 if ( ShapeUtils.isClockWise( ahole ) ) { 35209 35210 holes[ h ] = ahole.reverse(); 35211 35212 } 35213 35214 } 35215 35216 } 35217 35218 /**Merges index-adjacent points that are within a threshold distance of each other. Array is modified in-place. Threshold distance is empirical, and scaled based on the magnitude of point coordinates. 35219 * @param {Array<Vector2>} points 35220 */ 35221 function mergeOverlappingPoints( points ) { 35222 35223 const THRESHOLD = 1e-10; 35224 const THRESHOLD_SQ = THRESHOLD * THRESHOLD; 35225 let prevPos = points[ 0 ]; 35226 for ( let i = 1; i <= points.length; i ++ ) { 35227 35228 const currentIndex = i % points.length; 35229 const currentPos = points[ currentIndex ]; 35230 const dx = currentPos.x - prevPos.x; 35231 const dy = currentPos.y - prevPos.y; 35232 const distSq = dx * dx + dy * dy; 35233 35234 const scalingFactorSqrt = Math.max( 35235 Math.abs( currentPos.x ), 35236 Math.abs( currentPos.y ), 35237 Math.abs( prevPos.x ), 35238 Math.abs( prevPos.y ) 35239 ); 35240 const thresholdSqScaled = THRESHOLD_SQ * scalingFactorSqrt * scalingFactorSqrt; 35241 if ( distSq <= thresholdSqScaled ) { 35242 35243 points.splice( currentIndex, 1 ); 35244 i --; 35245 continue; 35246 35247 } 35248 35249 prevPos = currentPos; 35250 35251 } 35252 35253 } 35254 35255 mergeOverlappingPoints( vertices ); 35256 holes.forEach( mergeOverlappingPoints ); 35257 35258 const numHoles = holes.length; 35259 35260 /* Vertices */ 35261 35262 const contour = vertices; // vertices has all points but contour has only points of circumference 35263 35264 for ( let h = 0; h < numHoles; h ++ ) { 35265 35266 const ahole = holes[ h ]; 35267 35268 vertices = vertices.concat( ahole ); 35269 35270 } 35271 35272 35273 function scalePt2( pt, vec, size ) { 35274 35275 if ( ! vec ) error( 'ExtrudeGeometry: vec does not exist' ); 35276 35277 return pt.clone().addScaledVector( vec, size ); 35278 35279 } 35280 35281 const vlen = vertices.length; 35282 35283 35284 // Find directions for point movement 35285 35286 35287 function getBevelVec( inPt, inPrev, inNext ) { 35288 35289 // computes for inPt the corresponding point inPt' on a new contour 35290 // shifted by 1 unit (length of normalized vector) to the left 35291 // if we walk along contour clockwise, this new contour is outside the old one 35292 // 35293 // inPt' is the intersection of the two lines parallel to the two 35294 // adjacent edges of inPt at a distance of 1 unit on the left side. 35295 35296 let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt 35297 35298 // good reading for geometry algorithms (here: line-line intersection) 35299 // http://geomalgorithms.com/a05-_intersect-1.html 35300 35301 const v_prev_x = inPt.x - inPrev.x, 35302 v_prev_y = inPt.y - inPrev.y; 35303 const v_next_x = inNext.x - inPt.x, 35304 v_next_y = inNext.y - inPt.y; 35305 35306 const v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); 35307 35308 // check for collinear edges 35309 const collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); 35310 35311 if ( Math.abs( collinear0 ) > Number.EPSILON ) { 35312 35313 // not collinear 35314 35315 // length of vectors for normalizing 35316 35317 const v_prev_len = Math.sqrt( v_prev_lensq ); 35318 const v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); 35319 35320 // shift adjacent points by unit vectors to the left 35321 35322 const ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); 35323 const ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); 35324 35325 const ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); 35326 const ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); 35327 35328 // scaling factor for v_prev to intersection point 35329 35330 const sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - 35331 ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / 35332 ( v_prev_x * v_next_y - v_prev_y * v_next_x ); 35333 35334 // vector from inPt to intersection point 35335 35336 v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); 35337 v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); 35338 35339 // Don't normalize!, otherwise sharp corners become ugly 35340 // but prevent crazy spikes 35341 const v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); 35342 if ( v_trans_lensq <= 2 ) { 35343 35344 return new Vector2( v_trans_x, v_trans_y ); 35345 35346 } else { 35347 35348 shrink_by = Math.sqrt( v_trans_lensq / 2 ); 35349 35350 } 35351 35352 } else { 35353 35354 // handle special case of collinear edges 35355 35356 let direction_eq = false; // assumes: opposite 35357 35358 if ( v_prev_x > Number.EPSILON ) { 35359 35360 if ( v_next_x > Number.EPSILON ) { 35361 35362 direction_eq = true; 35363 35364 } 35365 35366 } else { 35367 35368 if ( v_prev_x < - Number.EPSILON ) { 35369 35370 if ( v_next_x < - Number.EPSILON ) { 35371 35372 direction_eq = true; 35373 35374 } 35375 35376 } else { 35377 35378 if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { 35379 35380 direction_eq = true; 35381 35382 } 35383 35384 } 35385 35386 } 35387 35388 if ( direction_eq ) { 35389 35390 // log("Warning: lines are a straight sequence"); 35391 v_trans_x = - v_prev_y; 35392 v_trans_y = v_prev_x; 35393 shrink_by = Math.sqrt( v_prev_lensq ); 35394 35395 } else { 35396 35397 // log("Warning: lines are a straight spike"); 35398 v_trans_x = v_prev_x; 35399 v_trans_y = v_prev_y; 35400 shrink_by = Math.sqrt( v_prev_lensq / 2 ); 35401 35402 } 35403 35404 } 35405 35406 return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); 35407 35408 } 35409 35410 35411 const contourMovements = []; 35412 35413 for ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { 35414 35415 if ( j === il ) j = 0; 35416 if ( k === il ) k = 0; 35417 35418 // (j)---(i)---(k) 35419 // log('i,j,k', i, j , k) 35420 35421 contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); 35422 35423 } 35424 35425 const holesMovements = []; 35426 let oneHoleMovements, verticesMovements = contourMovements.concat(); 35427 35428 for ( let h = 0, hl = numHoles; h < hl; h ++ ) { 35429 35430 const ahole = holes[ h ]; 35431 35432 oneHoleMovements = []; 35433 35434 for ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { 35435 35436 if ( j === il ) j = 0; 35437 if ( k === il ) k = 0; 35438 35439 // (j)---(i)---(k) 35440 oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); 35441 35442 } 35443 35444 holesMovements.push( oneHoleMovements ); 35445 verticesMovements = verticesMovements.concat( oneHoleMovements ); 35446 35447 } 35448 35449 let faces; 35450 35451 if ( bevelSegments === 0 ) { 35452 35453 faces = ShapeUtils.triangulateShape( contour, holes ); 35454 35455 } else { 35456 35457 const contractedContourVertices = []; 35458 const expandedHoleVertices = []; 35459 35460 // Loop bevelSegments, 1 for the front, 1 for the back 35461 35462 for ( let b = 0; b < bevelSegments; b ++ ) { 35463 35464 //for ( b = bevelSegments; b > 0; b -- ) { 35465 35466 const t = b / bevelSegments; 35467 const z = bevelThickness * Math.cos( t * Math.PI / 2 ); 35468 const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; 35469 35470 // contract shape 35471 35472 for ( let i = 0, il = contour.length; i < il; i ++ ) { 35473 35474 const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); 35475 35476 v( vert.x, vert.y, - z ); 35477 if ( t === 0 ) contractedContourVertices.push( vert ); 35478 35479 } 35480 35481 // expand holes 35482 35483 for ( let h = 0, hl = numHoles; h < hl; h ++ ) { 35484 35485 const ahole = holes[ h ]; 35486 oneHoleMovements = holesMovements[ h ]; 35487 const oneHoleVertices = []; 35488 for ( let i = 0, il = ahole.length; i < il; i ++ ) { 35489 35490 const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); 35491 35492 v( vert.x, vert.y, - z ); 35493 if ( t === 0 ) oneHoleVertices.push( vert ); 35494 35495 } 35496 35497 if ( t === 0 ) expandedHoleVertices.push( oneHoleVertices ); 35498 35499 } 35500 35501 } 35502 35503 faces = ShapeUtils.triangulateShape( contractedContourVertices, expandedHoleVertices ); 35504 35505 } 35506 35507 const flen = faces.length; 35508 35509 const bs = bevelSize + bevelOffset; 35510 35511 // Back facing vertices 35512 35513 for ( let i = 0; i < vlen; i ++ ) { 35514 35515 const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; 35516 35517 if ( ! extrudeByPath ) { 35518 35519 v( vert.x, vert.y, 0 ); 35520 35521 } else { 35522 35523 // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); 35524 35525 normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); 35526 binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); 35527 35528 position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); 35529 35530 v( position2.x, position2.y, position2.z ); 35531 35532 } 35533 35534 } 35535 35536 // Add stepped vertices... 35537 // Including front facing vertices 35538 35539 for ( let s = 1; s <= steps; s ++ ) { 35540 35541 for ( let i = 0; i < vlen; i ++ ) { 35542 35543 const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; 35544 35545 if ( ! extrudeByPath ) { 35546 35547 v( vert.x, vert.y, depth / steps * s ); 35548 35549 } else { 35550 35551 // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); 35552 35553 normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); 35554 binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); 35555 35556 position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); 35557 35558 v( position2.x, position2.y, position2.z ); 35559 35560 } 35561 35562 } 35563 35564 } 35565 35566 35567 // Add bevel segments planes 35568 35569 //for ( b = 1; b <= bevelSegments; b ++ ) { 35570 for ( let b = bevelSegments - 1; b >= 0; b -- ) { 35571 35572 const t = b / bevelSegments; 35573 const z = bevelThickness * Math.cos( t * Math.PI / 2 ); 35574 const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; 35575 35576 // contract shape 35577 35578 for ( let i = 0, il = contour.length; i < il; i ++ ) { 35579 35580 const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); 35581 v( vert.x, vert.y, depth + z ); 35582 35583 } 35584 35585 // expand holes 35586 35587 for ( let h = 0, hl = holes.length; h < hl; h ++ ) { 35588 35589 const ahole = holes[ h ]; 35590 oneHoleMovements = holesMovements[ h ]; 35591 35592 for ( let i = 0, il = ahole.length; i < il; i ++ ) { 35593 35594 const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); 35595 35596 if ( ! extrudeByPath ) { 35597 35598 v( vert.x, vert.y, depth + z ); 35599 35600 } else { 35601 35602 v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); 35603 35604 } 35605 35606 } 35607 35608 } 35609 35610 } 35611 35612 /* Faces */ 35613 35614 // Top and bottom faces 35615 35616 buildLidFaces(); 35617 35618 // Sides faces 35619 35620 buildSideFaces(); 35621 35622 35623 ///// Internal functions 35624 35625 function buildLidFaces() { 35626 35627 const start = verticesArray.length / 3; 35628 35629 if ( bevelEnabled ) { 35630 35631 let layer = 0; // steps + 1 35632 let offset = vlen * layer; 35633 35634 // Bottom faces 35635 35636 for ( let i = 0; i < flen; i ++ ) { 35637 35638 const face = faces[ i ]; 35639 f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); 35640 35641 } 35642 35643 layer = steps + bevelSegments * 2; 35644 offset = vlen * layer; 35645 35646 // Top faces 35647 35648 for ( let i = 0; i < flen; i ++ ) { 35649 35650 const face = faces[ i ]; 35651 f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); 35652 35653 } 35654 35655 } else { 35656 35657 // Bottom faces 35658 35659 for ( let i = 0; i < flen; i ++ ) { 35660 35661 const face = faces[ i ]; 35662 f3( face[ 2 ], face[ 1 ], face[ 0 ] ); 35663 35664 } 35665 35666 // Top faces 35667 35668 for ( let i = 0; i < flen; i ++ ) { 35669 35670 const face = faces[ i ]; 35671 f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); 35672 35673 } 35674 35675 } 35676 35677 scope.addGroup( start, verticesArray.length / 3 - start, 0 ); 35678 35679 } 35680 35681 // Create faces for the z-sides of the shape 35682 35683 function buildSideFaces() { 35684 35685 const start = verticesArray.length / 3; 35686 let layeroffset = 0; 35687 sidewalls( contour, layeroffset ); 35688 layeroffset += contour.length; 35689 35690 for ( let h = 0, hl = holes.length; h < hl; h ++ ) { 35691 35692 const ahole = holes[ h ]; 35693 sidewalls( ahole, layeroffset ); 35694 35695 //, true 35696 layeroffset += ahole.length; 35697 35698 } 35699 35700 35701 scope.addGroup( start, verticesArray.length / 3 - start, 1 ); 35702 35703 35704 } 35705 35706 function sidewalls( contour, layeroffset ) { 35707 35708 let i = contour.length; 35709 35710 while ( -- i >= 0 ) { 35711 35712 const j = i; 35713 let k = i - 1; 35714 if ( k < 0 ) k = contour.length - 1; 35715 35716 //log('b', i,j, i-1, k,vertices.length); 35717 35718 for ( let s = 0, sl = ( steps + bevelSegments * 2 ); s < sl; s ++ ) { 35719 35720 const slen1 = vlen * s; 35721 const slen2 = vlen * ( s + 1 ); 35722 35723 const a = layeroffset + j + slen1, 35724 b = layeroffset + k + slen1, 35725 c = layeroffset + k + slen2, 35726 d = layeroffset + j + slen2; 35727 35728 f4( a, b, c, d ); 35729 35730 } 35731 35732 } 35733 35734 } 35735 35736 function v( x, y, z ) { 35737 35738 placeholder.push( x ); 35739 placeholder.push( y ); 35740 placeholder.push( z ); 35741 35742 } 35743 35744 35745 function f3( a, b, c ) { 35746 35747 addVertex( a ); 35748 addVertex( b ); 35749 addVertex( c ); 35750 35751 const nextIndex = verticesArray.length / 3; 35752 const uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); 35753 35754 addUV( uvs[ 0 ] ); 35755 addUV( uvs[ 1 ] ); 35756 addUV( uvs[ 2 ] ); 35757 35758 } 35759 35760 function f4( a, b, c, d ) { 35761 35762 addVertex( a ); 35763 addVertex( b ); 35764 addVertex( d ); 35765 35766 addVertex( b ); 35767 addVertex( c ); 35768 addVertex( d ); 35769 35770 35771 const nextIndex = verticesArray.length / 3; 35772 const uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); 35773 35774 addUV( uvs[ 0 ] ); 35775 addUV( uvs[ 1 ] ); 35776 addUV( uvs[ 3 ] ); 35777 35778 addUV( uvs[ 1 ] ); 35779 addUV( uvs[ 2 ] ); 35780 addUV( uvs[ 3 ] ); 35781 35782 } 35783 35784 function addVertex( index ) { 35785 35786 verticesArray.push( placeholder[ index * 3 + 0 ] ); 35787 verticesArray.push( placeholder[ index * 3 + 1 ] ); 35788 verticesArray.push( placeholder[ index * 3 + 2 ] ); 35789 35790 } 35791 35792 35793 function addUV( vector2 ) { 35794 35795 uvArray.push( vector2.x ); 35796 uvArray.push( vector2.y ); 35797 35798 } 35799 35800 } 35801 35802 } 35803 35804 copy( source ) { 35805 35806 super.copy( source ); 35807 35808 this.parameters = Object.assign( {}, source.parameters ); 35809 35810 return this; 35811 35812 } 35813 35814 toJSON() { 35815 35816 const data = super.toJSON(); 35817 35818 const shapes = this.parameters.shapes; 35819 const options = this.parameters.options; 35820 35821 return toJSON$1( shapes, options, data ); 35822 35823 } 35824 35825 /** 35826 * Factory method for creating an instance of this class from the given 35827 * JSON object. 35828 * 35829 * @param {Object} data - A JSON object representing the serialized geometry. 35830 * @param {Array<Shape>} shapes - An array of shapes. 35831 * @return {ExtrudeGeometry} A new instance. 35832 */ 35833 static fromJSON( data, shapes ) { 35834 35835 const geometryShapes = []; 35836 35837 for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { 35838 35839 const shape = shapes[ data.shapes[ j ] ]; 35840 35841 geometryShapes.push( shape ); 35842 35843 } 35844 35845 const extrudePath = data.options.extrudePath; 35846 35847 if ( extrudePath !== undefined ) { 35848 35849 data.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath ); 35850 35851 } 35852 35853 return new ExtrudeGeometry( geometryShapes, data.options ); 35854 35855 } 35856 35857} 35858 35859const WorldUVGenerator = { 35860 35861 generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { 35862 35863 const a_x = vertices[ indexA * 3 ]; 35864 const a_y = vertices[ indexA * 3 + 1 ]; 35865 const b_x = vertices[ indexB * 3 ]; 35866 const b_y = vertices[ indexB * 3 + 1 ]; 35867 const c_x = vertices[ indexC * 3 ]; 35868 const c_y = vertices[ indexC * 3 + 1 ]; 35869 35870 return [ 35871 new Vector2( a_x, a_y ), 35872 new Vector2( b_x, b_y ), 35873 new Vector2( c_x, c_y ) 35874 ]; 35875 35876 }, 35877 35878 generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { 35879 35880 const a_x = vertices[ indexA * 3 ]; 35881 const a_y = vertices[ indexA * 3 + 1 ]; 35882 const a_z = vertices[ indexA * 3 + 2 ]; 35883 const b_x = vertices[ indexB * 3 ]; 35884 const b_y = vertices[ indexB * 3 + 1 ]; 35885 const b_z = vertices[ indexB * 3 + 2 ]; 35886 const c_x = vertices[ indexC * 3 ]; 35887 const c_y = vertices[ indexC * 3 + 1 ]; 35888 const c_z = vertices[ indexC * 3 + 2 ]; 35889 const d_x = vertices[ indexD * 3 ]; 35890 const d_y = vertices[ indexD * 3 + 1 ]; 35891 const d_z = vertices[ indexD * 3 + 2 ]; 35892 35893 if ( Math.abs( a_y - b_y ) < Math.abs( a_x - b_x ) ) { 35894 35895 return [ 35896 new Vector2( a_x, 1 - a_z ), 35897 new Vector2( b_x, 1 - b_z ), 35898 new Vector2( c_x, 1 - c_z ), 35899 new Vector2( d_x, 1 - d_z ) 35900 ]; 35901 35902 } else { 35903 35904 return [ 35905 new Vector2( a_y, 1 - a_z ), 35906 new Vector2( b_y, 1 - b_z ), 35907 new Vector2( c_y, 1 - c_z ), 35908 new Vector2( d_y, 1 - d_z ) 35909 ]; 35910 35911 } 35912 35913 } 35914 35915}; 35916 35917function toJSON$1( shapes, options, data ) { 35918 35919 data.shapes = []; 35920 35921 if ( Array.isArray( shapes ) ) { 35922 35923 for ( let i = 0, l = shapes.length; i < l; i ++ ) { 35924 35925 const shape = shapes[ i ]; 35926 35927 data.shapes.push( shape.uuid ); 35928 35929 } 35930 35931 } else { 35932 35933 data.shapes.push( shapes.uuid ); 35934 35935 } 35936 35937 data.options = Object.assign( {}, options ); 35938 35939 if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON(); 35940 35941 return data; 35942 35943} 35944 35945/** 35946 * A geometry class for representing an icosahedron. 35947 * 35948 * ```js 35949 * const geometry = new THREE.IcosahedronGeometry(); 35950 * const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); 35951 * const icosahedron = new THREE.Mesh( geometry, material ); 35952 * scene.add( icosahedron ); 35953 * ``` 35954 * 35955 * @augments PolyhedronGeometry 35956 * @demo scenes/geometry-browser.html#IcosahedronGeometry 35957 */ 35958class IcosahedronGeometry extends PolyhedronGeometry { 35959 35960 /** 35961 * Constructs a new icosahedron geometry. 35962 * 35963 * @param {number} [radius=1] - Radius of the icosahedron. 35964 * @param {number} [detail=0] - Setting this to a value greater than `0` adds vertices making it no longer a icosahedron. 35965 */ 35966 constructor( radius = 1, detail = 0 ) { 35967 35968 const t = ( 1 + Math.sqrt( 5 ) ) / 2; 35969 35970 const vertices = [ 35971 -1, t, 0, 1, t, 0, -1, - t, 0, 1, - t, 0, 35972 0, -1, t, 0, 1, t, 0, -1, - t, 0, 1, - t, 35973 t, 0, -1, t, 0, 1, - t, 0, -1, - t, 0, 1 35974 ]; 35975 35976 const indices = [ 35977 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 35978 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 35979 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 35980 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 35981 ]; 35982 35983 super( vertices, indices, radius, detail ); 35984 35985 this.type = 'IcosahedronGeometry'; 35986 35987 /** 35988 * Holds the constructor parameters that have been 35989 * used to generate the geometry. Any modification 35990 * after instantiation does not change the geometry. 35991 * 35992 * @type {Object} 35993 */ 35994 this.parameters = { 35995 radius: radius, 35996 detail: detail 35997 }; 35998 35999 } 36000 36001 /** 36002 * Factory method for creating an instance of this class from the given 36003 * JSON object. 36004 * 36005 * @param {Object} data - A JSON object representing the serialized geometry. 36006 * @return {IcosahedronGeometry} A new instance. 36007 */ 36008 static fromJSON( data ) { 36009 36010 return new IcosahedronGeometry( data.radius, data.detail ); 36011 36012 } 36013 36014} 36015 36016/** 36017 * Creates meshes with axial symmetry like vases. The lathe rotates around the Y axis. 36018 * 36019 * ```js 36020 * const points = []; 36021 * for ( let i = 0; i < 10; i ++ ) { 36022 * points.push( new THREE.Vector2( Math.sin( i * 0.2 ) * 10 + 5, ( i - 5 ) * 2 ) ); 36023 * } 36024 * const geometry = new THREE.LatheGeometry( points ); 36025 * const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); 36026 * const lathe = new THREE.Mesh( geometry, material ); 36027 * scene.add( lathe ); 36028 * ``` 36029 * 36030 * @augments BufferGeometry 36031 * @demo scenes/geometry-browser.html#LatheGeometry 36032 */ 36033class LatheGeometry extends BufferGeometry { 36034 36035 /** 36036 * Constructs a new lathe geometry. 36037 * 36038 * @param {Array<Vector2|Vector3>} [points] - An array of points in 2D space. The x-coordinate of each point 36039 * must be greater than zero. 36040 * @param {number} [segments=12] - The number of circumference segments to generate. 36041 * @param {number} [phiStart=0] - The starting angle in radians. 36042 * @param {number} [phiLength=Math.PI*2] - The radian (0 to 2PI) range of the lathed section 2PI is a 36043 * closed lathe, less than 2PI is a portion. 36044 */ 36045 constructor( points = [ new Vector2( 0, -0.5 ), new Vector2( 0.5, 0 ), new Vector2( 0, 0.5 ) ], segments = 12, phiStart = 0, phiLength = Math.PI * 2 ) { 36046 36047 super(); 36048 36049 this.type = 'LatheGeometry'; 36050 36051 /** 36052 * Holds the constructor parameters that have been 36053 * used to generate the geometry. Any modification 36054 * after instantiation does not change the geometry. 36055 * 36056 * @type {Object} 36057 */ 36058 this.parameters = { 36059 points: points, 36060 segments: segments, 36061 phiStart: phiStart, 36062 phiLength: phiLength 36063 }; 36064 36065 segments = Math.floor( segments ); 36066 36067 // clamp phiLength so it's in range of [ 0, 2PI ] 36068 36069 phiLength = clamp( phiLength, 0, Math.PI * 2 ); 36070 36071 // buffers 36072 36073 const indices = []; 36074 const vertices = []; 36075 const uvs = []; 36076 const initNormals = []; 36077 const normals = []; 36078 36079 // helper variables 36080 36081 const inverseSegments = 1.0 / segments; 36082 const vertex = new Vector3(); 36083 const uv = new Vector2(); 36084 const normal = new Vector3(); 36085 const curNormal = new Vector3(); 36086 const prevNormal = new Vector3(); 36087 let dx = 0; 36088 let dy = 0; 36089 36090 // pre-compute normals for initial "meridian" 36091 36092 for ( let j = 0; j <= ( points.length - 1 ); j ++ ) { 36093 36094 switch ( j ) { 36095 36096 case 0: // special handling for 1st vertex on path 36097 36098 dx = points[ j + 1 ].x - points[ j ].x; 36099 dy = points[ j + 1 ].y - points[ j ].y; 36100 36101 normal.x = dy * 1.0; 36102 normal.y = - dx; 36103 normal.z = dy * 0.0; 36104 36105 prevNormal.copy( normal ); 36106 36107 normal.normalize(); 36108 36109 initNormals.push( normal.x, normal.y, normal.z ); 36110 36111 break; 36112 36113 case ( points.length - 1 ): // special handling for last Vertex on path 36114 36115 initNormals.push( prevNormal.x, prevNormal.y, prevNormal.z ); 36116 36117 break; 36118 36119 default: // default handling for all vertices in between 36120 36121 dx = points[ j + 1 ].x - points[ j ].x; 36122 dy = points[ j + 1 ].y - points[ j ].y; 36123 36124 normal.x = dy * 1.0; 36125 normal.y = - dx; 36126 normal.z = dy * 0.0; 36127 36128 curNormal.copy( normal ); 36129 36130 normal.x += prevNormal.x; 36131 normal.y += prevNormal.y; 36132 normal.z += prevNormal.z; 36133 36134 normal.normalize(); 36135 36136 initNormals.push( normal.x, normal.y, normal.z ); 36137 36138 prevNormal.copy( curNormal ); 36139 36140 } 36141 36142 } 36143 36144 // generate vertices, uvs and normals 36145 36146 for ( let i = 0; i <= segments; i ++ ) { 36147 36148 const phi = phiStart + i * inverseSegments * phiLength; 36149 36150 const sin = Math.sin( phi ); 36151 const cos = Math.cos( phi ); 36152 36153 for ( let j = 0; j <= ( points.length - 1 ); j ++ ) { 36154 36155 // vertex 36156 36157 vertex.x = points[ j ].x * sin; 36158 vertex.y = points[ j ].y; 36159 vertex.z = points[ j ].x * cos; 36160 36161 vertices.push( vertex.x, vertex.y, vertex.z ); 36162 36163 // uv 36164 36165 uv.x = i / segments; 36166 uv.y = j / ( points.length - 1 ); 36167 36168 uvs.push( uv.x, uv.y ); 36169 36170 // normal 36171 36172 const x = initNormals[ 3 * j + 0 ] * sin; 36173 const y = initNormals[ 3 * j + 1 ]; 36174 const z = initNormals[ 3 * j + 0 ] * cos; 36175 36176 normals.push( x, y, z ); 36177 36178 } 36179 36180 } 36181 36182 // indices 36183 36184 for ( let i = 0; i < segments; i ++ ) { 36185 36186 for ( let j = 0; j < ( points.length - 1 ); j ++ ) { 36187 36188 const base = j + i * points.length; 36189 36190 const a = base; 36191 const b = base + points.length; 36192 const c = base + points.length + 1; 36193 const d = base + 1; 36194 36195 // faces 36196 36197 indices.push( a, b, d ); 36198 indices.push( c, d, b ); 36199 36200 } 36201 36202 } 36203 36204 // build geometry 36205 36206 this.setIndex( indices ); 36207 this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 36208 this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); 36209 this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); 36210 36211 } 36212 36213 copy( source ) { 36214 36215 super.copy( source ); 36216 36217 this.parameters = Object.assign( {}, source.parameters ); 36218 36219 return this; 36220 36221 } 36222 36223 /** 36224 * Factory method for creating an instance of this class from the given 36225 * JSON object. 36226 * 36227 * @param {Object} data - A JSON object representing the serialized geometry. 36228 * @return {LatheGeometry} A new instance. 36229 */ 36230 static fromJSON( data ) { 36231 36232 return new LatheGeometry( data.points, data.segments, data.phiStart, data.phiLength ); 36233 36234 } 36235 36236} 36237 36238/** 36239 * A geometry class for representing an octahedron. 36240 * 36241 * ```js 36242 * const geometry = new THREE.OctahedronGeometry(); 36243 * const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); 36244 * const octahedron = new THREE.Mesh( geometry, material ); 36245 * scene.add( octahedron ); 36246 * ``` 36247 * 36248 * @augments PolyhedronGeometry 36249 * @demo scenes/geometry-browser.html#OctahedronGeometry 36250 */ 36251class OctahedronGeometry extends PolyhedronGeometry { 36252 36253 /** 36254 * Constructs a new octahedron geometry. 36255 * 36256 * @param {number} [radius=1] - Radius of the octahedron. 36257 * @param {number} [detail=0] - Setting this to a value greater than `0` adds vertices making it no longer a octahedron. 36258 */ 36259 constructor( radius = 1, detail = 0 ) { 36260 36261 const vertices = [ 36262 1, 0, 0, -1, 0, 0, 0, 1, 0, 36263 0, -1, 0, 0, 0, 1, 0, 0, -1 36264 ]; 36265 36266 const indices = [ 36267 0, 2, 4, 0, 4, 3, 0, 3, 5, 36268 0, 5, 2, 1, 2, 5, 1, 5, 3, 36269 1, 3, 4, 1, 4, 2 36270 ]; 36271 36272 super( vertices, indices, radius, detail ); 36273 36274 this.type = 'OctahedronGeometry'; 36275 36276 /** 36277 * Holds the constructor parameters that have been 36278 * used to generate the geometry. Any modification 36279 * after instantiation does not change the geometry. 36280 * 36281 * @type {Object} 36282 */ 36283 this.parameters = { 36284 radius: radius, 36285 detail: detail 36286 }; 36287 36288 } 36289 36290 /** 36291 * Factory method for creating an instance of this class from the given 36292 * JSON object. 36293 * 36294 * @param {Object} data - A JSON object representing the serialized geometry. 36295 * @return {OctahedronGeometry} A new instance. 36296 */ 36297 static fromJSON( data ) { 36298 36299 return new OctahedronGeometry( data.radius, data.detail ); 36300 36301 } 36302 36303} 36304 36305/** 36306 * A geometry class for representing a plane. 36307 * 36308 * ```js 36309 * const geometry = new THREE.PlaneGeometry( 1, 1 ); 36310 * const material = new THREE.MeshBasicMaterial( { color: 0xffff00, side: THREE.DoubleSide } ); 36311 * const plane = new THREE.Mesh( geometry, material ); 36312 * scene.add( plane ); 36313 * ``` 36314 * 36315 * @augments BufferGeometry 36316 * @demo scenes/geometry-browser.html#PlaneGeometry 36317 */ 36318class PlaneGeometry extends BufferGeometry { 36319 36320 /** 36321 * Constructs a new plane geometry. 36322 * 36323 * @param {number} [width=1] - The width along the X axis. 36324 * @param {number} [height=1] - The height along the Y axis 36325 * @param {number} [widthSegments=1] - The number of segments along the X axis. 36326 * @param {number} [heightSegments=1] - The number of segments along the Y axis. 36327 */ 36328 constructor( width = 1, height = 1, widthSegments = 1, heightSegments = 1 ) { 36329 36330 super(); 36331 36332 this.type = 'PlaneGeometry'; 36333 36334 /** 36335 * Holds the constructor parameters that have been 36336 * used to generate the geometry. Any modification 36337 * after instantiation does not change the geometry. 36338 * 36339 * @type {Object} 36340 */ 36341 this.parameters = { 36342 width: width, 36343 height: height, 36344 widthSegments: widthSegments, 36345 heightSegments: heightSegments 36346 }; 36347 36348 const width_half = width / 2; 36349 const height_half = height / 2; 36350 36351 const gridX = Math.floor( widthSegments ); 36352 const gridY = Math.floor( heightSegments ); 36353 36354 const gridX1 = gridX + 1; 36355 const gridY1 = gridY + 1; 36356 36357 const segment_width = width / gridX; 36358 const segment_height = height / gridY; 36359 36360 // 36361 36362 const indices = []; 36363 const vertices = []; 36364 const normals = []; 36365 const uvs = []; 36366 36367 for ( let iy = 0; iy < gridY1; iy ++ ) { 36368 36369 const y = iy * segment_height - height_half; 36370 36371 for ( let ix = 0; ix < gridX1; ix ++ ) { 36372 36373 const x = ix * segment_width - width_half; 36374 36375 vertices.push( x, - y, 0 ); 36376 36377 normals.push( 0, 0, 1 ); 36378 36379 uvs.push( ix / gridX ); 36380 uvs.push( 1 - ( iy / gridY ) ); 36381 36382 } 36383 36384 } 36385 36386 for ( let iy = 0; iy < gridY; iy ++ ) { 36387 36388 for ( let ix = 0; ix < gridX; ix ++ ) { 36389 36390 const a = ix + gridX1 * iy; 36391 const b = ix + gridX1 * ( iy + 1 ); 36392 const c = ( ix + 1 ) + gridX1 * ( iy + 1 ); 36393 const d = ( ix + 1 ) + gridX1 * iy; 36394 36395 indices.push( a, b, d ); 36396 indices.push( b, c, d ); 36397 36398 } 36399 36400 } 36401 36402 this.setIndex( indices ); 36403 this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 36404 this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); 36405 this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); 36406 36407 } 36408 36409 copy( source ) { 36410 36411 super.copy( source ); 36412 36413 this.parameters = Object.assign( {}, source.parameters ); 36414 36415 return this; 36416 36417 } 36418 36419 /** 36420 * Factory method for creating an instance of this class from the given 36421 * JSON object. 36422 * 36423 * @param {Object} data - A JSON object representing the serialized geometry. 36424 * @return {PlaneGeometry} A new instance. 36425 */ 36426 static fromJSON( data ) { 36427 36428 return new PlaneGeometry( data.width, data.height, data.widthSegments, data.heightSegments ); 36429 36430 } 36431 36432} 36433 36434/** 36435 * A class for generating a two-dimensional ring geometry. 36436 * 36437 * ```js 36438 * const geometry = new THREE.RingGeometry( 1, 5, 32 ); 36439 * const material = new THREE.MeshBasicMaterial( { color: 0xffff00, side: THREE.DoubleSide } ); 36440 * const mesh = new THREE.Mesh( geometry, material ); 36441 * scene.add( mesh ); 36442 * ``` 36443 * 36444 * @augments BufferGeometry 36445 * @demo scenes/geometry-browser.html#RingGeometry 36446 */ 36447class RingGeometry extends BufferGeometry { 36448 36449 /** 36450 * Constructs a new ring geometry. 36451 * 36452 * @param {number} [innerRadius=0.5] - The inner radius of the ring. 36453 * @param {number} [outerRadius=1] - The outer radius of the ring. 36454 * @param {number} [thetaSegments=32] - Number of segments. A higher number means the ring will be more round. Minimum is `3`. 36455 * @param {number} [phiSegments=1] - Number of segments per ring segment. Minimum is `1`. 36456 * @param {number} [thetaStart=0] - Starting angle in radians. 36457 * @param {number} [thetaLength=Math.PI*2] - Central angle in radians. 36458 */ 36459 constructor( innerRadius = 0.5, outerRadius = 1, thetaSegments = 32, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2 ) { 36460 36461 super(); 36462 36463 this.type = 'RingGeometry'; 36464 36465 /** 36466 * Holds the constructor parameters that have been 36467 * used to generate the geometry. Any modification 36468 * after instantiation does not change the geometry. 36469 * 36470 * @type {Object} 36471 */ 36472 this.parameters = { 36473 innerRadius: innerRadius, 36474 outerRadius: outerRadius, 36475 thetaSegments: thetaSegments, 36476 phiSegments: phiSegments, 36477 thetaStart: thetaStart, 36478 thetaLength: thetaLength 36479 }; 36480 36481 thetaSegments = Math.max( 3, thetaSegments ); 36482 phiSegments = Math.max( 1, phiSegments ); 36483 36484 // buffers 36485 36486 const indices = []; 36487 const vertices = []; 36488 const normals = []; 36489 const uvs = []; 36490 36491 // some helper variables 36492 36493 let radius = innerRadius; 36494 const radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); 36495 const vertex = new Vector3(); 36496 const uv = new Vector2(); 36497 36498 // generate vertices, normals and uvs 36499 36500 for ( let j = 0; j <= phiSegments; j ++ ) { 36501 36502 for ( let i = 0; i <= thetaSegments; i ++ ) { 36503 36504 // values are generate from the inside of the ring to the outside 36505 36506 const segment = thetaStart + i / thetaSegments * thetaLength; 36507 36508 // vertex 36509 36510 vertex.x = radius * Math.cos( segment ); 36511 vertex.y = radius * Math.sin( segment ); 36512 36513 vertices.push( vertex.x, vertex.y, vertex.z ); 36514 36515 // normal 36516 36517 normals.push( 0, 0, 1 ); 36518 36519 // uv 36520 36521 uv.x = ( vertex.x / outerRadius + 1 ) / 2; 36522 uv.y = ( vertex.y / outerRadius + 1 ) / 2; 36523 36524 uvs.push( uv.x, uv.y ); 36525 36526 } 36527 36528 // increase the radius for next row of vertices 36529 36530 radius += radiusStep; 36531 36532 } 36533 36534 // indices 36535 36536 for ( let j = 0; j < phiSegments; j ++ ) { 36537 36538 const thetaSegmentLevel = j * ( thetaSegments + 1 ); 36539 36540 for ( let i = 0; i < thetaSegments; i ++ ) { 36541 36542 const segment = i + thetaSegmentLevel; 36543 36544 const a = segment; 36545 const b = segment + thetaSegments + 1; 36546 const c = segment + thetaSegments + 2; 36547 const d = segment + 1; 36548 36549 // faces 36550 36551 indices.push( a, b, d ); 36552 indices.push( b, c, d ); 36553 36554 } 36555 36556 } 36557 36558 // build geometry 36559 36560 this.setIndex( indices ); 36561 this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 36562 this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); 36563 this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); 36564 36565 } 36566 36567 copy( source ) { 36568 36569 super.copy( source ); 36570 36571 this.parameters = Object.assign( {}, source.parameters ); 36572 36573 return this; 36574 36575 } 36576 36577 /** 36578 * Factory method for creating an instance of this class from the given 36579 * JSON object. 36580 * 36581 * @param {Object} data - A JSON object representing the serialized geometry. 36582 * @return {RingGeometry} A new instance. 36583 */ 36584 static fromJSON( data ) { 36585 36586 return new RingGeometry( data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength ); 36587 36588 } 36589 36590} 36591 36592/** 36593 * Creates an one-sided polygonal geometry from one or more path shapes. 36594 * 36595 * ```js 36596 * const arcShape = new THREE.Shape() 36597 * .moveTo( 5, 1 ) 36598 * .absarc( 1, 1, 4, 0, Math.PI * 2, false ); 36599 * 36600 * const geometry = new THREE.ShapeGeometry( arcShape ); 36601 * const material = new THREE.MeshBasicMaterial( { color: 0x00ff00, side: THREE.DoubleSide } ); 36602 * const mesh = new THREE.Mesh( geometry, material ) ; 36603 * scene.add( mesh ); 36604 * ``` 36605 * 36606 * @augments BufferGeometry 36607 * @demo scenes/geometry-browser.html#ShapeGeometry 36608 */ 36609class ShapeGeometry extends BufferGeometry { 36610 36611 /** 36612 * Constructs a new shape geometry. 36613 * 36614 * @param {Shape|Array<Shape>} [shapes] - A shape or an array of shapes. 36615 * @param {number} [curveSegments=12] - Number of segments per shape. 36616 */ 36617 constructor( shapes = new Shape( [ new Vector2( 0, 0.5 ), new Vector2( -0.5, -0.5 ), new Vector2( 0.5, -0.5 ) ] ), curveSegments = 12 ) { 36618 36619 super(); 36620 36621 this.type = 'ShapeGeometry'; 36622 36623 /** 36624 * Holds the constructor parameters that have been 36625 * used to generate the geometry. Any modification 36626 * after instantiation does not change the geometry. 36627 * 36628 * @type {Object} 36629 */ 36630 this.parameters = { 36631 shapes: shapes, 36632 curveSegments: curveSegments 36633 }; 36634 36635 // buffers 36636 36637 const indices = []; 36638 const vertices = []; 36639 const normals = []; 36640 const uvs = []; 36641 36642 // helper variables 36643 36644 let groupStart = 0; 36645 let groupCount = 0; 36646 36647 // allow single and array values for "shapes" parameter 36648 36649 if ( Array.isArray( shapes ) === false ) { 36650 36651 addShape( shapes ); 36652 36653 } else { 36654 36655 for ( let i = 0; i < shapes.length; i ++ ) { 36656 36657 addShape( shapes[ i ] ); 36658 36659 this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support 36660 36661 groupStart += groupCount; 36662 groupCount = 0; 36663 36664 } 36665 36666 } 36667 36668 // build geometry 36669 36670 this.setIndex( indices ); 36671 this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 36672 this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); 36673 this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); 36674 36675 36676 // helper functions 36677 36678 function addShape( shape ) { 36679 36680 const indexOffset = vertices.length / 3; 36681 const points = shape.extractPoints( curveSegments ); 36682 36683 let shapeVertices = points.shape; 36684 const shapeHoles = points.holes; 36685 36686 // check direction of vertices 36687 36688 if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { 36689 36690 shapeVertices = shapeVertices.reverse(); 36691 36692 } 36693 36694 for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { 36695 36696 const shapeHole = shapeHoles[ i ]; 36697 36698 if ( ShapeUtils.isClockWise( shapeHole ) === true ) { 36699 36700 shapeHoles[ i ] = shapeHole.reverse(); 36701 36702 } 36703 36704 } 36705 36706 const faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); 36707 36708 // join vertices of inner and outer paths to a single array 36709 36710 for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { 36711 36712 const shapeHole = shapeHoles[ i ]; 36713 shapeVertices = shapeVertices.concat( shapeHole ); 36714 36715 } 36716 36717 // vertices, normals, uvs 36718 36719 for ( let i = 0, l = shapeVertices.length; i < l; i ++ ) { 36720 36721 const vertex = shapeVertices[ i ]; 36722 36723 vertices.push( vertex.x, vertex.y, 0 ); 36724 normals.push( 0, 0, 1 ); 36725 uvs.push( vertex.x, vertex.y ); // world uvs 36726 36727 } 36728 36729 // indices 36730 36731 for ( let i = 0, l = faces.length; i < l; i ++ ) { 36732 36733 const face = faces[ i ]; 36734 36735 const a = face[ 0 ] + indexOffset; 36736 const b = face[ 1 ] + indexOffset; 36737 const c = face[ 2 ] + indexOffset; 36738 36739 indices.push( a, b, c ); 36740 groupCount += 3; 36741 36742 } 36743 36744 } 36745 36746 } 36747 36748 copy( source ) { 36749 36750 super.copy( source ); 36751 36752 this.parameters = Object.assign( {}, source.parameters ); 36753 36754 return this; 36755 36756 } 36757 36758 toJSON() { 36759 36760 const data = super.toJSON(); 36761 36762 const shapes = this.parameters.shapes; 36763 36764 return toJSON( shapes, data ); 36765 36766 } 36767 36768 /** 36769 * Factory method for creating an instance of this class from the given 36770 * JSON object. 36771 * 36772 * @param {Object} data - A JSON object representing the serialized geometry. 36773 * @param {Array<Shape>} shapes - An array of shapes. 36774 * @return {ShapeGeometry} A new instance. 36775 */ 36776 static fromJSON( data, shapes ) { 36777 36778 const geometryShapes = []; 36779 36780 for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { 36781 36782 const shape = shapes[ data.shapes[ j ] ]; 36783 36784 geometryShapes.push( shape ); 36785 36786 } 36787 36788 return new ShapeGeometry( geometryShapes, data.curveSegments ); 36789 36790 } 36791 36792} 36793 36794function toJSON( shapes, data ) { 36795 36796 data.shapes = []; 36797 36798 if ( Array.isArray( shapes ) ) { 36799 36800 for ( let i = 0, l = shapes.length; i < l; i ++ ) { 36801 36802 const shape = shapes[ i ]; 36803 36804 data.shapes.push( shape.uuid ); 36805 36806 } 36807 36808 } else { 36809 36810 data.shapes.push( shapes.uuid ); 36811 36812 } 36813 36814 return data; 36815 36816} 36817 36818/** 36819 * A class for generating a sphere geometry. 36820 * 36821 * ```js 36822 * const geometry = new THREE.SphereGeometry( 15, 32, 16 ); 36823 * const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); 36824 * const sphere = new THREE.Mesh( geometry, material ); 36825 * scene.add( sphere ); 36826 * ``` 36827 * 36828 * @augments BufferGeometry 36829 * @demo scenes/geometry-browser.html#SphereGeometry 36830 */ 36831class SphereGeometry extends BufferGeometry { 36832 36833 /** 36834 * Constructs a new sphere geometry. 36835 * 36836 * @param {number} [radius=1] - The sphere radius. 36837 * @param {number} [widthSegments=32] - The number of horizontal segments. Minimum value is `3`. 36838 * @param {number} [heightSegments=16] - The number of vertical segments. Minimum value is `2`. 36839 * @param {number} [phiStart=0] - The horizontal starting angle in radians. 36840 * @param {number} [phiLength=Math.PI*2] - The horizontal sweep angle size. 36841 * @param {number} [thetaStart=0] - The vertical starting angle in radians. 36842 * @param {number} [thetaLength=Math.PI] - The vertical sweep angle size. 36843 */ 36844 constructor( radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI ) { 36845 36846 super(); 36847 36848 this.type = 'SphereGeometry'; 36849 36850 /** 36851 * Holds the constructor parameters that have been 36852 * used to generate the geometry. Any modification 36853 * after instantiation does not change the geometry. 36854 * 36855 * @type {Object} 36856 */ 36857 this.parameters = { 36858 radius: radius, 36859 widthSegments: widthSegments, 36860 heightSegments: heightSegments, 36861 phiStart: phiStart, 36862 phiLength: phiLength, 36863 thetaStart: thetaStart, 36864 thetaLength: thetaLength 36865 }; 36866 36867 widthSegments = Math.max( 3, Math.floor( widthSegments ) ); 36868 heightSegments = Math.max( 2, Math.floor( heightSegments ) ); 36869 36870 const thetaEnd = Math.min( thetaStart + thetaLength, Math.PI ); 36871 36872 let index = 0; 36873 const grid = []; 36874 36875 const vertex = new Vector3(); 36876 const normal = new Vector3(); 36877 36878 // buffers 36879 36880 const indices = []; 36881 const vertices = []; 36882 const normals = []; 36883 const uvs = []; 36884 36885 // generate vertices, normals and uvs 36886 36887 for ( let iy = 0; iy <= heightSegments; iy ++ ) { 36888 36889 const verticesRow = []; 36890 36891 const v = iy / heightSegments; 36892 const theta = thetaStart + v * thetaLength; 36893 36894 const y = radius * Math.cos( theta ); 36895 const ringRadius = Math.sqrt( radius * radius - y * y ); 36896 36897 // special case for the poles 36898 36899 let uOffset = 0; 36900 36901 if ( iy === 0 && thetaStart === 0 ) { 36902 36903 uOffset = 0.5 / widthSegments; 36904 36905 } else if ( iy === heightSegments && thetaEnd === Math.PI ) { 36906 36907 uOffset = -0.5 / widthSegments; 36908 36909 } 36910 36911 for ( let ix = 0; ix <= widthSegments; ix ++ ) { 36912 36913 const u = ix / widthSegments; 36914 const phi = phiStart + u * phiLength; 36915 36916 // vertex 36917 36918 vertex.x = - ringRadius * Math.cos( phi ); 36919 vertex.y = y; 36920 vertex.z = ringRadius * Math.sin( phi ); 36921 36922 vertices.push( vertex.x, vertex.y, vertex.z ); 36923 36924 // normal 36925 36926 normal.copy( vertex ).normalize(); 36927 normals.push( normal.x, normal.y, normal.z ); 36928 36929 // uv 36930 36931 uvs.push( u + uOffset, 1 - v ); 36932 36933 verticesRow.push( index ++ ); 36934 36935 } 36936 36937 grid.push( verticesRow ); 36938 36939 } 36940 36941 // indices 36942 36943 for ( let iy = 0; iy < heightSegments; iy ++ ) { 36944 36945 for ( let ix = 0; ix < widthSegments; ix ++ ) { 36946 36947 const a = grid[ iy ][ ix + 1 ]; 36948 const b = grid[ iy ][ ix ]; 36949 const c = grid[ iy + 1 ][ ix ]; 36950 const d = grid[ iy + 1 ][ ix + 1 ]; 36951 36952 if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d ); 36953 if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d ); 36954 36955 } 36956 36957 } 36958 36959 // build geometry 36960 36961 this.setIndex( indices ); 36962 this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 36963 this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); 36964 this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); 36965 36966 } 36967 36968 copy( source ) { 36969 36970 super.copy( source ); 36971 36972 this.parameters = Object.assign( {}, source.parameters ); 36973 36974 return this; 36975 36976 } 36977 36978 /** 36979 * Factory method for creating an instance of this class from the given 36980 * JSON object. 36981 * 36982 * @param {Object} data - A JSON object representing the serialized geometry. 36983 * @return {SphereGeometry} A new instance. 36984 */ 36985 static fromJSON( data ) { 36986 36987 return new SphereGeometry( data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength ); 36988 36989 } 36990 36991} 36992 36993/** 36994 * A geometry class for representing an tetrahedron. 36995 * 36996 * ```js 36997 * const geometry = new THREE.TetrahedronGeometry(); 36998 * const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); 36999 * const tetrahedron = new THREE.Mesh( geometry, material ); 37000 * scene.add( tetrahedron ); 37001 * ``` 37002 * 37003 * @augments PolyhedronGeometry 37004 * @demo scenes/geometry-browser.html#TetrahedronGeometry 37005 */ 37006class TetrahedronGeometry extends PolyhedronGeometry { 37007 37008 /** 37009 * Constructs a new tetrahedron geometry. 37010 * 37011 * @param {number} [radius=1] - Radius of the tetrahedron. 37012 * @param {number} [detail=0] - Setting this to a value greater than `0` adds vertices making it no longer a tetrahedron. 37013 */ 37014 constructor( radius = 1, detail = 0 ) { 37015 37016 const vertices = [ 37017 1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1 37018 ]; 37019 37020 const indices = [ 37021 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 37022 ]; 37023 37024 super( vertices, indices, radius, detail ); 37025 37026 this.type = 'TetrahedronGeometry'; 37027 37028 /** 37029 * Holds the constructor parameters that have been 37030 * used to generate the geometry. Any modification 37031 * after instantiation does not change the geometry. 37032 * 37033 * @type {Object} 37034 */ 37035 this.parameters = { 37036 radius: radius, 37037 detail: detail 37038 }; 37039 37040 } 37041 37042 /** 37043 * Factory method for creating an instance of this class from the given 37044 * JSON object. 37045 * 37046 * @param {Object} data - A JSON object representing the serialized geometry. 37047 * @return {TetrahedronGeometry} A new instance. 37048 */ 37049 static fromJSON( data ) { 37050 37051 return new TetrahedronGeometry( data.radius, data.detail ); 37052 37053 } 37054 37055} 37056 37057/** 37058 * A geometry class for representing an torus. 37059 * 37060 * ```js 37061 * const geometry = new THREE.TorusGeometry( 10, 3, 16, 100 ); 37062 * const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); 37063 * const torus = new THREE.Mesh( geometry, material ); 37064 * scene.add( torus ); 37065 * ``` 37066 * 37067 * @augments BufferGeometry 37068 * @demo scenes/geometry-browser.html#TorusGeometry 37069 */ 37070class TorusGeometry extends BufferGeometry { 37071 37072 /** 37073 * Constructs a new torus geometry. 37074 * 37075 * @param {number} [radius=1] - Radius of the torus, from the center of the torus to the center of the tube. 37076 * @param {number} [tube=0.4] - Radius of the tube. Must be smaller than `radius`. 37077 * @param {number} [radialSegments=12] - The number of radial segments. 37078 * @param {number} [tubularSegments=48] - The number of tubular segments. 37079 * @param {number} [arc=Math.PI*2] - Central angle in radians. 37080 * @param {number} [thetaStart=0] - Start of the tubular sweep in radians. 37081 * @param {number} [thetaLength=Math.PI*2] - Length of the tubular sweep in radians. 37082 */ 37083 constructor( radius = 1, tube = 0.4, radialSegments = 12, tubularSegments = 48, arc = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI * 2 ) { 37084 37085 super(); 37086 37087 this.type = 'TorusGeometry'; 37088 37089 /** 37090 * Holds the constructor parameters that have been 37091 * used to generate the geometry. Any modification 37092 * after instantiation does not change the geometry. 37093 * 37094 * @type {Object} 37095 */ 37096 this.parameters = { 37097 radius: radius, 37098 tube: tube, 37099 radialSegments: radialSegments, 37100 tubularSegments: tubularSegments, 37101 arc: arc, 37102 thetaStart: thetaStart, 37103 thetaLength: thetaLength, 37104 }; 37105 37106 radialSegments = Math.floor( radialSegments ); 37107 tubularSegments = Math.floor( tubularSegments ); 37108 37109 // buffers 37110 37111 const indices = []; 37112 const vertices = []; 37113 const normals = []; 37114 const uvs = []; 37115 37116 // helper variables 37117 37118 const center = new Vector3(); 37119 const vertex = new Vector3(); 37120 const normal = new Vector3(); 37121 37122 // generate vertices, normals and uvs 37123 37124 for ( let j = 0; j <= radialSegments; j ++ ) { 37125 37126 const v = thetaStart + ( j / radialSegments ) * thetaLength; 37127 37128 for ( let i = 0; i <= tubularSegments; i ++ ) { 37129 37130 const u = i / tubularSegments * arc; 37131 37132 // vertex 37133 37134 vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); 37135 vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); 37136 vertex.z = tube * Math.sin( v ); 37137 37138 vertices.push( vertex.x, vertex.y, vertex.z ); 37139 37140 // normal 37141 37142 center.x = radius * Math.cos( u ); 37143 center.y = radius * Math.sin( u ); 37144 normal.subVectors( vertex, center ).normalize(); 37145 37146 normals.push( normal.x, normal.y, normal.z ); 37147 37148 // uv 37149 37150 uvs.push( i / tubularSegments ); 37151 uvs.push( j / radialSegments ); 37152 37153 } 37154 37155 } 37156 37157 // generate indices 37158 37159 for ( let j = 1; j <= radialSegments; j ++ ) { 37160 37161 for ( let i = 1; i <= tubularSegments; i ++ ) { 37162 37163 // indices 37164 37165 const a = ( tubularSegments + 1 ) * j + i - 1; 37166 const b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; 37167 const c = ( tubularSegments + 1 ) * ( j - 1 ) + i; 37168 const d = ( tubularSegments + 1 ) * j + i; 37169 37170 // faces 37171 37172 indices.push( a, b, d ); 37173 indices.push( b, c, d ); 37174 37175 } 37176 37177 } 37178 37179 // build geometry 37180 37181 this.setIndex( indices ); 37182 this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 37183 this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); 37184 this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); 37185 37186 } 37187 37188 copy( source ) { 37189 37190 super.copy( source ); 37191 37192 this.parameters = Object.assign( {}, source.parameters ); 37193 37194 return this; 37195 37196 } 37197 37198 /** 37199 * Factory method for creating an instance of this class from the given 37200 * JSON object. 37201 * 37202 * @param {Object} data - A JSON object representing the serialized geometry. 37203 * @return {TorusGeometry} A new instance. 37204 */ 37205 static fromJSON( data ) { 37206 37207 return new TorusGeometry( data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc, data.thetaStart, data.thetaLength ); 37208 37209 } 37210 37211} 37212 37213/** 37214 * Creates a torus knot, the particular shape of which is defined by a pair 37215 * of coprime integers, p and q. If p and q are not coprime, the result will 37216 * be a torus link. 37217 * 37218 * ```js 37219 * const geometry = new THREE.TorusKnotGeometry( 10, 3, 100, 16 ); 37220 * const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); 37221 * const torusKnot = new THREE.Mesh( geometry, material ); 37222 * scene.add( torusKnot ); 37223 * ``` 37224 * 37225 * @augments BufferGeometry 37226 * @demo scenes/geometry-browser.html#TorusKnotGeometry 37227 */ 37228class TorusKnotGeometry extends BufferGeometry { 37229 37230 /** 37231 * Constructs a new torus knot geometry. 37232 * 37233 * @param {number} [radius=1] - Radius of the torus knot. 37234 * @param {number} [tube=0.4] - Radius of the tube. 37235 * @param {number} [tubularSegments=64] - The number of tubular segments. 37236 * @param {number} [radialSegments=8] - The number of radial segments. 37237 * @param {number} [p=2] - This value determines, how many times the geometry winds around its axis of rotational symmetry. 37238 * @param {number} [q=3] - This value determines, how many times the geometry winds around a circle in the interior of the torus. 37239 */ 37240 constructor( radius = 1, tube = 0.4, tubularSegments = 64, radialSegments = 8, p = 2, q = 3 ) { 37241 37242 super(); 37243 37244 this.type = 'TorusKnotGeometry'; 37245 37246 /** 37247 * Holds the constructor parameters that have been 37248 * used to generate the geometry. Any modification 37249 * after instantiation does not change the geometry. 37250 * 37251 * @type {Object} 37252 */ 37253 this.parameters = { 37254 radius: radius, 37255 tube: tube, 37256 tubularSegments: tubularSegments, 37257 radialSegments: radialSegments, 37258 p: p, 37259 q: q 37260 }; 37261 37262 tubularSegments = Math.floor( tubularSegments ); 37263 radialSegments = Math.floor( radialSegments ); 37264 37265 // buffers 37266 37267 const indices = []; 37268 const vertices = []; 37269 const normals = []; 37270 const uvs = []; 37271 37272 // helper variables 37273 37274 const vertex = new Vector3(); 37275 const normal = new Vector3(); 37276 37277 const P1 = new Vector3(); 37278 const P2 = new Vector3(); 37279 37280 const B = new Vector3(); 37281 const T = new Vector3(); 37282 const N = new Vector3(); 37283 37284 // generate vertices, normals and uvs 37285 37286 for ( let i = 0; i <= tubularSegments; ++ i ) { 37287 37288 // the radian "u" is used to calculate the position on the torus curve of the current tubular segment 37289 37290 const u = i / tubularSegments * p * Math.PI * 2; 37291 37292 // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. 37293 // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions 37294 37295 calculatePositionOnCurve( u, p, q, radius, P1 ); 37296 calculatePositionOnCurve( u + 0.01, p, q, radius, P2 ); 37297 37298 // calculate orthonormal basis 37299 37300 T.subVectors( P2, P1 ); 37301 N.addVectors( P2, P1 ); 37302 B.crossVectors( T, N ); 37303 N.crossVectors( B, T ); 37304 37305 // normalize B, N. T can be ignored, we don't use it 37306 37307 B.normalize(); 37308 N.normalize(); 37309 37310 for ( let j = 0; j <= radialSegments; ++ j ) { 37311 37312 // now calculate the vertices. they are nothing more than an extrusion of the torus curve. 37313 // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. 37314 37315 const v = j / radialSegments * Math.PI * 2; 37316 const cx = - tube * Math.cos( v ); 37317 const cy = tube * Math.sin( v ); 37318 37319 // now calculate the final vertex position. 37320 // first we orient the extrusion with our basis vectors, then we add it to the current position on the curve 37321 37322 vertex.x = P1.x + ( cx * N.x + cy * B.x ); 37323 vertex.y = P1.y + ( cx * N.y + cy * B.y ); 37324 vertex.z = P1.z + ( cx * N.z + cy * B.z ); 37325 37326 vertices.push( vertex.x, vertex.y, vertex.z ); 37327 37328 // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) 37329 37330 normal.subVectors( vertex, P1 ).normalize(); 37331 37332 normals.push( normal.x, normal.y, normal.z ); 37333 37334 // uv 37335 37336 uvs.push( i / tubularSegments ); 37337 uvs.push( j / radialSegments ); 37338 37339 } 37340 37341 } 37342 37343 // generate indices 37344 37345 for ( let j = 1; j <= tubularSegments; j ++ ) { 37346 37347 for ( let i = 1; i <= radialSegments; i ++ ) { 37348 37349 // indices 37350 37351 const a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); 37352 const b = ( radialSegments + 1 ) * j + ( i - 1 ); 37353 const c = ( radialSegments + 1 ) * j + i; 37354 const d = ( radialSegments + 1 ) * ( j - 1 ) + i; 37355 37356 // faces 37357 37358 indices.push( a, b, d ); 37359 indices.push( b, c, d ); 37360 37361 } 37362 37363 } 37364 37365 // build geometry 37366 37367 this.setIndex( indices ); 37368 this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 37369 this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); 37370 this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); 37371 37372 // this function calculates the current position on the torus curve 37373 37374 function calculatePositionOnCurve( u, p, q, radius, position ) { 37375 37376 const cu = Math.cos( u ); 37377 const su = Math.sin( u ); 37378 const quOverP = q / p * u; 37379 const cs = Math.cos( quOverP ); 37380 37381 position.x = radius * ( 2 + cs ) * 0.5 * cu; 37382 position.y = radius * ( 2 + cs ) * su * 0.5; 37383 position.z = radius * Math.sin( quOverP ) * 0.5; 37384 37385 } 37386 37387 } 37388 37389 copy( source ) { 37390 37391 super.copy( source ); 37392 37393 this.parameters = Object.assign( {}, source.parameters ); 37394 37395 return this; 37396 37397 } 37398 37399 /** 37400 * Factory method for creating an instance of this class from the given 37401 * JSON object. 37402 * 37403 * @param {Object} data - A JSON object representing the serialized geometry. 37404 * @return {TorusKnotGeometry} A new instance. 37405 */ 37406 static fromJSON( data ) { 37407 37408 return new TorusKnotGeometry( data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q ); 37409 37410 } 37411 37412} 37413 37414/** 37415 * Creates a tube that extrudes along a 3D curve. 37416 * 37417 * ```js 37418 * class CustomSinCurve extends THREE.Curve { 37419 * 37420 * getPoint( t, optionalTarget = new THREE.Vector3() ) { 37421 * 37422 * const tx = t * 3 - 1.5; 37423 * const ty = Math.sin( 2 * Math.PI * t ); 37424 * const tz = 0; 37425 * 37426 * return optionalTarget.set( tx, ty, tz ); 37427 * } 37428 * 37429 * } 37430 * 37431 * const path = new CustomSinCurve( 10 ); 37432 * const geometry = new THREE.TubeGeometry( path, 20, 2, 8, false ); 37433 * const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); 37434 * const mesh = new THREE.Mesh( geometry, material ); 37435 * scene.add( mesh ); 37436 * ``` 37437 * 37438 * @augments BufferGeometry 37439 * @demo scenes/geometry-browser.html#TubeGeometry 37440 */ 37441class TubeGeometry extends BufferGeometry { 37442 37443 /** 37444 * Constructs a new tube geometry. 37445 * 37446 * @param {Curve} [path=QuadraticBezierCurve3] - A 3D curve defining the path of the tube. 37447 * @param {number} [tubularSegments=64] - The number of segments that make up the tube. 37448 * @param {number} [radius=1] -The radius of the tube. 37449 * @param {number} [radialSegments=8] - The number of segments that make up the cross-section. 37450 * @param {boolean} [closed=false] - Whether the tube is closed or not. 37451 */ 37452 constructor( path = new QuadraticBezierCurve3( new Vector3( -1, -1, 0 ), new Vector3( -1, 1, 0 ), new Vector3( 1, 1, 0 ) ), tubularSegments = 64, radius = 1, radialSegments = 8, closed = false ) { 37453 37454 super(); 37455 37456 this.type = 'TubeGeometry'; 37457 37458 /** 37459 * Holds the constructor parameters that have been 37460 * used to generate the geometry. Any modification 37461 * after instantiation does not change the geometry. 37462 * 37463 * @type {Object} 37464 */ 37465 this.parameters = { 37466 path: path, 37467 tubularSegments: tubularSegments, 37468 radius: radius, 37469 radialSegments: radialSegments, 37470 closed: closed 37471 }; 37472 37473 const frames = path.computeFrenetFrames( tubularSegments, closed ); 37474 37475 // expose internals 37476 37477 this.tangents = frames.tangents; 37478 this.normals = frames.normals; 37479 this.binormals = frames.binormals; 37480 37481 // helper variables 37482 37483 const vertex = new Vector3(); 37484 const normal = new Vector3(); 37485 const uv = new Vector2(); 37486 let P = new Vector3(); 37487 37488 // buffer 37489 37490 const vertices = []; 37491 const normals = []; 37492 const uvs = []; 37493 const indices = []; 37494 37495 // create buffer data 37496 37497 generateBufferData(); 37498 37499 // build geometry 37500 37501 this.setIndex( indices ); 37502 this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 37503 this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); 37504 this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); 37505 37506 // functions 37507 37508 function generateBufferData() { 37509 37510 for ( let i = 0; i < tubularSegments; i ++ ) { 37511 37512 generateSegment( i ); 37513 37514 } 37515 37516 // if the geometry is not closed, generate the last row of vertices and normals 37517 // at the regular position on the given path 37518 // 37519 // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) 37520 37521 generateSegment( ( closed === false ) ? tubularSegments : 0 ); 37522 37523 // uvs are generated in a separate function. 37524 // this makes it easy compute correct values for closed geometries 37525 37526 generateUVs(); 37527 37528 // finally create faces 37529 37530 generateIndices(); 37531 37532 } 37533 37534 function generateSegment( i ) { 37535 37536 // we use getPointAt to sample evenly distributed points from the given path 37537 37538 P = path.getPointAt( i / tubularSegments, P ); 37539 37540 // retrieve corresponding normal and binormal 37541 37542 const N = frames.normals[ i ]; 37543 const B = frames.binormals[ i ]; 37544 37545 // generate normals and vertices for the current segment 37546 37547 for ( let j = 0; j <= radialSegments; j ++ ) { 37548 37549 const v = j / radialSegments * Math.PI * 2; 37550 37551 const sin = Math.sin( v ); 37552 const cos = - Math.cos( v ); 37553 37554 // normal 37555 37556 normal.x = ( cos * N.x + sin * B.x ); 37557 normal.y = ( cos * N.y + sin * B.y ); 37558 normal.z = ( cos * N.z + sin * B.z ); 37559 normal.normalize(); 37560 37561 normals.push( normal.x, normal.y, normal.z ); 37562 37563 // vertex 37564 37565 vertex.x = P.x + radius * normal.x; 37566 vertex.y = P.y + radius * normal.y; 37567 vertex.z = P.z + radius * normal.z; 37568 37569 vertices.push( vertex.x, vertex.y, vertex.z ); 37570 37571 } 37572 37573 } 37574 37575 function generateIndices() { 37576 37577 for ( let j = 1; j <= tubularSegments; j ++ ) { 37578 37579 for ( let i = 1; i <= radialSegments; i ++ ) { 37580 37581 const a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); 37582 const b = ( radialSegments + 1 ) * j + ( i - 1 ); 37583 const c = ( radialSegments + 1 ) * j + i; 37584 const d = ( radialSegments + 1 ) * ( j - 1 ) + i; 37585 37586 // faces 37587 37588 indices.push( a, b, d ); 37589 indices.push( b, c, d ); 37590 37591 } 37592 37593 } 37594 37595 } 37596 37597 function generateUVs() { 37598 37599 for ( let i = 0; i <= tubularSegments; i ++ ) { 37600 37601 for ( let j = 0; j <= radialSegments; j ++ ) { 37602 37603 uv.x = i / tubularSegments; 37604 uv.y = j / radialSegments; 37605 37606 uvs.push( uv.x, uv.y ); 37607 37608 } 37609 37610 } 37611 37612 } 37613 37614 } 37615 37616 copy( source ) { 37617 37618 super.copy( source ); 37619 37620 this.parameters = Object.assign( {}, source.parameters ); 37621 37622 return this; 37623 37624 } 37625 37626 toJSON() { 37627 37628 const data = super.toJSON(); 37629 37630 data.path = this.parameters.path.toJSON(); 37631 37632 return data; 37633 37634 } 37635 37636 /** 37637 * Factory method for creating an instance of this class from the given 37638 * JSON object. 37639 * 37640 * @param {Object} data - A JSON object representing the serialized geometry. 37641 * @return {TubeGeometry} A new instance. 37642 */ 37643 static fromJSON( data ) { 37644 37645 // This only works for built-in curves (e.g. CatmullRomCurve3). 37646 // User defined curves or instances of CurvePath will not be deserialized. 37647 return new TubeGeometry( 37648 new Curves[ data.path.type ]().fromJSON( data.path ), 37649 data.tubularSegments, 37650 data.radius, 37651 data.radialSegments, 37652 data.closed 37653 ); 37654 37655 } 37656 37657} 37658 37659/** 37660 * Can be used as a helper object to visualize a geometry as a wireframe. 37661 * 37662 * ```js 37663 * const geometry = new THREE.SphereGeometry(); 37664 * 37665 * const wireframe = new THREE.WireframeGeometry( geometry ); 37666 * 37667 * const line = new THREE.LineSegments( wireframe ); 37668 * line.material.depthWrite = false; 37669 * line.material.opacity = 0.25; 37670 * line.material.transparent = true; 37671 * 37672 * scene.add( line ); 37673 * ``` 37674 * 37675 * Note: It is not yet possible to serialize/deserialize instances of this class. 37676 * 37677 * @augments BufferGeometry 37678 */ 37679class WireframeGeometry extends BufferGeometry { 37680 37681 /** 37682 * Constructs a new wireframe geometry. 37683 * 37684 * @param {?BufferGeometry} [geometry=null] - The geometry. 37685 */ 37686 constructor( geometry = null ) { 37687 37688 super(); 37689 37690 this.type = 'WireframeGeometry'; 37691 37692 /** 37693 * Holds the constructor parameters that have been 37694 * used to generate the geometry. Any modification 37695 * after instantiation does not change the geometry. 37696 * 37697 * @type {Object} 37698 */ 37699 this.parameters = { 37700 geometry: geometry 37701 }; 37702 37703 if ( geometry !== null ) { 37704 37705 // buffer 37706 37707 const vertices = []; 37708 const edges = new Set(); 37709 37710 // helper variables 37711 37712 const start = new Vector3(); 37713 const end = new Vector3(); 37714 37715 if ( geometry.index !== null ) { 37716 37717 // indexed BufferGeometry 37718 37719 const position = geometry.attributes.position; 37720 const indices = geometry.index; 37721 let groups = geometry.groups; 37722 37723 if ( groups.length === 0 ) { 37724 37725 groups = [ { start: 0, count: indices.count, materialIndex: 0 } ]; 37726 37727 } 37728 37729 // create a data structure that contains all edges without duplicates 37730 37731 for ( let o = 0, ol = groups.length; o < ol; ++ o ) { 37732 37733 const group = groups[ o ]; 37734 37735 const groupStart = group.start; 37736 const groupCount = group.count; 37737 37738 for ( let i = groupStart, l = ( groupStart + groupCount ); i < l; i += 3 ) { 37739 37740 for ( let j = 0; j < 3; j ++ ) { 37741 37742 const index1 = indices.getX( i + j ); 37743 const index2 = indices.getX( i + ( j + 1 ) % 3 ); 37744 37745 start.fromBufferAttribute( position, index1 ); 37746 end.fromBufferAttribute( position, index2 ); 37747 37748 if ( isUniqueEdge( start, end, edges ) === true ) { 37749 37750 vertices.push( start.x, start.y, start.z ); 37751 vertices.push( end.x, end.y, end.z ); 37752 37753 } 37754 37755 } 37756 37757 } 37758 37759 } 37760 37761 } else { 37762 37763 // non-indexed BufferGeometry 37764 37765 const position = geometry.attributes.position; 37766 37767 for ( let i = 0, l = ( position.count / 3 ); i < l; i ++ ) { 37768 37769 for ( let j = 0; j < 3; j ++ ) { 37770 37771 // three edges per triangle, an edge is represented as (index1, index2) 37772 // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) 37773 37774 const index1 = 3 * i + j; 37775 const index2 = 3 * i + ( ( j + 1 ) % 3 ); 37776 37777 start.fromBufferAttribute( position, index1 ); 37778 end.fromBufferAttribute( position, index2 ); 37779 37780 if ( isUniqueEdge( start, end, edges ) === true ) { 37781 37782 vertices.push( start.x, start.y, start.z ); 37783 vertices.push( end.x, end.y, end.z ); 37784 37785 } 37786 37787 } 37788 37789 } 37790 37791 } 37792 37793 // build geometry 37794 37795 this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 37796 37797 } 37798 37799 } 37800 37801 copy( source ) { 37802 37803 super.copy( source ); 37804 37805 this.parameters = Object.assign( {}, source.parameters ); 37806 37807 return this; 37808 37809 } 37810 37811} 37812 37813function isUniqueEdge( start, end, edges ) { 37814 37815 const hash1 = `${start.x},${start.y},${start.z}-${end.x},${end.y},${end.z}`; 37816 const hash2 = `${end.x},${end.y},${end.z}-${start.x},${start.y},${start.z}`; // coincident edge 37817 37818 if ( edges.has( hash1 ) === true || edges.has( hash2 ) === true ) { 37819 37820 return false; 37821 37822 } else { 37823 37824 edges.add( hash1 ); 37825 edges.add( hash2 ); 37826 return true; 37827 37828 } 37829 37830} 37831 37832var Geometries = /*#__PURE__*/Object.freeze({ 37833 __proto__: null, 37834 BoxGeometry: BoxGeometry, 37835 CapsuleGeometry: CapsuleGeometry, 37836 CircleGeometry: CircleGeometry, 37837 ConeGeometry: ConeGeometry, 37838 CylinderGeometry: CylinderGeometry, 37839 DodecahedronGeometry: DodecahedronGeometry, 37840 EdgesGeometry: EdgesGeometry, 37841 ExtrudeGeometry: ExtrudeGeometry, 37842 IcosahedronGeometry: IcosahedronGeometry, 37843 LatheGeometry: LatheGeometry, 37844 OctahedronGeometry: OctahedronGeometry, 37845 PlaneGeometry: PlaneGeometry, 37846 PolyhedronGeometry: PolyhedronGeometry, 37847 RingGeometry: RingGeometry, 37848 ShapeGeometry: ShapeGeometry, 37849 SphereGeometry: SphereGeometry, 37850 TetrahedronGeometry: TetrahedronGeometry, 37851 TorusGeometry: TorusGeometry, 37852 TorusKnotGeometry: TorusKnotGeometry, 37853 TubeGeometry: TubeGeometry, 37854 WireframeGeometry: WireframeGeometry 37855}); 37856 37857/** 37858 * This material can receive shadows, but otherwise is completely transparent. 37859 * 37860 * ```js 37861 * const geometry = new THREE.PlaneGeometry( 2000, 2000 ); 37862 * geometry.rotateX( - Math.PI / 2 ); 37863 * 37864 * const material = new THREE.ShadowMaterial(); 37865 * material.opacity = 0.2; 37866 * 37867 * const plane = new THREE.Mesh( geometry, material ); 37868 * plane.position.y = -200; 37869 * plane.receiveShadow = true; 37870 * scene.add( plane ); 37871 * ``` 37872 * 37873 * @augments Material 37874 */ 37875class ShadowMaterial extends Material { 37876 37877 /** 37878 * Constructs a new shadow material. 37879 * 37880 * @param {Object} [parameters] - An object with one or more properties 37881 * defining the material's appearance. Any property of the material 37882 * (including any property from inherited materials) can be passed 37883 * in here. Color values can be passed any type of value accepted 37884 * by {@link Color#set}. 37885 */ 37886 constructor( parameters ) { 37887 37888 super(); 37889 37890 /** 37891 * This flag can be used for type testing. 37892 * 37893 * @type {boolean} 37894 * @readonly 37895 * @default true 37896 */ 37897 this.isShadowMaterial = true; 37898 37899 this.type = 'ShadowMaterial'; 37900 37901 /** 37902 * Color of the material. 37903 * 37904 * @type {Color} 37905 * @default (0,0,0) 37906 */ 37907 this.color = new Color( 0x000000 ); 37908 37909 /** 37910 * Overwritten since shadow materials are transparent 37911 * by default. 37912 * 37913 * @type {boolean} 37914 * @default true 37915 */ 37916 this.transparent = true; 37917 37918 /** 37919 * Whether the material is affected by fog or not. 37920 * 37921 * @type {boolean} 37922 * @default true 37923 */ 37924 this.fog = true; 37925 37926 this.setValues( parameters ); 37927 37928 } 37929 37930 copy( source ) { 37931 37932 super.copy( source ); 37933 37934 this.color.copy( source.color ); 37935 37936 this.fog = source.fog; 37937 37938 return this; 37939 37940 } 37941 37942} 37943 37944/** 37945 * Provides utility functions for managing uniforms. 37946 * 37947 * @module UniformsUtils 37948 */ 37949 37950/** 37951 * Clones the given uniform definitions by performing a deep-copy. That means 37952 * if the value of a uniform refers to an object like a Vector3 or Texture, 37953 * the cloned uniform will refer to a new object reference. 37954 * 37955 * @param {Object} src - An object representing uniform definitions. 37956 * @return {Object} The cloned uniforms. 37957 */ 37958function cloneUniforms( src ) { 37959 37960 const dst = {}; 37961 37962 for ( const u in src ) { 37963 37964 dst[ u ] = {}; 37965 37966 for ( const p in src[ u ] ) { 37967 37968 const property = src[ u ][ p ]; 37969 37970 if ( isThreeObject( property ) ) { 37971 37972 if ( property.isRenderTargetTexture ) { 37973 37974 warn( 'UniformsUtils: Textures of render targets cannot be cloned via cloneUniforms() or mergeUniforms().' ); 37975 dst[ u ][ p ] = null; 37976 37977 } else { 37978 37979 dst[ u ][ p ] = property.clone(); 37980 37981 } 37982 37983 } else if ( Array.isArray( property ) ) { 37984 37985 if ( isThreeObject( property[ 0 ] ) ) { 37986 37987 const clonedProperty = []; 37988 37989 for ( let i = 0, l = property.length; i < l; i ++ ) { 37990 37991 clonedProperty[ i ] = property[ i ].clone(); 37992 37993 } 37994 37995 dst[ u ][ p ] = clonedProperty; 37996 37997 } else { 37998 37999 dst[ u ][ p ] = property.slice(); 38000 38001 } 38002 38003 } else { 38004 38005 dst[ u ][ p ] = property; 38006 38007 } 38008 38009 } 38010 38011 } 38012 38013 return dst; 38014 38015} 38016 38017/** 38018 * Merges the given uniform definitions into a single object. Since the 38019 * method internally uses cloneUniforms(), it performs a deep-copy when 38020 * producing the merged uniform definitions. 38021 * 38022 * @param {Array} uniforms - An array of objects containing uniform definitions. 38023 * @return {Object} The merged uniforms. 38024 */ 38025function mergeUniforms( uniforms ) { 38026 38027 const merged = {}; 38028 38029 for ( let u = 0; u < uniforms.length; u ++ ) { 38030 38031 const tmp = cloneUniforms( uniforms[ u ] ); 38032 38033 for ( const p in tmp ) { 38034 38035 merged[ p ] = tmp[ p ]; 38036 38037 } 38038 38039 } 38040 38041 return merged; 38042 38043} 38044 38045function isThreeObject( property ) { 38046 38047 return ( property && ( property.isColor || 38048 property.isMatrix3 || property.isMatrix4 || 38049 property.isVector2 || property.isVector3 || property.isVector4 || 38050 property.isTexture || property.isQuaternion ) ); 38051 38052} 38053 38054function cloneUniformsGroups( src ) { 38055 38056 const dst = []; 38057 38058 for ( let u = 0; u < src.length; u ++ ) { 38059 38060 dst.push( src[ u ].clone() ); 38061 38062 } 38063 38064 return dst; 38065 38066} 38067 38068function getUnlitUniformColorSpace( renderer ) { 38069 38070 const currentRenderTarget = renderer.getRenderTarget(); 38071 38072 if ( currentRenderTarget === null ) { 38073 38074 // https://github.com/mrdoob/three.js/pull/23937#issuecomment-1111067398 38075 return renderer.outputColorSpace; 38076 38077 } 38078 38079 // https://github.com/mrdoob/three.js/issues/27868 38080 if ( currentRenderTarget.isXRRenderTarget === true ) { 38081 38082 return currentRenderTarget.texture.colorSpace; 38083 38084 } 38085 38086 return ColorManagement.workingColorSpace; 38087 38088} 38089 38090// Legacy 38091 38092const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; 38093 38094var default_vertex = "void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}"; 38095 38096var default_fragment = "void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}"; 38097 38098/** 38099 * A material rendered with custom shaders. A shader is a small program written in GLSL. 38100 * that runs on the GPU. You may want to use a custom shader if you need to implement an 38101 * effect not included with any of the built-in materials. 38102 * 38103 * There are the following notes to bear in mind when using a `ShaderMaterial`: 38104 * 38105 * - `ShaderMaterial` can only be used with {@link WebGLRenderer}. 38106 * - Built in attributes and uniforms are passed to the shaders along with your code. If 38107 * you don't want that, use {@link RawShaderMaterial} instead. 38108 * - You can use the directive `#pragma unroll_loop_start` and `#pragma unroll_loop_end` 38109 * in order to unroll a `for` loop in GLSL by the shader preprocessor. The directive has 38110 * to be placed right above the loop. The loop formatting has to correspond to a defined standard. 38111 * - The loop has to be [normalized](https://en.wikipedia.org/wiki/Normalized_loop). 38112 * - The loop variable has to be *i*. 38113 * - The value `UNROLLED_LOOP_INDEX` will be replaced with the explicitly 38114 * value of *i* for the given iteration and can be used in preprocessor 38115 * statements. 38116 * 38117 * ```js 38118 * const material = new THREE.ShaderMaterial( { 38119 * uniforms: { 38120 * time: { value: 1.0 }, 38121 * resolution: { value: new THREE.Vector2() } 38122 * }, 38123 * vertexShader: document.getElementById( 'vertexShader' ).textContent, 38124 * fragmentShader: document.getElementById( 'fragmentShader' ).textContent 38125 * } ); 38126 * ``` 38127 * 38128 * @augments Material 38129 */ 38130class ShaderMaterial extends Material { 38131 38132 /** 38133 * Constructs a new shader material. 38134 * 38135 * @param {Object} [parameters] - An object with one or more properties 38136 * defining the material's appearance. Any property of the material 38137 * (including any property from inherited materials) can be passed 38138 * in here. Color values can be passed any type of value accepted 38139 * by {@link Color#set}. 38140 */ 38141 constructor( parameters ) { 38142 38143 super(); 38144 38145 /** 38146 * This flag can be used for type testing. 38147 * 38148 * @type {boolean} 38149 * @readonly 38150 * @default true 38151 */ 38152 this.isShaderMaterial = true; 38153 38154 this.type = 'ShaderMaterial'; 38155 38156 /** 38157 * Defines custom constants using `#define` directives within the GLSL code 38158 * for both the vertex shader and the fragment shader; each key/value pair 38159 * yields another directive. 38160 * ```js 38161 * defines: { 38162 * FOO: 15, 38163 * BAR: true 38164 * } 38165 * ``` 38166 * Yields the lines: 38167 * ``` 38168 * #define FOO 15 38169 * #define BAR true 38170 * ``` 38171 * 38172 * @type {Object} 38173 */ 38174 this.defines = {}; 38175 38176 /** 38177 * An object of the form: 38178 * ```js 38179 * { 38180 * "uniform1": { value: 1.0 }, 38181 * "uniform2": { value: 2 } 38182 * } 38183 * ``` 38184 * specifying the uniforms to be passed to the shader code; keys are uniform 38185 * names, values are definitions of the form 38186 * ``` 38187 * { 38188 * value: 1.0 38189 * } 38190 * ``` 38191 * where `value` is the value of the uniform. Names must match the name of 38192 * the uniform, as defined in the GLSL code. Note that uniforms are refreshed 38193 * on every frame, so updating the value of the uniform will immediately 38194 * update the value available to the GLSL code. 38195 * 38196 * @type {Object} 38197 */ 38198 this.uniforms = {}; 38199 38200 /** 38201 * An array holding uniforms groups for configuring UBOs. 38202 * 38203 * @type {Array<UniformsGroup>} 38204 */ 38205 this.uniformsGroups = []; 38206 38207 /** 38208 * Vertex shader GLSL code. This is the actual code for the shader. 38209 * 38210 * @type {string} 38211 */ 38212 this.vertexShader = default_vertex; 38213 38214 /** 38215 * Fragment shader GLSL code. This is the actual code for the shader. 38216 * 38217 * @type {string} 38218 */ 38219 this.fragmentShader = default_fragment; 38220 38221 /** 38222 * Controls line thickness or lines. 38223 * 38224 * WebGL and WebGPU ignore this setting and always render line primitives with a 38225 * width of one pixel. 38226 * 38227 * @type {number} 38228 * @default 1 38229 */ 38230 this.linewidth = 1; 38231 38232 /** 38233 * Renders the geometry as a wireframe. 38234 * 38235 * @type {boolean} 38236 * @default false 38237 */ 38238 this.wireframe = false; 38239 38240 /** 38241 * Controls the thickness of the wireframe. 38242 * 38243 * WebGL and WebGPU ignore this property and always render 38244 * 1 pixel wide lines. 38245 * 38246 * @type {number} 38247 * @default 1 38248 */ 38249 this.wireframeLinewidth = 1; 38250 38251 /** 38252 * Defines whether the material color is affected by global fog settings; `true` 38253 * to pass fog uniforms to the shader. 38254 * 38255 * Setting this property to `true` requires the definition of fog uniforms. It is 38256 * recommended to use `UniformsUtils.merge()` to combine the custom shader uniforms 38257 * with predefined fog uniforms. 38258 * 38259 * ```js 38260 * const material = new ShaderMaterial( { 38261 * uniforms: UniformsUtils.merge( [ UniformsLib[ 'fog' ], shaderUniforms ] ); 38262 * vertexShader: vertexShader, 38263 * fragmentShader: fragmentShader, 38264 * fog: true 38265 * } ); 38266 * ``` 38267 * 38268 * @type {boolean} 38269 * @default false 38270 */ 38271 this.fog = false; 38272 38273 /** 38274 * Defines whether this material uses lighting; `true` to pass uniform data 38275 * related to lighting to this shader. 38276 * 38277 * @type {boolean} 38278 * @default false 38279 */ 38280 this.lights = false; 38281 38282 /** 38283 * Defines whether this material supports clipping; `true` to let the renderer 38284 * pass the clippingPlanes uniform. 38285 * 38286 * @type {boolean} 38287 * @default false 38288 */ 38289 this.clipping = false; 38290 38291 /** 38292 * Overwritten and set to `true` by default. 38293 * 38294 * @type {boolean} 38295 * @default true 38296 */ 38297 this.forceSinglePass = true; 38298 38299 /** 38300 * This object allows to enable certain WebGL 2 extensions. 38301 * 38302 * - clipCullDistance: set to `true` to use vertex shader clipping 38303 * - multiDraw: set to `true` to use vertex shader multi_draw / enable gl_DrawID 38304 * 38305 * @type {{clipCullDistance:false,multiDraw:false}} 38306 */ 38307 this.extensions = { 38308 clipCullDistance: false, // set to use vertex shader clipping 38309 multiDraw: false // set to use vertex shader multi_draw / enable gl_DrawID 38310 }; 38311 38312 /** 38313 * When the rendered geometry doesn't include these attributes but the 38314 * material does, these default values will be passed to the shaders. This 38315 * avoids errors when buffer data is missing. 38316 * 38317 * - color: [ 1, 1, 1 ] 38318 * - uv: [ 0, 0 ] 38319 * - uv1: [ 0, 0 ] 38320 * 38321 * @type {Object} 38322 */ 38323 this.defaultAttributeValues = { 38324 'color': [ 1, 1, 1 ], 38325 'uv': [ 0, 0 ], 38326 'uv1': [ 0, 0 ] 38327 }; 38328 38329 /** 38330 * If set, this calls [gl.bindAttribLocation](https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/bindAttribLocation) 38331 * to bind a generic vertex index to an attribute variable. 38332 * 38333 * @type {string|undefined} 38334 * @default undefined 38335 */ 38336 this.index0AttributeName = undefined; 38337 38338 /** 38339 * Can be used to force a uniform update while changing uniforms in 38340 * {@link Object3D#onBeforeRender}. 38341 * 38342 * @type {boolean} 38343 * @default false 38344 */ 38345 this.uniformsNeedUpdate = false; 38346 38347 /** 38348 * Defines the GLSL version of custom shader code. 38349 * 38350 * @type {?(GLSL1|GLSL3)} 38351 * @default null 38352 */ 38353 this.glslVersion = null; 38354 38355 if ( parameters !== undefined ) { 38356 38357 this.setValues( parameters ); 38358 38359 } 38360 38361 } 38362 38363 copy( source ) { 38364 38365 super.copy( source ); 38366 38367 this.fragmentShader = source.fragmentShader; 38368 this.vertexShader = source.vertexShader; 38369 38370 this.uniforms = cloneUniforms( source.uniforms ); 38371 this.uniformsGroups = cloneUniformsGroups( source.uniformsGroups ); 38372 38373 this.defines = Object.assign( {}, source.defines ); 38374 38375 this.wireframe = source.wireframe; 38376 this.wireframeLinewidth = source.wireframeLinewidth; 38377 38378 this.fog = source.fog; 38379 this.lights = source.lights; 38380 this.clipping = source.clipping; 38381 38382 this.extensions = Object.assign( {}, source.extensions ); 38383 38384 this.glslVersion = source.glslVersion; 38385 38386 this.defaultAttributeValues = Object.assign( {}, source.defaultAttributeValues ); 38387 38388 this.index0AttributeName = source.index0AttributeName; 38389 38390 this.uniformsNeedUpdate = source.uniformsNeedUpdate; 38391 38392 return this; 38393 38394 } 38395 38396 toJSON( meta ) { 38397 38398 const data = super.toJSON( meta ); 38399 38400 data.glslVersion = this.glslVersion; 38401 data.uniforms = {}; 38402 38403 for ( const name in this.uniforms ) { 38404 38405 const uniform = this.uniforms[ name ]; 38406 const value = uniform.value; 38407 38408 if ( value && value.isTexture ) { 38409 38410 data.uniforms[ name ] = { 38411 type: 't', 38412 value: value.toJSON( meta ).uuid 38413 }; 38414 38415 } else if ( value && value.isColor ) { 38416 38417 data.uniforms[ name ] = { 38418 type: 'c', 38419 value: value.getHex() 38420 }; 38421 38422 } else if ( value && value.isVector2 ) { 38423 38424 data.uniforms[ name ] = { 38425 type: 'v2', 38426 value: value.toArray() 38427 }; 38428 38429 } else if ( value && value.isVector3 ) { 38430 38431 data.uniforms[ name ] = { 38432 type: 'v3', 38433 value: value.toArray() 38434 }; 38435 38436 } else if ( value && value.isVector4 ) { 38437 38438 data.uniforms[ name ] = { 38439 type: 'v4', 38440 value: value.toArray() 38441 }; 38442 38443 } else if ( value && value.isMatrix3 ) { 38444 38445 data.uniforms[ name ] = { 38446 type: 'm3', 38447 value: value.toArray() 38448 }; 38449 38450 } else if ( value && value.isMatrix4 ) { 38451 38452 data.uniforms[ name ] = { 38453 type: 'm4', 38454 value: value.toArray() 38455 }; 38456 38457 } else { 38458 38459 data.uniforms[ name ] = { 38460 value: value 38461 }; 38462 38463 // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far 38464 38465 } 38466 38467 } 38468 38469 if ( Object.keys( this.defines ).length > 0 ) data.defines = this.defines; 38470 38471 data.vertexShader = this.vertexShader; 38472 data.fragmentShader = this.fragmentShader; 38473 38474 data.lights = this.lights; 38475 data.clipping = this.clipping; 38476 38477 const extensions = {}; 38478 38479 for ( const key in this.extensions ) { 38480 38481 if ( this.extensions[ key ] === true ) extensions[ key ] = true; 38482 38483 } 38484 38485 if ( Object.keys( extensions ).length > 0 ) data.extensions = extensions; 38486 38487 return data; 38488 38489 } 38490 38491 /** 38492 * Deserializes the material from the given JSON. 38493 * 38494 * @param {Object} json - The JSON holding the serialized material. 38495 * @param {Object<string,Texture>} textures - A dictionary holding textures referenced by the material. 38496 * @return {ShaderMaterial} A reference to this material. 38497 */ 38498 fromJSON( json, textures ) { 38499 38500 super.fromJSON( json, textures ); 38501 38502 if ( json.uniforms !== undefined ) { 38503 38504 for ( const name in json.uniforms ) { 38505 38506 const uniform = json.uniforms[ name ]; 38507 38508 this.uniforms[ name ] = {}; 38509 38510 switch ( uniform.type ) { 38511 38512 case 't': 38513 this.uniforms[ name ].value = textures[ uniform.value ] || null; 38514 break; 38515 38516 case 'c': 38517 this.uniforms[ name ].value = new Color().setHex( uniform.value ); 38518 break; 38519 38520 case 'v2': 38521 this.uniforms[ name ].value = new Vector2().fromArray( uniform.value ); 38522 break; 38523 38524 case 'v3': 38525 this.uniforms[ name ].value = new Vector3().fromArray( uniform.value ); 38526 break; 38527 38528 case 'v4': 38529 this.uniforms[ name ].value = new Vector4().fromArray( uniform.value ); 38530 break; 38531 38532 case 'm3': 38533 this.uniforms[ name ].value = new Matrix3().fromArray( uniform.value ); 38534 break; 38535 38536 case 'm4': 38537 this.uniforms[ name ].value = new Matrix4().fromArray( uniform.value ); 38538 break; 38539 38540 default: 38541 this.uniforms[ name ].value = uniform.value; 38542 38543 } 38544 38545 } 38546 38547 } 38548 38549 if ( json.defines !== undefined ) this.defines = json.defines; 38550 if ( json.vertexShader !== undefined ) this.vertexShader = json.vertexShader; 38551 if ( json.fragmentShader !== undefined ) this.fragmentShader = json.fragmentShader; 38552 if ( json.glslVersion !== undefined ) this.glslVersion = json.glslVersion; 38553 38554 if ( json.extensions !== undefined ) { 38555 38556 for ( const key in json.extensions ) { 38557 38558 this.extensions[ key ] = json.extensions[ key ]; 38559 38560 } 38561 38562 } 38563 38564 if ( json.lights !== undefined ) this.lights = json.lights; 38565 if ( json.clipping !== undefined ) this.clipping = json.clipping; 38566 38567 return this; 38568 38569 } 38570 38571} 38572 38573/** 38574 * This class works just like {@link ShaderMaterial}, except that definitions 38575 * of built-in uniforms and attributes are not automatically prepended to the 38576 * GLSL shader code. 38577 * 38578 * `RawShaderMaterial` can only be used with {@link WebGLRenderer}. 38579 * 38580 * @augments ShaderMaterial 38581 */ 38582class RawShaderMaterial extends ShaderMaterial { 38583 38584 /** 38585 * Constructs a new raw shader material. 38586 * 38587 * @param {Object} [parameters] - An object with one or more properties 38588 * defining the material's appearance. Any property of the material 38589 * (including any property from inherited materials) can be passed 38590 * in here. Color values can be passed any type of value accepted 38591 * by {@link Color#set}. 38592 */ 38593 constructor( parameters ) { 38594 38595 super( parameters ); 38596 38597 /** 38598 * This flag can be used for type testing. 38599 * 38600 * @type {boolean} 38601 * @readonly 38602 * @default true 38603 */ 38604 this.isRawShaderMaterial = true; 38605 38606 this.type = 'RawShaderMaterial'; 38607 38608 } 38609 38610} 38611 38612/** 38613 * A standard physically based material, using Metallic-Roughness workflow. 38614 * 38615 * Physically based rendering (PBR) has recently become the standard in many 38616 * 3D applications, such as [Unity](https://blogs.unity3d.com/2014/10/29/physically-based-shading-in-unity-5-a-primer/), 38617 * [Unreal](https://docs.unrealengine.com/latest/INT/Engine/Rendering/Materials/PhysicallyBased/) and 38618 * [3D Studio Max](http://area.autodesk.com/blogs/the-3ds-max-blog/what039s-new-for-rendering-in-3ds-max-2017). 38619 * 38620 * This approach differs from older approaches in that instead of using 38621 * approximations for the way in which light interacts with a surface, a 38622 * physically correct model is used. The idea is that, instead of tweaking 38623 * materials to look good under specific lighting, a material can be created 38624 * that will react 'correctly' under all lighting scenarios. 38625 * 38626 * In practice this gives a more accurate and realistic looking result than 38627 * the {@link MeshLambertMaterial} or {@link MeshPhongMaterial}, at the cost of 38628 * being somewhat more computationally expensive. `MeshStandardMaterial` uses per-fragment 38629 * shading. 38630 * 38631 * Note that for best results you should always specify an environment map when using this material. 38632 * 38633 * For a non-technical introduction to the concept of PBR and how to set up a 38634 * PBR material, check out these articles by the people at [marmoset](https://www.marmoset.co): 38635 * 38636 * - [Basic Theory of Physically Based Rendering](https://www.marmoset.co/posts/basic-theory-of-physically-based-rendering/) 38637 * - [Physically Based Rendering and You Can Too](https://www.marmoset.co/posts/physically-based-rendering-and-you-can-too/) 38638 * 38639 * Technical details of the approach used in three.js (and most other PBR systems) can be found is this 38640 * [paper from Disney](https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf) 38641 * (pdf), by Brent Burley. 38642 * 38643 * @augments Material 38644 * @demo scenes/material-browser.html#MeshStandardMaterial 38645 */ 38646class MeshStandardMaterial extends Material { 38647 38648 /** 38649 * Constructs a new mesh standard material. 38650 * 38651 * @param {Object} [parameters] - An object with one or more properties 38652 * defining the material's appearance. Any property of the material 38653 * (including any property from inherited materials) can be passed 38654 * in here. Color values can be passed any type of value accepted 38655 * by {@link Color#set}. 38656 */ 38657 constructor( parameters ) { 38658 38659 super(); 38660 38661 /** 38662 * This flag can be used for type testing. 38663 * 38664 * @type {boolean} 38665 * @readonly 38666 * @default true 38667 */ 38668 this.isMeshStandardMaterial = true; 38669 38670 this.type = 'MeshStandardMaterial'; 38671 38672 this.defines = { 'STANDARD': '' }; 38673 38674 /** 38675 * Color of the material. 38676 * 38677 * @type {Color} 38678 * @default (1,1,1) 38679 */ 38680 this.color = new Color( 0xffffff ); // diffuse 38681 38682 /** 38683 * How rough the material appears. `0.0` means a smooth mirror reflection, `1.0` 38684 * means fully diffuse. If `roughnessMap` is also provided, 38685 * both values are multiplied. 38686 * 38687 * @type {number} 38688 * @default 1 38689 */ 38690 this.roughness = 1.0; 38691 38692 /** 38693 * How much the material is like a metal. Non-metallic materials such as wood 38694 * or stone use `0.0`, metallic use `1.0`, with nothing (usually) in between. 38695 * A value between `0.0` and `1.0` could be used for a rusty metal look. 38696 * If `metalnessMap` is also provided, both values are multiplied. 38697 * 38698 * @type {number} 38699 * @default 0 38700 */ 38701 this.metalness = 0.0; 38702 38703 /** 38704 * The color map. May optionally include an alpha channel, typically combined 38705 * with {@link Material#transparent} or {@link Material#alphaTest}. The texture map 38706 * color is modulated by the diffuse `color`. 38707 * 38708 * `map` represents color data, and the texture must be assigned a 38709 * {@link Texture#colorSpace}. Most `map` textures set 38710 * `texture.colorSpace = SRGBColorSpace`. 38711 * 38712 * @type {?Texture} 38713 * @default null 38714 */ 38715 this.map = null; 38716 38717 /** 38718 * The light map. Requires a second set of UVs. 38719 * 38720 * `lightMap` represents pre-baked illuminance data, and the texture must be assigned 38721 * a {@link Texture#colorSpace}. Most `lightMap` textures set 38722 * `texture.colorSpace = LinearSRGBColorSpace` and use float-type formats 38723 * such as `.exr` or `.hdr`. 38724 * 38725 * @type {?Texture} 38726 * @default null 38727 */ 38728 this.lightMap = null; 38729 38730 /** 38731 * Intensity of the baked light. 38732 * 38733 * @type {number} 38734 * @default 1 38735 */ 38736 this.lightMapIntensity = 1.0; 38737 38738 /** 38739 * The red channel of this texture is used as the ambient occlusion map. 38740 * Requires a second set of UVs. 38741 * 38742 * `aoMap` represents non-color data. Any texture assigned must have 38743 * `texture.colorSpace = NoColorSpace` (default). 38744 * 38745 * @type {?Texture} 38746 * @default null 38747 */ 38748 this.aoMap = null; 38749 38750 /** 38751 * Intensity of the ambient occlusion effect. Range is `[0,1]`, where `0` 38752 * disables ambient occlusion. Where intensity is `1` and the AO map's 38753 * red channel is also `1`, ambient light is fully occluded on a surface. 38754 * 38755 * @type {number} 38756 * @default 1 38757 */ 38758 this.aoMapIntensity = 1.0; 38759 38760 /** 38761 * Emissive (light) color of the material, essentially a solid color 38762 * unaffected by other lighting. 38763 * 38764 * @type {Color} 38765 * @default (0,0,0) 38766 */ 38767 this.emissive = new Color( 0x000000 ); 38768 38769 /** 38770 * Intensity of the emissive light. Modulates the emissive color. 38771 * 38772 * @type {number} 38773 * @default 1 38774 */ 38775 this.emissiveIntensity = 1.0; 38776 38777 /** 38778 * Set emissive (glow) map. The emissive map color is modulated by the 38779 * emissive color and the emissive intensity. If you have an emissive map, 38780 * be sure to set the emissive color to something other than black. 38781 * 38782 * `emissiveMap` represents color data, and the texture must be assigned a 38783 * {@link Texture#colorSpace}. Most `emissiveMap` textures set 38784 * `texture.colorSpace = SRGBColorSpace`. 38785 * 38786 * @type {?Texture} 38787 * @default null 38788 */ 38789 this.emissiveMap = null; 38790 38791 /** 38792 * The texture to create a bump map. The black and white values map to the 38793 * perceived depth in relation to the lights. Bump doesn't actually affect 38794 * the geometry of the object, only the lighting. If a normal map is defined 38795 * this will be ignored. 38796 * 38797 * `bumpMap` represents non-color data. Any texture assigned must have 38798 * `texture.colorSpace = NoColorSpace` (default). 38799 * 38800 * @type {?Texture} 38801 * @default null 38802 */ 38803 this.bumpMap = null; 38804 38805 /** 38806 * How much the bump map affects the material. Typical range is `[0,1]`. 38807 * 38808 * @type {number} 38809 * @default 1 38810 */ 38811 this.bumpScale = 1; 38812 38813 /** 38814 * The texture to create a normal map. The RGB values affect the surface 38815 * normal for each pixel fragment and change the way the color is lit. Normal 38816 * maps do not change the actual shape of the surface, only the lighting. In 38817 * case the material has a normal map authored using the left handed 38818 * convention, the `y` component of `normalScale` should be negated to compensate 38819 * for the different handedness. 38820 * 38821 * `normalMap` represents non-color data. Any texture assigned must have 38822 * `texture.colorSpace = NoColorSpace` (default). 38823 * 38824 * @type {?Texture} 38825 * @default null 38826 */ 38827 this.normalMap = null; 38828 38829 /** 38830 * The type of normal map. 38831 * 38832 * @type {(TangentSpaceNormalMap|ObjectSpaceNormalMap)} 38833 * @default TangentSpaceNormalMap 38834 */ 38835 this.normalMapType = TangentSpaceNormalMap; 38836 38837 /** 38838 * How much the normal map affects the material. Typical value range is `[0,1]`. 38839 * 38840 * @type {Vector2} 38841 * @default (1,1) 38842 */ 38843 this.normalScale = new Vector2( 1, 1 ); 38844 38845 /** 38846 * The displacement map affects the position of the mesh's vertices. Unlike 38847 * other maps which only affect the light and shade of the material the 38848 * displaced vertices can cast shadows, block other objects, and otherwise 38849 * act as real geometry. The displacement texture is an image where the value 38850 * of each pixel (white being the highest) is mapped against, and 38851 * repositions, the vertices of the mesh. For best results, pair a 38852 * displacement map with a matching normal map, since the renderer can 38853 * not recompute surface normals from the displaced vertices. 38854 * 38855 * `displacementMap` represents non-color data. Any texture assigned must have 38856 * `texture.colorSpace = NoColorSpace` (default). 38857 * 38858 * @type {?Texture} 38859 * @default null 38860 */ 38861 this.displacementMap = null; 38862 38863 /** 38864 * How much the displacement map affects the mesh (where black is no 38865 * displacement, and white is maximum displacement). Without a displacement 38866 * map set, this value is not applied. 38867 * 38868 * @type {number} 38869 * @default 0 38870 */ 38871 this.displacementScale = 1; 38872 38873 /** 38874 * The offset of the displacement map's values on the mesh's vertices. 38875 * The bias is added to the scaled sample of the displacement map. 38876 * Without a displacement map set, this value is not applied. 38877 * 38878 * @type {number} 38879 * @default 0 38880 */ 38881 this.displacementBias = 0; 38882 38883 /** 38884 * The green channel of this texture is used to alter the roughness of the 38885 * material. 38886 * 38887 * `roughnessMap` represents non-color data. Any texture assigned must have 38888 * `texture.colorSpace = NoColorSpace` (default). 38889 * 38890 * @type {?Texture} 38891 * @default null 38892 */ 38893 this.roughnessMap = null; 38894 38895 /** 38896 * The blue channel of this texture is used to alter the metalness of the 38897 * material. 38898 * 38899 * `metalnessMap` represents non-color data. Any texture assigned must have 38900 * `texture.colorSpace = NoColorSpace` (default). 38901 * 38902 * @type {?Texture} 38903 * @default null 38904 */ 38905 this.metalnessMap = null; 38906 38907 /** 38908 * The alpha map is a grayscale texture that controls the opacity across the 38909 * surface (black: fully transparent; white: fully opaque). 38910 * 38911 * Only the color of the texture is used, ignoring the alpha channel if one 38912 * exists. For RGB and RGBA textures, the renderer will use the green channel 38913 * when sampling this texture due to the extra bit of precision provided for 38914 * green in DXT-compressed and uncompressed RGB 565 formats. Luminance-only and 38915 * luminance/alpha textures will also still work as expected. 38916 * 38917 * `alphaMap` represents non-color data. Any texture assigned must have 38918 * `texture.colorSpace = NoColorSpace` (default). 38919 * 38920 * @type {?Texture} 38921 * @default null 38922 */ 38923 this.alphaMap = null; 38924 38925 /** 38926 * The environment map. To ensure a physically correct rendering, environment maps 38927 * are internally pre-processed with {@link PMREMGenerator}. 38928 * 38929 * `envMap` represents luminance data, and the texture must be assigned 38930 * a {@link Texture#colorSpace}. Most `envMap` textures set 38931 * `texture.colorSpace = LinearSRGBColorSpace` and use float-type formats 38932 * such as `.exr` or `.hdr`. 38933 * 38934 * @type {?Texture} 38935 * @default null 38936 */ 38937 this.envMap = null; 38938 38939 /** 38940 * The rotation of the environment map in radians. 38941 * 38942 * @type {Euler} 38943 * @default (0,0,0) 38944 */ 38945 this.envMapRotation = new Euler(); 38946 38947 /** 38948 * Scales the effect of the environment map by multiplying its color. 38949 * 38950 * @type {number} 38951 * @default 1 38952 */ 38953 this.envMapIntensity = 1.0; 38954 38955 /** 38956 * Renders the geometry as a wireframe. 38957 * 38958 * @type {boolean} 38959 * @default false 38960 */ 38961 this.wireframe = false; 38962 38963 /** 38964 * Controls the thickness of the wireframe. 38965 * 38966 * Can only be used with {@link SVGRenderer}. 38967 * 38968 * @type {number} 38969 * @default 1 38970 */ 38971 this.wireframeLinewidth = 1; 38972 38973 /** 38974 * Defines appearance of wireframe ends. 38975 * 38976 * Can only be used with {@link SVGRenderer}. 38977 * 38978 * @type {('round'|'bevel'|'miter')} 38979 * @default 'round' 38980 */ 38981 this.wireframeLinecap = 'round'; 38982 38983 /** 38984 * Defines appearance of wireframe joints. 38985 * 38986 * Can only be used with {@link SVGRenderer}. 38987 * 38988 * @type {('round'|'bevel'|'miter')} 38989 * @default 'round' 38990 */ 38991 this.wireframeLinejoin = 'round'; 38992 38993 /** 38994 * Whether the material is rendered with flat shading or not. 38995 * 38996 * @type {boolean} 38997 * @default false 38998 */ 38999 this.flatShading = false; 39000 39001 /** 39002 * Whether the material is affected by fog or not. 39003 * 39004 * @type {boolean} 39005 * @default true 39006 */ 39007 this.fog = true; 39008 39009 this.setValues( parameters ); 39010 39011 } 39012 39013 copy( source ) { 39014 39015 super.copy( source ); 39016 39017 this.defines = { 'STANDARD': '' }; 39018 39019 this.color.copy( source.color ); 39020 this.roughness = source.roughness; 39021 this.metalness = source.metalness; 39022 39023 this.map = source.map; 39024 39025 this.lightMap = source.lightMap; 39026 this.lightMapIntensity = source.lightMapIntensity; 39027 39028 this.aoMap = source.aoMap; 39029 this.aoMapIntensity = source.aoMapIntensity; 39030 39031 this.emissive.copy( source.emissive ); 39032 this.emissiveMap = source.emissiveMap; 39033 this.emissiveIntensity = source.emissiveIntensity; 39034 39035 this.bumpMap = source.bumpMap; 39036 this.bumpScale = source.bumpScale; 39037 39038 this.normalMap = source.normalMap; 39039 this.normalMapType = source.normalMapType; 39040 this.normalScale.copy( source.normalScale ); 39041 39042 this.displacementMap = source.displacementMap; 39043 this.displacementScale = source.displacementScale; 39044 this.displacementBias = source.displacementBias; 39045 39046 this.roughnessMap = source.roughnessMap; 39047 39048 this.metalnessMap = source.metalnessMap; 39049 39050 this.alphaMap = source.alphaMap; 39051 39052 this.envMap = source.envMap; 39053 this.envMapRotation.copy( source.envMapRotation ); 39054 this.envMapIntensity = source.envMapIntensity; 39055 39056 this.wireframe = source.wireframe; 39057 this.wireframeLinewidth = source.wireframeLinewidth; 39058 this.wireframeLinecap = source.wireframeLinecap; 39059 this.wireframeLinejoin = source.wireframeLinejoin; 39060 39061 this.flatShading = source.flatShading; 39062 39063 this.fog = source.fog; 39064 39065 return this; 39066 39067 } 39068 39069} 39070 39071/** 39072 * An extension of the {@link MeshStandardMaterial}, providing more advanced 39073 * physically-based rendering properties: 39074 * 39075 * - Anisotropy: Ability to represent the anisotropic property of materials 39076 * as observable with brushed metals. 39077 * - Clearcoat: Some materials â like car paints, carbon fiber, and wet surfaces â require 39078 * a clear, reflective layer on top of another layer that may be irregular or rough. 39079 * Clearcoat approximates this effect, without the need for a separate transparent surface. 39080 * - Iridescence: Allows to render the effect where hue varies depending on the viewing 39081 * angle and illumination angle. This can be seen on soap bubbles, oil films, or on the 39082 * wings of many insects. 39083 * - Physically-based transparency: One limitation of {@link Material#opacity} is that highly 39084 * transparent materials are less reflective. Physically-based transmission provides a more 39085 * realistic option for thin, transparent surfaces like glass. 39086 * - Advanced reflectivity: More flexible reflectivity for non-metallic materials. 39087 * - Retroreflection: Redirects specular light back toward the light source for 39088 * safety materials like road markings and reflective tape. 39089 * - Sheen: Can be used for representing cloth and fabric materials. 39090 * 39091 * As a result of these complex shading features, `MeshPhysicalMaterial` has a 39092 * higher performance cost, per pixel, than other three.js materials. Most 39093 * effects are disabled by default, and add cost as they are enabled. For 39094 * best results, always specify an environment map when using this material. 39095 * 39096 * @augments MeshStandardMaterial 39097 * @demo scenes/material-browser.html#MeshPhysicalMaterial 39098 */ 39099class MeshPhysicalMaterial extends MeshStandardMaterial { 39100 39101 /** 39102 * Constructs a new mesh physical material. 39103 * 39104 * @param {Object} [parameters] - An object with one or more properties 39105 * defining the material's appearance. Any property of the material 39106 * (including any property from inherited materials) can be passed 39107 * in here. Color values can be passed any type of value accepted 39108 * by {@link Color#set}. 39109 */ 39110 constructor( parameters ) { 39111 39112 super(); 39113 39114 /** 39115 * This flag can be used for type testing. 39116 * 39117 * @type {boolean} 39118 * @readonly 39119 * @default true 39120 */ 39121 this.isMeshPhysicalMaterial = true; 39122 39123 this.defines = { 39124 39125 'STANDARD': '', 39126 'PHYSICAL': '' 39127 39128 }; 39129 39130 this.type = 'MeshPhysicalMaterial'; 39131 39132 /** 39133 * The rotation of the anisotropy in tangent, bitangent space, measured in radians 39134 * counter-clockwise from the tangent. When `anisotropyMap` is present, this 39135 * property provides additional rotation to the vectors in the texture. 39136 * 39137 * @type {number} 39138 * @default 1 39139 */ 39140 this.anisotropyRotation = 0; 39141 39142 /** 39143 * Red and green channels represent the anisotropy direction in `[-1, 1]` tangent, 39144 * bitangent space, to be rotated by `anisotropyRotation`. The blue channel 39145 * contains strength as `[0, 1]` to be multiplied by `anisotropy`. 39146 * 39147 * `anisotropyMap` represents non-color data. Any texture assigned must have 39148 * `texture.colorSpace = NoColorSpace` (default). 39149 * 39150 * @type {?Texture} 39151 * @default null 39152 */ 39153 this.anisotropyMap = null; 39154 39155 /** 39156 * The red channel of this texture is multiplied against `clearcoat`, 39157 * for per-pixel control over a coating's intensity. 39158 * 39159 * `clearcoatMap` represents non-color data. Any texture assigned must have 39160 * `texture.colorSpace = NoColorSpace` (default). 39161 * 39162 * @type {?Texture} 39163 * @default null 39164 */ 39165 this.clearcoatMap = null; 39166 39167 /** 39168 * Roughness of the clear coat layer, from `0.0` to `1.0`. 39169 * 39170 * @type {number} 39171 * @default 0 39172 */ 39173 this.clearcoatRoughness = 0.0; 39174 39175 /** 39176 * The green channel of this texture is multiplied against 39177 * `clearcoatRoughness`, for per-pixel control over a coating's roughness. 39178 * 39179 * `clearcoatRoughnessMap` represents non-color data. Any texture assigned must have 39180 * `texture.colorSpace = NoColorSpace` (default). 39181 * 39182 * @type {?Texture} 39183 * @default null 39184 */ 39185 this.clearcoatRoughnessMap = null; 39186 39187 /** 39188 * How much `clearcoatNormalMap` affects the clear coat layer, from 39189 * `(0,0)` to `(1,1)`. 39190 * 39191 * @type {Vector2} 39192 * @default (1,1) 39193 */ 39194 this.clearcoatNormalScale = new Vector2( 1, 1 ); 39195 39196 /** 39197 * Can be used to enable independent normals for the clear coat layer. 39198 * 39199 * `clearcoatNormalMap` represents non-color data. Any texture assigned must have 39200 * `texture.colorSpace = NoColorSpace` (default). 39201 * 39202 * @type {?Texture} 39203 * @default null 39204 */ 39205 this.clearcoatNormalMap = null; 39206 39207 /** 39208 * Index-of-refraction for non-metallic materials, from `1.0` to `2.333`. 39209 * 39210 * @type {number} 39211 * @default 1.5 39212 */ 39213 this.ior = 1.5; 39214 39215 /** 39216 * Degree of reflectivity, from `0.0` to `1.0`. Default is `0.5`, which 39217 * corresponds to an index-of-refraction of `1.5`. 39218 * 39219 * This models the reflectivity of non-metallic materials. It has no effect 39220 * when `metalness` is `1.0` 39221 * 39222 * @name MeshPhysicalMaterial#reflectivity 39223 * @type {number} 39224 * @default 0.5 39225 */ 39226 Object.defineProperty( this, 'reflectivity', { 39227 get: function () { 39228 39229 return ( clamp( 2.5 * ( this.ior - 1 ) / ( this.ior + 1 ), 0, 1 ) ); 39230 39231 }, 39232 set: function ( reflectivity ) { 39233 39234 this.ior = ( 1 + 0.4 * reflectivity ) / ( 1 - 0.4 * reflectivity ); 39235 39236 } 39237 } ); 39238 39239 /** 39240 * The red channel of this texture is multiplied against `iridescence`, for per-pixel 39241 * control over iridescence. 39242 * 39243 * `iridescenceMap` represents non-color data. Any texture assigned must have 39244 * `texture.colorSpace = NoColorSpace` (default). 39245 * 39246 * @type {?Texture} 39247 * @default null 39248 */ 39249 this.iridescenceMap = null; 39250 39251 /** 39252 * Strength of the iridescence RGB color shift effect, represented by an index-of-refraction. 39253 * Between `1.0` to `2.333`. 39254 * 39255 * @type {number} 39256 * @default 1.3 39257 */ 39258 this.iridescenceIOR = 1.3; 39259 39260 /** 39261 *Array of exactly 2 elements, specifying minimum and maximum thickness of the iridescence layer. 39262 Thickness of iridescence layer has an equivalent effect of the one `thickness` has on `ior`. 39263 * 39264 * @type {Array<number,number>} 39265 * @default [100,400] 39266 */ 39267 this.iridescenceThicknessRange = [ 100, 400 ]; 39268 39269 /** 39270 * A texture that defines the thickness of the iridescence layer, stored in the green channel. 39271 * Minimum and maximum values of thickness are defined by `iridescenceThicknessRange` array: 39272 * - `0.0` in the green channel will result in thickness equal to first element of the array. 39273 * - `1.0` in the green channel will result in thickness equal to second element of the array. 39274 * - Values in-between will linearly interpolate between the elements of the array. 39275 * 39276 * `iridescenceThicknessMap` represents non-color data. Any texture assigned must have 39277 * `texture.colorSpace = NoColorSpace` (default). 39278 * 39279 * @type {?Texture} 39280 * @default null 39281 */ 39282 this.iridescenceThicknessMap = null; 39283 39284 /** 39285 * The sheen tint. 39286 * 39287 * @type {Color} 39288 * @default (0,0,0) 39289 */ 39290 this.sheenColor = new Color( 0x000000 ); 39291 39292 /** 39293 * The RGB channels of this texture are multiplied against `sheenColor`, for per-pixel control 39294 * over sheen tint. 39295 * 39296 * `sheenColorMap` represents color data, and the texture must be assigned a 39297 * {@link Texture#colorSpace}. Most `sheenColorMap` textures set 39298 * `texture.colorSpace = SRGBColorSpace`. 39299 * 39300 * @type {?Texture} 39301 * @default null 39302 */ 39303 this.sheenColorMap = null; 39304 39305 /** 39306 * Roughness of the sheen layer, from `0.0` to `1.0`. 39307 * 39308 * @type {number} 39309 * @default 1 39310 */ 39311 this.sheenRoughness = 1.0; 39312 39313 /** 39314 * The alpha channel of this texture is multiplied against `sheenRoughness`, for per-pixel control 39315 * over sheen roughness. 39316 * 39317 * `sheenRoughnessMap` represents non-color data. Any texture assigned must have 39318 * `texture.colorSpace = NoColorSpace` (default). 39319 * 39320 * @type {?Texture} 39321 * @default null 39322 */ 39323 this.sheenRoughnessMap = null; 39324 39325 /** 39326 * The red channel of this texture is multiplied against `transmission`, for per-pixel control over 39327 * optical transparency. 39328 * 39329 * `transmissionMap` represents non-color data. Any texture assigned must have 39330 * `texture.colorSpace = NoColorSpace` (default). 39331 * 39332 * @type {?Texture} 39333 * @default null 39334 */ 39335 this.transmissionMap = null; 39336 39337 /** 39338 * The thickness of the volume beneath the surface. The value is given in the 39339 * coordinate space of the mesh. If the value is `0` the material is 39340 * thin-walled. Otherwise the material is a volume boundary. 39341 * 39342 * @type {number} 39343 * @default 0 39344 */ 39345 this.thickness = 0; 39346 39347 /** 39348 * A texture that defines the thickness, stored in the green channel. This will 39349 * be multiplied by `thickness`. 39350 * 39351 * `thicknessMap` represents non-color data. Any texture assigned must have 39352 * `texture.colorSpace = NoColorSpace` (default). 39353 * 39354 * @type {?Texture} 39355 * @default null 39356 */ 39357 this.thicknessMap = null; 39358 39359 /** 39360 * Density of the medium given as the average distance that light travels in 39361 * the medium before interacting with a particle. The value is given in world 39362 * space units, and must be greater than zero. 39363 * 39364 * @type {number} 39365 * @default Infinity 39366 */ 39367 this.attenuationDistance = Infinity; 39368 39369 /** 39370 * The color that white light turns into due to absorption when reaching the 39371 * attenuation distance. 39372 * 39373 * @type {Color} 39374 * @default (1,1,1) 39375 */ 39376 this.attenuationColor = new Color( 1, 1, 1 ); 39377 39378 /** 39379 * A float that scales the amount of specular reflection for non-metals only. 39380 * When set to zero, the model is effectively Lambertian. From `0.0` to `1.0`. 39381 * 39382 * @type {number} 39383 * @default 1 39384 */ 39385 this.specularIntensity = 1.0; 39386 39387 /** 39388 * The alpha channel of this texture is multiplied against `specularIntensity`, 39389 * for per-pixel control over specular intensity. 39390 * 39391 * `specularIntensityMap` represents non-color data. Any texture assigned must have 39392 * `texture.colorSpace = NoColorSpace` (default). 39393 * 39394 * @type {?Texture} 39395 * @default null 39396 */ 39397 this.specularIntensityMap = null; 39398 39399 /** 39400 * Tints the specular reflection at normal incidence for non-metals only. 39401 * 39402 * @type {Color} 39403 * @default (1,1,1) 39404 */ 39405 this.specularColor = new Color( 1, 1, 1 ); 39406 39407 /** 39408 * The RGB channels of this texture are multiplied against `specularColor`, 39409 * for per-pixel control over specular color. 39410 * 39411 * `specularColorMap` represents color data, and the texture must be assigned a 39412 * {@link Texture#colorSpace}. Most `specularColorMap` textures set 39413 * `texture.colorSpace = SRGBColorSpace`. 39414 * 39415 * @type {?Texture} 39416 * @default null 39417 */ 39418 this.specularColorMap = null; 39419 39420 this._anisotropy = 0; 39421 this._clearcoat = 0; 39422 this._dispersion = 0; 39423 this._iridescence = 0; 39424 this._retroreflectivity = 0; 39425 this._sheen = 0.0; 39426 this._transmission = 0; 39427 39428 this.setValues( parameters ); 39429 39430 } 39431 39432 /** 39433 * The anisotropy strength, from `0.0` to `1.0`. 39434 * 39435 * @type {number} 39436 * @default 0 39437 */ 39438 get anisotropy() { 39439 39440 return this._anisotropy; 39441 39442 } 39443 39444 set anisotropy( value ) { 39445 39446 if ( this._anisotropy > 0 !== value > 0 ) { 39447 39448 this.version ++; 39449 39450 } 39451 39452 this._anisotropy = value; 39453 39454 } 39455 39456 /** 39457 * Represents the intensity of the clear coat layer, from `0.0` to `1.0`. Use 39458 * clear coat related properties to enable multilayer materials that have a 39459 * thin translucent layer over the base layer. 39460 * 39461 * @type {number} 39462 * @default 0 39463 */ 39464 get clearcoat() { 39465 39466 return this._clearcoat; 39467 39468 } 39469 39470 set clearcoat( value ) { 39471 39472 if ( this._clearcoat > 0 !== value > 0 ) { 39473 39474 this.version ++; 39475 39476 } 39477 39478 this._clearcoat = value; 39479 39480 } 39481 /** 39482 * The intensity of the iridescence layer, simulating RGB color shift based on the angle between 39483 * the surface and the viewer, from `0.0` to `1.0`. 39484 * 39485 * @type {number} 39486 * @default 0 39487 */ 39488 get iridescence() { 39489 39490 return this._iridescence; 39491 39492 } 39493 39494 set iridescence( value ) { 39495 39496 if ( this._iridescence > 0 !== value > 0 ) { 39497 39498 this.version ++; 39499 39500 } 39501 39502 this._iridescence = value; 39503 39504 } 39505 39506 /** 39507 * Defines the strength of the angular separation of colors (chromatic aberration) transmitting 39508 * through a relatively clear volume. Any value zero or larger is valid, the typical range of 39509 * realistic values is `[0, 1]`. This property can be only be used with transmissive objects. 39510 * 39511 * @type {number} 39512 * @default 0 39513 */ 39514 get dispersion() { 39515 39516 return this._dispersion; 39517 39518 } 39519 39520 set dispersion( value ) { 39521 39522 if ( this._dispersion > 0 !== value > 0 ) { 39523 39524 this.version ++; 39525 39526 } 39527 39528 this._dispersion = value; 39529 39530 } 39531 39532 /** 39533 * The strength of retroreflection, from `0.0` to `1.0`. A value of `1.0` 39534 * evaluates the material's microfacet reflection with the view direction 39535 * reflected about the surface normal, redirecting the specular lobe back 39536 * toward the light source. 39537 * 39538 * @type {number} 39539 * @default 0 39540 */ 39541 get retroreflectivity() { 39542 39543 return this._retroreflectivity; 39544 39545 } 39546 39547 set retroreflectivity( value ) { 39548 39549 if ( this._retroreflectivity > 0 !== value > 0 ) { 39550 39551 this.version ++; 39552 39553 } 39554 39555 this._retroreflectivity = value; 39556 39557 } 39558 39559 /** 39560 * The intensity of the sheen layer, from `0.0` to `1.0`. 39561 * 39562 * @type {number} 39563 * @default 0 39564 */ 39565 get sheen() { 39566 39567 return this._sheen; 39568 39569 } 39570 39571 set sheen( value ) { 39572 39573 if ( this._sheen > 0 !== value > 0 ) { 39574 39575 this.version ++; 39576 39577 } 39578 39579 this._sheen = value; 39580 39581 } 39582 39583 /** 39584 * Degree of transmission (or optical transparency), from `0.0` to `1.0`. 39585 * 39586 * Thin, transparent or semitransparent, plastic or glass materials remain 39587 * largely reflective even if they are fully transmissive. The transmission 39588 * property can be used to model these materials. 39589 * 39590 * When transmission is non-zero, `opacity` should be set to `1`. 39591 * 39592 * @type {number} 39593 * @default 0 39594 */ 39595 get transmission() { 39596 39597 return this._transmission; 39598 39599 } 39600 39601 set transmission( value ) { 39602 39603 if ( this._transmission > 0 !== value > 0 ) { 39604 39605 this.version ++; 39606 39607 } 39608 39609 this._transmission = value; 39610 39611 } 39612 39613 copy( source ) { 39614 39615 super.copy( source ); 39616 39617 this.defines = { 39618 39619 'STANDARD': '', 39620 'PHYSICAL': '' 39621 39622 }; 39623 39624 this.anisotropy = source.anisotropy; 39625 this.anisotropyRotation = source.anisotropyRotation; 39626 this.anisotropyMap = source.anisotropyMap; 39627 39628 this.clearcoat = source.clearcoat; 39629 this.clearcoatMap = source.clearcoatMap; 39630 this.clearcoatRoughness = source.clearcoatRoughness; 39631 this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; 39632 this.clearcoatNormalMap = source.clearcoatNormalMap; 39633 this.clearcoatNormalScale.copy( source.clearcoatNormalScale ); 39634 39635 this.dispersion = source.dispersion; 39636 this.ior = source.ior; 39637 39638 this.iridescence = source.iridescence; 39639 this.iridescenceMap = source.iridescenceMap; 39640 this.iridescenceIOR = source.iridescenceIOR; 39641 this.iridescenceThicknessRange = [ ...source.iridescenceThicknessRange ]; 39642 this.iridescenceThicknessMap = source.iridescenceThicknessMap; 39643 39644 this.retroreflectivity = source.retroreflectivity; 39645 39646 this.sheen = source.sheen; 39647 this.sheenColor.copy( source.sheenColor ); 39648 this.sheenColorMap = source.sheenColorMap; 39649 this.sheenRoughness = source.sheenRoughness; 39650 this.sheenRoughnessMap = source.sheenRoughnessMap; 39651 39652 this.transmission = source.transmission; 39653 this.transmissionMap = source.transmissionMap; 39654 39655 this.thickness = source.thickness; 39656 this.thicknessMap = source.thicknessMap; 39657 this.attenuationDistance = source.attenuationDistance; 39658 this.attenuationColor.copy( source.attenuationColor ); 39659 39660 this.specularIntensity = source.specularIntensity; 39661 this.specularIntensityMap = source.specularIntensityMap; 39662 this.specularColor.copy( source.specularColor ); 39663 this.specularColorMap = source.specularColorMap; 39664 39665 return this; 39666 39667 } 39668 39669} 39670 39671/** 39672 * A material for shiny surfaces with specular highlights. 39673 * 39674 * The material uses a non-physically based [Blinn-Phong](https://en.wikipedia.org/wiki/Blinn-Phong_shading_model) 39675 * model for calculating reflectance. Unlike the Lambertian model used in the 39676 * {@link MeshLambertMaterial} this can simulate shiny surfaces with specular 39677 * highlights (such as varnished wood). `MeshPhongMaterial` uses per-fragment shading. 39678 * 39679 * Performance will generally be greater when using this material over the 39680 * {@link MeshStandardMaterial} or {@link MeshPhysicalMaterial}, at the cost of 39681 * some graphical accuracy. 39682 * 39683 * @augments Material 39684 * @demo scenes/material-browser.html#MeshPhongMaterial 39685 */ 39686class MeshPhongMaterial extends Material { 39687 39688 /** 39689 * Constructs a new mesh phong material. 39690 * 39691 * @param {Object} [parameters] - An object with one or more properties 39692 * defining the material's appearance. Any property of the material 39693 * (including any property from inherited materials) can be passed 39694 * in here. Color values can be passed any type of value accepted 39695 * by {@link Color#set}. 39696 */ 39697 constructor( parameters ) { 39698 39699 super(); 39700 39701 /** 39702 * This flag can be used for type testing. 39703 * 39704 * @type {boolean} 39705 * @readonly 39706 * @default true 39707 */ 39708 this.isMeshPhongMaterial = true; 39709 39710 this.type = 'MeshPhongMaterial'; 39711 39712 /** 39713 * Color of the material. 39714 * 39715 * @type {Color} 39716 * @default (1,1,1) 39717 */ 39718 this.color = new Color( 0xffffff ); // diffuse 39719 39720 /** 39721 * Specular color of the material. The default color is set to `0x111111` (very dark grey) 39722 * 39723 * This defines how shiny the material is and the color of its shine. 39724 * 39725 * @type {Color} 39726 */ 39727 this.specular = new Color( 0x111111 ); 39728 39729 /** 39730 * How shiny the specular highlight is; a higher value gives a sharper highlight. 39731 * 39732 * @type {number} 39733 * @default 30 39734 */ 39735 this.shininess = 30; 39736 39737 /** 39738 * The color map. May optionally include an alpha channel, typically combined 39739 * with {@link Material#transparent} or {@link Material#alphaTest}. The texture map 39740 * color is modulated by the diffuse `color`. 39741 * 39742 * `map` represents color data, and the texture must be assigned a 39743 * {@link Texture#colorSpace}. Most `map` textures set 39744 * `texture.colorSpace = SRGBColorSpace`. 39745 * 39746 * @type {?Texture} 39747 * @default null 39748 */ 39749 this.map = null; 39750 39751 /** 39752 * The light map. Requires a second set of UVs. 39753 * 39754 * `lightMap` represents pre-baked illuminance data, and the texture must be assigned 39755 * a {@link Texture#colorSpace}. Most `lightMap` textures set 39756 * `texture.colorSpace = LinearSRGBColorSpace` and use float-type formats 39757 * such as `.exr` or `.hdr`. 39758 * 39759 * @type {?Texture} 39760 * @default null 39761 */ 39762 this.lightMap = null; 39763 39764 /** 39765 * Intensity of the baked light. 39766 * 39767 * @type {number} 39768 * @default 1 39769 */ 39770 this.lightMapIntensity = 1.0; 39771 39772 /** 39773 * The red channel of this texture is used as the ambient occlusion map. 39774 * Requires a second set of UVs. 39775 * 39776 * `aoMap` represents non-color data. Any texture assigned must have 39777 * `texture.colorSpace = NoColorSpace` (default). 39778 * 39779 * @type {?Texture} 39780 * @default null 39781 */ 39782 this.aoMap = null; 39783 39784 /** 39785 * Intensity of the ambient occlusion effect. Range is `[0,1]`, where `0` 39786 * disables ambient occlusion. Where intensity is `1` and the AO map's 39787 * red channel is also `1`, ambient light is fully occluded on a surface. 39788 * 39789 * @type {number} 39790 * @default 1 39791 */ 39792 this.aoMapIntensity = 1.0; 39793 39794 /** 39795 * Emissive (light) color of the material, essentially a solid color 39796 * unaffected by other lighting. 39797 * 39798 * @type {Color} 39799 * @default (0,0,0) 39800 */ 39801 this.emissive = new Color( 0x000000 ); 39802 39803 /** 39804 * Intensity of the emissive light. Modulates the emissive color. 39805 * 39806 * @type {number} 39807 * @default 1 39808 */ 39809 this.emissiveIntensity = 1.0; 39810 39811 /** 39812 * Set emissive (glow) map. The emissive map color is modulated by the 39813 * emissive color and the emissive intensity. If you have an emissive map, 39814 * be sure to set the emissive color to something other than black. 39815 * 39816 * `emissiveMap` represents color data, and the texture must be assigned a 39817 * {@link Texture#colorSpace}. Most `emissiveMap` textures set 39818 * `texture.colorSpace = SRGBColorSpace`. 39819 * 39820 * @type {?Texture} 39821 * @default null 39822 */ 39823 this.emissiveMap = null; 39824 39825 /** 39826 * The texture to create a bump map. The black and white values map to the 39827 * perceived depth in relation to the lights. Bump doesn't actually affect 39828 * the geometry of the object, only the lighting. If a normal map is defined 39829 * this will be ignored. 39830 * 39831 * `bumpMap` represents non-color data. Any texture assigned must have 39832 * `texture.colorSpace = NoColorSpace` (default). 39833 * 39834 * @type {?Texture} 39835 * @default null 39836 */ 39837 this.bumpMap = null; 39838 39839 /** 39840 * How much the bump map affects the material. Typical range is `[0,1]`. 39841 * 39842 * @type {number} 39843 * @default 1 39844 */ 39845 this.bumpScale = 1; 39846 39847 /** 39848 * The texture to create a normal map. The RGB values affect the surface 39849 * normal for each pixel fragment and change the way the color is lit. Normal 39850 * maps do not change the actual shape of the surface, only the lighting. In 39851 * case the material has a normal map authored using the left handed 39852 * convention, the `y` component of `normalScale` should be negated to compensate 39853 * for the different handedness. 39854 * 39855 * `normalMap` represents non-color data. Any texture assigned must have 39856 * `texture.colorSpace = NoColorSpace` (default). 39857 * 39858 * @type {?Texture} 39859 * @default null 39860 */ 39861 this.normalMap = null; 39862 39863 /** 39864 * The type of normal map. 39865 * 39866 * @type {(TangentSpaceNormalMap|ObjectSpaceNormalMap)} 39867 * @default TangentSpaceNormalMap 39868 */ 39869 this.normalMapType = TangentSpaceNormalMap; 39870 39871 /** 39872 * How much the normal map affects the material. Typical value range is `[0,1]`. 39873 * 39874 * @type {Vector2} 39875 * @default (1,1) 39876 */ 39877 this.normalScale = new Vector2( 1, 1 ); 39878 39879 /** 39880 * The displacement map affects the position of the mesh's vertices. Unlike 39881 * other maps which only affect the light and shade of the material the 39882 * displaced vertices can cast shadows, block other objects, and otherwise 39883 * act as real geometry. The displacement texture is an image where the value 39884 * of each pixel (white being the highest) is mapped against, and 39885 * repositions, the vertices of the mesh. For best results, pair a 39886 * displacement map with a matching normal map, since the renderer can 39887 * not recompute surface normals from the displaced vertices. 39888 * 39889 * `displacementMap` represents non-color data. Any texture assigned must have 39890 * `texture.colorSpace = NoColorSpace` (default). 39891 * 39892 * @type {?Texture} 39893 * @default null 39894 */ 39895 this.displacementMap = null; 39896 39897 /** 39898 * How much the displacement map affects the mesh (where black is no 39899 * displacement, and white is maximum displacement). Without a displacement 39900 * map set, this value is not applied. 39901 * 39902 * @type {number} 39903 * @default 0 39904 */ 39905 this.displacementScale = 1; 39906 39907 /** 39908 * The offset of the displacement map's values on the mesh's vertices. 39909 * The bias is added to the scaled sample of the displacement map. 39910 * Without a displacement map set, this value is not applied. 39911 * 39912 * @type {number} 39913 * @default 0 39914 */ 39915 this.displacementBias = 0; 39916 39917 /** 39918 * The specular map value affects both how much the specular surface 39919 * highlight contributes and how much of the environment map affects the 39920 * surface. 39921 * 39922 * `specularMap` represents color data, and the texture must be assigned a 39923 * {@link Texture#colorSpace}. Most `specularMap` textures set 39924 * `texture.colorSpace = SRGBColorSpace`. 39925 * 39926 * @type {?Texture} 39927 * @default null 39928 */ 39929 this.specularMap = null; 39930 39931 /** 39932 * The alpha map is a grayscale texture that controls the opacity across the 39933 * surface (black: fully transparent; white: fully opaque). 39934 * 39935 * Only the color of the texture is used, ignoring the alpha channel if one 39936 * exists. For RGB and RGBA textures, the renderer will use the green channel 39937 * when sampling this texture due to the extra bit of precision provided for 39938 * green in DXT-compressed and uncompressed RGB 565 formats. Luminance-only and 39939 * luminance/alpha textures will also still work as expected. 39940 * 39941 * `alphaMap` represents non-color data. Any texture assigned must have 39942 * `texture.colorSpace = NoColorSpace` (default). 39943 * 39944 * @type {?Texture} 39945 * @default null 39946 */ 39947 this.alphaMap = null; 39948 39949 /** 39950 * The environment map. 39951 * 39952 * `envMap` represents luminance data, and the texture must be assigned 39953 * a {@link Texture#colorSpace}. Most `envMap` textures set 39954 * `texture.colorSpace = LinearSRGBColorSpace` and use float-type formats 39955 * such as `.exr` or `.hdr`. 39956 * 39957 * @type {?Texture} 39958 * @default null 39959 */ 39960 this.envMap = null; 39961 39962 /** 39963 * The rotation of the environment map in radians. 39964 * 39965 * @type {Euler} 39966 * @default (0,0,0) 39967 */ 39968 this.envMapRotation = new Euler(); 39969 39970 /** 39971 * How to combine the result of the surface's color with the environment map, if any. 39972 * 39973 * When set to `MixOperation`, the {@link MeshBasicMaterial#reflectivity} is used to 39974 * blend between the two colors. 39975 * 39976 * @type {(MultiplyOperation|MixOperation|AddOperation)} 39977 * @default MultiplyOperation 39978 */ 39979 this.combine = MultiplyOperation; 39980 39981 /** 39982 * How much the environment map affects the surface. 39983 * The valid range is between `0` (no reflections) and `1` (full reflections). 39984 * 39985 * @type {number} 39986 * @default 1 39987 */ 39988 this.reflectivity = 1; 39989 39990 /** 39991 * Scales the effect of the environment map by multiplying its color. 39992 * 39993 * @type {number} 39994 * @default 1 39995 */ 39996 this.envMapIntensity = 1.0; 39997 39998 /** 39999 * The index of refraction (IOR) of air (approximately 1) divided by the 40000 * index of refraction of the material. It is used with environment mapping 40001 * modes {@link CubeRefractionMapping} and {@link EquirectangularRefractionMapping}. 40002 * The refraction ratio should not exceed `1`. 40003 * 40004 * @type {number} 40005 * @default 0.98 40006 */ 40007 this.refractionRatio = 0.98; 40008 40009 /** 40010 * Renders the geometry as a wireframe. 40011 * 40012 * @type {boolean} 40013 * @default false 40014 */ 40015 this.wireframe = false; 40016 40017 /** 40018 * Controls the thickness of the wireframe. 40019 * 40020 * Can only be used with {@link SVGRenderer}. 40021 * 40022 * @type {number} 40023 * @default 1 40024 */ 40025 this.wireframeLinewidth = 1; 40026 40027 /** 40028 * Defines appearance of wireframe ends. 40029 * 40030 * Can only be used with {@link SVGRenderer}. 40031 * 40032 * @type {('round'|'bevel'|'miter')} 40033 * @default 'round' 40034 */ 40035 this.wireframeLinecap = 'round'; 40036 40037 /** 40038 * Defines appearance of wireframe joints. 40039 * 40040 * Can only be used with {@link SVGRenderer}. 40041 * 40042 * @type {('round'|'bevel'|'miter')} 40043 * @default 'round' 40044 */ 40045 this.wireframeLinejoin = 'round'; 40046 40047 /** 40048 * Whether the material is rendered with flat shading or not. 40049 * 40050 * @type {boolean} 40051 * @default false 40052 */ 40053 this.flatShading = false; 40054 40055 /** 40056 * Whether the material is affected by fog or not. 40057 * 40058 * @type {boolean} 40059 * @default true 40060 */ 40061 this.fog = true; 40062 40063 this.setValues( parameters ); 40064 40065 } 40066 40067 copy( source ) { 40068 40069 super.copy( source ); 40070 40071 this.color.copy( source.color ); 40072 this.specular.copy( source.specular ); 40073 this.shininess = source.shininess; 40074 40075 this.map = source.map; 40076 40077 this.lightMap = source.lightMap; 40078 this.lightMapIntensity = source.lightMapIntensity; 40079 40080 this.aoMap = source.aoMap; 40081 this.aoMapIntensity = source.aoMapIntensity; 40082 40083 this.emissive.copy( source.emissive ); 40084 this.emissiveMap = source.emissiveMap; 40085 this.emissiveIntensity = source.emissiveIntensity; 40086 40087 this.bumpMap = source.bumpMap; 40088 this.bumpScale = source.bumpScale; 40089 40090 this.normalMap = source.normalMap; 40091 this.normalMapType = source.normalMapType; 40092 this.normalScale.copy( source.normalScale ); 40093 40094 this.displacementMap = source.displacementMap; 40095 this.displacementScale = source.displacementScale; 40096 this.displacementBias = source.displacementBias; 40097 40098 this.specularMap = source.specularMap; 40099 40100 this.alphaMap = source.alphaMap; 40101 40102 this.envMap = source.envMap; 40103 this.envMapRotation.copy( source.envMapRotation ); 40104 this.combine = source.combine; 40105 this.reflectivity = source.reflectivity; 40106 this.envMapIntensity = source.envMapIntensity; 40107 this.refractionRatio = source.refractionRatio; 40108 40109 this.wireframe = source.wireframe; 40110 this.wireframeLinewidth = source.wireframeLinewidth; 40111 this.wireframeLinecap = source.wireframeLinecap; 40112 this.wireframeLinejoin = source.wireframeLinejoin; 40113 40114 this.flatShading = source.flatShading; 40115 40116 this.fog = source.fog; 40117 40118 return this; 40119 40120 } 40121 40122} 40123 40124/** 40125 * A material implementing toon shading. 40126 * 40127 * @augments Material 40128 * @demo scenes/material-browser.html#MeshToonMaterial 40129 */ 40130class MeshToonMaterial extends Material { 40131 40132 /** 40133 * Constructs a new mesh toon material. 40134 * 40135 * @param {Object} [parameters] - An object with one or more properties 40136 * defining the material's appearance. Any property of the material 40137 * (including any property from inherited materials) can be passed 40138 * in here. Color values can be passed any type of value accepted 40139 * by {@link Color#set}. 40140 */ 40141 constructor( parameters ) { 40142 40143 super(); 40144 40145 /** 40146 * This flag can be used for type testing. 40147 * 40148 * @type {boolean} 40149 * @readonly 40150 * @default true 40151 */ 40152 this.isMeshToonMaterial = true; 40153 40154 this.defines = { 'TOON': '' }; 40155 40156 this.type = 'MeshToonMaterial'; 40157 40158 /** 40159 * Color of the material. 40160 * 40161 * @type {Color} 40162 * @default (1,1,1) 40163 */ 40164 this.color = new Color( 0xffffff ); 40165 40166 /** 40167 * The color map. May optionally include an alpha channel, typically combined 40168 * with {@link Material#transparent} or {@link Material#alphaTest}. The texture map 40169 * color is modulated by the diffuse `color`. 40170 * 40171 * `map` represents color data, and the texture must be assigned a 40172 * {@link Texture#colorSpace}. Most `map` textures set 40173 * `texture.colorSpace = SRGBColorSpace`. 40174 * 40175 * @type {?Texture} 40176 * @default null 40177 */ 40178 this.map = null; 40179 40180 /** 40181 * Gradient map for toon shading. It's required to set 40182 * {@link Texture#minFilter} and {@link Texture#magFilter} to {@link NearestFilter} 40183 * when using this type of texture. 40184 * 40185 * `gradientMap` represents non-color data. Any texture assigned must have 40186 * `texture.colorSpace = NoColorSpace` (default). 40187 * 40188 * @type {?Texture} 40189 * @default null 40190 */ 40191 this.gradientMap = null; 40192 40193 /** 40194 * The light map. Requires a second set of UVs. 40195 * 40196 * `lightMap` represents pre-baked illuminance data, and the texture must be assigned 40197 * a {@link Texture#colorSpace}. Most `lightMap` textures set 40198 * `texture.colorSpace = LinearSRGBColorSpace` and use float-type formats 40199 * such as `.exr` or `.hdr`. 40200 * 40201 * @type {?Texture} 40202 * @default null 40203 */ 40204 this.lightMap = null; 40205 40206 /** 40207 * Intensity of the baked light. 40208 * 40209 * @type {number} 40210 * @default 1 40211 */ 40212 this.lightMapIntensity = 1.0; 40213 40214 /** 40215 * The red channel of this texture is used as the ambient occlusion map. 40216 * Requires a second set of UVs. 40217 * 40218 * `aoMap` represents non-color data. Any texture assigned must have 40219 * `texture.colorSpace = NoColorSpace` (default). 40220 * 40221 * @type {?Texture} 40222 * @default null 40223 */ 40224 this.aoMap = null; 40225 40226 /** 40227 * Intensity of the ambient occlusion effect. Range is `[0,1]`, where `0` 40228 * disables ambient occlusion. Where intensity is `1` and the AO map's 40229 * red channel is also `1`, ambient light is fully occluded on a surface. 40230 * 40231 * @type {number} 40232 * @default 1 40233 */ 40234 this.aoMapIntensity = 1.0; 40235 40236 /** 40237 * Emissive (light) color of the material, essentially a solid color 40238 * unaffected by other lighting. 40239 * 40240 * @type {Color} 40241 * @default (0,0,0) 40242 */ 40243 this.emissive = new Color( 0x000000 ); 40244 40245 /** 40246 * Intensity of the emissive light. Modulates the emissive color. 40247 * 40248 * @type {number} 40249 * @default 1 40250 */ 40251 this.emissiveIntensity = 1.0; 40252 40253 /** 40254 * Set emissive (glow) map. The emissive map color is modulated by the 40255 * emissive color and the emissive intensity. If you have an emissive map, 40256 * be sure to set the emissive color to something other than black. 40257 * 40258 * `emissiveMap` represents color data, and the texture must be assigned a 40259 * {@link Texture#colorSpace}. Most `emissiveMap` textures set 40260 * `texture.colorSpace = SRGBColorSpace`. 40261 * 40262 * @type {?Texture} 40263 * @default null 40264 */ 40265 this.emissiveMap = null; 40266 40267 /** 40268 * The texture to create a bump map. The black and white values map to the 40269 * perceived depth in relation to the lights. Bump doesn't actually affect 40270 * the geometry of the object, only the lighting. If a normal map is defined 40271 * this will be ignored. 40272 * 40273 * `bumpMap` represents non-color data. Any texture assigned must have 40274 * `texture.colorSpace = NoColorSpace` (default). 40275 * 40276 * @type {?Texture} 40277 * @default null 40278 */ 40279 this.bumpMap = null; 40280 40281 /** 40282 * How much the bump map affects the material. Typical range is `[0,1]`. 40283 * 40284 * @type {number} 40285 * @default 1 40286 */ 40287 this.bumpScale = 1; 40288 40289 /** 40290 * The texture to create a normal map. The RGB values affect the surface 40291 * normal for each pixel fragment and change the way the color is lit. Normal 40292 * maps do not change the actual shape of the surface, only the lighting. In 40293 * case the material has a normal map authored using the left handed 40294 * convention, the `y` component of `normalScale` should be negated to compensate 40295 * for the different handedness. 40296 * 40297 * `normalMap` represents non-color data. Any texture assigned must have 40298 * `texture.colorSpace = NoColorSpace` (default). 40299 * 40300 * @type {?Texture} 40301 * @default null 40302 */ 40303 this.normalMap = null; 40304 40305 /** 40306 * The type of normal map. 40307 * 40308 * @type {(TangentSpaceNormalMap|ObjectSpaceNormalMap)} 40309 * @default TangentSpaceNormalMap 40310 */ 40311 this.normalMapType = TangentSpaceNormalMap; 40312 40313 /** 40314 * How much the normal map affects the material. Typical value range is `[0,1]`. 40315 * 40316 * @type {Vector2} 40317 * @default (1,1) 40318 */ 40319 this.normalScale = new Vector2( 1, 1 ); 40320 40321 /** 40322 * The displacement map affects the position of the mesh's vertices. Unlike 40323 * other maps which only affect the light and shade of the material the 40324 * displaced vertices can cast shadows, block other objects, and otherwise 40325 * act as real geometry. The displacement texture is an image where the value 40326 * of each pixel (white being the highest) is mapped against, and 40327 * repositions, the vertices of the mesh. For best results, pair a 40328 * displacement map with a matching normal map, since the renderer can 40329 * not recompute surface normals from the displaced vertices. 40330 * 40331 * `displacementMap` represents non-color data. Any texture assigned must have 40332 * `texture.colorSpace = NoColorSpace` (default). 40333 * 40334 * @type {?Texture} 40335 * @default null 40336 */ 40337 this.displacementMap = null; 40338 40339 /** 40340 * How much the displacement map affects the mesh (where black is no 40341 * displacement, and white is maximum displacement). Without a displacement 40342 * map set, this value is not applied. 40343 * 40344 * @type {number} 40345 * @default 0 40346 */ 40347 this.displacementScale = 1; 40348 40349 /** 40350 * The offset of the displacement map's values on the mesh's vertices. 40351 * The bias is added to the scaled sample of the displacement map. 40352 * Without a displacement map set, this value is not applied. 40353 * 40354 * @type {number} 40355 * @default 0 40356 */ 40357 this.displacementBias = 0; 40358 40359 /** 40360 * The alpha map is a grayscale texture that controls the opacity across the 40361 * surface (black: fully transparent; white: fully opaque). 40362 * 40363 * Only the color of the texture is used, ignoring the alpha channel if one 40364 * exists. For RGB and RGBA textures, the renderer will use the green channel 40365 * when sampling this texture due to the extra bit of precision provided for 40366 * green in DXT-compressed and uncompressed RGB 565 formats. Luminance-only and 40367 * luminance/alpha textures will also still work as expected. 40368 * 40369 * `alphaMap` represents non-color data. Any texture assigned must have 40370 * `texture.colorSpace = NoColorSpace` (default). 40371 * 40372 * @type {?Texture} 40373 * @default null 40374 */ 40375 this.alphaMap = null; 40376 40377 /** 40378 * Renders the geometry as a wireframe. 40379 * 40380 * @type {boolean} 40381 * @default false 40382 */ 40383 this.wireframe = false; 40384 40385 /** 40386 * Controls the thickness of the wireframe. 40387 * 40388 * Can only be used with {@link SVGRenderer}. 40389 * 40390 * @type {number} 40391 * @default 1 40392 */ 40393 this.wireframeLinewidth = 1; 40394 40395 /** 40396 * Defines appearance of wireframe ends. 40397 * 40398 * Can only be used with {@link SVGRenderer}. 40399 * 40400 * @type {('round'|'bevel'|'miter')} 40401 * @default 'round' 40402 */ 40403 this.wireframeLinecap = 'round'; 40404 40405 /** 40406 * Defines appearance of wireframe joints. 40407 * 40408 * Can only be used with {@link SVGRenderer}. 40409 * 40410 * @type {('round'|'bevel'|'miter')} 40411 * @default 'round' 40412 */ 40413 this.wireframeLinejoin = 'round'; 40414 40415 /** 40416 * Whether the material is affected by fog or not. 40417 * 40418 * @type {boolean} 40419 * @default true 40420 */ 40421 this.fog = true; 40422 40423 this.setValues( parameters ); 40424 40425 } 40426 40427 copy( source ) { 40428 40429 super.copy( source ); 40430 40431 this.color.copy( source.color ); 40432 40433 this.map = source.map; 40434 this.gradientMap = source.gradientMap; 40435 40436 this.lightMap = source.lightMap; 40437 this.lightMapIntensity = source.lightMapIntensity; 40438 40439 this.aoMap = source.aoMap; 40440 this.aoMapIntensity = source.aoMapIntensity; 40441 40442 this.emissive.copy( source.emissive ); 40443 this.emissiveMap = source.emissiveMap; 40444 this.emissiveIntensity = source.emissiveIntensity; 40445 40446 this.bumpMap = source.bumpMap; 40447 this.bumpScale = source.bumpScale; 40448 40449 this.normalMap = source.normalMap; 40450 this.normalMapType = source.normalMapType; 40451 this.normalScale.copy( source.normalScale ); 40452 40453 this.displacementMap = source.displacementMap; 40454 this.displacementScale = source.displacementScale; 40455 this.displacementBias = source.displacementBias; 40456 40457 this.alphaMap = source.alphaMap; 40458 40459 this.wireframe = source.wireframe; 40460 this.wireframeLinewidth = source.wireframeLinewidth; 40461 this.wireframeLinecap = source.wireframeLinecap; 40462 this.wireframeLinejoin = source.wireframeLinejoin; 40463 40464 this.fog = source.fog; 40465 40466 return this; 40467 40468 } 40469 40470} 40471 40472/** 40473 * A material that maps the normal vectors to RGB colors. 40474 * 40475 * @augments Material 40476 * @demo scenes/material-browser.html#MeshNormalMaterial 40477 */ 40478class MeshNormalMaterial extends Material { 40479 40480 /** 40481 * Constructs a new mesh normal material. 40482 * 40483 * @param {Object} [parameters] - An object with one or more properties 40484 * defining the material's appearance. Any property of the material 40485 * (including any property from inherited materials) can be passed 40486 * in here. Color values can be passed any type of value accepted 40487 * by {@link Color#set}. 40488 */ 40489 constructor( parameters ) { 40490 40491 super(); 40492 40493 /** 40494 * This flag can be used for type testing. 40495 * 40496 * @type {boolean} 40497 * @readonly 40498 * @default true 40499 */ 40500 this.isMeshNormalMaterial = true; 40501 40502 this.type = 'MeshNormalMaterial'; 40503 40504 /** 40505 * The texture to create a bump map. The black and white values map to the 40506 * perceived depth in relation to the lights. Bump doesn't actually affect 40507 * the geometry of the object, only the lighting. If a normal map is defined 40508 * this will be ignored. 40509 * 40510 * `bumpMap` represents non-color data. Any texture assigned must have 40511 * `texture.colorSpace = NoColorSpace` (default). 40512 * 40513 * @type {?Texture} 40514 * @default null 40515 */ 40516 this.bumpMap = null; 40517 40518 /** 40519 * How much the bump map affects the material. Typical range is `[0,1]`. 40520 * 40521 * @type {number} 40522 * @default 1 40523 */ 40524 this.bumpScale = 1; 40525 40526 /** 40527 * The texture to create a normal map. The RGB values affect the surface 40528 * normal for each pixel fragment and change the way the color is lit. Normal 40529 * maps do not change the actual shape of the surface, only the lighting. In 40530 * case the material has a normal map authored using the left handed 40531 * convention, the `y` component of `normalScale` should be negated to compensate 40532 * for the different handedness. 40533 * 40534 * `normalMap` represents non-color data. Any texture assigned must have 40535 * `texture.colorSpace = NoColorSpace` (default). 40536 * 40537 * @type {?Texture} 40538 * @default null 40539 */ 40540 this.normalMap = null; 40541 40542 /** 40543 * The type of normal map. 40544 * 40545 * @type {(TangentSpaceNormalMap|ObjectSpaceNormalMap)} 40546 * @default TangentSpaceNormalMap 40547 */ 40548 this.normalMapType = TangentSpaceNormalMap; 40549 40550 /** 40551 * How much the normal map affects the material. Typical value range is `[0,1]`. 40552 * 40553 * @type {Vector2} 40554 * @default (1,1) 40555 */ 40556 this.normalScale = new Vector2( 1, 1 ); 40557 40558 /** 40559 * The displacement map affects the position of the mesh's vertices. Unlike 40560 * other maps which only affect the light and shade of the material the 40561 * displaced vertices can cast shadows, block other objects, and otherwise 40562 * act as real geometry. The displacement texture is an image where the value 40563 * of each pixel (white being the highest) is mapped against, and 40564 * repositions, the vertices of the mesh. For best results, pair a 40565 * displacement map with a matching normal map, since the renderer can 40566 * not recompute surface normals from the displaced vertices. 40567 * 40568 * @type {?Texture} 40569 * @default null 40570 */ 40571 this.displacementMap = null; 40572 40573 /** 40574 * How much the displacement map affects the mesh (where black is no 40575 * displacement, and white is maximum displacement). Without a displacement 40576 * map set, this value is not applied. 40577 * 40578 * @type {number} 40579 * @default 0 40580 */ 40581 this.displacementScale = 1; 40582 40583 /** 40584 * The offset of the displacement map's values on the mesh's vertices. 40585 * The bias is added to the scaled sample of the displacement map. 40586 * Without a displacement map set, this value is not applied. 40587 * 40588 * @type {number} 40589 * @default 0 40590 */ 40591 this.displacementBias = 0; 40592 40593 /** 40594 * Renders the geometry as a wireframe. 40595 * 40596 * @type {boolean} 40597 * @default false 40598 */ 40599 this.wireframe = false; 40600 40601 /** 40602 * Controls the thickness of the wireframe. 40603 * 40604 * WebGL and WebGPU ignore this property and always render 40605 * 1 pixel wide lines. 40606 * 40607 * @type {number} 40608 * @default 1 40609 */ 40610 this.wireframeLinewidth = 1; 40611 40612 /** 40613 * Whether the material is rendered with flat shading or not. 40614 * 40615 * @type {boolean} 40616 * @default false 40617 */ 40618 this.flatShading = false; 40619 40620 this.setValues( parameters ); 40621 40622 } 40623 40624 copy( source ) { 40625 40626 super.copy( source ); 40627 40628 this.bumpMap = source.bumpMap; 40629 this.bumpScale = source.bumpScale; 40630 40631 this.normalMap = source.normalMap; 40632 this.normalMapType = source.normalMapType; 40633 this.normalScale.copy( source.normalScale ); 40634 40635 this.displacementMap = source.displacementMap; 40636 this.displacementScale = source.displacementScale; 40637 this.displacementBias = source.displacementBias; 40638 40639 this.wireframe = source.wireframe; 40640 this.wireframeLinewidth = source.wireframeLinewidth; 40641 40642 this.flatShading = source.flatShading; 40643 40644 return this; 40645 40646 } 40647 40648} 40649 40650/** 40651 * A material for non-shiny surfaces, without specular highlights. 40652 * 40653 * The material uses a non-physically based [Lambertian](https://en.wikipedia.org/wiki/Lambertian_reflectance) 40654 * model for calculating reflectance. This can simulate some surfaces (such 40655 * as untreated wood or stone) well, but cannot simulate shiny surfaces with 40656 * specular highlights (such as varnished wood). `MeshLambertMaterial` uses per-fragment 40657 * shading. 40658 * 40659 * Due to the simplicity of the reflectance and illumination models, 40660 * performance will be greater when using this material over the 40661 * {@link MeshPhongMaterial}, {@link MeshStandardMaterial} or 40662 * {@link MeshPhysicalMaterial}, at the cost of some graphical accuracy. 40663 * 40664 * @augments Material 40665 * @demo scenes/material-browser.html#MeshLambertMaterial 40666 */ 40667class MeshLambertMaterial extends Material { 40668 40669 /** 40670 * Constructs a new mesh lambert material. 40671 * 40672 * @param {Object} [parameters] - An object with one or more properties 40673 * defining the material's appearance. Any property of the material 40674 * (including any property from inherited materials) can be passed 40675 * in here. Color values can be passed any type of value accepted 40676 * by {@link Color#set}. 40677 */ 40678 constructor( parameters ) { 40679 40680 super(); 40681 40682 /** 40683 * This flag can be used for type testing. 40684 * 40685 * @type {boolean} 40686 * @readonly 40687 * @default true 40688 */ 40689 this.isMeshLambertMaterial = true; 40690 40691 this.type = 'MeshLambertMaterial'; 40692 40693 /** 40694 * Color of the material. 40695 * 40696 * @type {Color} 40697 * @default (1,1,1) 40698 */ 40699 this.color = new Color( 0xffffff ); // diffuse 40700 40701 /** 40702 * The color map. May optionally include an alpha channel, typically combined 40703 * with {@link Material#transparent} or {@link Material#alphaTest}. The texture map 40704 * color is modulated by the diffuse `color`. 40705 * 40706 * `map` represents color data, and the texture must be assigned a 40707 * {@link Texture#colorSpace}. Most `map` textures set 40708 * `texture.colorSpace = SRGBColorSpace`. 40709 * 40710 * @type {?Texture} 40711 * @default null 40712 */ 40713 this.map = null; 40714 40715 /** 40716 * The light map. Requires a second set of UVs. 40717 * 40718 * `lightMap` represents pre-baked illuminance data, and the texture must be assigned 40719 * a {@link Texture#colorSpace}. Most `lightMap` textures set 40720 * `texture.colorSpace = LinearSRGBColorSpace` and use float-type formats 40721 * such as `.exr` or `.hdr`. 40722 * 40723 * @type {?Texture} 40724 * @default null 40725 */ 40726 this.lightMap = null; 40727 40728 /** 40729 * Intensity of the baked light. 40730 * 40731 * @type {number} 40732 * @default 1 40733 */ 40734 this.lightMapIntensity = 1.0; 40735 40736 /** 40737 * The red channel of this texture is used as the ambient occlusion map. 40738 * Requires a second set of UVs. 40739 * 40740 * `aoMap` represents non-color data. Any texture assigned must have 40741 * `texture.colorSpace = NoColorSpace` (default). 40742 * 40743 * @type {?Texture} 40744 * @default null 40745 */ 40746 this.aoMap = null; 40747 40748 /** 40749 * Intensity of the ambient occlusion effect. Range is `[0,1]`, where `0` 40750 * disables ambient occlusion. Where intensity is `1` and the AO map's 40751 * red channel is also `1`, ambient light is fully occluded on a surface. 40752 * 40753 * @type {number} 40754 * @default 1 40755 */ 40756 this.aoMapIntensity = 1.0; 40757 40758 /** 40759 * Emissive (light) color of the material, essentially a solid color 40760 * unaffected by other lighting. 40761 * 40762 * @type {Color} 40763 * @default (0,0,0) 40764 */ 40765 this.emissive = new Color( 0x000000 ); 40766 40767 /** 40768 * Intensity of the emissive light. Modulates the emissive color. 40769 * 40770 * @type {number} 40771 * @default 1 40772 */ 40773 this.emissiveIntensity = 1.0; 40774 40775 /** 40776 * Set emissive (glow) map. The emissive map color is modulated by the 40777 * emissive color and the emissive intensity. If you have an emissive map, 40778 * be sure to set the emissive color to something other than black. 40779 * 40780 * `emissiveMap` represents color data, and the texture must be assigned a 40781 * {@link Texture#colorSpace}. Most `emissiveMap` textures set 40782 * `texture.colorSpace = SRGBColorSpace`. 40783 * 40784 * @type {?Texture} 40785 * @default null 40786 */ 40787 this.emissiveMap = null; 40788 40789 /** 40790 * The texture to create a bump map. The black and white values map to the 40791 * perceived depth in relation to the lights. Bump doesn't actually affect 40792 * the geometry of the object, only the lighting. If a normal map is defined 40793 * this will be ignored. 40794 * 40795 * `bumpMap` represents non-color data. Any texture assigned must have 40796 * `texture.colorSpace = NoColorSpace` (default). 40797 * 40798 * @type {?Texture} 40799 * @default null 40800 */ 40801 this.bumpMap = null; 40802 40803 /** 40804 * How much the bump map affects the material. Typical range is `[0,1]`. 40805 * 40806 * @type {number} 40807 * @default 1 40808 */ 40809 this.bumpScale = 1; 40810 40811 /** 40812 * The texture to create a normal map. The RGB values affect the surface 40813 * normal for each pixel fragment and change the way the color is lit. Normal 40814 * maps do not change the actual shape of the surface, only the lighting. In 40815 * case the material has a normal map authored using the left handed 40816 * convention, the `y` component of `normalScale` should be negated to compensate 40817 * for the different handedness. 40818 * 40819 * `normalMap` represents non-color data. Any texture assigned must have 40820 * `texture.colorSpace = NoColorSpace` (default). 40821 * 40822 * @type {?Texture} 40823 * @default null 40824 */ 40825 this.normalMap = null; 40826 40827 /** 40828 * The type of normal map. 40829 * 40830 * @type {(TangentSpaceNormalMap|ObjectSpaceNormalMap)} 40831 * @default TangentSpaceNormalMap 40832 */ 40833 this.normalMapType = TangentSpaceNormalMap; 40834 40835 /** 40836 * How much the normal map affects the material. Typical value range is `[0,1]`. 40837 * 40838 * @type {Vector2} 40839 * @default (1,1) 40840 */ 40841 this.normalScale = new Vector2( 1, 1 ); 40842 40843 /** 40844 * The displacement map affects the position of the mesh's vertices. Unlike 40845 * other maps which only affect the light and shade of the material the 40846 * displaced vertices can cast shadows, block other objects, and otherwise 40847 * act as real geometry. The displacement texture is an image where the value 40848 * of each pixel (white being the highest) is mapped against, and 40849 * repositions, the vertices of the mesh. For best results, pair a 40850 * displacement map with a matching normal map, since the renderer can 40851 * not recompute surface normals from the displaced vertices. 40852 * 40853 * `displacementMap` represents non-color data. Any texture assigned must have 40854 * `texture.colorSpace = NoColorSpace` (default). 40855 * 40856 * @type {?Texture} 40857 * @default null 40858 */ 40859 this.displacementMap = null; 40860 40861 /** 40862 * How much the displacement map affects the mesh (where black is no 40863 * displacement, and white is maximum displacement). Without a displacement 40864 * map set, this value is not applied. 40865 * 40866 * @type {number} 40867 * @default 0 40868 */ 40869 this.displacementScale = 1; 40870 40871 /** 40872 * The offset of the displacement map's values on the mesh's vertices. 40873 * The bias is added to the scaled sample of the displacement map. 40874 * Without a displacement map set, this value is not applied. 40875 * 40876 * @type {number} 40877 * @default 0 40878 */ 40879 this.displacementBias = 0; 40880 40881 /** 40882 * Specular map used by the material. 40883 * 40884 * `specularMap` represents color data, and the texture must be assigned a 40885 * {@link Texture#colorSpace}. Most `specularMap` textures set 40886 * `texture.colorSpace = SRGBColorSpace`. 40887 * 40888 * @type {?Texture} 40889 * @default null 40890 */ 40891 this.specularMap = null; 40892 40893 /** 40894 * The alpha map is a grayscale texture that controls the opacity across the 40895 * surface (black: fully transparent; white: fully opaque). 40896 * 40897 * Only the color of the texture is used, ignoring the alpha channel if one 40898 * exists. For RGB and RGBA textures, the renderer will use the green channel 40899 * when sampling this texture due to the extra bit of precision provided for 40900 * green in DXT-compressed and uncompressed RGB 565 formats. Luminance-only and 40901 * luminance/alpha textures will also still work as expected. 40902 * 40903 * `alphaMap` represents non-color data. Any texture assigned must have 40904 * `texture.colorSpace = NoColorSpace` (default). 40905 * 40906 * @type {?Texture} 40907 * @default null 40908 */ 40909 this.alphaMap = null; 40910 40911 /** 40912 * The environment map. 40913 * 40914 * `envMap` represents luminance data, and the texture must be assigned 40915 * a {@link Texture#colorSpace}. Most `envMap` textures set 40916 * `texture.colorSpace = LinearSRGBColorSpace` and use float-type formats 40917 * such as `.exr` or `.hdr`. 40918 * 40919 * @type {?Texture} 40920 * @default null 40921 */ 40922 this.envMap = null; 40923 40924 /** 40925 * The rotation of the environment map in radians. 40926 * 40927 * @type {Euler} 40928 * @default (0,0,0) 40929 */ 40930 this.envMapRotation = new Euler(); 40931 40932 /** 40933 * How to combine the result of the surface's color with the environment map, if any. 40934 * 40935 * When set to `MixOperation`, the {@link MeshBasicMaterial#reflectivity} is used to 40936 * blend between the two colors. 40937 * 40938 * @type {(MultiplyOperation|MixOperation|AddOperation)} 40939 * @default MultiplyOperation 40940 */ 40941 this.combine = MultiplyOperation; 40942 40943 /** 40944 * How much the environment map affects the surface. 40945 * The valid range is between `0` (no reflections) and `1` (full reflections). 40946 * 40947 * @type {number} 40948 * @default 1 40949 */ 40950 this.reflectivity = 1; 40951 40952 /** 40953 * Scales the effect of the environment map by multiplying its color. 40954 * 40955 * @type {number} 40956 * @default 1 40957 */ 40958 this.envMapIntensity = 1.0; 40959 40960 /** 40961 * The index of refraction (IOR) of air (approximately 1) divided by the 40962 * index of refraction of the material. It is used with environment mapping 40963 * modes {@link CubeRefractionMapping} and {@link EquirectangularRefractionMapping}. 40964 * The refraction ratio should not exceed `1`. 40965 * 40966 * @type {number} 40967 * @default 0.98 40968 */ 40969 this.refractionRatio = 0.98; 40970 40971 /** 40972 * Renders the geometry as a wireframe. 40973 * 40974 * @type {boolean} 40975 * @default false 40976 */ 40977 this.wireframe = false; 40978 40979 /** 40980 * Controls the thickness of the wireframe. 40981 * 40982 * Can only be used with {@link SVGRenderer}. 40983 * 40984 * @type {number} 40985 * @default 1 40986 */ 40987 this.wireframeLinewidth = 1; 40988 40989 /** 40990 * Defines appearance of wireframe ends. 40991 * 40992 * Can only be used with {@link SVGRenderer}. 40993 * 40994 * @type {('round'|'bevel'|'miter')} 40995 * @default 'round' 40996 */ 40997 this.wireframeLinecap = 'round'; 40998 40999 /** 41000 * Defines appearance of wireframe joints. 41001 * 41002 * Can only be used with {@link SVGRenderer}. 41003 * 41004 * @type {('round'|'bevel'|'miter')} 41005 * @default 'round' 41006 */ 41007 this.wireframeLinejoin = 'round'; 41008 41009 /** 41010 * Whether the material is rendered with flat shading or not. 41011 * 41012 * @type {boolean} 41013 * @default false 41014 */ 41015 this.flatShading = false; 41016 41017 /** 41018 * Whether the material is affected by fog or not. 41019 * 41020 * @type {boolean} 41021 * @default true 41022 */ 41023 this.fog = true; 41024 41025 this.setValues( parameters ); 41026 41027 } 41028 41029 copy( source ) { 41030 41031 super.copy( source ); 41032 41033 this.color.copy( source.color ); 41034 41035 this.map = source.map; 41036 41037 this.lightMap = source.lightMap; 41038 this.lightMapIntensity = source.lightMapIntensity; 41039 41040 this.aoMap = source.aoMap; 41041 this.aoMapIntensity = source.aoMapIntensity; 41042 41043 this.emissive.copy( source.emissive ); 41044 this.emissiveMap = source.emissiveMap; 41045 this.emissiveIntensity = source.emissiveIntensity; 41046 41047 this.bumpMap = source.bumpMap; 41048 this.bumpScale = source.bumpScale; 41049 41050 this.normalMap = source.normalMap; 41051 this.normalMapType = source.normalMapType; 41052 this.normalScale.copy( source.normalScale ); 41053 41054 this.displacementMap = source.displacementMap; 41055 this.displacementScale = source.displacementScale; 41056 this.displacementBias = source.displacementBias; 41057 41058 this.specularMap = source.specularMap; 41059 41060 this.alphaMap = source.alphaMap; 41061 41062 this.envMap = source.envMap; 41063 this.envMapRotation.copy( source.envMapRotation ); 41064 this.combine = source.combine; 41065 this.reflectivity = source.reflectivity; 41066 this.envMapIntensity = source.envMapIntensity; 41067 this.refractionRatio = source.refractionRatio; 41068 41069 this.wireframe = source.wireframe; 41070 this.wireframeLinewidth = source.wireframeLinewidth; 41071 this.wireframeLinecap = source.wireframeLinecap; 41072 this.wireframeLinejoin = source.wireframeLinejoin; 41073 41074 this.flatShading = source.flatShading; 41075 41076 this.fog = source.fog; 41077 41078 return this; 41079 41080 } 41081 41082} 41083 41084/** 41085 * A material for drawing geometry by depth. Depth is based off of the camera 41086 * near and far plane. White is nearest, black is farthest. 41087 * 41088 * @augments Material 41089 * @demo scenes/material-browser.html#MeshDepthMaterial 41090 */ 41091class MeshDepthMaterial extends Material { 41092 41093 /** 41094 * Constructs a new mesh depth material. 41095 * 41096 * @param {Object} [parameters] - An object with one or more properties 41097 * defining the material's appearance. Any property of the material 41098 * (including any property from inherited materials) can be passed 41099 * in here. Color values can be passed any type of value accepted 41100 * by {@link Color#set}. 41101 */ 41102 constructor( parameters ) { 41103 41104 super(); 41105 41106 /** 41107 * This flag can be used for type testing. 41108 * 41109 * @type {boolean} 41110 * @readonly 41111 * @default true 41112 */ 41113 this.isMeshDepthMaterial = true; 41114 41115 this.type = 'MeshDepthMaterial'; 41116 41117 /** 41118 * Type for depth packing. 41119 * 41120 * @type {(BasicDepthPacking|RGBADepthPacking|RGBDepthPacking|RGDepthPacking)} 41121 * @default BasicDepthPacking 41122 */ 41123 this.depthPacking = BasicDepthPacking; 41124 41125 /** 41126 * The color map. May optionally include an alpha channel, typically combined 41127 * with {@link Material#transparent} or {@link Material#alphaTest}. 41128 * 41129 * `map` represents color data, and the texture must be assigned a 41130 * {@link Texture#colorSpace}. Most `map` textures set 41131 * `texture.colorSpace = SRGBColorSpace`. 41132 * 41133 * @type {?Texture} 41134 * @default null 41135 */ 41136 this.map = null; 41137 41138 /** 41139 * The alpha map is a grayscale texture that controls the opacity across the 41140 * surface (black: fully transparent; white: fully opaque). 41141 * 41142 * Only the color of the texture is used, ignoring the alpha channel if one 41143 * exists. For RGB and RGBA textures, the renderer will use the green channel 41144 * when sampling this texture due to the extra bit of precision provided for 41145 * green in DXT-compressed and uncompressed RGB 565 formats. Luminance-only and 41146 * luminance/alpha textures will also still work as expected. 41147 * 41148 * `alphaMap` represents non-color data. Any texture assigned must have 41149 * `texture.colorSpace = NoColorSpace` (default). 41150 * 41151 * @type {?Texture} 41152 * @default null 41153 */ 41154 this.alphaMap = null; 41155 41156 /** 41157 * The displacement map affects the position of the mesh's vertices. Unlike 41158 * other maps which only affect the light and shade of the material the 41159 * displaced vertices can cast shadows, block other objects, and otherwise 41160 * act as real geometry. The displacement texture is an image where the value 41161 * of each pixel (white being the highest) is mapped against, and 41162 * repositions, the vertices of the mesh. 41163 * 41164 * `displacementMap` represents non-color data. Any texture assigned must have 41165 * `texture.colorSpace = NoColorSpace` (default). 41166 * 41167 * @type {?Texture} 41168 * @default null 41169 */ 41170 this.displacementMap = null; 41171 41172 /** 41173 * How much the displacement map affects the mesh (where black is no 41174 * displacement, and white is maximum displacement). Without a displacement 41175 * map set, this value is not applied. 41176 * 41177 * @type {number} 41178 * @default 0 41179 */ 41180 this.displacementScale = 1; 41181 41182 /** 41183 * The offset of the displacement map's values on the mesh's vertices. 41184 * The bias is added to the scaled sample of the displacement map. 41185 * Without a displacement map set, this value is not applied. 41186 * 41187 * @type {number} 41188 * @default 0 41189 */ 41190 this.displacementBias = 0; 41191 41192 /** 41193 * Renders the geometry as a wireframe. 41194 * 41195 * @type {boolean} 41196 * @default false 41197 */ 41198 this.wireframe = false; 41199 41200 /** 41201 * Controls the thickness of the wireframe. 41202 * 41203 * WebGL and WebGPU ignore this property and always render 41204 * 1 pixel wide lines. 41205 * 41206 * @type {number} 41207 * @default 1 41208 */ 41209 this.wireframeLinewidth = 1; 41210 41211 this.setValues( parameters ); 41212 41213 } 41214 41215 copy( source ) { 41216 41217 super.copy( source ); 41218 41219 this.depthPacking = source.depthPacking; 41220 41221 this.map = source.map; 41222 41223 this.alphaMap = source.alphaMap; 41224 41225 this.displacementMap = source.displacementMap; 41226 this.displacementScale = source.displacementScale; 41227 this.displacementBias = source.displacementBias; 41228 41229 this.wireframe = source.wireframe; 41230 this.wireframeLinewidth = source.wireframeLinewidth; 41231 41232 return this; 41233 41234 } 41235 41236} 41237 41238/** 41239 * A material used internally for implementing shadow mapping with 41240 * point lights. 41241 * 41242 * Can also be used to customize the shadow casting of an object by assigning 41243 * an instance of `MeshDistanceMaterial` to {@link Object3D#customDistanceMaterial}. 41244 * The following examples demonstrates this approach in order to ensure 41245 * transparent parts of objects do not cast shadows. 41246 * 41247 * @augments Material 41248 */ 41249class MeshDistanceMaterial extends Material { 41250 41251 /** 41252 * Constructs a new mesh distance material. 41253 * 41254 * @param {Object} [parameters] - An object with one or more properties 41255 * defining the material's appearance. Any property of the material 41256 * (including any property from inherited materials) can be passed 41257 * in here. Color values can be passed any type of value accepted 41258 * by {@link Color#set}. 41259 */ 41260 constructor( parameters ) { 41261 41262 super(); 41263 41264 /** 41265 * This flag can be used for type testing. 41266 * 41267 * @type {boolean} 41268 * @readonly 41269 * @default true 41270 */ 41271 this.isMeshDistanceMaterial = true; 41272 41273 this.type = 'MeshDistanceMaterial'; 41274 41275 /** 41276 * The color map. May optionally include an alpha channel, typically combined 41277 * with {@link Material#transparent} or {@link Material#alphaTest}. 41278 * 41279 * `map` represents color data, and the texture must be assigned a 41280 * {@link Texture#colorSpace}. Most `map` textures set 41281 * `texture.colorSpace = SRGBColorSpace`. 41282 * 41283 * @type {?Texture} 41284 * @default null 41285 */ 41286 this.map = null; 41287 41288 /** 41289 * The alpha map is a grayscale texture that controls the opacity across the 41290 * surface (black: fully transparent; white: fully opaque). 41291 * 41292 * Only the color of the texture is used, ignoring the alpha channel if one 41293 * exists. For RGB and RGBA textures, the renderer will use the green channel 41294 * when sampling this texture due to the extra bit of precision provided for 41295 * green in DXT-compressed and uncompressed RGB 565 formats. Luminance-only and 41296 * luminance/alpha textures will also still work as expected. 41297 * 41298 * `alphaMap` represents non-color data. Any texture assigned must have 41299 * `texture.colorSpace = NoColorSpace` (default). 41300 * 41301 * @type {?Texture} 41302 * @default null 41303 */ 41304 this.alphaMap = null; 41305 41306 /** 41307 * The displacement map affects the position of the mesh's vertices. Unlike 41308 * other maps which only affect the light and shade of the material the 41309 * displaced vertices can cast shadows, block other objects, and otherwise 41310 * act as real geometry. The displacement texture is an image where the value 41311 * of each pixel (white being the highest) is mapped against, and 41312 * repositions, the vertices of the mesh. 41313 * 41314 * `displacementMap` represents non-color data. Any texture assigned must have 41315 * `texture.colorSpace = NoColorSpace` (default). 41316 * 41317 * @type {?Texture} 41318 * @default null 41319 */ 41320 this.displacementMap = null; 41321 41322 /** 41323 * How much the displacement map affects the mesh (where black is no 41324 * displacement, and white is maximum displacement). Without a displacement 41325 * map set, this value is not applied. 41326 * 41327 * @type {number} 41328 * @default 0 41329 */ 41330 this.displacementScale = 1; 41331 41332 /** 41333 * The offset of the displacement map's values on the mesh's vertices. 41334 * The bias is added to the scaled sample of the displacement map. 41335 * Without a displacement map set, this value is not applied. 41336 * 41337 * @type {number} 41338 * @default 0 41339 */ 41340 this.displacementBias = 0; 41341 41342 this.setValues( parameters ); 41343 41344 } 41345 41346 copy( source ) { 41347 41348 super.copy( source ); 41349 41350 this.map = source.map; 41351 41352 this.alphaMap = source.alphaMap; 41353 41354 this.displacementMap = source.displacementMap; 41355 this.displacementScale = source.displacementScale; 41356 this.displacementBias = source.displacementBias; 41357 41358 return this; 41359 41360 } 41361 41362} 41363 41364/** 41365 * This material is defined by a MatCap (or Lit Sphere) texture, which encodes the 41366 * material color and shading. 41367 * 41368 * `MeshMatcapMaterial` does not respond to lights since the matcap image file encodes 41369 * baked lighting. It will cast a shadow onto an object that receives shadows 41370 * (and shadow clipping works), but it will not self-shadow or receive 41371 * shadows. 41372 * 41373 * @augments Material 41374 * @demo scenes/material-browser.html#MeshMatcapMaterial 41375 */ 41376class MeshMatcapMaterial extends Material { 41377 41378 /** 41379 * Constructs a new mesh matcap material. 41380 * 41381 * @param {Object} [parameters] - An object with one or more properties 41382 * defining the material's appearance. Any property of the material 41383 * (including any property from inherited materials) can be passed 41384 * in here. Color values can be passed any type of value accepted 41385 * by {@link Color#set}. 41386 */ 41387 constructor( parameters ) { 41388 41389 super(); 41390 41391 /** 41392 * This flag can be used for type testing. 41393 * 41394 * @type {boolean} 41395 * @readonly 41396 * @default true 41397 */ 41398 this.isMeshMatcapMaterial = true; 41399 41400 this.defines = { 'MATCAP': '' }; 41401 41402 this.type = 'MeshMatcapMaterial'; 41403 41404 /** 41405 * Color of the material. 41406 * 41407 * @type {Color} 41408 * @default (1,1,1) 41409 */ 41410 this.color = new Color( 0xffffff ); // diffuse 41411 41412 /** 41413 * The matcap map. 41414 * 41415 * `matcap` represents luminance data, and the texture must be assigned 41416 * a {@link Texture#colorSpace}. HDR `matcap` textures (e.g. `.exr`) 41417 * typically set `texture.colorSpace = LinearSRGBColorSpace`, while LDR 41418 * `matcap` textures (e.g. `.png`, `.jpg`, `.webp`) typically set 41419 * `texture.colorSpace = SRGBColorSpace`. 41420 * 41421 * @type {?Texture} 41422 * @default null 41423 */ 41424 this.matcap = null; 41425 41426 /** 41427 * The color map. May optionally include an alpha channel, typically combined 41428 * with {@link Material#transparent} or {@link Material#alphaTest}. The texture map 41429 * color is modulated by the diffuse `color`. 41430 * 41431 * `map` represents color data, and the texture must be assigned a 41432 * {@link Texture#colorSpace}. Most `map` textures set 41433 * `texture.colorSpace = SRGBColorSpace`. 41434 * 41435 * @type {?Texture} 41436 * @default null 41437 */ 41438 this.map = null; 41439 41440 /** 41441 * The texture to create a bump map. The black and white values map to the 41442 * perceived depth in relation to the lights. Bump doesn't actually affect 41443 * the geometry of the object, only the lighting. If a normal map is defined 41444 * this will be ignored. 41445 * 41446 * `bumpMap` represents non-color data. Any texture assigned must have 41447 * `texture.colorSpace = NoColorSpace` (default). 41448 * 41449 * @type {?Texture} 41450 * @default null 41451 */ 41452 this.bumpMap = null; 41453 41454 /** 41455 * How much the bump map affects the material. Typical range is `[0,1]`. 41456 * 41457 * @type {number} 41458 * @default 1 41459 */ 41460 this.bumpScale = 1; 41461 41462 /** 41463 * The texture to create a normal map. The RGB values affect the surface 41464 * normal for each pixel fragment and change the way the color is lit. Normal 41465 * maps do not change the actual shape of the surface, only the lighting. In 41466 * case the material has a normal map authored using the left handed 41467 * convention, the `y` component of `normalScale` should be negated to compensate 41468 * for the different handedness. 41469 * 41470 * `normalMap` represents non-color data. Any texture assigned must have 41471 * `texture.colorSpace = NoColorSpace` (default). 41472 * 41473 * @type {?Texture} 41474 * @default null 41475 */ 41476 this.normalMap = null; 41477 41478 /** 41479 * The type of normal map. 41480 * 41481 * @type {(TangentSpaceNormalMap|ObjectSpaceNormalMap)} 41482 * @default TangentSpaceNormalMap 41483 */ 41484 this.normalMapType = TangentSpaceNormalMap; 41485 41486 /** 41487 * How much the normal map affects the material. Typical value range is `[0,1]`. 41488 * 41489 * @type {Vector2} 41490 * @default (1,1) 41491 */ 41492 this.normalScale = new Vector2( 1, 1 ); 41493 41494 /** 41495 * The displacement map affects the position of the mesh's vertices. Unlike 41496 * other maps which only affect the light and shade of the material the 41497 * displaced vertices can cast shadows, block other objects, and otherwise 41498 * act as real geometry. The displacement texture is an image where the value 41499 * of each pixel (white being the highest) is mapped against, and 41500 * repositions, the vertices of the mesh. For best results, pair a 41501 * displacement map with a matching normal map, since the renderer can 41502 * not recompute surface normals from the displaced vertices. 41503 * 41504 * `displacementMap` represents non-color data. Any texture assigned must have 41505 * `texture.colorSpace = NoColorSpace` (default). 41506 * 41507 * @type {?Texture} 41508 * @default null 41509 */ 41510 this.displacementMap = null; 41511 41512 /** 41513 * How much the displacement map affects the mesh (where black is no 41514 * displacement, and white is maximum displacement). Without a displacement 41515 * map set, this value is not applied. 41516 * 41517 * @type {number} 41518 * @default 0 41519 */ 41520 this.displacementScale = 1; 41521 41522 /** 41523 * The offset of the displacement map's values on the mesh's vertices. 41524 * The bias is added to the scaled sample of the displacement map. 41525 * Without a displacement map set, this value is not applied. 41526 * 41527 * @type {number} 41528 * @default 0 41529 */ 41530 this.displacementBias = 0; 41531 41532 /** 41533 * The alpha map is a grayscale texture that controls the opacity across the 41534 * surface (black: fully transparent; white: fully opaque). 41535 * 41536 * Only the color of the texture is used, ignoring the alpha channel if one 41537 * exists. For RGB and RGBA textures, the renderer will use the green channel 41538 * when sampling this texture due to the extra bit of precision provided for 41539 * green in DXT-compressed and uncompressed RGB 565 formats. Luminance-only and 41540 * luminance/alpha textures will also still work as expected. 41541 * 41542 * `alphaMap` represents non-color data. Any texture assigned must have 41543 * `texture.colorSpace = NoColorSpace` (default). 41544 * 41545 * @type {?Texture} 41546 * @default null 41547 */ 41548 this.alphaMap = null; 41549 41550 /** 41551 * Renders the geometry as a wireframe. 41552 * 41553 * @type {boolean} 41554 * @default false 41555 */ 41556 this.wireframe = false; 41557 41558 /** 41559 * Controls the thickness of the wireframe. 41560 * 41561 * Can only be used with {@link SVGRenderer}. 41562 * 41563 * @type {number} 41564 * @default 1 41565 */ 41566 this.wireframeLinewidth = 1; 41567 41568 /** 41569 * Whether the material is rendered with flat shading or not. 41570 * 41571 * @type {boolean} 41572 * @default false 41573 */ 41574 this.flatShading = false; 41575 41576 /** 41577 * Whether the material is affected by fog or not. 41578 * 41579 * @type {boolean} 41580 * @default true 41581 */ 41582 this.fog = true; 41583 41584 this.setValues( parameters ); 41585 41586 } 41587 41588 41589 copy( source ) { 41590 41591 super.copy( source ); 41592 41593 this.defines = { 'MATCAP': '' }; 41594 41595 this.color.copy( source.color ); 41596 41597 this.matcap = source.matcap; 41598 41599 this.map = source.map; 41600 41601 this.bumpMap = source.bumpMap; 41602 this.bumpScale = source.bumpScale; 41603 41604 this.normalMap = source.normalMap; 41605 this.normalMapType = source.normalMapType; 41606 this.normalScale.copy( source.normalScale ); 41607 41608 this.displacementMap = source.displacementMap; 41609 this.displacementScale = source.displacementScale; 41610 this.displacementBias = source.displacementBias; 41611 41612 this.alphaMap = source.alphaMap; 41613 41614 this.wireframe = source.wireframe; 41615 this.wireframeLinewidth = source.wireframeLinewidth; 41616 41617 this.flatShading = source.flatShading; 41618 41619 this.fog = source.fog; 41620 41621 return this; 41622 41623 } 41624 41625} 41626 41627/** 41628 * A material for rendering line primitives. 41629 * 41630 * Materials define the appearance of renderable 3D objects. 41631 * 41632 * ```js 41633 * const material = new THREE.LineDashedMaterial( { 41634 * color: 0xffffff, 41635 * scale: 1, 41636 * dashSize: 3, 41637 * gapSize: 1, 41638 * } ); 41639 * ``` 41640 * 41641 * @augments LineBasicMaterial 41642 */ 41643class LineDashedMaterial extends LineBasicMaterial { 41644 41645 /** 41646 * Constructs a new line dashed material. 41647 * 41648 * @param {Object} [parameters] - An object with one or more properties 41649 * defining the material's appearance. Any property of the material 41650 * (including any property from inherited materials) can be passed 41651 * in here. Color values can be passed any type of value accepted 41652 * by {@link Color#set}. 41653 */ 41654 constructor( parameters ) { 41655 41656 super(); 41657 41658 /** 41659 * This flag can be used for type testing. 41660 * 41661 * @type {boolean} 41662 * @readonly 41663 * @default true 41664 */ 41665 this.isLineDashedMaterial = true; 41666 this.type = 'LineDashedMaterial'; 41667 41668 /** 41669 * The scale of the dashed part of a line. 41670 * 41671 * @type {number} 41672 * @default 1 41673 */ 41674 this.scale = 1; 41675 41676 /** 41677 * The size of the dash. This is both the gap with the stroke. 41678 * 41679 * @type {number} 41680 * @default 3 41681 */ 41682 this.dashSize = 3; 41683 41684 /** 41685 * The size of the gap. 41686 * 41687 * @type {number} 41688 * @default 1 41689 */ 41690 this.gapSize = 1; 41691 41692 this.setValues( parameters ); 41693 41694 } 41695 41696 copy( source ) { 41697 41698 super.copy( source ); 41699 41700 this.scale = source.scale; 41701 this.dashSize = source.dashSize; 41702 this.gapSize = source.gapSize; 41703 41704 return this; 41705 41706 } 41707 41708} 41709 41710/** 41711 * Converts an array to a specific type. 41712 * 41713 * @param {TypedArray|Array} array - The array to convert. 41714 * @param {TypedArray.constructor} type - The constructor of a typed array that defines the new type. 41715 * @return {TypedArray} The converted array. 41716 */ 41717function convertArray( array, type ) { 41718 41719 if ( ! array || array.constructor === type ) return array; 41720 41721 if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { 41722 41723 return new type( array ); // create typed array 41724 41725 } 41726 41727 return Array.prototype.slice.call( array ); // create Array 41728 41729} 41730 41731/** 41732 * Returns `true` if the given keyframe track settings hold Bezier tangent data. 41733 * 41734 * @param {?Object} settings - The settings of a keyframe track. 41735 * @return {boolean} Whether both tangent arrays are defined or not. 41736 */ 41737function hasTangents( settings ) { 41738 41739 return settings !== undefined && settings.inTangents !== undefined && settings.outTangents !== undefined; 41740 41741} 41742 41743/** 41744 * Returns an array by which times and values can be sorted. 41745 * 41746 * @param {Array<number>} times - The keyframe time values. 41747 * @return {Array<number>} The array. 41748 */ 41749function getKeyframeOrder( times ) { 41750 41751 function compareTime( i, j ) { 41752 41753 return times[ i ] - times[ j ]; 41754 41755 } 41756 41757 const n = times.length; 41758 const result = new Array( n ); 41759 for ( let i = 0; i !== n; ++ i ) result[ i ] = i; 41760 41761 result.sort( compareTime ); 41762 41763 return result; 41764 41765} 41766 41767/** 41768 * Sorts the given array by the previously computed order via `getKeyframeOrder()`. 41769 * 41770 * @param {Array<number>} values - The values to sort. 41771 * @param {number} stride - The stride. 41772 * @param {Array<number>} order - The sort order. 41773 * @return {Array<number>} The sorted values. 41774 */ 41775function sortedArray( values, stride, order ) { 41776 41777 const nValues = values.length; 41778 const result = new values.constructor( nValues ); 41779 41780 for ( let i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { 41781 41782 const srcOffset = order[ i ] * stride; 41783 41784 for ( let j = 0; j !== stride; ++ j ) { 41785 41786 result[ dstOffset ++ ] = values[ srcOffset + j ]; 41787 41788 } 41789 41790 } 41791 41792 return result; 41793 41794} 41795 41796/** 41797 * Used for parsing AOS keyframe formats. 41798 * 41799 * @param {Array<number>} jsonKeys - A list of JSON keyframes. 41800 * @param {Array<number>} times - This array will be filled with keyframe times by this function. 41801 * @param {Array<number>} values - This array will be filled with keyframe values by this function. 41802 * @param {string} valuePropertyName - The name of the property to use. 41803 */ 41804function flattenJSON( jsonKeys, times, values, valuePropertyName ) { 41805 41806 let i = 1, key = jsonKeys[ 0 ]; 41807 41808 while ( key !== undefined && key[ valuePropertyName ] === undefined ) { 41809 41810 key = jsonKeys[ i ++ ]; 41811 41812 } 41813 41814 if ( key === undefined ) return; // no data 41815 41816 let value = key[ valuePropertyName ]; 41817 if ( value === undefined ) return; // no data 41818 41819 if ( Array.isArray( value ) ) { 41820 41821 do { 41822 41823 value = key[ valuePropertyName ]; 41824 41825 if ( value !== undefined ) { 41826 41827 times.push( key.time ); 41828 values.push( ...value ); // push all elements 41829 41830 } 41831 41832 key = jsonKeys[ i ++ ]; 41833 41834 } while ( key !== undefined ); 41835 41836 } else if ( value.toArray !== undefined ) { 41837 41838 // ...assume THREE.Math-ish 41839 41840 do { 41841 41842 value = key[ valuePropertyName ]; 41843 41844 if ( value !== undefined ) { 41845 41846 times.push( key.time ); 41847 value.toArray( values, values.length ); 41848 41849 } 41850 41851 key = jsonKeys[ i ++ ]; 41852 41853 } while ( key !== undefined ); 41854 41855 } else { 41856 41857 // otherwise push as-is 41858 41859 do { 41860 41861 value = key[ valuePropertyName ]; 41862 41863 if ( value !== undefined ) { 41864 41865 times.push( key.time ); 41866 values.push( value ); 41867 41868 } 41869 41870 key = jsonKeys[ i ++ ]; 41871 41872 } while ( key !== undefined ); 41873 41874 } 41875 41876} 41877 41878/** 41879 * Creates a new clip, containing only the segment of the original clip between the given frames. 41880 * 41881 * @param {AnimationClip} sourceClip - The values to sort. 41882 * @param {string} name - The name of the clip. 41883 * @param {number} startFrame - The start frame. 41884 * @param {number} endFrame - The end frame. 41885 * @param {number} [fps=30] - The FPS. 41886 * @return {AnimationClip} The new sub clip. 41887 */ 41888function subclip( sourceClip, name, startFrame, endFrame, fps = 30 ) { 41889 41890 const clip = sourceClip.clone(); 41891 41892 clip.name = name; 41893 41894 const tracks = []; 41895 41896 for ( let i = 0; i < clip.tracks.length; ++ i ) { 41897 41898 const track = clip.tracks[ i ]; 41899 const valueSize = track.getValueSize(); 41900 41901 const times = []; 41902 const values = []; 41903 41904 for ( let j = 0; j < track.times.length; ++ j ) { 41905 41906 const frame = track.times[ j ] * fps; 41907 41908 if ( frame < startFrame || frame >= endFrame ) continue; 41909 41910 times.push( track.times[ j ] ); 41911 41912 for ( let k = 0; k < valueSize; ++ k ) { 41913 41914 values.push( track.values[ j * valueSize + k ] ); 41915 41916 } 41917 41918 } 41919 41920 if ( times.length === 0 ) continue; 41921 41922 track.times = convertArray( times, track.times.constructor ); 41923 track.values = convertArray( values, track.values.constructor ); 41924 41925 tracks.push( track ); 41926 41927 } 41928 41929 clip.tracks = tracks; 41930 41931 // find minimum .times value across all tracks in the trimmed clip 41932 41933 let minStartTime = Infinity; 41934 41935 for ( let i = 0; i < clip.tracks.length; ++ i ) { 41936 41937 if ( minStartTime > clip.tracks[ i ].times[ 0 ] ) { 41938 41939 minStartTime = clip.tracks[ i ].times[ 0 ]; 41940 41941 } 41942 41943 } 41944 41945 // shift all tracks such that clip begins at t=0 41946 41947 for ( let i = 0; i < clip.tracks.length; ++ i ) { 41948 41949 clip.tracks[ i ].shift( -1 * minStartTime ); 41950 41951 } 41952 41953 clip.resetDuration(); 41954 41955 return clip; 41956 41957} 41958 41959/** 41960 * Converts the keyframes of the given animation clip to an additive format. 41961 * 41962 * @param {AnimationClip} targetClip - The clip to make additive. 41963 * @param {number} [referenceFrame=0] - The reference frame. 41964 * @param {AnimationClip} [referenceClip=targetClip] - The reference clip. 41965 * @param {number} [fps=30] - The FPS. 41966 * @return {AnimationClip} The updated clip which is now additive. 41967 */ 41968function makeClipAdditive( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30 ) { 41969 41970 if ( fps <= 0 ) fps = 30; 41971 41972 const numTracks = referenceClip.tracks.length; 41973 const referenceTime = referenceFrame / fps; 41974 41975 // Make each track's values relative to the values at the reference frame 41976 for ( let i = 0; i < numTracks; ++ i ) { 41977 41978 const referenceTrack = referenceClip.tracks[ i ]; 41979 const referenceTrackType = referenceTrack.ValueTypeName; 41980 41981 // Skip this track if it's non-numeric 41982 if ( referenceTrackType === 'bool' || referenceTrackType === 'string' ) continue; 41983 41984 // Find the track in the target clip whose name and type matches the reference track 41985 const targetTrack = targetClip.tracks.find( function ( track ) { 41986 41987 return track.name === referenceTrack.name 41988 && track.ValueTypeName === referenceTrackType; 41989 41990 } ); 41991 41992 if ( targetTrack === undefined ) continue; 41993 41994 let referenceOffset = 0; 41995 const referenceValueSize = referenceTrack.getValueSize(); 41996 41997 if ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { 41998 41999 referenceOffset = referenceValueSize / 3; 42000 42001 } 42002 42003 let targetOffset = 0; 42004 const targetValueSize = targetTrack.getValueSize(); 42005 42006 if ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { 42007 42008 targetOffset = targetValueSize / 3; 42009 42010 } 42011 42012 const lastIndex = referenceTrack.times.length - 1; 42013 let referenceValue; 42014 42015 // Find the value to subtract out of the track 42016 if ( referenceTime <= referenceTrack.times[ 0 ] ) { 42017 42018 // Reference frame is earlier than the first keyframe, so just use the first keyframe 42019 const startIndex = referenceOffset; 42020 const endIndex = referenceValueSize - referenceOffset; 42021 referenceValue = referenceTrack.values.slice( startIndex, endIndex ); 42022 42023 } else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) { 42024 42025 // Reference frame is after the last keyframe, so just use the last keyframe 42026 const startIndex = lastIndex * referenceValueSize + referenceOffset; 42027 const endIndex = startIndex + referenceValueSize - referenceOffset; 42028 referenceValue = referenceTrack.values.slice( startIndex, endIndex ); 42029 42030 } else { 42031 42032 // Interpolate to the reference value 42033 const interpolant = referenceTrack.createInterpolant(); 42034 const startIndex = referenceOffset; 42035 const endIndex = referenceValueSize - referenceOffset; 42036 interpolant.evaluate( referenceTime ); 42037 referenceValue = interpolant.resultBuffer.slice( startIndex, endIndex ); 42038 42039 } 42040 42041 // Conjugate the quaternion 42042 if ( referenceTrackType === 'quaternion' ) { 42043 42044 const referenceQuat = new Quaternion().fromArray( referenceValue ).normalize().conjugate(); 42045 referenceQuat.toArray( referenceValue ); 42046 42047 } 42048 42049 // Subtract the reference value from all of the track values 42050 42051 const numTimes = targetTrack.times.length; 42052 for ( let j = 0; j < numTimes; ++ j ) { 42053 42054 const valueStart = j * targetValueSize + targetOffset; 42055 42056 if ( referenceTrackType === 'quaternion' ) { 42057 42058 // Multiply the conjugate for quaternion track types 42059 Quaternion.multiplyQuaternionsFlat( 42060 targetTrack.values, 42061 valueStart, 42062 referenceValue, 42063 0, 42064 targetTrack.values, 42065 valueStart 42066 ); 42067 42068 } else { 42069 42070 const valueEnd = targetValueSize - targetOffset * 2; 42071 42072 // Subtract each value for all other numeric track types 42073 for ( let k = 0; k < valueEnd; ++ k ) { 42074 42075 targetTrack.values[ valueStart + k ] -= referenceValue[ k ]; 42076 42077 } 42078 42079 } 42080 42081 } 42082 42083 } 42084 42085 targetClip.blendMode = AdditiveAnimationBlendMode; 42086 42087 return targetClip; 42088 42089} 42090 42091/** 42092 * A class with various methods to assist with animations. 42093 * 42094 * @hideconstructor 42095 */ 42096class AnimationUtils { 42097 42098 /** 42099 * Converts an array to a specific type 42100 * 42101 * @static 42102 * @param {TypedArray|Array} array - The array to convert. 42103 * @param {TypedArray.constructor} type - The constructor of a type array. 42104 * @return {TypedArray} The converted array 42105 */ 42106 static convertArray( array, type ) { 42107 42108 return convertArray( array, type ); 42109 42110 } 42111 42112 /** 42113 * Returns `true` if the given object is a typed array. 42114 * 42115 * @static 42116 * @param {any} object - The object to check. 42117 * @return {boolean} Whether the given object is a typed array. 42118 */ 42119 static isTypedArray( object ) { 42120 42121 return isTypedArray( object ); 42122 42123 } 42124 42125 /** 42126 * Returns `true` if the given keyframe track settings hold Bezier tangent data. 42127 * 42128 * @static 42129 * @param {?Object} settings - The settings of a keyframe track. 42130 * @return {boolean} Whether both tangent arrays are defined or not. 42131 */ 42132 static hasTangents( settings ) { 42133 42134 return hasTangents( settings ); 42135 42136 } 42137 42138 /** 42139 * Returns an array by which times and values can be sorted. 42140 * 42141 * @static 42142 * @param {Array<number>} times - The keyframe time values. 42143 * @return {Array<number>} The array. 42144 */ 42145 static getKeyframeOrder( times ) { 42146 42147 return getKeyframeOrder( times ); 42148 42149 } 42150 42151 /** 42152 * Sorts the given array by the previously computed order via `getKeyframeOrder()`. 42153 * 42154 * @static 42155 * @param {Array<number>} values - The values to sort. 42156 * @param {number} stride - The stride. 42157 * @param {Array<number>} order - The sort order. 42158 * @return {Array<number>} The sorted values. 42159 */ 42160 static sortedArray( values, stride, order ) { 42161 42162 return sortedArray( values, stride, order ); 42163 42164 } 42165 42166 /** 42167 * Used for parsing AOS keyframe formats. 42168 * 42169 * @static 42170 * @param {Array<number>} jsonKeys - A list of JSON keyframes. 42171 * @param {Array<number>} times - This array will be filled with keyframe times by this method. 42172 * @param {Array<number>} values - This array will be filled with keyframe values by this method. 42173 * @param {string} valuePropertyName - The name of the property to use. 42174 */ 42175 static flattenJSON( jsonKeys, times, values, valuePropertyName ) { 42176 42177 flattenJSON( jsonKeys, times, values, valuePropertyName ); 42178 42179 } 42180 42181 /** 42182 * Creates a new clip, containing only the segment of the original clip between the given frames. 42183 * 42184 * @static 42185 * @param {AnimationClip} sourceClip - The values to sort. 42186 * @param {string} name - The name of the clip. 42187 * @param {number} startFrame - The start frame. 42188 * @param {number} endFrame - The end frame. 42189 * @param {number} [fps=30] - The FPS. 42190 * @return {AnimationClip} The new sub clip. 42191 */ 42192 static subclip( sourceClip, name, startFrame, endFrame, fps = 30 ) { 42193 42194 return subclip( sourceClip, name, startFrame, endFrame, fps ); 42195 42196 } 42197 42198 /** 42199 * Converts the keyframes of the given animation clip to an additive format. 42200 * 42201 * @static 42202 * @param {AnimationClip} targetClip - The clip to make additive. 42203 * @param {number} [referenceFrame=0] - The reference frame. 42204 * @param {AnimationClip} [referenceClip=targetClip] - The reference clip. 42205 * @param {number} [fps=30] - The FPS. 42206 * @return {AnimationClip} The updated clip which is now additive. 42207 */ 42208 static makeClipAdditive( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30 ) { 42209 42210 return makeClipAdditive( targetClip, referenceFrame, referenceClip, fps ); 42211 42212 } 42213 42214} 42215 42216/** 42217 * Abstract base class of interpolants over parametric samples. 42218 * 42219 * The parameter domain is one dimensional, typically the time or a path 42220 * along a curve defined by the data. 42221 * 42222 * The sample values can have any dimensionality and derived classes may 42223 * apply special interpretations to the data. 42224 * 42225 * This class provides the interval seek in a Template Method, deferring 42226 * the actual interpolation to derived classes. 42227 * 42228 * Time complexity is O(1) for linear access crossing at most two points 42229 * and O(log N) for random access, where N is the number of positions. 42230 * 42231 * References: {@link http://www.oodesign.com/template-method-pattern.html} 42232 * 42233 * @abstract 42234 */ 42235class Interpolant { 42236 42237 /** 42238 * Constructs a new interpolant. 42239 * 42240 * @param {TypedArray} parameterPositions - The parameter positions hold the interpolation factors. 42241 * @param {TypedArray} sampleValues - The sample values. 42242 * @param {number} sampleSize - The sample size 42243 * @param {TypedArray} [resultBuffer] - The result buffer. 42244 */ 42245 constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { 42246 42247 /** 42248 * The parameter positions. 42249 * 42250 * @type {TypedArray} 42251 */ 42252 this.parameterPositions = parameterPositions; 42253 42254 /** 42255 * A cache index. 42256 * 42257 * @private 42258 * @type {number} 42259 * @default 0 42260 */ 42261 this._cachedIndex = 0; 42262 42263 /** 42264 * The result buffer. 42265 * 42266 * @type {TypedArray} 42267 */ 42268 this.resultBuffer = resultBuffer !== undefined ? resultBuffer : new sampleValues.constructor( sampleSize ); 42269 42270 /** 42271 * The sample values. 42272 * 42273 * @type {TypedArray} 42274 */ 42275 this.sampleValues = sampleValues; 42276 42277 /** 42278 * The value size. 42279 * 42280 * @type {TypedArray} 42281 */ 42282 this.valueSize = sampleSize; 42283 42284 /** 42285 * The interpolation settings. 42286 * 42287 * @type {?Object} 42288 * @default null 42289 */ 42290 this.settings = null; 42291 42292 /** 42293 * The default settings object. 42294 * 42295 * @type {Object} 42296 */ 42297 this.DefaultSettings_ = {}; 42298 42299 } 42300 42301 /** 42302 * Evaluate the interpolant at position `t`. 42303 * 42304 * @param {number} t - The interpolation factor. 42305 * @return {TypedArray} The result buffer. 42306 */ 42307 evaluate( t ) { 42308 42309 const pp = this.parameterPositions; 42310 let i1 = this._cachedIndex, 42311 t1 = pp[ i1 ], 42312 t0 = pp[ i1 - 1 ]; 42313 42314 validate_interval: { 42315 42316 seek: { 42317 42318 let right; 42319 42320 linear_scan: { 42321 42322 //- See http://jsperf.com/comparison-to-undefined/3 42323 //- slower code: 42324 //- 42325 //- if ( t >= t1 || t1 === undefined ) { 42326 forward_scan: if ( ! ( t < t1 ) ) { 42327 42328 for ( let giveUpAt = i1 + 2; ; ) { 42329 42330 if ( t1 === undefined ) { 42331 42332 if ( t < t0 ) break forward_scan; 42333 42334 // after end 42335 42336 i1 = pp.length; 42337 this._cachedIndex = i1; 42338 return this.copySampleValue_( i1 - 1 ); 42339 42340 } 42341 42342 if ( i1 === giveUpAt ) break; // this loop 42343 42344 t0 = t1; 42345 t1 = pp[ ++ i1 ]; 42346 42347 if ( t < t1 ) { 42348 42349 // we have arrived at the sought interval 42350 break seek; 42351 42352 } 42353 42354 } 42355 42356 // prepare binary search on the right side of the index 42357 right = pp.length; 42358 break linear_scan; 42359 42360 } 42361 42362 //- slower code: 42363 //- if ( t < t0 || t0 === undefined ) { 42364 if ( ! ( t >= t0 ) ) { 42365 42366 // looping? 42367 42368 const t1global = pp[ 1 ]; 42369 42370 if ( t < t1global ) { 42371 42372 i1 = 2; // + 1, using the scan for the details 42373 t0 = t1global; 42374 42375 } 42376 42377 // linear reverse scan 42378 42379 for ( let giveUpAt = i1 - 2; ; ) { 42380 42381 if ( t0 === undefined ) { 42382 42383 // before start 42384 42385 this._cachedIndex = 0; 42386 return this.copySampleValue_( 0 ); 42387 42388 } 42389 42390 if ( i1 === giveUpAt ) break; // this loop 42391 42392 t1 = t0; 42393 t0 = pp[ -- i1 - 1 ]; 42394 42395 if ( t >= t0 ) { 42396 42397 // we have arrived at the sought interval 42398 break seek; 42399 42400 } 42401 42402 } 42403 42404 // prepare binary search on the left side of the index 42405 right = i1; 42406 i1 = 0; 42407 break linear_scan; 42408 42409 } 42410 42411 // the interval is valid 42412 42413 break validate_interval; 42414 42415 } // linear scan 42416 42417 // binary search 42418 42419 while ( i1 < right ) { 42420 42421 const mid = ( i1 + right ) >>> 1; 42422 42423 if ( t < pp[ mid ] ) { 42424 42425 right = mid; 42426 42427 } else { 42428 42429 i1 = mid + 1; 42430 42431 } 42432 42433 } 42434 42435 t1 = pp[ i1 ]; 42436 t0 = pp[ i1 - 1 ]; 42437 42438 // check boundary cases, again 42439 42440 if ( t0 === undefined ) { 42441 42442 this._cachedIndex = 0; 42443 return this.copySampleValue_( 0 ); 42444 42445 } 42446 42447 if ( t1 === undefined ) { 42448 42449 i1 = pp.length; 42450 this._cachedIndex = i1; 42451 return this.copySampleValue_( i1 - 1 ); 42452 42453 } 42454 42455 } // seek 42456 42457 this._cachedIndex = i1; 42458 42459 this.intervalChanged_( i1, t0, t1 ); 42460 42461 } // validate_interval 42462 42463 return this.interpolate_( i1, t0, t, t1 ); 42464 42465 } 42466 42467 /** 42468 * Returns the interpolation settings. 42469 * 42470 * @return {Object} The interpolation settings. 42471 */ 42472 getSettings_() { 42473 42474 return this.settings || this.DefaultSettings_; 42475 42476 } 42477 42478 /** 42479 * Copies a sample value to the result buffer. 42480 * 42481 * @param {number} index - An index into the sample value buffer. 42482 * @return {TypedArray} The result buffer. 42483 */ 42484 copySampleValue_( index ) { 42485 42486 // copies a sample value to the result buffer 42487 42488 const result = this.resultBuffer, 42489 values = this.sampleValues, 42490 stride = this.valueSize, 42491 offset = index * stride; 42492 42493 for ( let i = 0; i !== stride; ++ i ) { 42494 42495 result[ i ] = values[ offset + i ]; 42496 42497 } 42498 42499 return result; 42500 42501 } 42502 42503 /** 42504 * Copies a sample value to the result buffer. 42505 * 42506 * @abstract 42507 * @param {number} i1 - An index into the sample value buffer. 42508 * @param {number} t0 - The previous interpolation factor. 42509 * @param {number} t - The current interpolation factor. 42510 * @param {number} t1 - The next interpolation factor. 42511 * @return {TypedArray} The result buffer. 42512 */ 42513 interpolate_( /* i1, t0, t, t1 */ ) { 42514 42515 throw new Error( 'THREE.Interpolant: Call to abstract method.' ); 42516 // implementations shall return this.resultBuffer 42517 42518 } 42519 42520 /** 42521 * Optional method that is executed when the interval has changed. 42522 * 42523 * @param {number} i1 - An index into the sample value buffer. 42524 * @param {number} t0 - The previous interpolation factor. 42525 * @param {number} t - The current interpolation factor. 42526 */ 42527 intervalChanged_( /* i1, t0, t1 */ ) { 42528 42529 // empty 42530 42531 } 42532 42533} 42534 42535/** 42536 * Fast and simple cubic spline interpolant. 42537 * 42538 * It was derived from a Hermitian construction setting the first derivative 42539 * at each sample position to the linear slope between neighboring positions 42540 * over their parameter interval. 42541 * 42542 * @augments Interpolant 42543 */ 42544class CubicInterpolant extends Interpolant { 42545 42546 /** 42547 * Constructs a new cubic interpolant. 42548 * 42549 * @param {TypedArray} parameterPositions - The parameter positions hold the interpolation factors. 42550 * @param {TypedArray} sampleValues - The sample values. 42551 * @param {number} sampleSize - The sample size 42552 * @param {TypedArray} [resultBuffer] - The result buffer. 42553 */ 42554 constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { 42555 42556 super( parameterPositions, sampleValues, sampleSize, resultBuffer ); 42557 42558 this._weightPrev = -0; 42559 this._offsetPrev = -0; 42560 this._weightNext = -0; 42561 this._offsetNext = -0; 42562 42563 this.DefaultSettings_ = { 42564 42565 endingStart: ZeroCurvatureEnding, 42566 endingEnd: ZeroCurvatureEnding 42567 42568 }; 42569 42570 } 42571 42572 intervalChanged_( i1, t0, t1 ) { 42573 42574 const pp = this.parameterPositions; 42575 let iPrev = i1 - 2, 42576 iNext = i1 + 1, 42577 42578 tPrev = pp[ iPrev ], 42579 tNext = pp[ iNext ]; 42580 42581 if ( tPrev === undefined ) { 42582 42583 switch ( this.getSettings_().endingStart ) { 42584 42585 case ZeroSlopeEnding: 42586 42587 // f'(t0) = 0 42588 iPrev = i1; 42589 tPrev = 2 * t0 - t1; 42590 42591 break; 42592 42593 case WrapAroundEnding: 42594 42595 // use the other end of the curve 42596 iPrev = pp.length - 2; 42597 tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; 42598 42599 break; 42600 42601 default: // ZeroCurvatureEnding 42602 42603 // f''(t0) = 0 a.k.a. Natural Spline 42604 iPrev = i1; 42605 tPrev = t1; 42606 42607 } 42608 42609 } 42610 42611 if ( tNext === undefined ) { 42612 42613 switch ( this.getSettings_().endingEnd ) { 42614 42615 case ZeroSlopeEnding: 42616 42617 // f'(tN) = 0 42618 iNext = i1; 42619 tNext = 2 * t1 - t0; 42620 42621 break; 42622 42623 case WrapAroundEnding: 42624 42625 // use the other end of the curve 42626 iNext = 1; 42627 tNext = t1 + pp[ 1 ] - pp[ 0 ]; 42628 42629 break; 42630 42631 default: // ZeroCurvatureEnding 42632 42633 // f''(tN) = 0, a.k.a. Natural Spline 42634 iNext = i1 - 1; 42635 tNext = t0; 42636 42637 } 42638 42639 } 42640 42641 const halfDt = ( t1 - t0 ) * 0.5, 42642 stride = this.valueSize; 42643 42644 this._weightPrev = halfDt / ( t0 - tPrev ); 42645 this._weightNext = halfDt / ( tNext - t1 ); 42646 this._offsetPrev = iPrev * stride; 42647 this._offsetNext = iNext * stride; 42648 42649 } 42650 42651 interpolate_( i1, t0, t, t1 ) { 42652 42653 const result = this.resultBuffer, 42654 values = this.sampleValues, 42655 stride = this.valueSize, 42656 42657 o1 = i1 * stride, o0 = o1 - stride, 42658 oP = this._offsetPrev, oN = this._offsetNext, 42659 wP = this._weightPrev, wN = this._weightNext, 42660 42661 p = ( t - t0 ) / ( t1 - t0 ), 42662 pp = p * p, 42663 ppp = pp * p; 42664 42665 // evaluate polynomials 42666 42667 const sP = - wP * ppp + 2 * wP * pp - wP * p; 42668 const s0 = ( 1 + wP ) * ppp + ( -1.5 - 2 * wP ) * pp + ( -0.5 + wP ) * p + 1; 42669 const s1 = ( -1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; 42670 const sN = wN * ppp - wN * pp; 42671 42672 // combine data linearly 42673 42674 for ( let i = 0; i !== stride; ++ i ) { 42675 42676 result[ i ] = 42677 sP * values[ oP + i ] + 42678 s0 * values[ o0 + i ] + 42679 s1 * values[ o1 + i ] + 42680 sN * values[ oN + i ]; 42681 42682 } 42683 42684 return result; 42685 42686 } 42687 42688} 42689 42690/** 42691 * A basic linear interpolant. 42692 * 42693 * @augments Interpolant 42694 */ 42695class LinearInterpolant extends Interpolant { 42696 42697 /** 42698 * Constructs a new linear interpolant. 42699 * 42700 * @param {TypedArray} parameterPositions - The parameter positions hold the interpolation factors. 42701 * @param {TypedArray} sampleValues - The sample values. 42702 * @param {number} sampleSize - The sample size 42703 * @param {TypedArray} [resultBuffer] - The result buffer. 42704 */ 42705 constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { 42706 42707 super( parameterPositions, sampleValues, sampleSize, resultBuffer ); 42708 42709 } 42710 42711 interpolate_( i1, t0, t, t1 ) { 42712 42713 const result = this.resultBuffer, 42714 values = this.sampleValues, 42715 stride = this.valueSize, 42716 42717 offset1 = i1 * stride, 42718 offset0 = offset1 - stride, 42719 42720 weight1 = ( t - t0 ) / ( t1 - t0 ), 42721 weight0 = 1 - weight1; 42722 42723 for ( let i = 0; i !== stride; ++ i ) { 42724 42725 result[ i ] = 42726 values[ offset0 + i ] * weight0 + 42727 values[ offset1 + i ] * weight1; 42728 42729 } 42730 42731 return result; 42732 42733 } 42734 42735} 42736 42737/** 42738 * Interpolant that evaluates to the sample value at the position preceding 42739 * the parameter. 42740 * 42741 * @augments Interpolant 42742 */ 42743class DiscreteInterpolant extends Interpolant { 42744 42745 /** 42746 * Constructs a new discrete interpolant. 42747 * 42748 * @param {TypedArray} parameterPositions - The parameter positions hold the interpolation factors. 42749 * @param {TypedArray} sampleValues - The sample values. 42750 * @param {number} sampleSize - The sample size 42751 * @param {TypedArray} [resultBuffer] - The result buffer. 42752 */ 42753 constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { 42754 42755 super( parameterPositions, sampleValues, sampleSize, resultBuffer ); 42756 42757 } 42758 42759 interpolate_( i1 /*, t0, t, t1 */ ) { 42760 42761 return this.copySampleValue_( i1 - 1 ); 42762 42763 } 42764 42765} 42766 42767/** 42768 * A Bezier interpolant using cubic Bezier curves with 2D control points. 42769 * 42770 * This interpolant supports the COLLADA/Maya style of Bezier animation where 42771 * each keyframe has explicit in/out tangent control points specified as 42772 * 2D coordinates (time, value). 42773 * 42774 * Tangent data is read from `inTangents` and `outTangents` on the interpolant 42775 * (populated by `KeyframeTrack.InterpolantFactoryMethodBezier`). 42776 * 42777 * For a track with N keyframes and stride S: 42778 * - Each tangent array has N * S * 2 values 42779 * - Layout: [k0_c0_time, k0_c0_value, k0_c1_time, k0_c1_value, ..., k0_cS_time, k0_cS_value, 42780 * k1_c0_time, k1_c0_value, ...] 42781 * 42782 * @augments Interpolant 42783 */ 42784class BezierInterpolant extends Interpolant { 42785 42786 interpolate_( i1, t0, t, t1 ) { 42787 42788 const result = this.resultBuffer; 42789 const values = this.sampleValues; 42790 const stride = this.valueSize; 42791 42792 const offset1 = i1 * stride; 42793 const offset0 = offset1 - stride; 42794 42795 const inTangents = this.inTangents; 42796 const outTangents = this.outTangents; 42797 42798 // If no tangent data, fall back to linear interpolation 42799 if ( ! inTangents || ! outTangents ) { 42800 42801 const weight1 = ( t - t0 ) / ( t1 - t0 ); 42802 const weight0 = 1 - weight1; 42803 42804 for ( let i = 0; i !== stride; ++ i ) { 42805 42806 result[ i ] = values[ offset0 + i ] * weight0 + values[ offset1 + i ] * weight1; 42807 42808 } 42809 42810 return result; 42811 42812 } 42813 42814 const tangentStride = stride * 2; 42815 const i0 = i1 - 1; 42816 42817 for ( let i = 0; i !== stride; ++ i ) { 42818 42819 const v0 = values[ offset0 + i ]; 42820 const v1 = values[ offset1 + i ]; 42821 42822 // outTangent of previous keyframe (C0) 42823 const outTangentOffset = i0 * tangentStride + i * 2; 42824 const c0x = outTangents[ outTangentOffset ]; 42825 const c0y = outTangents[ outTangentOffset + 1 ]; 42826 42827 // inTangent of current keyframe (C1) 42828 const inTangentOffset = i1 * tangentStride + i * 2; 42829 const c1x = inTangents[ inTangentOffset ]; 42830 const c1y = inTangents[ inTangentOffset + 1 ]; 42831 42832 // Find the curve parameter s where the Bezier X(s) matches t, then evaluate Y(s) 42833 const s = solveBezierParameter( t, t0, c0x, c1x, t1 ); 42834 42835 result[ i ] = cubicBezier( s, v0, c0y, c1y, v1 ); 42836 42837 } 42838 42839 return result; 42840 42841 } 42842 42843} 42844 42845function cubicBezier( s, p0, p1, p2, p3 ) { 42846 42847 const k = 1 - s; 42848 42849 return k * k * k * p0 + 3 * k * k * s * p1 + 3 * k * s * s * p2 + s * s * s * p3; 42850 42851} 42852 42853function cubicBezierSlope( s, p0, p1, p2, p3 ) { 42854 42855 const k = 1 - s; 42856 42857 return 3 * k * k * ( p1 - p0 ) + 6 * k * s * ( p2 - p1 ) + 3 * s * s * ( p3 - p2 ); 42858 42859} 42860 42861// Solves cubicBezier( s, x0, x1, x2, x3 ) = x for s in [0,1] using Newton-Raphson 42862 42863function solveBezierParameter( x, x0, x1, x2, x3 ) { 42864 42865 let s = ( x - x0 ) / ( x3 - x0 ); 42866 42867 for ( let i = 0; i < 8; i ++ ) { 42868 42869 const error = cubicBezier( s, x0, x1, x2, x3 ) - x; 42870 if ( Math.abs( error ) < 1e-10 ) break; 42871 42872 const slope = cubicBezierSlope( s, x0, x1, x2, x3 ); 42873 if ( Math.abs( slope ) < 1e-10 ) break; 42874 42875 s = Math.max( 0, Math.min( 1, s - error / slope ) ); 42876 42877 } 42878 42879 return s; 42880 42881} 42882 42883/** 42884 * Represents a timed sequence of keyframes, which are composed of lists of 42885 * times and related values, and which are used to animate a specific property 42886 * of an object. 42887 */ 42888class KeyframeTrack { 42889 42890 /** 42891 * Constructs a new keyframe track. 42892 * 42893 * @param {string} name - The keyframe track's name. 42894 * @param {Array<number>} times - A list of keyframe times. 42895 * @param {Array<number|string|boolean>} values - A list of keyframe values. 42896 * @param {(InterpolateLinear|InterpolateDiscrete|InterpolateSmooth|InterpolateBezier)} [interpolation] - The interpolation type. 42897 */ 42898 constructor( name, times, values, interpolation ) { 42899 42900 if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' ); 42901 if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name ); 42902 42903 /** 42904 * The track's name can refer to morph targets or bones or 42905 * possibly other values within an animated object. See {@link PropertyBinding#parseTrackName} 42906 * for the forms of strings that can be parsed for property binding. 42907 * 42908 * @type {string} 42909 */ 42910 this.name = name; 42911 42912 /** 42913 * The keyframe times. 42914 * 42915 * @type {Float32Array} 42916 */ 42917 this.times = convertArray( times, this.TimeBufferType ); 42918 42919 /** 42920 * The keyframe values. 42921 * 42922 * @type {Float32Array} 42923 */ 42924 this.values = convertArray( values, this.ValueBufferType ); 42925 42926 this.setInterpolation( interpolation || this.DefaultInterpolation ); 42927 42928 } 42929 42930 /** 42931 * Converts the keyframe track to JSON. 42932 * 42933 * @static 42934 * @param {KeyframeTrack} track - The keyframe track to serialize. 42935 * @return {Object} The serialized keyframe track as JSON. 42936 */ 42937 static toJSON( track ) { 42938 42939 const trackType = track.constructor; 42940 42941 let json; 42942 42943 // derived classes can define a static toJSON method 42944 if ( trackType.toJSON !== this.toJSON ) { 42945 42946 json = trackType.toJSON( track ); 42947 42948 } else { 42949 42950 // by default, we assume the data can be serialized as-is 42951 json = { 42952 42953 'name': track.name, 42954 'times': convertArray( track.times, Array ), 42955 'values': convertArray( track.values, Array ) 42956 42957 }; 42958 42959 const interpolation = track.getInterpolation(); 42960 42961 if ( interpolation !== track.DefaultInterpolation ) { 42962 42963 json.interpolation = interpolation; 42964 42965 } 42966 42967 if ( hasTangents( track.settings ) ) { 42968 42969 json.settings = { 42970 inTangents: convertArray( track.settings.inTangents, Array ), 42971 outTangents: convertArray( track.settings.outTangents, Array ) 42972 }; 42973 42974 } 42975 42976 } 42977 42978 json.type = track.ValueTypeName; // mandatory 42979 42980 return json; 42981 42982 } 42983 42984 /** 42985 * Factory method for creating a new discrete interpolant. 42986 * 42987 * @static 42988 * @param {TypedArray} [result] - The result buffer. 42989 * @return {DiscreteInterpolant} The new interpolant. 42990 */ 42991 InterpolantFactoryMethodDiscrete( result ) { 42992 42993 return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); 42994 42995 } 42996 42997 /** 42998 * Factory method for creating a new linear interpolant. 42999 * 43000 * @static 43001 * @param {TypedArray} [result] - The result buffer. 43002 * @return {LinearInterpolant} The new interpolant. 43003 */ 43004 InterpolantFactoryMethodLinear( result ) { 43005 43006 return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); 43007 43008 } 43009 43010 /** 43011 * Factory method for creating a new smooth interpolant. 43012 * 43013 * @static 43014 * @param {TypedArray} [result] - The result buffer. 43015 * @return {CubicInterpolant} The new interpolant. 43016 */ 43017 InterpolantFactoryMethodSmooth( result ) { 43018 43019 return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); 43020 43021 } 43022 43023 /** 43024 * Factory method for creating a new Bezier interpolant. 43025 * 43026 * The Bezier interpolant requires tangent data to be set via the `settings` property 43027 * on the track before creating the interpolant. The settings should contain: 43028 * - `inTangents`: Float32Array with [time, value] pairs per keyframe per component 43029 * - `outTangents`: Float32Array with [time, value] pairs per keyframe per component 43030 * 43031 * @static 43032 * @param {TypedArray} [result] - The result buffer. 43033 * @return {BezierInterpolant} The new interpolant. 43034 */ 43035 InterpolantFactoryMethodBezier( result ) { 43036 43037 const interpolant = new BezierInterpolant( this.times, this.values, this.getValueSize(), result ); 43038 43039 if ( this.settings ) { 43040 43041 interpolant.inTangents = this.settings.inTangents; 43042 interpolant.outTangents = this.settings.outTangents; 43043 43044 } 43045 43046 return interpolant; 43047 43048 } 43049 43050 /** 43051 * Defines the interpolation factor method for this keyframe track. 43052 * 43053 * @param {(InterpolateLinear|InterpolateDiscrete|InterpolateSmooth|InterpolateBezier)} interpolation - The interpolation type. 43054 * @return {KeyframeTrack} A reference to this keyframe track. 43055 */ 43056 setInterpolation( interpolation ) { 43057 43058 let factoryMethod; 43059 43060 switch ( interpolation ) { 43061 43062 case InterpolateDiscrete: 43063 43064 factoryMethod = this.InterpolantFactoryMethodDiscrete; 43065 43066 break; 43067 43068 case InterpolateLinear: 43069 43070 factoryMethod = this.InterpolantFactoryMethodLinear; 43071 43072 break; 43073 43074 case InterpolateSmooth: 43075 43076 factoryMethod = this.InterpolantFactoryMethodSmooth; 43077 43078 break; 43079 43080 case InterpolateBezier: 43081 43082 factoryMethod = this.InterpolantFactoryMethodBezier; 43083 43084 break; 43085 43086 } 43087 43088 if ( factoryMethod === undefined ) { 43089 43090 const message = 'unsupported interpolation for ' + 43091 this.ValueTypeName + ' keyframe track named ' + this.name; 43092 43093 if ( this.createInterpolant === undefined ) { 43094 43095 // fall back to default, unless the default itself is messed up 43096 if ( interpolation !== this.DefaultInterpolation ) { 43097 43098 this.setInterpolation( this.DefaultInterpolation ); 43099 43100 } else { 43101 43102 throw new Error( message ); // fatal, in this case 43103 43104 } 43105 43106 } 43107 43108 warn( 'KeyframeTrack:', message ); 43109 return this; 43110 43111 } 43112 43113 this.createInterpolant = factoryMethod; 43114 43115 return this; 43116 43117 } 43118 43119 /** 43120 * Returns the current interpolation type. 43121 * 43122 * @return {(InterpolateLinear|InterpolateDiscrete|InterpolateSmooth|InterpolateBezier)} The interpolation type. 43123 */ 43124 getInterpolation() { 43125 43126 switch ( this.createInterpolant ) { 43127 43128 case this.InterpolantFactoryMethodDiscrete: 43129 43130 return InterpolateDiscrete; 43131 43132 case this.InterpolantFactoryMethodLinear: 43133 43134 return InterpolateLinear; 43135 43136 case this.InterpolantFactoryMethodSmooth: 43137 43138 return InterpolateSmooth; 43139 43140 case this.InterpolantFactoryMethodBezier: 43141 43142 return InterpolateBezier; 43143 43144 } 43145 43146 } 43147 43148 /** 43149 * Returns the value size. 43150 * 43151 * @return {number} The value size. 43152 */ 43153 getValueSize() { 43154 43155 return this.values.length / this.times.length; 43156 43157 } 43158 43159 /** 43160 * Moves all keyframes either forward or backward in time. 43161 * 43162 * @param {number} timeOffset - The offset to move the time values. 43163 * @return {KeyframeTrack} A reference to this keyframe track. 43164 */ 43165 shift( timeOffset ) { 43166 43167 if ( timeOffset !== 0.0 ) { 43168 43169 const times = this.times; 43170 43171 for ( let i = 0, n = times.length; i !== n; ++ i ) { 43172 43173 times[ i ] += timeOffset; 43174 43175 } 43176 43177 } 43178 43179 return this; 43180 43181 } 43182 43183 /** 43184 * Scale all keyframe times by a factor (useful for frame - seconds conversions). 43185 * 43186 * @param {number} timeScale - The time scale. 43187 * @return {KeyframeTrack} A reference to this keyframe track. 43188 */ 43189 scale( timeScale ) { 43190 43191 if ( timeScale !== 1.0 ) { 43192 43193 const times = this.times; 43194 43195 for ( let i = 0, n = times.length; i !== n; ++ i ) { 43196 43197 times[ i ] *= timeScale; 43198 43199 } 43200 43201 if ( hasTangents( this.settings ) ) { 43202 43203 scaleTangentTimes( this.settings.inTangents, timeScale ); 43204 scaleTangentTimes( this.settings.outTangents, timeScale ); 43205 43206 } 43207 43208 } 43209 43210 return this; 43211 43212 } 43213 43214 /** 43215 * Removes keyframes before and after animation without changing any values within the defined time range. 43216 * 43217 * Note: The method does not shift around keys to the start of the track time, because for interpolated 43218 * keys this will change their values 43219 * 43220 * @param {number} startTime - The start time. 43221 * @param {number} endTime - The end time. 43222 * @return {KeyframeTrack} A reference to this keyframe track. 43223 */ 43224 trim( startTime, endTime ) { 43225 43226 const times = this.times, 43227 nKeys = times.length; 43228 43229 let from = 0, 43230 to = nKeys - 1; 43231 43232 while ( from !== nKeys && times[ from ] < startTime ) { 43233 43234 ++ from; 43235 43236 } 43237 43238 while ( to !== -1 && times[ to ] > endTime ) { 43239 43240 -- to; 43241 43242 } 43243 43244 ++ to; // inclusive -> exclusive bound 43245 43246 if ( from !== 0 || to !== nKeys ) { 43247 43248 // empty tracks are forbidden, so keep at least one keyframe 43249 if ( from >= to ) { 43250 43251 to = Math.max( to, 1 ); 43252 from = to - 1; 43253 43254 } 43255 43256 const stride = this.getValueSize(); 43257 this.times = times.slice( from, to ); 43258 this.values = this.values.slice( from * stride, to * stride ); 43259 43260 } 43261 43262 return this; 43263 43264 } 43265 43266 /** 43267 * Performs minimal validation on the keyframe track. Returns `true` if the values 43268 * are valid. 43269 * 43270 * @return {boolean} Whether the keyframes are valid or not. 43271 */ 43272 validate() { 43273 43274 let valid = true; 43275 43276 const valueSize = this.getValueSize(); 43277 if ( valueSize - Math.floor( valueSize ) !== 0 ) { 43278 43279 error( 'KeyframeTrack: Invalid value size in track.', this ); 43280 valid = false; 43281 43282 } 43283 43284 const times = this.times, 43285 values = this.values, 43286 43287 nKeys = times.length; 43288 43289 if ( nKeys === 0 ) { 43290 43291 error( 'KeyframeTrack: Track is empty.', this ); 43292 valid = false; 43293 43294 } 43295 43296 let prevTime = null; 43297 43298 for ( let i = 0; i !== nKeys; i ++ ) { 43299 43300 const currTime = times[ i ]; 43301 43302 if ( typeof currTime === 'number' && isNaN( currTime ) ) { 43303 43304 error( 'KeyframeTrack: Time is not a valid number.', this, i, currTime ); 43305 valid = false; 43306 break; 43307 43308 } 43309 43310 if ( prevTime !== null && prevTime > currTime ) { 43311 43312 error( 'KeyframeTrack: Out of order keys.', this, i, currTime, prevTime ); 43313 valid = false; 43314 break; 43315 43316 } 43317 43318 prevTime = currTime; 43319 43320 } 43321 43322 if ( values !== undefined ) { 43323 43324 if ( isTypedArray( values ) ) { 43325 43326 for ( let i = 0, n = values.length; i !== n; ++ i ) { 43327 43328 const value = values[ i ]; 43329 43330 if ( isNaN( value ) ) { 43331 43332 error( 'KeyframeTrack: Value is not a valid number.', this, i, value ); 43333 valid = false; 43334 break; 43335 43336 } 43337 43338 } 43339 43340 } 43341 43342 } 43343 43344 return valid; 43345 43346 } 43347 43348 /** 43349 * Optimizes this keyframe track by removing equivalent sequential keys (which are 43350 * common in morph target sequences). 43351 * 43352 * @return {KeyframeTrack} A reference to this keyframe track. 43353 */ 43354 optimize() { 43355 43356 // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) 43357 43358 // times or values may be shared with other tracks, so overwriting is unsafe 43359 const times = this.times.slice(), 43360 values = this.values.slice(), 43361 stride = this.getValueSize(), 43362 43363 smoothInterpolation = this.getInterpolation() === InterpolateSmooth, 43364 43365 lastIndex = times.length - 1; 43366 43367 let writeIndex = 1; 43368 43369 for ( let i = 1; i < lastIndex; ++ i ) { 43370 43371 let keep = false; 43372 43373 const time = times[ i ]; 43374 const timeNext = times[ i + 1 ]; 43375 43376 // remove adjacent keyframes scheduled at the same time 43377 43378 if ( time !== timeNext && ( i !== 1 || time !== times[ 0 ] ) ) { 43379 43380 if ( ! smoothInterpolation ) { 43381 43382 // remove unnecessary keyframes same as their neighbors 43383 43384 const offset = i * stride, 43385 offsetP = offset - stride, 43386 offsetN = offset + stride; 43387 43388 for ( let j = 0; j !== stride; ++ j ) { 43389 43390 const value = values[ offset + j ]; 43391 43392 if ( value !== values[ offsetP + j ] || 43393 value !== values[ offsetN + j ] ) { 43394 43395 keep = true; 43396 break; 43397 43398 } 43399 43400 } 43401 43402 } else { 43403 43404 keep = true; 43405 43406 } 43407 43408 } 43409 43410 // in-place compaction 43411 43412 if ( keep ) { 43413 43414 if ( i !== writeIndex ) { 43415 43416 times[ writeIndex ] = times[ i ]; 43417 43418 const readOffset = i * stride, 43419 writeOffset = writeIndex * stride; 43420 43421 for ( let j = 0; j !== stride; ++ j ) { 43422 43423 values[ writeOffset + j ] = values[ readOffset + j ]; 43424 43425 } 43426 43427 } 43428 43429 ++ writeIndex; 43430 43431 } 43432 43433 } 43434 43435 // flush last keyframe (compaction looks ahead) 43436 43437 if ( lastIndex > 0 ) { 43438 43439 times[ writeIndex ] = times[ lastIndex ]; 43440 43441 for ( let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { 43442 43443 values[ writeOffset + j ] = values[ readOffset + j ]; 43444 43445 } 43446 43447 ++ writeIndex; 43448 43449 } 43450 43451 if ( writeIndex !== times.length ) { 43452 43453 this.times = times.slice( 0, writeIndex ); 43454 this.values = values.slice( 0, writeIndex * stride ); 43455 43456 } else { 43457 43458 this.times = times; 43459 this.values = values; 43460 43461 } 43462 43463 return this; 43464 43465 } 43466 43467 /** 43468 * Returns a new keyframe track with copied values from this instance. 43469 * 43470 * @return {KeyframeTrack} A clone of this instance. 43471 */ 43472 clone() { 43473 43474 const times = this.times.slice(); 43475 const values = this.values.slice(); 43476 43477 const TypedKeyframeTrack = this.constructor; 43478 const track = new TypedKeyframeTrack( this.name, times, values ); 43479 43480 // Interpolant argument to constructor is not saved, so copy the factory method directly. 43481 track.createInterpolant = this.createInterpolant; 43482 43483 if ( hasTangents( this.settings ) ) { 43484 43485 track.settings = { 43486 inTangents: this.settings.inTangents.slice(), 43487 outTangents: this.settings.outTangents.slice() 43488 }; 43489 43490 } 43491 43492 return track; 43493 43494 } 43495 43496} 43497 43498function scaleTangentTimes( tangents, timeScale ) { 43499 43500 // tangents are [ time, value ] pairs, so only every second entry is a time 43501 43502 for ( let i = 0, n = tangents.length; i !== n; i += 2 ) { 43503 43504 tangents[ i ] *= timeScale; 43505 43506 } 43507 43508} 43509 43510/** 43511 * The value type name. 43512 * 43513 * @type {string} 43514 * @default '' 43515 */ 43516KeyframeTrack.prototype.ValueTypeName = ''; 43517 43518/** 43519 * The time buffer type of this keyframe track. 43520 * 43521 * @type {TypedArray|Array} 43522 * @default Float32Array.constructor 43523 */ 43524KeyframeTrack.prototype.TimeBufferType = Float32Array; 43525 43526/** 43527 * The value buffer type of this keyframe track. 43528 * 43529 * @type {TypedArray|Array} 43530 * @default Float32Array.constructor 43531 */ 43532KeyframeTrack.prototype.ValueBufferType = Float32Array; 43533 43534/** 43535 * The default interpolation type of this keyframe track. 43536 * 43537 * @type {(InterpolateLinear|InterpolateDiscrete|InterpolateSmooth|InterpolateBezier)} 43538 * @default InterpolateLinear 43539 */ 43540KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; 43541 43542/** 43543 * A track for boolean keyframe values. 43544 * 43545 * @augments KeyframeTrack 43546 */ 43547class BooleanKeyframeTrack extends KeyframeTrack { 43548 43549 /** 43550 * Constructs a new boolean keyframe track. 43551 * 43552 * This keyframe track type has no `interpolation` parameter because the 43553 * interpolation is always discrete. 43554 * 43555 * @param {string} name - The keyframe track's name. 43556 * @param {Array<number>} times - A list of keyframe times. 43557 * @param {Array<boolean>} values - A list of keyframe values. 43558 */ 43559 constructor( name, times, values ) { 43560 43561 super( name, times, values ); 43562 43563 } 43564 43565} 43566 43567/** 43568 * The value type name. 43569 * 43570 * @type {string} 43571 * @default 'bool' 43572 */ 43573BooleanKeyframeTrack.prototype.ValueTypeName = 'bool'; 43574 43575/** 43576 * The value buffer type of this keyframe track. 43577 * 43578 * @type {TypedArray|Array} 43579 * @default Array.constructor 43580 */ 43581BooleanKeyframeTrack.prototype.ValueBufferType = Array; 43582 43583/** 43584 * The default interpolation type of this keyframe track. 43585 * 43586 * @type {(InterpolateLinear|InterpolateDiscrete|InterpolateSmooth)} 43587 * @default InterpolateDiscrete 43588 */ 43589BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; 43590BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; 43591BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; 43592 43593/** 43594 * A track for color keyframe values. 43595 * 43596 * @augments KeyframeTrack 43597 */ 43598class ColorKeyframeTrack extends KeyframeTrack { 43599 43600 /** 43601 * Constructs a new color keyframe track. 43602 * 43603 * @param {string} name - The keyframe track's name. 43604 * @param {Array<number>} times - A list of keyframe times. 43605 * @param {Array<number>} values - A list of keyframe values. 43606 * @param {(InterpolateLinear|InterpolateDiscrete|InterpolateSmooth)} [interpolation] - The interpolation type. 43607 */ 43608 constructor( name, times, values, interpolation ) { 43609 43610 super( name, times, values, interpolation ); 43611 43612 } 43613 43614} 43615 43616/** 43617 * The value type name. 43618 * 43619 * @type {string} 43620 * @default 'color' 43621 */ 43622ColorKeyframeTrack.prototype.ValueTypeName = 'color'; 43623 43624/** 43625 * A track for numeric keyframe values. 43626 * 43627 * @augments KeyframeTrack 43628 */ 43629class NumberKeyframeTrack extends KeyframeTrack { 43630 43631 /** 43632 * Constructs a new number keyframe track. 43633 * 43634 * @param {string} name - The keyframe track's name. 43635 * @param {Array<number>} times - A list of keyframe times. 43636 * @param {Array<number>} values - A list of keyframe values. 43637 * @param {(InterpolateLinear|InterpolateDiscrete|InterpolateSmooth)} [interpolation] - The interpolation type. 43638 */ 43639 constructor( name, times, values, interpolation ) { 43640 43641 super( name, times, values, interpolation ); 43642 43643 } 43644 43645} 43646 43647/** 43648 * The value type name. 43649 * 43650 * @type {string} 43651 * @default 'number' 43652 */ 43653NumberKeyframeTrack.prototype.ValueTypeName = 'number'; 43654 43655/** 43656 * Spherical linear unit quaternion interpolant. 43657 * 43658 * @augments Interpolant 43659 */ 43660class QuaternionLinearInterpolant extends Interpolant { 43661 43662 /** 43663 * Constructs a new SLERP interpolant. 43664 * 43665 * @param {TypedArray} parameterPositions - The parameter positions hold the interpolation factors. 43666 * @param {TypedArray} sampleValues - The sample values. 43667 * @param {number} sampleSize - The sample size 43668 * @param {TypedArray} [resultBuffer] - The result buffer. 43669 */ 43670 constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { 43671 43672 super( parameterPositions, sampleValues, sampleSize, resultBuffer ); 43673 43674 } 43675 43676 interpolate_( i1, t0, t, t1 ) { 43677 43678 const result = this.resultBuffer, 43679 values = this.sampleValues, 43680 stride = this.valueSize, 43681 43682 alpha = ( t - t0 ) / ( t1 - t0 ); 43683 43684 let offset = i1 * stride; 43685 43686 for ( let end = offset + stride; offset !== end; offset += 4 ) { 43687 43688 Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); 43689 43690 } 43691 43692 return result; 43693 43694 } 43695 43696} 43697 43698/** 43699 * A track for Quaternion keyframe values. 43700 * 43701 * @augments KeyframeTrack 43702 */ 43703class QuaternionKeyframeTrack extends KeyframeTrack { 43704 43705 /** 43706 * Constructs a new Quaternion keyframe track. 43707 * 43708 * @param {string} name - The keyframe track's name. 43709 * @param {Array<number>} times - A list of keyframe times. 43710 * @param {Array<number>} values - A list of keyframe values. 43711 * @param {(InterpolateLinear|InterpolateDiscrete|InterpolateSmooth)} [interpolation] - The interpolation type. 43712 */ 43713 constructor( name, times, values, interpolation ) { 43714 43715 super( name, times, values, interpolation ); 43716 43717 } 43718 43719 /** 43720 * Overwritten so the method returns Quaternion based interpolant. 43721 * 43722 * @static 43723 * @param {TypedArray} [result] - The result buffer. 43724 * @return {QuaternionLinearInterpolant} The new interpolant. 43725 */ 43726 InterpolantFactoryMethodLinear( result ) { 43727 43728 return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); 43729 43730 } 43731 43732} 43733 43734/** 43735 * The value type name. 43736 * 43737 * @type {string} 43738 * @default 'quaternion' 43739 */ 43740QuaternionKeyframeTrack.prototype.ValueTypeName = 'quaternion'; 43741// ValueBufferType is inherited 43742// DefaultInterpolation is inherited; 43743QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; 43744 43745/** 43746 * A track for string keyframe values. 43747 * 43748 * @augments KeyframeTrack 43749 */ 43750class StringKeyframeTrack extends KeyframeTrack { 43751 43752 /** 43753 * Constructs a new string keyframe track. 43754 * 43755 * This keyframe track type has no `interpolation` parameter because the 43756 * interpolation is always discrete. 43757 * 43758 * @param {string} name - The keyframe track's name. 43759 * @param {Array<number>} times - A list of keyframe times. 43760 * @param {Array<string>} values - A list of keyframe values. 43761 */ 43762 constructor( name, times, values ) { 43763 43764 super( name, times, values ); 43765 43766 } 43767 43768} 43769 43770/** 43771 * The value type name. 43772 * 43773 * @type {string} 43774 * @default 'string' 43775 */ 43776StringKeyframeTrack.prototype.ValueTypeName = 'string'; 43777 43778/** 43779 * The value buffer type of this keyframe track. 43780 * 43781 * @type {TypedArray|Array} 43782 * @default Array.constructor 43783 */ 43784StringKeyframeTrack.prototype.ValueBufferType = Array; 43785 43786/** 43787 * The default interpolation type of this keyframe track. 43788 * 43789 * @type {(InterpolateLinear|InterpolateDiscrete|InterpolateSmooth)} 43790 * @default InterpolateDiscrete 43791 */ 43792StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; 43793StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; 43794StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; 43795 43796/** 43797 * A track for vector keyframe values. 43798 * 43799 * @augments KeyframeTrack 43800 */ 43801class VectorKeyframeTrack extends KeyframeTrack { 43802 43803 /** 43804 * Constructs a new vector keyframe track. 43805 * 43806 * @param {string} name - The keyframe track's name. 43807 * @param {Array<number>} times - A list of keyframe times. 43808 * @param {Array<number>} values - A list of keyframe values. 43809 * @param {(InterpolateLinear|InterpolateDiscrete|InterpolateSmooth)} [interpolation] - The interpolation type. 43810 */ 43811 constructor( name, times, values, interpolation ) { 43812 43813 super( name, times, values, interpolation ); 43814 43815 } 43816 43817} 43818 43819/** 43820 * The value type name. 43821 * 43822 * @type {string} 43823 * @default 'vector' 43824 */ 43825VectorKeyframeTrack.prototype.ValueTypeName = 'vector'; 43826 43827/** 43828 * A reusable set of keyframe tracks which represent an animation. 43829 */ 43830class AnimationClip { 43831 43832 /** 43833 * Constructs a new animation clip. 43834 * 43835 * Note: Instead of instantiating an AnimationClip directly with the constructor, you can 43836 * use the static interface of this class for creating clips. In most cases though, animation clips 43837 * will automatically be created by loaders when importing animated 3D assets. 43838 * 43839 * @param {string} [name=''] - The clip's name. 43840 * @param {number} [duration=-1] - The clip's duration in seconds. If a negative value is passed, 43841 * the duration will be calculated from the passed keyframes. 43842 * @param {Array<KeyframeTrack>} tracks - An array of keyframe tracks. 43843 * @param {(NormalAnimationBlendMode|AdditiveAnimationBlendMode)} [blendMode=NormalAnimationBlendMode] - Defines how the animation 43844 * is blended/combined when two or more animations are simultaneously played. 43845 */ 43846 constructor( name = '', duration = -1, tracks = [], blendMode = NormalAnimationBlendMode ) { 43847 43848 /** 43849 * The clip's name. 43850 * 43851 * @type {string} 43852 */ 43853 this.name = name; 43854 43855 /** 43856 * An array of keyframe tracks. 43857 * 43858 * @type {Array<KeyframeTrack>} 43859 */ 43860 this.tracks = tracks; 43861 43862 /** 43863 * The clip's duration in seconds. 43864 * 43865 * @type {number} 43866 */ 43867 this.duration = duration; 43868 43869 /** 43870 * Defines how the animation is blended/combined when two or more animations 43871 * are simultaneously played. 43872 * 43873 * @type {(NormalAnimationBlendMode|AdditiveAnimationBlendMode)} 43874 */ 43875 this.blendMode = blendMode; 43876 43877 /** 43878 * The UUID of the animation clip. 43879 * 43880 * @type {string} 43881 * @readonly 43882 */ 43883 this.uuid = generateUUID(); 43884 43885 /** 43886 * An object that can be used to store custom data about the animation clip. 43887 * It should not hold references to functions as these will not be cloned. 43888 * 43889 * @type {Object} 43890 */ 43891 this.userData = {}; 43892 43893 // this means it should figure out its duration by scanning the tracks 43894 if ( this.duration < 0 ) { 43895 43896 this.resetDuration(); 43897 43898 } 43899 43900 } 43901 43902 /** 43903 * Factory method for creating an animation clip from the given JSON. 43904 * 43905 * @static 43906 * @param {Object} json - The serialized animation clip. 43907 * @return {AnimationClip} The new animation clip. 43908 */ 43909 static parse( json ) { 43910 43911 const tracks = [], 43912 jsonTracks = json.tracks, 43913 frameTime = 1.0 / ( json.fps || 1.0 ); 43914 43915 for ( let i = 0, n = jsonTracks.length; i !== n; ++ i ) { 43916 43917 tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) ); 43918 43919 } 43920 43921 const clip = new this( json.name, json.duration, tracks, json.blendMode ); 43922 clip.uuid = json.uuid; 43923 43924 clip.userData = JSON.parse( json.userData || '{}' ); 43925 43926 return clip; 43927 43928 } 43929 43930 /** 43931 * Serializes the given animation clip into JSON. 43932 * 43933 * @static 43934 * @param {AnimationClip} clip - The animation clip to serialize. 43935 * @return {Object} The JSON object. 43936 */ 43937 static toJSON( clip ) { 43938 43939 const tracks = [], 43940 clipTracks = clip.tracks; 43941 43942 const json = { 43943 43944 'name': clip.name, 43945 'duration': clip.duration, 43946 'tracks': tracks, 43947 'uuid': clip.uuid, 43948 'blendMode': clip.blendMode, 43949 'userData': JSON.stringify( clip.userData ), 43950 43951 }; 43952 43953 for ( let i = 0, n = clipTracks.length; i !== n; ++ i ) { 43954 43955 tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); 43956 43957 } 43958 43959 return json; 43960 43961 } 43962 43963 /** 43964 * Returns a new animation clip from the passed morph targets array of a 43965 * geometry, taking a name and the number of frames per second. 43966 * 43967 * Note: The fps parameter is required, but the animation speed can be 43968 * overridden via {@link AnimationAction#setDuration}. 43969 * 43970 * @static 43971 * @param {string} name - The name of the animation clip. 43972 * @param {Array<Object>} morphTargetSequence - A sequence of morph targets. 43973 * @param {number} fps - The Frames-Per-Second value. 43974 * @param {boolean} noLoop - Whether the clip should be no loop or not. 43975 * @return {AnimationClip} The new animation clip. 43976 */ 43977 static CreateFromMorphTargetSequence( name, morphTargetSequence, fps, noLoop ) { 43978 43979 const numMorphTargets = morphTargetSequence.length; 43980 const tracks = []; 43981 43982 for ( let i = 0; i < numMorphTargets; i ++ ) { 43983 43984 let times = []; 43985 let values = []; 43986 43987 times.push( 43988 ( i + numMorphTargets - 1 ) % numMorphTargets, 43989 i, 43990 ( i + 1 ) % numMorphTargets ); 43991 43992 values.push( 0, 1, 0 ); 43993 43994 const order = getKeyframeOrder( times ); 43995 times = sortedArray( times, 1, order ); 43996 values = sortedArray( values, 1, order ); 43997 43998 // if there is a key at the first frame, duplicate it as the 43999 // last frame as well for perfect loop. 44000 if ( ! noLoop && times[ 0 ] === 0 ) { 44001 44002 times.push( numMorphTargets ); 44003 values.push( values[ 0 ] ); 44004 44005 } 44006 44007 tracks.push( 44008 new NumberKeyframeTrack( 44009 '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', 44010 times, values 44011 ).scale( 1.0 / fps ) ); 44012 44013 } 44014 44015 return new this( name, -1, tracks ); 44016 44017 } 44018 44019 /** 44020 * Searches for an animation clip by name, taking as its first parameter 44021 * either an array of clips, or a mesh or geometry that contains an 44022 * array named "animations" property. 44023 * 44024 * @static 44025 * @param {(Array<AnimationClip>|Object3D)} objectOrClipArray - The array or object to search through. 44026 * @param {string} name - The name to search for. 44027 * @return {?AnimationClip} The found animation clip. Returns `null` if no clip has been found. 44028 */ 44029 static findByName( objectOrClipArray, name ) { 44030 44031 let clipArray = objectOrClipArray; 44032 44033 if ( ! Array.isArray( objectOrClipArray ) ) { 44034 44035 const o = objectOrClipArray; 44036 clipArray = o.geometry && o.geometry.animations || o.animations; 44037 44038 } 44039 44040 for ( let i = 0; i < clipArray.length; i ++ ) { 44041 44042 if ( clipArray[ i ].name === name ) { 44043 44044 return clipArray[ i ]; 44045 44046 } 44047 44048 } 44049 44050 return null; 44051 44052 } 44053 44054 /** 44055 * Returns an array of new AnimationClips created from the morph target 44056 * sequences of a geometry, trying to sort morph target names into 44057 * animation-group-based patterns like "Walk_001, Walk_002, Run_001, Run_002...". 44058 * 44059 * See {@link MD2Loader#parse} as an example for how the method should be used. 44060 * 44061 * @static 44062 * @param {Array<Object>} morphTargets - A sequence of morph targets. 44063 * @param {number} fps - The Frames-Per-Second value. 44064 * @param {boolean} noLoop - Whether the clip should be no loop or not. 44065 * @return {Array<AnimationClip>} An array of new animation clips. 44066 */ 44067 static CreateClipsFromMorphTargetSequences( morphTargets, fps, noLoop ) { 44068 44069 const animationToMorphTargets = {}; 44070 44071 // tested with https://regex101.com/ on trick sequences 44072 // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 44073 const pattern = /^([\w-]*?)([\d]+)$/; 44074 44075 // sort morph target names into animation groups based 44076 // patterns like Walk_001, Walk_002, Run_001, Run_002 44077 for ( let i = 0, il = morphTargets.length; i < il; i ++ ) { 44078 44079 const morphTarget = morphTargets[ i ]; 44080 const parts = morphTarget.name.match( pattern ); 44081 44082 if ( parts && parts.length > 1 ) { 44083 44084 const name = parts[ 1 ]; 44085 44086 let animationMorphTargets = animationToMorphTargets[ name ]; 44087 44088 if ( ! animationMorphTargets ) { 44089 44090 animationToMorphTargets[ name ] = animationMorphTargets = []; 44091 44092 } 44093 44094 animationMorphTargets.push( morphTarget ); 44095 44096 } 44097 44098 } 44099 44100 const clips = []; 44101 44102 for ( const name in animationToMorphTargets ) { 44103 44104 clips.push( this.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); 44105 44106 } 44107 44108 return clips; 44109 44110 } 44111 44112 /** 44113 * Sets the duration of this clip to the duration of its longest keyframe track. 44114 * 44115 * @return {AnimationClip} A reference to this animation clip. 44116 */ 44117 resetDuration() { 44118 44119 const tracks = this.tracks; 44120 let duration = 0; 44121 44122 for ( let i = 0, n = tracks.length; i !== n; ++ i ) { 44123 44124 const track = this.tracks[ i ]; 44125 44126 duration = Math.max( duration, track.times[ track.times.length - 1 ] ); 44127 44128 } 44129 44130 this.duration = duration; 44131 44132 return this; 44133 44134 } 44135 44136 /** 44137 * Trims all tracks to the clip's duration. 44138 * 44139 * @return {AnimationClip} A reference to this animation clip. 44140 */ 44141 trim() { 44142 44143 for ( let i = 0; i < this.tracks.length; i ++ ) { 44144 44145 this.tracks[ i ].trim( 0, this.duration ); 44146 44147 } 44148 44149 return this; 44150 44151 } 44152 44153 /** 44154 * Performs minimal validation on each track in the clip. Returns `true` if all 44155 * tracks are valid. 44156 * 44157 * @return {boolean} Whether the clip's keyframes are valid or not. 44158 */ 44159 validate() { 44160 44161 let valid = true; 44162 44163 for ( let i = 0; i < this.tracks.length; i ++ ) { 44164 44165 valid = valid && this.tracks[ i ].validate(); 44166 44167 } 44168 44169 return valid; 44170 44171 } 44172 44173 /** 44174 * Optimizes each track by removing equivalent sequential keys (which are 44175 * common in morph target sequences). 44176 * 44177 * @return {AnimationClip} A reference to this animation clip. 44178 */ 44179 optimize() { 44180 44181 for ( let i = 0; i < this.tracks.length; i ++ ) { 44182 44183 this.tracks[ i ].optimize(); 44184 44185 } 44186 44187 return this; 44188 44189 } 44190 44191 /** 44192 * Returns a new animation clip with copied values from this instance. 44193 * 44194 * @return {AnimationClip} A clone of this instance. 44195 */ 44196 clone() { 44197 44198 const tracks = []; 44199 44200 for ( let i = 0; i < this.tracks.length; i ++ ) { 44201 44202 tracks.push( this.tracks[ i ].clone() ); 44203 44204 } 44205 44206 const clip = new this.constructor( this.name, this.duration, tracks, this.blendMode ); 44207 44208 clip.userData = JSON.parse( JSON.stringify( this.userData ) ); 44209 44210 return clip; 44211 44212 } 44213 44214 /** 44215 * Serializes this animation clip into JSON. 44216 * 44217 * @return {Object} The JSON object. 44218 */ 44219 toJSON() { 44220 44221 return this.constructor.toJSON( this ); 44222 44223 } 44224 44225} 44226 44227function getTrackTypeForValueTypeName( typeName ) { 44228 44229 switch ( typeName.toLowerCase() ) { 44230 44231 case 'scalar': 44232 case 'double': 44233 case 'float': 44234 case 'number': 44235 case 'integer': 44236 44237 return NumberKeyframeTrack; 44238 44239 case 'vector': 44240 case 'vector2': 44241 case 'vector3': 44242 case 'vector4': 44243 44244 return VectorKeyframeTrack; 44245 44246 case 'color': 44247 44248 return ColorKeyframeTrack; 44249 44250 case 'quaternion': 44251 44252 return QuaternionKeyframeTrack; 44253 44254 case 'bool': 44255 case 'boolean': 44256 44257 return BooleanKeyframeTrack; 44258 44259 case 'string': 44260 44261 return StringKeyframeTrack; 44262 44263 } 44264 44265 throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName ); 44266 44267} 44268 44269function parseKeyframeTrack( json ) { 44270 44271 if ( json.type === undefined ) { 44272 44273 throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' ); 44274 44275 } 44276 44277 const trackType = getTrackTypeForValueTypeName( json.type ); 44278 44279 if ( json.times === undefined ) { 44280 44281 const times = [], values = []; 44282 44283 flattenJSON( json.keys, times, values, 'value' ); 44284 44285 json.times = times; 44286 json.values = values; 44287 44288 } 44289 44290 let track; 44291 44292 // derived classes can define a static parse method 44293 if ( trackType.parse !== undefined ) { 44294 44295 track = trackType.parse( json ); 44296 44297 } else { 44298 44299 // by default, we assume a constructor compatible with the base 44300 track = new trackType( json.name, json.times, json.values, json.interpolation ); 44301 44302 } 44303 44304 if ( hasTangents( json.settings ) ) { 44305 44306 track.settings = { 44307 inTangents: convertArray( json.settings.inTangents, Float32Array ), 44308 outTangents: convertArray( json.settings.outTangents, Float32Array ) 44309 }; 44310 44311 } 44312 44313 return track; 44314 44315} 44316 44317/** 44318 * @class 44319 * @classdesc A simple caching system, used internally by {@link FileLoader}. 44320 * To enable caching across all loaders that use {@link FileLoader}, add `THREE.Cache.enabled = true.` once in your app. 44321 * @hideconstructor 44322 */ 44323const Cache = { 44324 44325 /** 44326 * Whether caching is enabled or not. 44327 * 44328 * @static 44329 * @type {boolean} 44330 * @default false 44331 */ 44332 enabled: false, 44333 44334 /** 44335 * A dictionary that holds cached files. 44336 * 44337 * @static 44338 * @type {Object<string,Object>} 44339 */ 44340 files: {}, 44341 44342 /** 44343 * Adds a cache entry with a key to reference the file. If this key already 44344 * holds a file, it is overwritten. 44345 * 44346 * @static 44347 * @param {string} key - The key to reference the cached file. 44348 * @param {Object} file - The file to be cached. 44349 */ 44350 add: function ( key, file ) { 44351 44352 if ( this.enabled === false ) return; 44353 44354 if ( isBlobURL( key ) ) return; 44355 44356 // log( 'Cache', 'Adding key:', key ); 44357 44358 this.files[ key ] = file; 44359 44360 }, 44361 44362 /** 44363 * Gets the cached value for the given key. 44364 * 44365 * @static 44366 * @param {string} key - The key to reference the cached file. 44367 * @return {Object|undefined} The cached file. If the key does not exist `undefined` is returned. 44368 */ 44369 get: function ( key ) { 44370 44371 if ( this.enabled === false ) return; 44372 44373 if ( isBlobURL( key ) ) return; 44374 44375 // log( 'Cache', 'Checking key:', key ); 44376 44377 return this.files[ key ]; 44378 44379 }, 44380 44381 /** 44382 * Removes the cached file associated with the given key. 44383 * 44384 * @static 44385 * @param {string} key - The key to reference the cached file. 44386 */ 44387 remove: function ( key ) { 44388 44389 delete this.files[ key ]; 44390 44391 }, 44392 44393 /** 44394 * Remove all values from the cache. 44395 * 44396 * @static 44397 */ 44398 clear: function () { 44399 44400 this.files = {}; 44401 44402 } 44403 44404}; 44405 44406/** 44407 * Returns true if the given cache key contains the blob: scheme. 44408 * 44409 * @private 44410 * @param {string} key - The cache key. 44411 * @return {boolean} Whether the given cache key contains the blob: scheme or not. 44412 */ 44413function isBlobURL( key ) { 44414 44415 try { 44416 44417 const urlString = key.slice( key.indexOf( ':' ) + 1 ); // remove type identifier 44418 44419 const url = new URL( urlString ); 44420 return url.protocol === 'blob:'; 44421 44422 } catch ( e ) { 44423 44424 // If the string is not a valid URL, it throws an error 44425 return false; 44426 44427 } 44428 44429} 44430 44431/** 44432 * Handles and keeps track of loaded and pending data. A default global 44433 * instance of this class is created and used by loaders if not supplied 44434 * manually. 44435 * 44436 * In general that should be sufficient, however there are times when it can 44437 * be useful to have separate loaders - for example if you want to show 44438 * separate loading bars for objects and textures. 44439 * 44440 * ```js 44441 * const manager = new THREE.LoadingManager(); 44442 * manager.onLoad = () => console.log( 'Loading complete!' ); 44443 * 44444 * const loader1 = new OBJLoader( manager ); 44445 * const loader2 = new ColladaLoader( manager ); 44446 * ``` 44447 */ 44448class LoadingManager { 44449 44450 /** 44451 * Constructs a new loading manager. 44452 * 44453 * @param {Function} [onLoad] - Executes when all items have been loaded. 44454 * @param {Function} [onProgress] - Executes when single items have been loaded. 44455 * @param {Function} [onError] - Executes when an error occurs. 44456 */ 44457 constructor( onLoad, onProgress, onError ) { 44458 44459 const scope = this; 44460 44461 let isLoading = false; 44462 let itemsLoaded = 0; 44463 let itemsTotal = 0; 44464 let urlModifier = undefined; 44465 const handlers = []; 44466 44467 // Refer to #5689 for the reason why we don't set .onStart 44468 // in the constructor 44469 44470 /** 44471 * Executes when an item starts loading. 44472 * 44473 * @type {Function|undefined} 44474 * @default undefined 44475 */ 44476 this.onStart = undefined; 44477 44478 /** 44479 * Executes when all items have been loaded. 44480 * 44481 * @type {Function|undefined} 44482 * @default undefined 44483 */ 44484 this.onLoad = onLoad; 44485 44486 /** 44487 * Executes when single items have been loaded. 44488 * 44489 * @type {Function|undefined} 44490 * @default undefined 44491 */ 44492 this.onProgress = onProgress; 44493 44494 /** 44495 * Executes when an error occurs. 44496 * 44497 * @type {Function|undefined} 44498 * @default undefined 44499 */ 44500 this.onError = onError; 44501 44502 /** 44503 * Used for aborting ongoing requests in loaders using this manager. 44504 * 44505 * @private 44506 * @type {AbortController | null} 44507 */ 44508 this._abortController = null; 44509 44510 /** 44511 * This should be called by any loader using the manager when the loader 44512 * starts loading an item. 44513 * 44514 * @param {string} url - The URL to load. 44515 */ 44516 this.itemStart = function ( url ) { 44517 44518 itemsTotal ++; 44519 44520 if ( isLoading === false ) { 44521 44522 if ( scope.onStart !== undefined ) { 44523 44524 scope.onStart( url, itemsLoaded, itemsTotal ); 44525 44526 } 44527 44528 } 44529 44530 isLoading = true; 44531 44532 }; 44533 44534 /** 44535 * This should be called by any loader using the manager when the loader 44536 * ended loading an item. 44537 * 44538 * @param {string} url - The URL of the loaded item. 44539 */ 44540 this.itemEnd = function ( url ) { 44541 44542 itemsLoaded ++; 44543 44544 if ( scope.onProgress !== undefined ) { 44545 44546 scope.onProgress( url, itemsLoaded, itemsTotal ); 44547 44548 } 44549 44550 if ( itemsLoaded === itemsTotal ) { 44551 44552 isLoading = false; 44553 44554 if ( scope.onLoad !== undefined ) { 44555 44556 scope.onLoad(); 44557 44558 } 44559 44560 } 44561 44562 }; 44563 44564 /** 44565 * This should be called by any loader using the manager when the loader 44566 * encounters an error when loading an item. 44567 * 44568 * @param {string} url - The URL of the item that produces an error. 44569 */ 44570 this.itemError = function ( url ) { 44571 44572 if ( scope.onError !== undefined ) { 44573 44574 scope.onError( url ); 44575 44576 } 44577 44578 }; 44579 44580 /** 44581 * Given a URL, uses the URL modifier callback (if any) and returns a 44582 * resolved URL. If no URL modifier is set, returns the original URL. 44583 * 44584 * @param {string} url - The URL to load. 44585 * @return {string} The resolved URL. 44586 */ 44587 this.resolveURL = function ( url ) { 44588 44589 // Normalize to NFC so that Unicode URIs (e.g. from glTF) 44590 // are percent-encoded correctly per RFC 3987. 44591 44592 url = url.normalize( 'NFC' ); 44593 44594 if ( urlModifier ) { 44595 44596 return urlModifier( url ); 44597 44598 } 44599 44600 return url; 44601 44602 }; 44603 44604 /** 44605 * If provided, the callback will be passed each resource URL before a 44606 * request is sent. The callback may return the original URL, or a new URL to 44607 * override loading behavior. This behavior can be used to load assets from 44608 * .ZIP files, drag-and-drop APIs, and Data URIs. 44609 * 44610 * ```js 44611 * const blobs = {'fish.gltf': blob1, 'diffuse.png': blob2, 'normal.png': blob3}; 44612 * 44613 * const manager = new THREE.LoadingManager(); 44614 * 44615 * // Initialize loading manager with URL callback. 44616 * const objectURLs = []; 44617 * manager.setURLModifier( ( url ) => { 44618 * 44619 * url = URL.createObjectURL( blobs[ url ] ); 44620 * objectURLs.push( url ); 44621 * return url; 44622 * 44623 * } ); 44624 * 44625 * // Load as usual, then revoke the blob URLs. 44626 * const loader = new GLTFLoader( manager ); 44627 * loader.load( 'fish.gltf', (gltf) => { 44628 * 44629 * scene.add( gltf.scene ); 44630 * objectURLs.forEach( ( url ) => URL.revokeObjectURL( url ) ); 44631 * 44632 * } ); 44633 * ``` 44634 * 44635 * @param {function(string):string} transform - URL modifier callback. Called with an URL and must return a resolved URL. 44636 * @return {LoadingManager} A reference to this loading manager. 44637 */ 44638 this.setURLModifier = function ( transform ) { 44639 44640 urlModifier = transform; 44641 44642 return this; 44643 44644 }; 44645 44646 /** 44647 * Registers a loader with the given regular expression. Can be used to 44648 * define what loader should be used in order to load specific files. A 44649 * typical use case is to overwrite the default loader for textures. 44650 * 44651 * ```js 44652 * // add handler for TGA textures 44653 * manager.addHandler( /\.tga$/i, new TGALoader() ); 44654 * ``` 44655 * 44656 * @param {string} regex - A regular expression. 44657 * @param {Loader} loader - A loader that should handle matched cases. 44658 * @return {LoadingManager} A reference to this loading manager. 44659 */ 44660 this.addHandler = function ( regex, loader ) { 44661 44662 handlers.push( regex, loader ); 44663 44664 return this; 44665 44666 }; 44667 44668 /** 44669 * Removes the loader for the given regular expression. 44670 * 44671 * @param {string} regex - A regular expression. 44672 * @return {LoadingManager} A reference to this loading manager. 44673 */ 44674 this.removeHandler = function ( regex ) { 44675 44676 const index = handlers.indexOf( regex ); 44677 44678 if ( index !== -1 ) { 44679 44680 handlers.splice( index, 2 ); 44681 44682 } 44683 44684 return this; 44685 44686 }; 44687 44688 /** 44689 * Can be used to retrieve the registered loader for the given file path. 44690 * 44691 * @param {string} file - The file path. 44692 * @return {?Loader} The registered loader. Returns `null` if no loader was found. 44693 */ 44694 this.getHandler = function ( file ) { 44695 44696 for ( let i = 0, l = handlers.length; i < l; i += 2 ) { 44697 44698 const regex = handlers[ i ]; 44699 const loader = handlers[ i + 1 ]; 44700 44701 if ( regex.global ) regex.lastIndex = 0; // see #17920 44702 44703 if ( regex.test( file ) ) { 44704 44705 return loader; 44706 44707 } 44708 44709 } 44710 44711 return null; 44712 44713 }; 44714 44715 /** 44716 * Can be used to abort ongoing loading requests in loaders using this manager. 44717 * The abort only works if the loaders implement {@link Loader#abort} and `AbortSignal.any()` 44718 * is supported in the browser. 44719 * 44720 * @return {LoadingManager} A reference to this loading manager. 44721 */ 44722 this.abort = function () { 44723 44724 44725 this.abortController.abort(); 44726 this._abortController = null; 44727 44728 return this; 44729 44730 }; 44731 44732 } 44733 44734 // TODO: Revert this back to a single member variable once this issue has been fixed 44735 // https://github.com/cloudflare/workerd/issues/3657 44736 44737 /** 44738 * Used for aborting ongoing requests in loaders using this manager. 44739 * 44740 * @type {AbortController} 44741 */ 44742 get abortController() { 44743 44744 if ( ! this._abortController ) { 44745 44746 this._abortController = new AbortController(); 44747 44748 } 44749 44750 return this._abortController; 44751 44752 } 44753 44754} 44755 44756/** 44757 * The global default loading manager. 44758 * 44759 * @constant 44760 * @type {LoadingManager} 44761 */ 44762const DefaultLoadingManager = /*@__PURE__*/ new LoadingManager(); 44763 44764/** 44765 * Abstract base class for loaders. 44766 * 44767 * @abstract 44768 */ 44769class Loader { 44770 44771 /** 44772 * Constructs a new loader. 44773 * 44774 * @param {LoadingManager} [manager] - The loading manager. 44775 */ 44776 constructor( manager ) { 44777 44778 /** 44779 * The loading manager. 44780 * 44781 * @type {LoadingManager} 44782 * @default DefaultLoadingManager 44783 */ 44784 this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; 44785 44786 /** 44787 * The crossOrigin string to implement CORS for loading the url from a 44788 * different domain that allows CORS. 44789 * 44790 * @type {string} 44791 * @default 'anonymous' 44792 */ 44793 this.crossOrigin = 'anonymous'; 44794 44795 /** 44796 * Whether the XMLHttpRequest uses credentials. 44797 * 44798 * @type {boolean} 44799 * @default false 44800 */ 44801 this.withCredentials = false; 44802 44803 /** 44804 * The base path from which the asset will be loaded. 44805 * 44806 * @type {string} 44807 */ 44808 this.path = ''; 44809 44810 /** 44811 * The base path from which additional resources like textures will be loaded. 44812 * 44813 * @type {string} 44814 */ 44815 this.resourcePath = ''; 44816 44817 /** 44818 * The [request header](https://developer.mozilla.org/en-US/docs/Glossary/Request_header) 44819 * used in HTTP request. 44820 * 44821 * @type {Object<string, any>} 44822 */ 44823 this.requestHeader = {}; 44824 44825 if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { 44826 44827 __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); 44828 44829 } 44830 44831 } 44832 44833 /** 44834 * This method needs to be implemented by all concrete loaders. It holds the 44835 * logic for loading assets from the backend. 44836 * 44837 * @abstract 44838 * @param {string} url - The path/URL of the file to be loaded. 44839 * @param {Function} onLoad - Executed when the loading process has been finished. 44840 * @param {onProgressCallback} [onProgress] - Executed while the loading is in progress. 44841 * @param {onErrorCallback} [onError] - Executed when errors occur. 44842 */ 44843 load( /* url, onLoad, onProgress, onError */ ) {} 44844 44845 /** 44846 * A async version of {@link Loader#load}. 44847 * 44848 * @param {string} url - The path/URL of the file to be loaded. 44849 * @param {onProgressCallback} [onProgress] - Executed while the loading is in progress. 44850 * @return {Promise} A Promise that resolves when the asset has been loaded. 44851 */ 44852 loadAsync( url, onProgress ) { 44853 44854 const scope = this; 44855 44856 return new Promise( function ( resolve, reject ) { 44857 44858 scope.load( url, resolve, onProgress, reject ); 44859 44860 } ); 44861 44862 } 44863 44864 /** 44865 * This method needs to be implemented by all concrete loaders. It holds the 44866 * logic for parsing the asset into three.js entities. 44867 * 44868 * @abstract 44869 * @param {any} data - The data to parse. 44870 */ 44871 parse( /* data */ ) {} 44872 44873 /** 44874 * Sets the `crossOrigin` String to implement CORS for loading the URL 44875 * from a different domain that allows CORS. 44876 * 44877 * @param {string} crossOrigin - The `crossOrigin` value. 44878 * @return {Loader} A reference to this instance. 44879 */ 44880 setCrossOrigin( crossOrigin ) { 44881 44882 this.crossOrigin = crossOrigin; 44883 return this; 44884 44885 } 44886 44887 /** 44888 * Whether the XMLHttpRequest uses credentials such as cookies, authorization 44889 * headers or TLS client certificates, see [XMLHttpRequest.withCredentials](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials). 44890 * 44891 * Note: This setting has no effect if you are loading files locally or from the same domain. 44892 * 44893 * @param {boolean} value - The `withCredentials` value. 44894 * @return {Loader} A reference to this instance. 44895 */ 44896 setWithCredentials( value ) { 44897 44898 this.withCredentials = value; 44899 return this; 44900 44901 } 44902 44903 /** 44904 * Sets the base path for the asset. 44905 * 44906 * @param {string} path - The base path. 44907 * @return {Loader} A reference to this instance. 44908 */ 44909 setPath( path ) { 44910 44911 this.path = path; 44912 return this; 44913 44914 } 44915 44916 /** 44917 * Sets the base path for dependent resources like textures. 44918 * 44919 * @param {string} resourcePath - The resource path. 44920 * @return {Loader} A reference to this instance. 44921 */ 44922 setResourcePath( resourcePath ) { 44923 44924 this.resourcePath = resourcePath; 44925 return this; 44926 44927 } 44928 44929 /** 44930 * Sets the given request header. 44931 * 44932 * @param {Object} requestHeader - A [request header](https://developer.mozilla.org/en-US/docs/Glossary/Request_header) 44933 * for configuring the HTTP request. 44934 * @return {Loader} A reference to this instance. 44935 */ 44936 setRequestHeader( requestHeader ) { 44937 44938 this.requestHeader = requestHeader; 44939 return this; 44940 44941 } 44942 44943 /** 44944 * This method can be implemented in loaders for aborting ongoing requests. 44945 * 44946 * @abstract 44947 * @return {Loader} A reference to this instance. 44948 */ 44949 abort() { 44950 44951 return this; 44952 44953 } 44954 44955} 44956 44957/** 44958 * Callback for onProgress in loaders. 44959 * 44960 * @callback onProgressCallback 44961 * @param {ProgressEvent} event - An instance of `ProgressEvent` that represents the current loading status. 44962 */ 44963 44964/** 44965 * Callback for onError in loaders. 44966 * 44967 * @callback onErrorCallback 44968 * @param {Error} error - The error which occurred during the loading process. 44969 */ 44970 44971/** 44972 * The default material name that is used by loaders 44973 * when creating materials for loaded 3D objects. 44974 * 44975 * Note: Not all loaders might honor this setting. 44976 * 44977 * @static 44978 * @type {string} 44979 * @default '__DEFAULT' 44980 */ 44981Loader.DEFAULT_MATERIAL_NAME = '__DEFAULT'; 44982 44983const loading = {}; 44984 44985class HttpError extends Error { 44986 44987 constructor( message, response ) { 44988 44989 super( message ); 44990 this.response = response; 44991 44992 } 44993 44994} 44995 44996/** 44997 * A low level class for loading resources with the Fetch API, used internally by 44998 * most loaders. It can also be used directly to load any file type that does 44999 * not have a loader. 45000 * 45001 * This loader supports caching. If you want to use it, add `THREE.Cache.enabled = true;` 45002 * once to your application. 45003 * 45004 * ```js 45005 * const loader = new THREE.FileLoader(); 45006 * const data = await loader.loadAsync( 'example.txt' ); 45007 * ``` 45008 * 45009 * @augments Loader 45010 */ 45011class FileLoader extends Loader { 45012 45013 /** 45014 * Constructs a new file loader. 45015 * 45016 * @param {LoadingManager} [manager] - The loading manager. 45017 */ 45018 constructor( manager ) { 45019 45020 super( manager ); 45021 45022 /** 45023 * The expected mime type. Valid values can be found 45024 * [here](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString#mimetype) 45025 * 45026 * @type {string} 45027 */ 45028 this.mimeType = ''; 45029 45030 /** 45031 * The expected response type. 45032 * 45033 * @type {('arraybuffer'|'blob'|'document'|'json'|'')} 45034 * @default '' 45035 */ 45036 this.responseType = ''; 45037 45038 /** 45039 * Used for aborting requests. 45040 * 45041 * @private 45042 * @type {AbortController} 45043 */ 45044 this._abortController = new AbortController(); 45045 45046 } 45047 45048 /** 45049 * Starts loading from the given URL and pass the loaded response to the `onLoad()` callback. 45050 * 45051 * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI. 45052 * @param {function(any)} onLoad - Executed when the loading process has been finished. 45053 * @param {onProgressCallback} [onProgress] - Executed while the loading is in progress. 45054 * @param {onErrorCallback} [onError] - Executed when errors occur. 45055 */ 45056 load( url, onLoad, onProgress, onError ) { 45057 45058 if ( url === undefined ) url = ''; 45059 45060 if ( this.path !== undefined ) url = this.path + url; 45061 45062 url = this.manager.resolveURL( url ); 45063 45064 const cached = Cache.get( `file:${url}` ); 45065 45066 if ( cached !== undefined ) { 45067 45068 this.manager.itemStart( url ); 45069 45070 setTimeout( () => { 45071 45072 if ( onLoad ) onLoad( cached ); 45073 45074 this.manager.itemEnd( url ); 45075 45076 }, 0 ); 45077 45078 return; 45079 45080 } 45081 45082 // Check if request is duplicate 45083 45084 if ( loading[ url ] !== undefined ) { 45085 45086 loading[ url ].push( { 45087 45088 onLoad: onLoad, 45089 onProgress: onProgress, 45090 onError: onError 45091 45092 } ); 45093 45094 return; 45095 45096 } 45097 45098 // Initialise array for duplicate requests 45099 loading[ url ] = []; 45100 45101 loading[ url ].push( { 45102 onLoad: onLoad, 45103 onProgress: onProgress, 45104 onError: onError, 45105 } ); 45106 45107 // create request 45108 const req = new Request( url, { 45109 headers: new Headers( this.requestHeader ), 45110 credentials: this.withCredentials ? 'include' : 'same-origin', 45111 signal: ( typeof AbortSignal.any === 'function' ) ? AbortSignal.any( [ this._abortController.signal, this.manager.abortController.signal ] ) : this._abortController.signal 45112 } ); 45113 45114 // record states ( avoid data race ) 45115 const mimeType = this.mimeType; 45116 const responseType = this.responseType; 45117 45118 // start the fetch 45119 fetch( req ) 45120 .then( response => { 45121 45122 if ( response.status === 200 || response.status === 0 ) { 45123 45124 // Some browsers return HTTP Status 0 when using non-http protocol 45125 // e.g. 'file://' or 'data://'. Handle as success. 45126 45127 if ( response.status === 0 ) { 45128 45129 warn( 'FileLoader: HTTP Status 0 received.' ); 45130 45131 } 45132 45133 // Workaround: Checking if response.body === undefined for Alipay browser #23548 45134 45135 if ( typeof ReadableStream === 'undefined' || response.body === undefined || response.body.getReader === undefined ) { 45136 45137 return response; 45138 45139 } 45140 45141 const callbacks = loading[ url ]; 45142 const reader = response.body.getReader(); 45143 45144 // Nginx needs X-File-Size check 45145 // https://serverfault.com/questions/482875/why-does-nginx-remove-content-length-header-for-chunked-content 45146 const contentLength = response.headers.get( 'X-File-Size' ) || response.headers.get( 'Content-Length' ); 45147 const total = contentLength ? parseInt( contentLength ) : 0; 45148 const lengthComputable = total !== 0; 45149 let loaded = 0; 45150 45151 // periodically read data into the new stream tracking while download progress 45152 const stream = new ReadableStream( { 45153 start( controller ) { 45154 45155 readData(); 45156 45157 function readData() { 45158 45159 reader.read().then( ( { done, value } ) => { 45160 45161 if ( done ) { 45162 45163 controller.close(); 45164 45165 } else { 45166 45167 loaded += value.byteLength; 45168 45169 const event = new ProgressEvent( 'progress', { lengthComputable, loaded, total } ); 45170 for ( let i = 0, il = callbacks.length; i < il; i ++ ) { 45171 45172 const callback = callbacks[ i ]; 45173 if ( callback.onProgress ) callback.onProgress( event ); 45174 45175 } 45176 45177 controller.enqueue( value ); 45178 readData(); 45179 45180 } 45181 45182 }, ( e ) => { 45183 45184 controller.error( e ); 45185 45186 } ); 45187 45188 } 45189 45190 } 45191 45192 } ); 45193 45194 return new Response( stream ); 45195 45196 } else { 45197 45198 throw new HttpError( `fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`, response ); 45199 45200 } 45201 45202 } ) 45203 .then( response => { 45204 45205 switch ( responseType ) { 45206 45207 case 'arraybuffer': 45208 45209 return response.arrayBuffer(); 45210 45211 case 'blob': 45212 45213 return response.blob(); 45214 45215 case 'document': 45216 45217 return response.text() 45218 .then( text => { 45219 45220 const parser = new DOMParser(); 45221 return parser.parseFromString( text, mimeType ); 45222 45223 } ); 45224 45225 case 'json': 45226 45227 return response.json(); 45228 45229 default: 45230 45231 if ( mimeType === '' ) { 45232 45233 return response.text(); 45234 45235 } else { 45236 45237 // sniff encoding 45238 const re = /charset="?([^;"\s]*)"?/i; 45239 const exec = re.exec( mimeType ); 45240 const label = exec && exec[ 1 ] ? exec[ 1 ].toLowerCase() : undefined; 45241 const decoder = new TextDecoder( label ); 45242 return response.arrayBuffer().then( ab => decoder.decode( ab ) ); 45243 45244 } 45245 45246 } 45247 45248 } ) 45249 .then( data => { 45250 45251 // Add to cache only on HTTP success, so that we do not cache 45252 // error response bodies as proper responses to requests. 45253 Cache.add( `file:${url}`, data ); 45254 45255 const callbacks = loading[ url ]; 45256 delete loading[ url ]; 45257 45258 for ( let i = 0, il = callbacks.length; i < il; i ++ ) { 45259 45260 const callback = callbacks[ i ]; 45261 if ( callback.onLoad ) callback.onLoad( data ); 45262 45263 } 45264 45265 } ) 45266 .catch( err => { 45267 45268 // Abort errors and other errors are handled the same 45269 45270 const callbacks = loading[ url ]; 45271 45272 if ( callbacks === undefined ) { 45273 45274 // When onLoad was called and url was deleted in `loading` 45275 this.manager.itemError( url ); 45276 throw err; 45277 45278 } 45279 45280 delete loading[ url ]; 45281 45282 for ( let i = 0, il = callbacks.length; i < il; i ++ ) { 45283 45284 const callback = callbacks[ i ]; 45285 if ( callback.onError ) callback.onError( err ); 45286 45287 } 45288 45289 this.manager.itemError( url ); 45290 45291 } ) 45292 .finally( () => { 45293 45294 this.manager.itemEnd( url ); 45295 45296 } ); 45297 45298 this.manager.itemStart( url ); 45299 45300 } 45301 45302 /** 45303 * Sets the expected response type. 45304 * 45305 * @param {('arraybuffer'|'blob'|'document'|'json'|'')} value - The response type. 45306 * @return {FileLoader} A reference to this file loader. 45307 */ 45308 setResponseType( value ) { 45309 45310 this.responseType = value; 45311 return this; 45312 45313 } 45314 45315 /** 45316 * Sets the expected mime type of the loaded file. 45317 * 45318 * @param {string} value - The mime type. 45319 * @return {FileLoader} A reference to this file loader. 45320 */ 45321 setMimeType( value ) { 45322 45323 this.mimeType = value; 45324 return this; 45325 45326 } 45327 45328 /** 45329 * Aborts ongoing fetch requests. 45330 * 45331 * @return {FileLoader} A reference to this instance. 45332 */ 45333 abort() { 45334 45335 this._abortController.abort(); 45336 this._abortController = new AbortController(); 45337 45338 return this; 45339 45340 } 45341 45342} 45343 45344/** 45345 * Class for loading animation clips in the JSON format. The files are internally 45346 * loaded via {@link FileLoader}. 45347 * 45348 * ```js 45349 * const loader = new THREE.AnimationLoader(); 45350 * const animations = await loader.loadAsync( 'animations/animation.js' ); 45351 * ``` 45352 * 45353 * @augments Loader 45354 */ 45355class AnimationLoader extends Loader { 45356 45357 /** 45358 * Constructs a new animation loader. 45359 * 45360 * @param {LoadingManager} [manager] - The loading manager. 45361 */ 45362 constructor( manager ) { 45363 45364 super( manager ); 45365 45366 } 45367 45368 /** 45369 * Starts loading from the given URL and pass the loaded animations as an array 45370 * holding instances of {@link AnimationClip} to the `onLoad()` callback. 45371 * 45372 * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI. 45373 * @param {function(Array<AnimationClip>)} onLoad - Executed when the loading process has been finished. 45374 * @param {onProgressCallback} onProgress - Executed while the loading is in progress. 45375 * @param {onErrorCallback} onError - Executed when errors occur. 45376 */ 45377 load( url, onLoad, onProgress, onError ) { 45378 45379 const scope = this; 45380 45381 const loader = new FileLoader( this.manager ); 45382 loader.setPath( this.path ); 45383 loader.setRequestHeader( this.requestHeader ); 45384 loader.setWithCredentials( this.withCredentials ); 45385 loader.load( url, function ( text ) { 45386 45387 try { 45388 45389 onLoad( scope.parse( JSON.parse( text ) ) ); 45390 45391 } catch ( e ) { 45392 45393 if ( onError ) { 45394 45395 onError( e ); 45396 45397 } else { 45398 45399 error( e ); 45400 45401 } 45402 45403 scope.manager.itemError( url ); 45404 45405 } 45406 45407 }, onProgress, onError ); 45408 45409 } 45410 45411 /** 45412 * Parses the given JSON object and returns an array of animation clips. 45413 * 45414 * @param {Object} json - The serialized animation clips. 45415 * @return {Array<AnimationClip>} The parsed animation clips. 45416 */ 45417 parse( json ) { 45418 45419 const animations = []; 45420 45421 for ( let i = 0; i < json.length; i ++ ) { 45422 45423 const clip = AnimationClip.parse( json[ i ] ); 45424 45425 animations.push( clip ); 45426 45427 } 45428 45429 return animations; 45430 45431 } 45432 45433} 45434 45435/** 45436 * Abstract base class for loading compressed texture formats S3TC, ASTC or ETC. 45437 * Textures are internally loaded via {@link FileLoader}. 45438 * 45439 * Derived classes have to implement the `parse()` method which holds the parsing 45440 * for the respective format. 45441 * 45442 * @abstract 45443 * @augments Loader 45444 */ 45445class CompressedTextureLoader extends Loader { 45446 45447 /** 45448 * Constructs a new compressed texture loader. 45449 * 45450 * @param {LoadingManager} [manager] - The loading manager. 45451 */ 45452 constructor( manager ) { 45453 45454 super( manager ); 45455 45456 } 45457 45458 /** 45459 * Starts loading from the given URL and passes the loaded compressed texture 45460 * to the `onLoad()` callback. The method also returns a new texture object which can 45461 * directly be used for material creation. If you do it this way, the texture 45462 * may pop up in your scene once the respective loading process is finished. 45463 * 45464 * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI. 45465 * @param {function(CompressedTexture)} onLoad - Executed when the loading process has been finished. 45466 * @param {onProgressCallback} onProgress - Executed while the loading is in progress. 45467 * @param {onErrorCallback} onError - Executed when errors occur. 45468 * @return {CompressedTexture} The compressed texture. 45469 */ 45470 load( url, onLoad, onProgress, onError ) { 45471 45472 const scope = this; 45473 45474 const images = []; 45475 45476 const texture = new CompressedTexture(); 45477 45478 const loader = new FileLoader( this.manager ); 45479 loader.setPath( this.path ); 45480 loader.setResponseType( 'arraybuffer' ); 45481 loader.setRequestHeader( this.requestHeader ); 45482 loader.setWithCredentials( scope.withCredentials ); 45483 45484 let loaded = 0; 45485 45486 function loadTexture( i ) { 45487 45488 loader.load( url[ i ], function ( buffer ) { 45489 45490 const texDatas = scope.parse( buffer, true ); 45491 45492 images[ i ] = { 45493 width: texDatas.width, 45494 height: texDatas.height, 45495 format: texDatas.format, 45496 mipmaps: texDatas.mipmaps 45497 }; 45498 45499 loaded += 1; 45500 45501 if ( loaded === 6 ) { 45502 45503 if ( texDatas.mipmapCount === 1 ) texture.minFilter = LinearFilter; 45504 45505 texture.image = images; 45506 texture.format = texDatas.format; 45507 texture.needsUpdate = true; 45508 45509 if ( onLoad ) onLoad( texture ); 45510 45511 } 45512 45513 }, onProgress, onError ); 45514 45515 } 45516 45517 if ( Array.isArray( url ) ) { 45518 45519 for ( let i = 0, il = url.length; i < il; ++ i ) { 45520 45521 loadTexture( i ); 45522 45523 } 45524 45525 } else { 45526 45527 // compressed cubemap texture stored in a single DDS file 45528 45529 loader.load( url, function ( buffer ) { 45530 45531 const texDatas = scope.parse( buffer, true ); 45532 45533 if ( texDatas.isCubemap ) { 45534 45535 const faces = texDatas.mipmaps.length / texDatas.mipmapCount; 45536 45537 for ( let f = 0; f < faces; f ++ ) { 45538 45539 images[ f ] = { mipmaps: [] }; 45540 45541 for ( let i = 0; i < texDatas.mipmapCount; i ++ ) { 45542 45543 images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); 45544 images[ f ].format = texDatas.format; 45545 images[ f ].width = texDatas.width; 45546 images[ f ].height = texDatas.height; 45547 45548 } 45549 45550 } 45551 45552 texture.image = images; 45553 45554 } else { 45555 45556 texture.image.width = texDatas.width; 45557 texture.image.height = texDatas.height; 45558 texture.mipmaps = texDatas.mipmaps; 45559 45560 } 45561 45562 if ( texDatas.mipmapCount === 1 ) { 45563 45564 texture.minFilter = LinearFilter; 45565 45566 } 45567 45568 texture.format = texDatas.format; 45569 texture.needsUpdate = true; 45570 45571 if ( onLoad ) onLoad( texture ); 45572 45573 }, onProgress, onError ); 45574 45575 } 45576 45577 return texture; 45578 45579 } 45580 45581} 45582 45583const _loading = new WeakMap(); 45584 45585/** 45586 * A loader for loading images. The class loads images with the HTML `Image` API. 45587 * 45588 * ```js 45589 * const loader = new THREE.ImageLoader(); 45590 * const image = await loader.loadAsync( 'image.png' ); 45591 * ``` 45592 * Please note that `ImageLoader` has dropped support for progress 45593 * events in `r84`. For an `ImageLoader` that supports progress events, see 45594 * [this thread](https://github.com/mrdoob/three.js/issues/10439#issuecomment-275785639). 45595 * 45596 * @augments Loader 45597 */ 45598class ImageLoader extends Loader { 45599 45600 /** 45601 * Constructs a new image loader. 45602 * 45603 * @param {LoadingManager} [manager] - The loading manager. 45604 */ 45605 constructor( manager ) { 45606 45607 super( manager ); 45608 45609 } 45610 45611 /** 45612 * Starts loading from the given URL and passes the loaded image 45613 * to the `onLoad()` callback. The method also returns a new `Image` object which can 45614 * directly be used for texture creation. If you do it this way, the texture 45615 * may pop up in your scene once the respective loading process is finished. 45616 * 45617 * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI. 45618 * @param {function(Image)} onLoad - Executed when the loading process has been finished. 45619 * @param {onProgressCallback} onProgress - Unsupported in this loader. 45620 * @param {onErrorCallback} onError - Executed when errors occur. 45621 * @return {Image} The image. 45622 */ 45623 load( url, onLoad, onProgress, onError ) { 45624 45625 if ( this.path !== undefined ) url = this.path + url; 45626 45627 url = this.manager.resolveURL( url ); 45628 45629 const scope = this; 45630 45631 const cached = Cache.get( `image:${url}` ); 45632 45633 if ( cached !== undefined ) { 45634 45635 if ( cached.complete === true ) { 45636 45637 scope.manager.itemStart( url ); 45638 45639 setTimeout( function () { 45640 45641 if ( onLoad ) onLoad( cached ); 45642 45643 scope.manager.itemEnd( url ); 45644 45645 }, 0 ); 45646 45647 } else { 45648 45649 let arr = _loading.get( cached ); 45650 45651 if ( arr === undefined ) { 45652 45653 arr = []; 45654 _loading.set( cached, arr ); 45655 45656 } 45657 45658 arr.push( { onLoad, onError } ); 45659 45660 } 45661 45662 return cached; 45663 45664 } 45665 45666 const image = createElementNS( 'img' ); 45667 45668 function onImageLoad() { 45669 45670 removeEventListeners(); 45671 45672 if ( onLoad ) onLoad( this ); 45673 45674 // 45675 45676 const callbacks = _loading.get( this ) || []; 45677 45678 for ( let i = 0; i < callbacks.length; i ++ ) { 45679 45680 const callback = callbacks[ i ]; 45681 if ( callback.onLoad ) callback.onLoad( this ); 45682 45683 } 45684 45685 _loading.delete( this ); 45686 45687 scope.manager.itemEnd( url ); 45688 45689 } 45690 45691 function onImageError( event ) { 45692 45693 removeEventListeners(); 45694 45695 if ( onError ) onError( event ); 45696 45697 Cache.remove( `image:${url}` ); 45698 45699 // 45700 45701 const callbacks = _loading.get( this ) || []; 45702 45703 for ( let i = 0; i < callbacks.length; i ++ ) { 45704 45705 const callback = callbacks[ i ]; 45706 if ( callback.onError ) callback.onError( event ); 45707 45708 } 45709 45710 _loading.delete( this ); 45711 45712 45713 scope.manager.itemError( url ); 45714 scope.manager.itemEnd( url ); 45715 45716 } 45717 45718 function removeEventListeners() { 45719 45720 image.removeEventListener( 'load', onImageLoad, false ); 45721 image.removeEventListener( 'error', onImageError, false ); 45722 45723 } 45724 45725 image.addEventListener( 'load', onImageLoad, false ); 45726 image.addEventListener( 'error', onImageError, false ); 45727 45728 if ( url.slice( 0, 5 ) !== 'data:' ) { 45729 45730 if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; 45731 45732 } 45733 45734 Cache.add( `image:${url}`, image ); 45735 scope.manager.itemStart( url ); 45736 45737 image.src = url; 45738 45739 return image; 45740 45741 } 45742 45743} 45744 45745/** 45746 * Class for loading cube textures. Images are internally loaded via {@link ImageLoader}. 45747 * 45748 * The loader returns an instance of {@link CubeTexture} and expects the cube map to 45749 * be defined as six separate images representing the sides of a cube. Other cube map definitions 45750 * like vertical and horizontal cross, column and row layouts are not supported. 45751 * 45752 * Note that, by convention, cube maps are specified in a coordinate system 45753 * in which positive-x is to the right when looking up the positive-z axis -- 45754 * in other words, using a left-handed coordinate system. Since three.js uses 45755 * a right-handed coordinate system, environment maps used in three.js will 45756 * have pos-x and neg-x swapped. 45757 * 45758 * The loaded cube texture is in sRGB color space. Meaning {@link Texture#colorSpace} 45759 * is set to `SRGBColorSpace` by default. 45760 * 45761 * ```js 45762 * const loader = new THREE.CubeTextureLoader().setPath( 'textures/cubeMaps/' ); 45763 * const cubeTexture = await loader.loadAsync( [ 45764 * 'px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png' 45765 * ] ); 45766 * scene.background = cubeTexture; 45767 * ``` 45768 * 45769 * @augments Loader 45770 */ 45771class CubeTextureLoader extends Loader { 45772 45773 /** 45774 * Constructs a new cube texture loader. 45775 * 45776 * @param {LoadingManager} [manager] - The loading manager. 45777 */ 45778 constructor( manager ) { 45779 45780 super( manager ); 45781 45782 } 45783 45784 /** 45785 * Starts loading from the given URL and pass the fully loaded cube texture 45786 * to the `onLoad()` callback. The method also returns a new cube texture object which can 45787 * directly be used for material creation. If you do it this way, the cube texture 45788 * may pop up in your scene once the respective loading process is finished. 45789 * 45790 * @param {Array<string>} urls - Array of 6 URLs to images, one for each side of the 45791 * cube texture. The urls should be specified in the following order: pos-x, 45792 * neg-x, pos-y, neg-y, pos-z, neg-z. An array of data URIs are allowed as well. 45793 * @param {function(CubeTexture)} onLoad - Executed when the loading process has been finished. 45794 * @param {onProgressCallback} onProgress - Unsupported in this loader. 45795 * @param {onErrorCallback} onError - Executed when errors occur. 45796 * @return {CubeTexture} The cube texture. 45797 */ 45798 load( urls, onLoad, onProgress, onError ) { 45799 45800 const texture = new CubeTexture(); 45801 texture.colorSpace = SRGBColorSpace; 45802 45803 const loader = new ImageLoader( this.manager ); 45804 loader.setCrossOrigin( this.crossOrigin ); 45805 loader.setPath( this.path ); 45806 45807 let loaded = 0; 45808 45809 function loadTexture( i ) { 45810 45811 loader.load( urls[ i ], function ( image ) { 45812 45813 texture.images[ i ] = image; 45814 45815 loaded ++; 45816 45817 if ( loaded === 6 ) { 45818 45819 texture.needsUpdate = true; 45820 45821 if ( onLoad ) onLoad( texture ); 45822 45823 } 45824 45825 }, undefined, onError ); 45826 45827 } 45828 45829 for ( let i = 0; i < urls.length; ++ i ) { 45830 45831 loadTexture( i ); 45832 45833 } 45834 45835 return texture; 45836 45837 } 45838 45839} 45840 45841/** 45842 * Abstract base class for loading binary texture formats RGBE, EXR or TGA. 45843 * Textures are internally loaded via {@link FileLoader}. 45844 * 45845 * Derived classes have to implement the `parse()` method which holds the parsing 45846 * for the respective format. 45847 * 45848 * @abstract 45849 * @augments Loader 45850 */ 45851class DataTextureLoader extends Loader { 45852 45853 /** 45854 * Constructs a new data texture loader. 45855 * 45856 * @param {LoadingManager} [manager] - The loading manager. 45857 */ 45858 constructor( manager ) { 45859 45860 super( manager ); 45861 45862 } 45863 45864 /** 45865 * Starts loading from the given URL and passes the loaded data texture 45866 * to the `onLoad()` callback. The method also returns a new texture object which can 45867 * directly be used for material creation. If you do it this way, the texture 45868 * may pop up in your scene once the respective loading process is finished. 45869 * 45870 * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI. 45871 * @param {function(DataTexture)} onLoad - Executed when the loading process has been finished. 45872 * @param {onProgressCallback} onProgress - Executed while the loading is in progress. 45873 * @param {onErrorCallback} onError - Executed when errors occur. 45874 * @return {DataTexture} The data texture. 45875 */ 45876 load( url, onLoad, onProgress, onError ) { 45877 45878 const scope = this; 45879 45880 const texture = new DataTexture(); 45881 45882 const loader = new FileLoader( this.manager ); 45883 loader.setResponseType( 'arraybuffer' ); 45884 loader.setRequestHeader( this.requestHeader ); 45885 loader.setPath( this.path ); 45886 loader.setWithCredentials( scope.withCredentials ); 45887 loader.load( url, function ( buffer ) { 45888 45889 let texData; 45890 45891 try { 45892 45893 texData = scope.parse( buffer ); 45894 45895 } catch ( e ) { 45896 45897 if ( onError !== undefined ) { 45898 45899 onError( e ); 45900 45901 } else { 45902 45903 error( e ); 45904 45905 } 45906 45907 return; 45908 45909 } 45910 45911 scope._applyTexData( texture, texData ); 45912 45913 if ( onLoad ) onLoad( texture, texData ); 45914 45915 }, onProgress, onError ); 45916 45917 45918 return texture; 45919 45920 } 45921 45922 /** 45923 * Parses the given buffer and returns a configured data texture. Use this method 45924 * for parsing texture data that is already in memory (e.g. drag and drop or data 45925 * loaded from a server) without going through {@link DataTextureLoader#load}. 45926 * 45927 * @param {ArrayBuffer} buffer - The raw texture data. 45928 * @return {DataTexture} The data texture. 45929 */ 45930 createDataTexture( buffer ) { 45931 45932 const texture = new DataTexture(); 45933 45934 this._applyTexData( texture, this.parse( buffer ) ); 45935 45936 return texture; 45937 45938 } 45939 45940 /** 45941 * Applies the given parsed texture data to the given data texture. 45942 * 45943 * @private 45944 * @param {DataTexture} texture - The data texture. 45945 * @param {DataTextureLoader~TexData} texData - The parsed texture data. 45946 */ 45947 _applyTexData( texture, texData ) { 45948 45949 if ( texData.image !== undefined ) { 45950 45951 texture.image = texData.image; 45952 45953 } else if ( texData.data !== undefined ) { 45954 45955 texture.image.width = texData.width; 45956 texture.image.height = texData.height; 45957 texture.image.data = texData.data; 45958 45959 } 45960 45961 texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; 45962 texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; 45963 45964 texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; 45965 texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; 45966 45967 texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; 45968 45969 if ( texData.colorSpace !== undefined ) { 45970 45971 texture.colorSpace = texData.colorSpace; 45972 45973 } 45974 45975 if ( texData.flipY !== undefined ) { 45976 45977 texture.flipY = texData.flipY; 45978 45979 } 45980 45981 if ( texData.format !== undefined ) { 45982 45983 texture.format = texData.format; 45984 45985 } 45986 45987 if ( texData.type !== undefined ) { 45988 45989 texture.type = texData.type; 45990 45991 } 45992 45993 if ( texData.mipmaps !== undefined ) { 45994 45995 texture.mipmaps = texData.mipmaps; 45996 texture.minFilter = LinearMipmapLinearFilter; // presumably... 45997 45998 } 45999 46000 if ( texData.mipmapCount === 1 ) { 46001 46002 texture.minFilter = LinearFilter; 46003 46004 } 46005 46006 if ( texData.generateMipmaps !== undefined ) { 46007 46008 texture.generateMipmaps = texData.generateMipmaps; 46009 46010 } 46011 46012 texture.needsUpdate = true; 46013 46014 } 46015 46016} 46017 46018/** 46019 * Class for loading textures. Images are internally 46020 * loaded via {@link ImageLoader}. 46021 * 46022 * ```js 46023 * const loader = new THREE.TextureLoader(); 46024 * const texture = await loader.loadAsync( 'textures/land_ocean_ice_cloud_2048.jpg' ); 46025 * 46026 * const material = new THREE.MeshBasicMaterial( { map:texture } ); 46027 * ``` 46028 * Please note that `TextureLoader` has dropped support for progress 46029 * events in `r84`. For a `TextureLoader` that supports progress events, see 46030 * [this thread](https://github.com/mrdoob/three.js/issues/10439#issuecomment-293260145). 46031 * 46032 * @augments Loader 46033 */ 46034class TextureLoader extends Loader { 46035 46036 /** 46037 * Constructs a new texture loader. 46038 * 46039 * @param {LoadingManager} [manager] - The loading manager. 46040 */ 46041 constructor( manager ) { 46042 46043 super( manager ); 46044 46045 } 46046 46047 /** 46048 * Starts loading from the given URL and pass the fully loaded texture 46049 * to the `onLoad()` callback. The method also returns a new texture object which can 46050 * directly be used for material creation. If you do it this way, the texture 46051 * may pop up in your scene once the respective loading process is finished. 46052 * 46053 * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI. 46054 * @param {function(Texture)} onLoad - Executed when the loading process has been finished. 46055 * @param {onProgressCallback} onProgress - Unsupported in this loader. 46056 * @param {onErrorCallback} onError - Executed when errors occur. 46057 * @return {Texture} The texture. 46058 */ 46059 load( url, onLoad, onProgress, onError ) { 46060 46061 const texture = new Texture(); 46062 46063 const loader = new ImageLoader( this.manager ); 46064 loader.setCrossOrigin( this.crossOrigin ); 46065 loader.setPath( this.path ); 46066 46067 loader.load( url, function ( image ) { 46068 46069 texture.image = image; 46070 texture.needsUpdate = true; 46071 46072 if ( onLoad !== undefined ) { 46073 46074 onLoad( texture ); 46075 46076 } 46077 46078 }, onProgress, onError ); 46079 46080 return texture; 46081 46082 } 46083 46084} 46085 46086/** 46087 * Abstract base class for lights - all other light types inherit the 46088 * properties and methods described here. 46089 * 46090 * @abstract 46091 * @augments Object3D 46092 */ 46093class Light extends Object3D { 46094 46095 /** 46096 * Constructs a new light. 46097 * 46098 * @param {(number|Color|string)} [color=0xffffff] - The light's color. 46099 * @param {number} [intensity=1] - The light's strength/intensity. 46100 */ 46101 constructor( color, intensity = 1 ) { 46102 46103 super(); 46104 46105 /** 46106 * This flag can be used for type testing. 46107 * 46108 * @type {boolean} 46109 * @readonly 46110 * @default true 46111 */ 46112 this.isLight = true; 46113 46114 this.type = 'Light'; 46115 46116 /** 46117 * The light's color. 46118 * 46119 * @type {Color} 46120 */ 46121 this.color = new Color( color ); 46122 46123 /** 46124 * The light's intensity. 46125 * 46126 * @type {number} 46127 * @default 1 46128 */ 46129 this.intensity = intensity; 46130 46131 } 46132 46133 copy( source, recursive ) { 46134 46135 super.copy( source, recursive ); 46136 46137 this.color.copy( source.color ); 46138 this.intensity = source.intensity; 46139 46140 return this; 46141 46142 } 46143 46144 toJSON( meta ) { 46145 46146 const data = super.toJSON( meta ); 46147 46148 data.object.color = this.color.getHex(); 46149 data.object.intensity = this.intensity; 46150 46151 return data; 46152 46153 } 46154 46155} 46156 46157/** 46158 * A light source positioned directly above the scene, with color fading from 46159 * the sky color to the ground color. 46160 * 46161 * This light cannot be used to cast shadows. 46162 * 46163 * ```js 46164 * const light = new THREE.HemisphereLight( 0xffffbb, 0x080820, 1 ); 46165 * scene.add( light ); 46166 * ``` 46167 * 46168 * @augments Light 46169 */ 46170class HemisphereLight extends Light { 46171 46172 /** 46173 * Constructs a new hemisphere light. 46174 * 46175 * @param {(number|Color|string)} [skyColor=0xffffff] - The light's sky color. 46176 * @param {(number|Color|string)} [groundColor=0xffffff] - The light's ground color. 46177 * @param {number} [intensity=1] - The light's strength/intensity. 46178 */ 46179 constructor( skyColor, groundColor, intensity ) { 46180 46181 super( skyColor, intensity ); 46182 46183 /** 46184 * This flag can be used for type testing. 46185 * 46186 * @type {boolean} 46187 * @readonly 46188 * @default true 46189 */ 46190 this.isHemisphereLight = true; 46191 46192 this.type = 'HemisphereLight'; 46193 46194 this.position.copy( Object3D.DEFAULT_UP ); 46195 this.updateMatrix(); 46196 46197 /** 46198 * The light's ground color. 46199 * 46200 * @type {Color} 46201 */ 46202 this.groundColor = new Color( groundColor ); 46203 46204 } 46205 46206 copy( source, recursive ) { 46207 46208 super.copy( source, recursive ); 46209 46210 this.groundColor.copy( source.groundColor ); 46211 46212 return this; 46213 46214 } 46215 46216 toJSON( meta ) { 46217 46218 const data = super.toJSON( meta ); 46219 46220 data.object.groundColor = this.groundColor.getHex(); 46221 46222 return data; 46223 46224 } 46225 46226} 46227 46228const _projScreenMatrix = /*@__PURE__*/ new Matrix4(); 46229const _lightPositionWorld = /*@__PURE__*/ new Vector3(); 46230const _lookTarget = /*@__PURE__*/ new Vector3(); 46231 46232/** 46233 * Abstract base class for light shadow classes. These classes 46234 * represent the shadow configuration for different light types. 46235 * 46236 * @abstract 46237 */ 46238class LightShadow { 46239 46240 /** 46241 * Constructs a new light shadow. 46242 * 46243 * @param {Camera} camera - The light's view of the world. 46244 */ 46245 constructor( camera ) { 46246 46247 /** 46248 * The light's view of the world. 46249 * 46250 * @type {Camera} 46251 */ 46252 this.camera = camera; 46253 46254 /** 46255 * The intensity of the shadow. The default is `1`. 46256 * Valid values are in the range `[0, 1]`. 46257 * 46258 * @type {number} 46259 * @default 1 46260 */ 46261 this.intensity = 1; 46262 46263 /** 46264 * Shadow map bias, how much to add or subtract from the normalized depth 46265 * when deciding whether a surface is in shadow. 46266 * 46267 * The default is `0`. Very tiny adjustments here (in the order of `0.0001`) 46268 * may help reduce artifacts in shadows. 46269 * 46270 * @type {number} 46271 * @default 0 46272 */ 46273 this.bias = 0; 46274 46275 /** 46276 * A node version of `bias`. Only supported with `WebGPURenderer`. 46277 * 46278 * If a bias node is defined, `bias` has no effect. 46279 * 46280 * @type {?Node<float>} 46281 * @default null 46282 */ 46283 this.biasNode = null; 46284 46285 /** 46286 * Defines how much the position used to query the shadow map is offset along 46287 * the object normal. The default is `0`. Increasing this value can be used to 46288 * reduce shadow acne especially in large scenes where light shines onto 46289 * geometry at a shallow angle. The cost is that shadows may appear distorted. 46290 * 46291 * @type {number} 46292 * @default 0 46293 */ 46294 this.normalBias = 0; 46295 46296 /** 46297 * Setting this to values greater than 1 will blur the edges of the shadow. 46298 * High values will cause unwanted banding effects in the shadows - a greater 46299 * map size will allow for a higher value to be used here before these effects 46300 * become visible. 46301 * 46302 * The property has no effect when the shadow map type is `BasicShadowMap`. 46303 * 46304 * @type {number} 46305 * @default 1 46306 */ 46307 this.radius = 1; 46308 46309 /** 46310 * The amount of samples to use when blurring a VSM shadow map. 46311 * 46312 * @type {number} 46313 * @default 8 46314 */ 46315 this.blurSamples = 8; 46316 46317 /** 46318 * Defines the width and height of the shadow map. Higher values give better quality 46319 * shadows at the cost of computation time. Values must be powers of two. 46320 * 46321 * @type {Vector2} 46322 * @default (512,512) 46323 */ 46324 this.mapSize = new Vector2( 512, 512 ); 46325 46326 /** 46327 * The type of shadow texture. The default is `UnsignedByteType`. 46328 * 46329 * @type {number} 46330 * @default UnsignedByteType 46331 */ 46332 this.mapType = UnsignedByteType; 46333 46334 /** 46335 * The depth map generated using the internal camera; a location beyond a 46336 * pixel's depth is in shadow. Computed internally during rendering. 46337 * 46338 * @type {?RenderTarget} 46339 * @default null 46340 */ 46341 this.map = null; 46342 46343 /** 46344 * The distribution map generated using the internal camera; an occlusion is 46345 * calculated based on the distribution of depths. Computed internally during 46346 * rendering. 46347 * 46348 * @type {?RenderTarget} 46349 * @default null 46350 */ 46351 this.mapPass = null; 46352 46353 /** 46354 * Model to shadow camera space, to compute location and depth in shadow map. 46355 * This is computed internally during rendering. 46356 * 46357 * @type {Matrix4} 46358 */ 46359 this.matrix = new Matrix4(); 46360 46361 /** 46362 * Enables automatic updates of the light's shadow. If you do not require dynamic 46363 * lighting / shadows, you may set this to `false`. 46364 * 46365 * @type {boolean} 46366 * @default true 46367 */ 46368 this.autoUpdate = true; 46369 46370 /** 46371 * When set to `true`, shadow maps will be updated in the next `render` call. 46372 * If you have set {@link LightShadow#autoUpdate} to `false`, you will need to 46373 * set this property to `true` and then make a render call to update the light's shadow. 46374 * 46375 * @type {boolean} 46376 * @default false 46377 */ 46378 this.needsUpdate = false; 46379 46380 this._frustum = new Frustum(); 46381 this._frameExtents = new Vector2( 1, 1 ); 46382 46383 this._viewportCount = 1; 46384 46385 this._viewports = [ 46386 46387 new Vector4( 0, 0, 1, 1 ) 46388 46389 ]; 46390 46391 } 46392 46393 /** 46394 * Used internally by the renderer to get the number of viewports that need 46395 * to be rendered for this shadow. 46396 * 46397 * @return {number} The viewport count. 46398 */ 46399 getViewportCount() { 46400 46401 return this._viewportCount; 46402 46403 } 46404 46405 /** 46406 * Used internally by the renderer to get the camera that renders the given viewport. 46407 * 46408 * @param {number} [viewportIndex=0] - The viewport index. 46409 * @return {Camera} The shadow camera. 46410 */ 46411 getCamera( /* viewportIndex */ ) { 46412 46413 return this.camera; 46414 46415 } 46416 46417 /** 46418 * Gets the shadow cameras frustum. Used internally by the renderer to cull objects. 46419 * 46420 * @return {Frustum} The shadow camera frustum. 46421 */ 46422 getFrustum() { 46423 46424 return this._frustum; 46425 46426 } 46427 46428 /** 46429 * Update the matrices for the camera and shadow, used internally by the renderer. 46430 * 46431 * @param {Light} light - The light for which the shadow is being rendered. 46432 */ 46433 updateMatrices( light ) { 46434 46435 const shadowCamera = this.camera; 46436 _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); 46437 shadowCamera.position.copy( _lightPositionWorld ); 46438 46439 _lookTarget.setFromMatrixPosition( light.target.matrixWorld ); 46440 shadowCamera.lookAt( _lookTarget ); 46441 shadowCamera.updateMatrixWorld(); 46442 this._updateMatrix( shadowCamera, this.matrix, this._frustum ); 46443 46444 } 46445 46446 /** 46447 * Updates a shadow projection matrix and its corresponding frustum. 46448 * 46449 * @private 46450 * @param {Camera} shadowCamera - The shadow camera. 46451 * @param {Matrix4} shadowMatrix - The target shadow matrix. 46452 * @param {Frustum} frustum - The target frustum. 46453 * @param {Vector4} [viewport] - The viewport within the shadow atlas. 46454 */ 46455 _updateMatrix( shadowCamera, shadowMatrix, frustum, viewport ) { 46456 46457 _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); 46458 frustum.setFromProjectionMatrix( _projScreenMatrix, shadowCamera.coordinateSystem, shadowCamera.reversedDepth ); 46459 46460 const frameExtents = this._frameExtents; 46461 const scaleX = viewport ? viewport.z / frameExtents.x : 1; 46462 const scaleY = viewport ? viewport.w / frameExtents.y : 1; 46463 const offsetX = viewport ? viewport.x / frameExtents.x : 0; 46464 const offsetY = viewport ? viewport.y / frameExtents.y : 0; 46465 46466 if ( shadowCamera.coordinateSystem === WebGPUCoordinateSystem || shadowCamera.reversedDepth ) { 46467 46468 shadowMatrix.set( 46469 0.5 * scaleX, 0.0, 0.0, 0.5 * scaleX + offsetX, 46470 0.0, 0.5 * scaleY, 0.0, 0.5 * scaleY + offsetY, 46471 0.0, 0.0, 1.0, 0.0, // Identity Z (preserving the correct [0, 1] range from the projection matrix) 46472 0.0, 0.0, 0.0, 1.0 46473 ); 46474 46475 } else { 46476 46477 shadowMatrix.set( 46478 0.5 * scaleX, 0.0, 0.0, 0.5 * scaleX + offsetX, 46479 0.0, 0.5 * scaleY, 0.0, 0.5 * scaleY + offsetY, 46480 0.0, 0.0, 0.5, 0.5, 46481 0.0, 0.0, 0.0, 1.0 46482 ); 46483 46484 } 46485 46486 shadowMatrix.multiply( _projScreenMatrix ); 46487 46488 } 46489 46490 /** 46491 * Returns a viewport definition for the given viewport index. 46492 * 46493 * @param {number} viewportIndex - The viewport index. 46494 * @return {Vector4} The viewport. 46495 */ 46496 getViewport( viewportIndex ) { 46497 46498 return this._viewports[ viewportIndex ]; 46499 46500 } 46501 46502 /** 46503 * Returns the frame extends. 46504 * 46505 * @return {Vector2} The frame extends. 46506 */ 46507 getFrameExtents() { 46508 46509 return this._frameExtents; 46510 46511 } 46512 46513 /** 46514 * Frees the GPU-related resources allocated by this instance. Call this 46515 * method whenever this instance is no longer used in your app. 46516 */ 46517 dispose() { 46518 46519 if ( this.map ) { 46520 46521 this.map.dispose(); 46522 46523 } 46524 46525 if ( this.mapPass ) { 46526 46527 this.mapPass.dispose(); 46528 46529 } 46530 46531 } 46532 46533 /** 46534 * Copies the values of the given light shadow instance to this instance. 46535 * 46536 * @param {LightShadow} source - The light shadow to copy. 46537 * @return {LightShadow} A reference to this light shadow instance. 46538 */ 46539 copy( source ) { 46540 46541 this.camera = source.camera.clone(); 46542 46543 this.intensity = source.intensity; 46544 46545 this.bias = source.bias; 46546 this.radius = source.radius; 46547 46548 this.autoUpdate = source.autoUpdate; 46549 this.needsUpdate = source.needsUpdate; 46550 this.normalBias = source.normalBias; 46551 this.blurSamples = source.blurSamples; 46552 46553 this.mapSize.copy( source.mapSize ); 46554 46555 this.biasNode = source.biasNode; 46556 46557 return this; 46558 46559 } 46560 46561 /** 46562 * Returns a new light shadow instance with copied values from this instance. 46563 * 46564 * @return {LightShadow} A clone of this instance. 46565 */ 46566 clone() { 46567 46568 return new this.constructor().copy( this ); 46569 46570 } 46571 46572 /** 46573 * Serializes the light shadow into JSON. 46574 * 46575 * @return {Object} A JSON object representing the serialized light shadow. 46576 * @see {@link ObjectLoader#parse} 46577 */ 46578 toJSON() { 46579 46580 const object = {}; 46581 46582 object.intensity = this.intensity; 46583 object.bias = this.bias; 46584 object.normalBias = this.normalBias; 46585 object.radius = this.radius; 46586 object.blurSamples = this.blurSamples; 46587 object.mapSize = this.mapSize.toArray(); 46588 46589 object.camera = this.camera.toJSON( false ).object; 46590 delete object.camera.matrix; 46591 46592 return object; 46593 46594 } 46595 46596} 46597 46598const _position$2 = /*@__PURE__*/ new Vector3(); 46599const _quaternion$2 = /*@__PURE__*/ new Quaternion(); 46600const _scale$2 = /*@__PURE__*/ new Vector3(); 46601 46602/** 46603 * Abstract base class for cameras. This class should always be inherited 46604 * when you build a new camera. 46605 * 46606 * @abstract 46607 * @augments Object3D 46608 */ 46609class Camera extends Object3D { 46610 46611 /** 46612 * Constructs a new camera. 46613 */ 46614 constructor() { 46615 46616 super(); 46617 46618 /** 46619 * This flag can be used for type testing. 46620 * 46621 * @type {boolean} 46622 * @readonly 46623 * @default true 46624 */ 46625 this.isCamera = true; 46626 46627 this.type = 'Camera'; 46628 46629 /** 46630 * The inverse of the camera's world matrix. 46631 * 46632 * @type {Matrix4} 46633 */ 46634 this.matrixWorldInverse = new Matrix4(); 46635 46636 /** 46637 * The camera's projection matrix. 46638 * 46639 * @type {Matrix4} 46640 */ 46641 this.projectionMatrix = new Matrix4(); 46642 46643 /** 46644 * The inverse of the camera's projection matrix. 46645 * 46646 * @type {Matrix4} 46647 */ 46648 this.projectionMatrixInverse = new Matrix4(); 46649 46650 /** 46651 * The coordinate system in which the camera is used. 46652 * 46653 * @type {(WebGLCoordinateSystem|WebGPUCoordinateSystem)} 46654 */ 46655 this.coordinateSystem = WebGLCoordinateSystem; 46656 46657 this._reversedDepth = false; 46658 46659 } 46660 46661 /** 46662 * The flag that indicates whether the camera uses a reversed depth buffer. 46663 * 46664 * @type {boolean} 46665 * @default false 46666 */ 46667 get reversedDepth() { 46668 46669 return this._reversedDepth; 46670 46671 } 46672 46673 copy( source, recursive ) { 46674 46675 super.copy( source, recursive ); 46676 46677 this.matrixWorldInverse.copy( source.matrixWorldInverse ); 46678 46679 this.projectionMatrix.copy( source.projectionMatrix ); 46680 this.projectionMatrixInverse.copy( source.projectionMatrixInverse ); 46681 46682 this.coordinateSystem = source.coordinateSystem; 46683 46684 return this; 46685 46686 } 46687 46688 /** 46689 * Returns a vector representing the ("look") direction of the 3D object in world space. 46690 * 46691 * This method is overwritten since cameras have a different forward vector compared to other 46692 * 3D objects. A camera looks down its local, negative z-axis by default. 46693 * 46694 * @param {Vector3} target - The target vector the result is stored to. 46695 * @return {Vector3} The 3D object's direction in world space. 46696 */ 46697 getWorldDirection( target ) { 46698 46699 return super.getWorldDirection( target ).negate(); 46700 46701 } 46702 46703 updateMatrixWorld( force ) { 46704 46705 super.updateMatrixWorld( force ); 46706 46707 // exclude scale from view matrix to be glTF conform 46708 46709 this.matrixWorld.decompose( _position$2, _quaternion$2, _scale$2 ); 46710 46711 if ( _scale$2.x === 1 && _scale$2.y === 1 && _scale$2.z === 1 ) { 46712 46713 this.matrixWorldInverse.copy( this.matrixWorld ).invert(); 46714 46715 } else { 46716 46717 this.matrixWorldInverse.compose( _position$2, _quaternion$2, _scale$2.set( 1, 1, 1 ) ).invert(); 46718 46719 } 46720 46721 } 46722 46723 updateWorldMatrix( updateParents, updateChildren, force = false ) { 46724 46725 super.updateWorldMatrix( updateParents, updateChildren, force ); 46726 46727 // exclude scale from view matrix to be glTF conform 46728 46729 this.matrixWorld.decompose( _position$2, _quaternion$2, _scale$2 ); 46730 46731 if ( _scale$2.x === 1 && _scale$2.y === 1 && _scale$2.z === 1 ) { 46732 46733 this.matrixWorldInverse.copy( this.matrixWorld ).invert(); 46734 46735 } else { 46736 46737 this.matrixWorldInverse.compose( _position$2, _quaternion$2, _scale$2.set( 1, 1, 1 ) ).invert(); 46738 46739 } 46740 46741 } 46742 46743 clone() { 46744 46745 return new this.constructor().copy( this ); 46746 46747 } 46748 46749} 46750 46751const _v3$1 = /*@__PURE__*/ new Vector3(); 46752const _minTarget = /*@__PURE__*/ new Vector2(); 46753const _maxTarget = /*@__PURE__*/ new Vector2(); 46754 46755/** 46756 * Camera that uses [perspective projection](https://en.wikipedia.org/wiki/Perspective_(graphical)). 46757 * 46758 * This projection mode is designed to mimic the way the human eye sees. It 46759 * is the most common projection mode used for rendering a 3D scene. 46760 * 46761 * ```js 46762 * const camera = new THREE.PerspectiveCamera( 45, width / height, 1, 1000 ); 46763 * scene.add( camera ); 46764 * ``` 46765 * 46766 * @augments Camera 46767 */ 46768class PerspectiveCamera extends Camera { 46769 46770 /** 46771 * Constructs a new perspective camera. 46772 * 46773 * @param {number} [fov=50] - The vertical field of view. 46774 * @param {number} [aspect=1] - The aspect ratio. 46775 * @param {number} [near=0.1] - The camera's near plane. 46776 * @param {number} [far=2000] - The camera's far plane. 46777 */ 46778 constructor( fov = 50, aspect = 1, near = 0.1, far = 2000 ) { 46779 46780 super(); 46781 46782 /** 46783 * This flag can be used for type testing. 46784 * 46785 * @type {boolean} 46786 * @readonly 46787 * @default true 46788 */ 46789 this.isPerspectiveCamera = true; 46790 46791 this.type = 'PerspectiveCamera'; 46792 46793 /** 46794 * The vertical field of view, from bottom to top of view, 46795 * in degrees. 46796 * 46797 * @type {number} 46798 * @default 50 46799 */ 46800 this.fov = fov; 46801 46802 /** 46803 * The zoom factor of the camera. 46804 * 46805 * @type {number} 46806 * @default 1 46807 */ 46808 this.zoom = 1; 46809 46810 /** 46811 * The camera's near plane. The valid range is greater than `0` 46812 * and less than the current value of {@link PerspectiveCamera#far}. 46813 * 46814 * Note that, unlike for the {@link OrthographicCamera}, `0` is <em>not</em> a 46815 * valid value for a perspective camera's near plane. 46816 * 46817 * @type {number} 46818 * @default 0.1 46819 */ 46820 this.near = near; 46821 46822 /** 46823 * The camera's far plane. Must be greater than the 46824 * current value of {@link PerspectiveCamera#near}. 46825 * 46826 * @type {number} 46827 * @default 2000 46828 */ 46829 this.far = far; 46830 46831 /** 46832 * Object distance used for stereoscopy and depth-of-field effects. This 46833 * parameter does not influence the projection matrix unless a 46834 * {@link StereoCamera} is being used. 46835 * 46836 * @type {number} 46837 * @default 10 46838 */ 46839 this.focus = 10; 46840 46841 /** 46842 * The aspect ratio, usually the canvas width / canvas height. 46843 * 46844 * @type {number} 46845 * @default 1 46846 */ 46847 this.aspect = aspect; 46848 46849 /** 46850 * Represents the frustum window specification. This property should not be edited 46851 * directly but via {@link PerspectiveCamera#setViewOffset} and {@link PerspectiveCamera#clearViewOffset}. 46852 * 46853 * @type {?Object} 46854 * @default null 46855 */ 46856 this.view = null; 46857 46858 /** 46859 * Film size used for the larger axis. Default is `35` (millimeters). This 46860 * parameter does not influence the projection matrix unless {@link PerspectiveCamera#filmOffset} 46861 * is set to a nonzero value. 46862 * 46863 * @type {number} 46864 * @default 35 46865 */ 46866 this.filmGauge = 35; 46867 46868 /** 46869 * Horizontal off-center offset in the same unit as {@link PerspectiveCamera#filmGauge}. 46870 * 46871 * @type {number} 46872 * @default 0 46873 */ 46874 this.filmOffset = 0; 46875 46876 this.updateProjectionMatrix(); 46877 46878 } 46879 46880 copy( source, recursive ) { 46881 46882 super.copy( source, recursive ); 46883 46884 this.fov = source.fov; 46885 this.zoom = source.zoom; 46886 46887 this.near = source.near; 46888 this.far = source.far; 46889 this.focus = source.focus; 46890 46891 this.aspect = source.aspect; 46892 this.view = source.view === null ? null : Object.assign( {}, source.view ); 46893 46894 this.filmGauge = source.filmGauge; 46895 this.filmOffset = source.filmOffset; 46896 46897 return this; 46898 46899 } 46900 46901 /** 46902 * Sets the FOV by focal length in respect to the current {@link PerspectiveCamera#filmGauge}. 46903 * 46904 * The default film gauge is 35, so that the focal length can be specified for 46905 * a 35mm (full frame) camera. 46906 * 46907 * @param {number} focalLength - Values for focal length and film gauge must have the same unit. 46908 */ 46909 setFocalLength( focalLength ) { 46910 46911 /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ 46912 const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; 46913 46914 this.fov = RAD2DEG * 2 * Math.atan( vExtentSlope ); 46915 this.updateProjectionMatrix(); 46916 46917 } 46918 46919 /** 46920 * Returns the focal length from the current {@link PerspectiveCamera#fov} and 46921 * {@link PerspectiveCamera#filmGauge}. 46922 * 46923 * @return {number} The computed focal length. 46924 */ 46925 getFocalLength() { 46926 46927 const vExtentSlope = Math.tan( DEG2RAD * 0.5 * this.fov ); 46928 46929 return 0.5 * this.getFilmHeight() / vExtentSlope; 46930 46931 } 46932 46933 /** 46934 * Returns the current vertical field of view angle in degrees considering {@link PerspectiveCamera#zoom}. 46935 * 46936 * @return {number} The effective FOV. 46937 */ 46938 getEffectiveFOV() { 46939 46940 return RAD2DEG * 2 * Math.atan( 46941 Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom ); 46942 46943 } 46944 46945 /** 46946 * Returns the width of the image on the film. If {@link PerspectiveCamera#aspect} is greater than or 46947 * equal to one (landscape format), the result equals {@link PerspectiveCamera#filmGauge}. 46948 * 46949 * @return {number} The film width. 46950 */ 46951 getFilmWidth() { 46952 46953 // film not completely covered in portrait format (aspect < 1) 46954 return this.filmGauge * Math.min( this.aspect, 1 ); 46955 46956 } 46957 46958 /** 46959 * Returns the height of the image on the film. If {@link PerspectiveCamera#aspect} is greater than or 46960 * equal to one (landscape format), the result equals {@link PerspectiveCamera#filmGauge}. 46961 * 46962 * @return {number} The film width. 46963 */ 46964 getFilmHeight() { 46965 46966 // film not completely covered in landscape format (aspect > 1) 46967 return this.filmGauge / Math.max( this.aspect, 1 ); 46968 46969 } 46970 46971 /** 46972 * Computes the 2D bounds of the camera's viewable rectangle at a given distance along the viewing direction. 46973 * Sets `minTarget` and `maxTarget` to the coordinates of the lower-left and upper-right corners of the view rectangle. 46974 * 46975 * @param {number} distance - The viewing distance. 46976 * @param {Vector2} minTarget - The lower-left corner of the view rectangle is written into this vector. 46977 * @param {Vector2} maxTarget - The upper-right corner of the view rectangle is written into this vector. 46978 */ 46979 getViewBounds( distance, minTarget, maxTarget ) { 46980 46981 _v3$1.set( -1, -1, 0.5 ).applyMatrix4( this.projectionMatrixInverse ); 46982 46983 minTarget.set( _v3$1.x, _v3$1.y ).multiplyScalar( - distance / _v3$1.z ); 46984 46985 _v3$1.set( 1, 1, 0.5 ).applyMatrix4( this.projectionMatrixInverse ); 46986 46987 maxTarget.set( _v3$1.x, _v3$1.y ).multiplyScalar( - distance / _v3$1.z ); 46988 46989 } 46990 46991 /** 46992 * Computes the width and height of the camera's viewable rectangle at a given distance along the viewing direction. 46993 * 46994 * @param {number} distance - The viewing distance. 46995 * @param {Vector2} target - The target vector that is used to store result where x is width and y is height. 46996 * @returns {Vector2} The view size. 46997 */ 46998 getViewSize( distance, target ) { 46999 47000 this.getViewBounds( distance, _minTarget, _maxTarget ); 47001 47002 return target.subVectors( _maxTarget, _minTarget ); 47003 47004 } 47005 47006 /** 47007 * Sets an offset in a larger frustum. This is useful for multi-window or 47008 * multi-monitor/multi-machine setups. 47009 * 47010 * For example, if you have 3x2 monitors and each monitor is 1920x1080 and 47011 * the monitors are in grid like this 47012 *``` 47013 * +---+---+---+ 47014 * | A | B | C | 47015 * +---+---+---+ 47016 * | D | E | F | 47017 * +---+---+---+ 47018 *``` 47019 * then for each monitor you would call it like this: 47020 *```js 47021 * const w = 1920; 47022 * const h = 1080; 47023 * const fullWidth = w * 3; 47024 * const fullHeight = h * 2; 47025 * 47026 * // --A-- 47027 * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); 47028 * // --B-- 47029 * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); 47030 * // --C-- 47031 * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); 47032 * // --D-- 47033 * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); 47034 * // --E-- 47035 * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); 47036 * // --F-- 47037 * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); 47038 * ``` 47039 * 47040 * Note there is no reason monitors have to be the same size or in a grid. 47041 * 47042 * @param {number} fullWidth - The full width of multiview setup. 47043 * @param {number} fullHeight - The full height of multiview setup. 47044 * @param {number} x - The horizontal offset of the subcamera. 47045 * @param {number} y - The vertical offset of the subcamera. 47046 * @param {number} width - The width of subcamera. 47047 * @param {number} height - The height of subcamera. 47048 */ 47049 setViewOffset( fullWidth, fullHeight, x, y, width, height ) { 47050 47051 this.aspect = fullWidth / fullHeight; 47052 47053 if ( this.view === null ) { 47054 47055 this.view = { 47056 enabled: true, 47057 fullWidth: 1, 47058 fullHeight: 1, 47059 offsetX: 0, 47060 offsetY: 0, 47061 width: 1, 47062 height: 1 47063 }; 47064 47065 } 47066 47067 this.view.enabled = true; 47068 this.view.fullWidth = fullWidth; 47069 this.view.fullHeight = fullHeight; 47070 this.view.offsetX = x; 47071 this.view.offsetY = y; 47072 this.view.width = width; 47073 this.view.height = height; 47074 47075 this.updateProjectionMatrix(); 47076 47077 } 47078 47079 /** 47080 * Removes the view offset from the projection matrix. 47081 */ 47082 clearViewOffset() { 47083 47084 if ( this.view !== null ) { 47085 47086 this.view.enabled = false; 47087 47088 } 47089 47090 this.updateProjectionMatrix(); 47091 47092 } 47093 47094 /** 47095 * Updates the camera's projection matrix. Must be called after any change of 47096 * camera properties. 47097 */ 47098 updateProjectionMatrix() { 47099 47100 const near = this.near; 47101 let top = near * Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom; 47102 let height = 2 * top; 47103 let width = this.aspect * height; 47104 let left = -0.5 * width; 47105 const view = this.view; 47106 47107 if ( this.view !== null && this.view.enabled ) { 47108 47109 const fullWidth = view.fullWidth, 47110 fullHeight = view.fullHeight; 47111 47112 left += view.offsetX * width / fullWidth; 47113 top -= view.offsetY * height / fullHeight; 47114 width *= view.width / fullWidth; 47115 height *= view.height / fullHeight; 47116 47117 } 47118 47119 const skew = this.filmOffset; 47120 if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); 47121 47122 this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far, this.coordinateSystem, this.reversedDepth ); 47123 47124 this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); 47125 47126 } 47127 47128 toJSON( meta ) { 47129 47130 const data = super.toJSON( meta ); 47131 47132 data.object.fov = this.fov; 47133 data.object.zoom = this.zoom; 47134 47135 data.object.near = this.near; 47136 data.object.far = this.far; 47137 data.object.focus = this.focus; 47138 47139 data.object.aspect = this.aspect; 47140 47141 if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); 47142 47143 data.object.filmGauge = this.filmGauge; 47144 data.object.filmOffset = this.filmOffset; 47145 47146 return data; 47147 47148 } 47149 47150} 47151 47152/** 47153 * Represents the shadow configuration of directional lights. 47154 * 47155 * @augments LightShadow 47156 */ 47157class SpotLightShadow extends LightShadow { 47158 47159 /** 47160 * Constructs a new spot light shadow. 47161 */ 47162 constructor() { 47163 47164 super( new PerspectiveCamera( 50, 1, 0.5, 500 ) ); 47165 47166 /** 47167 * This flag can be used for type testing. 47168 * 47169 * @type {boolean} 47170 * @readonly 47171 * @default true 47172 */ 47173 this.isSpotLightShadow = true; 47174 47175 /** 47176 * Used to focus the shadow camera. The camera's field of view is set as a 47177 * percentage of the spotlight's field-of-view. Range is `[0, 1]`. 47178 * 47179 * @type {number} 47180 * @default 1 47181 */ 47182 this.focus = 1; 47183 47184 /** 47185 * Texture aspect ratio. 47186 * 47187 * @type {number} 47188 * @default 1 47189 */ 47190 this.aspect = 1; 47191 47192 } 47193 47194 updateMatrices( light ) { 47195 47196 const camera = this.camera; 47197 47198 const fov = RAD2DEG * 2 * light.angle * this.focus; 47199 const aspect = ( this.mapSize.width / this.mapSize.height ) * this.aspect; 47200 const far = light.distance || camera.far; 47201 47202 if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { 47203 47204 camera.fov = fov; 47205 camera.aspect = aspect; 47206 camera.far = far; 47207 camera.updateProjectionMatrix(); 47208 47209 } 47210 47211 super.updateMatrices( light ); 47212 47213 } 47214 47215 copy( source ) { 47216 47217 super.copy( source ); 47218 47219 this.focus = source.focus; 47220 this.aspect = source.aspect; 47221 47222 return this; 47223 47224 } 47225 47226 /** 47227 * Serializes the light shadow into JSON. 47228 * 47229 * @return {Object} A JSON object representing the serialized light shadow. 47230 * @see {@link ObjectLoader#parse} 47231 */ 47232 toJSON() { 47233 47234 const object = super.toJSON(); 47235 47236 object.focus = this.focus; 47237 object.aspect = this.aspect; 47238 47239 return object; 47240 47241 } 47242 47243} 47244 47245/** 47246 * This light gets emitted from a single point in one direction, along a cone 47247 * that increases in size the further from the light it gets. 47248 * 47249 * This light can cast shadows - see the {@link SpotLightShadow} for details. 47250 * 47251 * ```js 47252 * // white spotlight shining from the side, modulated by a texture 47253 * const spotLight = new THREE.SpotLight( 0xffffff ); 47254 * spotLight.position.set( 100, 1000, 100 ); 47255 * spotLight.map = new THREE.TextureLoader().load( url ); 47256 * 47257 * spotLight.castShadow = true; 47258 * spotLight.shadow.mapSize.width = 1024; 47259 * spotLight.shadow.mapSize.height = 1024; 47260 * spotLight.shadow.camera.near = 500; 47261 * spotLight.shadow.camera.far = 4000; 47262 * spotLight.shadow.camera.fov = 30;s 47263 * ``` 47264 * 47265 * @augments Light 47266 */ 47267class SpotLight extends Light { 47268 47269 /** 47270 * Constructs a new spot light. 47271 * 47272 * @param {(number|Color|string)} [color=0xffffff] - The light's color. 47273 * @param {number} [intensity=1] - The light's strength/intensity measured in candela (cd). 47274 * @param {number} [distance=0] - Maximum range of the light. `0` means no limit. 47275 * @param {number} [angle=Math.PI/3] - Maximum angle of light dispersion from its direction whose upper bound is `Math.PI/2`. 47276 * @param {number} [penumbra=0] - Percent of the spotlight cone that is attenuated due to penumbra. Value range is `[0,1]`. 47277 * @param {number} [decay=2] - The amount the light dims along the distance of the light. 47278 */ 47279 constructor( color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 2 ) { 47280 47281 super( color, intensity ); 47282 47283 /** 47284 * This flag can be used for type testing. 47285 * 47286 * @type {boolean} 47287 * @readonly 47288 * @default true 47289 */ 47290 this.isSpotLight = true; 47291 47292 this.type = 'SpotLight'; 47293 47294 this.position.copy( Object3D.DEFAULT_UP ); 47295 this.updateMatrix(); 47296 47297 /** 47298 * The spot light points from its position to the 47299 * target's position. 47300 * 47301 * For the target's position to be changed to anything other 47302 * than the default, it must be added to the scene. 47303 * 47304 * It is also possible to set the target to be another 3D object 47305 * in the scene. The light will now track the target object. 47306 * 47307 * @type {Object3D} 47308 */ 47309 this.target = new Object3D(); 47310 47311 /** 47312 * Maximum range of the light. `0` means no limit. 47313 * 47314 * @type {number} 47315 * @default 0 47316 */ 47317 this.distance = distance; 47318 47319 /** 47320 * Maximum angle of light dispersion from its direction whose upper bound is `Math.PI/2`. 47321 * 47322 * @type {number} 47323 * @default Math.PI/3 47324 */ 47325 this.angle = angle; 47326 47327 /** 47328 * Percent of the spotlight cone that is attenuated due to penumbra. 47329 * Value range is `[0,1]`. 47330 * 47331 * @type {number} 47332 * @default 0 47333 */ 47334 this.penumbra = penumbra; 47335 47336 /** 47337 * The amount the light dims along the distance of the light. In context of 47338 * physically-correct rendering the default value should not be changed. 47339 * 47340 * @type {number} 47341 * @default 2 47342 */ 47343 this.decay = decay; 47344 47345 /** 47346 * A texture used to modulate the color of the light. The spot light 47347 * color is mixed with the RGB value of this texture, with a ratio 47348 * corresponding to its alpha value. The cookie-like masking effect is 47349 * reproduced using pixel values (0, 0, 0, 1-cookie_value). 47350 * 47351 * *Warning*: This property is disabled if {@link Object3D#castShadow} is set to `false`. 47352 * 47353 * @type {?Texture} 47354 * @default null 47355 */ 47356 this.map = null; 47357 47358 /** 47359 * This property holds the light's shadow configuration. 47360 * 47361 * @type {SpotLightShadow} 47362 */ 47363 this.shadow = new SpotLightShadow(); 47364 47365 } 47366 47367 /** 47368 * The light's power. Power is the luminous power of the light measured in lumens (lm). 47369 * Changing the power will also change the light's intensity. 47370 * 47371 * @type {number} 47372 */ 47373 get power() { 47374 47375 // compute the light's luminous power (in lumens) from its intensity (in candela) 47376 // by convention for a spotlight, luminous power (lm) = Ï * luminous intensity (cd) 47377 return this.intensity * Math.PI; 47378 47379 } 47380 47381 set power( power ) { 47382 47383 // set the light's intensity (in candela) from the desired luminous power (in lumens) 47384 this.intensity = power / Math.PI; 47385 47386 } 47387 47388 dispose() { 47389 47390 super.dispose(); 47391 47392 this.shadow.dispose(); 47393 47394 } 47395 47396 copy( source, recursive ) { 47397 47398 super.copy( source, recursive ); 47399 47400 this.distance = source.distance; 47401 this.angle = source.angle; 47402 this.penumbra = source.penumbra; 47403 this.decay = source.decay; 47404 47405 this.target = source.target.clone(); 47406 this.map = source.map; 47407 this.shadow = source.shadow.clone(); 47408 47409 return this; 47410 47411 } 47412 47413 toJSON( meta ) { 47414 47415 const data = super.toJSON( meta ); 47416 47417 data.object.distance = this.distance; 47418 data.object.angle = this.angle; 47419 data.object.decay = this.decay; 47420 data.object.penumbra = this.penumbra; 47421 47422 data.object.target = this.target.uuid; 47423 47424 if ( this.map && this.map.isTexture ) data.object.map = this.map.toJSON( meta ).uuid; 47425 47426 data.object.shadow = this.shadow.toJSON(); 47427 47428 return data; 47429 47430 } 47431 47432} 47433 47434/** 47435 * Represents the shadow configuration of point lights. 47436 * 47437 * @augments LightShadow 47438 */ 47439class PointLightShadow extends LightShadow { 47440 47441 /** 47442 * Constructs a new point light shadow. 47443 */ 47444 constructor() { 47445 47446 super( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); 47447 47448 /** 47449 * This flag can be used for type testing. 47450 * 47451 * @type {boolean} 47452 * @readonly 47453 * @default true 47454 */ 47455 this.isPointLightShadow = true; 47456 47457 } 47458 47459} 47460 47461/** 47462 * A light that gets emitted from a single point in all directions. A common 47463 * use case for this is to replicate the light emitted from a bare 47464 * lightbulb. 47465 * 47466 * This light can cast shadows - see the {@link PointLightShadow} for details. 47467 * 47468 * ```js 47469 * const light = new THREE.PointLight( 0xff0000, 1, 100 ); 47470 * light.position.set( 50, 50, 50 ); 47471 * scene.add( light ); 47472 * ``` 47473 * 47474 * @augments Light 47475 */ 47476class PointLight extends Light { 47477 47478 /** 47479 * Constructs a new point light. 47480 * 47481 * @param {(number|Color|string)} [color=0xffffff] - The light's color. 47482 * @param {number} [intensity=1] - The light's strength/intensity measured in candela (cd). 47483 * @param {number} [distance=0] - Maximum range of the light. `0` means no limit. 47484 * @param {number} [decay=2] - The amount the light dims along the distance of the light. 47485 */ 47486 constructor( color, intensity, distance = 0, decay = 2 ) { 47487 47488 super( color, intensity ); 47489 47490 /** 47491 * This flag can be used for type testing. 47492 * 47493 * @type {boolean} 47494 * @readonly 47495 * @default true 47496 */ 47497 this.isPointLight = true; 47498 47499 this.type = 'PointLight'; 47500 47501 /** 47502 * When distance is zero, light will attenuate according to inverse-square 47503 * law to infinite distance. When distance is non-zero, light will attenuate 47504 * according to inverse-square law until near the distance cutoff, where it 47505 * will then attenuate quickly and smoothly to 0. Inherently, cutoffs are not 47506 * physically correct. 47507 * 47508 * @type {number} 47509 * @default 0 47510 */ 47511 this.distance = distance; 47512 47513 /** 47514 * The amount the light dims along the distance of the light. In context of 47515 * physically-correct rendering the default value should not be changed. 47516 * 47517 * @type {number} 47518 * @default 2 47519 */ 47520 this.decay = decay; 47521 47522 /** 47523 * This property holds the light's shadow configuration. 47524 * 47525 * @type {PointLightShadow} 47526 */ 47527 this.shadow = new PointLightShadow(); 47528 47529 } 47530 47531 /** 47532 * The light's power. Power is the luminous power of the light measured in lumens (lm). 47533 * Changing the power will also change the light's intensity. 47534 * 47535 * @type {number} 47536 */ 47537 get power() { 47538 47539 // compute the light's luminous power (in lumens) from its intensity (in candela) 47540 // for an isotropic light source, luminous power (lm) = 4 Ï luminous intensity (cd) 47541 return this.intensity * 4 * Math.PI; 47542 47543 } 47544 47545 set power( power ) { 47546 47547 // set the light's intensity (in candela) from the desired luminous power (in lumens) 47548 this.intensity = power / ( 4 * Math.PI ); 47549 47550 } 47551 47552 dispose() { 47553 47554 super.dispose(); 47555 47556 this.shadow.dispose(); 47557 47558 } 47559 47560 copy( source, recursive ) { 47561 47562 super.copy( source, recursive ); 47563 47564 this.distance = source.distance; 47565 this.decay = source.decay; 47566 47567 this.shadow = source.shadow.clone(); 47568 47569 return this; 47570 47571 } 47572 47573 toJSON( meta ) { 47574 47575 const data = super.toJSON( meta ); 47576 47577 data.object.distance = this.distance; 47578 data.object.decay = this.decay; 47579 47580 data.object.shadow = this.shadow.toJSON(); 47581 47582 return data; 47583 47584 } 47585 47586} 47587 47588/** 47589 * Camera that uses [orthographic projection](https://en.wikipedia.org/wiki/Orthographic_projection). 47590 * 47591 * In this projection mode, an object's size in the rendered image stays 47592 * constant regardless of its distance from the camera. This can be useful 47593 * for rendering 2D scenes and UI elements, amongst other things. 47594 * 47595 * ```js 47596 * const camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 1000 ); 47597 * scene.add( camera ); 47598 * ``` 47599 * 47600 * @augments Camera 47601 */ 47602class OrthographicCamera extends Camera { 47603 47604 /** 47605 * Constructs a new orthographic camera. 47606 * 47607 * @param {number} [left=-1] - The left plane of the camera's frustum. 47608 * @param {number} [right=1] - The right plane of the camera's frustum. 47609 * @param {number} [top=1] - The top plane of the camera's frustum. 47610 * @param {number} [bottom=-1] - The bottom plane of the camera's frustum. 47611 * @param {number} [near=0.1] - The camera's near plane. 47612 * @param {number} [far=2000] - The camera's far plane. 47613 */ 47614 constructor( left = -1, right = 1, top = 1, bottom = -1, near = 0.1, far = 2000 ) { 47615 47616 super(); 47617 47618 /** 47619 * This flag can be used for type testing. 47620 * 47621 * @type {boolean} 47622 * @readonly 47623 * @default true 47624 */ 47625 this.isOrthographicCamera = true; 47626 47627 this.type = 'OrthographicCamera'; 47628 47629 /** 47630 * The zoom factor of the camera. 47631 * 47632 * @type {number} 47633 * @default 1 47634 */ 47635 this.zoom = 1; 47636 47637 /** 47638 * Represents the frustum window specification. This property should not be edited 47639 * directly but via {@link PerspectiveCamera#setViewOffset} and {@link PerspectiveCamera#clearViewOffset}. 47640 * 47641 * @type {?Object} 47642 * @default null 47643 */ 47644 this.view = null; 47645 47646 /** 47647 * The left plane of the camera's frustum. 47648 * 47649 * @type {number} 47650 * @default -1 47651 */ 47652 this.left = left; 47653 47654 /** 47655 * The right plane of the camera's frustum. 47656 * 47657 * @type {number} 47658 * @default 1 47659 */ 47660 this.right = right; 47661 47662 /** 47663 * The top plane of the camera's frustum. 47664 * 47665 * @type {number} 47666 * @default 1 47667 */ 47668 this.top = top; 47669 47670 /** 47671 * The bottom plane of the camera's frustum. 47672 * 47673 * @type {number} 47674 * @default -1 47675 */ 47676 this.bottom = bottom; 47677 47678 /** 47679 * The camera's near plane. The valid range is greater than `0` 47680 * and less than the current value of {@link OrthographicCamera#far}. 47681 * 47682 * Note that, unlike for the {@link PerspectiveCamera}, `0` is a 47683 * valid value for an orthographic camera's near plane. 47684 * 47685 * @type {number} 47686 * @default 0.1 47687 */ 47688 this.near = near; 47689 47690 /** 47691 * The camera's far plane. Must be greater than the 47692 * current value of {@link OrthographicCamera#near}. 47693 * 47694 * @type {number} 47695 * @default 2000 47696 */ 47697 this.far = far; 47698 47699 this.updateProjectionMatrix(); 47700 47701 } 47702 47703 copy( source, recursive ) { 47704 47705 super.copy( source, recursive ); 47706 47707 this.left = source.left; 47708 this.right = source.right; 47709 this.top = source.top; 47710 this.bottom = source.bottom; 47711 this.near = source.near; 47712 this.far = source.far; 47713 47714 this.zoom = source.zoom; 47715 this.view = source.view === null ? null : Object.assign( {}, source.view ); 47716 47717 return this; 47718 47719 } 47720 47721 /** 47722 * Sets an offset in a larger frustum. This is useful for multi-window or 47723 * multi-monitor/multi-machine setups. 47724 * 47725 * @param {number} fullWidth - The full width of multiview setup. 47726 * @param {number} fullHeight - The full height of multiview setup. 47727 * @param {number} x - The horizontal offset of the subcamera. 47728 * @param {number} y - The vertical offset of the subcamera. 47729 * @param {number} width - The width of subcamera. 47730 * @param {number} height - The height of subcamera. 47731 * @see {@link PerspectiveCamera#setViewOffset} 47732 */ 47733 setViewOffset( fullWidth, fullHeight, x, y, width, height ) { 47734 47735 if ( this.view === null ) { 47736 47737 this.view = { 47738 enabled: true, 47739 fullWidth: 1, 47740 fullHeight: 1, 47741 offsetX: 0, 47742 offsetY: 0, 47743 width: 1, 47744 height: 1 47745 }; 47746 47747 } 47748 47749 this.view.enabled = true; 47750 this.view.fullWidth = fullWidth; 47751 this.view.fullHeight = fullHeight; 47752 this.view.offsetX = x; 47753 this.view.offsetY = y; 47754 this.view.width = width; 47755 this.view.height = height; 47756 47757 this.updateProjectionMatrix(); 47758 47759 } 47760 47761 /** 47762 * Removes the view offset from the projection matrix. 47763 */ 47764 clearViewOffset() { 47765 47766 if ( this.view !== null ) { 47767 47768 this.view.enabled = false; 47769 47770 } 47771 47772 this.updateProjectionMatrix(); 47773 47774 } 47775 47776 /** 47777 * Updates the camera's projection matrix. Must be called after any change of 47778 * camera properties. 47779 */ 47780 updateProjectionMatrix() { 47781 47782 const dx = ( this.right - this.left ) / ( 2 * this.zoom ); 47783 const dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); 47784 const cx = ( this.right + this.left ) / 2; 47785 const cy = ( this.top + this.bottom ) / 2; 47786 47787 let left = cx - dx; 47788 let right = cx + dx; 47789 let top = cy + dy; 47790 let bottom = cy - dy; 47791 47792 if ( this.view !== null && this.view.enabled ) { 47793 47794 const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; 47795 const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; 47796 47797 left += scaleW * this.view.offsetX; 47798 right = left + scaleW * this.view.width; 47799 top -= scaleH * this.view.offsetY; 47800 bottom = top - scaleH * this.view.height; 47801 47802 } 47803 47804 this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far, this.coordinateSystem, this.reversedDepth ); 47805 47806 this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); 47807 47808 } 47809 47810 toJSON( meta ) { 47811 47812 const data = super.toJSON( meta ); 47813 47814 data.object.zoom = this.zoom; 47815 data.object.left = this.left; 47816 data.object.right = this.right; 47817 data.object.top = this.top; 47818 data.object.bottom = this.bottom; 47819 data.object.near = this.near; 47820 data.object.far = this.far; 47821 47822 if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); 47823 47824 return data; 47825 47826 } 47827 47828} 47829 47830/** 47831 * Represents the shadow configuration of directional lights. 47832 * 47833 * @augments LightShadow 47834 */ 47835class DirectionalLightShadow extends LightShadow { 47836 47837 /** 47838 * Constructs a new directional light shadow. 47839 */ 47840 constructor() { 47841 47842 super( new OrthographicCamera( -5, 5, 5, -5, 0.5, 500 ) ); 47843 47844 /** 47845 * This flag can be used for type testing. 47846 * 47847 * @type {boolean} 47848 * @readonly 47849 * @default true 47850 */ 47851 this.isDirectionalLightShadow = true; 47852 47853 } 47854 47855} 47856 47857/** 47858 * A light that gets emitted in a specific direction. This light will behave 47859 * as though it is infinitely far away and the rays produced from it are all 47860 * parallel. The common use case for this is to simulate daylight; the sun is 47861 * far enough away that its position can be considered to be infinite, and 47862 * all light rays coming from it are parallel. 47863 * 47864 * A common point of confusion for directional lights is that setting the 47865 * rotation has no effect. This is because three.js's DirectionalLight is the 47866 * equivalent to what is often called a 'Target Direct Light' in other 47867 * applications. 47868 * 47869 * This means that its direction is calculated as pointing from the light's 47870 * {@link Object3D#position} to the {@link DirectionalLight#target} position 47871 * (as opposed to a 'Free Direct Light' that just has a rotation 47872 * component). 47873 * 47874 * This light can cast shadows - see the {@link DirectionalLightShadow} for details. 47875 * 47876 * ```js 47877 * // White directional light at half intensity shining from the top. 47878 * const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 ); 47879 * scene.add( directionalLight ); 47880 * ``` 47881 * 47882 * @augments Light 47883 */ 47884class DirectionalLight extends Light { 47885 47886 /** 47887 * Constructs a new directional light. 47888 * 47889 * @param {(number|Color|string)} [color=0xffffff] - The light's color. 47890 * @param {number} [intensity=1] - The light's strength/intensity. 47891 */ 47892 constructor( color, intensity ) { 47893 47894 super( color, intensity ); 47895 47896 /** 47897 * This flag can be used for type testing. 47898 * 47899 * @type {boolean} 47900 * @readonly 47901 * @default true 47902 */ 47903 this.isDirectionalLight = true; 47904 47905 this.type = 'DirectionalLight'; 47906 47907 this.position.copy( Object3D.DEFAULT_UP ); 47908 this.updateMatrix(); 47909 47910 /** 47911 * The directional light points from its position to the 47912 * target's position. 47913 * 47914 * For the target's position to be changed to anything other 47915 * than the default, it must be added to the scene. 47916 * 47917 * It is also possible to set the target to be another 3D object 47918 * in the scene. The light will now track the target object. 47919 * 47920 * @type {Object3D} 47921 */ 47922 this.target = new Object3D(); 47923 47924 /** 47925 * This property holds the light's shadow configuration. 47926 * 47927 * @type {DirectionalLightShadow} 47928 */ 47929 this.shadow = new DirectionalLightShadow(); 47930 47931 } 47932 47933 dispose() { 47934 47935 super.dispose(); 47936 47937 this.shadow.dispose(); 47938 47939 } 47940 47941 copy( source ) { 47942 47943 super.copy( source ); 47944 47945 this.target = source.target.clone(); 47946 this.shadow = source.shadow.clone(); 47947 47948 return this; 47949 47950 } 47951 47952 toJSON( meta ) { 47953 47954 const data = super.toJSON( meta ); 47955 47956 data.object.shadow = this.shadow.toJSON(); 47957 data.object.target = this.target.uuid; 47958 47959 return data; 47960 47961 } 47962 47963} 47964 47965/** 47966 * This light globally illuminates all objects in the scene equally. 47967 * 47968 * It cannot be used to cast shadows as it does not have a direction. 47969 * 47970 * ```js 47971 * const light = new THREE.AmbientLight( 0x404040 ); // soft white light 47972 * scene.add( light ); 47973 * ``` 47974 * 47975 * @augments Light 47976 */ 47977class AmbientLight extends Light { 47978 47979 /** 47980 * Constructs a new ambient light. 47981 * 47982 * @param {(number|Color|string)} [color=0xffffff] - The light's color. 47983 * @param {number} [intensity=1] - The light's strength/intensity. 47984 */ 47985 constructor( color, intensity ) { 47986 47987 super( color, intensity ); 47988 47989 /** 47990 * This flag can be used for type testing. 47991 * 47992 * @type {boolean} 47993 * @readonly 47994 * @default true 47995 */ 47996 this.isAmbientLight = true; 47997 47998 this.type = 'AmbientLight'; 47999 48000 } 48001 48002} 48003 48004/** 48005 * This class emits light uniformly across the face a rectangular plane. 48006 * This light type can be used to simulate light sources such as bright 48007 * windows or strip lighting. 48008 * 48009 * Important Notes: 48010 * 48011 * - There is no shadow support. 48012 * - Only PBR materials are supported. 48013 * - You have to include `RectAreaLightUniformsLib` (`WebGLRenderer`) or `RectAreaLightTexturesLib` (`WebGPURenderer`) 48014 * into your app and init the uniforms/textures. 48015 * 48016 * ```js 48017 * RectAreaLightUniformsLib.init(); // only relevant for WebGLRenderer 48018 * THREE.RectAreaLightNode.setLTC( RectAreaLightTexturesLib.init() ); // only relevant for WebGPURenderer 48019 * 48020 * const intensity = 1; const width = 10; const height = 10; 48021 * const rectLight = new THREE.RectAreaLight( 0xffffff, intensity, width, height ); 48022 * rectLight.position.set( 5, 5, 0 ); 48023 * rectLight.lookAt( 0, 0, 0 ); 48024 * scene.add( rectLight ) 48025 * ``` 48026 * 48027 * @augments Light 48028 */ 48029class RectAreaLight extends Light { 48030 48031 /** 48032 * Constructs a new area light. 48033 * 48034 * @param {(number|Color|string)} [color=0xffffff] - The light's color. 48035 * @param {number} [intensity=1] - The light's strength/intensity. 48036 * @param {number} [width=10] - The width of the light. 48037 * @param {number} [height=10] - The height of the light. 48038 */ 48039 constructor( color, intensity, width = 10, height = 10 ) { 48040 48041 super( color, intensity ); 48042 48043 /** 48044 * This flag can be used for type testing. 48045 * 48046 * @type {boolean} 48047 * @readonly 48048 * @default true 48049 */ 48050 this.isRectAreaLight = true; 48051 48052 this.type = 'RectAreaLight'; 48053 48054 /** 48055 * The width of the light. 48056 * 48057 * @type {number} 48058 * @default 10 48059 */ 48060 this.width = width; 48061 48062 /** 48063 * The height of the light. 48064 * 48065 * @type {number} 48066 * @default 10 48067 */ 48068 this.height = height; 48069 48070 } 48071 48072 /** 48073 * The light's power. Power is the luminous power of the light measured in lumens (lm). 48074 * Changing the power will also change the light's intensity. 48075 * 48076 * @type {number} 48077 */ 48078 get power() { 48079 48080 // compute the light's luminous power (in lumens) from its intensity (in nits) 48081 return this.intensity * this.width * this.height * Math.PI; 48082 48083 } 48084 48085 set power( power ) { 48086 48087 // set the light's intensity (in nits) from the desired luminous power (in lumens) 48088 this.intensity = power / ( this.width * this.height * Math.PI ); 48089 48090 } 48091 48092 copy( source ) { 48093 48094 super.copy( source ); 48095 48096 this.width = source.width; 48097 this.height = source.height; 48098 48099 return this; 48100 48101 } 48102 48103 toJSON( meta ) { 48104 48105 const data = super.toJSON( meta ); 48106 48107 data.object.width = this.width; 48108 data.object.height = this.height; 48109 48110 return data; 48111 48112 } 48113 48114} 48115 48116/** 48117 * Represents a third-order spherical harmonics (SH). Light probes use this class 48118 * to encode lighting information. 48119 * 48120 * - Primary reference: {@link https://graphics.stanford.edu/papers/envmap/envmap.pdf} 48121 * - Secondary reference: {@link https://www.ppsloan.org/publications/StupidSH36.pdf} 48122 */ 48123class SphericalHarmonics3 { 48124 48125 /** 48126 * Constructs a new spherical harmonics. 48127 */ 48128 constructor() { 48129 48130 /** 48131 * This flag can be used for type testing. 48132 * 48133 * @type {boolean} 48134 * @readonly 48135 * @default true 48136 */ 48137 this.isSphericalHarmonics3 = true; 48138 48139 /** 48140 * An array holding the (9) SH coefficients. 48141 * 48142 * @type {Array<Vector3>} 48143 */ 48144 this.coefficients = []; 48145 48146 for ( let i = 0; i < 9; i ++ ) { 48147 48148 this.coefficients.push( new Vector3() ); 48149 48150 } 48151 48152 } 48153 48154 /** 48155 * Sets the given SH coefficients to this instance by copying 48156 * the values. 48157 * 48158 * @param {Array<Vector3>} coefficients - The SH coefficients. 48159 * @return {SphericalHarmonics3} A reference to this spherical harmonics. 48160 */ 48161 set( coefficients ) { 48162 48163 for ( let i = 0; i < 9; i ++ ) { 48164 48165 this.coefficients[ i ].copy( coefficients[ i ] ); 48166 48167 } 48168 48169 return this; 48170 48171 } 48172 48173 /** 48174 * Sets all SH coefficients to `0`. 48175 * 48176 * @return {SphericalHarmonics3} A reference to this spherical harmonics. 48177 */ 48178 zero() { 48179 48180 for ( let i = 0; i < 9; i ++ ) { 48181 48182 this.coefficients[ i ].set( 0, 0, 0 ); 48183 48184 } 48185 48186 return this; 48187 48188 } 48189 48190 /** 48191 * Returns the radiance in the direction of the given normal. 48192 * 48193 * @param {Vector3} normal - The normal vector (assumed to be unit length) 48194 * @param {Vector3} target - The target vector that is used to store the method's result. 48195 * @return {Vector3} The radiance. 48196 */ 48197 getAt( normal, target ) { 48198 48199 // normal is assumed to be unit length 48200 48201 const x = normal.x, y = normal.y, z = normal.z; 48202 48203 const coeff = this.coefficients; 48204 48205 // band 0 48206 target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 ); 48207 48208 // band 1 48209 target.addScaledVector( coeff[ 1 ], 0.488603 * y ); 48210 target.addScaledVector( coeff[ 2 ], 0.488603 * z ); 48211 target.addScaledVector( coeff[ 3 ], 0.488603 * x ); 48212 48213 // band 2 48214 target.addScaledVector( coeff[ 4 ], 1.092548 * ( x * y ) ); 48215 target.addScaledVector( coeff[ 5 ], 1.092548 * ( y * z ) ); 48216 target.addScaledVector( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) ); 48217 target.addScaledVector( coeff[ 7 ], 1.092548 * ( x * z ) ); 48218 target.addScaledVector( coeff[ 8 ], 0.546274 * ( x * x - y * y ) ); 48219 48220 return target; 48221 48222 } 48223 48224 /** 48225 * Returns the irradiance (radiance convolved with cosine lobe) in the 48226 * direction of the given normal. 48227 * 48228 * @param {Vector3} normal - The normal vector (assumed to be unit length) 48229 * @param {Vector3} target - The target vector that is used to store the method's result. 48230 * @return {Vector3} The irradiance. 48231 */ 48232 getIrradianceAt( normal, target ) { 48233 48234 // normal is assumed to be unit length 48235 48236 const x = normal.x, y = normal.y, z = normal.z; 48237 48238 const coeff = this.coefficients; 48239 48240 // band 0 48241 target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // Ï * 0.282095 48242 48243 // band 1 48244 target.addScaledVector( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * Ï / 3 ) * 0.488603 48245 target.addScaledVector( coeff[ 2 ], 2.0 * 0.511664 * z ); 48246 target.addScaledVector( coeff[ 3 ], 2.0 * 0.511664 * x ); 48247 48248 // band 2 48249 target.addScaledVector( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( Ï / 4 ) * 1.092548 48250 target.addScaledVector( coeff[ 5 ], 2.0 * 0.429043 * y * z ); 48251 target.addScaledVector( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( Ï / 4 ) * 0.315392 * 3 48252 target.addScaledVector( coeff[ 7 ], 2.0 * 0.429043 * x * z ); 48253 target.addScaledVector( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( Ï / 4 ) * 0.546274 48254 48255 return target; 48256 48257 } 48258 48259 /** 48260 * Adds the given SH to this instance. 48261 * 48262 * @param {SphericalHarmonics3} sh - The SH to add. 48263 * @return {SphericalHarmonics3} A reference to this spherical harmonics. 48264 */ 48265 add( sh ) { 48266 48267 for ( let i = 0; i < 9; i ++ ) { 48268 48269 this.coefficients[ i ].add( sh.coefficients[ i ] ); 48270 48271 } 48272 48273 return this; 48274 48275 } 48276 48277 /** 48278 * A convenience method for performing {@link SphericalHarmonics3#add} and 48279 * {@link SphericalHarmonics3#scale} at once. 48280 * 48281 * @param {SphericalHarmonics3} sh - The SH to add. 48282 * @param {number} s - The scale factor. 48283 * @return {SphericalHarmonics3} A reference to this spherical harmonics. 48284 */ 48285 addScaledSH( sh, s ) { 48286 48287 for ( let i = 0; i < 9; i ++ ) { 48288 48289 this.coefficients[ i ].addScaledVector( sh.coefficients[ i ], s ); 48290 48291 } 48292 48293 return this; 48294 48295 } 48296 48297 /** 48298 * Scales this SH by the given scale factor. 48299 * 48300 * @param {number} s - The scale factor. 48301 * @return {SphericalHarmonics3} A reference to this spherical harmonics. 48302 */ 48303 scale( s ) { 48304 48305 for ( let i = 0; i < 9; i ++ ) { 48306 48307 this.coefficients[ i ].multiplyScalar( s ); 48308 48309 } 48310 48311 return this; 48312 48313 } 48314 48315 /** 48316 * Linear interpolates between the given SH and this instance by the given 48317 * alpha factor. 48318 * 48319 * @param {SphericalHarmonics3} sh - The SH to interpolate with. 48320 * @param {number} alpha - The alpha factor. 48321 * @return {SphericalHarmonics3} A reference to this spherical harmonics. 48322 */ 48323 lerp( sh, alpha ) { 48324 48325 for ( let i = 0; i < 9; i ++ ) { 48326 48327 this.coefficients[ i ].lerp( sh.coefficients[ i ], alpha ); 48328 48329 } 48330 48331 return this; 48332 48333 } 48334 48335 /** 48336 * Returns `true` if this spherical harmonics is equal with the given one. 48337 * 48338 * @param {SphericalHarmonics3} sh - The spherical harmonics to test for equality. 48339 * @return {boolean} Whether this spherical harmonics is equal with the given one. 48340 */ 48341 equals( sh ) { 48342 48343 for ( let i = 0; i < 9; i ++ ) { 48344 48345 if ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) { 48346 48347 return false; 48348 48349 } 48350 48351 } 48352 48353 return true; 48354 48355 } 48356 48357 /** 48358 * Copies the values of the given spherical harmonics to this instance. 48359 * 48360 * @param {SphericalHarmonics3} sh - The spherical harmonics to copy. 48361 * @return {SphericalHarmonics3} A reference to this spherical harmonics. 48362 */ 48363 copy( sh ) { 48364 48365 return this.set( sh.coefficients ); 48366 48367 } 48368 48369 /** 48370 * Returns a new spherical harmonics with copied values from this instance. 48371 * 48372 * @return {SphericalHarmonics3} A clone of this instance. 48373 */ 48374 clone() { 48375 48376 return new this.constructor().copy( this ); 48377 48378 } 48379 48380 /** 48381 * Sets the SH coefficients of this instance from the given array. 48382 * 48383 * @param {Array<number>} array - An array holding the SH coefficients. 48384 * @param {number} [offset=0] - The array offset where to start copying. 48385 * @return {SphericalHarmonics3} A clone of this instance. 48386 */ 48387 fromArray( array, offset = 0 ) { 48388 48389 const coefficients = this.coefficients; 48390 48391 for ( let i = 0; i < 9; i ++ ) { 48392 48393 coefficients[ i ].fromArray( array, offset + ( i * 3 ) ); 48394 48395 } 48396 48397 return this; 48398 48399 } 48400 48401 /** 48402 * Returns an array with the SH coefficients, or copies them into the provided 48403 * array. The coefficients are represented as numbers. 48404 * 48405 * @param {Array<number>} [array=[]] - The target array. 48406 * @param {number} [offset=0] - The array offset where to start copying. 48407 * @return {Array<number>} An array with flat SH coefficients. 48408 */ 48409 toArray( array = [], offset = 0 ) { 48410 48411 const coefficients = this.coefficients; 48412 48413 for ( let i = 0; i < 9; i ++ ) { 48414 48415 coefficients[ i ].toArray( array, offset + ( i * 3 ) ); 48416 48417 } 48418 48419 return array; 48420 48421 } 48422 48423 /** 48424 * Computes the SH basis for the given normal vector. 48425 * 48426 * @param {Vector3} normal - The normal. 48427 * @param {Array<number>} shBasis - The target array holding the SH basis. 48428 */ 48429 static getBasisAt( normal, shBasis ) { 48430 48431 // normal is assumed to be unit length 48432 48433 const x = normal.x, y = normal.y, z = normal.z; 48434 48435 // band 0 48436 shBasis[ 0 ] = 0.282095; 48437 48438 // band 1 48439 shBasis[ 1 ] = 0.488603 * y; 48440 shBasis[ 2 ] = 0.488603 * z; 48441 shBasis[ 3 ] = 0.488603 * x; 48442 48443 // band 2 48444 shBasis[ 4 ] = 1.092548 * x * y; 48445 shBasis[ 5 ] = 1.092548 * y * z; 48446 shBasis[ 6 ] = 0.315392 * ( 3 * z * z - 1 ); 48447 shBasis[ 7 ] = 1.092548 * x * z; 48448 shBasis[ 8 ] = 0.546274 * ( x * x - y * y ); 48449 48450 } 48451 48452} 48453 48454/** 48455 * Light probes are an alternative way of adding light to a 3D scene. Unlike 48456 * classical light sources (e.g. directional, point or spot lights), light 48457 * probes do not emit light. Instead they store information about light 48458 * passing through 3D space. During rendering, the light that hits a 3D 48459 * object is approximated by using the data from the light probe. 48460 * 48461 * Light probes are usually created from (radiance) environment maps. The 48462 * class {@link LightProbeGenerator} can be used to create light probes from 48463 * cube textures or render targets. However, light estimation data could also 48464 * be provided in other forms e.g. by WebXR. This enables the rendering of 48465 * augmented reality content that reacts to real world lighting. 48466 * 48467 * The current probe implementation in three.js supports so-called diffuse 48468 * light probes. This type of light probe is functionally equivalent to an 48469 * irradiance environment map. 48470 * 48471 * @augments Light 48472 */ 48473class LightProbe extends Light { 48474 48475 /** 48476 * Constructs a new light probe. 48477 * 48478 * @param {SphericalHarmonics3} sh - The spherical harmonics which represents encoded lighting information. 48479 * @param {number} [intensity=1] - The light's strength/intensity. 48480 */ 48481 constructor( sh = new SphericalHarmonics3(), intensity = 1 ) { 48482 48483 super( undefined, intensity ); 48484 48485 /** 48486 * This flag can be used for type testing. 48487 * 48488 * @type {boolean} 48489 * @readonly 48490 * @default true 48491 */ 48492 this.isLightProbe = true; 48493 48494 /** 48495 * A light probe uses spherical harmonics to encode lighting information. 48496 * 48497 * @type {SphericalHarmonics3} 48498 */ 48499 this.sh = sh; 48500 48501 } 48502 48503 copy( source ) { 48504 48505 super.copy( source ); 48506 48507 this.sh.copy( source.sh ); 48508 48509 return this; 48510 48511 } 48512 48513 toJSON( meta ) { 48514 48515 const data = super.toJSON( meta ); 48516 48517 data.object.sh = this.sh.toArray(); 48518 48519 return data; 48520 48521 } 48522 48523} 48524 48525const _customMaterials = {}; 48526 48527/** 48528 * Class for loading materials. The files are internally 48529 * loaded via {@link FileLoader}. 48530 * 48531 * ```js 48532 * const loader = new THREE.MaterialLoader(); 48533 * const material = await loader.loadAsync( 'material.json' ); 48534 * ``` 48535 * This loader does not support node materials. Use {@link NodeMaterialLoader} instead. 48536 * 48537 * @augments Loader 48538 */ 48539class MaterialLoader extends Loader { 48540 48541 /** 48542 * Constructs a new material loader. 48543 * 48544 * @param {LoadingManager} [manager] - The loading manager. 48545 */ 48546 constructor( manager ) { 48547 48548 super( manager ); 48549 48550 /** 48551 * A dictionary holding textures used by the material. 48552 * 48553 * @type {Object<string,Texture>} 48554 */ 48555 this.textures = {}; 48556 48557 } 48558 48559 /** 48560 * Starts loading from the given URL and pass the loaded material to the `onLoad()` callback. 48561 * 48562 * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI. 48563 * @param {function(Material)} onLoad - Executed when the loading process has been finished. 48564 * @param {onProgressCallback} onProgress - Executed while the loading is in progress. 48565 * @param {onErrorCallback} onError - Executed when errors occur. 48566 */ 48567 load( url, onLoad, onProgress, onError ) { 48568 48569 const scope = this; 48570 48571 const loader = new FileLoader( scope.manager ); 48572 loader.setPath( scope.path ); 48573 loader.setRequestHeader( scope.requestHeader ); 48574 loader.setWithCredentials( scope.withCredentials ); 48575 loader.load( url, function ( text ) { 48576 48577 try { 48578 48579 onLoad( scope.parse( JSON.parse( text ) ) ); 48580 48581 } catch ( e ) { 48582 48583 if ( onError ) { 48584 48585 onError( e ); 48586 48587 } else { 48588 48589 error( e ); 48590 48591 } 48592 48593 scope.manager.itemError( url ); 48594 48595 } 48596 48597 }, onProgress, onError ); 48598 48599 } 48600 48601 /** 48602 * Parses the given JSON object and returns a material. 48603 * 48604 * @param {Object} json - The serialized material. 48605 * @return {Material} The parsed material. 48606 */ 48607 parse( json ) { 48608 48609 const material = this.createMaterialFromType( json.type ); 48610 48611 material.fromJSON( json, this.textures ); 48612 48613 return material; 48614 48615 } 48616 48617 /** 48618 * Textures are not embedded in the material JSON so they have 48619 * to be injected before the loading process starts. 48620 * 48621 * @param {Object} value - A dictionary holding textures for material properties. 48622 * @return {MaterialLoader} A reference to this material loader. 48623 */ 48624 setTextures( value ) { 48625 48626 this.textures = value; 48627 return this; 48628 48629 } 48630 48631 /** 48632 * Creates a material for the given type. 48633 * 48634 * @param {string} type - The material type. 48635 * @return {Material} The new material. 48636 */ 48637 createMaterialFromType( type ) { 48638 48639 return MaterialLoader.createMaterialFromType( type ); 48640 48641 } 48642 48643 /** 48644 * Creates a material for the given type. 48645 * 48646 * @static 48647 * @param {string} type - The material type. 48648 * @return {Material} The new material. 48649 */ 48650 static createMaterialFromType( type ) { 48651 48652 const materialLib = { 48653 ShadowMaterial, 48654 SpriteMaterial, 48655 RawShaderMaterial, 48656 ShaderMaterial, 48657 PointsMaterial, 48658 MeshPhysicalMaterial, 48659 MeshStandardMaterial, 48660 MeshPhongMaterial, 48661 MeshToonMaterial, 48662 MeshNormalMaterial, 48663 MeshLambertMaterial, 48664 MeshDepthMaterial, 48665 MeshDistanceMaterial, 48666 MeshBasicMaterial, 48667 MeshMatcapMaterial, 48668 LineDashedMaterial, 48669 LineBasicMaterial, 48670 Material, 48671 ... _customMaterials 48672 }; 48673 48674 const MaterialType = materialLib[ type ]; 48675 48676 let materialInstance; 48677 48678 if ( MaterialType === undefined ) { 48679 48680 warnOnce( `MaterialLoader: Unknown material type "${ type }". Use .registerMaterial() before starting the deserialization process.` ); 48681 materialInstance = new Material(); 48682 48683 } else { 48684 48685 materialInstance = new MaterialType(); 48686 48687 } 48688 48689 return materialInstance; 48690 48691 } 48692 48693 /** 48694 * Registers the given material at the internal 48695 * material library. 48696 * 48697 * @static 48698 * @param {string} type - The material type. 48699 * @param {Material.constructor} materialClass - The material class. 48700 */ 48701 static registerMaterial( type, materialClass ) { 48702 48703 _customMaterials[ type ] = materialClass; 48704 48705 } 48706 48707} 48708 48709/** 48710 * A class with loader utility functions. 48711 */ 48712class LoaderUtils { 48713 48714 /** 48715 * Extracts the base URL from the given URL. 48716 * 48717 * @param {string} url -The URL to extract the base URL from. 48718 * @return {string} The extracted base URL. 48719 */ 48720 static extractUrlBase( url ) { 48721 48722 const index = url.lastIndexOf( '/' ); 48723 48724 if ( index === -1 ) return './'; 48725 48726 return url.slice( 0, index + 1 ); 48727 48728 } 48729 48730 /** 48731 * Resolves relative URLs against the given path. Absolute paths, data urls, 48732 * and blob URLs will be returned as is. Invalid URLs will return an empty 48733 * string. 48734 * 48735 * @param {string} url -The URL to resolve. 48736 * @param {string} path - The base path for relative URLs to be resolved against. 48737 * @return {string} The resolved URL. 48738 */ 48739 static resolveURL( url, path ) { 48740 48741 // Invalid URL 48742 if ( typeof url !== 'string' || url === '' ) return ''; 48743 48744 // Host Relative URL 48745 if ( /^https?:\/\//i.test( path ) && /^\//.test( url ) ) { 48746 48747 path = path.replace( /(^https?:\/\/[^\/]+).*/i, '$1' ); 48748 48749 } 48750 48751 // Absolute URL http://,https://,// 48752 if ( /^(https?:)?\/\//i.test( url ) ) return url; 48753 48754 // Data URI 48755 if ( /^data:.*,.*$/i.test( url ) ) return url; 48756 48757 // Blob URL 48758 if ( /^blob:.*$/i.test( url ) ) return url; 48759 48760 // Relative URL 48761 return path + url; 48762 48763 } 48764 48765} 48766 48767/** 48768 * An instanced version of a geometry. 48769 */ 48770class InstancedBufferGeometry extends BufferGeometry { 48771 48772 /** 48773 * Constructs a new instanced buffer geometry. 48774 */ 48775 constructor() { 48776 48777 super(); 48778 48779 /** 48780 * This flag can be used for type testing. 48781 * 48782 * @type {boolean} 48783 * @readonly 48784 * @default true 48785 */ 48786 this.isInstancedBufferGeometry = true; 48787 48788 this.type = 'InstancedBufferGeometry'; 48789 48790 /** 48791 * The instance count. 48792 * 48793 * @type {number} 48794 * @default Infinity 48795 */ 48796 this.instanceCount = Infinity; 48797 48798 } 48799 48800 copy( source ) { 48801 48802 super.copy( source ); 48803 48804 this.instanceCount = source.instanceCount; 48805 48806 return this; 48807 48808 } 48809 48810 toJSON() { 48811 48812 const data = super.toJSON(); 48813 48814 data.instanceCount = this.instanceCount; 48815 48816 data.isInstancedBufferGeometry = true; 48817 48818 return data; 48819 48820 } 48821 48822} 48823 48824/** 48825 * Class for loading geometries. The files are internally 48826 * loaded via {@link FileLoader}. 48827 * 48828 * ```js 48829 * const loader = new THREE.BufferGeometryLoader(); 48830 * const geometry = await loader.loadAsync( 'models/json/pressure.json' ); 48831 * 48832 * const material = new THREE.MeshBasicMaterial( { color: 0xF5F5F5 } ); 48833 * const object = new THREE.Mesh( geometry, material ); 48834 * scene.add( object ); 48835 * ``` 48836 * 48837 * @augments Loader 48838 */ 48839class BufferGeometryLoader extends Loader { 48840 48841 /** 48842 * Constructs a new geometry loader. 48843 * 48844 * @param {LoadingManager} [manager] - The loading manager. 48845 */ 48846 constructor( manager ) { 48847 48848 super( manager ); 48849 48850 } 48851 48852 /** 48853 * Starts loading from the given URL and pass the loaded geometry to the `onLoad()` callback. 48854 * 48855 * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI. 48856 * @param {function(BufferGeometry)} onLoad - Executed when the loading process has been finished. 48857 * @param {onProgressCallback} onProgress - Executed while the loading is in progress. 48858 * @param {onErrorCallback} onError - Executed when errors occur. 48859 */ 48860 load( url, onLoad, onProgress, onError ) { 48861 48862 const scope = this; 48863 48864 const loader = new FileLoader( scope.manager ); 48865 loader.setPath( scope.path ); 48866 loader.setRequestHeader( scope.requestHeader ); 48867 loader.setWithCredentials( scope.withCredentials ); 48868 loader.load( url, function ( text ) { 48869 48870 try { 48871 48872 onLoad( scope.parse( JSON.parse( text ) ) ); 48873 48874 } catch ( e ) { 48875 48876 if ( onError ) { 48877 48878 onError( e ); 48879 48880 } else { 48881 48882 error( e ); 48883 48884 } 48885 48886 scope.manager.itemError( url ); 48887 48888 } 48889 48890 }, onProgress, onError ); 48891 48892 } 48893 48894 /** 48895 * Parses the given JSON object and returns a geometry. 48896 * 48897 * @param {Object} json - The serialized geometry. 48898 * @return {BufferGeometry} The parsed geometry. 48899 */ 48900 parse( json ) { 48901 48902 const interleavedBufferMap = {}; 48903 const arrayBufferMap = {}; 48904 48905 function getInterleavedBuffer( json, uuid ) { 48906 48907 if ( interleavedBufferMap[ uuid ] !== undefined ) return interleavedBufferMap[ uuid ]; 48908 48909 const interleavedBuffers = json.interleavedBuffers; 48910 const interleavedBuffer = interleavedBuffers[ uuid ]; 48911 48912 const buffer = getArrayBuffer( json, interleavedBuffer.buffer ); 48913 48914 const array = getTypedArray( interleavedBuffer.type, buffer ); 48915 const ib = new InterleavedBuffer( array, interleavedBuffer.stride ); 48916 ib.uuid = interleavedBuffer.uuid; 48917 48918 if ( interleavedBuffer.usage !== undefined ) ib.setUsage( interleavedBuffer.usage ); 48919 48920 interleavedBufferMap[ uuid ] = ib; 48921 48922 return ib; 48923 48924 } 48925 48926 function getArrayBuffer( json, uuid ) { 48927 48928 if ( arrayBufferMap[ uuid ] !== undefined ) return arrayBufferMap[ uuid ]; 48929 48930 const arrayBuffers = json.arrayBuffers; 48931 const arrayBuffer = arrayBuffers[ uuid ]; 48932 48933 const ab = new Uint32Array( arrayBuffer ).buffer; 48934 48935 arrayBufferMap[ uuid ] = ab; 48936 48937 return ab; 48938 48939 } 48940 48941 const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); 48942 48943 const index = json.data.index; 48944 48945 if ( index !== undefined ) { 48946 48947 const typedArray = getTypedArray( index.type, index.array ); 48948 geometry.setIndex( new BufferAttribute( typedArray, 1 ) ); 48949 48950 } 48951 48952 const attributes = json.data.attributes; 48953 48954 for ( const key in attributes ) { 48955 48956 const attribute = attributes[ key ]; 48957 let bufferAttribute; 48958 48959 if ( attribute.isInterleavedBufferAttribute ) { 48960 48961 const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data ); 48962 bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized ); 48963 48964 } else { 48965 48966 const typedArray = getTypedArray( attribute.type, attribute.array ); 48967 const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; 48968 bufferAttribute = new bufferAttributeConstr( typedArray, attribute.itemSize, attribute.normalized ); 48969 48970 } 48971 48972 if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; 48973 if ( attribute.usage !== undefined ) bufferAttribute.setUsage( attribute.usage ); 48974 if ( attribute.gpuType !== undefined ) bufferAttribute.gpuType = attribute.gpuType; 48975 48976 geometry.setAttribute( key, bufferAttribute ); 48977 48978 } 48979 48980 const morphAttributes = json.data.morphAttributes; 48981 48982 if ( morphAttributes ) { 48983 48984 for ( const key in morphAttributes ) { 48985 48986 const attributeArray = morphAttributes[ key ]; 48987 48988 const array = []; 48989 48990 for ( let i = 0, il = attributeArray.length; i < il; i ++ ) { 48991 48992 const attribute = attributeArray[ i ]; 48993 let bufferAttribute; 48994 48995 if ( attribute.isInterleavedBufferAttribute ) { 48996 48997 const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data ); 48998 bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized ); 48999 49000 } else { 49001 49002 const typedArray = getTypedArray( attribute.type, attribute.array ); 49003 bufferAttribute = new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ); 49004 49005 } 49006 49007 if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; 49008 if ( attribute.usage !== undefined ) bufferAttribute.setUsage( attribute.usage ); 49009 if ( attribute.gpuType !== undefined ) bufferAttribute.gpuType = attribute.gpuType; 49010 array.push( bufferAttribute ); 49011 49012 } 49013 49014 geometry.morphAttributes[ key ] = array; 49015 49016 } 49017 49018 } 49019 49020 const morphTargetsRelative = json.data.morphTargetsRelative; 49021 49022 if ( morphTargetsRelative ) { 49023 49024 geometry.morphTargetsRelative = true; 49025 49026 } 49027 49028 const groups = json.data.groups || json.data.drawcalls || json.data.offsets; 49029 49030 if ( groups !== undefined ) { 49031 49032 for ( let i = 0, n = groups.length; i !== n; ++ i ) { 49033 49034 const group = groups[ i ]; 49035 49036 geometry.addGroup( group.start, group.count, group.materialIndex ); 49037 49038 } 49039 49040 } 49041 49042 const boundingSphere = json.data.boundingSphere; 49043 49044 if ( boundingSphere !== undefined ) { 49045 49046 geometry.boundingSphere = new Sphere().fromJSON( boundingSphere ); 49047 49048 } 49049 49050 if ( json.name ) geometry.name = json.name; 49051 if ( json.userData ) geometry.userData = json.userData; 49052 49053 return geometry; 49054 49055 } 49056 49057} 49058 49059const _customGeometries = {}; 49060 49061/** 49062 * A loader for loading a JSON resource in the [JSON Object/Scene format](https://github.com/mrdoob/three.js/wiki/JSON-Object-Scene-format-4). 49063 * The files are internally loaded via {@link FileLoader}. 49064 * 49065 * ```js 49066 * const loader = new THREE.ObjectLoader(); 49067 * const obj = await loader.loadAsync( 'models/json/example.json' ); 49068 * scene.add( obj ); 49069 * 49070 * // Alternatively, to parse a previously loaded JSON structure 49071 * const object = await loader.parseAsync( a_json_object ); 49072 * scene.add( object ); 49073 * ``` 49074 * 49075 * @augments Loader 49076 */ 49077class ObjectLoader extends Loader { 49078 49079 /** 49080 * Constructs a new object loader. 49081 * 49082 * @param {LoadingManager} [manager] - The loading manager. 49083 */ 49084 constructor( manager ) { 49085 49086 super( manager ); 49087 49088 } 49089 49090 /** 49091 * Starts loading from the given URL and pass the loaded 3D object to the `onLoad()` callback. 49092 * 49093 * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI. 49094 * @param {function(Object3D)} onLoad - Executed when the loading process has been finished. 49095 * @param {onProgressCallback} onProgress - Executed while the loading is in progress. 49096 * @param {onErrorCallback} onError - Executed when errors occur. 49097 */ 49098 load( url, onLoad, onProgress, onError ) { 49099 49100 const scope = this; 49101 49102 const path = ( this.path === '' ) ? LoaderUtils.extractUrlBase( url ) : this.path; 49103 this.resourcePath = this.resourcePath || path; 49104 49105 const loader = new FileLoader( this.manager ); 49106 loader.setPath( this.path ); 49107 loader.setRequestHeader( this.requestHeader ); 49108 loader.setWithCredentials( this.withCredentials ); 49109 loader.load( url, function ( text ) { 49110 49111 let json = null; 49112 49113 try { 49114 49115 json = JSON.parse( text ); 49116 49117 } catch ( e ) { 49118 49119 if ( onError !== undefined ) onError( e ); 49120 49121 error( 'ObjectLoader: Can\'t parse ' + url + '.', e.message ); 49122 49123 return; 49124 49125 } 49126 49127 const metadata = json.metadata; 49128 49129 if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) { 49130 49131 if ( onError !== undefined ) onError( new Error( 'THREE.ObjectLoader: Can\'t load ' + url ) ); 49132 49133 error( 'ObjectLoader: Can\'t load ' + url ); 49134 return; 49135 49136 } 49137 49138 scope.parse( json, onLoad ); 49139 49140 }, onProgress, onError ); 49141 49142 } 49143 49144 /** 49145 * Async version of {@link ObjectLoader#load}. 49146 * 49147 * @async 49148 * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI. 49149 * @param {onProgressCallback} onProgress - Executed while the loading is in progress. 49150 * @return {Promise<Object3D>} A Promise that resolves with the loaded 3D object. 49151 */ 49152 async loadAsync( url, onProgress ) { 49153 49154 const scope = this; 49155 49156 const path = ( this.path === '' ) ? LoaderUtils.extractUrlBase( url ) : this.path; 49157 this.resourcePath = this.resourcePath || path; 49158 49159 const loader = new FileLoader( this.manager ); 49160 loader.setPath( this.path ); 49161 loader.setRequestHeader( this.requestHeader ); 49162 loader.setWithCredentials( this.withCredentials ); 49163 49164 const text = await loader.loadAsync( url, onProgress ); 49165 49166 let json; 49167 49168 try { 49169 49170 json = JSON.parse( text ); 49171 49172 } catch ( e ) { 49173 49174 throw new Error( 'THREE.ObjectLoader: Can\'t parse ' + url + '. ' + e.message ); 49175 49176 } 49177 49178 const metadata = json.metadata; 49179 49180 if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) { 49181 49182 throw new Error( 'THREE.ObjectLoader: Can\'t load ' + url ); 49183 49184 } 49185 49186 return await scope.parseAsync( json ); 49187 49188 } 49189 49190 /** 49191 * Parses the given JSON. This is used internally by {@link ObjectLoader#load} 49192 * but can also be used directly to parse a previously loaded JSON structure. 49193 * 49194 * @param {Object} json - The serialized 3D object. 49195 * @param {onLoad} onLoad - Executed when all resources (e.g. textures) have been fully loaded. 49196 * @return {Object3D} The parsed 3D object. 49197 */ 49198 parse( json, onLoad ) { 49199 49200 const animations = this.parseAnimations( json.animations ); 49201 const shapes = this.parseShapes( json.shapes ); 49202 const geometries = this.parseGeometries( json.geometries, shapes ); 49203 49204 const images = this.parseImages( json.images, function () { 49205 49206 if ( onLoad !== undefined ) onLoad( object ); 49207 49208 } ); 49209 49210 const textures = this.parseTextures( json.textures, images ); 49211 const materials = this.parseMaterials( json.materials, textures ); 49212 49213 const object = this.parseObject( json.object, geometries, materials, textures, animations ); 49214 const skeletons = this.parseSkeletons( json.skeletons, object ); 49215 49216 this.bindSkeletons( object, skeletons ); 49217 this.bindLightTargets( object ); 49218 49219 // 49220 49221 if ( onLoad !== undefined ) { 49222 49223 let hasImages = false; 49224 49225 for ( const uuid in images ) { 49226 49227 if ( images[ uuid ].data instanceof HTMLImageElement ) { 49228 49229 hasImages = true; 49230 break; 49231 49232 } 49233 49234 } 49235 49236 if ( hasImages === false ) onLoad( object ); 49237 49238 } 49239 49240 return object; 49241 49242 } 49243 49244 /** 49245 * Async version of {@link ObjectLoader#parse}. 49246 * 49247 * @param {Object} json - The serialized 3D object. 49248 * @return {Promise<Object3D>} A Promise that resolves with the parsed 3D object. 49249 */ 49250 async parseAsync( json ) { 49251 49252 const animations = this.parseAnimations( json.animations ); 49253 const shapes = this.parseShapes( json.shapes ); 49254 const geometries = this.parseGeometries( json.geometries, shapes ); 49255 49256 const images = await this.parseImagesAsync( json.images ); 49257 49258 const textures = this.parseTextures( json.textures, images ); 49259 const materials = this.parseMaterials( json.materials, textures ); 49260 49261 const object = this.parseObject( json.object, geometries, materials, textures, animations ); 49262 const skeletons = this.parseSkeletons( json.skeletons, object ); 49263 49264 this.bindSkeletons( object, skeletons ); 49265 this.bindLightTargets( object ); 49266 49267 return object; 49268 49269 } 49270 49271 /** 49272 * Registers the given geometry at the internal 49273 * geometry library. 49274 * 49275 * @static 49276 * @param {string} type - The geometry type. 49277 * @param {BufferGeometry.constructor} geometryClass - The geometry class. 49278 */ 49279 static registerGeometry( type, geometryClass ) { 49280 49281 _customGeometries[ type ] = geometryClass; 49282 49283 } 49284 49285 // internals 49286 49287 parseShapes( json ) { 49288 49289 const shapes = {}; 49290 49291 if ( json !== undefined ) { 49292 49293 for ( let i = 0, l = json.length; i < l; i ++ ) { 49294 49295 const shape = new Shape().fromJSON( json[ i ] ); 49296 49297 shapes[ shape.uuid ] = shape; 49298 49299 } 49300 49301 } 49302 49303 return shapes; 49304 49305 } 49306 49307 parseSkeletons( json, object ) { 49308 49309 const skeletons = {}; 49310 const bones = {}; 49311 49312 // generate bone lookup table 49313 49314 object.traverse( function ( child ) { 49315 49316 if ( child.isBone ) bones[ child.uuid ] = child; 49317 49318 } ); 49319 49320 // create skeletons 49321 49322 if ( json !== undefined ) { 49323 49324 for ( let i = 0, l = json.length; i < l; i ++ ) { 49325 49326 const skeleton = new Skeleton().fromJSON( json[ i ], bones ); 49327 49328 skeletons[ skeleton.uuid ] = skeleton; 49329 49330 } 49331 49332 } 49333 49334 return skeletons; 49335 49336 } 49337 49338 parseGeometries( json, shapes ) { 49339 49340 const geometries = {}; 49341 49342 if ( json !== undefined ) { 49343 49344 const bufferGeometryLoader = new BufferGeometryLoader(); 49345 49346 for ( let i = 0, l = json.length; i < l; i ++ ) { 49347 49348 let geometry; 49349 const data = json[ i ]; 49350 49351 switch ( data.type ) { 49352 49353 case 'BufferGeometry': 49354 case 'InstancedBufferGeometry': 49355 49356 geometry = bufferGeometryLoader.parse( data ); 49357 break; 49358 49359 default: 49360 49361 if ( data.type in Geometries ) { 49362 49363 geometry = Geometries[ data.type ].fromJSON( data, shapes ); 49364 49365 } else if ( data.type in _customGeometries ) { 49366 49367 geometry = _customGeometries[ data.type ].fromJSON( data, shapes ); 49368 49369 } else { 49370 49371 warn( `ObjectLoader: Unknown geometry type "${ data.type }". Use .registerGeometry() before starting the deserialization process.` ); 49372 49373 } 49374 49375 } 49376 49377 geometry.uuid = data.uuid; 49378 49379 if ( data.name !== undefined ) geometry.name = data.name; 49380 if ( data.userData !== undefined ) geometry.userData = data.userData; 49381 49382 geometries[ data.uuid ] = geometry; 49383 49384 } 49385 49386 } 49387 49388 return geometries; 49389 49390 } 49391 49392 parseMaterials( json, textures ) { 49393 49394 const cache = {}; // MultiMaterial 49395 const materials = {}; 49396 49397 if ( json !== undefined ) { 49398 49399 const loader = new MaterialLoader(); 49400 loader.setTextures( textures ); 49401 49402 for ( let i = 0, l = json.length; i < l; i ++ ) { 49403 49404 const data = json[ i ]; 49405 49406 if ( cache[ data.uuid ] === undefined ) { 49407 49408 cache[ data.uuid ] = loader.parse( data ); 49409 49410 } 49411 49412 materials[ data.uuid ] = cache[ data.uuid ]; 49413 49414 } 49415 49416 } 49417 49418 return materials; 49419 49420 } 49421 49422 parseAnimations( json ) { 49423 49424 const animations = {}; 49425 49426 if ( json !== undefined ) { 49427 49428 for ( let i = 0; i < json.length; i ++ ) { 49429 49430 const data = json[ i ]; 49431 49432 const clip = AnimationClip.parse( data ); 49433 49434 animations[ clip.uuid ] = clip; 49435 49436 } 49437 49438 } 49439 49440 return animations; 49441 49442 } 49443 49444 parseImages( json, onLoad ) { 49445 49446 const scope = this; 49447 const images = {}; 49448 49449 let loader; 49450 49451 function loadImage( url ) { 49452 49453 url = scope.manager.resolveURL( url ); 49454 49455 scope.manager.itemStart( url ); 49456 49457 return loader.load( url, function () { 49458 49459 scope.manager.itemEnd( url ); 49460 49461 }, undefined, function () { 49462 49463 scope.manager.itemError( url ); 49464 scope.manager.itemEnd( url ); 49465 49466 } ); 49467 49468 } 49469 49470 function deserializeImage( image ) { 49471 49472 if ( typeof image === 'string' ) { 49473 49474 const url = image; 49475 49476 const path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( url ) ? url : scope.resourcePath + url; 49477 49478 return loadImage( path ); 49479 49480 } else { 49481 49482 if ( image.data ) { 49483 49484 return { 49485 data: getTypedArray( image.type, image.data ), 49486 width: image.width, 49487 height: image.height 49488 }; 49489 49490 } else { 49491 49492 return null; 49493 49494 } 49495 49496 } 49497 49498 } 49499 49500 if ( json !== undefined && json.length > 0 ) { 49501 49502 const manager = new LoadingManager( onLoad ); 49503 49504 loader = new ImageLoader( manager ); 49505 loader.setCrossOrigin( this.crossOrigin ); 49506 49507 for ( let i = 0, il = json.length; i < il; i ++ ) { 49508 49509 const image = json[ i ]; 49510 const url = image.url; 49511 49512 if ( Array.isArray( url ) ) { 49513 49514 // load array of images e.g CubeTexture 49515 49516 const imageArray = []; 49517 49518 for ( let j = 0, jl = url.length; j < jl; j ++ ) { 49519 49520 const currentUrl = url[ j ]; 49521 49522 const deserializedImage = deserializeImage( currentUrl ); 49523 49524 if ( deserializedImage !== null ) { 49525 49526 if ( deserializedImage instanceof HTMLImageElement ) { 49527 49528 imageArray.push( deserializedImage ); 49529 49530 } else { 49531 49532 // special case: handle array of data textures for cube textures 49533 49534 imageArray.push( new DataTexture( deserializedImage.data, deserializedImage.width, deserializedImage.height ) ); 49535 49536 } 49537 49538 } 49539 49540 } 49541 49542 images[ image.uuid ] = new TextureSource( imageArray ); 49543 49544 } else { 49545 49546 // load single image 49547 49548 const deserializedImage = deserializeImage( image.url ); 49549 images[ image.uuid ] = new TextureSource( deserializedImage ); 49550 49551 49552 } 49553 49554 } 49555 49556 } 49557 49558 return images; 49559 49560 } 49561 49562 async parseImagesAsync( json ) { 49563 49564 const scope = this; 49565 const images = {}; 49566 49567 let loader; 49568 49569 async function deserializeImage( image ) { 49570 49571 if ( typeof image === 'string' ) { 49572 49573 const url = image; 49574 49575 const path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( url ) ? url : scope.resourcePath + url; 49576 49577 return await loader.loadAsync( path ); 49578 49579 } else { 49580 49581 if ( image.data ) { 49582 49583 return { 49584 data: getTypedArray( image.type, image.data ), 49585 width: image.width, 49586 height: image.height 49587 }; 49588 49589 } else { 49590 49591 return null; 49592 49593 } 49594 49595 } 49596 49597 } 49598 49599 if ( json !== undefined && json.length > 0 ) { 49600 49601 loader = new ImageLoader( this.manager ); 49602 loader.setCrossOrigin( this.crossOrigin ); 49603 49604 for ( let i = 0, il = json.length; i < il; i ++ ) { 49605 49606 const image = json[ i ]; 49607 const url = image.url; 49608 49609 if ( Array.isArray( url ) ) { 49610 49611 // load array of images e.g CubeTexture 49612 49613 const imageArray = []; 49614 49615 for ( let j = 0, jl = url.length; j < jl; j ++ ) { 49616 49617 const currentUrl = url[ j ]; 49618 49619 const deserializedImage = await deserializeImage( currentUrl ); 49620 49621 if ( deserializedImage !== null ) { 49622 49623 if ( deserializedImage instanceof HTMLImageElement ) { 49624 49625 imageArray.push( deserializedImage ); 49626 49627 } else { 49628 49629 // special case: handle array of data textures for cube textures 49630 49631 imageArray.push( new DataTexture( deserializedImage.data, deserializedImage.width, deserializedImage.height ) ); 49632 49633 } 49634 49635 } 49636 49637 } 49638 49639 images[ image.uuid ] = new TextureSource( imageArray ); 49640 49641 } else { 49642 49643 // load single image 49644 49645 const deserializedImage = await deserializeImage( image.url ); 49646 images[ image.uuid ] = new TextureSource( deserializedImage ); 49647 49648 } 49649 49650 } 49651 49652 } 49653 49654 return images; 49655 49656 } 49657 49658 parseTextures( json, images ) { 49659 49660 function parseConstant( value, type ) { 49661 49662 if ( typeof value === 'number' ) return value; 49663 49664 warn( 'ObjectLoader.parseTexture: Constant should be in numeric form.', value ); 49665 49666 return type[ value ]; 49667 49668 } 49669 49670 const textures = {}; 49671 49672 if ( json !== undefined ) { 49673 49674 for ( let i = 0, l = json.length; i < l; i ++ ) { 49675 49676 const data = json[ i ]; 49677 49678 if ( data.image === undefined ) { 49679 49680 warn( 'ObjectLoader: No "image" specified for', data.uuid ); 49681 49682 } 49683 49684 if ( images[ data.image ] === undefined ) { 49685 49686 warn( 'ObjectLoader: Undefined image', data.image ); 49687 49688 } 49689 49690 const source = images[ data.image ]; 49691 const image = source.data; 49692 49693 let texture; 49694 49695 if ( Array.isArray( image ) ) { 49696 49697 texture = new CubeTexture(); 49698 49699 if ( image.length === 6 ) texture.needsUpdate = true; 49700 49701 } else { 49702 49703 if ( image && image.data ) { 49704 49705 texture = new DataTexture(); 49706 49707 } else { 49708 49709 texture = new Texture(); 49710 49711 } 49712 49713 if ( image ) texture.needsUpdate = true; // textures can have undefined image data 49714 49715 } 49716 49717 texture.source = source; 49718 49719 texture.uuid = data.uuid; 49720 49721 if ( data.name !== undefined ) texture.name = data.name; 49722 49723 if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING ); 49724 if ( data.channel !== undefined ) texture.channel = data.channel; 49725 49726 if ( data.offset !== undefined ) texture.offset.fromArray( data.offset ); 49727 if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat ); 49728 if ( data.center !== undefined ) texture.center.fromArray( data.center ); 49729 if ( data.rotation !== undefined ) texture.rotation = data.rotation; 49730 49731 if ( data.wrap !== undefined ) { 49732 49733 texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING ); 49734 texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING ); 49735 49736 } 49737 49738 if ( data.format !== undefined ) texture.format = data.format; 49739 if ( data.internalFormat !== undefined ) texture.internalFormat = data.internalFormat; 49740 if ( data.type !== undefined ) texture.type = data.type; 49741 if ( data.colorSpace !== undefined ) texture.colorSpace = data.colorSpace; 49742 49743 if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER ); 49744 if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER ); 49745 if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; 49746 49747 if ( data.flipY !== undefined ) texture.flipY = data.flipY; 49748 49749 if ( data.generateMipmaps !== undefined ) texture.generateMipmaps = data.generateMipmaps; 49750 if ( data.premultiplyAlpha !== undefined ) texture.premultiplyAlpha = data.premultiplyAlpha; 49751 if ( data.unpackAlignment !== undefined ) texture.unpackAlignment = data.unpackAlignment; 49752 if ( data.compareFunction !== undefined ) texture.compareFunction = data.compareFunction; 49753 if ( data.normalized !== undefined ) texture.normalized = data.normalized; 49754 49755 if ( data.userData !== undefined ) texture.userData = data.userData; 49756 49757 textures[ data.uuid ] = texture; 49758 49759 } 49760 49761 } 49762 49763 return textures; 49764 49765 } 49766 49767 parseObject( data, geometries, materials, textures, animations ) { 49768 49769 let object; 49770 49771 function getGeometry( name ) { 49772 49773 if ( geometries[ name ] === undefined ) { 49774 49775 warn( 'ObjectLoader: Undefined geometry', name ); 49776 49777 } 49778 49779 return geometries[ name ]; 49780 49781 } 49782 49783 function getMaterial( name ) { 49784 49785 if ( name === undefined ) return undefined; 49786 49787 if ( Array.isArray( name ) ) { 49788 49789 const array = []; 49790 49791 for ( let i = 0, l = name.length; i < l; i ++ ) { 49792 49793 const uuid = name[ i ]; 49794 49795 if ( materials[ uuid ] === undefined ) { 49796 49797 warn( 'ObjectLoader: Undefined material', uuid ); 49798 49799 } 49800 49801 array.push( materials[ uuid ] ); 49802 49803 } 49804 49805 return array; 49806 49807 } 49808 49809 if ( materials[ name ] === undefined ) { 49810 49811 warn( 'ObjectLoader: Undefined material', name ); 49812 49813 } 49814 49815 return materials[ name ]; 49816 49817 } 49818 49819 function getTexture( uuid ) { 49820 49821 if ( textures[ uuid ] === undefined ) { 49822 49823 warn( 'ObjectLoader: Undefined texture', uuid ); 49824 49825 } 49826 49827 return textures[ uuid ]; 49828 49829 } 49830 49831 let geometry, material; 49832 49833 switch ( data.type ) { 49834 49835 case 'Scene': 49836 49837 object = new Scene(); 49838 49839 if ( data.background !== undefined ) { 49840 49841 if ( Number.isInteger( data.background ) ) { 49842 49843 object.background = new Color( data.background ); 49844 49845 } else { 49846 49847 object.background = getTexture( data.background ); 49848 49849 } 49850 49851 } 49852 49853 if ( data.environment !== undefined ) { 49854 49855 object.environment = getTexture( data.environment ); 49856 49857 } 49858 49859 if ( data.fog !== undefined ) { 49860 49861 if ( data.fog.type === 'Fog' ) { 49862 49863 object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far ); 49864 49865 } else if ( data.fog.type === 'FogExp2' ) { 49866 49867 object.fog = new FogExp2( data.fog.color, data.fog.density ); 49868 49869 } 49870 49871 if ( data.fog.name !== '' ) { 49872 49873 object.fog.name = data.fog.name; 49874 49875 } 49876 49877 } 49878 49879 if ( data.backgroundBlurriness !== undefined ) object.backgroundBlurriness = data.backgroundBlurriness; 49880 if ( data.backgroundIntensity !== undefined ) object.backgroundIntensity = data.backgroundIntensity; 49881 if ( data.backgroundRotation !== undefined ) object.backgroundRotation.fromArray( data.backgroundRotation ); 49882 49883 if ( data.environmentIntensity !== undefined ) object.environmentIntensity = data.environmentIntensity; 49884 if ( data.environmentRotation !== undefined ) object.environmentRotation.fromArray( data.environmentRotation ); 49885 49886 break; 49887 49888 case 'PerspectiveCamera': 49889 49890 object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); 49891 49892 if ( data.focus !== undefined ) object.focus = data.focus; 49893 if ( data.zoom !== undefined ) object.zoom = data.zoom; 49894 if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge; 49895 if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset; 49896 if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); 49897 49898 break; 49899 49900 case 'OrthographicCamera': 49901 49902 object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); 49903 49904 if ( data.zoom !== undefined ) object.zoom = data.zoom; 49905 if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); 49906 49907 break; 49908 49909 case 'AmbientLight': 49910 49911 object = new AmbientLight( data.color, data.intensity ); 49912 49913 break; 49914 49915 case 'DirectionalLight': 49916 49917 object = new DirectionalLight( data.color, data.intensity ); 49918 object.target = data.target || ''; 49919 49920 break; 49921 49922 case 'PointLight': 49923 49924 object = new PointLight( data.color, data.intensity, data.distance, data.decay ); 49925 49926 break; 49927 49928 case 'RectAreaLight': 49929 49930 object = new RectAreaLight( data.color, data.intensity, data.width, data.height ); 49931 49932 break; 49933 49934 case 'SpotLight': 49935 49936 object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); 49937 object.target = data.target || ''; 49938 49939 break; 49940 49941 case 'HemisphereLight': 49942 49943 object = new HemisphereLight( data.color, data.groundColor, data.intensity ); 49944 49945 break; 49946 49947 case 'LightProbe': 49948 49949 const sh = new SphericalHarmonics3().fromArray( data.sh ); 49950 object = new LightProbe( sh, data.intensity ); 49951 49952 break; 49953 49954 case 'SkinnedMesh': 49955 49956 geometry = getGeometry( data.geometry ); 49957 material = getMaterial( data.material ); 49958 49959 object = new SkinnedMesh( geometry, material ); 49960 49961 if ( data.bindMode !== undefined ) object.bindMode = data.bindMode; 49962 if ( data.bindMatrix !== undefined ) object.bindMatrix.fromArray( data.bindMatrix ); 49963 if ( data.skeleton !== undefined ) object.skeleton = data.skeleton; 49964 49965 break; 49966 49967 case 'Mesh': 49968 49969 geometry = getGeometry( data.geometry ); 49970 material = getMaterial( data.material ); 49971 49972 object = new Mesh( geometry, material ); 49973 49974 break; 49975 49976 case 'InstancedMesh': 49977 49978 geometry = getGeometry( data.geometry ); 49979 material = getMaterial( data.material ); 49980 const count = data.count; 49981 const instanceMatrix = data.instanceMatrix; 49982 const instanceColor = data.instanceColor; 49983 49984 object = new InstancedMesh( geometry, material, count ); 49985 object.instanceMatrix = new InstancedBufferAttribute( new Float32Array( instanceMatrix.array ), 16 ); 49986 if ( instanceColor !== undefined ) object.instanceColor = new InstancedBufferAttribute( new Float32Array( instanceColor.array ), instanceColor.itemSize ); 49987 49988 break; 49989 49990 case 'BatchedMesh': 49991 49992 geometry = getGeometry( data.geometry ); 49993 material = getMaterial( data.material ); 49994 49995 object = new BatchedMesh( data.maxInstanceCount, data.maxVertexCount, data.maxIndexCount, material ); 49996 object.geometry = geometry; 49997 object.perObjectFrustumCulled = data.perObjectFrustumCulled; 49998 object.sortObjects = data.sortObjects; 49999 50000 object._drawRanges = data.drawRanges; 50001 object._reservedRanges = data.reservedRanges; 50002 50003 object._geometryInfo = data.geometryInfo.map( info => { 50004 50005 let box = null; 50006 let sphere = null; 50007 if ( info.boundingBox !== undefined ) { 50008 50009 box = new Box3().fromJSON( info.boundingBox ); 50010 50011 } 50012 50013 if ( info.boundingSphere !== undefined ) { 50014 50015 sphere = new Sphere().fromJSON( info.boundingSphere ); 50016 50017 } 50018 50019 return { 50020 ...info, 50021 boundingBox: box, 50022 boundingSphere: sphere 50023 }; 50024 50025 } ); 50026 object._instanceInfo = data.instanceInfo; 50027 50028 object._availableInstanceIds = data._availableInstanceIds; 50029 object._availableGeometryIds = data._availableGeometryIds; 50030 50031 object._nextIndexStart = data.nextIndexStart; 50032 object._nextVertexStart = data.nextVertexStart; 50033 object._geometryCount = data.geometryCount; 50034 50035 object._maxInstanceCount = data.maxInstanceCount; 50036 object._maxVertexCount = data.maxVertexCount; 50037 object._maxIndexCount = data.maxIndexCount; 50038 50039 object._geometryInitialized = data.geometryInitialized; 50040 50041 object._matricesTexture = getTexture( data.matricesTexture.uuid ); 50042 50043 object._indirectTexture = getTexture( data.indirectTexture.uuid ); 50044 50045 if ( data.colorsTexture !== undefined ) { 50046 50047 object._colorsTexture = getTexture( data.colorsTexture.uuid ); 50048 50049 } 50050 50051 if ( data.boundingSphere !== undefined ) { 50052 50053 object.boundingSphere = new Sphere().fromJSON( data.boundingSphere ); 50054 50055 } 50056 50057 if ( data.boundingBox !== undefined ) { 50058 50059 object.boundingBox = new Box3().fromJSON( data.boundingBox ); 50060 50061 } 50062 50063 break; 50064 50065 case 'LOD': 50066 50067 object = new LOD(); 50068 50069 break; 50070 50071 case 'Line': 50072 50073 object = new Line( getGeometry( data.geometry ), getMaterial( data.material ) ); 50074 50075 break; 50076 50077 case 'LineLoop': 50078 50079 object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) ); 50080 50081 break; 50082 50083 case 'LineSegments': 50084 50085 object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) ); 50086 50087 break; 50088 50089 case 'PointCloud': 50090 case 'Points': 50091 50092 object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) ); 50093 50094 break; 50095 50096 case 'Sprite': 50097 50098 object = new Sprite( getMaterial( data.material ) ); 50099 50100 break; 50101 50102 case 'Group': 50103 50104 object = new Group(); 50105 50106 break; 50107 50108 case 'Bone': 50109 50110 object = new Bone(); 50111 50112 break; 50113 50114 default: 50115 50116 object = new Object3D(); 50117 50118 } 50119 50120 object.uuid = data.uuid; 50121 50122 if ( data.name !== undefined ) object.name = data.name; 50123 50124 if ( data.matrix !== undefined ) { 50125 50126 object.matrix.fromArray( data.matrix ); 50127 50128 if ( data.matrixAutoUpdate !== undefined ) object.matrixAutoUpdate = data.matrixAutoUpdate; 50129 if ( object.matrixAutoUpdate ) object.matrix.decompose( object.position, object.quaternion, object.scale ); 50130 50131 } else { 50132 50133 if ( data.position !== undefined ) object.position.fromArray( data.position ); 50134 if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); 50135 if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion ); 50136 if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); 50137 50138 } 50139 50140 if ( data.up !== undefined ) object.up.fromArray( data.up ); 50141 50142 if ( data.pivot !== undefined ) object.pivot = new Vector3().fromArray( data.pivot ); 50143 50144 if ( data.morphTargetDictionary !== undefined ) object.morphTargetDictionary = Object.assign( {}, data.morphTargetDictionary ); 50145 if ( data.morphTargetInfluences !== undefined ) object.morphTargetInfluences = data.morphTargetInfluences.slice(); 50146 50147 if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; 50148 if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; 50149 50150 if ( data.shadow ) { 50151 50152 if ( data.shadow.intensity !== undefined ) object.shadow.intensity = data.shadow.intensity; 50153 if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias; 50154 if ( data.shadow.normalBias !== undefined ) object.shadow.normalBias = data.shadow.normalBias; 50155 if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius; 50156 if ( data.shadow.blurSamples !== undefined ) object.shadow.blurSamples = data.shadow.blurSamples; 50157 if ( data.shadow.focus !== undefined ) object.shadow.focus = data.shadow.focus; 50158 if ( data.shadow.aspect !== undefined ) object.shadow.aspect = data.shadow.aspect; 50159 if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize ); 50160 if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera ); 50161 50162 } 50163 50164 if ( data.visible !== undefined ) object.visible = data.visible; 50165 if ( data.frustumCulled !== undefined ) object.frustumCulled = data.frustumCulled; 50166 if ( data.renderOrder !== undefined ) object.renderOrder = data.renderOrder; 50167 if ( data.static !== undefined ) object.static = data.static; 50168 if ( data.userData !== undefined ) object.userData = data.userData; 50169 if ( data.layers !== undefined ) object.layers.mask = data.layers; 50170 50171 if ( data.children !== undefined ) { 50172 50173 const children = data.children; 50174 50175 for ( let i = 0; i < children.length; i ++ ) { 50176 50177 object.add( this.parseObject( children[ i ], geometries, materials, textures, animations ) ); 50178 50179 } 50180 50181 } 50182 50183 if ( data.animations !== undefined ) { 50184 50185 const objectAnimations = data.animations; 50186 50187 for ( let i = 0; i < objectAnimations.length; i ++ ) { 50188 50189 const uuid = objectAnimations[ i ]; 50190 50191 object.animations.push( animations[ uuid ] ); 50192 50193 } 50194 50195 } 50196 50197 if ( data.type === 'LOD' ) { 50198 50199 if ( data.autoUpdate !== undefined ) object.autoUpdate = data.autoUpdate; 50200 50201 const levels = data.levels; 50202 50203 for ( let l = 0; l < levels.length; l ++ ) { 50204 50205 const level = levels[ l ]; 50206 const child = object.getObjectByProperty( 'uuid', level.object ); 50207 50208 if ( child !== undefined ) { 50209 50210 object.addLevel( child, level.distance, level.hysteresis ); 50211 50212 } 50213 50214 } 50215 50216 } 50217 50218 return object; 50219 50220 } 50221 50222 bindSkeletons( object, skeletons ) { 50223 50224 if ( Object.keys( skeletons ).length === 0 ) return; 50225 50226 object.traverse( function ( child ) { 50227 50228 if ( child.isSkinnedMesh === true && child.skeleton !== undefined ) { 50229 50230 const skeleton = skeletons[ child.skeleton ]; 50231 50232 if ( skeleton === undefined ) { 50233 50234 warn( 'ObjectLoader: No skeleton found with UUID:', child.skeleton ); 50235 50236 } else { 50237 50238 child.bind( skeleton, child.bindMatrix ); 50239 50240 } 50241 50242 } 50243 50244 } ); 50245 50246 } 50247 50248 bindLightTargets( object ) { 50249 50250 object.traverse( function ( child ) { 50251 50252 if ( child.isDirectionalLight || child.isSpotLight ) { 50253 50254 const uuid = child.target; 50255 50256 const target = object.getObjectByProperty( 'uuid', uuid ); 50257 50258 if ( target !== undefined ) { 50259 50260 child.target = target; 50261 50262 } else { 50263 50264 child.target = new Object3D(); 50265 50266 } 50267 50268 } 50269 50270 } ); 50271 50272 } 50273 50274} 50275 50276const TEXTURE_MAPPING = { 50277 UVMapping: UVMapping, 50278 CubeReflectionMapping: CubeReflectionMapping, 50279 CubeRefractionMapping: CubeRefractionMapping, 50280 EquirectangularReflectionMapping: EquirectangularReflectionMapping, 50281 EquirectangularRefractionMapping: EquirectangularRefractionMapping, 50282 CubeUVReflectionMapping: CubeUVReflectionMapping 50283}; 50284 50285const TEXTURE_WRAPPING = { 50286 RepeatWrapping: RepeatWrapping, 50287 ClampToEdgeWrapping: ClampToEdgeWrapping, 50288 MirroredRepeatWrapping: MirroredRepeatWrapping 50289}; 50290 50291const TEXTURE_FILTER = { 50292 NearestFilter: NearestFilter, 50293 NearestMipmapNearestFilter: NearestMipmapNearestFilter, 50294 NearestMipmapLinearFilter: NearestMipmapLinearFilter, 50295 LinearFilter: LinearFilter, 50296 LinearMipmapNearestFilter: LinearMipmapNearestFilter, 50297 LinearMipmapLinearFilter: LinearMipmapLinearFilter 50298}; 50299 50300const _errorMap = new WeakMap(); 50301 50302/** 50303 * A loader for loading images as an [ImageBitmap](https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap). 50304 * An `ImageBitmap` provides an asynchronous and resource efficient pathway to prepare 50305 * textures for rendering. 50306 * 50307 * Note that {@link Texture#flipY} and {@link Texture#premultiplyAlpha} are ignored with image bitmaps. 50308 * These options need to be configured via {@link ImageBitmapLoader#setOptions} prior to loading, 50309 * unlike regular images which can be configured on the Texture to set these options on GPU upload instead. 50310 * 50311 * To match the default behaviour of {@link Texture}, the following options are needed: 50312 * 50313 * ```js 50314 * { imageOrientation: 'flipY', premultiplyAlpha: 'none' } 50315 * ``` 50316 * 50317 * Also note that unlike {@link FileLoader}, this loader will only avoid multiple concurrent requests to the same URL if {@link Cache} is enabled. 50318 * 50319 * ```js 50320 * const loader = new THREE.ImageBitmapLoader(); 50321 * loader.setOptions( { imageOrientation: 'flipY' } ); // set options if needed 50322 * const imageBitmap = await loader.loadAsync( 'image.png' ); 50323 * 50324 * const texture = new THREE.Texture( imageBitmap ); 50325 * texture.needsUpdate = true; 50326 * ``` 50327 * 50328 * @augments Loader 50329 */ 50330class ImageBitmapLoader extends Loader { 50331 50332 /** 50333 * Constructs a new image bitmap loader. 50334 * 50335 * @param {LoadingManager} [manager] - The loading manager. 50336 */ 50337 constructor( manager ) { 50338 50339 super( manager ); 50340 50341 /** 50342 * This flag can be used for type testing. 50343 * 50344 * @type {boolean} 50345 * @readonly 50346 * @default true 50347 */ 50348 this.isImageBitmapLoader = true; 50349 50350 if ( typeof createImageBitmap === 'undefined' ) { 50351 50352 warn( 'ImageBitmapLoader: createImageBitmap() not supported.' ); 50353 50354 } 50355 50356 if ( typeof fetch === 'undefined' ) { 50357 50358 warn( 'ImageBitmapLoader: fetch() not supported.' ); 50359 50360 } 50361 50362 /** 50363 * Represents the loader options. 50364 * 50365 * @type {Object} 50366 * @default {premultiplyAlpha:'none'} 50367 */ 50368 this.options = { premultiplyAlpha: 'none' }; 50369 50370 /** 50371 * Used for aborting requests. 50372 * 50373 * @private 50374 * @type {AbortController} 50375 */ 50376 this._abortController = new AbortController(); 50377 50378 } 50379 50380 /** 50381 * Sets the given loader options. The structure of the object must match the `options` parameter of 50382 * [createImageBitmap](https://developer.mozilla.org/en-US/docs/Web/API/Window/createImageBitmap). 50383 * 50384 * Note: When caching is enabled, the cache key is based on the URL only. Loading the same URL with 50385 * different options will return the cached result of the first request. 50386 * 50387 * @param {Object} options - The loader options to set. 50388 * @return {ImageBitmapLoader} A reference to this image bitmap loader. 50389 */ 50390 setOptions( options ) { 50391 50392 this.options = options; 50393 50394 return this; 50395 50396 } 50397 50398 /** 50399 * Starts loading from the given URL and pass the loaded image bitmap to the `onLoad()` callback. 50400 * 50401 * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI. 50402 * @param {function(ImageBitmap)} onLoad - Executed when the loading process has been finished. 50403 * @param {onProgressCallback} onProgress - Unsupported in this loader. 50404 * @param {onErrorCallback} onError - Executed when errors occur. 50405 */ 50406 load( url, onLoad, onProgress, onError ) { 50407 50408 if ( url === undefined ) url = ''; 50409 50410 if ( this.path !== undefined ) url = this.path + url; 50411 50412 url = this.manager.resolveURL( url ); 50413 50414 const scope = this; 50415 50416 const cached = Cache.get( `image-bitmap:${url}` ); 50417 50418 if ( cached !== undefined ) { 50419 50420 scope.manager.itemStart( url ); 50421 50422 // If cached is a promise, wait for it to resolve 50423 if ( cached.then ) { 50424 50425 cached.then( imageBitmap => { 50426 50427 // check if there is an error for the cached promise 50428 50429 if ( _errorMap.has( cached ) === true ) { 50430 50431 if ( onError ) onError( _errorMap.get( cached ) ); 50432 50433 scope.manager.itemError( url ); 50434 scope.manager.itemEnd( url ); 50435 50436 } else { 50437 50438 if ( onLoad ) onLoad( imageBitmap ); 50439 50440 scope.manager.itemEnd( url ); 50441 50442 } 50443 50444 } ); 50445 50446 return; 50447 50448 } 50449 50450 // If cached is not a promise (i.e., it's already an imageBitmap) 50451 setTimeout( function () { 50452 50453 if ( onLoad ) onLoad( cached ); 50454 50455 scope.manager.itemEnd( url ); 50456 50457 }, 0 ); 50458 50459 return; 50460 50461 } 50462 50463 const fetchOptions = {}; 50464 fetchOptions.credentials = ( this.crossOrigin === 'anonymous' ) ? 'same-origin' : 'include'; 50465 fetchOptions.headers = this.requestHeader; 50466 fetchOptions.signal = ( typeof AbortSignal.any === 'function' ) ? AbortSignal.any( [ this._abortController.signal, this.manager.abortController.signal ] ) : this._abortController.signal; 50467 50468 const promise = fetch( url, fetchOptions ).then( function ( res ) { 50469 50470 return res.blob(); 50471 50472 } ).then( function ( blob ) { 50473 50474 return createImageBitmap( blob, Object.assign( {}, scope.options, { colorSpaceConversion: 'none' } ) ); 50475 50476 } ).then( function ( imageBitmap ) { 50477 50478 Cache.add( `image-bitmap:${url}`, imageBitmap ); 50479 50480 if ( onLoad ) onLoad( imageBitmap ); 50481 50482 scope.manager.itemEnd( url ); 50483 50484 return imageBitmap; // see #34150 50485 50486 } ).catch( function ( e ) { 50487 50488 if ( onError ) onError( e ); 50489 50490 _errorMap.set( promise, e ); 50491 50492 Cache.remove( `image-bitmap:${url}` ); 50493 50494 scope.manager.itemError( url ); 50495 scope.manager.itemEnd( url ); 50496 50497 } ); 50498 50499 Cache.add( `image-bitmap:${url}`, promise ); 50500 scope.manager.itemStart( url ); 50501 50502 } 50503 50504 /** 50505 * Aborts ongoing fetch requests. 50506 * 50507 * @return {ImageBitmapLoader} A reference to this instance. 50508 */ 50509 abort() { 50510 50511 this._abortController.abort(); 50512 this._abortController = new AbortController(); 50513 50514 return this; 50515 50516 } 50517 50518} 50519 50520let _context; 50521 50522/** 50523 * Manages the global audio context in the engine. 50524 * 50525 * @hideconstructor 50526 */ 50527class AudioContext { 50528 50529 /** 50530 * Returns the global native audio context. 50531 * 50532 * @return {Window.AudioContext} The native audio context. 50533 */ 50534 static getContext() { 50535 50536 if ( _context === undefined ) { 50537 50538 _context = new ( window.AudioContext || window.webkitAudioContext )(); 50539 50540 } 50541 50542 return _context; 50543 50544 } 50545 50546 /** 50547 * Allows to set the global native audio context from outside. 50548 * 50549 * @param {Window.AudioContext} value - The native context to set. 50550 */ 50551 static setContext( value ) { 50552 50553 _context = value; 50554 50555 } 50556 50557} 50558 50559/** 50560 * Class for loading audio buffers. Audios are internally 50561 * loaded via {@link FileLoader}. 50562 * 50563 * ```js 50564 * const audioListener = new THREE.AudioListener(); 50565 * const ambientSound = new THREE.Audio( audioListener ); 50566 * 50567 * const loader = new THREE.AudioLoader(); 50568 * const audioBuffer = await loader.loadAsync( 'audio/ambient_ocean.ogg' ); 50569 * 50570 * ambientSound.setBuffer( audioBuffer ); 50571 * ambientSound.play(); 50572 * ``` 50573 * 50574 * @augments Loader 50575 */ 50576class AudioLoader extends Loader { 50577 50578 /** 50579 * Constructs a new audio loader. 50580 * 50581 * @param {LoadingManager} [manager] - The loading manager. 50582 */ 50583 constructor( manager ) { 50584 50585 super( manager ); 50586 50587 } 50588 50589 /** 50590 * Starts loading from the given URL and passes the loaded audio buffer 50591 * to the `onLoad()` callback. 50592 * 50593 * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI. 50594 * @param {function(AudioBuffer)} onLoad - Executed when the loading process has been finished. 50595 * @param {onProgressCallback} onProgress - Executed while the loading is in progress. 50596 * @param {onErrorCallback} onError - Executed when errors occur. 50597 */ 50598 load( url, onLoad, onProgress, onError ) { 50599 50600 const scope = this; 50601 50602 const loader = new FileLoader( this.manager ); 50603 loader.setResponseType( 'arraybuffer' ); 50604 loader.setPath( this.path ); 50605 loader.setRequestHeader( this.requestHeader ); 50606 loader.setWithCredentials( this.withCredentials ); 50607 loader.load( url, function ( buffer ) { 50608 50609 try { 50610 50611 // Create a copy of the buffer. The `decodeAudioData` method 50612 // detaches the buffer when complete, preventing reuse. 50613 const bufferCopy = buffer.slice( 0 ); 50614 50615 const context = AudioContext.getContext(); 50616 50617 const decodeUrl = url + '#decode'; 50618 scope.manager.itemStart( decodeUrl ); // prevent loading manager from completing too early, see #33378 50619 50620 context.decodeAudioData( bufferCopy, function ( audioBuffer ) { 50621 50622 onLoad( audioBuffer ); 50623 scope.manager.itemEnd( decodeUrl ); 50624 50625 } ).catch( function ( e ) { 50626 50627 handleError( e ); 50628 scope.manager.itemEnd( decodeUrl ); 50629 50630 } ); 50631 50632 } catch ( e ) { 50633 50634 handleError( e ); 50635 50636 } 50637 50638 }, onProgress, onError ); 50639 50640 function handleError( e ) { 50641 50642 if ( onError ) { 50643 50644 onError( e ); 50645 50646 } else { 50647 50648 error( e ); 50649 50650 } 50651 50652 scope.manager.itemError( url ); 50653 50654 } 50655 50656 } 50657 50658} 50659 50660const _eyeRight = /*@__PURE__*/ new Matrix4(); 50661const _eyeLeft = /*@__PURE__*/ new Matrix4(); 50662const _projectionMatrix = /*@__PURE__*/ new Matrix4(); 50663 50664/** 50665 * A special type of camera that uses two perspective cameras with 50666 * stereoscopic projection. Can be used for rendering stereo effects 50667 * like [3D Anaglyph](https://en.wikipedia.org/wiki/Anaglyph_3D) or 50668 * [Parallax Barrier](https://en.wikipedia.org/wiki/parallax_barrier). 50669 */ 50670class StereoCamera { 50671 50672 /** 50673 * Constructs a new stereo camera. 50674 */ 50675 constructor() { 50676 50677 /** 50678 * The type property is used for detecting the object type 50679 * in context of serialization/deserialization. 50680 * 50681 * @type {string} 50682 * @readonly 50683 */ 50684 this.type = 'StereoCamera'; 50685 50686 /** 50687 * The aspect. 50688 * 50689 * @type {number} 50690 * @default 1 50691 */ 50692 this.aspect = 1; 50693 50694 /** 50695 * The eye separation which represents the distance 50696 * between the left and right camera. 50697 * 50698 * @type {number} 50699 * @default 0.064 50700 */ 50701 this.eyeSep = 0.064; 50702 50703 /** 50704 * The camera representing the left eye. This is added to layer `1` so objects to be 50705 * rendered by the left camera must also be added to this layer. 50706 * 50707 * @type {PerspectiveCamera} 50708 */ 50709 this.cameraL = new PerspectiveCamera(); 50710 this.cameraL.layers.enable( 1 ); 50711 this.cameraL.matrixAutoUpdate = false; 50712 50713 /** 50714 * The camera representing the right eye. This is added to layer `2` so objects to be 50715 * rendered by the right camera must also be added to this layer. 50716 * 50717 * @type {PerspectiveCamera} 50718 */ 50719 this.cameraR = new PerspectiveCamera(); 50720 this.cameraR.layers.enable( 2 ); 50721 this.cameraR.matrixAutoUpdate = false; 50722 50723 this._cache = { 50724 focus: null, 50725 fov: null, 50726 aspect: null, 50727 near: null, 50728 far: null, 50729 zoom: null, 50730 eyeSep: null 50731 }; 50732 50733 } 50734 50735 /** 50736 * Updates the stereo camera based on the given perspective camera. 50737 * 50738 * @param {PerspectiveCamera} camera - The perspective camera. 50739 */ 50740 update( camera ) { 50741 50742 const cache = this._cache; 50743 50744 const needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || 50745 cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || 50746 cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; 50747 50748 if ( needsUpdate ) { 50749 50750 cache.focus = camera.focus; 50751 cache.fov = camera.fov; 50752 cache.aspect = camera.aspect * this.aspect; 50753 cache.near = camera.near; 50754 cache.far = camera.far; 50755 cache.zoom = camera.zoom; 50756 cache.eyeSep = this.eyeSep; 50757 50758 // Off-axis stereoscopic effect based on 50759 // http://paulbourke.net/stereographics/stereorender/ 50760 50761 _projectionMatrix.copy( camera.projectionMatrix ); 50762 const eyeSepHalf = cache.eyeSep / 2; 50763 const eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus; 50764 const ymax = ( cache.near * Math.tan( DEG2RAD * cache.fov * 0.5 ) ) / cache.zoom; 50765 let xmin, xmax; 50766 50767 // translate xOffset 50768 50769 _eyeLeft.elements[ 12 ] = - eyeSepHalf; 50770 _eyeRight.elements[ 12 ] = eyeSepHalf; 50771 50772 // for left eye 50773 50774 xmin = - ymax * cache.aspect + eyeSepOnProjection; 50775 xmax = ymax * cache.aspect + eyeSepOnProjection; 50776 50777 _projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); 50778 _projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); 50779 50780 this.cameraL.projectionMatrix.copy( _projectionMatrix ); 50781 50782 // for right eye 50783 50784 xmin = - ymax * cache.aspect - eyeSepOnProjection; 50785 xmax = ymax * cache.aspect - eyeSepOnProjection; 50786 50787 _projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); 50788 _projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); 50789 50790 this.cameraR.projectionMatrix.copy( _projectionMatrix ); 50791 50792 } 50793 50794 this.cameraL.matrix.copy( camera.matrixWorld ).multiply( _eyeLeft ); 50795 this.cameraL.matrixWorldNeedsUpdate = true; 50796 50797 this.cameraR.matrix.copy( camera.matrixWorld ).multiply( _eyeRight ); 50798 this.cameraR.matrixWorldNeedsUpdate = true; 50799 50800 } 50801 50802} 50803 50804const fov = -90; // negative fov is not an error 50805const aspect = 1; 50806 50807/** 50808 * A special type of camera that is positioned in 3D space to render its surroundings into a 50809 * cube render target. The render target can then be used as an environment map for rendering 50810 * realtime reflections in your scene. 50811 * 50812 * ```js 50813 * // Create cube render target 50814 * const cubeRenderTarget = new THREE.WebGLCubeRenderTarget( 256, { generateMipmaps: true, minFilter: THREE.LinearMipmapLinearFilter } ); 50815 * 50816 * // Create cube camera 50817 * const cubeCamera = new THREE.CubeCamera( 1, 100000, cubeRenderTarget ); 50818 * scene.add( cubeCamera ); 50819 * 50820 * // Create car 50821 * const chromeMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff, envMap: cubeRenderTarget.texture } ); 50822 * const car = new THREE.Mesh( carGeometry, chromeMaterial ); 50823 * scene.add( car ); 50824 * 50825 * // Update the render target cube 50826 * car.visible = false; 50827 * cubeCamera.position.copy( car.position ); 50828 * cubeCamera.update( renderer, scene ); 50829 * 50830 * // Render the scene 50831 * car.visible = true; 50832 * renderer.render( scene, camera ); 50833 * ``` 50834 * 50835 * @augments Object3D 50836 */ 50837class CubeCamera extends Object3D { 50838 50839 /** 50840 * Constructs a new cube camera. 50841 * 50842 * @param {number} near - The camera's near plane. 50843 * @param {number} far - The camera's far plane. 50844 * @param {WebGLCubeRenderTarget} renderTarget - The cube render target. 50845 */ 50846 constructor( near, far, renderTarget ) { 50847 50848 super(); 50849 50850 this.type = 'CubeCamera'; 50851 50852 /** 50853 * A reference to the cube render target. 50854 * 50855 * @type {WebGLCubeRenderTarget} 50856 */ 50857 this.renderTarget = renderTarget; 50858 50859 /** 50860 * The current active coordinate system. 50861 * 50862 * @type {?(WebGLCoordinateSystem|WebGPUCoordinateSystem)} 50863 * @default null 50864 */ 50865 this.coordinateSystem = null; 50866 50867 /** 50868 * The current active mipmap level 50869 * 50870 * @type {number} 50871 * @default 0 50872 */ 50873 this.activeMipmapLevel = 0; 50874 50875 const cameraPX = new PerspectiveCamera( fov, aspect, near, far ); 50876 cameraPX.layers = this.layers; 50877 this.add( cameraPX ); 50878 50879 const cameraNX = new PerspectiveCamera( fov, aspect, near, far ); 50880 cameraNX.layers = this.layers; 50881 this.add( cameraNX ); 50882 50883 const cameraPY = new PerspectiveCamera( fov, aspect, near, far ); 50884 cameraPY.layers = this.layers; 50885 this.add( cameraPY ); 50886 50887 const cameraNY = new PerspectiveCamera( fov, aspect, near, far ); 50888 cameraNY.layers = this.layers; 50889 this.add( cameraNY ); 50890 50891 const cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); 50892 cameraPZ.layers = this.layers; 50893 this.add( cameraPZ ); 50894 50895 const cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); 50896 cameraNZ.layers = this.layers; 50897 this.add( cameraNZ ); 50898 50899 } 50900 50901 /** 50902 * Must be called when the coordinate system of the cube camera is changed. 50903 */ 50904 updateCoordinateSystem() { 50905 50906 const coordinateSystem = this.coordinateSystem; 50907 50908 const cameras = this.children.concat(); 50909 50910 const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = cameras; 50911 50912 for ( const camera of cameras ) this.remove( camera ); 50913 50914 if ( coordinateSystem === WebGLCoordinateSystem ) { 50915 50916 cameraPX.up.set( 0, 1, 0 ); 50917 cameraPX.lookAt( 1, 0, 0 ); 50918 50919 cameraNX.up.set( 0, 1, 0 ); 50920 cameraNX.lookAt( -1, 0, 0 ); 50921 50922 cameraPY.up.set( 0, 0, -1 ); 50923 cameraPY.lookAt( 0, 1, 0 ); 50924 50925 cameraNY.up.set( 0, 0, 1 ); 50926 cameraNY.lookAt( 0, -1, 0 ); 50927 50928 cameraPZ.up.set( 0, 1, 0 ); 50929 cameraPZ.lookAt( 0, 0, 1 ); 50930 50931 cameraNZ.up.set( 0, 1, 0 ); 50932 cameraNZ.lookAt( 0, 0, -1 ); 50933 50934 } else if ( coordinateSystem === WebGPUCoordinateSystem ) { 50935 50936 cameraPX.up.set( 0, -1, 0 ); 50937 cameraPX.lookAt( -1, 0, 0 ); 50938 50939 cameraNX.up.set( 0, -1, 0 ); 50940 cameraNX.lookAt( 1, 0, 0 ); 50941 50942 cameraPY.up.set( 0, 0, 1 ); 50943 cameraPY.lookAt( 0, 1, 0 ); 50944 50945 cameraNY.up.set( 0, 0, -1 ); 50946 cameraNY.lookAt( 0, -1, 0 ); 50947 50948 cameraPZ.up.set( 0, -1, 0 ); 50949 cameraPZ.lookAt( 0, 0, 1 ); 50950 50951 cameraNZ.up.set( 0, -1, 0 ); 50952 cameraNZ.lookAt( 0, 0, -1 ); 50953 50954 } else { 50955 50956 throw new Error( 'THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: ' + coordinateSystem ); 50957 50958 } 50959 50960 for ( const camera of cameras ) { 50961 50962 this.add( camera ); 50963 50964 camera.updateMatrixWorld(); 50965 50966 } 50967 50968 } 50969 50970 /** 50971 * Calling this method will render the given scene with the given renderer 50972 * into the cube render target of the camera. 50973 * 50974 * @param {(Renderer|WebGLRenderer)} renderer - The renderer. 50975 * @param {Scene} scene - The scene to render. 50976 */ 50977 update( renderer, scene ) { 50978 50979 if ( this.parent === null ) this.updateMatrixWorld(); 50980 50981 const { renderTarget, activeMipmapLevel } = this; 50982 50983 if ( this.coordinateSystem !== renderer.coordinateSystem ) { 50984 50985 this.coordinateSystem = renderer.coordinateSystem; 50986 50987 this.updateCoordinateSystem(); 50988 50989 } 50990 50991 const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = this.children; 50992 50993 const currentRenderTarget = renderer.getRenderTarget(); 50994 const currentActiveCubeFace = renderer.getActiveCubeFace(); 50995 const currentActiveMipmapLevel = renderer.getActiveMipmapLevel(); 50996 50997 const currentXrEnabled = renderer.xr.enabled; 50998 50999 renderer.xr.enabled = false; 51000 51001 const generateMipmaps = renderTarget.texture.generateMipmaps; 51002 51003 renderTarget.texture.generateMipmaps = false; 51004 51005 // https://github.com/mrdoob/three.js/issues/31413#issuecomment-3095966812 51006 51007 let reversedDepthBuffer = false; 51008 51009 if ( renderer.isWebGLRenderer === true ) { 51010 51011 reversedDepthBuffer = renderer.state.buffers.depth.getReversed(); 51012 51013 } else { 51014 51015 reversedDepthBuffer = renderer.reversedDepthBuffer; 51016 51017 } 51018 51019 renderer.setRenderTarget( renderTarget, 0, activeMipmapLevel ); 51020 if ( reversedDepthBuffer && renderer.autoClear === false ) renderer.clearDepth(); 51021 renderer.render( scene, cameraPX ); 51022 51023 renderer.setRenderTarget( renderTarget, 1, activeMipmapLevel ); 51024 if ( reversedDepthBuffer && renderer.autoClear === false ) renderer.clearDepth(); 51025 renderer.render( scene, cameraNX ); 51026 51027 renderer.setRenderTarget( renderTarget, 2, activeMipmapLevel ); 51028 if ( reversedDepthBuffer && renderer.autoClear === false ) renderer.clearDepth(); 51029 renderer.render( scene, cameraPY ); 51030 51031 renderer.setRenderTarget( renderTarget, 3, activeMipmapLevel ); 51032 if ( reversedDepthBuffer && renderer.autoClear === false ) renderer.clearDepth(); 51033 renderer.render( scene, cameraNY ); 51034 51035 renderer.setRenderTarget( renderTarget, 4, activeMipmapLevel ); 51036 if ( reversedDepthBuffer && renderer.autoClear === false ) renderer.clearDepth(); 51037 renderer.render( scene, cameraPZ ); 51038 51039 // mipmaps are generated during the last call of render() 51040 // at this point, all sides of the cube render target are defined 51041 51042 renderTarget.texture.generateMipmaps = generateMipmaps; 51043 51044 renderer.setRenderTarget( renderTarget, 5, activeMipmapLevel ); 51045 if ( reversedDepthBuffer && renderer.autoClear === false ) renderer.clearDepth(); 51046 renderer.render( scene, cameraNZ ); 51047 51048 renderer.setRenderTarget( currentRenderTarget, currentActiveCubeFace, currentActiveMipmapLevel ); 51049 51050 renderer.xr.enabled = currentXrEnabled; 51051 51052 renderTarget.texture.needsPMREMUpdate = true; 51053 51054 } 51055 51056} 51057 51058/** 51059 * This type of camera can be used in order to efficiently render a scene with a 51060 * predefined set of cameras. This is an important performance aspect for 51061 * rendering VR scenes. 51062 * 51063 * An instance of `ArrayCamera` always has an array of sub cameras. It's mandatory 51064 * to define for each sub camera the `viewport` property which determines the 51065 * part of the viewport that is rendered with this camera. 51066 * 51067 * @augments PerspectiveCamera 51068 */ 51069class ArrayCamera extends PerspectiveCamera { 51070 51071 /** 51072 * Constructs a new array camera. 51073 * 51074 * @param {Array<PerspectiveCamera>} [array=[]] - An array of perspective sub cameras. 51075 */ 51076 constructor( array = [] ) { 51077 51078 super(); 51079 51080 /** 51081 * This flag can be used for type testing. 51082 * 51083 * @type {boolean} 51084 * @readonly 51085 * @default true 51086 */ 51087 this.isArrayCamera = true; 51088 51089 /** 51090 * Whether this camera is used with multiview rendering or not. 51091 * 51092 * @type {boolean} 51093 * @readonly 51094 * @default false 51095 */ 51096 this.isMultiViewCamera = false; 51097 51098 /** 51099 * An array of perspective sub cameras. 51100 * 51101 * @type {Array<PerspectiveCamera>} 51102 */ 51103 this.cameras = array; 51104 51105 } 51106 51107} 51108 51109/** 51110 * This class is an alternative to {@link Clock} with a different API design and behavior. 51111 * The goal is to avoid the conceptual flaws that became apparent in `Clock` over time. 51112 * 51113 * - `Timer` has an `update()` method that updates its internal state. That makes it possible to 51114 * call `getDelta()` and `getElapsed()` multiple times per simulation step without getting different values. 51115 * - The class can make use of the Page Visibility API to avoid large time delta values when the app 51116 * is inactive (e.g. tab switched or browser hidden). 51117 * 51118 * ```js 51119 * const timer = new Timer(); 51120 * timer.connect( document ); // use Page Visibility API 51121 * ``` 51122 */ 51123class Timer { 51124 51125 /** 51126 * Constructs a new timer. 51127 */ 51128 constructor() { 51129 51130 this._previousTime = 0; 51131 this._currentTime = 0; 51132 this._startTime = performance.now(); 51133 51134 this._delta = 0; 51135 this._elapsed = 0; 51136 51137 this._timescale = 1; 51138 51139 this._document = null; 51140 this._pageVisibilityHandler = null; 51141 51142 } 51143 51144 /** 51145 * Connect the timer to the given document.Calling this method is not mandatory to 51146 * use the timer but enables the usage of the Page Visibility API to avoid large time 51147 * delta values. 51148 * 51149 * @param {Document} document - The document. 51150 */ 51151 connect( document ) { 51152 51153 this._document = document; 51154 51155 // use Page Visibility API to avoid large time delta values 51156 51157 if ( document.hidden !== undefined ) { 51158 51159 this._pageVisibilityHandler = handleVisibilityChange.bind( this ); 51160 51161 document.addEventListener( 'visibilitychange', this._pageVisibilityHandler, false ); 51162 51163 } 51164 51165 } 51166 51167 /** 51168 * Disconnects the timer from the DOM and also disables the usage of the Page Visibility API. 51169 */ 51170 disconnect() { 51171 51172 if ( this._pageVisibilityHandler !== null ) { 51173 51174 this._document.removeEventListener( 'visibilitychange', this._pageVisibilityHandler ); 51175 this._pageVisibilityHandler = null; 51176 51177 } 51178 51179 this._document = null; 51180 51181 } 51182 51183 /** 51184 * Returns the time delta in seconds. 51185 * 51186 * @return {number} The time delta in second. 51187 */ 51188 getDelta() { 51189 51190 return this._delta / 1000; 51191 51192 } 51193 51194 /** 51195 * Returns the elapsed time in seconds. 51196 * 51197 * @return {number} The elapsed time in second. 51198 */ 51199 getElapsed() { 51200 51201 return this._elapsed / 1000; 51202 51203 } 51204 51205 /** 51206 * Returns the timescale. 51207 * 51208 * @return {number} The timescale. 51209 */ 51210 getTimescale() { 51211 51212 return this._timescale; 51213 51214 } 51215 51216 /** 51217 * Sets the given timescale which scale the time delta computation 51218 * in `update()`. 51219 * 51220 * @param {number} timescale - The timescale to set. 51221 * @return {Timer} A reference to this timer. 51222 */ 51223 setTimescale( timescale ) { 51224 51225 this._timescale = timescale; 51226 51227 return this; 51228 51229 } 51230 51231 /** 51232 * Resets the time computation for the current simulation step. 51233 * 51234 * @return {Timer} A reference to this timer. 51235 */ 51236 reset() { 51237 51238 this._currentTime = performance.now() - this._startTime; 51239 51240 return this; 51241 51242 } 51243 51244 /** 51245 * Can be used to free all internal resources. Usually called when 51246 * the timer instance isn't required anymore. 51247 */ 51248 dispose() { 51249 51250 this.disconnect(); 51251 51252 } 51253 51254 /** 51255 * Updates the internal state of the timer. This method should be called 51256 * once per simulation step and before you perform queries against the timer 51257 * (e.g. via `getDelta()`). 51258 * 51259 * @param {number} timestamp - The current time in milliseconds. Can be obtained 51260 * from the `requestAnimationFrame` callback argument. If not provided, the current 51261 * time will be determined with `performance.now`. 51262 * @return {Timer} A reference to this timer. 51263 */ 51264 update( timestamp ) { 51265 51266 if ( this._pageVisibilityHandler !== null && this._document.hidden === true ) { 51267 51268 this._delta = 0; 51269 51270 } else { 51271 51272 this._previousTime = this._currentTime; 51273 this._currentTime = ( timestamp !== undefined ? timestamp : performance.now() ) - this._startTime; 51274 51275 this._delta = ( this._currentTime - this._previousTime ) * this._timescale; 51276 this._elapsed += this._delta; // _elapsed is the accumulation of all previous deltas 51277 51278 } 51279 51280 return this; 51281 51282 } 51283 51284} 51285 51286function handleVisibilityChange() { 51287 51288 if ( this._document.hidden === false ) this.reset(); 51289 51290} 51291 51292const _position$1 = /*@__PURE__*/ new Vector3(); 51293const _quaternion$1 = /*@__PURE__*/ new Quaternion(); 51294const _scale$1 = /*@__PURE__*/ new Vector3(); 51295 51296const _forward = /*@__PURE__*/ new Vector3(); 51297const _up = /*@__PURE__*/ new Vector3(); 51298 51299/** 51300 * The class represents a virtual listener of the all positional and non-positional audio effects 51301 * in the scene. A three.js application usually creates a single listener. It is a mandatory 51302 * constructor parameter for audios entities like {@link Audio} and {@link PositionalAudio}. 51303 * 51304 * In most cases, the listener object is a child of the camera. So the 3D transformation of the 51305 * camera represents the 3D transformation of the listener. 51306 * 51307 * @augments Object3D 51308 */ 51309class AudioListener extends Object3D { 51310 51311 /** 51312 * Constructs a new audio listener. 51313 */ 51314 constructor() { 51315 51316 super(); 51317 51318 this.type = 'AudioListener'; 51319 51320 /** 51321 * The native audio context. 51322 * 51323 * @type {AudioContext} 51324 * @readonly 51325 */ 51326 this.context = AudioContext.getContext(); 51327 51328 /** 51329 * The gain node used for volume control. 51330 * 51331 * @type {GainNode} 51332 * @readonly 51333 */ 51334 this.gain = this.context.createGain(); 51335 this.gain.connect( this.context.destination ); 51336 51337 /** 51338 * An optional filter. 51339 * 51340 * Defined via {@link AudioListener#setFilter}. 51341 * 51342 * @type {?AudioNode} 51343 * @default null 51344 * @readonly 51345 */ 51346 this.filter = null; 51347 51348 /** 51349 * Time delta values required for `linearRampToValueAtTime()` usage. 51350 * 51351 * @type {number} 51352 * @default 0 51353 * @readonly 51354 */ 51355 this.timeDelta = 0; 51356 51357 // private 51358 51359 this._timer = new Timer(); 51360 51361 } 51362 51363 /** 51364 * Returns the listener's input node. 51365 * 51366 * This method is used by other audio nodes to connect to this listener. 51367 * 51368 * @return {GainNode} The input node. 51369 */ 51370 getInput() { 51371 51372 return this.gain; 51373 51374 } 51375 51376 /** 51377 * Removes the current filter from this listener. 51378 * 51379 * @return {AudioListener} A reference to this listener. 51380 */ 51381 removeFilter() { 51382 51383 if ( this.filter !== null ) { 51384 51385 this.gain.disconnect( this.filter ); 51386 this.filter.disconnect( this.context.destination ); 51387 this.gain.connect( this.context.destination ); 51388 this.filter = null; 51389 51390 } 51391 51392 return this; 51393 51394 } 51395 51396 /** 51397 * Returns the current set filter. 51398 * 51399 * @return {?AudioNode} The filter. 51400 */ 51401 getFilter() { 51402 51403 return this.filter; 51404 51405 } 51406 51407 /** 51408 * Sets the given filter to this listener. 51409 * 51410 * @param {AudioNode} value - The filter to set. 51411 * @return {AudioListener} A reference to this listener. 51412 */ 51413 setFilter( value ) { 51414 51415 if ( this.filter !== null ) { 51416 51417 this.gain.disconnect( this.filter ); 51418 this.filter.disconnect( this.context.destination ); 51419 51420 } else { 51421 51422 this.gain.disconnect( this.context.destination ); 51423 51424 } 51425 51426 this.filter = value; 51427 this.gain.connect( this.filter ); 51428 this.filter.connect( this.context.destination ); 51429 51430 return this; 51431 51432 } 51433 51434 /** 51435 * Returns the applications master volume. 51436 * 51437 * @return {number} The master volume. 51438 */ 51439 getMasterVolume() { 51440 51441 return this.gain.gain.value; 51442 51443 } 51444 51445 /** 51446 * Sets the applications master volume. This volume setting affects 51447 * all audio nodes in the scene. 51448 * 51449 * @param {number} value - The master volume to set. 51450 * @return {AudioListener} A reference to this listener. 51451 */ 51452 setMasterVolume( value ) { 51453 51454 this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); 51455 51456 return this; 51457 51458 } 51459 51460 updateMatrixWorld( force ) { 51461 51462 super.updateMatrixWorld( force ); 51463 51464 this._timer.update(); 51465 51466 const listener = this.context.listener; 51467 51468 this.timeDelta = this._timer.getDelta(); 51469 51470 this.matrixWorld.decompose( _position$1, _quaternion$1, _scale$1 ); 51471 51472 // the initial forward and up directions must be orthogonal 51473 _forward.set( 0, 0, -1 ).applyQuaternion( _quaternion$1 ); 51474 _up.set( 0, 1, 0 ).applyQuaternion( _quaternion$1 ); 51475 51476 if ( listener.positionX ) { 51477 51478 // code path for Chrome (see #14393) 51479 51480 const endTime = this.context.currentTime + this.timeDelta; 51481 51482 listener.positionX.linearRampToValueAtTime( _position$1.x, endTime ); 51483 listener.positionY.linearRampToValueAtTime( _position$1.y, endTime ); 51484 listener.positionZ.linearRampToValueAtTime( _position$1.z, endTime ); 51485 listener.forwardX.linearRampToValueAtTime( _forward.x, endTime ); 51486 listener.forwardY.linearRampToValueAtTime( _forward.y, endTime ); 51487 listener.forwardZ.linearRampToValueAtTime( _forward.z, endTime ); 51488 listener.upX.linearRampToValueAtTime( _up.x, endTime ); 51489 listener.upY.linearRampToValueAtTime( _up.y, endTime ); 51490 listener.upZ.linearRampToValueAtTime( _up.z, endTime ); 51491 51492 } else { 51493 51494 listener.setPosition( _position$1.x, _position$1.y, _position$1.z ); 51495 listener.setOrientation( _forward.x, _forward.y, _forward.z, _up.x, _up.y, _up.z ); 51496 51497 } 51498 51499 } 51500 51501} 51502 51503/** 51504 * Represents a non-positional ( global ) audio object. 51505 * 51506 * This and related audio modules make use of the [Web Audio API](https://www.w3.org/TR/webaudio-1.1/). 51507 * 51508 * ```js 51509 * // create an AudioListener and add it to the camera 51510 * const listener = new THREE.AudioListener(); 51511 * camera.add( listener ); 51512 * 51513 * // create a global audio source 51514 * const sound = new THREE.Audio( listener ); 51515 * 51516 * // load a sound and set it as the Audio object's buffer 51517 * const audioLoader = new THREE.AudioLoader(); 51518 * audioLoader.load( 'sounds/ambient.ogg', function( buffer ) { 51519 * sound.setBuffer( buffer ); 51520 * sound.setLoop( true ); 51521 * sound.setVolume( 0.5 ); 51522 * sound.play(); 51523 * }); 51524 * ``` 51525 * 51526 * @augments Object3D 51527 */ 51528class Audio extends Object3D { 51529 51530 /** 51531 * Constructs a new audio. 51532 * 51533 * @param {AudioListener} listener - The global audio listener. 51534 */ 51535 constructor( listener ) { 51536 51537 super(); 51538 51539 this.type = 'Audio'; 51540 51541 /** 51542 * The global audio listener. 51543 * 51544 * @type {AudioListener} 51545 * @readonly 51546 */ 51547 this.listener = listener; 51548 51549 /** 51550 * The audio context. 51551 * 51552 * @type {AudioContext} 51553 * @readonly 51554 */ 51555 this.context = listener.context; 51556 51557 /** 51558 * The gain node used for volume control. 51559 * 51560 * @type {GainNode} 51561 * @readonly 51562 */ 51563 this.gain = this.context.createGain(); 51564 this.gain.connect( listener.getInput() ); 51565 51566 /** 51567 * Whether to start playback automatically or not. 51568 * 51569 * @type {boolean} 51570 * @default false 51571 */ 51572 this.autoplay = false; 51573 51574 /** 51575 * A reference to an audio buffer. 51576 * 51577 * Defined via {@link Audio#setBuffer}. 51578 * 51579 * @type {?AudioBuffer} 51580 * @default null 51581 * @readonly 51582 */ 51583 this.buffer = null; 51584 51585 /** 51586 * Modify pitch, measured in cents. +/- 100 is a semitone. 51587 * +/- 1200 is an octave. 51588 * 51589 * Defined via {@link Audio#setDetune}. 51590 * 51591 * @type {number} 51592 * @default 0 51593 * @readonly 51594 */ 51595 this.detune = 0; 51596 51597 /** 51598 * Whether the audio should loop or not. 51599 * 51600 * Defined via {@link Audio#setLoop}. 51601 * 51602 * @type {boolean} 51603 * @default false 51604 * @readonly 51605 */ 51606 this.loop = false; 51607 51608 /** 51609 * Defines where in the audio buffer the replay should 51610 * start, in seconds. 51611 * 51612 * @type {number} 51613 * @default 0 51614 */ 51615 this.loopStart = 0; 51616 51617 /** 51618 * Defines where in the audio buffer the replay should 51619 * stop, in seconds. 51620 * 51621 * @type {number} 51622 * @default 0 51623 */ 51624 this.loopEnd = 0; 51625 51626 /** 51627 * An offset to the time within the audio buffer the playback 51628 * should begin, in seconds. 51629 * 51630 * @type {number} 51631 * @default 0 51632 */ 51633 this.offset = 0; 51634 51635 /** 51636 * Overrides the default duration of the audio. 51637 * 51638 * @type {undefined|number} 51639 * @default undefined 51640 */ 51641 this.duration = undefined; 51642 51643 /** 51644 * The playback speed. 51645 * 51646 * Defined via {@link Audio#setPlaybackRate}. 51647 * 51648 * @type {number} 51649 * @readonly 51650 * @default 1 51651 */ 51652 this.playbackRate = 1; 51653 51654 /** 51655 * Indicates whether the audio is playing or not. 51656 * 51657 * This flag will be automatically set when using {@link Audio#play}, 51658 * {@link Audio#pause}, {@link Audio#stop}. 51659 * 51660 * @type {boolean} 51661 * @readonly 51662 * @default false 51663 */ 51664 this.isPlaying = false; 51665 51666 /** 51667 * Indicates whether the audio playback can be controlled 51668 * with method like {@link Audio#play} or {@link Audio#pause}. 51669 * 51670 * This flag will be automatically set when audio sources are 51671 * defined. 51672 * 51673 * @type {boolean} 51674 * @readonly 51675 * @default true 51676 */ 51677 this.hasPlaybackControl = true; 51678 51679 /** 51680 * Holds a reference to the current audio source. 51681 * 51682 * The property is automatically by one of the `set*()` methods. 51683 * 51684 * @type {?AudioNode} 51685 * @readonly 51686 * @default null 51687 */ 51688 this.source = null; 51689 51690 /** 51691 * Defines the source type. 51692 * 51693 * The property is automatically set by one of the `set*()` methods. 51694 * 51695 * @type {('empty'|'audioNode'|'mediaNode'|'mediaStreamNode'|'buffer')} 51696 * @readonly 51697 * @default 'empty' 51698 */ 51699 this.sourceType = 'empty'; 51700 51701 this._startedAt = 0; 51702 this._progress = 0; 51703 this._connected = false; 51704 51705 /** 51706 * Can be used to apply a variety of low-order filters to create 51707 * more complex sound effects e.g. via `BiquadFilterNode`. 51708 * 51709 * The property is automatically set by {@link Audio#setFilters}. 51710 * 51711 * @type {Array<AudioNode>} 51712 * @readonly 51713 */ 51714 this.filters = []; 51715 51716 } 51717 51718 /** 51719 * Returns the output audio node. 51720 * 51721 * @return {GainNode} The output node. 51722 */ 51723 getOutput() { 51724 51725 return this.gain; 51726 51727 } 51728 51729 /** 51730 * Sets the given audio node as the source of this instance. 51731 * 51732 * {@link Audio#sourceType} is set to `audioNode` and {@link Audio#hasPlaybackControl} to `false`. 51733 * 51734 * @param {AudioNode} audioNode - The audio node like an instance of `OscillatorNode`. 51735 * @return {Audio} A reference to this instance. 51736 */ 51737 setNodeSource( audioNode ) { 51738 51739 this.hasPlaybackControl = false; 51740 this.sourceType = 'audioNode'; 51741 this.source = audioNode; 51742 this.connect(); 51743 51744 return this; 51745 51746 } 51747 51748 /** 51749 * Sets the given media element as the source of this instance. 51750 * 51751 * {@link Audio#sourceType} is set to `mediaNode` and {@link Audio#hasPlaybackControl} to `false`. 51752 * 51753 * @param {HTMLMediaElement} mediaElement - The media element. 51754 * @return {Audio} A reference to this instance. 51755 */ 51756 setMediaElementSource( mediaElement ) { 51757 51758 this.hasPlaybackControl = false; 51759 this.sourceType = 'mediaNode'; 51760 this.source = this.context.createMediaElementSource( mediaElement ); 51761 this.connect(); 51762 51763 return this; 51764 51765 } 51766 51767 /** 51768 * Sets the given media stream as the source of this instance. 51769 * 51770 * {@link Audio#sourceType} is set to `mediaStreamNode` and {@link Audio#hasPlaybackControl} to `false`. 51771 * 51772 * @param {MediaStream} mediaStream - The media stream. 51773 * @return {Audio} A reference to this instance. 51774 */ 51775 setMediaStreamSource( mediaStream ) { 51776 51777 this.hasPlaybackControl = false; 51778 this.sourceType = 'mediaStreamNode'; 51779 this.source = this.context.createMediaStreamSource( mediaStream ); 51780 this.connect(); 51781 51782 return this; 51783 51784 } 51785 51786 /** 51787 * Sets the given audio buffer as the source of this instance. 51788 * 51789 * {@link Audio#sourceType} is set to `buffer` and {@link Audio#hasPlaybackControl} to `true`. 51790 * 51791 * @param {AudioBuffer} audioBuffer - The audio buffer. 51792 * @return {Audio} A reference to this instance. 51793 */ 51794 setBuffer( audioBuffer ) { 51795 51796 this.buffer = audioBuffer; 51797 this.sourceType = 'buffer'; 51798 51799 if ( this.autoplay ) this.play(); 51800 51801 return this; 51802 51803 } 51804 51805 /** 51806 * Starts the playback of the audio. 51807 * 51808 * Can only be used with compatible audio sources that allow playback control. 51809 * 51810 * @param {number} [delay=0] - The delay, in seconds, at which the audio should start playing. 51811 * @return {Audio|undefined} A reference to this instance. 51812 */ 51813 play( delay = 0 ) { 51814 51815 if ( this.isPlaying === true ) { 51816 51817 warn( 'Audio: Audio is already playing.' ); 51818 return; 51819 51820 } 51821 51822 if ( this.hasPlaybackControl === false ) { 51823 51824 warn( 'Audio: this Audio has no playback control.' ); 51825 return; 51826 51827 } 51828 51829 this._startedAt = this.context.currentTime + delay; 51830 51831 const source = this.context.createBufferSource(); 51832 source.buffer = this.buffer; 51833 source.loop = this.loop; 51834 source.loopStart = this.loopStart; 51835 source.loopEnd = this.loopEnd; 51836 source.onended = this.onEnded.bind( this ); 51837 source.start( this._startedAt, this._progress + this.offset, this.duration ); 51838 51839 this.isPlaying = true; 51840 51841 this.source = source; 51842 51843 this.setDetune( this.detune ); 51844 this.setPlaybackRate( this.playbackRate ); 51845 51846 return this.connect(); 51847 51848 } 51849 51850 /** 51851 * Pauses the playback of the audio. 51852 * 51853 * Can only be used with compatible audio sources that allow playback control. 51854 * 51855 * @return {Audio|undefined} A reference to this instance. 51856 */ 51857 pause() { 51858 51859 if ( this.hasPlaybackControl === false ) { 51860 51861 warn( 'Audio: this Audio has no playback control.' ); 51862 return; 51863 51864 } 51865 51866 if ( this.isPlaying === true ) { 51867 51868 // update current progress 51869 51870 this._progress += Math.max( this.context.currentTime - this._startedAt, 0 ) * this.playbackRate; 51871 51872 if ( this.loop === true ) { 51873 51874 // ensure _progress does not exceed duration with looped audios 51875 51876 this._progress = this._progress % ( this.duration || this.buffer.duration ); 51877 51878 } 51879 51880 this.source.stop(); 51881 this.source.onended = null; 51882 51883 this.isPlaying = false; 51884 51885 } 51886 51887 return this; 51888 51889 } 51890 51891 /** 51892 * Stops the playback of the audio. 51893 * 51894 * Can only be used with compatible audio sources that allow playback control. 51895 * 51896 * @param {number} [delay=0] - The delay, in seconds, at which the audio should stop playing. 51897 * @return {Audio|undefined} A reference to this instance. 51898 */ 51899 stop( delay = 0 ) { 51900 51901 if ( this.hasPlaybackControl === false ) { 51902 51903 warn( 'Audio: this Audio has no playback control.' ); 51904 return; 51905 51906 } 51907 51908 this._progress = 0; 51909 51910 if ( this.source !== null ) { 51911 51912 this.source.stop( this.context.currentTime + delay ); 51913 this.source.onended = null; 51914 51915 } 51916 51917 this.isPlaying = false; 51918 51919 return this; 51920 51921 } 51922 51923 /** 51924 * Connects to the audio source. This is used internally on 51925 * initialisation and when setting / removing filters. 51926 * 51927 * @return {Audio} A reference to this instance. 51928 */ 51929 connect() { 51930 51931 if ( this.filters.length > 0 ) { 51932 51933 this.source.connect( this.filters[ 0 ] ); 51934 51935 for ( let i = 1, l = this.filters.length; i < l; i ++ ) { 51936 51937 this.filters[ i - 1 ].connect( this.filters[ i ] ); 51938 51939 } 51940 51941 this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); 51942 51943 } else { 51944 51945 this.source.connect( this.getOutput() ); 51946 51947 } 51948 51949 this._connected = true; 51950 51951 return this; 51952 51953 } 51954 51955 /** 51956 * Disconnects to the audio source. This is used internally on 51957 * initialisation and when setting / removing filters. 51958 * 51959 * @return {Audio|undefined} A reference to this instance. 51960 */ 51961 disconnect() { 51962 51963 if ( this._connected === false ) { 51964 51965 return; 51966 51967 } 51968 51969 if ( this.filters.length > 0 ) { 51970 51971 this.source.disconnect( this.filters[ 0 ] ); 51972 51973 for ( let i = 1, l = this.filters.length; i < l; i ++ ) { 51974 51975 this.filters[ i - 1 ].disconnect( this.filters[ i ] ); 51976 51977 } 51978 51979 this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); 51980 51981 } else { 51982 51983 this.source.disconnect( this.getOutput() ); 51984 51985 } 51986 51987 this._connected = false; 51988 51989 return this; 51990 51991 } 51992 51993 /** 51994 * Returns the current set filters. 51995 * 51996 * @return {Array<AudioNode>} The list of filters. 51997 */ 51998 getFilters() { 51999 52000 return this.filters; 52001 52002 } 52003 52004 /** 52005 * Sets an array of filters and connects them with the audio source. 52006 * 52007 * @param {Array<AudioNode>} [value] - A list of filters. 52008 * @return {Audio} A reference to this instance. 52009 */ 52010 setFilters( value ) { 52011 52012 if ( ! value ) value = []; 52013 52014 if ( this._connected === true ) { 52015 52016 this.disconnect(); 52017 this.filters = value.slice(); 52018 this.connect(); 52019 52020 } else { 52021 52022 this.filters = value.slice(); 52023 52024 } 52025 52026 return this; 52027 52028 } 52029 52030 /** 52031 * Defines the detuning of oscillation in cents. 52032 * 52033 * @param {number} value - The detuning of oscillation in cents. 52034 * @return {Audio} A reference to this instance. 52035 */ 52036 setDetune( value ) { 52037 52038 this.detune = value; 52039 52040 if ( this.isPlaying === true && this.source.detune !== undefined ) { 52041 52042 this.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01 ); 52043 52044 } 52045 52046 return this; 52047 52048 } 52049 52050 /** 52051 * Returns the detuning of oscillation in cents. 52052 * 52053 * @return {number} The detuning of oscillation in cents. 52054 */ 52055 getDetune() { 52056 52057 return this.detune; 52058 52059 } 52060 52061 /** 52062 * Returns the first filter in the list of filters. 52063 * 52064 * @return {AudioNode|undefined} The first filter in the list of filters. 52065 */ 52066 getFilter() { 52067 52068 return this.getFilters()[ 0 ]; 52069 52070 } 52071 52072 /** 52073 * Applies a single filter node to the audio. 52074 * 52075 * @param {AudioNode} [filter] - The filter to set. 52076 * @return {Audio} A reference to this instance. 52077 */ 52078 setFilter( filter ) { 52079 52080 return this.setFilters( filter ? [ filter ] : [] ); 52081 52082 } 52083 52084 /** 52085 * Sets the playback rate. 52086 * 52087 * Can only be used with compatible audio sources that allow playback control. 52088 * 52089 * @param {number} [value] - The playback rate to set. 52090 * @return {Audio|undefined} A reference to this instance. 52091 */ 52092 setPlaybackRate( value ) { 52093 52094 if ( this.hasPlaybackControl === false ) { 52095 52096 warn( 'Audio: this Audio has no playback control.' ); 52097 return; 52098 52099 } 52100 52101 this.playbackRate = value; 52102 52103 if ( this.isPlaying === true ) { 52104 52105 this.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01 ); 52106 52107 } 52108 52109 return this; 52110 52111 } 52112 52113 /** 52114 * Returns the current playback rate. 52115 52116 * @return {number} The playback rate. 52117 */ 52118 getPlaybackRate() { 52119 52120 return this.playbackRate; 52121 52122 } 52123 52124 /** 52125 * Automatically called when playback finished. 52126 */ 52127 onEnded() { 52128 52129 this.isPlaying = false; 52130 this._progress = 0; 52131 52132 } 52133 52134 /** 52135 * Returns the loop flag. 52136 * 52137 * Can only be used with compatible audio sources that allow playback control. 52138 * 52139 * @return {boolean} Whether the audio should loop or not. 52140 */ 52141 getLoop() { 52142 52143 if ( this.hasPlaybackControl === false ) { 52144 52145 warn( 'Audio: this Audio has no playback control.' ); 52146 return false; 52147 52148 } 52149 52150 return this.loop; 52151 52152 } 52153 52154 /** 52155 * Sets the loop flag. 52156 * 52157 * Can only be used with compatible audio sources that allow playback control. 52158 * 52159 * @param {boolean} value - Whether the audio should loop or not. 52160 * @return {Audio|undefined} A reference to this instance. 52161 */ 52162 setLoop( value ) { 52163 52164 if ( this.hasPlaybackControl === false ) { 52165 52166 warn( 'Audio: this Audio has no playback control.' ); 52167 return; 52168 52169 } 52170 52171 this.loop = value; 52172 52173 if ( this.isPlaying === true ) { 52174 52175 this.source.loop = this.loop; 52176 52177 } 52178 52179 return this; 52180 52181 } 52182 52183 /** 52184 * Sets the loop start value which defines where in the audio buffer the replay should 52185 * start, in seconds. 52186 * 52187 * @param {number} value - The loop start value. 52188 * @return {Audio} A reference to this instance. 52189 */ 52190 setLoopStart( value ) { 52191 52192 this.loopStart = value; 52193 52194 return this; 52195 52196 } 52197 52198 /** 52199 * Sets the loop end value which defines where in the audio buffer the replay should 52200 * stop, in seconds. 52201 * 52202 * @param {number} value - The loop end value. 52203 * @return {Audio} A reference to this instance. 52204 */ 52205 setLoopEnd( value ) { 52206 52207 this.loopEnd = value; 52208 52209 return this; 52210 52211 } 52212 52213 /** 52214 * Returns the volume. 52215 * 52216 * @return {number} The volume. 52217 */ 52218 getVolume() { 52219 52220 return this.gain.gain.value; 52221 52222 } 52223 52224 /** 52225 * Sets the volume. 52226 * 52227 * @param {number} value - The volume to set. 52228 * @return {Audio} A reference to this instance. 52229 */ 52230 setVolume( value ) { 52231 52232 this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); 52233 52234 return this; 52235 52236 } 52237 52238 copy( source, recursive ) { 52239 52240 super.copy( source, recursive ); 52241 52242 if ( source.sourceType !== 'buffer' ) { 52243 52244 warn( 'Audio: Audio source type cannot be copied.' ); 52245 52246 return this; 52247 52248 } 52249 52250 this.autoplay = source.autoplay; 52251 52252 this.buffer = source.buffer; 52253 this.detune = source.detune; 52254 this.loop = source.loop; 52255 this.loopStart = source.loopStart; 52256 this.loopEnd = source.loopEnd; 52257 this.offset = source.offset; 52258 this.duration = source.duration; 52259 this.playbackRate = source.playbackRate; 52260 this.hasPlaybackControl = source.hasPlaybackControl; 52261 this.sourceType = source.sourceType; 52262 52263 this.filters = source.filters.slice(); 52264 52265 return this; 52266 52267 } 52268 52269 clone( recursive ) { 52270 52271 return new this.constructor( this.listener ).copy( this, recursive ); 52272 52273 } 52274 52275} 52276 52277const _position = /*@__PURE__*/ new Vector3(); 52278const _quaternion = /*@__PURE__*/ new Quaternion(); 52279const _scale = /*@__PURE__*/ new Vector3(); 52280const _orientation = /*@__PURE__*/ new Vector3(); 52281 52282/** 52283 * Represents a positional audio object. 52284 * 52285 * ```js 52286 * // create an AudioListener and add it to the camera 52287 * const listener = new THREE.AudioListener(); 52288 * camera.add( listener ); 52289 * 52290 * // create the PositionalAudio object (passing in the listener) 52291 * const sound = new THREE.PositionalAudio( listener ); 52292 * 52293 * // load a sound and set it as the PositionalAudio object's buffer 52294 * const audioLoader = new THREE.AudioLoader(); 52295 * audioLoader.load( 'sounds/song.ogg', function( buffer ) { 52296 * sound.setBuffer( buffer ); 52297 * sound.setRefDistance( 20 ); 52298 * sound.play(); 52299 * }); 52300 * 52301 * // create an object for the sound to play from 52302 * const sphere = new THREE.SphereGeometry( 20, 32, 16 ); 52303 * const material = new THREE.MeshPhongMaterial( { color: 0xff2200 } ); 52304 * const mesh = new THREE.Mesh( sphere, material ); 52305 * scene.add( mesh ); 52306 * 52307 * // finally add the sound to the mesh 52308 * mesh.add( sound ); 52309 * 52310 * @augments Audio 52311 */ 52312class PositionalAudio extends Audio { 52313 52314 /** 52315 * Constructs a positional audio. 52316 * 52317 * @param {AudioListener} listener - The global audio listener. 52318 */ 52319 constructor( listener ) { 52320 52321 super( listener ); 52322 52323 /** 52324 * The panner node represents the location, direction, and behavior of an audio 52325 * source in 3D space. 52326 * 52327 * @type {PannerNode} 52328 * @readonly 52329 */ 52330 this.panner = this.context.createPanner(); 52331 this.panner.panningModel = 'HRTF'; 52332 this.panner.connect( this.gain ); 52333 52334 } 52335 52336 connect() { 52337 52338 super.connect(); 52339 52340 this.panner.connect( this.gain ); 52341 52342 return this; 52343 52344 } 52345 52346 disconnect() { 52347 52348 super.disconnect(); 52349 52350 this.panner.disconnect( this.gain ); 52351 52352 return this; 52353 52354 } 52355 52356 getOutput() { 52357 52358 return this.panner; 52359 52360 } 52361 52362 /** 52363 * Returns the current reference distance. 52364 * 52365 * @return {number} The reference distance. 52366 */ 52367 getRefDistance() { 52368 52369 return this.panner.refDistance; 52370 52371 } 52372 52373 /** 52374 * Defines the reference distance for reducing volume as the audio source moves 52375 * further from the listener â i.e. the distance at which the volume reduction 52376 * starts taking effect. 52377 * 52378 * @param {number} value - The reference distance to set. 52379 * @return {PositionalAudio} A reference to this instance. 52380 */ 52381 setRefDistance( value ) { 52382 52383 this.panner.refDistance = value; 52384 52385 return this; 52386 52387 } 52388 52389 /** 52390 * Returns the current rolloff factor. 52391 * 52392 * @return {number} The rolloff factor. 52393 */ 52394 getRolloffFactor() { 52395 52396 return this.panner.rolloffFactor; 52397 52398 } 52399 52400 /** 52401 * Defines how quickly the volume is reduced as the source moves away from the listener. 52402 * 52403 * @param {number} value - The rolloff factor. 52404 * @return {PositionalAudio} A reference to this instance. 52405 */ 52406 setRolloffFactor( value ) { 52407 52408 this.panner.rolloffFactor = value; 52409 52410 return this; 52411 52412 } 52413 52414 /** 52415 * Returns the current distance model. 52416 * 52417 * @return {('linear'|'inverse'|'exponential')} The distance model. 52418 */ 52419 getDistanceModel() { 52420 52421 return this.panner.distanceModel; 52422 52423 } 52424 52425 /** 52426 * Defines which algorithm to use to reduce the volume of the audio source 52427 * as it moves away from the listener. 52428 * 52429 * Read [the spec](https://www.w3.org/TR/webaudio-1.1/#enumdef-distancemodeltype) 52430 * for more details. 52431 * 52432 * @param {('linear'|'inverse'|'exponential')} value - The distance model to set. 52433 * @return {PositionalAudio} A reference to this instance. 52434 */ 52435 setDistanceModel( value ) { 52436 52437 this.panner.distanceModel = value; 52438 52439 return this; 52440 52441 } 52442 52443 /** 52444 * Returns the current max distance. 52445 * 52446 * @return {number} The max distance. 52447 */ 52448 getMaxDistance() { 52449 52450 return this.panner.maxDistance; 52451 52452 } 52453 52454 /** 52455 * Defines the maximum distance between the audio source and the listener, 52456 * after which the volume is not reduced any further. 52457 * 52458 * This value is used only by the `linear` distance model. 52459 * 52460 * @param {number} value - The max distance. 52461 * @return {PositionalAudio} A reference to this instance. 52462 */ 52463 setMaxDistance( value ) { 52464 52465 this.panner.maxDistance = value; 52466 52467 return this; 52468 52469 } 52470 52471 /** 52472 * Sets the directional cone in which the audio can be listened. 52473 * 52474 * @param {number} coneInnerAngle - An angle, in degrees, of a cone inside of which there will be no volume reduction. 52475 * @param {number} coneOuterAngle - An angle, in degrees, of a cone outside of which the volume will be reduced by a constant value, defined by the `coneOuterGain` parameter. 52476 * @param {number} coneOuterGain - The amount of volume reduction outside the cone defined by the `coneOuterAngle`. When set to `0`, no sound can be heard. 52477 * @return {PositionalAudio} A reference to this instance. 52478 */ 52479 setDirectionalCone( coneInnerAngle, coneOuterAngle, coneOuterGain ) { 52480 52481 this.panner.coneInnerAngle = coneInnerAngle; 52482 this.panner.coneOuterAngle = coneOuterAngle; 52483 this.panner.coneOuterGain = coneOuterGain; 52484 52485 return this; 52486 52487 } 52488 52489 updateMatrixWorld( force ) { 52490 52491 super.updateMatrixWorld( force ); 52492 52493 if ( this.hasPlaybackControl === true && this.isPlaying === false ) return; 52494 52495 this.matrixWorld.decompose( _position, _quaternion, _scale ); 52496 52497 _orientation.set( 0, 0, 1 ).applyQuaternion( _quaternion ); 52498 52499 const panner = this.panner; 52500 52501 if ( panner.positionX ) { 52502 52503 // code path for Chrome and Firefox (see #14393) 52504 52505 const endTime = this.context.currentTime + this.listener.timeDelta; 52506 52507 panner.positionX.linearRampToValueAtTime( _position.x, endTime ); 52508 panner.positionY.linearRampToValueAtTime( _position.y, endTime ); 52509 panner.positionZ.linearRampToValueAtTime( _position.z, endTime ); 52510 panner.orientationX.linearRampToValueAtTime( _orientation.x, endTime ); 52511 panner.orientationY.linearRampToValueAtTime( _orientation.y, endTime ); 52512 panner.orientationZ.linearRampToValueAtTime( _orientation.z, endTime ); 52513 52514 } else { 52515 52516 panner.setPosition( _position.x, _position.y, _position.z ); 52517 panner.setOrientation( _orientation.x, _orientation.y, _orientation.z ); 52518 52519 } 52520 52521 } 52522 52523} 52524 52525/** 52526 * This class can be used to analyse audio data. 52527 * 52528 * ```js 52529 * // create an AudioListener and add it to the camera 52530 * const listener = new THREE.AudioListener(); 52531 * camera.add( listener ); 52532 * 52533 * // create an Audio source 52534 * const sound = new THREE.Audio( listener ); 52535 * 52536 * // load a sound and set it as the Audio object's buffer 52537 * const audioLoader = new THREE.AudioLoader(); 52538 * audioLoader.load( 'sounds/ambient.ogg', function( buffer ) { 52539 * sound.setBuffer( buffer ); 52540 * sound.setLoop(true); 52541 * sound.setVolume(0.5); 52542 * sound.play(); 52543 * }); 52544 * 52545 * // create an AudioAnalyser, passing in the sound and desired fftSize 52546 * const analyser = new THREE.AudioAnalyser( sound, 32 ); 52547 * 52548 * // get the average frequency of the sound 52549 * const data = analyser.getAverageFrequency(); 52550 * ``` 52551 */ 52552class AudioAnalyser { 52553 52554 /** 52555 * Constructs a new audio analyzer. 52556 * 52557 * @param {Audio} audio - The audio to analyze. 52558 * @param {number} [fftSize=2048] - The window size in samples that is used when performing a Fast Fourier Transform (FFT) to get frequency domain data. 52559 */ 52560 constructor( audio, fftSize = 2048 ) { 52561 52562 /** 52563 * The global audio listener. 52564 * 52565 * @type {AnalyserNode} 52566 */ 52567 this.analyser = audio.context.createAnalyser(); 52568 this.analyser.fftSize = fftSize; 52569 52570 /** 52571 * Holds the analyzed data. 52572 * 52573 * @type {Uint8Array} 52574 */ 52575 this.data = new Uint8Array( this.analyser.frequencyBinCount ); 52576 52577 audio.getOutput().connect( this.analyser ); 52578 52579 } 52580 52581 /** 52582 * Returns an array with frequency data of the audio. 52583 * 52584 * Each item in the array represents the decibel value for a specific frequency. 52585 * The frequencies are spread linearly from 0 to 1/2 of the sample rate. 52586 * For example, for 48000 sample rate, the last item of the array will represent 52587 * the decibel value for 24000 Hz. 52588 * 52589 * @return {Uint8Array} The frequency data. 52590 */ 52591 getFrequencyData() { 52592 52593 this.analyser.getByteFrequencyData( this.data ); 52594 52595 return this.data; 52596 52597 } 52598 52599 /** 52600 * Returns the average of the frequencies returned by {@link AudioAnalyser#getFrequencyData}. 52601 * 52602 * @return {number} The average frequency. 52603 */ 52604 getAverageFrequency() { 52605 52606 let value = 0; 52607 const data = this.getFrequencyData(); 52608 52609 for ( let i = 0; i < data.length; i ++ ) { 52610 52611 value += data[ i ]; 52612 52613 } 52614 52615 return value / data.length; 52616 52617 } 52618 52619} 52620 52621/** 52622 * Buffered scene graph property that allows weighted accumulation; used internally. 52623 */ 52624class PropertyMixer { 52625 52626 /** 52627 * Constructs a new property mixer. 52628 * 52629 * @param {PropertyBinding} binding - The property binding. 52630 * @param {string} typeName - The keyframe track type name. 52631 * @param {number} valueSize - The keyframe track value size. 52632 */ 52633 constructor( binding, typeName, valueSize ) { 52634 52635 /** 52636 * The property binding. 52637 * 52638 * @type {PropertyBinding} 52639 */ 52640 this.binding = binding; 52641 52642 /** 52643 * The keyframe track value size. 52644 * 52645 * @type {number} 52646 */ 52647 this.valueSize = valueSize; 52648 52649 let mixFunction, 52650 mixFunctionAdditive, 52651 setIdentity; 52652 52653 // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] 52654 // 52655 // interpolators can use .buffer as their .result 52656 // the data then goes to 'incoming' 52657 // 52658 // 'accu0' and 'accu1' are used frame-interleaved for 52659 // the cumulative result and are compared to detect 52660 // changes 52661 // 52662 // 'orig' stores the original state of the property 52663 // 52664 // 'add' is used for additive cumulative results 52665 // 52666 // 'work' is optional and is only present for quaternion types. It is used 52667 // to store intermediate quaternion multiplication results 52668 52669 switch ( typeName ) { 52670 52671 case 'quaternion': 52672 mixFunction = this._slerp; 52673 mixFunctionAdditive = this._slerpAdditive; 52674 setIdentity = this._setAdditiveIdentityQuaternion; 52675 52676 this.buffer = new Float64Array( valueSize * 6 ); 52677 this._workIndex = 5; 52678 break; 52679 52680 case 'string': 52681 case 'bool': 52682 mixFunction = this._select; 52683 52684 // Use the regular mix function and for additive on these types, 52685 // additive is not relevant for non-numeric types 52686 mixFunctionAdditive = this._select; 52687 52688 setIdentity = this._setAdditiveIdentityOther; 52689 52690 this.buffer = new Array( valueSize * 5 ); 52691 break; 52692 52693 default: 52694 mixFunction = this._lerp; 52695 mixFunctionAdditive = this._lerpAdditive; 52696 setIdentity = this._setAdditiveIdentityNumeric; 52697 52698 this.buffer = new Float64Array( valueSize * 5 ); 52699 52700 } 52701 52702 this._mixBufferRegion = mixFunction; 52703 this._mixBufferRegionAdditive = mixFunctionAdditive; 52704 this._setIdentity = setIdentity; 52705 this._origIndex = 3; 52706 this._addIndex = 4; 52707 52708 /** 52709 * Accumulated weight of the property binding. 52710 * 52711 * @type {number} 52712 * @default 0 52713 */ 52714 this.cumulativeWeight = 0; 52715 52716 /** 52717 * Accumulated additive weight of the property binding. 52718 * 52719 * @type {number} 52720 * @default 0 52721 */ 52722 this.cumulativeWeightAdditive = 0; 52723 52724 /** 52725 * Number of active keyframe tracks currently using this property binding. 52726 * 52727 * @type {number} 52728 * @default 0 52729 */ 52730 this.useCount = 0; 52731 52732 /** 52733 * Number of keyframe tracks referencing this property binding. 52734 * 52735 * @type {number} 52736 * @default 0 52737 */ 52738 this.referenceCount = 0; 52739 52740 } 52741 52742 /** 52743 * Accumulates data in the `incoming` region into `accu<i>`. 52744 * 52745 * @param {number} accuIndex - The accumulation index. 52746 * @param {number} weight - The weight. 52747 */ 52748 accumulate( accuIndex, weight ) { 52749 52750 // note: happily accumulating nothing when weight = 0, the caller knows 52751 // the weight and shouldn't have made the call in the first place 52752 52753 const buffer = this.buffer, 52754 stride = this.valueSize, 52755 offset = accuIndex * stride + stride; 52756 52757 let currentWeight = this.cumulativeWeight; 52758 52759 if ( currentWeight === 0 ) { 52760 52761 // accuN := incoming * weight 52762 52763 for ( let i = 0; i !== stride; ++ i ) { 52764 52765 buffer[ offset + i ] = buffer[ i ]; 52766 52767 } 52768 52769 currentWeight = weight; 52770 52771 } else { 52772 52773 // accuN := accuN + incoming * weight 52774 52775 currentWeight += weight; 52776 const mix = weight / currentWeight; 52777 this._mixBufferRegion( buffer, offset, 0, mix, stride ); 52778 52779 } 52780 52781 this.cumulativeWeight = currentWeight; 52782 52783 } 52784 52785 /** 52786 * Accumulates data in the `incoming` region into `add`. 52787 * 52788 * @param {number} weight - The weight. 52789 */ 52790 accumulateAdditive( weight ) { 52791 52792 const buffer = this.buffer, 52793 stride = this.valueSize, 52794 offset = stride * this._addIndex; 52795 52796 if ( this.cumulativeWeightAdditive === 0 ) { 52797 52798 // add = identity 52799 52800 this._setIdentity(); 52801 52802 } 52803 52804 // add := add + incoming * weight 52805 52806 this._mixBufferRegionAdditive( buffer, offset, 0, weight, stride ); 52807 this.cumulativeWeightAdditive += weight; 52808 52809 } 52810 52811 /** 52812 * Applies the state of `accu<i>` to the binding when accus differ. 52813 * 52814 * @param {number} accuIndex - The accumulation index. 52815 */ 52816 apply( accuIndex ) { 52817 52818 const stride = this.valueSize, 52819 buffer = this.buffer, 52820 offset = accuIndex * stride + stride, 52821 52822 weight = this.cumulativeWeight, 52823 weightAdditive = this.cumulativeWeightAdditive, 52824 52825 binding = this.binding; 52826 52827 this.cumulativeWeight = 0; 52828 this.cumulativeWeightAdditive = 0; 52829 52830 if ( weight < 1 ) { 52831 52832 // accuN := accuN + original * ( 1 - cumulativeWeight ) 52833 52834 const originalValueOffset = stride * this._origIndex; 52835 52836 this._mixBufferRegion( 52837 buffer, offset, originalValueOffset, 1 - weight, stride ); 52838 52839 } 52840 52841 if ( weightAdditive > 0 ) { 52842 52843 // accuN := accuN + additive accuN 52844 52845 this._mixBufferRegionAdditive( buffer, offset, this._addIndex * stride, 1, stride ); 52846 52847 } 52848 52849 for ( let i = stride, e = stride + stride; i !== e; ++ i ) { 52850 52851 if ( buffer[ i ] !== buffer[ i + stride ] ) { 52852 52853 // value has changed -> update scene graph 52854 52855 binding.setValue( buffer, offset ); 52856 break; 52857 52858 } 52859 52860 } 52861 52862 } 52863 52864 52865 /** 52866 * Remembers the state of the bound property and copy it to both accus. 52867 */ 52868 saveOriginalState() { 52869 52870 const binding = this.binding; 52871 52872 const buffer = this.buffer, 52873 stride = this.valueSize, 52874 52875 originalValueOffset = stride * this._origIndex; 52876 52877 binding.getValue( buffer, originalValueOffset ); 52878 52879 // accu[0..1] := orig -- initially detect changes against the original 52880 for ( let i = stride, e = originalValueOffset; i !== e; ++ i ) { 52881 52882 buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; 52883 52884 } 52885 52886 // Add to identity for additive 52887 this._setIdentity(); 52888 52889 this.cumulativeWeight = 0; 52890 this.cumulativeWeightAdditive = 0; 52891 52892 } 52893 52894 /** 52895 * Applies the state previously taken via {@link PropertyMixer#saveOriginalState} to the binding. 52896 */ 52897 restoreOriginalState() { 52898 52899 const originalValueOffset = this.valueSize * 3; 52900 this.binding.setValue( this.buffer, originalValueOffset ); 52901 52902 } 52903 52904 // internals 52905 52906 _setAdditiveIdentityNumeric() { 52907 52908 const startIndex = this._addIndex * this.valueSize; 52909 const endIndex = startIndex + this.valueSize; 52910 52911 for ( let i = startIndex; i < endIndex; i ++ ) { 52912 52913 this.buffer[ i ] = 0; 52914 52915 } 52916 52917 } 52918 52919 _setAdditiveIdentityQuaternion() { 52920 52921 this._setAdditiveIdentityNumeric(); 52922 this.buffer[ this._addIndex * this.valueSize + 3 ] = 1; 52923 52924 } 52925 52926 _setAdditiveIdentityOther() { 52927 52928 const startIndex = this._origIndex * this.valueSize; 52929 const targetIndex = this._addIndex * this.valueSize; 52930 52931 for ( let i = 0; i < this.valueSize; i ++ ) { 52932 52933 this.buffer[ targetIndex + i ] = this.buffer[ startIndex + i ]; 52934 52935 } 52936 52937 } 52938 52939 52940 // mix functions 52941 52942 _select( buffer, dstOffset, srcOffset, t, stride ) { 52943 52944 if ( t >= 0.5 ) { 52945 52946 for ( let i = 0; i !== stride; ++ i ) { 52947 52948 buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; 52949 52950 } 52951 52952 } 52953 52954 } 52955 52956 _slerp( buffer, dstOffset, srcOffset, t ) { 52957 52958 Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); 52959 52960 } 52961 52962 _slerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { 52963 52964 const workOffset = this._workIndex * stride; 52965 52966 // Store result in intermediate buffer offset 52967 Quaternion.multiplyQuaternionsFlat( buffer, workOffset, buffer, dstOffset, buffer, srcOffset ); 52968 52969 // Slerp to the intermediate result 52970 Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t ); 52971 52972 } 52973 52974 _lerp( buffer, dstOffset, srcOffset, t, stride ) { 52975 52976 const s = 1 - t; 52977 52978 for ( let i = 0; i !== stride; ++ i ) { 52979 52980 const j = dstOffset + i; 52981 52982 buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; 52983 52984 } 52985 52986 } 52987 52988 _lerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { 52989 52990 for ( let i = 0; i !== stride; ++ i ) { 52991 52992 const j = dstOffset + i; 52993 52994 buffer[ j ] = buffer[ j ] + buffer[ srcOffset + i ] * t; 52995 52996 } 52997 52998 } 52999 53000} 53001 53002// Characters [].:/ are reserved for track binding syntax. 53003const _RESERVED_CHARS_RE = '\\[\\]\\.:\\/'; 53004const _reservedRe = new RegExp( '[' + _RESERVED_CHARS_RE + ']', 'g' ); 53005 53006// Attempts to allow node names from any language. ES5's `\w` regexp matches 53007// only latin characters, and the unicode \p{L} is not yet supported. So 53008// instead, we exclude reserved characters and match everything else. 53009const _wordChar = '[^' + _RESERVED_CHARS_RE + ']'; 53010const _wordCharOrDot = '[^' + _RESERVED_CHARS_RE.replace( '\\.', '' ) + ']'; 53011 53012// Parent directories, delimited by '/' or ':'. Currently unused, but must 53013// be matched to parse the rest of the track name. 53014const _directoryRe = /*@__PURE__*/ /((?:WC+[\/:])*)/.source.replace( 'WC', _wordChar ); 53015 53016// Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'. 53017const _nodeRe = /*@__PURE__*/ /(WCOD+)?/.source.replace( 'WCOD', _wordCharOrDot ); 53018 53019// Object on target node, and accessor. May not contain reserved 53020// characters. Accessor may contain any character except closing bracket. 53021const _objectRe = /*@__PURE__*/ /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', _wordChar ); 53022 53023// Property and accessor. May not contain reserved characters. Accessor may 53024// contain any non-bracket characters. 53025const _propertyRe = /*@__PURE__*/ /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', _wordChar ); 53026 53027const _trackRe = new RegExp( '' 53028 + '^' 53029 + _directoryRe 53030 + _nodeRe 53031 + _objectRe 53032 + _propertyRe 53033 + '$' 53034); 53035 53036const _supportedObjectNames = [ 'material', 'materials', 'bones', 'map' ]; 53037 53038class Composite { 53039 53040 constructor( targetGroup, path, optionalParsedPath ) { 53041 53042 const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); 53043 53044 this._targetGroup = targetGroup; 53045 this._bindings = targetGroup.subscribe_( path, parsedPath ); 53046 53047 } 53048 53049 getValue( array, offset ) { 53050 53051 this.bind(); // bind all binding 53052 53053 const firstValidIndex = this._targetGroup.nCachedObjects_, 53054 binding = this._bindings[ firstValidIndex ]; 53055 53056 // and only call .getValue on the first 53057 if ( binding !== undefined ) binding.getValue( array, offset ); 53058 53059 } 53060 53061 setValue( array, offset ) { 53062 53063 const bindings = this._bindings; 53064 53065 for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { 53066 53067 bindings[ i ].setValue( array, offset ); 53068 53069 } 53070 53071 } 53072 53073 bind() { 53074 53075 const bindings = this._bindings; 53076 53077 for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { 53078 53079 bindings[ i ].bind(); 53080 53081 } 53082 53083 } 53084 53085 unbind() { 53086 53087 const bindings = this._bindings; 53088 53089 for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { 53090 53091 bindings[ i ].unbind(); 53092 53093 } 53094 53095 } 53096 53097} 53098 53099// Note: This class uses a State pattern on a per-method basis: 53100// 'bind' sets 'this.getValue' / 'setValue' and shadows the 53101// prototype version of these methods with one that represents 53102// the bound state. When the property is not found, the methods 53103// become no-ops. 53104 53105 53106/** 53107 * This holds a reference to a real property in the scene graph; used internally. 53108 */ 53109class PropertyBinding { 53110 53111 /** 53112 * Constructs a new property binding. 53113 * 53114 * @param {Object} rootNode - The root node. 53115 * @param {string} path - The path. 53116 * @param {?Object} [parsedPath] - The parsed path. 53117 */ 53118 constructor( rootNode, path, parsedPath ) { 53119 53120 /** 53121 * The object path to the animated property. 53122 * 53123 * @type {string} 53124 */ 53125 this.path = path; 53126 53127 /** 53128 * An object holding information about the path. 53129 * 53130 * @type {Object} 53131 */ 53132 this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); 53133 53134 /** 53135 * The object owns the animated property. 53136 * 53137 * @type {?Object} 53138 */ 53139 this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ); 53140 53141 /** 53142 * The root node. 53143 * 53144 * @type {Object3D|Skeleton} 53145 */ 53146 this.rootNode = rootNode; 53147 53148 // initial state of these methods that calls 'bind' 53149 this.getValue = this._getValue_unbound; 53150 this.setValue = this._setValue_unbound; 53151 53152 } 53153 53154 53155 /** 53156 * Factory method for creating a property binding from the given parameters. 53157 * 53158 * @static 53159 * @param {Object} root - The root node. 53160 * @param {string} path - The path. 53161 * @param {?Object} [parsedPath] - The parsed path. 53162 * @return {PropertyBinding|Composite} The created property binding or composite. 53163 */ 53164 static create( root, path, parsedPath ) { 53165 53166 if ( ! ( root && root.isAnimationObjectGroup ) ) { 53167 53168 return new PropertyBinding( root, path, parsedPath ); 53169 53170 } else { 53171 53172 return new PropertyBinding.Composite( root, path, parsedPath ); 53173 53174 } 53175 53176 } 53177 53178 /** 53179 * Replaces spaces with underscores and removes unsupported characters from 53180 * node names, to ensure compatibility with parseTrackName(). 53181 * 53182 * @param {string} name - Node name to be sanitized. 53183 * @return {string} The sanitized node name. 53184 */ 53185 static sanitizeNodeName( name ) { 53186 53187 return name.replace( /\s/g, '_' ).replace( _reservedRe, '' ); 53188 53189 } 53190 53191 /** 53192 * Parses the given track name (an object path to an animated property) and 53193 * returns an object with information about the path. Matches strings in the following forms: 53194 * 53195 * - nodeName.property 53196 * - nodeName.property[accessor] 53197 * - nodeName.material.property[accessor] 53198 * - uuid.property[accessor] 53199 * - uuid.objectName[objectIndex].propertyName[propertyIndex] 53200 * - parentName/nodeName.property 53201 * - parentName/parentName/nodeName.property[index] 53202 * - .bone[Armature.DEF_cog].position 53203 * - scene:helium_balloon_model:helium_balloon_model.position 53204 * 53205 * @static 53206 * @param {string} trackName - The track name to parse. 53207 * @return {Object} The parsed track name as an object. 53208 */ 53209 static parseTrackName( trackName ) { 53210 53211 const matches = _trackRe.exec( trackName ); 53212 53213 if ( matches === null ) { 53214 53215 throw new Error( 'THREE.PropertyBinding: Cannot parse trackName: ' + trackName ); 53216 53217 } 53218 53219 const results = { 53220 // directoryName: matches[ 1 ], // (tschw) currently unused 53221 nodeName: matches[ 2 ], 53222 objectName: matches[ 3 ], 53223 objectIndex: matches[ 4 ], 53224 propertyName: matches[ 5 ], // required 53225 propertyIndex: matches[ 6 ] 53226 }; 53227 53228 const lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' ); 53229 53230 if ( lastDot !== undefined && lastDot !== -1 ) { 53231 53232 const objectName = results.nodeName.substring( lastDot + 1 ); 53233 53234 // Object names must be checked against an allowlist. Otherwise, there 53235 // is no way to parse 'foo.bar.baz': 'baz' must be a property, but 53236 // 'bar' could be the objectName, or part of a nodeName (which can 53237 // include '.' characters). 53238 if ( _supportedObjectNames.indexOf( objectName ) !== -1 ) { 53239 53240 results.nodeName = results.nodeName.substring( 0, lastDot ); 53241 results.objectName = objectName; 53242 53243 } 53244 53245 } 53246 53247 if ( results.propertyName === null || results.propertyName.length === 0 ) { 53248 53249 throw new Error( 'THREE.PropertyBinding: can not parse propertyName from trackName: ' + trackName ); 53250 53251 } 53252 53253 return results; 53254 53255 } 53256 53257 /** 53258 * Searches for a node in the hierarchy of the given root object by the given 53259 * node name. 53260 * 53261 * @static 53262 * @param {Object} root - The root object. 53263 * @param {string|number} nodeName - The name of the node. 53264 * @return {?Object} The found node. Returns `null` if no object was found. 53265 */ 53266 static findNode( root, nodeName ) { 53267 53268 if ( nodeName === undefined || nodeName === '' || nodeName === '.' || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) { 53269 53270 return root; 53271 53272 } 53273 53274 // search into skeleton bones. 53275 if ( root.skeleton ) { 53276 53277 const bone = root.skeleton.getBoneByName( nodeName ); 53278 53279 if ( bone !== undefined ) { 53280 53281 return bone; 53282 53283 } 53284 53285 } 53286 53287 // search into node subtree. 53288 if ( root.children ) { 53289 53290 const searchNodeSubtree = function ( children ) { 53291 53292 for ( let i = 0; i < children.length; i ++ ) { 53293 53294 const childNode = children[ i ]; 53295 53296 if ( childNode.name === nodeName || childNode.uuid === nodeName ) { 53297 53298 return childNode; 53299 53300 } 53301 53302 const result = searchNodeSubtree( childNode.children ); 53303 53304 if ( result ) return result; 53305 53306 } 53307 53308 return null; 53309 53310 }; 53311 53312 const subTreeNode = searchNodeSubtree( root.children ); 53313 53314 if ( subTreeNode ) { 53315 53316 return subTreeNode; 53317 53318 } 53319 53320 } 53321 53322 return null; 53323 53324 } 53325 53326 // these are used to "bind" a nonexistent property 53327 _getValue_unavailable() {} 53328 _setValue_unavailable() {} 53329 53330 // Getters 53331 53332 _getValue_direct( buffer, offset ) { 53333 53334 buffer[ offset ] = this.targetObject[ this.propertyName ]; 53335 53336 } 53337 53338 _getValue_array( buffer, offset ) { 53339 53340 const source = this.resolvedProperty; 53341 53342 for ( let i = 0, n = source.length; i !== n; ++ i ) { 53343 53344 buffer[ offset ++ ] = source[ i ]; 53345 53346 } 53347 53348 } 53349 53350 _getValue_arrayElement( buffer, offset ) { 53351 53352 buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; 53353 53354 } 53355 53356 _getValue_toArray( buffer, offset ) { 53357 53358 this.resolvedProperty.toArray( buffer, offset ); 53359 53360 } 53361 53362 // Direct 53363 53364 _setValue_direct( buffer, offset ) { 53365 53366 this.targetObject[ this.propertyName ] = buffer[ offset ]; 53367 53368 } 53369 53370 _setValue_direct_setNeedsUpdate( buffer, offset ) { 53371 53372 this.targetObject[ this.propertyName ] = buffer[ offset ]; 53373 this.targetObject.needsUpdate = true; 53374 53375 } 53376 53377 _setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { 53378 53379 this.targetObject[ this.propertyName ] = buffer[ offset ]; 53380 this.targetObject.matrixWorldNeedsUpdate = true; 53381 53382 } 53383 53384 // EntireArray 53385 53386 _setValue_array( buffer, offset ) { 53387 53388 const dest = this.resolvedProperty; 53389 53390 for ( let i = 0, n = dest.length; i !== n; ++ i ) { 53391 53392 dest[ i ] = buffer[ offset ++ ]; 53393 53394 } 53395 53396 } 53397 53398 _setValue_array_setNeedsUpdate( buffer, offset ) { 53399 53400 const dest = this.resolvedProperty; 53401 53402 for ( let i = 0, n = dest.length; i !== n; ++ i ) { 53403 53404 dest[ i ] = buffer[ offset ++ ]; 53405 53406 } 53407 53408 this.targetObject.needsUpdate = true; 53409 53410 } 53411 53412 _setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { 53413 53414 const dest = this.resolvedProperty; 53415 53416 for ( let i = 0, n = dest.length; i !== n; ++ i ) { 53417 53418 dest[ i ] = buffer[ offset ++ ]; 53419 53420 } 53421 53422 this.targetObject.matrixWorldNeedsUpdate = true; 53423 53424 } 53425 53426 // ArrayElement 53427 53428 _setValue_arrayElement( buffer, offset ) { 53429 53430 this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; 53431 53432 } 53433 53434 _setValue_arrayElement_setNeedsUpdate( buffer, offset ) { 53435 53436 this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; 53437 this.targetObject.needsUpdate = true; 53438 53439 } 53440 53441 _setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { 53442 53443 this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; 53444 this.targetObject.matrixWorldNeedsUpdate = true; 53445 53446 } 53447 53448 // HasToFromArray 53449 53450 _setValue_fromArray( buffer, offset ) { 53451 53452 this.resolvedProperty.fromArray( buffer, offset ); 53453 53454 } 53455 53456 _setValue_fromArray_setNeedsUpdate( buffer, offset ) { 53457 53458 this.resolvedProperty.fromArray( buffer, offset ); 53459 this.targetObject.needsUpdate = true; 53460 53461 } 53462 53463 _setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { 53464 53465 this.resolvedProperty.fromArray( buffer, offset ); 53466 this.targetObject.matrixWorldNeedsUpdate = true; 53467 53468 } 53469 53470 _getValue_unbound( targetArray, offset ) { 53471 53472 this.bind(); 53473 this.getValue( targetArray, offset ); 53474 53475 } 53476 53477 _setValue_unbound( sourceArray, offset ) { 53478 53479 this.bind(); 53480 this.setValue( sourceArray, offset ); 53481 53482 } 53483 53484 /** 53485 * Creates a getter / setter pair for the property tracked by this binding. 53486 */ 53487 bind() { 53488 53489 let targetObject = this.node; 53490 const parsedPath = this.parsedPath; 53491 53492 const objectName = parsedPath.objectName; 53493 const propertyName = parsedPath.propertyName; 53494 let propertyIndex = parsedPath.propertyIndex; 53495 53496 if ( ! targetObject ) { 53497 53498 targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ); 53499 53500 this.node = targetObject; 53501 53502 } 53503 53504 // set fail state so we can just 'return' on error 53505 this.getValue = this._getValue_unavailable; 53506 this.setValue = this._setValue_unavailable; 53507 53508 // ensure there is a value node 53509 if ( ! targetObject ) { 53510 53511 warn( 'PropertyBinding: No target node found for track: ' + this.path + '.' ); 53512 return; 53513 53514 } 53515 53516 if ( objectName ) { 53517 53518 let objectIndex = parsedPath.objectIndex; 53519 53520 // special cases were we need to reach deeper into the hierarchy to get the face materials.... 53521 switch ( objectName ) { 53522 53523 case 'materials': 53524 53525 if ( ! targetObject.material ) { 53526 53527 error( 'PropertyBinding: Can not bind to material as node does not have a material.', this ); 53528 return; 53529 53530 } 53531 53532 if ( ! targetObject.material.materials ) { 53533 53534 error( 'PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this ); 53535 return; 53536 53537 } 53538 53539 targetObject = targetObject.material.materials; 53540 53541 break; 53542 53543 case 'bones': 53544 53545 if ( ! targetObject.skeleton ) { 53546 53547 error( 'PropertyBinding: Can not bind to bones as node does not have a skeleton.', this ); 53548 return; 53549 53550 } 53551 53552 // potential future optimization: skip this if propertyIndex is already an integer 53553 // and convert the integer string to a true integer. 53554 53555 targetObject = targetObject.skeleton.bones; 53556 53557 // support resolving morphTarget names into indices. 53558 for ( let i = 0; i < targetObject.length; i ++ ) { 53559 53560 if ( targetObject[ i ].name === objectIndex ) { 53561 53562 objectIndex = i; 53563 break; 53564 53565 } 53566 53567 } 53568 53569 break; 53570 53571 case 'map': 53572 53573 if ( 'map' in targetObject ) { 53574 53575 targetObject = targetObject.map; 53576 break; 53577 53578 } 53579 53580 if ( ! targetObject.material ) { 53581 53582 error( 'PropertyBinding: Can not bind to material as node does not have a material.', this ); 53583 return; 53584 53585 } 53586 53587 if ( ! targetObject.material.map ) { 53588 53589 error( 'PropertyBinding: Can not bind to material.map as node.material does not have a map.', this ); 53590 return; 53591 53592 } 53593 53594 targetObject = targetObject.material.map; 53595 break; 53596 53597 default: 53598 53599 if ( targetObject[ objectName ] === undefined ) { 53600 53601 error( 'PropertyBinding: Can not bind to objectName of node undefined.', this ); 53602 return; 53603 53604 } 53605 53606 targetObject = targetObject[ objectName ]; 53607 53608 } 53609 53610 53611 if ( objectIndex !== undefined ) { 53612 53613 if ( targetObject[ objectIndex ] === undefined ) { 53614 53615 error( 'PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject ); 53616 return; 53617 53618 } 53619 53620 targetObject = targetObject[ objectIndex ]; 53621 53622 } 53623 53624 } 53625 53626 // resolve property 53627 const nodeProperty = targetObject[ propertyName ]; 53628 53629 if ( nodeProperty === undefined ) { 53630 53631 const nodeName = parsedPath.nodeName; 53632 53633 error( 'PropertyBinding: Trying to update property for track: ' + nodeName + 53634 '.' + propertyName + ' but it wasn\'t found.', targetObject ); 53635 return; 53636 53637 } 53638 53639 // determine versioning scheme 53640 let versioning = this.Versioning.None; 53641 53642 this.targetObject = targetObject; 53643 53644 if ( targetObject.isMaterial === true ) { 53645 53646 versioning = this.Versioning.NeedsUpdate; 53647 53648 } else if ( targetObject.isObject3D === true ) { 53649 53650 versioning = this.Versioning.MatrixWorldNeedsUpdate; 53651 53652 } 53653 53654 // determine how the property gets bound 53655 let bindingType = this.BindingType.Direct; 53656 53657 if ( propertyIndex !== undefined ) { 53658 53659 // access a sub element of the property array (only primitives are supported right now) 53660 53661 if ( propertyName === 'morphTargetInfluences' ) { 53662 53663 // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. 53664 53665 // support resolving morphTarget names into indices. 53666 if ( ! targetObject.geometry ) { 53667 53668 error( 'PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this ); 53669 return; 53670 53671 } 53672 53673 if ( ! targetObject.geometry.morphAttributes ) { 53674 53675 error( 'PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this ); 53676 return; 53677 53678 } 53679 53680 if ( targetObject.morphTargetDictionary[ propertyIndex ] !== undefined ) { 53681 53682 propertyIndex = targetObject.morphTargetDictionary[ propertyIndex ]; 53683 53684 } 53685 53686 } 53687 53688 bindingType = this.BindingType.ArrayElement; 53689 53690 this.resolvedProperty = nodeProperty; 53691 this.propertyIndex = propertyIndex; 53692 53693 } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { 53694 53695 // must use copy for Object3D.Euler/Quaternion 53696 53697 bindingType = this.BindingType.HasFromToArray; 53698 53699 this.resolvedProperty = nodeProperty; 53700 53701 } else if ( Array.isArray( nodeProperty ) ) { 53702 53703 bindingType = this.BindingType.EntireArray; 53704 53705 this.resolvedProperty = nodeProperty; 53706 53707 } else { 53708 53709 this.propertyName = propertyName; 53710 53711 } 53712 53713 // select getter / setter 53714 this.getValue = this.GetterByBindingType[ bindingType ]; 53715 this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; 53716 53717 } 53718 53719 /** 53720 * Unbinds the property. 53721 */ 53722 unbind() { 53723 53724 this.node = null; 53725 53726 // back to the prototype version of getValue / setValue 53727 // note: avoiding to mutate the shape of 'this' via 'delete' 53728 this.getValue = this._getValue_unbound; 53729 this.setValue = this._setValue_unbound; 53730 53731 } 53732 53733} 53734 53735PropertyBinding.Composite = Composite; 53736 53737PropertyBinding.prototype.BindingType = { 53738 Direct: 0, 53739 EntireArray: 1, 53740 ArrayElement: 2, 53741 HasFromToArray: 3 53742}; 53743 53744PropertyBinding.prototype.Versioning = { 53745 None: 0, 53746 NeedsUpdate: 1, 53747 MatrixWorldNeedsUpdate: 2 53748}; 53749 53750PropertyBinding.prototype.GetterByBindingType = [ 53751 53752 PropertyBinding.prototype._getValue_direct, 53753 PropertyBinding.prototype._getValue_array, 53754 PropertyBinding.prototype._getValue_arrayElement, 53755 PropertyBinding.prototype._getValue_toArray, 53756 53757]; 53758 53759PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [ 53760 53761 [ 53762 // Direct 53763 PropertyBinding.prototype._setValue_direct, 53764 PropertyBinding.prototype._setValue_direct_setNeedsUpdate, 53765 PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate, 53766 53767 ], [ 53768 53769 // EntireArray 53770 53771 PropertyBinding.prototype._setValue_array, 53772 PropertyBinding.prototype._setValue_array_setNeedsUpdate, 53773 PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate, 53774 53775 ], [ 53776 53777 // ArrayElement 53778 PropertyBinding.prototype._setValue_arrayElement, 53779 PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, 53780 PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate, 53781 53782 ], [ 53783 53784 // HasToFromArray 53785 PropertyBinding.prototype._setValue_fromArray, 53786 PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, 53787 PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate, 53788 53789 ] 53790 53791]; 53792 53793/** 53794 * A group of objects that receives a shared animation state. 53795 * 53796 * Usage: 53797 * 53798 * - Add objects you would otherwise pass as 'root' to the 53799 * constructor or the .clipAction method of AnimationMixer. 53800 * - Instead pass this object as 'root'. 53801 * - You can also add and remove objects later when the mixer is running. 53802 * 53803 * Note: 53804 * 53805 * - Objects of this class appear as one object to the mixer, 53806 * so cache control of the individual objects must be done on the group. 53807 * 53808 * Limitation: 53809 * 53810 * - The animated properties must be compatible among the all objects in the group. 53811 * - A single property can either be controlled through a target group or directly, but not both. 53812 */ 53813class AnimationObjectGroup { 53814 53815 /** 53816 * Constructs a new animation group. 53817 * 53818 * @param {...Object3D} arguments - An arbitrary number of 3D objects that share the same animation state. 53819 */ 53820 constructor() { 53821 53822 /** 53823 * This flag can be used for type testing. 53824 * 53825 * @type {boolean} 53826 * @readonly 53827 * @default true 53828 */ 53829 this.isAnimationObjectGroup = true; 53830 53831 /** 53832 * The UUID of the 3D object. 53833 * 53834 * @type {string} 53835 * @readonly 53836 */ 53837 this.uuid = generateUUID(); 53838 53839 // cached objects followed by the active ones 53840 this._objects = Array.prototype.slice.call( arguments ); 53841 53842 this.nCachedObjects_ = 0; // threshold 53843 // note: read by PropertyBinding.Composite 53844 53845 const indices = {}; 53846 this._indicesByUUID = indices; // for bookkeeping 53847 53848 for ( let i = 0, n = arguments.length; i !== n; ++ i ) { 53849 53850 indices[ arguments[ i ].uuid ] = i; 53851 53852 } 53853 53854 this._paths = []; // inside: string 53855 this._parsedPaths = []; // inside: { we don't care, here } 53856 this._bindings = []; // inside: Array< PropertyBinding > 53857 this._bindingsIndicesByPath = {}; // inside: indices in these arrays 53858 53859 const scope = this; 53860 53861 this.stats = { 53862 53863 objects: { 53864 get total() { 53865 53866 return scope._objects.length; 53867 53868 }, 53869 get inUse() { 53870 53871 return this.total - scope.nCachedObjects_; 53872 53873 } 53874 }, 53875 get bindingsPerObject() { 53876 53877 return scope._bindings.length; 53878 53879 } 53880 53881 }; 53882 53883 } 53884 53885 /** 53886 * Adds an arbitrary number of objects to this animation group. 53887 * 53888 * @param {...Object3D} arguments - The 3D objects to add. 53889 */ 53890 add() { 53891 53892 const objects = this._objects, 53893 indicesByUUID = this._indicesByUUID, 53894 paths = this._paths, 53895 parsedPaths = this._parsedPaths, 53896 bindings = this._bindings, 53897 nBindings = bindings.length; 53898 53899 let knownObject = undefined, 53900 nObjects = objects.length, 53901 nCachedObjects = this.nCachedObjects_; 53902 53903 for ( let i = 0, n = arguments.length; i !== n; ++ i ) { 53904 53905 const object = arguments[ i ], 53906 uuid = object.uuid; 53907 let index = indicesByUUID[ uuid ]; 53908 53909 if ( index === undefined ) { 53910 53911 // unknown object -> add it to the ACTIVE region 53912 53913 index = nObjects ++; 53914 indicesByUUID[ uuid ] = index; 53915 objects.push( object ); 53916 53917 // accounting is done, now do the same for all bindings 53918 53919 for ( let j = 0, m = nBindings; j !== m; ++ j ) { 53920 53921 bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) ); 53922 53923 } 53924 53925 } else if ( index < nCachedObjects ) { 53926 53927 knownObject = objects[ index ]; 53928 53929 // move existing object to the ACTIVE region 53930 53931 const firstActiveIndex = -- nCachedObjects, 53932 lastCachedObject = objects[ firstActiveIndex ]; 53933 53934 indicesByUUID[ lastCachedObject.uuid ] = index; 53935 objects[ index ] = lastCachedObject; 53936 53937 indicesByUUID[ uuid ] = firstActiveIndex; 53938 objects[ firstActiveIndex ] = object; 53939 53940 // accounting is done, now do the same for all bindings 53941 53942 for ( let j = 0, m = nBindings; j !== m; ++ j ) { 53943 53944 const bindingsForPath = bindings[ j ], 53945 lastCached = bindingsForPath[ firstActiveIndex ]; 53946 53947 let binding = bindingsForPath[ index ]; 53948 53949 bindingsForPath[ index ] = lastCached; 53950 53951 if ( binding === undefined ) { 53952 53953 // since we do not bother to create new bindings 53954 // for objects that are cached, the binding may 53955 // or may not exist 53956 53957 binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ); 53958 53959 } 53960 53961 bindingsForPath[ firstActiveIndex ] = binding; 53962 53963 } 53964 53965 } else if ( objects[ index ] !== knownObject ) { 53966 53967 error( 'AnimationObjectGroup: Different objects with the same UUID ' + 53968 'detected. Clean the caches or recreate your infrastructure when reloading scenes.' ); 53969 53970 } // else the object is already where we want it to be 53971 53972 } // for arguments 53973 53974 this.nCachedObjects_ = nCachedObjects; 53975 53976 } 53977 53978 /** 53979 * Removes an arbitrary number of objects to this animation group 53980 * 53981 * @param {...Object3D} arguments - The 3D objects to remove. 53982 */ 53983 remove() { 53984 53985 const objects = this._objects, 53986 indicesByUUID = this._indicesByUUID, 53987 bindings = this._bindings, 53988 nBindings = bindings.length; 53989 53990 let nCachedObjects = this.nCachedObjects_; 53991 53992 for ( let i = 0, n = arguments.length; i !== n; ++ i ) { 53993 53994 const object = arguments[ i ], 53995 uuid = object.uuid, 53996 index = indicesByUUID[ uuid ]; 53997 53998 if ( index !== undefined && index >= nCachedObjects ) { 53999 54000 // move existing object into the CACHED region 54001 54002 const lastCachedIndex = nCachedObjects ++, 54003 firstActiveObject = objects[ lastCachedIndex ]; 54004 54005 indicesByUUID[ firstActiveObject.uuid ] = index; 54006 objects[ index ] = firstActiveObject; 54007 54008 indicesByUUID[ uuid ] = lastCachedIndex; 54009 objects[ lastCachedIndex ] = object; 54010 54011 // accounting is done, now do the same for all bindings 54012 54013 for ( let j = 0, m = nBindings; j !== m; ++ j ) { 54014 54015 const bindingsForPath = bindings[ j ], 54016 firstActive = bindingsForPath[ lastCachedIndex ], 54017 binding = bindingsForPath[ index ]; 54018 54019 bindingsForPath[ index ] = firstActive; 54020 bindingsForPath[ lastCachedIndex ] = binding; 54021 54022 } 54023 54024 } 54025 54026 } // for arguments 54027 54028 this.nCachedObjects_ = nCachedObjects; 54029 54030 } 54031 54032 /** 54033 * Deallocates all memory resources for the passed 3D objects of this animation group. 54034 * 54035 * @param {...Object3D} arguments - The 3D objects to uncache. 54036 */ 54037 uncache() { 54038 54039 const objects = this._objects, 54040 indicesByUUID = this._indicesByUUID, 54041 bindings = this._bindings, 54042 nBindings = bindings.length; 54043 54044 let nCachedObjects = this.nCachedObjects_, 54045 nObjects = objects.length; 54046 54047 for ( let i = 0, n = arguments.length; i !== n; ++ i ) { 54048 54049 const object = arguments[ i ], 54050 uuid = object.uuid, 54051 index = indicesByUUID[ uuid ]; 54052 54053 if ( index !== undefined ) { 54054 54055 delete indicesByUUID[ uuid ]; 54056 54057 if ( index < nCachedObjects ) { 54058 54059 // object is cached, shrink the CACHED region 54060 54061 const firstActiveIndex = -- nCachedObjects, 54062 lastCachedObject = objects[ firstActiveIndex ], 54063 lastIndex = -- nObjects, 54064 lastObject = objects[ lastIndex ]; 54065 54066 if ( index !== firstActiveIndex ) { 54067 54068 // last cached object takes this object's place 54069 54070 indicesByUUID[ lastCachedObject.uuid ] = index; 54071 54072 } 54073 54074 objects[ index ] = lastCachedObject; 54075 54076 if ( firstActiveIndex !== lastIndex ) { 54077 54078 // last object goes to the activated slot and pop 54079 54080 indicesByUUID[ lastObject.uuid ] = firstActiveIndex; 54081 54082 } 54083 54084 objects[ firstActiveIndex ] = lastObject; 54085 objects.pop(); 54086 54087 // accounting is done, now do the same for all bindings 54088 54089 for ( let j = 0, m = nBindings; j !== m; ++ j ) { 54090 54091 const bindingsForPath = bindings[ j ], 54092 lastCached = bindingsForPath[ firstActiveIndex ], 54093 last = bindingsForPath[ lastIndex ]; 54094 54095 bindingsForPath[ index ] = lastCached; 54096 bindingsForPath[ firstActiveIndex ] = last; 54097 bindingsForPath.pop(); 54098 54099 } 54100 54101 } else { 54102 54103 // object is active, just swap with the last and pop 54104 54105 const lastIndex = -- nObjects, 54106 lastObject = objects[ lastIndex ]; 54107 54108 if ( index !== lastIndex ) { 54109 54110 indicesByUUID[ lastObject.uuid ] = index; 54111 54112 } 54113 54114 objects[ index ] = lastObject; 54115 objects.pop(); 54116 54117 // accounting is done, now do the same for all bindings 54118 54119 for ( let j = 0, m = nBindings; j !== m; ++ j ) { 54120 54121 const bindingsForPath = bindings[ j ]; 54122 54123 bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; 54124 bindingsForPath.pop(); 54125 54126 } 54127 54128 } // cached or active 54129 54130 } // if object is known 54131 54132 } // for arguments 54133 54134 this.nCachedObjects_ = nCachedObjects; 54135 54136 } 54137 54138 // Internal interface used by befriended PropertyBinding.Composite: 54139 54140 subscribe_( path, parsedPath ) { 54141 54142 // returns an array of bindings for the given path that is changed 54143 // according to the contained objects in the group 54144 54145 const indicesByPath = this._bindingsIndicesByPath; 54146 let index = indicesByPath[ path ]; 54147 const bindings = this._bindings; 54148 54149 if ( index !== undefined ) return bindings[ index ]; 54150 54151 const paths = this._paths, 54152 parsedPaths = this._parsedPaths, 54153 objects = this._objects, 54154 nObjects = objects.length, 54155 nCachedObjects = this.nCachedObjects_, 54156 bindingsForPath = new Array( nObjects ); 54157 54158 index = bindings.length; 54159 54160 indicesByPath[ path ] = index; 54161 54162 paths.push( path ); 54163 parsedPaths.push( parsedPath ); 54164 bindings.push( bindingsForPath ); 54165 54166 for ( let i = nCachedObjects, n = objects.length; i !== n; ++ i ) { 54167 54168 const object = objects[ i ]; 54169 bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath ); 54170 54171 } 54172 54173 return bindingsForPath; 54174 54175 } 54176 54177 unsubscribe_( path ) { 54178 54179 // tells the group to forget about a property path and no longer 54180 // update the array previously obtained with 'subscribe_' 54181 54182 const indicesByPath = this._bindingsIndicesByPath, 54183 index = indicesByPath[ path ]; 54184 54185 if ( index !== undefined ) { 54186 54187 const paths = this._paths, 54188 parsedPaths = this._parsedPaths, 54189 bindings = this._bindings, 54190 lastBindingsIndex = bindings.length - 1, 54191 lastBindings = bindings[ lastBindingsIndex ], 54192 lastBindingsPath = paths[ lastBindingsIndex ]; 54193 54194 indicesByPath[ lastBindingsPath ] = index; 54195 54196 bindings[ index ] = lastBindings; 54197 bindings.pop(); 54198 54199 parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; 54200 parsedPaths.pop(); 54201 54202 paths[ index ] = paths[ lastBindingsIndex ]; 54203 paths.pop(); 54204 54205 } 54206 54207 } 54208 54209} 54210 54211/** 54212 * An instance of `AnimationAction` schedules the playback of an animation which is 54213 * stored in {@link AnimationClip}. 54214 */ 54215class AnimationAction { 54216 54217 /** 54218 * Constructs a new animation action. 54219 * 54220 * @param {AnimationMixer} mixer - The mixer that is controlled by this action. 54221 * @param {AnimationClip} clip - The animation clip that holds the actual keyframes. 54222 * @param {?Object3D} [localRoot=null] - The root object on which this action is performed. 54223 * @param {(NormalAnimationBlendMode|AdditiveAnimationBlendMode)} [blendMode] - The blend mode. 54224 */ 54225 constructor( mixer, clip, localRoot = null, blendMode = clip.blendMode ) { 54226 54227 this._mixer = mixer; 54228 this._clip = clip; 54229 this._localRoot = localRoot; 54230 54231 /** 54232 * Defines how the animation is blended/combined when two or more animations 54233 * are simultaneously played. 54234 * 54235 * @type {(NormalAnimationBlendMode|AdditiveAnimationBlendMode)} 54236 */ 54237 this.blendMode = blendMode; 54238 54239 const tracks = clip.tracks, 54240 nTracks = tracks.length, 54241 interpolants = new Array( nTracks ); 54242 54243 const interpolantSettings = { 54244 endingStart: ZeroCurvatureEnding, 54245 endingEnd: ZeroCurvatureEnding 54246 }; 54247 54248 for ( let i = 0; i !== nTracks; ++ i ) { 54249 54250 const interpolant = tracks[ i ].createInterpolant( null ); 54251 interpolants[ i ] = interpolant; 54252 interpolant.settings = interpolantSettings; 54253 54254 } 54255 54256 this._interpolantSettings = interpolantSettings; 54257 54258 this._interpolants = interpolants; // bound by the mixer 54259 54260 // inside: PropertyMixer (managed by the mixer) 54261 this._propertyBindings = new Array( nTracks ); 54262 54263 this._cacheIndex = null; // for the memory manager 54264 this._byClipCacheIndex = null; // for the memory manager 54265 54266 this._timeScaleInterpolant = null; 54267 this._restoreTimeScale = null; 54268 this._weightInterpolant = null; 54269 54270 /** 54271 * The loop mode, set via {@link AnimationAction#setLoop}. 54272 * 54273 * @type {(LoopRepeat|LoopOnce|LoopPingPong)} 54274 * @default LoopRepeat 54275 */ 54276 this.loop = LoopRepeat; 54277 this._loopCount = -1; 54278 54279 // global mixer time when the action is to be started 54280 // it's set back to 'null' upon start of the action 54281 this._startTime = null; 54282 54283 /** 54284 * The local time of this action (in seconds, starting with `0`). 54285 * 54286 * The value gets clamped or wrapped to `[0,clip.duration]` (according to the 54287 * loop state). 54288 * 54289 * @type {number} 54290 * @default Infinity 54291 */ 54292 this.time = 0; 54293 54294 /** 54295 * Scaling factor for the {@link AnimationAction#time}. A value of `0` causes the 54296 * animation to pause. Negative values cause the animation to play backwards. 54297 * 54298 * @type {number} 54299 * @default 1 54300 */ 54301 this.timeScale = 1; 54302 this._effectiveTimeScale = 1; 54303 54304 /** 54305 * The degree of influence of this action (in the interval `[0, 1]`). Values 54306 * between `0` (no impact) and `1` (full impact) can be used to blend between 54307 * several actions. 54308 * 54309 * @type {number} 54310 * @default 1 54311 */ 54312 this.weight = 1; 54313 this._effectiveWeight = 1; 54314 54315 /** 54316 * The number of repetitions of the performed clip over the course of this action. 54317 * Can be set via {@link AnimationAction#setLoop}. 54318 * 54319 * Setting this number has no effect if {@link AnimationAction#loop} is set to 54320 * `THREE:LoopOnce`. 54321 * 54322 * @type {number} 54323 * @default Infinity 54324 */ 54325 this.repetitions = Infinity; 54326 54327 /** 54328 * If set to `true`, the playback of the action is paused. 54329 * 54330 * @type {boolean} 54331 * @default false 54332 */ 54333 this.paused = false; 54334 54335 /** 54336 * If set to `false`, the action is disabled so it has no impact. 54337 * 54338 * When the action is re-enabled, the animation continues from its current 54339 * time (setting `enabled` to `false` doesn't reset the action). 54340 * 54341 * @type {boolean} 54342 * @default true 54343 */ 54344 this.enabled = true; 54345 54346 /** 54347 * If set to true the animation will automatically be paused on its last frame. 54348 * 54349 * If set to false, {@link AnimationAction#enabled} will automatically be switched 54350 * to `false` when the last loop of the action has finished, so that this action has 54351 * no further impact. 54352 * 54353 * Note: This member has no impact if the action is interrupted (it 54354 * has only an effect if its last loop has really finished). 54355 * 54356 * @type {boolean} 54357 * @default false 54358 */ 54359 this.clampWhenFinished = false; 54360 54361 /** 54362 * Enables smooth interpolation without separate clips for start, loop and end. 54363 * 54364 * @type {boolean} 54365 * @default true 54366 */ 54367 this.zeroSlopeAtStart = true; 54368 54369 /** 54370 * Enables smooth interpolation without separate clips for start, loop and end. 54371 * 54372 * @type {boolean} 54373 * @default true 54374 */ 54375 this.zeroSlopeAtEnd = true; 54376 54377 } 54378 54379 /** 54380 * Starts the playback of the animation. 54381 * 54382 * @return {AnimationAction} A reference to this animation action. 54383 */ 54384 play() { 54385 54386 this._mixer._activateAction( this ); 54387 54388 return this; 54389 54390 } 54391 54392 /** 54393 * Stops the playback of the animation. 54394 * 54395 * @return {AnimationAction} A reference to this animation action. 54396 */ 54397 stop() { 54398 54399 this._mixer._deactivateAction( this ); 54400 54401 return this.reset(); 54402 54403 } 54404 54405 /** 54406 * Resets the playback of the animation. 54407 * 54408 * @return {AnimationAction} A reference to this animation action. 54409 */ 54410 reset() { 54411 54412 this.paused = false; 54413 this.enabled = true; 54414 54415 this.time = 0; // restart clip 54416 this._loopCount = -1;// forget previous loops 54417 this._startTime = null;// forget scheduling 54418 54419 return this.stopFading().stopWarping(); 54420 54421 } 54422 54423 /** 54424 * Returns `true` if the animation is running. 54425 * 54426 * @return {boolean} Whether the animation is running or not. 54427 */ 54428 isRunning() { 54429 54430 return this.enabled && ! this.paused && this.timeScale !== 0 && 54431 this._startTime === null && this._mixer._isActiveAction( this ); 54432 54433 } 54434 54435 /** 54436 * Returns `true` when {@link AnimationAction#play} has been called. 54437 * 54438 * @return {boolean} Whether the animation is scheduled or not. 54439 */ 54440 isScheduled() { 54441 54442 return this._mixer._isActiveAction( this ); 54443 54444 } 54445 54446 /** 54447 * Defines the time when the animation should start. 54448 * 54449 * @param {number} time - The start time in seconds. 54450 * @return {AnimationAction} A reference to this animation action. 54451 */ 54452 startAt( time ) { 54453 54454 this._startTime = time; 54455 54456 return this; 54457 54458 } 54459 54460 /** 54461 * Configures the loop settings for this action. 54462 * 54463 * @param {(LoopRepeat|LoopOnce|LoopPingPong)} mode - The loop mode. 54464 * @param {number} repetitions - The number of repetitions. 54465 * @return {AnimationAction} A reference to this animation action. 54466 */ 54467 setLoop( mode, repetitions ) { 54468 54469 this.loop = mode; 54470 this.repetitions = repetitions; 54471 54472 return this; 54473 54474 } 54475 54476 /** 54477 * Sets the effective weight of this action. 54478 * 54479 * An action has no effect and thus an effective weight of zero when the 54480 * action is disabled. 54481 * 54482 * @param {number} weight - The weight to set. 54483 * @return {AnimationAction} A reference to this animation action. 54484 */ 54485 setEffectiveWeight( weight ) { 54486 54487 this.weight = weight; 54488 54489 // note: same logic as when updated at runtime 54490 this._effectiveWeight = this.enabled ? weight : 0; 54491 54492 return this.stopFading(); 54493 54494 } 54495 54496 /** 54497 * Returns the effective weight of this action. 54498 * 54499 * @return {number} The effective weight. 54500 */ 54501 getEffectiveWeight() { 54502 54503 return this._effectiveWeight; 54504 54505 } 54506 54507 /** 54508 * Fades the animation in by increasing its weight gradually from `0` to `1`, 54509 * within the passed time interval. 54510 * 54511 * @param {number} duration - The duration of the fade. 54512 * @return {AnimationAction} A reference to this animation action. 54513 */ 54514 fadeIn( duration ) { 54515 54516 return this._scheduleFading( duration, 0, 1 ); 54517 54518 } 54519 54520 /** 54521 * Fades the animation out by decreasing its weight gradually from `1` to `0`, 54522 * within the passed time interval. 54523 * 54524 * @param {number} duration - The duration of the fade. 54525 * @return {AnimationAction} A reference to this animation action. 54526 */ 54527 fadeOut( duration ) { 54528 54529 return this._scheduleFading( duration, 1, 0 ); 54530 54531 } 54532 54533 /** 54534 * Causes this action to fade in and the given action to fade out, 54535 * within the passed time interval. 54536 * 54537 * @param {AnimationAction} fadeOutAction - The animation action to fade out. 54538 * @param {number} duration - The duration of the fade. 54539 * @param {boolean} [warp=false] - Whether warping should be used or not. 54540 * @return {AnimationAction} A reference to this animation action. 54541 */ 54542 crossFadeFrom( fadeOutAction, duration, warp = false ) { 54543 54544 fadeOutAction.fadeOut( duration ); 54545 this.fadeIn( duration ); 54546 54547 if ( warp === true ) { 54548 54549 const fadeInDuration = this._clip.duration, 54550 fadeOutDuration = fadeOutAction._clip.duration, 54551 54552 startEndRatio = fadeOutDuration / fadeInDuration, 54553 endStartRatio = fadeInDuration / fadeOutDuration; 54554 54555 54556 fadeOutAction._restoreTimeScale = fadeOutAction.timeScale; 54557 this._restoreTimeScale = this.timeScale; 54558 54559 fadeOutAction.warp( 1.0, startEndRatio, duration ); 54560 this.warp( endStartRatio, 1.0, duration ); 54561 54562 } 54563 54564 return this; 54565 54566 } 54567 54568 /** 54569 * Causes this action to fade out and the given action to fade in, 54570 * within the passed time interval. 54571 * 54572 * @param {AnimationAction} fadeInAction - The animation action to fade in. 54573 * @param {number} duration - The duration of the fade. 54574 * @param {boolean} [warp=false] - Whether warping should be used or not. 54575 * @return {AnimationAction} A reference to this animation action. 54576 */ 54577 crossFadeTo( fadeInAction, duration, warp = false ) { 54578 54579 return fadeInAction.crossFadeFrom( this, duration, warp ); 54580 54581 } 54582 54583 /** 54584 * Stops any fading which is applied to this action. 54585 * 54586 * @return {AnimationAction} A reference to this animation action. 54587 */ 54588 stopFading() { 54589 54590 const weightInterpolant = this._weightInterpolant; 54591 54592 if ( weightInterpolant !== null ) { 54593 54594 this._weightInterpolant = null; 54595 this._mixer._takeBackControlInterpolant( weightInterpolant ); 54596 54597 } 54598 54599 return this; 54600 54601 } 54602 54603 /** 54604 * Sets the effective time scale of this action. 54605 * 54606 * An action has no effect and thus an effective time scale of zero when the 54607 * action is paused. 54608 * 54609 * @param {number} timeScale - The time scale to set. 54610 * @return {AnimationAction} A reference to this animation action. 54611 */ 54612 setEffectiveTimeScale( timeScale ) { 54613 54614 this.timeScale = timeScale; 54615 this._effectiveTimeScale = this.paused ? 0 : timeScale; 54616 54617 return this.stopWarping(); 54618 54619 } 54620 54621 /** 54622 * Returns the effective time scale of this action. 54623 * 54624 * @return {number} The effective time scale. 54625 */ 54626 getEffectiveTimeScale() { 54627 54628 return this._effectiveTimeScale; 54629 54630 } 54631 54632 /** 54633 * Sets the duration for a single loop of this action. 54634 * 54635 * @param {number} duration - The duration to set. 54636 * @return {AnimationAction} A reference to this animation action. 54637 */ 54638 setDuration( duration ) { 54639 54640 this.timeScale = this._clip.duration / duration; 54641 54642 return this.stopWarping(); 54643 54644 } 54645 54646 /** 54647 * Synchronizes this action with the passed other action. 54648 * 54649 * @param {AnimationAction} action - The action to sync with. 54650 * @return {AnimationAction} A reference to this animation action. 54651 */ 54652 syncWith( action ) { 54653 54654 this.time = action.time; 54655 this.timeScale = action.timeScale; 54656 54657 return this.stopWarping(); 54658 54659 } 54660 54661 /** 54662 * Decelerates this animation's speed to `0` within the passed time interval. 54663 * 54664 * @param {number} duration - The duration. 54665 * @return {AnimationAction} A reference to this animation action. 54666 */ 54667 halt( duration ) { 54668 54669 return this.warp( this._effectiveTimeScale, 0, duration ); 54670 54671 } 54672 54673 /** 54674 * Changes the playback speed, within the passed time interval, by modifying 54675 * {@link AnimationAction#timeScale} gradually from `startTimeScale` to 54676 * `endTimeScale`. 54677 * 54678 * @param {number} startTimeScale - The start time scale. 54679 * @param {number} endTimeScale - The end time scale. 54680 * @param {number} duration - The duration. 54681 * @return {AnimationAction} A reference to this animation action. 54682 */ 54683 warp( startTimeScale, endTimeScale, duration ) { 54684 54685 const mixer = this._mixer, 54686 now = mixer.time, 54687 timeScale = this.timeScale; 54688 54689 let interpolant = this._timeScaleInterpolant; 54690 54691 if ( interpolant === null ) { 54692 54693 interpolant = mixer._lendControlInterpolant(); 54694 this._timeScaleInterpolant = interpolant; 54695 54696 } 54697 54698 const times = interpolant.parameterPositions, 54699 values = interpolant.sampleValues; 54700 54701 times[ 0 ] = now; 54702 times[ 1 ] = now + duration; 54703 54704 values[ 0 ] = startTimeScale / timeScale; 54705 values[ 1 ] = endTimeScale / timeScale; 54706 54707 return this; 54708 54709 } 54710 54711 /** 54712 * Stops any scheduled warping which is applied to this action. 54713 * 54714 * @return {AnimationAction} A reference to this animation action. 54715 */ 54716 stopWarping() { 54717 54718 const timeScaleInterpolant = this._timeScaleInterpolant; 54719 54720 if ( timeScaleInterpolant !== null ) { 54721 54722 this._timeScaleInterpolant = null; 54723 this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); 54724 54725 } 54726 54727 this._restoreTimeScale = null; 54728 54729 return this; 54730 54731 } 54732 54733 /** 54734 * Returns the animation mixer of this animation action. 54735 * 54736 * @return {AnimationMixer} The animation mixer. 54737 */ 54738 getMixer() { 54739 54740 return this._mixer; 54741 54742 } 54743 54744 /** 54745 * Returns the animation clip of this animation action. 54746 * 54747 * @return {AnimationClip} The animation clip. 54748 */ 54749 getClip() { 54750 54751 return this._clip; 54752 54753 } 54754 54755 /** 54756 * Returns the root object of this animation action. 54757 * 54758 * @return {Object3D} The root object. 54759 */ 54760 getRoot() { 54761 54762 return this._localRoot || this._mixer._root; 54763 54764 } 54765 54766 // Internal 54767 54768 _update( time, deltaTime, timeDirection, accuIndex ) { 54769 54770 // called by the mixer 54771 54772 if ( ! this.enabled ) { 54773 54774 // call ._updateWeight() to update ._effectiveWeight 54775 54776 this._updateWeight( time ); 54777 return; 54778 54779 } 54780 54781 const startTime = this._startTime; 54782 54783 if ( startTime !== null ) { 54784 54785 // check for scheduled start of action 54786 54787 const timeRunning = ( time - startTime ) * timeDirection; 54788 if ( timeRunning < 0 || timeDirection === 0 ) { 54789 54790 deltaTime = 0; 54791 54792 } else { 54793 54794 54795 this._startTime = null; // unschedule 54796 deltaTime = timeDirection * timeRunning; 54797 54798 } 54799 54800 } 54801 54802 // apply time scale and advance time 54803 54804 deltaTime *= this._updateTimeScale( time ); 54805 const clipTime = this._updateTime( deltaTime ); 54806 54807 // note: _updateTime may disable the action resulting in 54808 // an effective weight of 0 54809 54810 const weight = this._updateWeight( time ); 54811 54812 if ( weight > 0 ) { 54813 54814 const interpolants = this._interpolants; 54815 const propertyMixers = this._propertyBindings; 54816 54817 switch ( this.blendMode ) { 54818 54819 case AdditiveAnimationBlendMode: 54820 54821 for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { 54822 54823 interpolants[ j ].evaluate( clipTime ); 54824 propertyMixers[ j ].accumulateAdditive( weight ); 54825 54826 } 54827 54828 break; 54829 54830 case NormalAnimationBlendMode: 54831 default: 54832 54833 for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { 54834 54835 interpolants[ j ].evaluate( clipTime ); 54836 propertyMixers[ j ].accumulate( accuIndex, weight ); 54837 54838 } 54839 54840 } 54841 54842 } 54843 54844 } 54845 54846 _updateWeight( time ) { 54847 54848 let weight = 0; 54849 54850 if ( this.enabled ) { 54851 54852 weight = this.weight; 54853 const interpolant = this._weightInterpolant; 54854 54855 if ( interpolant !== null ) { 54856 54857 const interpolantValue = interpolant.evaluate( time )[ 0 ]; 54858 54859 weight *= interpolantValue; 54860 54861 if ( time > interpolant.parameterPositions[ 1 ] ) { 54862 54863 this.stopFading(); 54864 54865 if ( interpolantValue === 0 ) { 54866 54867 // faded out, disable 54868 this.enabled = false; 54869 54870 } 54871 54872 } 54873 54874 } 54875 54876 } 54877 54878 this._effectiveWeight = weight; 54879 return weight; 54880 54881 } 54882 54883 _updateTimeScale( time ) { 54884 54885 let timeScale = 0; 54886 54887 if ( ! this.paused ) { 54888 54889 timeScale = this.timeScale; 54890 54891 const interpolant = this._timeScaleInterpolant; 54892 54893 if ( interpolant !== null ) { 54894 54895 const interpolantValue = interpolant.evaluate( time )[ 0 ]; 54896 54897 timeScale *= interpolantValue; 54898 54899 if ( time > interpolant.parameterPositions[ 1 ] ) { 54900 54901 if ( timeScale === 0 ) { 54902 54903 // motion has halted, pause 54904 this.paused = true; 54905 54906 } else { 54907 54908 if ( this._restoreTimeScale !== null ) { 54909 54910 timeScale = this._restoreTimeScale; 54911 54912 } 54913 54914 // warp done - apply final time scale 54915 this.timeScale = timeScale; 54916 54917 } 54918 54919 this.stopWarping(); 54920 54921 } 54922 54923 } 54924 54925 } 54926 54927 this._effectiveTimeScale = timeScale; 54928 return timeScale; 54929 54930 } 54931 54932 _updateTime( deltaTime ) { 54933 54934 const duration = this._clip.duration; 54935 const loop = this.loop; 54936 54937 let time = this.time + deltaTime; 54938 let loopCount = this._loopCount; 54939 54940 const pingPong = ( loop === LoopPingPong ); 54941 54942 if ( deltaTime === 0 ) { 54943 54944 if ( loopCount === -1 ) return time; 54945 54946 return ( pingPong && ( loopCount & 1 ) === 1 ) ? duration - time : time; 54947 54948 } 54949 54950 if ( loop === LoopOnce ) { 54951 54952 if ( loopCount === -1 ) { 54953 54954 // just started 54955 54956 this._loopCount = 0; 54957 this._setEndings( true, true, false ); 54958 54959 } 54960 54961 handle_stop: { 54962 54963 if ( time >= duration ) { 54964 54965 time = duration; 54966 54967 } else if ( time < 0 ) { 54968 54969 time = 0; 54970 54971 } else { 54972 54973 this.time = time; 54974 54975 break handle_stop; 54976 54977 } 54978 54979 if ( this.clampWhenFinished ) this.paused = true; 54980 else this.enabled = false; 54981 54982 this.time = time; 54983 54984 this._mixer.dispatchEvent( { 54985 type: 'finished', action: this, 54986 direction: deltaTime < 0 ? -1 : 1 54987 } ); 54988 54989 } 54990 54991 } else { // repetitive Repeat or PingPong 54992 54993 if ( loopCount === -1 ) { 54994 54995 // just started 54996 54997 if ( deltaTime >= 0 ) { 54998 54999 loopCount = 0; 55000 55001 this._setEndings( true, this.repetitions === 0, pingPong ); 55002 55003 } else { 55004 55005 // when looping in reverse direction, the initial 55006 // transition through zero counts as a repetition, 55007 // so leave loopCount at -1 55008 55009 this._setEndings( this.repetitions === 0, true, pingPong ); 55010 55011 } 55012 55013 } 55014 55015 if ( time >= duration || time < 0 ) { 55016 55017 // wrap around 55018 55019 const loopDelta = Math.floor( time / duration ); // signed 55020 time -= duration * loopDelta; 55021 55022 loopCount += Math.abs( loopDelta ); 55023 55024 const pending = this.repetitions - loopCount; 55025 55026 if ( pending <= 0 ) { 55027 55028 // have to stop (switch state, clamp time, fire event) 55029 55030 if ( this.clampWhenFinished ) this.paused = true; 55031 else this.enabled = false; 55032 55033 time = deltaTime > 0 ? duration : 0; 55034 55035 this.time = time; 55036 55037 this._mixer.dispatchEvent( { 55038 type: 'finished', action: this, 55039 direction: deltaTime > 0 ? 1 : -1 55040 } ); 55041 55042 } else { 55043 55044 // keep running 55045 55046 if ( pending === 1 ) { 55047 55048 // entering the last round 55049 55050 const atStart = deltaTime < 0; 55051 this._setEndings( atStart, ! atStart, pingPong ); 55052 55053 } else { 55054 55055 this._setEndings( false, false, pingPong ); 55056 55057 } 55058 55059 this._loopCount = loopCount; 55060 55061 this.time = time; 55062 55063 this._mixer.dispatchEvent( { 55064 type: 'loop', action: this, loopDelta: loopDelta 55065 } ); 55066 55067 } 55068 55069 } else { 55070 55071 this._loopCount = loopCount; 55072 this.time = time; 55073 55074 } 55075 55076 if ( pingPong && ( loopCount & 1 ) === 1 ) { 55077 55078 // invert time for the "pong round" 55079 55080 return duration - time; 55081 55082 } 55083 55084 } 55085 55086 return time; 55087 55088 } 55089 55090 _setEndings( atStart, atEnd, pingPong ) { 55091 55092 const settings = this._interpolantSettings; 55093 55094 if ( pingPong ) { 55095 55096 settings.endingStart = ZeroSlopeEnding; 55097 settings.endingEnd = ZeroSlopeEnding; 55098 55099 } else { 55100 55101 // assuming for LoopOnce atStart == atEnd == true 55102 55103 if ( atStart ) { 55104 55105 settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; 55106 55107 } else { 55108 55109 settings.endingStart = WrapAroundEnding; 55110 55111 } 55112 55113 if ( atEnd ) { 55114 55115 settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; 55116 55117 } else { 55118 55119 settings.endingEnd = WrapAroundEnding; 55120 55121 } 55122 55123 } 55124 55125 } 55126 55127 _scheduleFading( duration, weightNow, weightThen ) { 55128 55129 const mixer = this._mixer, now = mixer.time; 55130 let interpolant = this._weightInterpolant; 55131 55132 if ( interpolant === null ) { 55133 55134 interpolant = mixer._lendControlInterpolant(); 55135 this._weightInterpolant = interpolant; 55136 55137 } 55138 55139 const times = interpolant.parameterPositions, 55140 values = interpolant.sampleValues; 55141 55142 times[ 0 ] = now; 55143 values[ 0 ] = weightNow; 55144 times[ 1 ] = now + duration; 55145 values[ 1 ] = weightThen; 55146 55147 return this; 55148 55149 } 55150 55151} 55152 55153const _controlInterpolantsResultBuffer = new Float32Array( 1 ); 55154 55155/** 55156 * `AnimationMixer` is a player for animations on a particular object in 55157 * the scene. When multiple objects in the scene are animated independently, 55158 * one `AnimationMixer` may be used for each object. 55159 */ 55160class AnimationMixer extends EventDispatcher { 55161 55162 /** 55163 * Constructs a new animation mixer. 55164 * 55165 * @param {Object3D} root - The object whose animations shall be played by this mixer. 55166 */ 55167 constructor( root ) { 55168 55169 super(); 55170 55171 this._root = root; 55172 this._initMemoryManager(); 55173 this._accuIndex = 0; 55174 55175 /** 55176 * The global mixer time (in seconds; starting with `0` on the mixer's creation). 55177 * 55178 * @type {number} 55179 * @default 0 55180 */ 55181 this.time = 0; 55182 55183 /** 55184 * A scaling factor for the global time. 55185 * 55186 * Note: Setting this member to `0` and later back to `1` is a 55187 * possibility to pause/unpause all actions that are controlled by this 55188 * mixer. 55189 * 55190 * @type {number} 55191 * @default 1 55192 */ 55193 this.timeScale = 1.0; 55194 55195 if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { 55196 55197 __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); 55198 55199 } 55200 55201 } 55202 55203 _bindAction( action, prototypeAction ) { 55204 55205 const root = action._localRoot || this._root, 55206 tracks = action._clip.tracks, 55207 nTracks = tracks.length, 55208 bindings = action._propertyBindings, 55209 interpolants = action._interpolants, 55210 rootUuid = root.uuid, 55211 bindingsByRoot = this._bindingsByRootAndName; 55212 55213 let bindingsByName = bindingsByRoot[ rootUuid ]; 55214 55215 if ( bindingsByName === undefined ) { 55216 55217 bindingsByName = {}; 55218 bindingsByRoot[ rootUuid ] = bindingsByName; 55219 55220 } 55221 55222 for ( let i = 0; i !== nTracks; ++ i ) { 55223 55224 const track = tracks[ i ], 55225 trackName = track.name; 55226 55227 let binding = bindingsByName[ trackName ]; 55228 55229 if ( binding !== undefined ) { 55230 55231 ++ binding.referenceCount; 55232 bindings[ i ] = binding; 55233 55234 } else { 55235 55236 binding = bindings[ i ]; 55237 55238 if ( binding !== undefined ) { 55239 55240 // existing binding, make sure the cache knows 55241 55242 if ( binding._cacheIndex === null ) { 55243 55244 ++ binding.referenceCount; 55245 this._addInactiveBinding( binding, rootUuid, trackName ); 55246 55247 } 55248 55249 continue; 55250 55251 } 55252 55253 const path = prototypeAction && prototypeAction. 55254 _propertyBindings[ i ].binding.parsedPath; 55255 55256 binding = new PropertyMixer( 55257 PropertyBinding.create( root, trackName, path ), 55258 track.ValueTypeName, track.getValueSize() ); 55259 55260 ++ binding.referenceCount; 55261 this._addInactiveBinding( binding, rootUuid, trackName ); 55262 55263 bindings[ i ] = binding; 55264 55265 } 55266 55267 interpolants[ i ].resultBuffer = binding.buffer; 55268 55269 } 55270 55271 } 55272 55273 _activateAction( action ) { 55274 55275 if ( ! this._isActiveAction( action ) ) { 55276 55277 if ( action._cacheIndex === null ) { 55278 55279 // this action has been forgotten by the cache, but the user 55280 // appears to be still using it -> rebind 55281 55282 const rootUuid = ( action._localRoot || this._root ).uuid, 55283 clipUuid = action._clip.uuid, 55284 actionsForClip = this._actionsByClip[ clipUuid ]; 55285 55286 this._bindAction( action, 55287 actionsForClip && actionsForClip.knownActions[ 0 ] ); 55288 55289 this._addInactiveAction( action, clipUuid, rootUuid ); 55290 55291 } 55292 55293 const bindings = action._propertyBindings; 55294 55295 // increment reference counts / sort out state 55296 for ( let i = 0, n = bindings.length; i !== n; ++ i ) { 55297 55298 const binding = bindings[ i ]; 55299 55300 if ( binding.useCount ++ === 0 ) { 55301 55302 this._lendBinding( binding ); 55303 binding.saveOriginalState(); 55304 55305 } 55306 55307 } 55308 55309 this._lendAction( action ); 55310 55311 } 55312 55313 } 55314 55315 _deactivateAction( action ) { 55316 55317 if ( this._isActiveAction( action ) ) { 55318 55319 const bindings = action._propertyBindings; 55320 55321 // decrement reference counts / sort out state 55322 for ( let i = 0, n = bindings.length; i !== n; ++ i ) { 55323 55324 const binding = bindings[ i ]; 55325 55326 if ( -- binding.useCount === 0 ) { 55327 55328 binding.restoreOriginalState(); 55329 this._takeBackBinding( binding ); 55330 55331 } 55332 55333 } 55334 55335 this._takeBackAction( action ); 55336 55337 } 55338 55339 } 55340 55341 // Memory manager 55342 55343 _initMemoryManager() { 55344 55345 this._actions = []; // 'nActiveActions' followed by inactive ones 55346 this._nActiveActions = 0; 55347 55348 this._actionsByClip = {}; 55349 // inside: 55350 // { 55351 // knownActions: Array< AnimationAction > - used as prototypes 55352 // actionByRoot: AnimationAction - lookup 55353 // } 55354 55355 55356 this._bindings = []; // 'nActiveBindings' followed by inactive ones 55357 this._nActiveBindings = 0; 55358 55359 this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > 55360 55361 55362 this._controlInterpolants = []; // same game as above 55363 this._nActiveControlInterpolants = 0; 55364 55365 const scope = this; 55366 55367 this.stats = { 55368 55369 actions: { 55370 get total() { 55371 55372 return scope._actions.length; 55373 55374 }, 55375 get inUse() { 55376 55377 return scope._nActiveActions; 55378 55379 } 55380 }, 55381 bindings: { 55382 get total() { 55383 55384 return scope._bindings.length; 55385 55386 }, 55387 get inUse() { 55388 55389 return scope._nActiveBindings; 55390 55391 } 55392 }, 55393 controlInterpolants: { 55394 get total() { 55395 55396 return scope._controlInterpolants.length; 55397 55398 }, 55399 get inUse() { 55400 55401 return scope._nActiveControlInterpolants; 55402 55403 } 55404 } 55405 55406 }; 55407 55408 } 55409 55410 // Memory management for AnimationAction objects 55411 55412 _isActiveAction( action ) { 55413 55414 const index = action._cacheIndex; 55415 return index !== null && index < this._nActiveActions; 55416 55417 } 55418 55419 _addInactiveAction( action, clipUuid, rootUuid ) { 55420 55421 const actions = this._actions, 55422 actionsByClip = this._actionsByClip; 55423 55424 let actionsForClip = actionsByClip[ clipUuid ]; 55425 55426 if ( actionsForClip === undefined ) { 55427 55428 actionsForClip = { 55429 55430 knownActions: [ action ], 55431 actionByRoot: {} 55432 55433 }; 55434 55435 action._byClipCacheIndex = 0; 55436 55437 actionsByClip[ clipUuid ] = actionsForClip; 55438 55439 } else { 55440 55441 const knownActions = actionsForClip.knownActions; 55442 55443 action._byClipCacheIndex = knownActions.length; 55444 knownActions.push( action ); 55445 55446 } 55447 55448 action._cacheIndex = actions.length; 55449 actions.push( action ); 55450 55451 actionsForClip.actionByRoot[ rootUuid ] = action; 55452 55453 } 55454 55455 _removeInactiveAction( action ) { 55456 55457 const actions = this._actions, 55458 lastInactiveAction = actions[ actions.length - 1 ], 55459 cacheIndex = action._cacheIndex; 55460 55461 lastInactiveAction._cacheIndex = cacheIndex; 55462 actions[ cacheIndex ] = lastInactiveAction; 55463 actions.pop(); 55464 55465 action._cacheIndex = null; 55466 55467 55468 const clipUuid = action._clip.uuid, 55469 actionsByClip = this._actionsByClip, 55470 actionsForClip = actionsByClip[ clipUuid ], 55471 knownActionsForClip = actionsForClip.knownActions, 55472 55473 lastKnownAction = 55474 knownActionsForClip[ knownActionsForClip.length - 1 ], 55475 55476 byClipCacheIndex = action._byClipCacheIndex; 55477 55478 lastKnownAction._byClipCacheIndex = byClipCacheIndex; 55479 knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; 55480 knownActionsForClip.pop(); 55481 55482 action._byClipCacheIndex = null; 55483 55484 55485 const actionByRoot = actionsForClip.actionByRoot, 55486 rootUuid = ( action._localRoot || this._root ).uuid; 55487 55488 delete actionByRoot[ rootUuid ]; 55489 55490 if ( knownActionsForClip.length === 0 ) { 55491 55492 delete actionsByClip[ clipUuid ]; 55493 55494 } 55495 55496 this._removeInactiveBindingsForAction( action ); 55497 55498 } 55499 55500 _removeInactiveBindingsForAction( action ) { 55501 55502 const bindings = action._propertyBindings; 55503 55504 for ( let i = 0, n = bindings.length; i !== n; ++ i ) { 55505 55506 const binding = bindings[ i ]; 55507 55508 if ( -- binding.referenceCount === 0 ) { 55509 55510 this._removeInactiveBinding( binding ); 55511 55512 } 55513 55514 } 55515 55516 } 55517 55518 _lendAction( action ) { 55519 55520 // [ active actions | inactive actions ] 55521 // [ active actions >| inactive actions ] 55522 // s a 55523 // <-swap-> 55524 // a s 55525 55526 const actions = this._actions, 55527 prevIndex = action._cacheIndex, 55528 55529 lastActiveIndex = this._nActiveActions ++, 55530 55531 firstInactiveAction = actions[ lastActiveIndex ]; 55532 55533 action._cacheIndex = lastActiveIndex; 55534 actions[ lastActiveIndex ] = action; 55535 55536 firstInactiveAction._cacheIndex = prevIndex; 55537 actions[ prevIndex ] = firstInactiveAction; 55538 55539 } 55540 55541 _takeBackAction( action ) { 55542 55543 // [ active actions | inactive actions ] 55544 // [ active actions |< inactive actions ] 55545 // a s 55546 // <-swap-> 55547 // s a 55548 55549 const actions = this._actions, 55550 prevIndex = action._cacheIndex, 55551 55552 firstInactiveIndex = -- this._nActiveActions, 55553 55554 lastActiveAction = actions[ firstInactiveIndex ]; 55555 55556 action._cacheIndex = firstInactiveIndex; 55557 actions[ firstInactiveIndex ] = action; 55558 55559 lastActiveAction._cacheIndex = prevIndex; 55560 actions[ prevIndex ] = lastActiveAction; 55561 55562 } 55563 55564 // Memory management for PropertyMixer objects 55565 55566 _addInactiveBinding( binding, rootUuid, trackName ) { 55567 55568 const bindingsByRoot = this._bindingsByRootAndName, 55569 bindings = this._bindings; 55570 55571 let bindingByName = bindingsByRoot[ rootUuid ]; 55572 55573 if ( bindingByName === undefined ) { 55574 55575 bindingByName = {}; 55576 bindingsByRoot[ rootUuid ] = bindingByName; 55577 55578 } 55579 55580 bindingByName[ trackName ] = binding; 55581 55582 binding._cacheIndex = bindings.length; 55583 bindings.push( binding ); 55584 55585 } 55586 55587 _removeInactiveBinding( binding ) { 55588 55589 const bindings = this._bindings, 55590 propBinding = binding.binding, 55591 rootUuid = propBinding.rootNode.uuid, 55592 trackName = propBinding.path, 55593 bindingsByRoot = this._bindingsByRootAndName, 55594 bindingByName = bindingsByRoot[ rootUuid ], 55595 55596 lastInactiveBinding = bindings[ bindings.length - 1 ], 55597 cacheIndex = binding._cacheIndex; 55598 55599 lastInactiveBinding._cacheIndex = cacheIndex; 55600 bindings[ cacheIndex ] = lastInactiveBinding; 55601 bindings.pop(); 55602 55603 delete bindingByName[ trackName ]; 55604 55605 if ( Object.keys( bindingByName ).length === 0 ) { 55606 55607 delete bindingsByRoot[ rootUuid ]; 55608 55609 } 55610 55611 } 55612 55613 _lendBinding( binding ) { 55614 55615 const bindings = this._bindings, 55616 prevIndex = binding._cacheIndex, 55617 55618 lastActiveIndex = this._nActiveBindings ++, 55619 55620 firstInactiveBinding = bindings[ lastActiveIndex ]; 55621 55622 binding._cacheIndex = lastActiveIndex; 55623 bindings[ lastActiveIndex ] = binding; 55624 55625 firstInactiveBinding._cacheIndex = prevIndex; 55626 bindings[ prevIndex ] = firstInactiveBinding; 55627 55628 } 55629 55630 _takeBackBinding( binding ) { 55631 55632 const bindings = this._bindings, 55633 prevIndex = binding._cacheIndex, 55634 55635 firstInactiveIndex = -- this._nActiveBindings, 55636 55637 lastActiveBinding = bindings[ firstInactiveIndex ]; 55638 55639 binding._cacheIndex = firstInactiveIndex; 55640 bindings[ firstInactiveIndex ] = binding; 55641 55642 lastActiveBinding._cacheIndex = prevIndex; 55643 bindings[ prevIndex ] = lastActiveBinding; 55644 55645 } 55646 55647 55648 // Memory management of Interpolants for weight and time scale 55649 55650 _lendControlInterpolant() { 55651 55652 const interpolants = this._controlInterpolants, 55653 lastActiveIndex = this._nActiveControlInterpolants ++; 55654 55655 let interpolant = interpolants[ lastActiveIndex ]; 55656 55657 if ( interpolant === undefined ) { 55658 55659 interpolant = new LinearInterpolant( 55660 new Float32Array( 2 ), new Float32Array( 2 ), 55661 1, _controlInterpolantsResultBuffer ); 55662 55663 interpolant.__cacheIndex = lastActiveIndex; 55664 interpolants[ lastActiveIndex ] = interpolant; 55665 55666 } 55667 55668 return interpolant; 55669 55670 } 55671 55672 _takeBackControlInterpolant( interpolant ) { 55673 55674 const interpolants = this._controlInterpolants, 55675 prevIndex = interpolant.__cacheIndex, 55676 55677 firstInactiveIndex = -- this._nActiveControlInterpolants, 55678 55679 lastActiveInterpolant = interpolants[ firstInactiveIndex ]; 55680 55681 interpolant.__cacheIndex = firstInactiveIndex; 55682 interpolants[ firstInactiveIndex ] = interpolant; 55683 55684 lastActiveInterpolant.__cacheIndex = prevIndex; 55685 interpolants[ prevIndex ] = lastActiveInterpolant; 55686 55687 } 55688 55689 /** 55690 * Returns an instance of {@link AnimationAction} for the passed clip. 55691 * 55692 * If an action fitting the clip and root parameters doesn't yet exist, it 55693 * will be created by this method. Calling this method several times with the 55694 * same clip and root parameters always returns the same action. 55695 * 55696 * @param {AnimationClip|string} clip - An animation clip or alternatively the name of the animation clip. 55697 * @param {Object3D} [optionalRoot] - An alternative root object. 55698 * @param {(NormalAnimationBlendMode|AdditiveAnimationBlendMode)} [blendMode] - The blend mode. 55699 * @return {?AnimationAction} The animation action. 55700 */ 55701 clipAction( clip, optionalRoot, blendMode ) { 55702 55703 const root = optionalRoot || this._root, 55704 rootUuid = root.uuid; 55705 55706 let clipObject = typeof clip === 'string' ? AnimationClip.findByName( root, clip ) : clip; 55707 55708 const clipUuid = clipObject !== null ? clipObject.uuid : clip; 55709 55710 const actionsForClip = this._actionsByClip[ clipUuid ]; 55711 let prototypeAction = null; 55712 55713 if ( blendMode === undefined ) { 55714 55715 if ( clipObject !== null ) { 55716 55717 blendMode = clipObject.blendMode; 55718 55719 } else { 55720 55721 blendMode = NormalAnimationBlendMode; 55722 55723 } 55724 55725 } 55726 55727 if ( actionsForClip !== undefined ) { 55728 55729 const existingAction = actionsForClip.actionByRoot[ rootUuid ]; 55730 55731 if ( existingAction !== undefined && existingAction.blendMode === blendMode ) { 55732 55733 return existingAction; 55734 55735 } 55736 55737 // we know the clip, so we don't have to parse all 55738 // the bindings again but can just copy 55739 prototypeAction = actionsForClip.knownActions[ 0 ]; 55740 55741 // also, take the clip from the prototype action 55742 if ( clipObject === null ) 55743 clipObject = prototypeAction._clip; 55744 55745 } 55746 55747 // clip must be known when specified via string 55748 if ( clipObject === null ) return null; 55749 55750 // allocate all resources required to run it 55751 const newAction = new AnimationAction( this, clipObject, optionalRoot, blendMode ); 55752 55753 this._bindAction( newAction, prototypeAction ); 55754 55755 // and make the action known to the memory manager 55756 this._addInactiveAction( newAction, clipUuid, rootUuid ); 55757 55758 return newAction; 55759 55760 } 55761 55762 /** 55763 * Returns an existing animation action for the passed clip. 55764 * 55765 * @param {AnimationClip|string} clip - An animation clip or alternatively the name of the animation clip. 55766 * @param {Object3D} [optionalRoot] - An alternative root object. 55767 * @return {?AnimationAction} The animation action. Returns `null` if no action was found. 55768 */ 55769 existingAction( clip, optionalRoot ) { 55770 55771 const root = optionalRoot || this._root, 55772 rootUuid = root.uuid, 55773 55774 clipObject = typeof clip === 'string' ? 55775 AnimationClip.findByName( root, clip ) : clip, 55776 55777 clipUuid = clipObject ? clipObject.uuid : clip, 55778 55779 actionsForClip = this._actionsByClip[ clipUuid ]; 55780 55781 if ( actionsForClip !== undefined ) { 55782 55783 return actionsForClip.actionByRoot[ rootUuid ] || null; 55784 55785 } 55786 55787 return null; 55788 55789 } 55790 55791 /** 55792 * Deactivates all previously scheduled actions on this mixer. 55793 * 55794 * @return {AnimationMixer} A reference to this animation mixer. 55795 */ 55796 stopAllAction() { 55797 55798 const actions = this._actions, 55799 nActions = this._nActiveActions; 55800 55801 for ( let i = nActions - 1; i >= 0; -- i ) { 55802 55803 actions[ i ].stop(); 55804 55805 } 55806 55807 return this; 55808 55809 } 55810 55811 /** 55812 * Advances the global mixer time and updates the animation. 55813 * 55814 * This is usually done in the render loop by passing the delta 55815 * time from {@link Clock} or {@link Timer}. 55816 * 55817 * @param {number} deltaTime - The delta time in seconds. 55818 * @return {AnimationMixer} A reference to this animation mixer. 55819 */ 55820 update( deltaTime ) { 55821 55822 deltaTime *= this.timeScale; 55823 55824 const actions = this._actions, 55825 nActions = this._nActiveActions, 55826 55827 time = this.time += deltaTime, 55828 timeDirection = Math.sign( deltaTime ), 55829 55830 accuIndex = this._accuIndex ^= 1; 55831 55832 // run active actions 55833 55834 for ( let i = 0; i !== nActions; ++ i ) { 55835 55836 const action = actions[ i ]; 55837 55838 action._update( time, deltaTime, timeDirection, accuIndex ); 55839 55840 } 55841 55842 // update scene graph 55843 55844 const bindings = this._bindings, 55845 nBindings = this._nActiveBindings; 55846 55847 for ( let i = 0; i !== nBindings; ++ i ) { 55848 55849 bindings[ i ].apply( accuIndex ); 55850 55851 } 55852 55853 return this; 55854 55855 } 55856 55857 /** 55858 * Sets the global mixer to a specific time and updates the animation accordingly. 55859 * 55860 * This is useful when you need to jump to an exact time in an animation. The 55861 * input parameter will be scaled by {@link AnimationMixer#timeScale} 55862 * 55863 * @param {number} time - The time to set in seconds. 55864 * @return {AnimationMixer} A reference to this animation mixer. 55865 */ 55866 setTime( time ) { 55867 55868 this.time = 0; // Zero out time attribute for AnimationMixer object; 55869 for ( let i = 0; i < this._actions.length; i ++ ) { 55870 55871 this._actions[ i ].time = 0; // Zero out time attribute for all associated AnimationAction objects. 55872 55873 } 55874 55875 return this.update( time ); // Update used to set exact time. Returns "this" AnimationMixer object. 55876 55877 } 55878 55879 /** 55880 * Returns this mixer's root object. 55881 * 55882 * @return {Object3D} The mixer's root object. 55883 */ 55884 getRoot() { 55885 55886 return this._root; 55887 55888 } 55889 55890 /** 55891 * Deallocates all memory resources for a clip. Before using this method make 55892 * sure to call {@link AnimationAction#stop} for all related actions. 55893 * 55894 * @param {AnimationClip} clip - The clip to uncache. 55895 */ 55896 uncacheClip( clip ) { 55897 55898 const actions = this._actions, 55899 clipUuid = clip.uuid, 55900 actionsByClip = this._actionsByClip, 55901 actionsForClip = actionsByClip[ clipUuid ]; 55902 55903 if ( actionsForClip !== undefined ) { 55904 55905 // note: just calling _removeInactiveAction would mess up the 55906 // iteration state and also require updating the state we can 55907 // just throw away 55908 55909 const actionsToRemove = actionsForClip.knownActions; 55910 55911 for ( let i = 0, n = actionsToRemove.length; i !== n; ++ i ) { 55912 55913 const action = actionsToRemove[ i ]; 55914 55915 this._deactivateAction( action ); 55916 55917 const cacheIndex = action._cacheIndex, 55918 lastInactiveAction = actions[ actions.length - 1 ]; 55919 55920 action._cacheIndex = null; 55921 action._byClipCacheIndex = null; 55922 55923 lastInactiveAction._cacheIndex = cacheIndex; 55924 actions[ cacheIndex ] = lastInactiveAction; 55925 actions.pop(); 55926 55927 this._removeInactiveBindingsForAction( action ); 55928 55929 } 55930 55931 delete actionsByClip[ clipUuid ]; 55932 55933 } 55934 55935 } 55936 55937 /** 55938 * Deallocates all memory resources for a root object. Before using this 55939 * method make sure to call {@link AnimationAction#stop} for all related 55940 * actions or alternatively {@link AnimationMixer#stopAllAction} when the 55941 * mixer operates on a single root. 55942 * 55943 * @param {Object3D} root - The root object to uncache. 55944 */ 55945 uncacheRoot( root ) { 55946 55947 const rootUuid = root.uuid, 55948 actionsByClip = this._actionsByClip; 55949 55950 for ( const clipUuid in actionsByClip ) { 55951 55952 const actionByRoot = actionsByClip[ clipUuid ].actionByRoot, 55953 action = actionByRoot[ rootUuid ]; 55954 55955 if ( action !== undefined ) { 55956 55957 this._deactivateAction( action ); 55958 this._removeInactiveAction( action ); 55959 55960 } 55961 55962 } 55963 55964 const bindingsByRoot = this._bindingsByRootAndName, 55965 bindingByName = bindingsByRoot[ rootUuid ]; 55966 55967 if ( bindingByName !== undefined ) { 55968 55969 for ( const trackName in bindingByName ) { 55970 55971 const binding = bindingByName[ trackName ]; 55972 binding.restoreOriginalState(); 55973 this._removeInactiveBinding( binding ); 55974 55975 } 55976 55977 } 55978 55979 } 55980 55981 /** 55982 * Deallocates all memory resources for an action. The action is identified by the 55983 * given clip and an optional root object. Before using this method make 55984 * sure to call {@link AnimationAction#stop} to deactivate the action. 55985 * 55986 * @param {AnimationClip|string} clip - An animation clip or alternatively the name of the animation clip. 55987 * @param {Object3D} [optionalRoot] - An alternative root object. 55988 */ 55989 uncacheAction( clip, optionalRoot ) { 55990 55991 const action = this.existingAction( clip, optionalRoot ); 55992 55993 if ( action !== null ) { 55994 55995 this._deactivateAction( action ); 55996 this._removeInactiveAction( action ); 55997 55998 } 55999 56000 } 56001 56002} 56003 56004/** 56005 * Represents a 3D render target. 56006 * 56007 * @augments RenderTarget 56008 */ 56009class RenderTarget3D extends RenderTarget { 56010 56011 /** 56012 * Constructs a new 3D render target. 56013 * 56014 * @param {number} [width=1] - The width of the render target. 56015 * @param {number} [height=1] - The height of the render target. 56016 * @param {number} [depth=1] - The height of the render target. 56017 * @param {RenderTarget~Options} [options] - The configuration object. 56018 */ 56019 constructor( width = 1, height = 1, depth = 1, options = {} ) { 56020 56021 super( width, height, options ); 56022 56023 /** 56024 * This flag can be used for type testing. 56025 * 56026 * @type {boolean} 56027 * @readonly 56028 * @default true 56029 */ 56030 this.isRenderTarget3D = true; 56031 56032 this.depth = depth; 56033 56034 // overwrite attachments with 3D textures 56035 56036 for ( let i = 0; i < this.textures.length; i ++ ) { 56037 56038 const texture = new Data3DTexture( null, width, height, depth ); 56039 texture.isRenderTargetTexture = true; 56040 texture.renderTarget = this; 56041 56042 this.textures[ i ] = texture; 56043 56044 } 56045 56046 this._setTextureOptions( options ); 56047 56048 } 56049 56050} 56051 56052/** 56053 * Represents a uniform which is a global shader variable. They are passed to shader programs. 56054 * 56055 * When declaring a uniform of a {@link ShaderMaterial}, it is declared by value or by object. 56056 * ```js 56057 * uniforms: { 56058 * time: { value: 1.0 }, 56059 * resolution: new Uniform( new Vector2() ) 56060 * }; 56061 * ``` 56062 * Since this class can only be used in context of {@link ShaderMaterial}, it is only supported 56063 * in {@link WebGLRenderer}. 56064 */ 56065class Uniform { 56066 56067 /** 56068 * Constructs a new uniform. 56069 * 56070 * @param {any} value - The uniform value. 56071 */ 56072 constructor( value ) { 56073 56074 /** 56075 * The uniform value. 56076 * 56077 * @type {any} 56078 */ 56079 this.value = value; 56080 56081 } 56082 56083 /** 56084 * Returns a new uniform with copied values from this instance. 56085 * If the value has a `clone()` method, the value is cloned as well. 56086 * 56087 * @return {Uniform} A clone of this instance. 56088 */ 56089 clone() { 56090 56091 return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() ); 56092 56093 } 56094 56095} 56096 56097let _id = 0; 56098 56099/** 56100 * A class for managing multiple uniforms in a single group. The renderer will process 56101 * such a definition as a single UBO. 56102 * 56103 * Since this class can only be used in context of {@link ShaderMaterial}, it is only supported 56104 * in {@link WebGLRenderer}. 56105 * 56106 * @augments EventDispatcher 56107 */ 56108class UniformsGroup extends EventDispatcher { 56109 56110 /** 56111 * Constructs a new uniforms group. 56112 */ 56113 constructor() { 56114 56115 super(); 56116 56117 /** 56118 * This flag can be used for type testing. 56119 * 56120 * @type {boolean} 56121 * @readonly 56122 * @default true 56123 */ 56124 this.isUniformsGroup = true; 56125 56126 /** 56127 * The ID of the 3D object. 56128 * 56129 * @name UniformsGroup#id 56130 * @type {number} 56131 * @readonly 56132 */ 56133 Object.defineProperty( this, 'id', { value: _id ++ } ); 56134 56135 /** 56136 * The name of the uniforms group. 56137 * 56138 * @type {string} 56139 */ 56140 this.name = ''; 56141 56142 /** 56143 * The buffer usage. 56144 * 56145 * @type {(StaticDrawUsage|DynamicDrawUsage|StreamDrawUsage|StaticReadUsage|DynamicReadUsage|StreamReadUsage|StaticCopyUsage|DynamicCopyUsage|StreamCopyUsage)} 56146 * @default StaticDrawUsage 56147 */ 56148 this.usage = StaticDrawUsage; 56149 56150 /** 56151 * An array holding the uniforms. 56152 * 56153 * @type {Array<Uniform>} 56154 */ 56155 this.uniforms = []; 56156 56157 } 56158 56159 /** 56160 * Adds the given uniform to this uniforms group. 56161 * 56162 * @param {Uniform} uniform - The uniform to add. 56163 * @return {UniformsGroup} A reference to this uniforms group. 56164 */ 56165 add( uniform ) { 56166 56167 this.uniforms.push( uniform ); 56168 56169 return this; 56170 56171 } 56172 56173 /** 56174 * Removes the given uniform from this uniforms group. 56175 * 56176 * @param {Uniform} uniform - The uniform to remove. 56177 * @return {UniformsGroup} A reference to this uniforms group. 56178 */ 56179 remove( uniform ) { 56180 56181 const index = this.uniforms.indexOf( uniform ); 56182 56183 if ( index !== -1 ) this.uniforms.splice( index, 1 ); 56184 56185 return this; 56186 56187 } 56188 56189 /** 56190 * Sets the name of this uniforms group. 56191 * 56192 * @param {string} name - The name to set. 56193 * @return {UniformsGroup} A reference to this uniforms group. 56194 */ 56195 setName( name ) { 56196 56197 this.name = name; 56198 56199 return this; 56200 56201 } 56202 56203 /** 56204 * Sets the usage of this uniforms group. 56205 * 56206 * @param {(StaticDrawUsage|DynamicDrawUsage|StreamDrawUsage|StaticReadUsage|DynamicReadUsage|StreamReadUsage|StaticCopyUsage|DynamicCopyUsage|StreamCopyUsage)} value - The usage to set. 56207 * @return {UniformsGroup} A reference to this uniforms group. 56208 */ 56209 setUsage( value ) { 56210 56211 this.usage = value; 56212 56213 return this; 56214 56215 } 56216 56217 /** 56218 * Frees the GPU-related resources allocated by this instance. Call this 56219 * method whenever this instance is no longer used in your app. 56220 * 56221 * @fires Texture#dispose 56222 */ 56223 dispose() { 56224 56225 this.dispatchEvent( { type: 'dispose' } ); 56226 56227 } 56228 56229 /** 56230 * Copies the values of the given uniforms group to this instance. 56231 * 56232 * @param {UniformsGroup} source - The uniforms group to copy. 56233 * @return {UniformsGroup} A reference to this uniforms group. 56234 */ 56235 copy( source ) { 56236 56237 this.name = source.name; 56238 this.usage = source.usage; 56239 56240 const uniformsSource = source.uniforms; 56241 56242 this.uniforms.length = 0; 56243 56244 for ( let i = 0, l = uniformsSource.length; i < l; i ++ ) { 56245 56246 const uniforms = Array.isArray( uniformsSource[ i ] ) ? uniformsSource[ i ] : [ uniformsSource[ i ] ]; 56247 56248 for ( let j = 0; j < uniforms.length; j ++ ) { 56249 56250 this.uniforms.push( uniforms[ j ].clone() ); 56251 56252 } 56253 56254 } 56255 56256 return this; 56257 56258 } 56259 56260 /** 56261 * Returns a new uniforms group with copied values from this instance. 56262 * 56263 * @return {UniformsGroup} A clone of this instance. 56264 */ 56265 clone() { 56266 56267 return new this.constructor().copy( this ); 56268 56269 } 56270 56271} 56272 56273/** 56274 * An instanced version of an interleaved buffer. 56275 * 56276 * @augments InterleavedBuffer 56277 */ 56278class InstancedInterleavedBuffer extends InterleavedBuffer { 56279 56280 /** 56281 * Constructs a new instanced interleaved buffer. 56282 * 56283 * @param {TypedArray} array - A typed array with a shared buffer storing attribute data. 56284 * @param {number} stride - The number of typed-array elements per vertex. 56285 * @param {number} [meshPerAttribute=1] - Defines how often a value of this interleaved buffer should be repeated. 56286 */ 56287 constructor( array, stride, meshPerAttribute = 1 ) { 56288 56289 super( array, stride ); 56290 56291 /** 56292 * This flag can be used for type testing. 56293 * 56294 * @type {boolean} 56295 * @readonly 56296 * @default true 56297 */ 56298 this.isInstancedInterleavedBuffer = true; 56299 56300 /** 56301 * Defines how often a value of this buffer attribute should be repeated, 56302 * see {@link InstancedBufferAttribute#meshPerAttribute}. 56303 * 56304 * @type {number} 56305 * @default 1 56306 */ 56307 this.meshPerAttribute = meshPerAttribute; 56308 56309 } 56310 56311 copy( source ) { 56312 56313 super.copy( source ); 56314 56315 this.meshPerAttribute = source.meshPerAttribute; 56316 56317 return this; 56318 56319 } 56320 56321 clone( data ) { 56322 56323 const ib = super.clone( data ); 56324 56325 ib.meshPerAttribute = this.meshPerAttribute; 56326 56327 return ib; 56328 56329 } 56330 56331 toJSON( data ) { 56332 56333 const json = super.toJSON( data ); 56334 56335 json.isInstancedInterleavedBuffer = true; 56336 json.meshPerAttribute = this.meshPerAttribute; 56337 56338 return json; 56339 56340 } 56341 56342} 56343 56344/** 56345 * An alternative version of a buffer attribute with more control over the VBO. 56346 * 56347 * The renderer does not construct a VBO for this kind of attribute. Instead, it uses 56348 * whatever VBO is passed in constructor and can later be altered via the `buffer` property. 56349 * 56350 * The most common use case for this class is when some kind of GPGPU calculation interferes 56351 * or even produces the VBOs in question. 56352 * 56353 * Notice that this class can only be used with {@link WebGLRenderer}. 56354 */ 56355class GLBufferAttribute { 56356 56357 /** 56358 * Constructs a new GL buffer attribute. 56359 * 56360 * @param {WebGLBuffer} buffer - The native WebGL buffer. 56361 * @param {number} type - The native data type (e.g. `gl.FLOAT`). 56362 * @param {number} itemSize - The item size. 56363 * @param {number} elementSize - The corresponding size (in bytes) for the given `type` parameter. 56364 * @param {number} count - The expected number of vertices in VBO. 56365 * @param {boolean} [normalized=false] - Whether the data are normalized or not. 56366 */ 56367 constructor( buffer, type, itemSize, elementSize, count, normalized = false ) { 56368 56369 /** 56370 * This flag can be used for type testing. 56371 * 56372 * @type {boolean} 56373 * @readonly 56374 * @default true 56375 */ 56376 this.isGLBufferAttribute = true; 56377 56378 /** 56379 * The name of the buffer attribute. 56380 * 56381 * @type {string} 56382 */ 56383 this.name = ''; 56384 56385 /** 56386 * The native WebGL buffer. 56387 * 56388 * @type {WebGLBuffer} 56389 */ 56390 this.buffer = buffer; 56391 56392 /** 56393 * The native data type. 56394 * 56395 * @type {number} 56396 */ 56397 this.type = type; 56398 56399 /** 56400 * The item size, see {@link BufferAttribute#itemSize}. 56401 * 56402 * @type {number} 56403 */ 56404 this.itemSize = itemSize; 56405 56406 /** 56407 * The corresponding size (in bytes) for the given `type` parameter. 56408 * 56409 * @type {number} 56410 */ 56411 this.elementSize = elementSize; 56412 56413 /** 56414 * The expected number of vertices in VBO. 56415 * 56416 * @type {number} 56417 */ 56418 this.count = count; 56419 56420 /** 56421 * Applies to integer data only. Indicates how the underlying data in the buffer maps to 56422 * the values in the GLSL code. For instance, if `buffer` contains data of `gl.UNSIGNED_SHORT`, 56423 * and `normalized` is `true`, the values `0 - +65535` in the buffer data will be mapped to 56424 * `0.0f - +1.0f` in the GLSL attribute. If `normalized` is `false`, the values will be converted 56425 * to floats unmodified, i.e. `65535` becomes `65535.0f`. 56426 * 56427 * @type {boolean} 56428 */ 56429 this.normalized = normalized; 56430 56431 /** 56432 * A version number, incremented every time the `needsUpdate` is set to `true`. 56433 * 56434 * @type {number} 56435 */ 56436 this.version = 0; 56437 56438 } 56439 56440 /** 56441 * Flag to indicate that this attribute has changed and should be re-sent to 56442 * the GPU. Set this to `true` when you modify the value of the array. 56443 * 56444 * @type {number} 56445 * @default false 56446 * @param {boolean} value 56447 */ 56448 set needsUpdate( value ) { 56449 56450 if ( value === true ) this.version ++; 56451 56452 } 56453 56454 /** 56455 * Sets the given native WebGL buffer. 56456 * 56457 * @param {WebGLBuffer} buffer - The buffer to set. 56458 * @return {BufferAttribute} A reference to this instance. 56459 */ 56460 setBuffer( buffer ) { 56461 56462 this.buffer = buffer; 56463 56464 return this; 56465 56466 } 56467 56468 /** 56469 * Sets the given native data type and element size. 56470 * 56471 * @param {number} type - The native data type (e.g. `gl.FLOAT`). 56472 * @param {number} elementSize - The corresponding size (in bytes) for the given `type` parameter. 56473 * @return {BufferAttribute} A reference to this instance. 56474 */ 56475 setType( type, elementSize ) { 56476 56477 this.type = type; 56478 this.elementSize = elementSize; 56479 56480 return this; 56481 56482 } 56483 56484 /** 56485 * Sets the item size. 56486 * 56487 * @param {number} itemSize - The item size. 56488 * @return {BufferAttribute} A reference to this instance. 56489 */ 56490 setItemSize( itemSize ) { 56491 56492 this.itemSize = itemSize; 56493 56494 return this; 56495 56496 } 56497 56498 /** 56499 * Sets the count (the expected number of vertices in VBO). 56500 * 56501 * @param {number} count - The count. 56502 * @return {BufferAttribute} A reference to this instance. 56503 */ 56504 setCount( count ) { 56505 56506 this.count = count; 56507 56508 return this; 56509 56510 } 56511 56512} 56513 56514const _matrix = /*@__PURE__*/ new Matrix4(); 56515 56516/** 56517 * This class is designed to assist with raycasting. Raycasting is used for 56518 * mouse picking (working out what objects in the 3d space the mouse is over) 56519 * amongst other things. 56520 */ 56521class Raycaster { 56522 56523 /** 56524 * Constructs a new raycaster. 56525 * 56526 * @param {Vector3} origin - The origin vector where the ray casts from. 56527 * @param {Vector3} direction - The (normalized) direction vector that gives direction to the ray. 56528 * @param {number} [near=0] - All results returned are further away than near. Near can't be negative. 56529 * @param {number} [far=Infinity] - All results returned are closer than far. Far can't be lower than near. 56530 */ 56531 constructor( origin, direction, near = 0, far = Infinity ) { 56532 56533 /** 56534 * The ray used for raycasting. 56535 * 56536 * @type {Ray} 56537 */ 56538 this.ray = new Ray( origin, direction ); 56539 56540 /** 56541 * All results returned are further away than near. Near can't be negative. 56542 * 56543 * @type {number} 56544 * @default 0 56545 */ 56546 this.near = near; 56547 56548 /** 56549 * All results returned are closer than far. Far can't be lower than near. 56550 * 56551 * @type {number} 56552 * @default Infinity 56553 */ 56554 this.far = far; 56555 56556 /** 56557 * The camera to use when raycasting against view-dependent objects such as 56558 * billboarded objects like sprites. This field can be set manually or 56559 * is set when calling `setFromCamera()`. 56560 * 56561 * @type {?Camera} 56562 * @default null 56563 */ 56564 this.camera = null; 56565 56566 /** 56567 * Allows to selectively ignore 3D objects when performing intersection tests. 56568 * The following code example ensures that only 3D objects on layer `1` will be 56569 * honored by raycaster. 56570 * ```js 56571 * raycaster.layers.set( 1 ); 56572 * object.layers.enable( 1 ); 56573 * ``` 56574 * 56575 * @type {Layers} 56576 */ 56577 this.layers = new Layers(); 56578 56579 56580 /** 56581 * A parameter object that configures the raycasting. It has the structure: 56582 * 56583 * ``` 56584 * { 56585 * Mesh: {}, 56586 * Line: { threshold: 1 }, 56587 * LOD: {}, 56588 * Points: { threshold: 1 }, 56589 * Sprite: {} 56590 * } 56591 * ``` 56592 * Where `threshold` is the precision of the raycaster when intersecting objects, in world units. 56593 * 56594 * @type {Object} 56595 */ 56596 this.params = { 56597 Mesh: {}, 56598 Line: { threshold: 1 }, 56599 LOD: {}, 56600 Points: { threshold: 1 }, 56601 Sprite: {} 56602 }; 56603 56604 } 56605 56606 /** 56607 * Updates the ray with a new origin and direction by copying the values from the arguments. 56608 * 56609 * @param {Vector3} origin - The origin vector where the ray casts from. 56610 * @param {Vector3} direction - The (normalized) direction vector that gives direction to the ray. 56611 */ 56612 set( origin, direction ) { 56613 56614 // direction is assumed to be normalized (for accurate distance calculations) 56615 56616 this.ray.set( origin, direction ); 56617 56618 } 56619 56620 /** 56621 * Uses the given coordinates and camera to compute a new origin and direction for the internal ray. 56622 * 56623 * @param {Vector2} coords - 2D coordinates of the mouse, in normalized device coordinates (NDC). 56624 * X and Y components should be between `-1` and `1`. 56625 * @param {Camera} camera - The camera from which the ray should originate. 56626 */ 56627 setFromCamera( coords, camera ) { 56628 56629 if ( camera.isPerspectiveCamera ) { 56630 56631 this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); 56632 this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); 56633 this.camera = camera; 56634 56635 } else if ( camera.isOrthographicCamera ) { 56636 56637 this.ray.origin.set( coords.x, coords.y, camera.projectionMatrix.elements[ 14 ] ).unproject( camera ); // set origin in plane of camera 56638 this.ray.direction.set( 0, 0, -1 ).transformDirection( camera.matrixWorld ); 56639 this.camera = camera; 56640 56641 } else { 56642 56643 error( 'Raycaster: Unsupported camera type: ' + camera.type ); 56644 56645 } 56646 56647 } 56648 56649 /** 56650 * Uses the given WebXR controller to compute a new origin and direction for the internal ray. 56651 * 56652 * @param {WebXRController} controller - The controller to copy the position and direction from. 56653 * @return {Raycaster} A reference to this raycaster. 56654 */ 56655 setFromXRController( controller ) { 56656 56657 _matrix.identity().extractRotation( controller.matrixWorld ); 56658 56659 this.ray.origin.setFromMatrixPosition( controller.matrixWorld ); 56660 this.ray.direction.set( 0, 0, -1 ).applyMatrix4( _matrix ); 56661 56662 return this; 56663 56664 } 56665 56666 /** 56667 * The intersection point of a raycaster intersection test. 56668 * @typedef {Object} Raycaster~Intersection 56669 * @property {number} distance - The distance from the ray's origin to the intersection point. 56670 * @property {number} distanceToRay - Some 3D objects e.g. {@link Points} provide the distance of the 56671 * intersection to the nearest point on the ray. For other objects it will be `undefined`. 56672 * @property {Vector3} point - The intersection point, in world coordinates. 56673 * @property {Object} face - The face that has been intersected. 56674 * @property {number} faceIndex - The face index. 56675 * @property {Object3D} object - The 3D object that has been intersected. 56676 * @property {Vector2} uv - U,V coordinates at point of intersection. 56677 * @property {Vector2} uv1 - Second set of U,V coordinates at point of intersection. 56678 * @property {Vector3} normal - Interpolated normal vector at point of intersection. 56679 * @property {number} instanceId - The index number of the instance where the ray 56680 * intersects the {@link InstancedMesh}. 56681 */ 56682 56683 /** 56684 * Checks all intersection between the ray and the object with or without the 56685 * descendants. Intersections are returned sorted by distance, closest first. 56686 * 56687 * `Raycaster` delegates to the `raycast()` method of the passed 3D object, when 56688 * evaluating whether the ray intersects the object or not. This allows meshes to respond 56689 * differently to ray casting than lines or points. 56690 * 56691 * Note that for meshes, faces must be pointed towards the origin of the ray in order 56692 * to be detected; intersections of the ray passing through the back of a face will not 56693 * be detected. To raycast against both faces of an object, you'll want to set {@link Material#side} 56694 * to `THREE.DoubleSide`. 56695 * 56696 * Note that a ray hitting a triangle mesh exactly along an edge shared by two faces may be 56697 * reported by both faces, resulting in two coincident intersections (identical point and 56698 * distance) in the returned array. 56699 * 56700 * @param {Object3D} object - The 3D object to check for intersection with the ray. 56701 * @param {boolean} [recursive=true] - If set to `true`, it also checks all descendants. 56702 * Otherwise it only checks intersection with the object. 56703 * @param {Array<Raycaster~Intersection>} [intersects=[]] The target array that holds the result of the method. 56704 * @return {Array<Raycaster~Intersection>} An array holding the intersection points. 56705 */ 56706 intersectObject( object, recursive = true, intersects = [] ) { 56707 56708 intersect( object, this, intersects, recursive ); 56709 56710 intersects.sort( ascSort ); 56711 56712 return intersects; 56713 56714 } 56715 56716 /** 56717 * Checks all intersection between the ray and the objects with or without 56718 * the descendants. Intersections are returned sorted by distance, closest first. 56719 * 56720 * @param {Array<Object3D>} objects - The 3D objects to check for intersection with the ray. 56721 * @param {boolean} [recursive=true] - If set to `true`, it also checks all descendants. 56722 * Otherwise it only checks intersection with the object. 56723 * @param {Array<Raycaster~Intersection>} [intersects=[]] The target array that holds the result of the method. 56724 * @return {Array<Raycaster~Intersection>} An array holding the intersection points. 56725 */ 56726 intersectObjects( objects, recursive = true, intersects = [] ) { 56727 56728 for ( let i = 0, l = objects.length; i < l; i ++ ) { 56729 56730 intersect( objects[ i ], this, intersects, recursive ); 56731 56732 } 56733 56734 intersects.sort( ascSort ); 56735 56736 return intersects; 56737 56738 } 56739 56740} 56741 56742function ascSort( a, b ) { 56743 56744 return a.distance - b.distance; 56745 56746} 56747 56748function intersect( object, raycaster, intersects, recursive ) { 56749 56750 let propagate = true; 56751 56752 if ( object.layers.test( raycaster.layers ) ) { 56753 56754 const result = object.raycast( raycaster, intersects ); 56755 56756 if ( result === false ) propagate = false; 56757 56758 } 56759 56760 if ( propagate === true && recursive === true ) { 56761 56762 const children = object.children; 56763 56764 for ( let i = 0, l = children.length; i < l; i ++ ) { 56765 56766 intersect( children[ i ], raycaster, intersects, true ); 56767 56768 } 56769 56770 } 56771 56772} 56773 56774/** 56775 * Class for keeping track of time. 56776 * 56777 * @deprecated since r183. 56778 */ 56779class Clock { 56780 56781 /** 56782 * Constructs a new clock. 56783 * 56784 * @deprecated since 183. 56785 * @param {boolean} [autoStart=true] - Whether to automatically start the clock when 56786 * `getDelta()` is called for the first time. 56787 */ 56788 constructor( autoStart = true ) { 56789 56790 /** 56791 * If set to `true`, the clock starts automatically when `getDelta()` is called 56792 * for the first time. 56793 * 56794 * @type {boolean} 56795 * @default true 56796 */ 56797 this.autoStart = autoStart; 56798 56799 /** 56800 * Holds the time at which the clock's `start()` method was last called. 56801 * 56802 * @type {number} 56803 * @default 0 56804 */ 56805 this.startTime = 0; 56806 56807 /** 56808 * Holds the time at which the clock's `start()`, `getElapsedTime()` or 56809 * `getDelta()` methods were last called. 56810 * 56811 * @type {number} 56812 * @default 0 56813 */ 56814 this.oldTime = 0; 56815 56816 /** 56817 * Keeps track of the total time that the clock has been running. 56818 * 56819 * @type {number} 56820 * @default 0 56821 */ 56822 this.elapsedTime = 0; 56823 56824 /** 56825 * Whether the clock is running or not. 56826 * 56827 * @type {boolean} 56828 * @default true 56829 */ 56830 this.running = false; 56831 56832 warn( 'Clock: This module has been deprecated. Please use THREE.Timer instead.' ); // @deprecated, r183 56833 56834 } 56835 56836 /** 56837 * Starts the clock. When `autoStart` is set to `true`, the method is automatically 56838 * called by the class. 56839 */ 56840 start() { 56841 56842 this.startTime = performance.now(); 56843 56844 this.oldTime = this.startTime; 56845 this.elapsedTime = 0; 56846 this.running = true; 56847 56848 } 56849 56850 /** 56851 * Stops the clock. 56852 */ 56853 stop() { 56854 56855 this.getElapsedTime(); 56856 this.running = false; 56857 this.autoStart = false; 56858 56859 } 56860 56861 /** 56862 * Returns the elapsed time in seconds. 56863 * 56864 * @return {number} The elapsed time. 56865 */ 56866 getElapsedTime() { 56867 56868 this.getDelta(); 56869 return this.elapsedTime; 56870 56871 } 56872 56873 /** 56874 * Returns the delta time in seconds. 56875 * 56876 * @return {number} The delta time. 56877 */ 56878 getDelta() { 56879 56880 let diff = 0; 56881 56882 if ( this.autoStart && ! this.running ) { 56883 56884 this.start(); 56885 return 0; 56886 56887 } 56888 56889 if ( this.running ) { 56890 56891 const newTime = performance.now(); 56892 56893 diff = ( newTime - this.oldTime ) / 1000; 56894 this.oldTime = newTime; 56895 56896 this.elapsedTime += diff; 56897 56898 } 56899 56900 return diff; 56901 56902 } 56903 56904} 56905 56906/** 56907 * This class can be used to represent points in 3D space as 56908 * [Spherical coordinates](https://en.wikipedia.org/wiki/Spherical_coordinate_system). 56909 */ 56910class Spherical { 56911 56912 /** 56913 * Constructs a new spherical. 56914 * 56915 * @param {number} [radius=1] - The radius, or the Euclidean distance (straight-line distance) from the point to the origin. 56916 * @param {number} [phi=0] - The polar angle in radians from the y (up) axis. 56917 * @param {number} [theta=0] - The equator/azimuthal angle in radians around the y (up) axis. 56918 */ 56919 constructor( radius = 1, phi = 0, theta = 0 ) { 56920 56921 /** 56922 * The radius, or the Euclidean distance (straight-line distance) from the point to the origin. 56923 * 56924 * @type {number} 56925 * @default 1 56926 */ 56927 this.radius = radius; 56928 56929 /** 56930 * The polar angle in radians from the y (up) axis. 56931 * 56932 * @type {number} 56933 * @default 0 56934 */ 56935 this.phi = phi; 56936 56937 /** 56938 * The equator/azimuthal angle in radians around the y (up) axis. 56939 * 56940 * @type {number} 56941 * @default 0 56942 */ 56943 this.theta = theta; 56944 56945 } 56946 56947 /** 56948 * Sets the spherical components by copying the given values. 56949 * 56950 * @param {number} radius - The radius. 56951 * @param {number} phi - The polar angle. 56952 * @param {number} theta - The azimuthal angle. 56953 * @return {Spherical} A reference to this spherical. 56954 */ 56955 set( radius, phi, theta ) { 56956 56957 this.radius = radius; 56958 this.phi = phi; 56959 this.theta = theta; 56960 56961 return this; 56962 56963 } 56964 56965 /** 56966 * Copies the values of the given spherical to this instance. 56967 * 56968 * @param {Spherical} other - The spherical to copy. 56969 * @return {Spherical} A reference to this spherical. 56970 */ 56971 copy( other ) { 56972 56973 this.radius = other.radius; 56974 this.phi = other.phi; 56975 this.theta = other.theta; 56976 56977 return this; 56978 56979 } 56980 56981 /** 56982 * Restricts the polar angle [page:.phi phi] to be between `0.000001` and pi - 56983 * `0.000001`. 56984 * 56985 * @return {Spherical} A reference to this spherical. 56986 */ 56987 makeSafe() { 56988 56989 const EPS = 0.000001; 56990 this.phi = clamp( this.phi, EPS, Math.PI - EPS ); 56991 56992 return this; 56993 56994 } 56995 56996 /** 56997 * Sets the spherical components from the given vector which is assumed to hold 56998 * Cartesian coordinates. 56999 * 57000 * @param {Vector3} v - The vector to set. 57001 * @return {Spherical} A reference to this spherical. 57002 */ 57003 setFromVector3( v ) { 57004 57005 return this.setFromCartesianCoords( v.x, v.y, v.z ); 57006 57007 } 57008 57009 /** 57010 * Sets the spherical components from the given Cartesian coordinates. 57011 * 57012 * @param {number} x - The x value. 57013 * @param {number} y - The y value. 57014 * @param {number} z - The z value. 57015 * @return {Spherical} A reference to this spherical. 57016 */ 57017 setFromCartesianCoords( x, y, z ) { 57018 57019 this.radius = Math.sqrt( x * x + y * y + z * z ); 57020 57021 if ( this.radius === 0 ) { 57022 57023 this.theta = 0; 57024 this.phi = 0; 57025 57026 } else { 57027 57028 this.theta = Math.atan2( x, z ); 57029 this.phi = Math.acos( clamp( y / this.radius, -1, 1 ) ); 57030 57031 } 57032 57033 return this; 57034 57035 } 57036 57037 /** 57038 * Returns a new spherical with copied values from this instance. 57039 * 57040 * @return {Spherical} A clone of this instance. 57041 */ 57042 clone() { 57043 57044 return new this.constructor().copy( this ); 57045 57046 } 57047 57048} 57049 57050/** 57051 * This class can be used to represent points in 3D space as 57052 * [Cylindrical coordinates](https://en.wikipedia.org/wiki/Cylindrical_coordinate_system). 57053 */ 57054class Cylindrical { 57055 57056 /** 57057 * Constructs a new cylindrical. 57058 * 57059 * @param {number} [radius=1] - The distance from the origin to a point in the x-z plane. 57060 * @param {number} [theta=0] - A counterclockwise angle in the x-z plane measured in radians from the positive z-axis. 57061 * @param {number} [y=0] - The height above the x-z plane. 57062 */ 57063 constructor( radius = 1, theta = 0, y = 0 ) { 57064 57065 /** 57066 * The distance from the origin to a point in the x-z plane. 57067 * 57068 * @type {number} 57069 * @default 1 57070 */ 57071 this.radius = radius; 57072 57073 /** 57074 * A counterclockwise angle in the x-z plane measured in radians from the positive z-axis. 57075 * 57076 * @type {number} 57077 * @default 0 57078 */ 57079 this.theta = theta; 57080 57081 /** 57082 * The height above the x-z plane. 57083 * 57084 * @type {number} 57085 * @default 0 57086 */ 57087 this.y = y; 57088 57089 } 57090 57091 /** 57092 * Sets the cylindrical components by copying the given values. 57093 * 57094 * @param {number} radius - The radius. 57095 * @param {number} theta - The theta angle. 57096 * @param {number} y - The height value. 57097 * @return {Cylindrical} A reference to this cylindrical. 57098 */ 57099 set( radius, theta, y ) { 57100 57101 this.radius = radius; 57102 this.theta = theta; 57103 this.y = y; 57104 57105 return this; 57106 57107 } 57108 57109 /** 57110 * Copies the values of the given cylindrical to this instance. 57111 * 57112 * @param {Cylindrical} other - The cylindrical to copy. 57113 * @return {Cylindrical} A reference to this cylindrical. 57114 */ 57115 copy( other ) { 57116 57117 this.radius = other.radius; 57118 this.theta = other.theta; 57119 this.y = other.y; 57120 57121 return this; 57122 57123 } 57124 57125 /** 57126 * Sets the cylindrical components from the given vector which is assumed to hold 57127 * Cartesian coordinates. 57128 * 57129 * @param {Vector3} v - The vector to set. 57130 * @return {Cylindrical} A reference to this cylindrical. 57131 */ 57132 setFromVector3( v ) { 57133 57134 return this.setFromCartesianCoords( v.x, v.y, v.z ); 57135 57136 } 57137 57138 /** 57139 * Sets the cylindrical components from the given Cartesian coordinates. 57140 * 57141 * @param {number} x - The x value. 57142 * @param {number} y - The x value. 57143 * @param {number} z - The x value. 57144 * @return {Cylindrical} A reference to this cylindrical. 57145 */ 57146 setFromCartesianCoords( x, y, z ) { 57147 57148 this.radius = Math.sqrt( x * x + z * z ); 57149 this.theta = Math.atan2( x, z ); 57150 this.y = y; 57151 57152 return this; 57153 57154 } 57155 57156 /** 57157 * Returns a new cylindrical with copied values from this instance. 57158 * 57159 * @return {Cylindrical} A clone of this instance. 57160 */ 57161 clone() { 57162 57163 return new this.constructor().copy( this ); 57164 57165 } 57166 57167} 57168 57169/** 57170 * Represents a 2x2 matrix. 57171 * 57172 * A Note on Row-Major and Column-Major Ordering: 57173 * 57174 * The constructor and {@link Matrix2#set} method take arguments in 57175 * [row-major](https://en.wikipedia.org/wiki/Row-_and_column-major_order#Column-major_order) 57176 * order, while internally they are stored in the {@link Matrix2#elements} array in column-major order. 57177 * This means that calling: 57178 * ```js 57179 * const m = new THREE.Matrix2(); 57180 * m.set( 11, 12, 57181 * 21, 22 ); 57182 * ``` 57183 * will result in the elements array containing: 57184 * ```js 57185 * m.elements = [ 11, 21, 57186 * 12, 22 ]; 57187 * ``` 57188 * and internally all calculations are performed using column-major ordering. 57189 * However, as the actual ordering makes no difference mathematically and 57190 * most people are used to thinking about matrices in row-major order, the 57191 * three.js documentation shows matrices in row-major order. Just bear in 57192 * mind that if you are reading the source code, you'll have to take the 57193 * transpose of any matrices outlined here to make sense of the calculations. 57194 */ 57195class Matrix2 { 57196 57197 static { 57198 57199 /** 57200 * This flag can be used for type testing. 57201 * 57202 * @type {boolean} 57203 * @readonly 57204 * @default true 57205 */ 57206 Matrix2.prototype.isMatrix2 = true; 57207 57208 } 57209 57210 /** 57211 * Constructs a new 2x2 matrix. The arguments are supposed to be 57212 * in row-major order. If no arguments are provided, the constructor 57213 * initializes the matrix as an identity matrix. 57214 * 57215 * @param {number} [n11] - 1-1 matrix element. 57216 * @param {number} [n12] - 1-2 matrix element. 57217 * @param {number} [n21] - 2-1 matrix element. 57218 * @param {number} [n22] - 2-2 matrix element. 57219 */ 57220 constructor( n11, n12, n21, n22 ) { 57221 57222 /** 57223 * A column-major list of matrix values. 57224 * 57225 * @type {Array<number>} 57226 */ 57227 this.elements = [ 57228 1, 0, 57229 0, 1, 57230 ]; 57231 57232 if ( n11 !== undefined ) { 57233 57234 this.set( n11, n12, n21, n22 ); 57235 57236 } 57237 57238 } 57239 57240 /** 57241 * Sets this matrix to the 2x2 identity matrix. 57242 * 57243 * @return {Matrix2} A reference to this matrix. 57244 */ 57245 identity() { 57246 57247 this.set( 57248 1, 0, 57249 0, 1, 57250 ); 57251 57252 return this; 57253 57254 } 57255 57256 /** 57257 * Sets the elements of the matrix from the given array. 57258 * 57259 * @param {Array<number>} array - The matrix elements in column-major order. 57260 * @param {number} [offset=0] - Index of the first element in the array. 57261 * @return {Matrix2} A reference to this matrix. 57262 */ 57263 fromArray( array, offset = 0 ) { 57264 57265 for ( let i = 0; i < 4; i ++ ) { 57266 57267 this.elements[ i ] = array[ i + offset ]; 57268 57269 } 57270 57271 return this; 57272 57273 } 57274 57275 /** 57276 * Sets the elements of the matrix.The arguments are supposed to be 57277 * in row-major order. 57278 * 57279 * @param {number} n11 - 1-1 matrix element. 57280 * @param {number} n12 - 1-2 matrix element. 57281 * @param {number} n21 - 2-1 matrix element. 57282 * @param {number} n22 - 2-2 matrix element. 57283 * @return {Matrix2} A reference to this matrix. 57284 */ 57285 set( n11, n12, n21, n22 ) { 57286 57287 const te = this.elements; 57288 57289 te[ 0 ] = n11; te[ 2 ] = n12; 57290 te[ 1 ] = n21; te[ 3 ] = n22; 57291 57292 return this; 57293 57294 } 57295 57296} 57297 57298const _vector$4 = /*@__PURE__*/ new Vector2(); 57299 57300/** 57301 * Represents an axis-aligned bounding box (AABB) in 2D space. 57302 */ 57303class Box2 { 57304 57305 /** 57306 * Constructs a new bounding box. 57307 * 57308 * @param {Vector2} [min=(Infinity,Infinity)] - A vector representing the lower boundary of the box. 57309 * @param {Vector2} [max=(-Infinity,-Infinity)] - A vector representing the upper boundary of the box. 57310 */ 57311 constructor( min = new Vector2( + Infinity, + Infinity ), max = new Vector2( - Infinity, - Infinity ) ) { 57312 57313 /** 57314 * This flag can be used for type testing. 57315 * 57316 * @type {boolean} 57317 * @readonly 57318 * @default true 57319 */ 57320 this.isBox2 = true; 57321 57322 /** 57323 * The lower boundary of the box. 57324 * 57325 * @type {Vector2} 57326 */ 57327 this.min = min; 57328 57329 /** 57330 * The upper boundary of the box. 57331 * 57332 * @type {Vector2} 57333 */ 57334 this.max = max; 57335 57336 } 57337 57338 /** 57339 * Sets the lower and upper boundaries of this box. 57340 * Please note that this method only copies the values from the given objects. 57341 * 57342 * @param {Vector2} min - The lower boundary of the box. 57343 * @param {Vector2} max - The upper boundary of the box. 57344 * @return {Box2} A reference to this bounding box. 57345 */ 57346 set( min, max ) { 57347 57348 this.min.copy( min ); 57349 this.max.copy( max ); 57350 57351 return this; 57352 57353 } 57354 57355 /** 57356 * Sets the upper and lower bounds of this box so it encloses the position data 57357 * in the given array. 57358 * 57359 * @param {Array<Vector2>} points - An array holding 2D position data as instances of {@link Vector2}. 57360 * @return {Box2} A reference to this bounding box. 57361 */ 57362 setFromPoints( points ) { 57363 57364 this.makeEmpty(); 57365 57366 for ( let i = 0, il = points.length; i < il; i ++ ) { 57367 57368 this.expandByPoint( points[ i ] ); 57369 57370 } 57371 57372 return this; 57373 57374 } 57375 57376 /** 57377 * Centers this box on the given center vector and sets this box's width, height and 57378 * depth to the given size values. 57379 * 57380 * @param {Vector2} center - The center of the box. 57381 * @param {Vector2} size - The x and y dimensions of the box. 57382 * @return {Box2} A reference to this bounding box. 57383 */ 57384 setFromCenterAndSize( center, size ) { 57385 57386 const halfSize = _vector$4.copy( size ).multiplyScalar( 0.5 ); 57387 this.min.copy( center ).sub( halfSize ); 57388 this.max.copy( center ).add( halfSize ); 57389 57390 return this; 57391 57392 } 57393 57394 /** 57395 * Returns a new box with copied values from this instance. 57396 * 57397 * @return {Box2} A clone of this instance. 57398 */ 57399 clone() { 57400 57401 return new this.constructor().copy( this ); 57402 57403 } 57404 57405 /** 57406 * Copies the values of the given box to this instance. 57407 * 57408 * @param {Box2} box - The box to copy. 57409 * @return {Box2} A reference to this bounding box. 57410 */ 57411 copy( box ) { 57412 57413 this.min.copy( box.min ); 57414 this.max.copy( box.max ); 57415 57416 return this; 57417 57418 } 57419 57420 /** 57421 * Makes this box empty which means in encloses a zero space in 2D. 57422 * 57423 * @return {Box2} A reference to this bounding box. 57424 */ 57425 makeEmpty() { 57426 57427 this.min.x = this.min.y = + Infinity; 57428 this.max.x = this.max.y = - Infinity; 57429 57430 return this; 57431 57432 } 57433 57434 /** 57435 * Returns true if this box includes zero points within its bounds. 57436 * Note that a box with equal lower and upper bounds still includes one 57437 * point, the one both bounds share. 57438 * 57439 * @return {boolean} Whether this box is empty or not. 57440 */ 57441 isEmpty() { 57442 57443 // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes 57444 57445 return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); 57446 57447 } 57448 57449 /** 57450 * Returns the center point of this box. 57451 * 57452 * @param {Vector2} target - The target vector that is used to store the method's result. 57453 * @return {Vector2} The center point. 57454 */ 57455 getCenter( target ) { 57456 57457 return this.isEmpty() ? target.set( 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); 57458 57459 } 57460 57461 /** 57462 * Returns the dimensions of this box. 57463 * 57464 * @param {Vector2} target - The target vector that is used to store the method's result. 57465 * @return {Vector2} The size. 57466 */ 57467 getSize( target ) { 57468 57469 return this.isEmpty() ? target.set( 0, 0 ) : target.subVectors( this.max, this.min ); 57470 57471 } 57472 57473 /** 57474 * Expands the boundaries of this box to include the given point. 57475 * 57476 * @param {Vector2} point - The point that should be included by the bounding box. 57477 * @return {Box2} A reference to this bounding box. 57478 */ 57479 expandByPoint( point ) { 57480 57481 this.min.min( point ); 57482 this.max.max( point ); 57483 57484 return this; 57485 57486 } 57487 57488 /** 57489 * Expands this box equilaterally by the given vector. The width of this 57490 * box will be expanded by the x component of the vector in both 57491 * directions. The height of this box will be expanded by the y component of 57492 * the vector in both directions. 57493 * 57494 * @param {Vector2} vector - The vector that should expand the bounding box. 57495 * @return {Box2} A reference to this bounding box. 57496 */ 57497 expandByVector( vector ) { 57498 57499 this.min.sub( vector ); 57500 this.max.add( vector ); 57501 57502 return this; 57503 57504 } 57505 57506 /** 57507 * Expands each dimension of the box by the given scalar. If negative, the 57508 * dimensions of the box will be contracted. 57509 * 57510 * @param {number} scalar - The scalar value that should expand the bounding box. 57511 * @return {Box2} A reference to this bounding box. 57512 */ 57513 expandByScalar( scalar ) { 57514 57515 this.min.addScalar( - scalar ); 57516 this.max.addScalar( scalar ); 57517 57518 return this; 57519 57520 } 57521 57522 /** 57523 * Returns `true` if the given point lies within or on the boundaries of this box. 57524 * 57525 * @param {Vector2} point - The point to test. 57526 * @return {boolean} Whether the bounding box contains the given point or not. 57527 */ 57528 containsPoint( point ) { 57529 57530 return point.x >= this.min.x && point.x <= this.max.x && 57531 point.y >= this.min.y && point.y <= this.max.y; 57532 57533 } 57534 57535 /** 57536 * Returns `true` if this bounding box includes the entirety of the given bounding box. 57537 * If this box and the given one are identical, this function also returns `true`. 57538 * 57539 * @param {Box2} box - The bounding box to test. 57540 * @return {boolean} Whether the bounding box contains the given bounding box or not. 57541 */ 57542 containsBox( box ) { 57543 57544 return this.min.x <= box.min.x && box.max.x <= this.max.x && 57545 this.min.y <= box.min.y && box.max.y <= this.max.y; 57546 57547 } 57548 57549 /** 57550 * Returns a point as a proportion of this box's width and height. 57551 * 57552 * @param {Vector2} point - A point in 2D space. 57553 * @param {Vector2} target - The target vector that is used to store the method's result. 57554 * @return {Vector2} A point as a proportion of this box's width and height. 57555 */ 57556 getParameter( point, target ) { 57557 57558 // This can potentially have a divide by zero if the box 57559 // has a size dimension of 0. 57560 57561 return target.set( 57562 ( point.x - this.min.x ) / ( this.max.x - this.min.x ), 57563 ( point.y - this.min.y ) / ( this.max.y - this.min.y ) 57564 ); 57565 57566 } 57567 57568 /** 57569 * Returns `true` if the given bounding box intersects with this bounding box. 57570 * 57571 * @param {Box2} box - The bounding box to test. 57572 * @return {boolean} Whether the given bounding box intersects with this bounding box. 57573 */ 57574 intersectsBox( box ) { 57575 57576 // using 4 splitting planes to rule out intersections 57577 57578 return box.max.x >= this.min.x && box.min.x <= this.max.x && 57579 box.max.y >= this.min.y && box.min.y <= this.max.y; 57580 57581 } 57582 57583 /** 57584 * Clamps the given point within the bounds of this box. 57585 * 57586 * @param {Vector2} point - The point to clamp. 57587 * @param {Vector2} target - The target vector that is used to store the method's result. 57588 * @return {Vector2} The clamped point. 57589 */ 57590 clampPoint( point, target ) { 57591 57592 return target.copy( point ).clamp( this.min, this.max ); 57593 57594 } 57595 57596 /** 57597 * Returns the euclidean distance from any edge of this box to the specified point. If 57598 * the given point lies inside of this box, the distance will be `0`. 57599 * 57600 * @param {Vector2} point - The point to compute the distance to. 57601 * @return {number} The euclidean distance. 57602 */ 57603 distanceToPoint( point ) { 57604 57605 return this.clampPoint( point, _vector$4 ).distanceTo( point ); 57606 57607 } 57608 57609 /** 57610 * Computes the intersection of this bounding box and the given one, setting the upper 57611 * bound of this box to the lesser of the two boxes' upper bounds and the 57612 * lower bound of this box to the greater of the two boxes' lower bounds. If 57613 * there's no overlap, makes this box empty. 57614 * 57615 * @param {Box2} box - The bounding box to intersect with. 57616 * @return {Box2} A reference to this bounding box. 57617 */ 57618 intersect( box ) { 57619 57620 this.min.max( box.min ); 57621 this.max.min( box.max ); 57622 57623 if ( this.isEmpty() ) this.makeEmpty(); 57624 57625 return this; 57626 57627 } 57628 57629 /** 57630 * Computes the union of this box and another and the given one, setting the upper 57631 * bound of this box to the greater of the two boxes' upper bounds and the 57632 * lower bound of this box to the lesser of the two boxes' lower bounds. 57633 * 57634 * @param {Box2} box - The bounding box that will be unioned with this instance. 57635 * @return {Box2} A reference to this bounding box. 57636 */ 57637 union( box ) { 57638 57639 this.min.min( box.min ); 57640 this.max.max( box.max ); 57641 57642 return this; 57643 57644 } 57645 57646 /** 57647 * Adds the given offset to both the upper and lower bounds of this bounding box, 57648 * effectively moving it in 2D space. 57649 * 57650 * @param {Vector2} offset - The offset that should be used to translate the bounding box. 57651 * @return {Box2} A reference to this bounding box. 57652 */ 57653 translate( offset ) { 57654 57655 this.min.add( offset ); 57656 this.max.add( offset ); 57657 57658 return this; 57659 57660 } 57661 57662 /** 57663 * Returns `true` if this bounding box is equal with the given one. 57664 * 57665 * @param {Box2} box - The box to test for equality. 57666 * @return {boolean} Whether this bounding box is equal with the given one. 57667 */ 57668 equals( box ) { 57669 57670 return box.min.equals( this.min ) && box.max.equals( this.max ); 57671 57672 } 57673 57674} 57675 57676const _startP = /*@__PURE__*/ new Vector3(); 57677const _startEnd = /*@__PURE__*/ new Vector3(); 57678 57679const _d1 = /*@__PURE__*/ new Vector3(); 57680const _d2 = /*@__PURE__*/ new Vector3(); 57681const _r = /*@__PURE__*/ new Vector3(); 57682const _c1 = /*@__PURE__*/ new Vector3(); 57683const _c2 = /*@__PURE__*/ new Vector3(); 57684 57685/** 57686 * An analytical line segment in 3D space represented by a start and end point. 57687 */ 57688class Line3 { 57689 57690 /** 57691 * Constructs a new line segment. 57692 * 57693 * @param {Vector3} [start=(0,0,0)] - Start of the line segment. 57694 * @param {Vector3} [end=(0,0,0)] - End of the line segment. 57695 */ 57696 constructor( start = new Vector3(), end = new Vector3() ) { 57697 57698 /** 57699 * Start of the line segment. 57700 * 57701 * @type {Vector3} 57702 */ 57703 this.start = start; 57704 57705 /** 57706 * End of the line segment. 57707 * 57708 * @type {Vector3} 57709 */ 57710 this.end = end; 57711 57712 } 57713 57714 /** 57715 * Sets the start and end values by copying the given vectors. 57716 * 57717 * @param {Vector3} start - The start point. 57718 * @param {Vector3} end - The end point. 57719 * @return {Line3} A reference to this line segment. 57720 */ 57721 set( start, end ) { 57722 57723 this.start.copy( start ); 57724 this.end.copy( end ); 57725 57726 return this; 57727 57728 } 57729 57730 /** 57731 * Copies the values of the given line segment to this instance. 57732 * 57733 * @param {Line3} line - The line segment to copy. 57734 * @return {Line3} A reference to this line segment. 57735 */ 57736 copy( line ) { 57737 57738 this.start.copy( line.start ); 57739 this.end.copy( line.end ); 57740 57741 return this; 57742 57743 } 57744 57745 /** 57746 * Returns the center of the line segment. 57747 * 57748 * @param {Vector3} target - The target vector that is used to store the method's result. 57749 * @return {Vector3} The center point. 57750 */ 57751 getCenter( target ) { 57752 57753 return target.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); 57754 57755 } 57756 57757 /** 57758 * Returns the delta vector of the line segment's start and end point. 57759 * 57760 * @param {Vector3} target - The target vector that is used to store the method's result. 57761 * @return {Vector3} The delta vector. 57762 */ 57763 delta( target ) { 57764 57765 return target.subVectors( this.end, this.start ); 57766 57767 } 57768 57769 /** 57770 * Returns the squared Euclidean distance between the line' start and end point. 57771 * 57772 * @return {number} The squared Euclidean distance. 57773 */ 57774 distanceSq() { 57775 57776 return this.start.distanceToSquared( this.end ); 57777 57778 } 57779 57780 /** 57781 * Returns the Euclidean distance between the line' start and end point. 57782 * 57783 * @return {number} The Euclidean distance. 57784 */ 57785 distance() { 57786 57787 return this.start.distanceTo( this.end ); 57788 57789 } 57790 57791 /** 57792 * Returns a vector at a certain position along the line segment. 57793 * 57794 * @param {number} t - A value between `[0,1]` to represent a position along the line segment. 57795 * @param {Vector3} target - The target vector that is used to store the method's result. 57796 * @return {Vector3} The delta vector. 57797 */ 57798 at( t, target ) { 57799 57800 return this.delta( target ).multiplyScalar( t ).add( this.start ); 57801 57802 } 57803 57804 /** 57805 * Returns a point parameter based on the closest point as projected on the line segment. 57806 * 57807 * @param {Vector3} point - The point for which to return a point parameter. 57808 * @param {boolean} clampToLine - Whether to clamp the result to the range `[0,1]` or not. 57809 * @return {number} The point parameter. 57810 */ 57811 closestPointToPointParameter( point, clampToLine ) { 57812 57813 _startP.subVectors( point, this.start ); 57814 _startEnd.subVectors( this.end, this.start ); 57815 57816 const startEnd2 = _startEnd.dot( _startEnd ); 57817 57818 if ( startEnd2 === 0 ) return 0; 57819 57820 const startEnd_startP = _startEnd.dot( _startP ); 57821 57822 let t = startEnd_startP / startEnd2; 57823 57824 if ( clampToLine ) { 57825 57826 t = clamp( t, 0, 1 ); 57827 57828 } 57829 57830 return t; 57831 57832 } 57833 57834 /** 57835 * Returns the closest point on the line for a given point. 57836 * 57837 * @param {Vector3} point - The point to compute the closest point on the line for. 57838 * @param {boolean} clampToLine - Whether to clamp the result to the range `[0,1]` or not. 57839 * @param {Vector3} target - The target vector that is used to store the method's result. 57840 * @return {Vector3} The closest point on the line. 57841 */ 57842 closestPointToPoint( point, clampToLine, target ) { 57843 57844 const t = this.closestPointToPointParameter( point, clampToLine ); 57845 57846 return this.delta( target ).multiplyScalar( t ).add( this.start ); 57847 57848 } 57849 57850 /** 57851 * Returns the closest squared distance between this line segment and the given one. 57852 * 57853 * @param {Line3} line - The line segment to compute the closest squared distance to. 57854 * @param {Vector3} [c1] - The closest point on this line segment. 57855 * @param {Vector3} [c2] - The closest point on the given line segment. 57856 * @return {number} The squared distance between this line segment and the given one. 57857 */ 57858 distanceSqToLine3( line, c1 = _c1, c2 = _c2 ) { 57859 57860 // from Real-Time Collision Detection by Christer Ericson, chapter 5.1.9 57861 57862 // Computes closest points C1 and C2 of S1(s)=P1+s*(Q1-P1) and 57863 // S2(t)=P2+t*(Q2-P2), returning s and t. Function result is squared 57864 // distance between between S1(s) and S2(t) 57865 57866 const EPSILON = 1e-8 * 1e-8; // must be squared since we compare squared length 57867 let s, t; 57868 57869 const p1 = this.start; 57870 const p2 = line.start; 57871 const q1 = this.end; 57872 const q2 = line.end; 57873 57874 _d1.subVectors( q1, p1 ); // Direction vector of segment S1 57875 _d2.subVectors( q2, p2 ); // Direction vector of segment S2 57876 _r.subVectors( p1, p2 ); 57877 57878 const a = _d1.dot( _d1 ); // Squared length of segment S1, always nonnegative 57879 const e = _d2.dot( _d2 ); // Squared length of segment S2, always nonnegative 57880 const f = _d2.dot( _r ); 57881 57882 // Check if either or both segments degenerate into points 57883 57884 if ( a <= EPSILON && e <= EPSILON ) { 57885 57886 // Both segments degenerate into points 57887 57888 c1.copy( p1 ); 57889 c2.copy( p2 ); 57890 57891 c1.sub( c2 ); 57892 57893 return c1.dot( c1 ); 57894 57895 } 57896 57897 if ( a <= EPSILON ) { 57898 57899 // First segment degenerates into a point 57900 57901 s = 0; 57902 t = f / e; // s = 0 => t = (b*s + f) / e = f / e 57903 t = clamp( t, 0, 1 ); 57904 57905 57906 } else { 57907 57908 const c = _d1.dot( _r ); 57909 57910 if ( e <= EPSILON ) { 57911 57912 // Second segment degenerates into a point 57913 57914 t = 0; 57915 s = clamp( - c / a, 0, 1 ); // t = 0 => s = (b*t - c) / a = -c / a 57916 57917 } else { 57918 57919 // The general nondegenerate case starts here 57920 57921 const b = _d1.dot( _d2 ); 57922 const denom = a * e - b * b; // Always nonnegative 57923 57924 // If segments not parallel, compute closest point on L1 to L2 and 57925 // clamp to segment S1. Else pick arbitrary s (here 0) 57926 57927 if ( denom !== 0 ) { 57928 57929 s = clamp( ( b * f - c * e ) / denom, 0, 1 ); 57930 57931 } else { 57932 57933 s = 0; 57934 57935 } 57936 57937 // Compute point on L2 closest to S1(s) using 57938 // t = Dot((P1 + D1*s) - P2,D2) / Dot(D2,D2) = (b*s + f) / e 57939 57940 t = ( b * s + f ) / e; 57941 57942 // If t in [0,1] done. Else clamp t, recompute s for the new value 57943 // of t using s = Dot((P2 + D2*t) - P1,D1) / Dot(D1,D1)= (t*b - c) / a 57944 // and clamp s to [0, 1] 57945 57946 if ( t < 0 ) { 57947 57948 t = 0.; 57949 s = clamp( - c / a, 0, 1 ); 57950 57951 } else if ( t > 1 ) { 57952 57953 t = 1; 57954 s = clamp( ( b - c ) / a, 0, 1 ); 57955 57956 } 57957 57958 } 57959 57960 } 57961 57962 c1.copy( p1 ).addScaledVector( _d1, s ); 57963 c2.copy( p2 ).addScaledVector( _d2, t ); 57964 57965 return c1.distanceToSquared( c2 ); 57966 57967 } 57968 57969 /** 57970 * Applies a 4x4 transformation matrix to this line segment. 57971 * 57972 * @param {Matrix4} matrix - The transformation matrix. 57973 * @return {Line3} A reference to this line segment. 57974 */ 57975 applyMatrix4( matrix ) { 57976 57977 this.start.applyMatrix4( matrix ); 57978 this.end.applyMatrix4( matrix ); 57979 57980 return this; 57981 57982 } 57983 57984 /** 57985 * Returns `true` if this line segment is equal with the given one. 57986 * 57987 * @param {Line3} line - The line segment to test for equality. 57988 * @return {boolean} Whether this line segment is equal with the given one. 57989 */ 57990 equals( line ) { 57991 57992 return line.start.equals( this.start ) && line.end.equals( this.end ); 57993 57994 } 57995 57996 /** 57997 * Returns a new line segment with copied values from this instance. 57998 * 57999 * @return {Line3} A clone of this instance. 58000 */ 58001 clone() { 58002 58003 return new this.constructor().copy( this ); 58004 58005 } 58006 58007} 58008 58009const _vector$3 = /*@__PURE__*/ new Vector3(); 58010 58011/** 58012 * This displays a cone shaped helper object for a {@link SpotLight}. 58013 * 58014 * When the spot light or its target are transformed or light properties are 58015 * changed, it's necessary to call the `update()` method of the respective helper. 58016 * 58017 * ```js 58018 * const spotLight = new THREE.SpotLight( 0xffffff ); 58019 * spotLight.position.set( 10, 10, 10 ); 58020 * scene.add( spotLight ); 58021 * 58022 * const spotLightHelper = new THREE.SpotLightHelper( spotLight ); 58023 * scene.add( spotLightHelper ); 58024 * ``` 58025 * 58026 * @augments Object3D 58027 */ 58028class SpotLightHelper extends Object3D { 58029 58030 /** 58031 * Constructs a new spot light helper. 58032 * 58033 * @param {HemisphereLight} light - The light to be visualized. 58034 * @param {number|Color|string} [color] - The helper's color. If not set, the helper will take 58035 * the color of the light. 58036 */ 58037 constructor( light, color ) { 58038 58039 super(); 58040 58041 /** 58042 * The light being visualized. 58043 * 58044 * @type {SpotLight} 58045 */ 58046 this.light = light; 58047 58048 this.matrixAutoUpdate = false; 58049 58050 /** 58051 * The color parameter passed in the constructor. 58052 * If not set, the helper will take the color of the light. 58053 * 58054 * @type {number|Color|string} 58055 */ 58056 this.color = color; 58057 58058 this.type = 'SpotLightHelper'; 58059 58060 const geometry = new BufferGeometry(); 58061 58062 const positions = [ 58063 0, 0, 0, 0, 0, 1, 58064 0, 0, 0, 1, 0, 1, 58065 0, 0, 0, -1, 0, 1, 58066 0, 0, 0, 0, 1, 1, 58067 0, 0, 0, 0, -1, 1 58068 ]; 58069 58070 for ( let i = 0, j = 1, l = 32; i < l; i ++, j ++ ) { 58071 58072 const p1 = ( i / l ) * Math.PI * 2; 58073 const p2 = ( j / l ) * Math.PI * 2; 58074 58075 positions.push( 58076 Math.cos( p1 ), Math.sin( p1 ), 1, 58077 Math.cos( p2 ), Math.sin( p2 ), 1 58078 ); 58079 58080 } 58081 58082 geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); 58083 58084 const material = new LineBasicMaterial( { fog: false, toneMapped: false } ); 58085 58086 this.cone = new LineSegments( geometry, material ); 58087 this.add( this.cone ); 58088 58089 this.update(); 58090 58091 } 58092 58093 /** 58094 * Frees the GPU-related resources allocated by this instance. Call this 58095 * method whenever this instance is no longer used in your app. 58096 */ 58097 dispose() { 58098 58099 super.dispose(); 58100 58101 this.cone.geometry.dispose(); 58102 this.cone.material.dispose(); 58103 58104 } 58105 58106 /** 58107 * Updates the helper to match the position and direction of the 58108 * light being visualized. 58109 */ 58110 update() { 58111 58112 this.light.updateWorldMatrix( true, false ); 58113 this.light.target.updateWorldMatrix( true, false ); 58114 58115 // update the local matrix based on the parent and light target transforms 58116 if ( this.parent ) { 58117 58118 this.parent.updateWorldMatrix( true ); 58119 58120 this.matrix 58121 .copy( this.parent.matrixWorld ) 58122 .invert() 58123 .multiply( this.light.matrixWorld ); 58124 58125 } else { 58126 58127 this.matrix.copy( this.light.matrixWorld ); 58128 58129 } 58130 58131 this.matrixWorldNeedsUpdate = true; 58132 58133 const coneLength = this.light.distance ? this.light.distance : 1000; 58134 const coneWidth = coneLength * Math.tan( this.light.angle ); 58135 58136 this.cone.scale.set( coneWidth, coneWidth, coneLength ); 58137 58138 _vector$3.setFromMatrixPosition( this.light.target.matrixWorld ); 58139 58140 this.cone.lookAt( _vector$3 ); 58141 58142 if ( this.color !== undefined ) { 58143 58144 this.cone.material.color.set( this.color ); 58145 58146 } else { 58147 58148 this.cone.material.color.copy( this.light.color ); 58149 58150 } 58151 58152 } 58153 58154} 58155 58156const _vector$2 = /*@__PURE__*/ new Vector3(); 58157const _boneMatrix = /*@__PURE__*/ new Matrix4(); 58158const _matrixWorldInv = /*@__PURE__*/ new Matrix4(); 58159 58160/** 58161 * A helper object to assist with visualizing a {@link Skeleton}. 58162 * 58163 * ```js 58164 * const helper = new THREE.SkeletonHelper( skinnedMesh ); 58165 * scene.add( helper ); 58166 * ``` 58167 * 58168 * @augments LineSegments 58169 */ 58170class SkeletonHelper extends LineSegments { 58171 58172 /** 58173 * Constructs a new skeleton helper. 58174 * 58175 * @param {Object3D} object - Usually an instance of {@link SkinnedMesh}. However, any 3D object 58176 * can be used if it represents a hierarchy of bones (see {@link Bone}). 58177 */ 58178 constructor( object ) { 58179 58180 const bones = getBoneList( object ); 58181 58182 const geometry = new BufferGeometry(); 58183 58184 const vertices = []; 58185 const colors = []; 58186 58187 for ( let i = 0; i < bones.length; i ++ ) { 58188 58189 const bone = bones[ i ]; 58190 58191 if ( bone.parent && bone.parent.isBone ) { 58192 58193 vertices.push( 0, 0, 0 ); 58194 vertices.push( 0, 0, 0 ); 58195 colors.push( 0, 0, 0 ); 58196 colors.push( 0, 0, 0 ); 58197 58198 } 58199 58200 } 58201 58202 geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 58203 geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); 58204 58205 const material = new LineBasicMaterial( { vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true } ); 58206 58207 super( geometry, material ); 58208 58209 /** 58210 * This flag can be used for type testing. 58211 * 58212 * @type {boolean} 58213 * @readonly 58214 * @default true 58215 */ 58216 this.isSkeletonHelper = true; 58217 58218 this.type = 'SkeletonHelper'; 58219 58220 /** 58221 * The object being visualized. 58222 * 58223 * @type {Object3D} 58224 */ 58225 this.root = object; 58226 58227 /** 58228 * The list of bones that the helper visualizes. 58229 * 58230 * @type {Array<Bone>} 58231 */ 58232 this.bones = bones; 58233 58234 this.matrix = object.matrixWorld; 58235 this.matrixAutoUpdate = false; 58236 58237 // colors 58238 58239 const color1 = new Color( 0x0000ff ); 58240 const color2 = new Color( 0x00ff00 ); 58241 58242 this.setColors( color1, color2 ); 58243 58244 } 58245 58246 updateMatrixWorld( force ) { 58247 58248 const bones = this.bones; 58249 58250 const geometry = this.geometry; 58251 const position = geometry.getAttribute( 'position' ); 58252 58253 _matrixWorldInv.copy( this.root.matrixWorld ).invert(); 58254 58255 for ( let i = 0, j = 0; i < bones.length; i ++ ) { 58256 58257 const bone = bones[ i ]; 58258 58259 if ( bone.parent && bone.parent.isBone ) { 58260 58261 _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.matrixWorld ); 58262 _vector$2.setFromMatrixPosition( _boneMatrix ); 58263 position.setXYZ( j, _vector$2.x, _vector$2.y, _vector$2.z ); 58264 58265 _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.parent.matrixWorld ); 58266 _vector$2.setFromMatrixPosition( _boneMatrix ); 58267 position.setXYZ( j + 1, _vector$2.x, _vector$2.y, _vector$2.z ); 58268 58269 j += 2; 58270 58271 } 58272 58273 } 58274 58275 geometry.getAttribute( 'position' ).needsUpdate = true; 58276 58277 super.updateMatrixWorld( force ); 58278 58279 } 58280 58281 /** 58282 * Defines the colors of the helper. 58283 * 58284 * @param {Color} color1 - The first line color for each bone. 58285 * @param {Color} color2 - The second line color for each bone. 58286 * @return {SkeletonHelper} A reference to this helper. 58287 */ 58288 setColors( color1, color2 ) { 58289 58290 const geometry = this.geometry; 58291 const colorAttribute = geometry.getAttribute( 'color' ); 58292 58293 for ( let i = 0; i < colorAttribute.count; i += 2 ) { 58294 58295 colorAttribute.setXYZ( i, color1.r, color1.g, color1.b ); 58296 colorAttribute.setXYZ( i + 1, color2.r, color2.g, color2.b ); 58297 58298 } 58299 58300 colorAttribute.needsUpdate = true; 58301 58302 return this; 58303 58304 } 58305 58306 /** 58307 * Frees the GPU-related resources allocated by this instance. Call this 58308 * method whenever this instance is no longer used in your app. 58309 */ 58310 dispose() { 58311 58312 super.dispose(); 58313 58314 this.geometry.dispose(); 58315 this.material.dispose(); 58316 58317 } 58318 58319} 58320 58321 58322function getBoneList( object ) { 58323 58324 const boneList = []; 58325 58326 if ( object.isBone === true ) { 58327 58328 boneList.push( object ); 58329 58330 } 58331 58332 for ( let i = 0; i < object.children.length; i ++ ) { 58333 58334 boneList.push( ...getBoneList( object.children[ i ] ) ); 58335 58336 } 58337 58338 return boneList; 58339 58340} 58341 58342/** 58343 * This displays a helper object consisting of a spherical mesh for 58344 * visualizing an instance of {@link PointLight}. 58345 * 58346 * ```js 58347 * const pointLight = new THREE.PointLight( 0xff0000, 1, 100 ); 58348 * pointLight.position.set( 10, 10, 10 ); 58349 * scene.add( pointLight ); 58350 * 58351 * const sphereSize = 1; 58352 * const pointLightHelper = new THREE.PointLightHelper( pointLight, sphereSize ); 58353 * scene.add( pointLightHelper ); 58354 * ``` 58355 * 58356 * @augments Mesh 58357 */ 58358class PointLightHelper extends Mesh { 58359 58360 /** 58361 * Constructs a new point light helper. 58362 * 58363 * @param {PointLight} light - The light to be visualized. 58364 * @param {number} [sphereSize=1] - The size of the sphere helper. 58365 * @param {number|Color|string} [color] - The helper's color. If not set, the helper will take 58366 * the color of the light. 58367 */ 58368 constructor( light, sphereSize, color ) { 58369 58370 const geometry = new SphereGeometry( sphereSize, 4, 2 ); 58371 const material = new MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } ); 58372 58373 super( geometry, material ); 58374 58375 /** 58376 * The light being visualized. 58377 * 58378 * @type {PointLight} 58379 */ 58380 this.light = light; 58381 58382 /** 58383 * The color parameter passed in the constructor. 58384 * If not set, the helper will take the color of the light. 58385 * 58386 * @type {number|Color|string} 58387 */ 58388 this.color = color; 58389 58390 this.type = 'PointLightHelper'; 58391 58392 this.matrix = this.light.matrixWorld; 58393 this.matrixAutoUpdate = false; 58394 58395 this.update(); 58396 58397 } 58398 58399 /** 58400 * Frees the GPU-related resources allocated by this instance. Call this 58401 * method whenever this instance is no longer used in your app. 58402 */ 58403 dispose() { 58404 58405 super.dispose(); 58406 58407 this.geometry.dispose(); 58408 this.material.dispose(); 58409 58410 } 58411 58412 /** 58413 * Updates the helper to match the position of the 58414 * light being visualized. 58415 */ 58416 update() { 58417 58418 this.matrixWorldNeedsUpdate = true; 58419 58420 this.light.updateWorldMatrix( true, false ); 58421 58422 if ( this.color !== undefined ) { 58423 58424 this.material.color.set( this.color ); 58425 58426 } else { 58427 58428 this.material.color.copy( this.light.color ); 58429 58430 } 58431 58432 /* 58433 const d = this.light.distance; 58434 58435 if ( d === 0.0 ) { 58436 58437 this.lightDistance.visible = false; 58438 58439 } else { 58440 58441 this.lightDistance.visible = true; 58442 this.lightDistance.scale.set( d, d, d ); 58443 58444 } 58445 */ 58446 58447 } 58448 58449} 58450 58451const _vector$1 = /*@__PURE__*/ new Vector3(); 58452const _color1 = /*@__PURE__*/ new Color(); 58453const _color2 = /*@__PURE__*/ new Color(); 58454 58455/** 58456 * Creates a visual aid consisting of a spherical mesh for a 58457 * given {@link HemisphereLight}. 58458 * 58459 * When the hemisphere light is transformed or its light properties are changed, 58460 * it's necessary to call the `update()` method of the respective helper. 58461 * 58462 * ```js 58463 * const light = new THREE.HemisphereLight( 0xffffbb, 0x080820, 1 ); 58464 * const helper = new THREE.HemisphereLightHelper( light, 5 ); 58465 * scene.add( helper ); 58466 * ``` 58467 * 58468 * @augments Object3D 58469 */ 58470class HemisphereLightHelper extends Object3D { 58471 58472 /** 58473 * Constructs a new hemisphere light helper. 58474 * 58475 * @param {HemisphereLight} light - The light to be visualized. 58476 * @param {number} [size=1] - The size of the mesh used to visualize the light. 58477 * @param {number|Color|string} [color] - The helper's color. If not set, the helper will take 58478 * the color of the light. 58479 */ 58480 constructor( light, size, color ) { 58481 58482 super(); 58483 58484 /** 58485 * The light being visualized. 58486 * 58487 * @type {HemisphereLight} 58488 */ 58489 this.light = light; 58490 58491 this.matrix = light.matrixWorld; 58492 this.matrixAutoUpdate = false; 58493 58494 /** 58495 * The color parameter passed in the constructor. 58496 * If not set, the helper will take the color of the light. 58497 * 58498 * @type {number|Color|string} 58499 */ 58500 this.color = color; 58501 58502 this.type = 'HemisphereLightHelper'; 58503 58504 const geometry = new OctahedronGeometry( size ); 58505 geometry.rotateY( Math.PI * 0.5 ); 58506 58507 this.material = new MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } ); 58508 if ( this.color === undefined ) this.material.vertexColors = true; 58509 58510 const position = geometry.getAttribute( 'position' ); 58511 const colors = new Float32Array( position.count * 3 ); 58512 58513 geometry.setAttribute( 'color', new BufferAttribute( colors, 3 ) ); 58514 58515 this.add( new Mesh( geometry, this.material ) ); 58516 58517 this.update(); 58518 58519 } 58520 58521 /** 58522 * Frees the GPU-related resources allocated by this instance. Call this 58523 * method whenever this instance is no longer used in your app. 58524 */ 58525 dispose() { 58526 58527 super.dispose(); 58528 58529 this.children[ 0 ].geometry.dispose(); 58530 this.children[ 0 ].material.dispose(); 58531 58532 } 58533 58534 /** 58535 * Updates the helper to match the position and direction of the 58536 * light being visualized. 58537 */ 58538 update() { 58539 58540 const mesh = this.children[ 0 ]; 58541 58542 if ( this.color !== undefined ) { 58543 58544 this.material.color.set( this.color ); 58545 58546 } else { 58547 58548 const colors = mesh.geometry.getAttribute( 'color' ); 58549 58550 _color1.copy( this.light.color ); 58551 _color2.copy( this.light.groundColor ); 58552 58553 for ( let i = 0, l = colors.count; i < l; i ++ ) { 58554 58555 const color = ( i < ( l / 2 ) ) ? _color1 : _color2; 58556 58557 colors.setXYZ( i, color.r, color.g, color.b ); 58558 58559 } 58560 58561 colors.needsUpdate = true; 58562 58563 } 58564 58565 this.matrixWorldNeedsUpdate = true; 58566 58567 this.light.updateWorldMatrix( true, false ); 58568 58569 mesh.lookAt( _vector$1.setFromMatrixPosition( this.light.matrixWorld ).negate() ); 58570 58571 } 58572 58573} 58574 58575/** 58576 * The helper is an object to define grids. Grids are two-dimensional 58577 * arrays of lines. 58578 * 58579 * ```js 58580 * const size = 10; 58581 * const divisions = 10; 58582 * 58583 * const gridHelper = new THREE.GridHelper( size, divisions ); 58584 * scene.add( gridHelper ); 58585 * ``` 58586 * 58587 * @augments LineSegments 58588 */ 58589class GridHelper extends LineSegments { 58590 58591 /** 58592 * Constructs a new grid helper. 58593 * 58594 * @param {number} [size=10] - The size of the grid. 58595 * @param {number} [divisions=10] - The number of divisions across the grid. 58596 * @param {number|Color|string} [color1=0x444444] - The color of the center line. 58597 * @param {number|Color|string} [color2=0x888888] - The color of the lines of the grid. 58598 */ 58599 constructor( size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888 ) { 58600 58601 color1 = new Color( color1 ); 58602 color2 = new Color( color2 ); 58603 58604 const center = divisions / 2; 58605 const step = size / divisions; 58606 const halfSize = size / 2; 58607 58608 const vertices = [], colors = []; 58609 58610 for ( let i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) { 58611 58612 vertices.push( - halfSize, 0, k, halfSize, 0, k ); 58613 vertices.push( k, 0, - halfSize, k, 0, halfSize ); 58614 58615 const color = i === center ? color1 : color2; 58616 58617 color.toArray( colors, j ); j += 3; 58618 color.toArray( colors, j ); j += 3; 58619 color.toArray( colors, j ); j += 3; 58620 color.toArray( colors, j ); j += 3; 58621 58622 } 58623 58624 const geometry = new BufferGeometry(); 58625 geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 58626 geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); 58627 58628 const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); 58629 58630 super( geometry, material ); 58631 58632 this.type = 'GridHelper'; 58633 58634 } 58635 58636 /** 58637 * Frees the GPU-related resources allocated by this instance. Call this 58638 * method whenever this instance is no longer used in your app. 58639 */ 58640 dispose() { 58641 58642 super.dispose(); 58643 58644 this.geometry.dispose(); 58645 this.material.dispose(); 58646 58647 } 58648 58649} 58650 58651/** 58652 * This helper is an object to define polar grids. Grids are 58653 * two-dimensional arrays of lines. 58654 * 58655 * ```js 58656 * const radius = 10; 58657 * const sectors = 16; 58658 * const rings = 8; 58659 * const divisions = 64; 58660 * 58661 * const helper = new THREE.PolarGridHelper( radius, sectors, rings, divisions ); 58662 * scene.add( helper ); 58663 * ``` 58664 * 58665 * @augments LineSegments 58666 */ 58667class PolarGridHelper extends LineSegments { 58668 58669 /** 58670 * Constructs a new polar grid helper. 58671 * 58672 * @param {number} [radius=10] - The radius of the polar grid. This can be any positive number. 58673 * @param {number} [sectors=16] - The number of sectors the grid will be divided into. This can be any positive integer. 58674 * @param {number} [rings=16] - The number of rings. This can be any positive integer. 58675 * @param {number} [divisions=64] - The number of line segments used for each circle. This can be any positive integer. 58676 * @param {number|Color|string} [color1=0x444444] - The first color used for grid elements. 58677 * @param {number|Color|string} [color2=0x888888] - The second color used for grid elements. 58678 */ 58679 constructor( radius = 10, sectors = 16, rings = 8, divisions = 64, color1 = 0x444444, color2 = 0x888888 ) { 58680 58681 color1 = new Color( color1 ); 58682 color2 = new Color( color2 ); 58683 58684 const vertices = []; 58685 const colors = []; 58686 58687 // create the sectors 58688 58689 if ( sectors > 1 ) { 58690 58691 for ( let i = 0; i < sectors; i ++ ) { 58692 58693 const v = ( i / sectors ) * ( Math.PI * 2 ); 58694 58695 const x = Math.sin( v ) * radius; 58696 const z = Math.cos( v ) * radius; 58697 58698 vertices.push( 0, 0, 0 ); 58699 vertices.push( x, 0, z ); 58700 58701 const color = ( i & 1 ) ? color1 : color2; 58702 58703 colors.push( color.r, color.g, color.b ); 58704 colors.push( color.r, color.g, color.b ); 58705 58706 } 58707 58708 } 58709 58710 // create the rings 58711 58712 for ( let i = 0; i < rings; i ++ ) { 58713 58714 const color = ( i & 1 ) ? color1 : color2; 58715 58716 const r = radius - ( radius / rings * i ); 58717 58718 for ( let j = 0; j < divisions; j ++ ) { 58719 58720 // first vertex 58721 58722 let v = ( j / divisions ) * ( Math.PI * 2 ); 58723 58724 let x = Math.sin( v ) * r; 58725 let z = Math.cos( v ) * r; 58726 58727 vertices.push( x, 0, z ); 58728 colors.push( color.r, color.g, color.b ); 58729 58730 // second vertex 58731 58732 v = ( ( j + 1 ) / divisions ) * ( Math.PI * 2 ); 58733 58734 x = Math.sin( v ) * r; 58735 z = Math.cos( v ) * r; 58736 58737 vertices.push( x, 0, z ); 58738 colors.push( color.r, color.g, color.b ); 58739 58740 } 58741 58742 } 58743 58744 const geometry = new BufferGeometry(); 58745 geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 58746 geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); 58747 58748 const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); 58749 58750 super( geometry, material ); 58751 58752 this.type = 'PolarGridHelper'; 58753 58754 } 58755 58756 /** 58757 * Frees the GPU-related resources allocated by this instance. Call this 58758 * method whenever this instance is no longer used in your app. 58759 */ 58760 dispose() { 58761 58762 super.dispose(); 58763 58764 this.geometry.dispose(); 58765 this.material.dispose(); 58766 58767 } 58768 58769} 58770 58771const _v1 = /*@__PURE__*/ new Vector3(); 58772const _v2 = /*@__PURE__*/ new Vector3(); 58773const _v3 = /*@__PURE__*/ new Vector3(); 58774 58775/** 58776 * Helper object to assist with visualizing a {@link DirectionalLight}'s 58777 * effect on the scene. This consists of a plane and a line representing the 58778 * light's position and direction. 58779 * 58780 * When the directional light or its target are transformed or light properties 58781 * are changed, it's necessary to call the `update()` method of the respective helper. 58782 * 58783 * ```js 58784 * const light = new THREE.DirectionalLight( 0xFFFFFF ); 58785 * scene.add( light ); 58786 * 58787 * const helper = new THREE.DirectionalLightHelper( light, 5 ); 58788 * scene.add( helper ); 58789 * ``` 58790 * 58791 * @augments Object3D 58792 */ 58793class DirectionalLightHelper extends Object3D { 58794 58795 /** 58796 * Constructs a new directional light helper. 58797 * 58798 * @param {DirectionalLight} light - The light to be visualized. 58799 * @param {number} [size=1] - The dimensions of the plane. 58800 * @param {number|Color|string} [color] - The helper's color. If not set, the helper will take 58801 * the color of the light. 58802 */ 58803 constructor( light, size, color ) { 58804 58805 super(); 58806 58807 /** 58808 * The light being visualized. 58809 * 58810 * @type {DirectionalLight} 58811 */ 58812 this.light = light; 58813 58814 this.matrix = light.matrixWorld; 58815 this.matrixAutoUpdate = false; 58816 58817 /** 58818 * The color parameter passed in the constructor. 58819 * If not set, the helper will take the color of the light. 58820 * 58821 * @type {number|Color|string} 58822 */ 58823 this.color = color; 58824 58825 this.type = 'DirectionalLightHelper'; 58826 58827 if ( size === undefined ) size = 1; 58828 58829 let geometry = new BufferGeometry(); 58830 geometry.setAttribute( 'position', new Float32BufferAttribute( [ 58831 - size, size, 0, 58832 size, size, 0, 58833 size, - size, 0, 58834 - size, - size, 0, 58835 - size, size, 0 58836 ], 3 ) ); 58837 58838 const material = new LineBasicMaterial( { fog: false, toneMapped: false } ); 58839 58840 /** 58841 * Contains the line showing the location of the directional light. 58842 * 58843 * @type {Line} 58844 */ 58845 this.lightPlane = new Line( geometry, material ); 58846 this.add( this.lightPlane ); 58847 58848 geometry = new BufferGeometry(); 58849 geometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) ); 58850 58851 /** 58852 * Represents the target line of the directional light. 58853 * 58854 * @type {Line} 58855 */ 58856 this.targetLine = new Line( geometry, material ); 58857 this.add( this.targetLine ); 58858 58859 this.update(); 58860 58861 } 58862 58863 /** 58864 * Frees the GPU-related resources allocated by this instance. Call this 58865 * method whenever this instance is no longer used in your app. 58866 */ 58867 dispose() { 58868 58869 super.dispose(); 58870 58871 this.lightPlane.geometry.dispose(); 58872 this.lightPlane.material.dispose(); 58873 this.targetLine.geometry.dispose(); 58874 this.targetLine.material.dispose(); 58875 58876 } 58877 58878 /** 58879 * Updates the helper to match the position and direction of the 58880 * light being visualized. 58881 */ 58882 update() { 58883 58884 this.matrixWorldNeedsUpdate = true; 58885 58886 this.light.updateWorldMatrix( true, false ); 58887 this.light.target.updateWorldMatrix( true, false ); 58888 58889 _v1.setFromMatrixPosition( this.light.matrixWorld ); 58890 _v2.setFromMatrixPosition( this.light.target.matrixWorld ); 58891 _v3.subVectors( _v2, _v1 ); 58892 58893 this.lightPlane.lookAt( _v2 ); 58894 58895 if ( this.color !== undefined ) { 58896 58897 this.lightPlane.material.color.set( this.color ); 58898 this.targetLine.material.color.set( this.color ); 58899 58900 } else { 58901 58902 this.lightPlane.material.color.copy( this.light.color ); 58903 this.targetLine.material.color.copy( this.light.color ); 58904 58905 } 58906 58907 this.targetLine.lookAt( _v2 ); 58908 this.targetLine.scale.z = _v3.length(); 58909 58910 } 58911 58912} 58913 58914const _vector = /*@__PURE__*/ new Vector3(); 58915const _camera = /*@__PURE__*/ new Camera(); 58916 58917/** 58918 * This helps with visualizing what a camera contains in its frustum. It 58919 * visualizes the frustum of a camera using a line segments. 58920 * 58921 * Based on frustum visualization in [lightgl.js shadowmap example](https://github.com/evanw/lightgl.js/blob/master/tests/shadowmap.html). 58922 * 58923 * `CameraHelper` must be a child of the scene. 58924 * 58925 * When the camera is transformed or its projection matrix is changed, it's necessary 58926 * to call the `update()` method of the respective helper. 58927 * 58928 * ```js 58929 * const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); 58930 * const helper = new THREE.CameraHelper( camera ); 58931 * scene.add( helper ); 58932 * ``` 58933 * 58934 * @augments LineSegments 58935 */ 58936class CameraHelper extends LineSegments { 58937 58938 /** 58939 * Constructs a new arrow helper. 58940 * 58941 * @param {Camera} camera - The camera to visualize. 58942 */ 58943 constructor( camera ) { 58944 58945 const geometry = new BufferGeometry(); 58946 const material = new LineBasicMaterial( { color: 0xffffff, vertexColors: true, toneMapped: false } ); 58947 58948 const vertices = []; 58949 const colors = []; 58950 58951 const pointMap = {}; 58952 58953 // near 58954 58955 addLine( 'n1', 'n2' ); 58956 addLine( 'n2', 'n4' ); 58957 addLine( 'n4', 'n3' ); 58958 addLine( 'n3', 'n1' ); 58959 58960 // far 58961 58962 addLine( 'f1', 'f2' ); 58963 addLine( 'f2', 'f4' ); 58964 addLine( 'f4', 'f3' ); 58965 addLine( 'f3', 'f1' ); 58966 58967 // sides 58968 58969 addLine( 'n1', 'f1' ); 58970 addLine( 'n2', 'f2' ); 58971 addLine( 'n3', 'f3' ); 58972 addLine( 'n4', 'f4' ); 58973 58974 // cone 58975 58976 addLine( 'p', 'n1' ); 58977 addLine( 'p', 'n2' ); 58978 addLine( 'p', 'n3' ); 58979 addLine( 'p', 'n4' ); 58980 58981 // up 58982 58983 addLine( 'u1', 'u2' ); 58984 addLine( 'u2', 'u3' ); 58985 addLine( 'u3', 'u1' ); 58986 58987 // target 58988 58989 addLine( 'c', 't' ); 58990 addLine( 'p', 'c' ); 58991 58992 // cross 58993 58994 addLine( 'cn1', 'cn2' ); 58995 addLine( 'cn3', 'cn4' ); 58996 58997 addLine( 'cf1', 'cf2' ); 58998 addLine( 'cf3', 'cf4' ); 58999 59000 function addLine( a, b ) { 59001 59002 addPoint( a ); 59003 addPoint( b ); 59004 59005 } 59006 59007 function addPoint( id ) { 59008 59009 vertices.push( 0, 0, 0 ); 59010 colors.push( 0, 0, 0 ); 59011 59012 if ( pointMap[ id ] === undefined ) { 59013 59014 pointMap[ id ] = []; 59015 59016 } 59017 59018 pointMap[ id ].push( ( vertices.length / 3 ) - 1 ); 59019 59020 } 59021 59022 geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 59023 geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); 59024 59025 super( geometry, material ); 59026 59027 this.type = 'CameraHelper'; 59028 59029 /** 59030 * The camera being visualized. 59031 * 59032 * @type {Camera} 59033 */ 59034 this.camera = camera; 59035 if ( this.camera.updateProjectionMatrix ) this.camera.updateProjectionMatrix(); 59036 59037 this.matrix = camera.matrixWorld; 59038 this.matrixAutoUpdate = false; 59039 59040 /** 59041 * This contains the points used to visualize the camera. 59042 * 59043 * @type {Object<string,Array<number>>} 59044 */ 59045 this.pointMap = pointMap; 59046 59047 this.update(); 59048 59049 // colors 59050 59051 const colorFrustum = new Color( 0xffaa00 ); 59052 const colorCone = new Color( 0xff0000 ); 59053 const colorUp = new Color( 0x00aaff ); 59054 const colorTarget = new Color( 0xffffff ); 59055 const colorCross = new Color( 0x333333 ); 59056 59057 this.setColors( colorFrustum, colorCone, colorUp, colorTarget, colorCross ); 59058 59059 } 59060 59061 /** 59062 * Defines the colors of the helper. 59063 * 59064 * @param {Color} frustum - The frustum line color. 59065 * @param {Color} cone - The cone line color. 59066 * @param {Color} up - The up line color. 59067 * @param {Color} target - The target line color. 59068 * @param {Color} cross - The cross line color. 59069 * @return {CameraHelper} A reference to this helper. 59070 */ 59071 setColors( frustum, cone, up, target, cross ) { 59072 59073 const geometry = this.geometry; 59074 59075 const colorAttribute = geometry.getAttribute( 'color' ); 59076 59077 // near 59078 59079 colorAttribute.setXYZ( 0, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 1, frustum.r, frustum.g, frustum.b ); // n1, n2 59080 colorAttribute.setXYZ( 2, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 3, frustum.r, frustum.g, frustum.b ); // n2, n4 59081 colorAttribute.setXYZ( 4, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 5, frustum.r, frustum.g, frustum.b ); // n4, n3 59082 colorAttribute.setXYZ( 6, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 7, frustum.r, frustum.g, frustum.b ); // n3, n1 59083 59084 // far 59085 59086 colorAttribute.setXYZ( 8, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 9, frustum.r, frustum.g, frustum.b ); // f1, f2 59087 colorAttribute.setXYZ( 10, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 11, frustum.r, frustum.g, frustum.b ); // f2, f4 59088 colorAttribute.setXYZ( 12, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 13, frustum.r, frustum.g, frustum.b ); // f4, f3 59089 colorAttribute.setXYZ( 14, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 15, frustum.r, frustum.g, frustum.b ); // f3, f1 59090 59091 // sides 59092 59093 colorAttribute.setXYZ( 16, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 17, frustum.r, frustum.g, frustum.b ); // n1, f1 59094 colorAttribute.setXYZ( 18, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 19, frustum.r, frustum.g, frustum.b ); // n2, f2 59095 colorAttribute.setXYZ( 20, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 21, frustum.r, frustum.g, frustum.b ); // n3, f3 59096 colorAttribute.setXYZ( 22, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 23, frustum.r, frustum.g, frustum.b ); // n4, f4 59097 59098 // cone 59099 59100 colorAttribute.setXYZ( 24, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 25, cone.r, cone.g, cone.b ); // p, n1 59101 colorAttribute.setXYZ( 26, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 27, cone.r, cone.g, cone.b ); // p, n2 59102 colorAttribute.setXYZ( 28, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 29, cone.r, cone.g, cone.b ); // p, n3 59103 colorAttribute.setXYZ( 30, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 31, cone.r, cone.g, cone.b ); // p, n4 59104 59105 // up 59106 59107 colorAttribute.setXYZ( 32, up.r, up.g, up.b ); colorAttribute.setXYZ( 33, up.r, up.g, up.b ); // u1, u2 59108 colorAttribute.setXYZ( 34, up.r, up.g, up.b ); colorAttribute.setXYZ( 35, up.r, up.g, up.b ); // u2, u3 59109 colorAttribute.setXYZ( 36, up.r, up.g, up.b ); colorAttribute.setXYZ( 37, up.r, up.g, up.b ); // u3, u1 59110 59111 // target 59112 59113 colorAttribute.setXYZ( 38, target.r, target.g, target.b ); colorAttribute.setXYZ( 39, target.r, target.g, target.b ); // c, t 59114 colorAttribute.setXYZ( 40, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 41, cross.r, cross.g, cross.b ); // p, c 59115 59116 // cross 59117 59118 colorAttribute.setXYZ( 42, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 43, cross.r, cross.g, cross.b ); // cn1, cn2 59119 colorAttribute.setXYZ( 44, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 45, cross.r, cross.g, cross.b ); // cn3, cn4 59120 59121 colorAttribute.setXYZ( 46, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 47, cross.r, cross.g, cross.b ); // cf1, cf2 59122 colorAttribute.setXYZ( 48, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 49, cross.r, cross.g, cross.b ); // cf3, cf4 59123 59124 colorAttribute.needsUpdate = true; 59125 59126 return this; 59127 59128 } 59129 59130 /** 59131 * Updates the helper based on the projection matrix of the camera. 59132 */ 59133 update() { 59134 59135 const geometry = this.geometry; 59136 const pointMap = this.pointMap; 59137 59138 const w = 1, h = 1; 59139 59140 let nearZ, farZ; 59141 59142 // we need just camera projection matrix inverse 59143 // world matrix must be identity 59144 59145 _camera.projectionMatrixInverse.copy( this.camera.projectionMatrixInverse ); 59146 59147 // Adjust z values based on coordinate system 59148 59149 if ( this.camera.reversedDepth === true ) { 59150 59151 nearZ = 1; 59152 farZ = 0; 59153 59154 } else { 59155 59156 if ( this.camera.coordinateSystem === WebGLCoordinateSystem ) { 59157 59158 nearZ = -1; 59159 farZ = 1; 59160 59161 } else if ( this.camera.coordinateSystem === WebGPUCoordinateSystem ) { 59162 59163 nearZ = 0; 59164 farZ = 1; 59165 59166 } else { 59167 59168 throw new Error( 'THREE.CameraHelper.update(): Invalid coordinate system: ' + this.camera.coordinateSystem ); 59169 59170 } 59171 59172 } 59173 59174 59175 // center / target 59176 setPoint( 'c', pointMap, geometry, _camera, 0, 0, nearZ ); 59177 setPoint( 't', pointMap, geometry, _camera, 0, 0, farZ ); 59178 59179 // near 59180 59181 setPoint( 'n1', pointMap, geometry, _camera, - w, - h, nearZ ); 59182 setPoint( 'n2', pointMap, geometry, _camera, w, - h, nearZ ); 59183 setPoint( 'n3', pointMap, geometry, _camera, - w, h, nearZ ); 59184 setPoint( 'n4', pointMap, geometry, _camera, w, h, nearZ ); 59185 59186 // far 59187 59188 setPoint( 'f1', pointMap, geometry, _camera, - w, - h, farZ ); 59189 setPoint( 'f2', pointMap, geometry, _camera, w, - h, farZ ); 59190 setPoint( 'f3', pointMap, geometry, _camera, - w, h, farZ ); 59191 setPoint( 'f4', pointMap, geometry, _camera, w, h, farZ ); 59192 59193 // up 59194 59195 setPoint( 'u1', pointMap, geometry, _camera, w * 0.7, h * 1.1, nearZ ); 59196 setPoint( 'u2', pointMap, geometry, _camera, - w * 0.7, h * 1.1, nearZ ); 59197 setPoint( 'u3', pointMap, geometry, _camera, 0, h * 2, nearZ ); 59198 59199 // cross 59200 59201 setPoint( 'cf1', pointMap, geometry, _camera, - w, 0, farZ ); 59202 setPoint( 'cf2', pointMap, geometry, _camera, w, 0, farZ ); 59203 setPoint( 'cf3', pointMap, geometry, _camera, 0, - h, farZ ); 59204 setPoint( 'cf4', pointMap, geometry, _camera, 0, h, farZ ); 59205 59206 setPoint( 'cn1', pointMap, geometry, _camera, - w, 0, nearZ ); 59207 setPoint( 'cn2', pointMap, geometry, _camera, w, 0, nearZ ); 59208 setPoint( 'cn3', pointMap, geometry, _camera, 0, - h, nearZ ); 59209 setPoint( 'cn4', pointMap, geometry, _camera, 0, h, nearZ ); 59210 59211 geometry.getAttribute( 'position' ).needsUpdate = true; 59212 59213 } 59214 59215 /** 59216 * Frees the GPU-related resources allocated by this instance. Call this 59217 * method whenever this instance is no longer used in your app. 59218 */ 59219 dispose() { 59220 59221 super.dispose(); 59222 59223 this.geometry.dispose(); 59224 this.material.dispose(); 59225 59226 } 59227 59228} 59229 59230 59231function setPoint( point, pointMap, geometry, camera, x, y, z ) { 59232 59233 _vector.set( x, y, z ).unproject( camera ); 59234 59235 const points = pointMap[ point ]; 59236 59237 if ( points !== undefined ) { 59238 59239 const position = geometry.getAttribute( 'position' ); 59240 59241 for ( let i = 0, l = points.length; i < l; i ++ ) { 59242 59243 position.setXYZ( points[ i ], _vector.x, _vector.y, _vector.z ); 59244 59245 } 59246 59247 } 59248 59249} 59250 59251const _box = /*@__PURE__*/ new Box3(); 59252 59253/** 59254 * Helper object to graphically show the world-axis-aligned bounding box 59255 * around an object. The actual bounding box is handled with {@link Box3}, 59256 * this is just a visual helper for debugging. It can be automatically 59257 * resized with {@link BoxHelper#update} when the object it's created from 59258 * is transformed. Note that the object must have a geometry for this to work, 59259 * so it won't work with sprites. 59260 * 59261 * ```js 59262 * const sphere = new THREE.SphereGeometry(); 59263 * const object = new THREE.Mesh( sphere, new THREE.MeshBasicMaterial( 0xff0000 ) ); 59264 * const box = new THREE.BoxHelper( object, 0xffff00 ); 59265 * scene.add( box ); 59266 * ``` 59267 * 59268 * @augments LineSegments 59269 */ 59270class BoxHelper extends LineSegments { 59271 59272 /** 59273 * Constructs a new box helper. 59274 * 59275 * @param {Object3D} [object] - The 3D object to show the world-axis-aligned bounding box. 59276 * @param {number|Color|string} [color=0xffff00] - The box's color. 59277 */ 59278 constructor( object, color = 0xffff00 ) { 59279 59280 const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); 59281 const positions = new Float32Array( 8 * 3 ); 59282 59283 const geometry = new BufferGeometry(); 59284 geometry.setIndex( new BufferAttribute( indices, 1 ) ); 59285 geometry.setAttribute( 'position', new BufferAttribute( positions, 3 ) ); 59286 59287 super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); 59288 59289 /** 59290 * The 3D object being visualized. 59291 * 59292 * @type {Object3D} 59293 */ 59294 this.object = object; 59295 this.type = 'BoxHelper'; 59296 59297 this.matrixAutoUpdate = false; 59298 59299 this.update(); 59300 59301 } 59302 59303 /** 59304 * Updates the helper's geometry to match the dimensions of the object, 59305 * including any children. 59306 */ 59307 update() { 59308 59309 if ( this.object !== undefined ) { 59310 59311 _box.setFromObject( this.object ); 59312 59313 } 59314 59315 if ( _box.isEmpty() ) return; 59316 59317 const min = _box.min; 59318 const max = _box.max; 59319 59320 /* 59321 5____4 59322 1/___0/| 59323 | 6__|_7 59324 2/___3/ 59325 59326 0: max.x, max.y, max.z 59327 1: min.x, max.y, max.z 59328 2: min.x, min.y, max.z 59329 3: max.x, min.y, max.z 59330 4: max.x, max.y, min.z 59331 5: min.x, max.y, min.z 59332 6: min.x, min.y, min.z 59333 7: max.x, min.y, min.z 59334 */ 59335 59336 const position = this.geometry.attributes.position; 59337 const array = position.array; 59338 59339 array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z; 59340 array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z; 59341 array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z; 59342 array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z; 59343 array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z; 59344 array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z; 59345 array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z; 59346 array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z; 59347 59348 position.needsUpdate = true; 59349 59350 this.geometry.computeBoundingSphere(); 59351 59352 } 59353 59354 /** 59355 * Updates the wireframe box for the passed object. 59356 * 59357 * @param {Object3D} object - The 3D object to create the helper for. 59358 * @return {BoxHelper} A reference to this instance. 59359 */ 59360 setFromObject( object ) { 59361 59362 this.object = object; 59363 this.update(); 59364 59365 return this; 59366 59367 } 59368 59369 copy( source, recursive ) { 59370 59371 super.copy( source, recursive ); 59372 59373 this.object = source.object; 59374 59375 return this; 59376 59377 } 59378 59379 /** 59380 * Frees the GPU-related resources allocated by this instance. Call this 59381 * method whenever this instance is no longer used in your app. 59382 */ 59383 dispose() { 59384 59385 super.dispose(); 59386 59387 this.geometry.dispose(); 59388 this.material.dispose(); 59389 59390 } 59391 59392} 59393 59394/** 59395 * A helper object to visualize an instance of {@link Box3}. 59396 * 59397 * ```js 59398 * const box = new THREE.Box3(); 59399 * box.setFromCenterAndSize( new THREE.Vector3( 1, 1, 1 ), new THREE.Vector3( 2, 1, 3 ) ); 59400 * 59401 * const helper = new THREE.Box3Helper( box, 0xffff00 ); 59402 * scene.add( helper ) 59403 * ``` 59404 * 59405 * @augments LineSegments 59406 */ 59407class Box3Helper extends LineSegments { 59408 59409 /** 59410 * Constructs a new box3 helper. 59411 * 59412 * @param {Box3} box - The box to visualize. 59413 * @param {number|Color|string} [color=0xffff00] - The box's color. 59414 */ 59415 constructor( box, color = 0xffff00 ) { 59416 59417 const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); 59418 59419 const positions = [ 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1 ]; 59420 59421 const geometry = new BufferGeometry(); 59422 59423 geometry.setIndex( new BufferAttribute( indices, 1 ) ); 59424 59425 geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); 59426 59427 super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); 59428 59429 /** 59430 * The box being visualized. 59431 * 59432 * @type {Box3} 59433 */ 59434 this.box = box; 59435 59436 this.type = 'Box3Helper'; 59437 59438 this.geometry.computeBoundingSphere(); 59439 59440 } 59441 59442 updateMatrixWorld( force ) { 59443 59444 const box = this.box; 59445 59446 if ( box.isEmpty() ) return; 59447 59448 box.getCenter( this.position ); 59449 59450 box.getSize( this.scale ); 59451 59452 this.scale.multiplyScalar( 0.5 ); 59453 59454 super.updateMatrixWorld( force ); 59455 59456 } 59457 59458 /** 59459 * Frees the GPU-related resources allocated by this instance. Call this 59460 * method whenever this instance is no longer used in your app. 59461 */ 59462 dispose() { 59463 59464 super.dispose(); 59465 59466 this.geometry.dispose(); 59467 this.material.dispose(); 59468 59469 } 59470 59471} 59472 59473/** 59474 * A helper object to visualize an instance of {@link Plane}. 59475 * 59476 * ```js 59477 * const plane = new THREE.Plane( new THREE.Vector3( 1, 1, 0.2 ), 3 ); 59478 * const helper = new THREE.PlaneHelper( plane, 1, 0xffff00 ); 59479 * scene.add( helper ); 59480 * ``` 59481 * 59482 * @augments Line 59483 */ 59484class PlaneHelper extends Line { 59485 59486 /** 59487 * Constructs a new plane helper. 59488 * 59489 * @param {Plane} plane - The plane to be visualized. 59490 * @param {number} [size=1] - The side length of plane helper. 59491 * @param {number|Color|string} [hex=0xffff00] - The helper's color. 59492 */ 59493 constructor( plane, size = 1, hex = 0xffff00 ) { 59494 59495 const color = hex; 59496 59497 const positions = [ 1, -1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0, 1, 1, 0 ]; 59498 59499 const geometry = new BufferGeometry(); 59500 geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); 59501 geometry.computeBoundingSphere(); 59502 59503 super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); 59504 59505 this.type = 'PlaneHelper'; 59506 59507 /** 59508 * The plane being visualized. 59509 * 59510 * @type {Plane} 59511 */ 59512 this.plane = plane; 59513 59514 /** 59515 * The side length of plane helper. 59516 * 59517 * @type {number} 59518 * @default 1 59519 */ 59520 this.size = size; 59521 59522 const positions2 = [ 1, 1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0 ]; 59523 59524 const geometry2 = new BufferGeometry(); 59525 geometry2.setAttribute( 'position', new Float32BufferAttribute( positions2, 3 ) ); 59526 geometry2.computeBoundingSphere(); 59527 59528 this.add( new Mesh( geometry2, new MeshBasicMaterial( { color: color, opacity: 0.2, transparent: true, depthWrite: false, toneMapped: false } ) ) ); 59529 59530 } 59531 59532 updateMatrixWorld( force ) { 59533 59534 this.position.set( 0, 0, 0 ); 59535 59536 this.scale.set( 0.5 * this.size, 0.5 * this.size, 1 ); 59537 59538 this.lookAt( this.plane.normal ); 59539 59540 this.translateZ( - this.plane.constant ); 59541 59542 super.updateMatrixWorld( force ); 59543 59544 } 59545 59546 /** 59547 * Updates the helper to match the position and direction of the 59548 * light being visualized. 59549 */ 59550 dispose() { 59551 59552 super.dispose(); 59553 59554 this.geometry.dispose(); 59555 this.material.dispose(); 59556 this.children[ 0 ].geometry.dispose(); 59557 this.children[ 0 ].material.dispose(); 59558 59559 } 59560 59561} 59562 59563const _axis = /*@__PURE__*/ new Vector3(); 59564let _lineGeometry, _coneGeometry; 59565 59566/** 59567 * An 3D arrow object for visualizing directions. 59568 * 59569 * ```js 59570 * const dir = new THREE.Vector3( 1, 2, 0 ); 59571 * 59572 * //normalize the direction vector (convert to vector of length 1) 59573 * dir.normalize(); 59574 * 59575 * const origin = new THREE.Vector3( 0, 0, 0 ); 59576 * const length = 1; 59577 * const hex = 0xffff00; 59578 * 59579 * const arrowHelper = new THREE.ArrowHelper( dir, origin, length, hex ); 59580 * scene.add( arrowHelper ); 59581 * ``` 59582 * 59583 * @augments Object3D 59584 */ 59585class ArrowHelper extends Object3D { 59586 59587 /** 59588 * Constructs a new arrow helper. 59589 * 59590 * @param {Vector3} [dir=(0, 0, 1)] - The (normalized) direction vector. 59591 * @param {Vector3} [origin=(0, 0, 0)] - Point at which the arrow starts. 59592 * @param {number} [length=1] - Length of the arrow in world units. 59593 * @param {(number|Color|string)} [color=0xffff00] - Color of the arrow. 59594 * @param {number} [headLength=length*0.2] - The length of the head of the arrow. 59595 * @param {number} [headWidth=headLength*0.2] - The width of the head of the arrow. 59596 */ 59597 constructor( dir = new Vector3( 0, 0, 1 ), origin = new Vector3( 0, 0, 0 ), length = 1, color = 0xffff00, headLength = length * 0.2, headWidth = headLength * 0.2 ) { 59598 59599 super(); 59600 59601 this.type = 'ArrowHelper'; 59602 59603 if ( _lineGeometry === undefined ) { 59604 59605 _lineGeometry = new BufferGeometry(); 59606 _lineGeometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) ); 59607 59608 _coneGeometry = new ConeGeometry( 0.5, 1, 5, 1 ); 59609 _coneGeometry.translate( 0, -0.5, 0 ); 59610 59611 } 59612 59613 this.position.copy( origin ); 59614 59615 /** 59616 * The line part of the arrow helper. 59617 * 59618 * @type {Line} 59619 */ 59620 this.line = new Line( _lineGeometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); 59621 this.line.matrixAutoUpdate = false; 59622 this.add( this.line ); 59623 59624 /** 59625 * The cone part of the arrow helper. 59626 * 59627 * @type {Mesh} 59628 */ 59629 this.cone = new Mesh( _coneGeometry, new MeshBasicMaterial( { color: color, toneMapped: false } ) ); 59630 this.cone.matrixAutoUpdate = false; 59631 this.add( this.cone ); 59632 59633 this.setDirection( dir ); 59634 this.setLength( length, headLength, headWidth ); 59635 59636 } 59637 59638 /** 59639 * Sets the direction of the helper. 59640 * 59641 * @param {Vector3} dir - The normalized direction vector. 59642 */ 59643 setDirection( dir ) { 59644 59645 // dir is assumed to be normalized 59646 59647 if ( dir.y > 0.99999 ) { 59648 59649 this.quaternion.set( 0, 0, 0, 1 ); 59650 59651 } else if ( dir.y < -0.99999 ) { 59652 59653 this.quaternion.set( 1, 0, 0, 0 ); 59654 59655 } else { 59656 59657 _axis.set( dir.z, 0, - dir.x ).normalize(); 59658 59659 const radians = Math.acos( dir.y ); 59660 59661 this.quaternion.setFromAxisAngle( _axis, radians ); 59662 59663 } 59664 59665 } 59666 59667 /** 59668 * Sets the length of the helper. 59669 * 59670 * @param {number} length - Length of the arrow in world units. 59671 * @param {number} [headLength=length*0.2] - The length of the head of the arrow. 59672 * @param {number} [headWidth=headLength*0.2] - The width of the head of the arrow. 59673 */ 59674 setLength( length, headLength = length * 0.2, headWidth = headLength * 0.2 ) { 59675 59676 this.line.scale.set( 1, Math.max( 0.0001, length - headLength ), 1 ); // see #17458 59677 this.line.updateMatrix(); 59678 59679 this.cone.scale.set( headWidth, headLength, headWidth ); 59680 this.cone.position.y = length; 59681 this.cone.updateMatrix(); 59682 59683 } 59684 59685 /** 59686 * Sets the color of the helper. 59687 * 59688 * @param {number|Color|string} color - The color to set. 59689 */ 59690 setColor( color ) { 59691 59692 this.line.material.color.set( color ); 59693 this.cone.material.color.set( color ); 59694 59695 } 59696 59697 copy( source ) { 59698 59699 super.copy( source, false ); 59700 59701 this.line.copy( source.line ); 59702 this.cone.copy( source.cone ); 59703 59704 return this; 59705 59706 } 59707 59708 /** 59709 * Frees the GPU-related resources allocated by this instance. Call this 59710 * method whenever this instance is no longer used in your app. 59711 */ 59712 dispose() { 59713 59714 super.dispose(); 59715 59716 this.line.geometry.dispose(); 59717 this.line.material.dispose(); 59718 this.cone.geometry.dispose(); 59719 this.cone.material.dispose(); 59720 59721 } 59722 59723} 59724 59725/** 59726 * An axis object to visualize the 3 axes in a simple way. 59727 * The X axis is red. The Y axis is green. The Z axis is blue. 59728 * 59729 * ```js 59730 * const axesHelper = new THREE.AxesHelper( 5 ); 59731 * scene.add( axesHelper ); 59732 * ``` 59733 * 59734 * @augments LineSegments 59735 */ 59736class AxesHelper extends LineSegments { 59737 59738 /** 59739 * Constructs a new axes helper. 59740 * 59741 * @param {number} [size=1] - Size of the lines representing the axes. 59742 */ 59743 constructor( size = 1 ) { 59744 59745 const vertices = [ 59746 0, 0, 0, size, 0, 0, 59747 0, 0, 0, 0, size, 0, 59748 0, 0, 0, 0, 0, size 59749 ]; 59750 59751 const colors = [ 59752 1, 0, 0, 1, 0.6, 0, 59753 0, 1, 0, 0.6, 1, 0, 59754 0, 0, 1, 0, 0.6, 1 59755 ]; 59756 59757 const geometry = new BufferGeometry(); 59758 geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); 59759 geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); 59760 59761 const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); 59762 59763 super( geometry, material ); 59764 59765 this.type = 'AxesHelper'; 59766 59767 } 59768 59769 /** 59770 * Defines the colors of the axes helper. 59771 * 59772 * @param {number|Color|string} xAxisColor - The color for the x axis. 59773 * @param {number|Color|string} yAxisColor - The color for the y axis. 59774 * @param {number|Color|string} zAxisColor - The color for the z axis. 59775 * @return {AxesHelper} A reference to this axes helper. 59776 */ 59777 setColors( xAxisColor, yAxisColor, zAxisColor ) { 59778 59779 const color = new Color(); 59780 const array = this.geometry.attributes.color.array; 59781 59782 color.set( xAxisColor ); 59783 color.toArray( array, 0 ); 59784 color.toArray( array, 3 ); 59785 59786 color.set( yAxisColor ); 59787 color.toArray( array, 6 ); 59788 color.toArray( array, 9 ); 59789 59790 color.set( zAxisColor ); 59791 color.toArray( array, 12 ); 59792 color.toArray( array, 15 ); 59793 59794 this.geometry.attributes.color.needsUpdate = true; 59795 59796 return this; 59797 59798 } 59799 59800 /** 59801 * Frees the GPU-related resources allocated by this instance. Call this 59802 * method whenever this instance is no longer used in your app. 59803 */ 59804 dispose() { 59805 59806 super.dispose(); 59807 59808 this.geometry.dispose(); 59809 this.material.dispose(); 59810 59811 } 59812 59813} 59814 59815/** 59816 * This class is used to convert a series of paths to an array of 59817 * shapes. It is specifically used in context of fonts and SVG. 59818 */ 59819class ShapePath { 59820 59821 /** 59822 * Constructs a new shape path. 59823 */ 59824 constructor() { 59825 59826 this.type = 'ShapePath'; 59827 59828 /** 59829 * The color of the shape. 59830 * 59831 * @type {Color} 59832 */ 59833 this.color = new Color(); 59834 59835 /** 59836 * The paths that have been generated for this shape. 59837 * 59838 * @type {Array<Path>} 59839 * @default null 59840 */ 59841 this.subPaths = []; 59842 59843 /** 59844 * The current path that is being generated. 59845 * 59846 * @type {?Path} 59847 * @default null 59848 */ 59849 this.currentPath = null; 59850 59851 /** 59852 * An object that can be used to store custom data about the shape path. 59853 * Mainly used by SVGLoader to store style information. 59854 * 59855 * @type {Object} 59856 */ 59857 this.userData = {}; 59858 59859 } 59860 59861 /** 59862 * Creates a new path and moves it current point to the given one. 59863 * 59864 * @param {number} x - The x coordinate. 59865 * @param {number} y - The y coordinate. 59866 * @return {ShapePath} A reference to this shape path. 59867 */ 59868 moveTo( x, y ) { 59869 59870 this.currentPath = new Path(); 59871 this.subPaths.push( this.currentPath ); 59872 this.currentPath.moveTo( x, y ); 59873 59874 return this; 59875 59876 } 59877 59878 /** 59879 * Adds an instance of {@link LineCurve} to the path by connecting 59880 * the current point with the given one. 59881 * 59882 * @param {number} x - The x coordinate of the end point. 59883 * @param {number} y - The y coordinate of the end point. 59884 * @return {ShapePath} A reference to this shape path. 59885 */ 59886 lineTo( x, y ) { 59887 59888 this.currentPath.lineTo( x, y ); 59889 59890 return this; 59891 59892 } 59893 59894 /** 59895 * Adds an instance of {@link QuadraticBezierCurve} to the path by connecting 59896 * the current point with the given one. 59897 * 59898 * @param {number} aCPx - The x coordinate of the control point. 59899 * @param {number} aCPy - The y coordinate of the control point. 59900 * @param {number} aX - The x coordinate of the end point. 59901 * @param {number} aY - The y coordinate of the end point. 59902 * @return {ShapePath} A reference to this shape path. 59903 */ 59904 quadraticCurveTo( aCPx, aCPy, aX, aY ) { 59905 59906 this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); 59907 59908 return this; 59909 59910 } 59911 59912 /** 59913 * Adds an instance of {@link CubicBezierCurve} to the path by connecting 59914 * the current point with the given one. 59915 * 59916 * @param {number} aCP1x - The x coordinate of the first control point. 59917 * @param {number} aCP1y - The y coordinate of the first control point. 59918 * @param {number} aCP2x - The x coordinate of the second control point. 59919 * @param {number} aCP2y - The y coordinate of the second control point. 59920 * @param {number} aX - The x coordinate of the end point. 59921 * @param {number} aY - The y coordinate of the end point. 59922 * @return {ShapePath} A reference to this shape path. 59923 */ 59924 bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { 59925 59926 this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); 59927 59928 return this; 59929 59930 } 59931 59932 /** 59933 * Adds an instance of {@link SplineCurve} to the path by connecting 59934 * the current point with the given list of points. 59935 * 59936 * @param {Array<Vector2>} pts - An array of points in 2D space. 59937 * @return {ShapePath} A reference to this shape path. 59938 */ 59939 splineThru( pts ) { 59940 59941 this.currentPath.splineThru( pts ); 59942 59943 return this; 59944 59945 } 59946 59947 /** 59948 * Converts the paths into an array of shapes. 59949 * 59950 * @return {Array<Shape>} An array of shapes. 59951 */ 59952 toShapes() { 59953 59954 // Point-in-polygon test using the even-odd ray-casting rule. Valid for 59955 // simple (non self-intersecting) polygons. 59956 function pointInPolygon( p, polygon ) { 59957 59958 let inside = false; 59959 const n = polygon.length; 59960 59961 for ( let i = 0, j = n - 1; i < n; j = i ++ ) { 59962 59963 const a = polygon[ i ]; 59964 const b = polygon[ j ]; 59965 59966 if ( ( a.y > p.y ) !== ( b.y > p.y ) && 59967 p.x < ( b.x - a.x ) * ( p.y - a.y ) / ( b.y - a.y ) + a.x ) { 59968 59969 inside = ! inside; 59970 59971 } 59972 59973 } 59974 59975 return inside; 59976 59977 } 59978 59979 // Returns a point guaranteed to be strictly inside the given simple 59980 // polygon. First tries the bounding-box center; if that falls outside 59981 // the polygon, casts a horizontal ray at the center's y and picks the 59982 // midpoint between the first two sorted intercepts. 59983 // 59984 // Port of paper.js' Path#getInteriorPoint() 59985 // https://github.com/paperjs/paper.js/blob/develop/src/path/PathItem.Boolean.js 59986 function getInteriorPoint( polygon, boundingBox ) { 59987 59988 const point = boundingBox.getCenter( new Vector2() ); 59989 59990 if ( pointInPolygon( point, polygon ) ) return point; 59991 59992 const y = point.y; 59993 const intercepts = []; 59994 const n = polygon.length; 59995 59996 for ( let i = 0; i < n; i ++ ) { 59997 59998 const a = polygon[ i ]; 59999 const b = polygon[ ( i + 1 ) % n ]; 60000 60001 // Half-open crossing rule â counts each vertex exactly once and 60002 // skips horizontal edges. 60003 if ( ( a.y > y ) !== ( b.y > y ) ) { 60004 60005 const x = a.x + ( y - a.y ) * ( b.x - a.x ) / ( b.y - a.y ); 60006 intercepts.push( x ); 60007 60008 } 60009 60010 } 60011 60012 if ( intercepts.length > 1 ) { 60013 60014 intercepts.sort( ( a, b ) => a - b ); 60015 point.x = ( intercepts[ 0 ] + intercepts[ 1 ] ) / 2; 60016 60017 } 60018 60019 return point; 60020 60021 } 60022 60023 // Resolve fill-rule. Defaults to 'nonzero'. 60024 let fillRule = ( this.userData.style && this.userData.style.fillRule ) || 'nonzero'; 60025 60026 if ( fillRule !== 'nonzero' && fillRule !== 'evenodd' ) { 60027 60028 warn( 'Fill-rule "' + fillRule + '" is not supported, falling back to "nonzero".' ); 60029 fillRule = 'nonzero'; 60030 60031 } 60032 60033 // Predicate that decides whether a winding number falls inside the fill 60034 // region, per the SVG fill-rule spec. Works for negative windings too, 60035 // because JavaScript's bitwise AND preserves odd/even under two's 60036 // complement. 60037 const isInside = fillRule === 'nonzero' 60038 ? ( w => w !== 0 ) 60039 : ( w => ( w & 1 ) !== 0 ); 60040 60041 // Build an entry per usable subpath. Self-winding follows the standard 60042 // convention used by ShapeUtils: counter-clockwise (signed area > 0) 60043 // contributes +1 to the winding number at an interior point, 60044 // clockwise contributes -1. 60045 const entries = []; 60046 60047 for ( const subPath of this.subPaths ) { 60048 60049 const points = subPath.getPoints(); 60050 if ( points.length < 3 ) continue; 60051 60052 const area = ShapeUtils.area( points ); 60053 if ( area === 0 ) continue; 60054 60055 const boundingBox = new Box2(); 60056 for ( let i = 0; i < points.length; i ++ ) boundingBox.expandByPoint( points[ i ] ); 60057 60058 entries.push( { 60059 subPath: subPath, 60060 points: points, 60061 boundingBox: boundingBox, 60062 interiorPoint: getInteriorPoint( points, boundingBox ), 60063 absArea: Math.abs( area ), 60064 winding: area < 0 ? -1 : 1, 60065 container: null, 60066 exclude: false, 60067 role: null 60068 } ); 60069 60070 } 60071 60072 // Sort by area descending. This guarantees that any subpath that could 60073 // contain `entries[i]` is located at a smaller index and has already 60074 // been processed when it's entries[i]'s turn. Port of paper.js' 60075 // reorientPaths() algorithm. 60076 entries.sort( ( a, b ) => b.absArea - a.absArea ); 60077 60078 // Walk already-processed entries from closest-in-size to largest, 60079 // stopping at the innermost container. Accumulate the container's 60080 // cumulative winding into this entry's winding so that the final value 60081 // equals the winding number at this entry's interior point. 60082 // 60083 // A subpath only contributes to the fill boundary when crossing it 60084 // actually flips the "insideness" per the fill rule; otherwise it's a 60085 // redundant overlap and gets excluded to avoid double-counting. 60086 for ( let i = 0; i < entries.length; i ++ ) { 60087 60088 const entry = entries[ i ]; 60089 let containerWinding = 0; 60090 60091 for ( let j = i - 1; j >= 0; j -- ) { 60092 60093 const candidate = entries[ j ]; 60094 60095 if ( ! candidate.boundingBox.containsBox( entry.boundingBox ) ) continue; 60096 if ( ! pointInPolygon( entry.interiorPoint, candidate.points ) ) continue; 60097 60098 entry.container = candidate.exclude ? candidate.container : candidate; 60099 containerWinding = candidate.winding; 60100 entry.winding += containerWinding; 60101 break; 60102 60103 } 60104 60105 if ( isInside( entry.winding ) === isInside( containerWinding ) ) { 60106 60107 entry.exclude = true; 60108 60109 } 60110 60111 } 60112 60113 // Classify retained entries. An entry is an outer shape if it has no 60114 // container or if its container is itself a hole (a solid nested inside 60115 // a hole becomes a new top-level shape); otherwise it's a hole in its 60116 // container. Entries were already sorted outermost-first, so each 60117 // container's role is known by the time we look at it. 60118 for ( const entry of entries ) { 60119 60120 if ( entry.exclude ) continue; 60121 entry.role = ( entry.container === null || entry.container.role === 'hole' ) ? 'outer' : 'hole'; 60122 60123 } 60124 60125 // Build Shapes for outers first, then attach holes to their container's 60126 // Shape. 60127 const shapes = []; 60128 const shapeByEntry = new Map(); 60129 60130 for ( const entry of entries ) { 60131 60132 if ( entry.exclude || entry.role !== 'outer' ) continue; 60133 60134 const shape = new Shape(); 60135 shape.curves = entry.subPath.curves; 60136 shapes.push( shape ); 60137 shapeByEntry.set( entry, shape ); 60138 60139 } 60140 60141 for ( const entry of entries ) { 60142 60143 if ( entry.exclude || entry.role !== 'hole' ) continue; 60144 60145 const shape = shapeByEntry.get( entry.container ); 60146 if ( ! shape ) continue; 60147 60148 const hole = new Path(); 60149 hole.curves = entry.subPath.curves; 60150 shape.holes.push( hole ); 60151 60152 } 60153 60154 return shapes; 60155 60156 60157 } 60158 60159} 60160 60161/** 60162 * Abstract base class for controls. 60163 * 60164 * @abstract 60165 * @augments EventDispatcher 60166 */ 60167class Controls extends EventDispatcher { 60168 60169 /** 60170 * Constructs a new controls instance. 60171 * 60172 * @param {Object3D} object - The object that is managed by the controls. 60173 * @param {?HTMLElement} domElement - The HTML element used for event listeners. 60174 */ 60175 constructor( object, domElement = null ) { 60176 60177 super(); 60178 60179 /** 60180 * The object that is managed by the controls. 60181 * 60182 * @type {Object3D} 60183 */ 60184 this.object = object; 60185 60186 /** 60187 * The HTML element used for event listeners. 60188 * 60189 * @type {?HTMLElement} 60190 * @default null 60191 */ 60192 this.domElement = domElement; 60193 60194 /** 60195 * Whether the controls responds to user input or not. 60196 * 60197 * @type {boolean} 60198 * @default true 60199 */ 60200 this.enabled = true; 60201 60202 /** 60203 * The internal state of the controls. 60204 * 60205 * @type {number} 60206 * @default -1 60207 */ 60208 this.state = -1; 60209 60210 /** 60211 * This object defines the keyboard input of the controls. 60212 * 60213 * @type {Object} 60214 */ 60215 this.keys = {}; 60216 60217 /** 60218 * This object defines what type of actions are assigned to the available mouse buttons. 60219 * It depends on the control implementation what kind of mouse buttons and actions are supported. 60220 * 60221 * @type {{LEFT: ?number, MIDDLE: ?number, RIGHT: ?number}} 60222 */ 60223 this.mouseButtons = { LEFT: null, MIDDLE: null, RIGHT: null }; 60224 60225 /** 60226 * This object defines what type of actions are assigned to what kind of touch interaction. 60227 * It depends on the control implementation what kind of touch interaction and actions are supported. 60228 * 60229 * @type {{ONE: ?number, TWO: ?number}} 60230 */ 60231 this.touches = { ONE: null, TWO: null }; 60232 60233 } 60234 60235 /** 60236 * Connects the controls to the DOM. This method has so called "side effects" since 60237 * it adds the module's event listeners to the DOM. 60238 * 60239 * @param {HTMLElement} element - The DOM element to connect to. 60240 */ 60241 connect( element ) { 60242 60243 if ( this.domElement !== null ) this.disconnect(); 60244 60245 this.domElement = element; 60246 60247 } 60248 60249 /** 60250 * Disconnects the controls from the DOM. 60251 */ 60252 disconnect() {} 60253 60254 /** 60255 * Call this method if you no longer want use to the controls. It frees all internal 60256 * resources and removes all event listeners. 60257 */ 60258 dispose() {} 60259 60260 /** 60261 * Controls should implement this method if they have to update their internal state 60262 * per simulation step. 60263 * 60264 * @param {number} [delta] - The time delta in seconds. 60265 */ 60266 update( /* delta */ ) {} 60267 60268} 60269 60270/** 60271 * Scales the texture as large as possible within its surface without cropping 60272 * or stretching the texture. The method preserves the original aspect ratio of 60273 * the texture. Akin to CSS `object-fit: contain` 60274 * 60275 * @param {Texture} texture - The texture. 60276 * @param {number} aspect - The texture's aspect ratio. 60277 * @return {Texture} The updated texture. 60278 */ 60279function contain( texture, aspect ) { 60280 60281 const imageAspect = ( texture.image && texture.image.width ) ? texture.image.width / texture.image.height : 1; 60282 60283 if ( imageAspect > aspect ) { 60284 60285 texture.repeat.x = 1; 60286 texture.repeat.y = imageAspect / aspect; 60287 60288 texture.offset.x = 0; 60289 texture.offset.y = ( 1 - texture.repeat.y ) / 2; 60290 60291 } else { 60292 60293 texture.repeat.x = aspect / imageAspect; 60294 texture.repeat.y = 1; 60295 60296 texture.offset.x = ( 1 - texture.repeat.x ) / 2; 60297 texture.offset.y = 0; 60298 60299 } 60300 60301 return texture; 60302 60303} 60304 60305/** 60306 * Scales the texture to the smallest possible size to fill the surface, leaving 60307 * no empty space. The method preserves the original aspect ratio of the texture. 60308 * Akin to CSS `object-fit: cover`. 60309 * 60310 * @param {Texture} texture - The texture. 60311 * @param {number} aspect - The texture's aspect ratio. 60312 * @return {Texture} The updated texture. 60313 */ 60314function cover( texture, aspect ) { 60315 60316 const imageAspect = ( texture.image && texture.image.width ) ? texture.image.width / texture.image.height : 1; 60317 60318 if ( imageAspect > aspect ) { 60319 60320 texture.repeat.x = aspect / imageAspect; 60321 texture.repeat.y = 1; 60322 60323 texture.offset.x = ( 1 - texture.repeat.x ) / 2; 60324 texture.offset.y = 0; 60325 60326 } else { 60327 60328 texture.repeat.x = 1; 60329 texture.repeat.y = imageAspect / aspect; 60330 60331 texture.offset.x = 0; 60332 texture.offset.y = ( 1 - texture.repeat.y ) / 2; 60333 60334 } 60335 60336 return texture; 60337 60338} 60339 60340/** 60341 * Configures the texture to the default transformation. Akin to CSS `object-fit: fill`. 60342 * 60343 * @param {Texture} texture - The texture. 60344 * @return {Texture} The updated texture. 60345 */ 60346function fill( texture ) { 60347 60348 texture.repeat.x = 1; 60349 texture.repeat.y = 1; 60350 60351 texture.offset.x = 0; 60352 texture.offset.y = 0; 60353 60354 return texture; 60355 60356} 60357 60358/** 60359 * Determines how many bytes must be used to represent the texture. 60360 * 60361 * @param {number} width - The width of the texture. 60362 * @param {number} height - The height of the texture. 60363 * @param {number} format - The texture's format. 60364 * @param {number} type - The texture's type. 60365 * @return {number} The byte length. 60366 */ 60367function getByteLength( width, height, format, type ) { 60368 60369 const typeByteLength = getTextureTypeByteLength( type ); 60370 60371 switch ( format ) { 60372 60373 // https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml 60374 case AlphaFormat: 60375 return width * height; 60376 case RedFormat: 60377 return ( ( width * height ) / typeByteLength.components ) * typeByteLength.byteLength; 60378 case RedIntegerFormat: 60379 return ( ( width * height ) / typeByteLength.components ) * typeByteLength.byteLength; 60380 case RGFormat: 60381 return ( ( width * height * 2 ) / typeByteLength.components ) * typeByteLength.byteLength; 60382 case RGIntegerFormat: 60383 return ( ( width * height * 2 ) / typeByteLength.components ) * typeByteLength.byteLength; 60384 case RGBFormat: 60385 return ( ( width * height * 3 ) / typeByteLength.components ) * typeByteLength.byteLength; 60386 case RGBAFormat: 60387 return ( ( width * height * 4 ) / typeByteLength.components ) * typeByteLength.byteLength; 60388 case RGBAIntegerFormat: 60389 return ( ( width * height * 4 ) / typeByteLength.components ) * typeByteLength.byteLength; 60390 60391 // https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_s3tc_srgb/ 60392 case RGB_S3TC_DXT1_Format: 60393 case RGBA_S3TC_DXT1_Format: 60394 return Math.floor( ( width + 3 ) / 4 ) * Math.floor( ( height + 3 ) / 4 ) * 8; 60395 case RGBA_S3TC_DXT3_Format: 60396 case RGBA_S3TC_DXT5_Format: 60397 return Math.floor( ( width + 3 ) / 4 ) * Math.floor( ( height + 3 ) / 4 ) * 16; 60398 60399 // https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_pvrtc/ 60400 case RGB_PVRTC_2BPPV1_Format: 60401 case RGBA_PVRTC_2BPPV1_Format: 60402 return ( Math.max( width, 16 ) * Math.max( height, 8 ) ) / 4; 60403 case RGB_PVRTC_4BPPV1_Format: 60404 case RGBA_PVRTC_4BPPV1_Format: 60405 return ( Math.max( width, 8 ) * Math.max( height, 8 ) ) / 2; 60406 60407 // https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_etc/ 60408 case RGB_ETC1_Format: 60409 case RGB_ETC2_Format: 60410 case R11_EAC_Format: 60411 case SIGNED_R11_EAC_Format: 60412 return Math.floor( ( width + 3 ) / 4 ) * Math.floor( ( height + 3 ) / 4 ) * 8; 60413 case RGBA_ETC2_EAC_Format: 60414 case RG11_EAC_Format: 60415 case SIGNED_RG11_EAC_Format: 60416 return Math.floor( ( width + 3 ) / 4 ) * Math.floor( ( height + 3 ) / 4 ) * 16; 60417 60418 // https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_astc/ 60419 case RGBA_ASTC_4x4_Format: 60420 return Math.floor( ( width + 3 ) / 4 ) * Math.floor( ( height + 3 ) / 4 ) * 16; 60421 case RGBA_ASTC_5x4_Format: 60422 return Math.floor( ( width + 4 ) / 5 ) * Math.floor( ( height + 3 ) / 4 ) * 16; 60423 case RGBA_ASTC_5x5_Format: 60424 return Math.floor( ( width + 4 ) / 5 ) * Math.floor( ( height + 4 ) / 5 ) * 16; 60425 case RGBA_ASTC_6x5_Format: 60426 return Math.floor( ( width + 5 ) / 6 ) * Math.floor( ( height + 4 ) / 5 ) * 16; 60427 case RGBA_ASTC_6x6_Format: 60428 return Math.floor( ( width + 5 ) / 6 ) * Math.floor( ( height + 5 ) / 6 ) * 16; 60429 case RGBA_ASTC_8x5_Format: 60430 return Math.floor( ( width + 7 ) / 8 ) * Math.floor( ( height + 4 ) / 5 ) * 16; 60431 case RGBA_ASTC_8x6_Format: 60432 return Math.floor( ( width + 7 ) / 8 ) * Math.floor( ( height + 5 ) / 6 ) * 16; 60433 case RGBA_ASTC_8x8_Format: 60434 return Math.floor( ( width + 7 ) / 8 ) * Math.floor( ( height + 7 ) / 8 ) * 16; 60435 case RGBA_ASTC_10x5_Format: 60436 return Math.floor( ( width + 9 ) / 10 ) * Math.floor( ( height + 4 ) / 5 ) * 16; 60437 case RGBA_ASTC_10x6_Format: 60438 return Math.floor( ( width + 9 ) / 10 ) * Math.floor( ( height + 5 ) / 6 ) * 16; 60439 case RGBA_ASTC_10x8_Format: 60440 return Math.floor( ( width + 9 ) / 10 ) * Math.floor( ( height + 7 ) / 8 ) * 16; 60441 case RGBA_ASTC_10x10_Format: 60442 return Math.floor( ( width + 9 ) / 10 ) * Math.floor( ( height + 9 ) / 10 ) * 16; 60443 case RGBA_ASTC_12x10_Format: 60444 return Math.floor( ( width + 11 ) / 12 ) * Math.floor( ( height + 9 ) / 10 ) * 16; 60445 case RGBA_ASTC_12x12_Format: 60446 return Math.floor( ( width + 11 ) / 12 ) * Math.floor( ( height + 11 ) / 12 ) * 16; 60447 60448 // https://registry.khronos.org/webgl/extensions/EXT_texture_compression_bptc/ 60449 case RGBA_BPTC_Format: 60450 case RGB_BPTC_SIGNED_Format: 60451 case RGB_BPTC_UNSIGNED_Format: 60452 return Math.ceil( width / 4 ) * Math.ceil( height / 4 ) * 16; 60453 60454 // https://registry.khronos.org/webgl/extensions/EXT_texture_compression_rgtc/ 60455 case RED_RGTC1_Format: 60456 case SIGNED_RED_RGTC1_Format: 60457 return Math.ceil( width / 4 ) * Math.ceil( height / 4 ) * 8; 60458 case RED_GREEN_RGTC2_Format: 60459 case SIGNED_RED_GREEN_RGTC2_Format: 60460 return Math.ceil( width / 4 ) * Math.ceil( height / 4 ) * 16; 60461 60462 } 60463 60464 throw new Error( 60465 `Unable to determine texture byte length for ${format} format.`, 60466 ); 60467 60468} 60469 60470function getTextureTypeByteLength( type ) { 60471 60472 switch ( type ) { 60473 60474 case UnsignedByteType: 60475 case ByteType: 60476 return { byteLength: 1, components: 1 }; 60477 case UnsignedShortType: 60478 case ShortType: 60479 case HalfFloatType: 60480 return { byteLength: 2, components: 1 }; 60481 case UnsignedShort4444Type: 60482 case UnsignedShort5551Type: 60483 return { byteLength: 2, components: 4 }; 60484 case UnsignedIntType: 60485 case IntType: 60486 case FloatType: 60487 return { byteLength: 4, components: 1 }; 60488 case UnsignedInt5999Type: 60489 case UnsignedInt101111Type: 60490 return { byteLength: 4, components: 3 }; 60491 60492 } 60493 60494 throw new Error( `THREE.TextureUtils: Unknown texture type ${type}.` ); 60495 60496} 60497 60498/** 60499 * A class containing utility functions for textures. 60500 * 60501 * @hideconstructor 60502 */ 60503class TextureUtils { 60504 60505 /** 60506 * Scales the texture as large as possible within its surface without cropping 60507 * or stretching the texture. The method preserves the original aspect ratio of 60508 * the texture. Akin to CSS `object-fit: contain` 60509 * 60510 * @param {Texture} texture - The texture. 60511 * @param {number} aspect - The texture's aspect ratio. 60512 * @return {Texture} The updated texture. 60513 */ 60514 static contain( texture, aspect ) { 60515 60516 return contain( texture, aspect ); 60517 60518 } 60519 60520 /** 60521 * Scales the texture to the smallest possible size to fill the surface, leaving 60522 * no empty space. The method preserves the original aspect ratio of the texture. 60523 * Akin to CSS `object-fit: cover`. 60524 * 60525 * @param {Texture} texture - The texture. 60526 * @param {number} aspect - The texture's aspect ratio. 60527 * @return {Texture} The updated texture. 60528 */ 60529 static cover( texture, aspect ) { 60530 60531 return cover( texture, aspect ); 60532 60533 } 60534 60535 /** 60536 * Configures the texture to the default transformation. Akin to CSS `object-fit: fill`. 60537 * 60538 * @param {Texture} texture - The texture. 60539 * @return {Texture} The updated texture. 60540 */ 60541 static fill( texture ) { 60542 60543 return fill( texture ); 60544 60545 } 60546 60547 /** 60548 * Determines how many bytes must be used to represent the texture. 60549 * 60550 * @param {number} width - The width of the texture. 60551 * @param {number} height - The height of the texture. 60552 * @param {number} format - The texture's format. 60553 * @param {number} type - The texture's type. 60554 * @return {number} The byte length. 60555 */ 60556 static getByteLength( width, height, format, type ) { 60557 60558 return getByteLength( width, height, format, type ); 60559 60560 } 60561 60562} 60563 60564if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { 60565 60566 __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'register', { detail: { 60567 revision: REVISION, 60568 } } ) ); 60569 60570} 60571 60572if ( typeof window !== 'undefined' ) { 60573 60574 if ( window.__THREE__ ) { 60575 60576 warn( 'WARNING: Multiple instances of Three.js being imported.' ); 60577 60578 } else { 60579 60580 window.__THREE__ = REVISION; 60581 60582 } 60583 60584} 60585 60586export { ACESFilmicToneMapping, AddEquation, AddOperation, AdditiveAnimationBlendMode, AdditiveBlending, AgXToneMapping, AlphaFormat, AlwaysCompare, AlwaysDepth, AlwaysStencilFunc, AmbientLight, AnimationAction, AnimationClip, AnimationLoader, AnimationMixer, AnimationObjectGroup, AnimationUtils, ArcCurve, ArrayCamera, ArrowHelper, AttachedBindMode, Audio, AudioAnalyser, AudioContext, AudioListener, AudioLoader, AxesHelper, BackSide, BasicDepthPacking, BasicShadowMap, BatchedMesh, BezierInterpolant, Bone, BooleanKeyframeTrack, Box2, Box3, Box3Helper, BoxGeometry, BoxHelper, BufferAttribute, BufferGeometry, BufferGeometryLoader, ByteType, Cache, Camera, CameraHelper, CanvasTexture, CapsuleGeometry, CatmullRomCurve3, CineonToneMapping, CircleGeometry, ClampToEdgeWrapping, Clock, Color, ColorKeyframeTrack, ColorManagement, Compatibility, CompressedArrayTexture, CompressedCubeTexture, CompressedTexture, CompressedTextureLoader, ConeGeometry, ConstantAlphaFactor, ConstantColorFactor, Controls, CubeCamera, CubeDepthTexture, CubeReflectionMapping, CubeRefractionMapping, CubeTexture, CubeTextureLoader, CubeUVReflectionMapping, CubicBezierCurve, CubicBezierCurve3, CubicInterpolant, CullFaceBack, CullFaceFront, CullFaceFrontBack, CullFaceNone, Curve, CurvePath, CustomBlending, CustomToneMapping, CylinderGeometry, Cylindrical, Data3DTexture, DataArrayTexture, DataTexture, DataTextureLoader, DataUtils, DecrementStencilOp, DecrementWrapStencilOp, DefaultLoadingManager, DepthFormat, DepthStencilFormat, DepthTexture, DetachedBindMode, DirectionalLight, DirectionalLightHelper, DiscreteInterpolant, DodecahedronGeometry, DoubleSide, DstAlphaFactor, DstColorFactor, DynamicCopyUsage, DynamicDrawUsage, DynamicReadUsage, EdgesGeometry, EllipseCurve, EqualCompare, EqualDepth, EqualStencilFunc, EquirectangularReflectionMapping, EquirectangularRefractionMapping, Euler, EventDispatcher, ExternalTexture, ExtrudeGeometry, FileLoader, Float16BufferAttribute, Float32BufferAttribute, FloatType, Fog, FogExp2, FramebufferTexture, FrontSide, Frustum, FrustumArray, GLBufferAttribute, GLSL1, GLSL3, GreaterCompare, GreaterDepth, GreaterEqualCompare, GreaterEqualDepth, GreaterEqualStencilFunc, GreaterStencilFunc, GridHelper, Group, HTMLTexture, HalfFloatType, HemisphereLight, HemisphereLightHelper, IcosahedronGeometry, ImageBitmapLoader, ImageLoader, ImageUtils, IncrementStencilOp, IncrementWrapStencilOp, InstancedBufferAttribute, InstancedBufferGeometry, InstancedInterleavedBuffer, InstancedMesh, Int16BufferAttribute, Int32BufferAttribute, Int8BufferAttribute, IntType, InterleavedBuffer, InterleavedBufferAttribute, Interpolant, InterpolateBezier, InterpolateDiscrete, InterpolateLinear, InterpolateSmooth, InterpolationSamplingMode, InterpolationSamplingType, InvertStencilOp, KeepStencilOp, KeyframeTrack, LOD, LatheGeometry, Layers, LessCompare, LessDepth, LessEqualCompare, LessEqualDepth, LessEqualStencilFunc, LessStencilFunc, Light, LightProbe, LightShadow, Line, Line3, LineBasicMaterial, LineCurve, LineCurve3, LineDashedMaterial, LineLoop, LineSegments, LinearFilter, LinearInterpolant, LinearMipMapLinearFilter, LinearMipMapNearestFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, LinearSRGBColorSpace, LinearToneMapping, LinearTransfer, Loader, LoaderUtils, LoadingManager, LoopOnce, LoopPingPong, LoopRepeat, MOUSE, Material, MaterialBlending, MaterialLoader, MathUtils, Matrix2, Matrix3, Matrix4, MaxEquation, Mesh, MeshBasicMaterial, MeshDepthMaterial, MeshDistanceMaterial, MeshLambertMaterial, MeshMatcapMaterial, MeshNormalMaterial, MeshPhongMaterial, MeshPhysicalMaterial, MeshStandardMaterial, MeshToonMaterial, MinEquation, MirroredRepeatWrapping, MixOperation, MultiplyBlending, MultiplyOperation, NearestFilter, NearestMipMapLinearFilter, NearestMipMapNearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, NeutralToneMapping, NeverCompare, NeverDepth, NeverStencilFunc, NoBlending, NoColorSpace, NoNormalPacking, NoToneMapping, NormalAnimationBlendMode, NormalBlending, NormalGAPacking, NormalRGPacking, NotEqualCompare, NotEqualDepth, NotEqualStencilFunc, NumberKeyframeTrack, Object3D, ObjectLoader, ObjectSpaceNormalMap, OctahedronGeometry, OneFactor, OneMinusConstantAlphaFactor, OneMinusConstantColorFactor, OneMinusDstAlphaFactor, OneMinusDstColorFactor, OneMinusSrcAlphaFactor, OneMinusSrcColorFactor, OrthographicCamera, PCFShadowMap, PCFSoftShadowMap, Path, PerspectiveCamera, Plane, PlaneGeometry, PlaneHelper, PointLight, PointLightHelper, Points, PointsMaterial, PolarGridHelper, PolyhedronGeometry, PositionalAudio, PropertyBinding, PropertyMixer, QuadraticBezierCurve, QuadraticBezierCurve3, Quaternion, QuaternionKeyframeTrack, QuaternionLinearInterpolant, R11_EAC_Format, RAD2DEG, RED_GREEN_RGTC2_Format, RED_RGTC1_Format, REVISION, RG11_EAC_Format, RGBADepthPacking, RGBAFormat, RGBAIntegerFormat, RGBA_ASTC_10x10_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_BPTC_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, RGBDepthPacking, RGBFormat, RGBIntegerFormat, RGB_BPTC_SIGNED_Format, RGB_BPTC_UNSIGNED_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGB_S3TC_DXT1_Format, RGDepthPacking, RGFormat, RGIntegerFormat, RawShaderMaterial, Ray, Raycaster, RectAreaLight, RedFormat, RedIntegerFormat, ReinhardToneMapping, RenderObjectRefreshType, RenderTarget, RenderTarget3D, RepeatWrapping, ReplaceStencilOp, ReverseSubtractEquation, ReversedDepthFuncs, RingGeometry, SIGNED_R11_EAC_Format, SIGNED_RED_GREEN_RGTC2_Format, SIGNED_RED_RGTC1_Format, SIGNED_RG11_EAC_Format, SRGBColorSpace, SRGBTransfer, Scene, ShaderMaterial, ShadowMaterial, Shape, ShapeGeometry, ShapePath, ShapeUtils, ShortType, Skeleton, SkeletonHelper, SkinnedMesh, Source, Sphere, SphereGeometry, Spherical, SphericalHarmonics3, SplineCurve, SpotLight, SpotLightHelper, Sprite, SpriteMaterial, SrcAlphaFactor, SrcAlphaSaturateFactor, SrcColorFactor, StaticCopyUsage, StaticDrawUsage, StaticReadUsage, StereoCamera, StreamCopyUsage, StreamDrawUsage, StreamReadUsage, StringKeyframeTrack, SubtractEquation, SubtractiveBlending, TOUCH, TangentSpaceNormalMap, TetrahedronGeometry, Texture, TextureLoader, TextureSource, TextureUtils, Timer, TimestampQuery, TorusGeometry, TorusKnotGeometry, Triangle, TriangleFanDrawMode, TriangleStripDrawMode, TrianglesDrawMode, TubeGeometry, UVMapping, Uint16BufferAttribute, Uint32BufferAttribute, Uint8BufferAttribute, Uint8ClampedBufferAttribute, Uniform, UniformsGroup, UniformsUtils, UnsignedByteType, UnsignedInt101111Type, UnsignedInt248Type, UnsignedInt5999Type, UnsignedIntType, UnsignedShort4444Type, UnsignedShort5551Type, UnsignedShortType, VSMShadowMap, Vector2, Vector3, Vector4, VectorKeyframeTrack, VideoFrameTexture, VideoTexture, WebGL3DRenderTarget, WebGLArrayRenderTarget, WebGLCoordinateSystem, WebGLRenderTarget, WebGPUCoordinateSystem, WebXRController, WireframeGeometry, WrapAroundEnding, ZeroCurvatureEnding, ZeroFactor, ZeroSlopeEnding, ZeroStencilOp, cloneUniforms, createCanvasElement, createElementNS, error, getByteLength, getConsoleFunction, getUnlitUniformColorSpace, isTypedArray, log, mergeUniforms, probeAsync, setConsoleFunction, warn, warnOnce, yieldToMain };
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.