impact-ui 5.0.1 → 5.0.2-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/index14.js +2 -2
- package/dist/_virtual/index15.js +2 -2
- package/dist/_virtual/index16.js +2 -2
- package/dist/_virtual/index3.js +2 -4
- package/dist/_virtual/index4.js +3 -3
- package/dist/_virtual/index5.js +4 -2
- package/dist/assets/animated-bar.lottie.js +4 -0
- package/dist/assets/animated-bulb.lottie.js +4 -0
- package/dist/assets/animated-chip.lottie.js +4 -0
- package/dist/assets/confetti-cone.lottie.js +4 -0
- package/dist/assets/halloween_devil_morning_final.html.js +1304 -0
- package/dist/assets/halloween_devil_night_Final.html.js +1299 -0
- package/dist/components/Accordion/index.js +1 -1
- package/dist/components/AccordionModern/SortableAccordionComp.d.ts +2 -2
- package/dist/components/AccordionModern/SortableAccordionComp.d.ts.map +1 -1
- package/dist/components/AccordionModern/SortableAccordionComp.js +2 -2
- package/dist/components/AccordionModern/index.d.ts +1 -1
- package/dist/components/AccordionModern/index.d.ts.map +1 -1
- package/dist/components/AccordionModern/index.js +34 -41
- package/dist/components/Alert/index.js +1 -1
- package/dist/components/Avatar/index.js +1 -1
- package/dist/components/Badge/filled/filledIconLabelBadges/index.d.ts +1 -1
- package/dist/components/Badge/filled/filledIconLabelBadges/index.d.ts.map +1 -1
- package/dist/components/Badge/filled/filledIconLabelBadges/index.js +1 -1
- package/dist/components/Badge/filled/filledOnlyIconBadges/index.d.ts +1 -1
- package/dist/components/Badge/filled/filledOnlyIconBadges/index.d.ts.map +1 -1
- package/dist/components/Badge/filled/filledOnlyIconBadges/index.js +1 -1
- package/dist/components/Badge/filled/filledOnlyLabelBadges/index.d.ts +1 -1
- package/dist/components/Badge/filled/filledOnlyLabelBadges/index.d.ts.map +1 -1
- package/dist/components/Badge/filled/filledOnlyLabelBadges/index.js +1 -1
- package/dist/components/Badge/filled/index.d.ts +1 -1
- package/dist/components/Badge/filled/index.d.ts.map +1 -1
- package/dist/components/Badge/index.d.ts +1 -1
- package/dist/components/Badge/index.js +1 -1
- package/dist/components/Badge/stroke/index.d.ts +1 -1
- package/dist/components/Badge/stroke/index.d.ts.map +1 -1
- package/dist/components/Badge/stroke/strokeIconLabelBadges/index.d.ts +1 -1
- package/dist/components/Badge/stroke/strokeIconLabelBadges/index.d.ts.map +1 -1
- package/dist/components/Badge/stroke/strokeIconLabelBadges/index.js +1 -1
- package/dist/components/Badge/stroke/strokeOnlyIconBadges/index.d.ts +1 -1
- package/dist/components/Badge/stroke/strokeOnlyIconBadges/index.d.ts.map +1 -1
- package/dist/components/Badge/stroke/strokeOnlyIconBadges/index.js +1 -1
- package/dist/components/Badge/stroke/strokeOnlyLabelBadges/index.d.ts +1 -1
- package/dist/components/Badge/stroke/strokeOnlyLabelBadges/index.d.ts.map +1 -1
- package/dist/components/Badge/stroke/strokeOnlyLabelBadges/index.js +1 -1
- package/dist/components/Badge/subtle/index.d.ts +1 -1
- package/dist/components/Badge/subtle/index.d.ts.map +1 -1
- package/dist/components/Badge/subtle/subtleIOnlyIconBadges/index.d.ts +1 -1
- package/dist/components/Badge/subtle/subtleIOnlyIconBadges/index.d.ts.map +1 -1
- package/dist/components/Badge/subtle/subtleIOnlyIconBadges/index.js +1 -1
- package/dist/components/Badge/subtle/subtleIconLabelBadges/index.d.ts +1 -1
- package/dist/components/Badge/subtle/subtleIconLabelBadges/index.d.ts.map +1 -1
- package/dist/components/Badge/subtle/subtleIconLabelBadges/index.js +1 -1
- package/dist/components/Badge/subtle/subtleOnlyLabelBadges/index.d.ts +1 -1
- package/dist/components/Badge/subtle/subtleOnlyLabelBadges/index.d.ts.map +1 -1
- package/dist/components/Badge/subtle/subtleOnlyLabelBadges/index.js +1 -1
- package/dist/components/BottomSheet/index.d.ts +1 -1
- package/dist/components/BottomSheet/index.d.ts.map +1 -1
- package/dist/components/BottomSheet/index.js +1 -1
- package/dist/components/Breadcrumbs/Breadcrumbs.types.d.ts +1 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.types.d.ts.map +1 -1
- package/dist/components/Breadcrumbs/HomeIcon.d.ts +1 -1
- package/dist/components/Breadcrumbs/HomeIcon.d.ts.map +1 -1
- package/dist/components/Breadcrumbs/HomeIcon.js +1 -1
- package/dist/components/Breadcrumbs/index.d.ts +1 -1
- package/dist/components/Breadcrumbs/index.d.ts.map +1 -1
- package/dist/components/Breadcrumbs/index.js +16 -6
- package/dist/components/Button/index.d.ts.map +1 -1
- package/dist/components/Button/index.js +5 -1
- package/dist/components/ButtonGroup/index.d.ts +1 -1
- package/dist/components/ButtonGroup/index.d.ts.map +1 -1
- package/dist/components/ButtonGroup/index.js +1 -1
- package/dist/components/Card/index.js +1 -1
- package/dist/components/Chart/ChartSkeleton.d.ts +1 -1
- package/dist/components/Chart/ChartSkeleton.d.ts.map +1 -1
- package/dist/components/Chart/ChartSkeleton.js +24 -17
- package/dist/components/Chart/index.d.ts +2 -1
- package/dist/components/Chart/index.d.ts.map +1 -1
- package/dist/components/Chart/index.js +84 -28
- package/dist/components/Chatbot/bookMarkPanel.d.ts +1 -1
- package/dist/components/Chatbot/bookMarkPanel.d.ts.map +1 -1
- package/dist/components/Chatbot/bookMarkPanel.js +12 -5
- package/dist/components/Chatbot/chatBotFooter.d.ts +1 -1
- package/dist/components/Chatbot/chatBotFooter.d.ts.map +1 -1
- package/dist/components/Chatbot/chatBotLandingScreen.d.ts +1 -1
- package/dist/components/Chatbot/chatBotLandingScreen.d.ts.map +1 -1
- package/dist/components/Chatbot/chatInput.d.ts +1 -1
- package/dist/components/Chatbot/chatInput.d.ts.map +1 -1
- package/dist/components/Chatbot/chatInput.js +7 -9
- package/dist/components/Chatbot/chatbotModuleMenu.d.ts +1 -1
- package/dist/components/Chatbot/chatbotModuleMenu.d.ts.map +1 -1
- package/dist/components/Chatbot/chatbotModuleMenu.js +69 -62
- package/dist/components/Chatbot/constant.d.ts.map +1 -1
- package/dist/components/Chatbot/constant.js +12 -12
- package/dist/components/Chatbot/conversationScreen.d.ts +1 -1
- package/dist/components/Chatbot/conversationScreen.d.ts.map +1 -1
- package/dist/components/Chatbot/customScreen.d.ts +1 -1
- package/dist/components/Chatbot/customScreen.d.ts.map +1 -1
- package/dist/components/Chatbot/historyPanel.d.ts +1 -1
- package/dist/components/Chatbot/historyPanel.d.ts.map +1 -1
- package/dist/components/Chatbot/historyPanel.js +11 -3
- package/dist/components/Chatbot/index.d.ts +1 -1
- package/dist/components/Chatbot/index.d.ts.map +1 -1
- package/dist/components/Chatbot/index.js +8 -7
- package/dist/components/Chatbot/newChatComponent.d.ts +1 -1
- package/dist/components/Chatbot/newChatComponent.d.ts.map +1 -1
- package/dist/components/Checkbox/WithDropDown.d.ts +1 -1
- package/dist/components/Checkbox/WithDropDown.d.ts.map +1 -1
- package/dist/components/Checkbox/WithoutFormLabel.d.ts +1 -1
- package/dist/components/Checkbox/WithoutFormLabel.d.ts.map +1 -1
- package/dist/components/Checkbox/index.js +1 -1
- package/dist/components/Chips/chipLeadingIcon.d.ts +6 -0
- package/dist/components/Chips/chipLeadingIcon.d.ts.map +1 -0
- package/dist/components/Chips/chipLeadingIcon.js +46 -0
- package/dist/components/Chips/index.d.ts.map +1 -1
- package/dist/components/Chips/index.js +9 -3
- package/dist/components/CommentsPanel/CommentsContext.d.ts +1 -1
- package/dist/components/CommentsPanel/CommentsContext.d.ts.map +1 -1
- package/dist/components/CommentsPanel/CommentsContext.js +5 -4
- package/dist/components/CommentsPanel/CommentsPanel.impl.d.ts +1 -1
- package/dist/components/CommentsPanel/CommentsPanel.impl.d.ts.map +1 -1
- package/dist/components/CommentsPanel/CommentsPanel.impl.js +11 -8
- package/dist/components/CommentsPanel/CommentsPanel.types.d.ts +1 -1
- package/dist/components/CommentsPanel/CommentsPanel.types.d.ts.map +1 -1
- package/dist/components/CreateItemFlow/index.js +1 -1
- package/dist/components/DatePicker/datePickerDropdown.d.ts +1 -1
- package/dist/components/DatePicker/datePickerDropdown.d.ts.map +1 -1
- package/dist/components/DatePicker/datePickerFooter.d.ts +1 -1
- package/dist/components/DatePicker/datePickerFooter.d.ts.map +1 -1
- package/dist/components/DatePicker/datePickerInput.d.ts.map +1 -1
- package/dist/components/DatePicker/datePickerInput.js +5 -2
- package/dist/components/DatePicker/index.js +2 -2
- package/dist/components/DatePicker/showMonthYearSelect.d.ts +1 -1
- package/dist/components/DatePicker/showMonthYearSelect.d.ts.map +1 -1
- package/dist/components/DateRangePicker/dateRangePickerCustom.d.ts +1 -1
- package/dist/components/DateRangePicker/dateRangePickerCustom.d.ts.map +1 -1
- package/dist/components/DateRangePicker/dateRangePickerDropdown.d.ts +3 -1
- package/dist/components/DateRangePicker/dateRangePickerDropdown.d.ts.map +1 -1
- package/dist/components/DateRangePicker/dateRangePickerFooter.d.ts +1 -1
- package/dist/components/DateRangePicker/dateRangePickerFooter.d.ts.map +1 -1
- package/dist/components/DateRangePicker/dateRangePickerInput.d.ts +1 -1
- package/dist/components/DateRangePicker/dateRangePickerInput.d.ts.map +1 -1
- package/dist/components/DateRangePicker/dateRangePickerInput.js +6 -7
- package/dist/components/DateRangePicker/index.js +2 -2
- package/dist/components/DateRangePicker/showMonthYearSelect.d.ts +1 -1
- package/dist/components/DateRangePicker/showMonthYearSelect.d.ts.map +1 -1
- package/dist/components/DynamicLayout/index.d.ts +2 -1
- package/dist/components/DynamicLayout/index.d.ts.map +1 -1
- package/dist/components/DynamicLayout/index.js +1 -1
- package/dist/components/EmptyState/EmptyStateImage.d.ts +2 -2
- package/dist/components/EmptyState/EmptyStateImage.d.ts.map +1 -1
- package/dist/components/EmptyState/EmptyStateImage.js +26 -13
- package/dist/components/EmptyState/index.d.ts +1 -1
- package/dist/components/EmptyState/index.d.ts.map +1 -1
- package/dist/components/EmptyState/index.js +29 -1
- package/dist/components/FileUpload/TemplatePreview.d.ts +1 -1
- package/dist/components/FileUpload/TemplatePreview.d.ts.map +1 -1
- package/dist/components/FileUpload/fileUploadAssets.loader.d.ts +0 -1
- package/dist/components/FileUpload/fileUploadAssets.loader.d.ts.map +1 -1
- package/dist/components/FileUpload/fileUploadAssets.loader.js +0 -1
- package/dist/components/FileUpload/index.d.ts +1 -1
- package/dist/components/FileUpload/index.d.ts.map +1 -1
- package/dist/components/FileUpload/index.js +27 -11
- package/dist/components/FilterPanel/FilterPanelHeader.d.ts +1 -1
- package/dist/components/FilterPanel/FilterPanelHeader.d.ts.map +1 -1
- package/dist/components/FilterPanel/PanelFooter.d.ts +1 -1
- package/dist/components/FilterPanel/PanelFooter.d.ts.map +1 -1
- package/dist/components/FilterPanel/PanelSidebar.d.ts +1 -1
- package/dist/components/FilterPanel/PanelSidebar.d.ts.map +1 -1
- package/dist/components/FilterPanel/index.d.ts +1 -1
- package/dist/components/FilterPanel/index.d.ts.map +1 -1
- package/dist/components/FilterPanel/index.js +67 -43
- package/dist/components/FiltersStrip/AppliedFilters.d.ts +1 -1
- package/dist/components/FiltersStrip/AppliedFilters.d.ts.map +1 -1
- package/dist/components/FiltersStrip/FilterDropDown.d.ts +1 -1
- package/dist/components/FiltersStrip/FilterDropDown.d.ts.map +1 -1
- package/dist/components/FiltersStrip/FilterSlider.d.ts +1 -1
- package/dist/components/FiltersStrip/FilterSlider.d.ts.map +1 -1
- package/dist/components/FiltersStrip/FilterSlider.js +1 -1
- package/dist/components/FiltersStrip/FilterSliderPortal.d.ts +1 -1
- package/dist/components/FiltersStrip/FilterSliderPortal.d.ts.map +1 -1
- package/dist/components/FiltersStrip/index.d.ts +1 -1
- package/dist/components/FiltersStrip/index.d.ts.map +1 -1
- package/dist/components/FiltersStrip/index.js +1 -1
- package/dist/components/HalloweenStyleIntro/HalloweenStyleIntroProvider.d.ts +14 -0
- package/dist/components/HalloweenStyleIntro/HalloweenStyleIntroProvider.d.ts.map +1 -0
- package/dist/components/HalloweenStyleIntro/HalloweenStyleIntroProvider.js +131 -0
- package/dist/components/HalloweenStyleIntro/halloweenIntro.constants.d.ts +13 -0
- package/dist/components/HalloweenStyleIntro/halloweenIntro.constants.d.ts.map +1 -0
- package/dist/components/HalloweenStyleIntro/halloweenIntro.constants.js +9 -0
- package/dist/components/HalloweenStyleIntro/halloweenIntroHtml.d.ts +15 -0
- package/dist/components/HalloweenStyleIntro/halloweenIntroHtml.d.ts.map +1 -0
- package/dist/components/HalloweenStyleIntro/halloweenIntroHtml.js +141 -0
- package/dist/components/HalloweenStyleIntro/halloweenIntroHtml.test.d.ts +2 -0
- package/dist/components/HalloweenStyleIntro/halloweenIntroHtml.test.d.ts.map +1 -0
- package/dist/components/HalloweenStyleIntro/index.d.ts +4 -0
- package/dist/components/HalloweenStyleIntro/index.d.ts.map +1 -0
- package/dist/components/Header/Header.types.d.ts +22 -5
- package/dist/components/Header/Header.types.d.ts.map +1 -1
- package/dist/components/Header/index.d.ts +1 -1
- package/dist/components/Header/index.d.ts.map +1 -1
- package/dist/components/Header/index.js +88 -57
- package/dist/components/HomePage/HomePage.types.d.ts +3 -2
- package/dist/components/HomePage/HomePage.types.d.ts.map +1 -1
- package/dist/components/HomePage/constants.d.ts.map +1 -1
- package/dist/components/HomePage/constants.js +23 -44
- package/dist/components/HomePage/index.d.ts +1 -1
- package/dist/components/HomePage/index.d.ts.map +1 -1
- package/dist/components/HomePage/index.js +48 -53
- package/dist/components/ImpactIcon/index.d.ts +19 -0
- package/dist/components/ImpactIcon/index.d.ts.map +1 -0
- package/dist/components/ImpactIcon/index.js +52 -0
- package/dist/components/Input/HelperText.d.ts +1 -1
- package/dist/components/Input/HelperText.d.ts.map +1 -1
- package/dist/components/Input/HelperText.js +3 -1
- package/dist/components/Input/InputWithIcon.d.ts +1 -1
- package/dist/components/Input/InputWithIcon.d.ts.map +1 -1
- package/dist/components/Input/InputWithIcon.js +1 -1
- package/dist/components/Input/VanillaInput.d.ts +1 -1
- package/dist/components/Input/VanillaInput.d.ts.map +1 -1
- package/dist/components/Input/VanillaInput.js +1 -1
- package/dist/components/Input/index.js +3 -3
- package/dist/components/KeyboardShortCuts/Keycombo.d.ts +2 -2
- package/dist/components/KeyboardShortCuts/Keycombo.d.ts.map +1 -1
- package/dist/components/KeyboardShortCuts/ShortcutLandingPage.d.ts +1 -1
- package/dist/components/KeyboardShortCuts/ShortcutLandingPage.d.ts.map +1 -1
- package/dist/components/KeyboardShortCuts/ShortcutLandingPage.js +5 -4
- package/dist/components/KeyboardShortCuts/ShortcutModal.d.ts +1 -1
- package/dist/components/KeyboardShortCuts/ShortcutModal.d.ts.map +1 -1
- package/dist/components/KeyboardShortCuts/ShortcutModal.js +2 -2
- package/dist/components/KeyboardShortCuts/StarOutlineIcon.d.ts +1 -1
- package/dist/components/KeyboardShortCuts/StarOutlineIcon.d.ts.map +1 -1
- package/dist/components/KeyboardShortCuts/allSetBox.d.ts +4 -3
- package/dist/components/KeyboardShortCuts/allSetBox.d.ts.map +1 -1
- package/dist/components/KeyboardShortCuts/allSetBox.js +12 -16
- package/dist/components/KeyboardShortCuts/index.d.ts +1 -1
- package/dist/components/KeyboardShortCuts/index.d.ts.map +1 -1
- package/dist/components/KeyboardShortCuts/index.js +1 -1
- package/dist/components/KeyboardShortCuts/screen2.d.ts +1 -1
- package/dist/components/KeyboardShortCuts/screen2.d.ts.map +1 -1
- package/dist/components/KeyboardShortCuts/screen3.d.ts +1 -1
- package/dist/components/KeyboardShortCuts/screen3.d.ts.map +1 -1
- package/dist/components/KeyboardShortCuts/tutorial.d.ts +2 -1
- package/dist/components/KeyboardShortCuts/tutorial.d.ts.map +1 -1
- package/dist/components/KeyboardShortCuts/tutorial.js +3 -2
- package/dist/components/Loader/HalloweenLoaderSlides.d.ts +13 -0
- package/dist/components/Loader/HalloweenLoaderSlides.d.ts.map +1 -0
- package/dist/components/Loader/HalloweenLoaderSlides.js +109 -0
- package/dist/components/Loader/index.d.ts +1 -1
- package/dist/components/Loader/index.d.ts.map +1 -1
- package/dist/components/Loader/index.js +3 -1
- package/dist/components/Menu/IconMenuItem.js +1 -1
- package/dist/components/Menu/MenuHeaderInfo.d.ts +2 -2
- package/dist/components/Menu/MenuHeaderInfo.d.ts.map +1 -1
- package/dist/components/Menu/MenuHeaderInfo.js +10 -2
- package/dist/components/Menu/NestedMenuItem.js +1 -1
- package/dist/components/Menu/index.js +5 -14
- package/dist/components/Menu/nestedMenuItemsFromObject.js +1 -1
- package/dist/components/Modal/index.d.ts +1 -1
- package/dist/components/Modal/index.d.ts.map +1 -1
- package/dist/components/Modal/index.js +1 -1
- package/dist/components/MonthPicker/index.d.ts +1 -1
- package/dist/components/MonthPicker/index.d.ts.map +1 -1
- package/dist/components/MonthPicker/index.js +10 -4
- package/dist/components/MonthRangePicker/constants.d.ts +4 -0
- package/dist/components/MonthRangePicker/constants.d.ts.map +1 -1
- package/dist/components/MonthRangePicker/constants.js +15 -1
- package/dist/components/MonthRangePicker/index.d.ts +1 -1
- package/dist/components/MonthRangePicker/index.d.ts.map +1 -1
- package/dist/components/MonthRangePicker/index.js +68 -30
- package/dist/components/NewCreateItemFlow/index.d.ts +1 -1
- package/dist/components/NewCreateItemFlow/index.d.ts.map +1 -1
- package/dist/components/NewCreateItemFlow/index.js +1 -1
- package/dist/components/NewDynamicLayout/index.d.ts +1 -1
- package/dist/components/NewDynamicLayout/index.d.ts.map +1 -1
- package/dist/components/NewDynamicLayout/index.js +13 -15
- package/dist/components/Notification/NotificationBody/NotificationList/NotificationChipsList.d.ts +1 -1
- package/dist/components/Notification/NotificationBody/NotificationList/NotificationChipsList.d.ts.map +1 -1
- package/dist/components/Notification/NotificationBody/NotificationList/NotificationChipsList.js +3 -1
- package/dist/components/Notification/NotificationBody/NotificationList/NotificationList.d.ts +1 -1
- package/dist/components/Notification/NotificationBody/NotificationList/NotificationList.d.ts.map +1 -1
- package/dist/components/Notification/NotificationBody/NotificationList/NotificationList.js +1 -1
- package/dist/components/Notification/NotificationBody/NotificationList/NotificationListItem.d.ts +1 -1
- package/dist/components/Notification/NotificationBody/NotificationList/NotificationListItem.d.ts.map +1 -1
- package/dist/components/Notification/NotificationBody/NotificationList/NotificationListItem.js +12 -16
- package/dist/components/Notification/NotificationBody/NotificationPanelContent.d.ts +1 -1
- package/dist/components/Notification/NotificationBody/NotificationPanelContent.d.ts.map +1 -1
- package/dist/components/Notification/NotificationBody/NotificationTaskList.d.ts +1 -1
- package/dist/components/Notification/NotificationBody/NotificationTaskList.d.ts.map +1 -1
- package/dist/components/Notification/NotificationBody/NotificationTaskList.js +4 -2
- package/dist/components/Notification/NotificationBody/index.d.ts +1 -1
- package/dist/components/Notification/NotificationBody/index.d.ts.map +1 -1
- package/dist/components/Notification/NotificationHeader.d.ts.map +1 -1
- package/dist/components/Notification/NotificationHeader.js +13 -3
- package/dist/components/Notification/index.d.ts +1 -1
- package/dist/components/Notification/index.d.ts.map +1 -1
- package/dist/components/Notification/index.js +1 -1
- package/dist/components/OnboardingTour/index.d.ts +1 -1
- package/dist/components/OnboardingTour/index.d.ts.map +1 -1
- package/dist/components/OnboardingTour/index.js +10 -4
- package/dist/components/OnboardingTour/productTourList.d.ts +1 -1
- package/dist/components/OnboardingTour/productTourList.d.ts.map +1 -1
- package/dist/components/OnboardingTour/productTourList.js +4 -4
- package/dist/components/OnboardingTour/tooltipCard.d.ts +1 -1
- package/dist/components/OnboardingTour/tooltipCard.d.ts.map +1 -1
- package/dist/components/OnboardingTour/tooltipCard.js +2 -2
- package/dist/components/Panel/index.d.ts +1 -1
- package/dist/components/Panel/index.d.ts.map +1 -1
- package/dist/components/Panel/index.js +13 -7
- package/dist/components/Popover/Popover.types.d.ts +5 -0
- package/dist/components/Popover/Popover.types.d.ts.map +1 -1
- package/dist/components/Popover/index.d.ts +1 -1
- package/dist/components/Popover/index.d.ts.map +1 -1
- package/dist/components/Popover/index.js +12 -4
- package/dist/components/ProfileMenu/ProfileMenu.types.d.ts +55 -0
- package/dist/components/ProfileMenu/ProfileMenu.types.d.ts.map +1 -0
- package/dist/components/ProfileMenu/ProfileMenuListItem.d.ts +12 -0
- package/dist/components/ProfileMenu/ProfileMenuListItem.d.ts.map +1 -0
- package/dist/components/ProfileMenu/ProfileMenuListItem.js +42 -0
- package/dist/components/ProfileMenu/index.d.ts +11 -0
- package/dist/components/ProfileMenu/index.d.ts.map +1 -0
- package/dist/components/ProfileMenu/index.js +164 -0
- package/dist/components/ProgressBar/index.d.ts +1 -1
- package/dist/components/ProgressBar/index.d.ts.map +1 -1
- package/dist/components/ProgressBar/index.js +1 -1
- package/dist/components/Prompt/index.d.ts +1 -1
- package/dist/components/Prompt/index.d.ts.map +1 -1
- package/dist/components/Prompt/index.js +10 -2
- package/dist/components/RadioButtonGroup/index.d.ts +1 -1
- package/dist/components/RadioButtonGroup/index.d.ts.map +1 -1
- package/dist/components/RadioButtonGroup/index.js +1 -1
- package/dist/components/Select/customGroupHeading.d.ts +1 -1
- package/dist/components/Select/customGroupHeading.d.ts.map +1 -1
- package/dist/components/Select/dropdown.d.ts +1 -1
- package/dist/components/Select/dropdown.d.ts.map +1 -1
- package/dist/components/Select/index.js +2 -2
- package/dist/components/Select/menuList.d.ts +2 -1
- package/dist/components/Select/menuList.d.ts.map +1 -1
- package/dist/components/Select/menuList.js +2 -1
- package/dist/components/Select/optionList.d.ts +2 -1
- package/dist/components/Select/optionList.d.ts.map +1 -1
- package/dist/components/Select/selected-option-tags.d.ts +1 -1
- package/dist/components/Select/selected-option-tags.d.ts.map +1 -1
- package/dist/components/Shortcuts/ShortcutHint.d.ts +1 -1
- package/dist/components/Shortcuts/ShortcutHint.d.ts.map +1 -1
- package/dist/components/Shortcuts/ShortcutHint.js +4 -18
- package/dist/components/Shortcuts/ShortcutScope.d.ts +1 -1
- package/dist/components/Shortcuts/ShortcutScope.d.ts.map +1 -1
- package/dist/components/Shortcuts/ShortcutsProvider.d.ts +1 -1
- package/dist/components/Shortcuts/ShortcutsProvider.d.ts.map +1 -1
- package/dist/components/Sidebar/ChildMenu.d.ts +1 -1
- package/dist/components/Sidebar/ChildMenu.d.ts.map +1 -1
- package/dist/components/Sidebar/ParentMenu.d.ts +1 -1
- package/dist/components/Sidebar/ParentMenu.d.ts.map +1 -1
- package/dist/components/Sidebar/SidebarActions.d.ts +1 -1
- package/dist/components/Sidebar/SidebarActions.d.ts.map +1 -1
- package/dist/components/Sidebar/SidebarActions.js +3 -3
- package/dist/components/Sidebar/SidebarMenus.d.ts +1 -1
- package/dist/components/Sidebar/SidebarMenus.d.ts.map +1 -1
- package/dist/components/Sidebar/index.d.ts +1 -1
- package/dist/components/Sidebar/index.d.ts.map +1 -1
- package/dist/components/Sidebar/index.js +1 -1
- package/dist/components/Skeleton/index.d.ts +1 -1
- package/dist/components/Skeleton/index.d.ts.map +1 -1
- package/dist/components/Skeleton/index.js +1 -1
- package/dist/components/Slider/index.d.ts +1 -1
- package/dist/components/Slider/index.d.ts.map +1 -1
- package/dist/components/Slider/index.js +8 -6
- package/dist/components/Slider/sliderValueLabel.d.ts +7 -0
- package/dist/components/Slider/sliderValueLabel.d.ts.map +1 -0
- package/dist/components/Slider/sliderValueLabel.js +31 -0
- package/dist/components/Stepper/index.d.ts +1 -1
- package/dist/components/Stepper/index.d.ts.map +1 -1
- package/dist/components/Stepper/index.js +2 -2
- package/dist/components/Switch/index.d.ts.map +1 -1
- package/dist/components/Switch/index.js +11 -10
- package/dist/components/TableChat/EmptyContainer.d.ts +2 -1
- package/dist/components/TableChat/EmptyContainer.d.ts.map +1 -1
- package/dist/components/TableChat/EmptyContainer.js +1 -1
- package/dist/components/TableChat/TableChatSidePanel.d.ts +1 -1
- package/dist/components/TableChat/TableChatSidePanel.d.ts.map +1 -1
- package/dist/components/TableChat/TableChatSidePanel.js +1 -1
- package/dist/components/TableChat/components/ChatMessageSkeleton/ChatFooterSkeleton.d.ts +2 -1
- package/dist/components/TableChat/components/ChatMessageSkeleton/ChatFooterSkeleton.d.ts.map +1 -1
- package/dist/components/TableChat/components/ChatMessageSkeleton/ChatFooterSkeleton.js +1 -1
- package/dist/components/TableChat/components/ChatMessageSkeleton/ChatHeaderSkeleton.d.ts +2 -1
- package/dist/components/TableChat/components/ChatMessageSkeleton/ChatHeaderSkeleton.d.ts.map +1 -1
- package/dist/components/TableChat/components/ChatMessageSkeleton/ChatHeaderSkeleton.js +1 -1
- package/dist/components/TableChat/components/ChatMessageSkeleton/ChatMessageListSkeleton.d.ts +2 -1
- package/dist/components/TableChat/components/ChatMessageSkeleton/ChatMessageListSkeleton.d.ts.map +1 -1
- package/dist/components/TableChat/components/ChatMessageSkeleton/ChatMessageListSkeleton.js +1 -1
- package/dist/components/TableChat/components/ChatMessageSkeleton/ChatSkeleton.d.ts +2 -1
- package/dist/components/TableChat/components/ChatMessageSkeleton/ChatSkeleton.d.ts.map +1 -1
- package/dist/components/TableChat/components/ChatMessageSkeleton/ChatSkeleton.js +1 -1
- package/dist/components/TableChat/components/SkeletonList.d.ts +1 -1
- package/dist/components/TableChat/components/SkeletonList.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatLeftSection/AddChatContainer/AddChatContainer.d.ts +1 -1
- package/dist/components/TableChat/components/TableChatLeftSection/AddChatContainer/AddChatContainer.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatLeftSection/AddChatContainer/AddChatContainer.js +1 -1
- package/dist/components/TableChat/components/TableChatLeftSection/ChannelCheckbox/ChannelCheckbox.d.ts +3 -1
- package/dist/components/TableChat/components/TableChatLeftSection/ChannelCheckbox/ChannelCheckbox.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatLeftSection/ChatChannelItem.d.ts +1 -1
- package/dist/components/TableChat/components/TableChatLeftSection/ChatChannelItem.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatLeftSection/ChatChannelItem.js +6 -5
- package/dist/components/TableChat/components/TableChatLeftSection/ChatChannelList.d.ts +1 -1
- package/dist/components/TableChat/components/TableChatLeftSection/ChatChannelList.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatLeftSection/ChatHeaderButton/ChatHeaderButton.d.ts +2 -2
- package/dist/components/TableChat/components/TableChatLeftSection/ChatHeaderButton/ChatHeaderButton.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatLeftSection/ChatHeaderNavigation.js +1 -1
- package/dist/components/TableChat/components/TableChatLeftSection/ChatIconButton/ChatIconButton.d.ts +3 -1
- package/dist/components/TableChat/components/TableChatLeftSection/ChatIconButton/ChatIconButton.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatLeftSection/TableChatChannelHeader.d.ts +1 -1
- package/dist/components/TableChat/components/TableChatLeftSection/TableChatChannelHeader.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatLeftSection/TableChatChannelHeader.js +1 -1
- package/dist/components/TableChat/components/TableChatLeftSection/UnreadBadge/UnreadBadge.d.ts +3 -1
- package/dist/components/TableChat/components/TableChatLeftSection/UnreadBadge/UnreadBadge.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/AvatarGroup/AvatarGroup.d.ts +2 -2
- package/dist/components/TableChat/components/TableChatRightSection/AvatarGroup/AvatarGroup.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/AvatarGroup/AvatarGroup.js +19 -37
- package/dist/components/TableChat/components/TableChatRightSection/ChatDefaultMessageType/ChatDefaultMessageType.d.ts +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatDefaultMessageType/ChatDefaultMessageType.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatDefaultMessageType/ChatDefaultMessageType.js +13 -21
- package/dist/components/TableChat/components/TableChatRightSection/ChatDeleteMessageType/ChatDeletedMessageType.d.ts +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatDeleteMessageType/ChatDeletedMessageType.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatDeleteMessageType/ChatDeletedMessageType.js +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatEditedMessageType/ChatEditedMessageType.d.ts +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatEditedMessageType/ChatEditedMessageType.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatEditedMessageType/ChatEditedMessageType.js +24 -13
- package/dist/components/TableChat/components/TableChatRightSection/ChatHeader/ChatHeader.d.ts +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatHeader/ChatHeader.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatHeader/ChatHeader.js +4 -4
- package/dist/components/TableChat/components/TableChatRightSection/ChatHeader/ChatHeaderAction.d.ts +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatHeader/ChatHeaderAction.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatHeader/ChatHeaderAction.js +23 -11
- package/dist/components/TableChat/components/TableChatRightSection/ChatHeader/ChatHeaderInfo.d.ts +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatHeader/ChatHeaderInfo.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatInput/ChatInput.d.ts +2 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatInput/ChatInput.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatInput/ChatInput.js +21 -24
- package/dist/components/TableChat/components/TableChatRightSection/ChatMessageActions/ChatMessageActions.d.ts +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatMessageActions/ChatMessageActions.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatMessageAvatar/ChatMessageAvatar.d.ts +2 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatMessageAvatar/ChatMessageAvatar.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatMessages/ChatMessage.d.ts +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatMessages/ChatMessage.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatMessages/ChatMessage.js +5 -6
- package/dist/components/TableChat/components/TableChatRightSection/ChatNotificationSettings/ChatNotificationSettings.d.ts +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatNotificationSettings/ChatNotificationSettings.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ChatNotificationSettings/ChatNotificationSettings.js +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/DateSeparator/DateSeparator.d.ts +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/DateSeparator/DateSeparator.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/DateSeparator/DateSeparator.js +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/EmojiPicker/EmojiPicker.d.ts +3 -1
- package/dist/components/TableChat/components/TableChatRightSection/EmojiPicker/EmojiPicker.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/FileUploader/FileUploader.d.ts +3 -1
- package/dist/components/TableChat/components/TableChatRightSection/FileUploader/FileUploader.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/FileUploader/FileUploader.js +6 -6
- package/dist/components/TableChat/components/TableChatRightSection/ImageUploader/ImageUploader.d.ts +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ImageUploader/ImageUploader.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ImageUploader/ImageUploader.js +5 -5
- package/dist/components/TableChat/components/TableChatRightSection/PinedChatPanel/PinedChatPanel.d.ts +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/PinedChatPanel/PinedChatPanel.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/PinedChatPanel/PinedChatPanel.js +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ReplyBadge/ReplyBadge.d.ts +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/ReplyBadge/ReplyBadge.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/TableChatRightSection.d.ts +2 -1
- package/dist/components/TableChat/components/TableChatRightSection/TableChatRightSection.d.ts.map +1 -1
- package/dist/components/TableChat/components/TableChatRightSection/TableChatRightSection.js +12 -5
- package/dist/components/TableChat/components/TextEditor/EditorToolbar.js +1 -1
- package/dist/components/TableChat/components/TextEditor/Mention.d.ts.map +1 -1
- package/dist/components/TableChat/components/TextEditor/Mention.js +2 -7
- package/dist/components/TableChat/components/TextEditor/MentionList.js +1 -1
- package/dist/components/TableV36/AdvanceSearchModalItem.d.ts +3 -1
- package/dist/components/TableV36/AdvanceSearchModalItem.d.ts.map +1 -1
- package/dist/components/TableV36/AdvanceSortModal.d.ts +3 -1
- package/dist/components/TableV36/AdvanceSortModal.d.ts.map +1 -1
- package/dist/components/TableV36/AdvanceSortModal.js +42 -42
- package/dist/components/TableV36/AgGridHeader.d.ts +1 -1
- package/dist/components/TableV36/AgGridHeader.d.ts.map +1 -1
- package/dist/components/TableV36/AgGridHeader.js +21 -11
- package/dist/components/TableV36/CellTooltip.d.ts +1 -1
- package/dist/components/TableV36/CellTooltip.d.ts.map +1 -1
- package/dist/components/TableV36/CommentsPortal.d.ts.map +1 -1
- package/dist/components/TableV36/CommentsPortal.js +7 -4
- package/dist/components/TableV36/NewAdvanceSearchModal/NewAdvanceSearchModal.d.ts +3 -1
- package/dist/components/TableV36/NewAdvanceSearchModal/NewAdvanceSearchModal.d.ts.map +1 -1
- package/dist/components/TableV36/NewAdvanceSearchModal/NewAdvanceSearchModalItem.d.ts +3 -1
- package/dist/components/TableV36/NewAdvanceSearchModal/NewAdvanceSearchModalItem.d.ts.map +1 -1
- package/dist/components/TableV36/NewAdvanceSearchModal/NewAdvanceSearchModalItem.js +5 -0
- package/dist/components/TableV36/NoComments.d.ts +3 -1
- package/dist/components/TableV36/NoComments.d.ts.map +1 -1
- package/dist/components/TableV36/NoComments.js +1 -1
- package/dist/components/TableV36/Table.types.d.ts +2 -2
- package/dist/components/TableV36/Table.types.d.ts.map +1 -1
- package/dist/components/TableV36/TableSkeletonCell.d.ts +1 -1
- package/dist/components/TableV36/TableSkeletonCell.d.ts.map +1 -1
- package/dist/components/TableV36/TableSkeletonCell.js +3 -15
- package/dist/components/TableV36/actionBar.d.ts +3 -1
- package/dist/components/TableV36/actionBar.d.ts.map +1 -1
- package/dist/components/TableV36/advanceSearchModal.d.ts +3 -1
- package/dist/components/TableV36/advanceSearchModal.d.ts.map +1 -1
- package/dist/components/TableV36/filterUtils.d.ts +21 -0
- package/dist/components/TableV36/filterUtils.d.ts.map +1 -1
- package/dist/components/TableV36/filterUtils.js +126 -16
- package/dist/components/TableV36/filterUtils.test.d.ts +2 -0
- package/dist/components/TableV36/filterUtils.test.d.ts.map +1 -0
- package/dist/components/TableV36/index.d.ts.map +1 -1
- package/dist/components/TableV36/index.js +34 -16
- package/dist/components/TableV36/tableFunctions.d.ts.map +1 -1
- package/dist/components/TableV36/tableFunctions.js +107 -37
- package/dist/components/TableV36/tableHeader.d.ts +1 -1
- package/dist/components/TableV36/tableHeader.d.ts.map +1 -1
- package/dist/components/TableV36/tableHeader.js +5 -4
- package/dist/components/TableV36/tableIcons.d.ts +4 -2
- package/dist/components/TableV36/tableIcons.d.ts.map +1 -1
- package/dist/components/TableV36/tableMoreOptions.d.ts +3 -1
- package/dist/components/TableV36/tableMoreOptions.d.ts.map +1 -1
- package/dist/components/TableV36/tableMoreOptions.js +36 -18
- package/dist/components/TableV36/tablePagination.d.ts +3 -1
- package/dist/components/TableV36/tablePagination.d.ts.map +1 -1
- package/dist/components/TableV36/tableSettings.d.ts +3 -1
- package/dist/components/TableV36/tableSettings.d.ts.map +1 -1
- package/dist/components/TableV36/tableSettings.js +8 -9
- package/dist/components/TableV36/withCommentIcon.d.ts.map +1 -1
- package/dist/components/TableV36/withCommentIcon.js +13 -5
- package/dist/components/Tabs/TabPanel.d.ts +1 -1
- package/dist/components/Tabs/TabPanel.d.ts.map +1 -1
- package/dist/components/Tabs/index.d.ts +1 -1
- package/dist/components/Tabs/index.d.ts.map +1 -1
- package/dist/components/Tabs/index.js +1 -1
- package/dist/components/Tag/filled.d.ts +1 -1
- package/dist/components/Tag/filled.d.ts.map +1 -1
- package/dist/components/Tag/index.js +1 -1
- package/dist/components/Tag/solid.d.ts +1 -1
- package/dist/components/Tag/solid.d.ts.map +1 -1
- package/dist/components/Tag/stroke.d.ts +1 -1
- package/dist/components/Tag/stroke.d.ts.map +1 -1
- package/dist/components/TagGroup/index.d.ts +1 -1
- package/dist/components/TagGroup/index.d.ts.map +1 -1
- package/dist/components/TextArea/index.d.ts +1 -1
- package/dist/components/TextArea/index.d.ts.map +1 -1
- package/dist/components/TextArea/index.js +3 -3
- package/dist/components/ThemingPanel/AccentColorSection.d.ts +11 -0
- package/dist/components/ThemingPanel/AccentColorSection.d.ts.map +1 -0
- package/dist/components/ThemingPanel/AccentColorSection.js +45 -0
- package/dist/components/ThemingPanel/EnhanceContrastSection.d.ts +11 -0
- package/dist/components/ThemingPanel/EnhanceContrastSection.d.ts.map +1 -0
- package/dist/components/ThemingPanel/EnhanceContrastSection.js +42 -0
- package/dist/components/ThemingPanel/InterfaceThemeSection.d.ts +13 -0
- package/dist/components/ThemingPanel/InterfaceThemeSection.d.ts.map +1 -0
- package/dist/components/ThemingPanel/InterfaceThemeSection.js +118 -0
- package/dist/components/ThemingPanel/StylesSection.d.ts +11 -0
- package/dist/components/ThemingPanel/StylesSection.d.ts.map +1 -0
- package/dist/components/ThemingPanel/StylesSection.js +44 -0
- package/dist/components/ThemingPanel/ThemesTab.d.ts +23 -0
- package/dist/components/ThemingPanel/ThemesTab.d.ts.map +1 -0
- package/dist/components/ThemingPanel/ThemesTab.js +49 -0
- package/dist/components/ThemingPanel/ThemingPanel.d.ts +10 -0
- package/dist/components/ThemingPanel/ThemingPanel.d.ts.map +1 -0
- package/dist/components/ThemingPanel/ThemingPanel.js +233 -0
- package/dist/components/ThemingPanel/ThemingPanel.types.d.ts +33 -0
- package/dist/components/ThemingPanel/ThemingPanel.types.d.ts.map +1 -0
- package/dist/components/ThemingPanel/ThemingPanel.types.js +8 -0
- package/dist/components/ThemingPanel/index.d.ts +10 -0
- package/dist/components/ThemingPanel/index.d.ts.map +1 -0
- package/dist/components/ThemingPanel/themingPanel.constants.d.ts +24 -0
- package/dist/components/ThemingPanel/themingPanel.constants.d.ts.map +1 -0
- package/dist/components/ThemingPanel/themingPanel.constants.js +49 -0
- package/dist/components/Toast/index.d.ts +1 -1
- package/dist/components/Toast/index.d.ts.map +1 -1
- package/dist/components/Toast/index.js +1 -1
- package/dist/components/Tooltip/index.js +1 -1
- package/dist/components/common/DotLottiePlayer/index.d.ts +5 -0
- package/dist/components/common/DotLottiePlayer/index.d.ts.map +1 -0
- package/dist/components/common/DotLottiePlayer/index.js +9 -0
- package/dist/components/common/HelperText/index.d.ts +1 -1
- package/dist/components/common/HelperText/index.d.ts.map +1 -1
- package/dist/components/common/HelperText/index.js +1 -1
- package/dist/components/common/MentionEditor/MentionList/index.js +1 -1
- package/dist/components/common/MentionEditor/index.js +1 -1
- package/dist/components/index.d.ts +9 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +48 -0
- package/dist/components/shared/tableColumnSettings/columnsSettingsVirtualList.d.ts +1 -1
- package/dist/components/shared/tableColumnSettings/columnsSettingsVirtualList.d.ts.map +1 -1
- package/dist/components/shared/tableColumnSettings/columnsTabV36.d.ts.map +1 -1
- package/dist/components/shared/tableColumnSettings/tableSettingsV36.d.ts +3 -1
- package/dist/components/shared/tableColumnSettings/tableSettingsV36.d.ts.map +1 -1
- package/dist/i18n/I18nContext.d.ts +1 -1
- package/dist/i18n/I18nContext.d.ts.map +1 -1
- package/dist/locales/de-DE.json.d.ts +43 -0
- package/dist/locales/de-DE.json.js +43 -0
- package/dist/locales/en-US.json.d.ts +43 -0
- package/dist/locales/en-US.json.js +43 -0
- package/dist/locales/es-ES.json.d.ts +43 -0
- package/dist/locales/es-ES.json.js +43 -0
- package/dist/mcp-component-registry.json +186 -129
- package/dist/node_modules/@emotion/cache/dist/emotion-cache.browser.esm.js +4 -4
- package/dist/node_modules/@impactsmartsuite/impact-icons/dist/index.js +232 -78
- package/dist/node_modules/@lottiefiles/dotlottie-react/dist/index.js +87 -0
- package/dist/node_modules/@lottiefiles/dotlottie-web/dist/index.js +1871 -0
- package/dist/node_modules/@mui/icons-material/utils/createSvgIcon.js +3 -3
- package/dist/node_modules/@mui/material/styles/ThemeProvider.js +43 -0
- package/dist/node_modules/@mui/private-theming/ThemeProvider/ThemeProvider.js +59 -0
- package/dist/node_modules/@mui/private-theming/ThemeProvider/nested.js +5 -0
- package/dist/node_modules/@mui/private-theming/node_modules/@mui/utils/esm/exactProp/exactProp.js +19 -0
- package/dist/node_modules/@mui/private-theming/useTheme/ThemeContext.js +8 -0
- package/dist/node_modules/@mui/private-theming/useTheme/useTheme.js +12 -0
- package/dist/node_modules/@mui/system/colorManipulator.js +2 -2
- package/dist/node_modules/@mui/system/esm/DefaultPropsProvider/DefaultPropsProvider.js +12 -2
- package/dist/node_modules/@mui/system/esm/RtlProvider/index.js +14 -4
- package/dist/node_modules/@mui/system/esm/ThemeProvider/ThemeProvider.js +86 -0
- package/dist/node_modules/@mui/system/esm/ThemeProvider/useLayerOrder.js +50 -0
- package/dist/node_modules/@mui/system/esm/createStyled.js +1 -1
- package/dist/node_modules/@mui/system/node_modules/@mui/utils/esm/exactProp/exactProp.js +19 -0
- package/dist/node_modules/@mui/system/node_modules/@mui/utils/esm/useEnhancedEffect/useEnhancedEffect.js +5 -0
- package/dist/node_modules/@mui/system/node_modules/@mui/utils/esm/useId/useId.js +24 -0
- package/dist/node_modules/call-bind/index.js +3 -3
- package/dist/node_modules/es-abstract/2025/ToString.js +21 -0
- package/dist/node_modules/es-abstract-get/GetMethod.js +33 -0
- package/dist/node_modules/es-abstract-get/GetV.js +25 -0
- package/dist/node_modules/es-abstract-get/isPropertyKey.js +13 -0
- package/dist/node_modules/es-to-primitive/es2015.js +28 -28
- package/dist/node_modules/hoist-non-react-statics/node_modules/react-is/cjs/react-is.development.js +1 -1
- package/dist/node_modules/hoist-non-react-statics/node_modules/react-is/cjs/react-is.production.min.js +1 -1
- package/dist/node_modules/hoist-non-react-statics/node_modules/react-is/index.js +1 -1
- package/dist/node_modules/is-symbol/index.js +1 -1
- package/dist/node_modules/moment/dist/locale/de.js +21 -1
- package/dist/node_modules/moment/dist/moment.js +792 -624
- package/dist/node_modules/prop-types/node_modules/react-is/cjs/react-is.development.js +1 -1
- package/dist/node_modules/react-dates/lib/theme/DefaultTheme.js +4 -4
- package/dist/node_modules/react-dates/lib/utils/registerCSSInterfaceWithDefaultTheme.js +3 -3
- package/dist/node_modules/react-dates/lib/utils/registerInterfaceWithDefaultTheme.js +3 -3
- package/dist/node_modules/react-is/cjs/react-is.development.js +1 -1
- package/dist/node_modules/react-is/cjs/react-is.production.js +1 -1
- package/dist/node_modules/react-is/index.js +1 -1
- package/dist/node_modules/react-with-styles/lib/ThemedStyleSheet.js +6 -6
- package/dist/node_modules/react-with-styles-interface-css/dist/index.js +4 -4
- package/dist/node_modules/react-with-styles-interface-css/dist/utils/getClassName.js +3 -3
- package/dist/node_modules/react-with-styles-interface-css/dist/utils/separateStyles.js +4 -4
- package/dist/node_modules/string.prototype.trim/implementation.js +12 -4
- package/dist/node_modules/stylis/src/Parser.js +1 -1
- package/dist/node_modules/stylis/src/Tokenizer.js +1 -1
- package/dist/stories/LocaleDictionaryCollapse.d.ts +1 -1
- package/dist/stories/LocaleDictionaryCollapse.d.ts.map +1 -1
- package/dist/stories/designTokensStoryHelpers.d.ts +2 -0
- package/dist/stories/designTokensStoryHelpers.d.ts.map +1 -0
- package/dist/stories/i18nDemo.d.ts +1 -1
- package/dist/stories/i18nDemo.d.ts.map +1 -1
- package/dist/stories/tableV36ChatApiMock.d.ts +10 -29
- package/dist/stories/tableV36ChatApiMock.d.ts.map +1 -1
- package/dist/styles/style.css +1 -1
- package/dist/styles/tokens/accent-palettes.generated.d.ts +13 -0
- package/dist/styles/tokens/accent-palettes.generated.d.ts.map +1 -0
- package/dist/styles/tokens/accent-palettes.generated.js +8 -0
- package/dist/styles/tokens/effects.generated.d.ts +60 -0
- package/dist/styles/tokens/effects.generated.d.ts.map +1 -0
- package/dist/styles/tokens/effects.generated.js +80 -0
- package/dist/styles/tokens/tokens.generated.d.ts +438 -0
- package/dist/styles/tokens/tokens.generated.d.ts.map +1 -0
- package/dist/styles/tokens/tokens.generated.js +431 -0
- package/dist/tokens/ImpactUiAppProviders.d.ts +18 -0
- package/dist/tokens/ImpactUiAppProviders.d.ts.map +1 -0
- package/dist/tokens/ImpactUiAppProviders.js +23 -0
- package/dist/tokens/ImpactUiThemeProvider.d.ts +17 -0
- package/dist/tokens/ImpactUiThemeProvider.d.ts.map +1 -0
- package/dist/tokens/ImpactUiThemeProvider.js +86 -0
- package/dist/tokens/accentDom.d.ts +17 -0
- package/dist/tokens/accentDom.d.ts.map +1 -0
- package/dist/tokens/accentDom.js +44 -0
- package/dist/tokens/accentDom.test.d.ts +2 -0
- package/dist/tokens/accentDom.test.d.ts.map +1 -0
- package/dist/tokens/contrastDom.d.ts +18 -0
- package/dist/tokens/contrastDom.d.ts.map +1 -0
- package/dist/tokens/contrastDom.js +34 -0
- package/dist/tokens/contrastDom.test.d.ts +2 -0
- package/dist/tokens/contrastDom.test.d.ts.map +1 -0
- package/dist/tokens/createImpactUiMuiTheme.d.ts +13 -0
- package/dist/tokens/createImpactUiMuiTheme.d.ts.map +1 -0
- package/dist/tokens/createImpactUiMuiTheme.js +122 -0
- package/dist/tokens/impactUiPreferences.d.ts +25 -0
- package/dist/tokens/impactUiPreferences.d.ts.map +1 -0
- package/dist/tokens/impactUiPreferences.js +109 -0
- package/dist/tokens/impactUiPreferences.test.d.ts +2 -0
- package/dist/tokens/impactUiPreferences.test.d.ts.map +1 -0
- package/dist/tokens/index.d.ts +22 -0
- package/dist/tokens/index.d.ts.map +1 -0
- package/dist/tokens/interfaceStyleDom.d.ts +26 -0
- package/dist/tokens/interfaceStyleDom.d.ts.map +1 -0
- package/dist/tokens/interfaceStyleDom.js +58 -0
- package/dist/tokens/theme.types.d.ts +19 -0
- package/dist/tokens/theme.types.d.ts.map +1 -0
- package/dist/tokens/themeDom.d.ts +34 -0
- package/dist/tokens/themeDom.d.ts.map +1 -0
- package/dist/tokens/themeDom.js +97 -0
- package/dist/tokens/useImpactUiInterfaceStyle.d.ts +7 -0
- package/dist/tokens/useImpactUiInterfaceStyle.d.ts.map +1 -0
- package/dist/tokens/useImpactUiInterfaceStyle.js +19 -0
- package/dist/tokens/useImpactUiTheme.d.ts +9 -0
- package/dist/tokens/useImpactUiTheme.d.ts.map +1 -0
- package/dist/tokens/useImpactUiTheme.js +46 -0
- package/dist/utils/helper.d.ts +2 -0
- package/dist/utils/helper.d.ts.map +1 -1
- package/package.json +12 -3
- /package/dist/_virtual/{react-is.development2.js → react-is2.development.js} +0 -0
- /package/dist/_virtual/{react-is.production.min2.js → react-is2.production.min.js} +0 -0
- /package/dist/_virtual/{react-is.development3.js → react-is3.development.js} +0 -0
|
@@ -0,0 +1,1304 @@
|
|
|
1
|
+
const halloweenDevilLightHtml = `<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
6
|
+
<title>The Last Candle: Morning</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Creepster&display=swap">
|
|
10
|
+
<style>
|
|
11
|
+
html, body { margin: 0; height: 100%; background: #fff; overflow: hidden; }
|
|
12
|
+
canvas { display: block; }
|
|
13
|
+
#hint { position: fixed; inset: 0; display: grid; place-items: center; margin: 0; pointer-events: none; user-select: none;
|
|
14
|
+
font: 400 15px/1.5 "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif; letter-spacing: 0.05em;
|
|
15
|
+
color: #7f766a; transition: opacity 1.4s ease; }
|
|
16
|
+
#hint.gone { opacity: 0; }
|
|
17
|
+
</style>
|
|
18
|
+
<script type="importmap">
|
|
19
|
+
{ "imports": {
|
|
20
|
+
"three": "https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js",
|
|
21
|
+
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/"
|
|
22
|
+
} }
|
|
23
|
+
<\/script>
|
|
24
|
+
</head>
|
|
25
|
+
<body>
|
|
26
|
+
<p id="hint">Click anywhere to begin. Best with sound.</p>
|
|
27
|
+
<script type="module">
|
|
28
|
+
import * as THREE from 'three';
|
|
29
|
+
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
|
|
30
|
+
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
|
|
31
|
+
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
|
|
32
|
+
import { BokehPass } from 'three/addons/postprocessing/BokehPass.js';
|
|
33
|
+
import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
|
|
34
|
+
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
|
|
35
|
+
import { mergeGeometries, mergeVertices } from 'three/addons/utils/BufferGeometryUtils.js';
|
|
36
|
+
|
|
37
|
+
/* ==========================================================================
|
|
38
|
+
THE LAST CANDLE
|
|
39
|
+
Climax shot (designed first): camera at z 2.9, eye 1.62, 32mm-ish lens,
|
|
40
|
+
looking slightly up at the hooded devil dead centre, candle raised to its
|
|
41
|
+
face. A low morning sun back-lights the scene from the upper right; the candle
|
|
42
|
+
is the warm key on the hood and horns from below.
|
|
43
|
+
|
|
44
|
+
Timeline (seconds)
|
|
45
|
+
(story clock: the 20 s piece below plays in 18 s of video)
|
|
46
|
+
0.0 - 0.6 pure white
|
|
47
|
+
0.6 - 3.6 the white fades away, revealing the sunlit graveyard
|
|
48
|
+
2.4 - 10.6 bats come out of the fog and take their time flying past the lens, squeaking
|
|
49
|
+
0.8 - 6.5 morning sun, sky, haze and environment fade up; pumpkins kindle
|
|
50
|
+
1.5 - 3.5 a lone flame appears deep in the fog beyond the gate
|
|
51
|
+
2.2 - 14.4 the devil floats the length of the aisle (physical glide)
|
|
52
|
+
15.0 - 16.1 it lifts the candle to its face and draws breath
|
|
53
|
+
16.25- 16.9 it blows; the flame bends and gutters
|
|
54
|
+
1.0 - 16.9 a small "Applying Style...." status line at the bottom centre, dots loading
|
|
55
|
+
9.8 - 16.9 "Happy Halloween" burns in at the top-left corner: fire-lit letters,
|
|
56
|
+
a hot igniting edge, heat shimmer and rising embers
|
|
57
|
+
16.9 every light hits exactly 0
|
|
58
|
+
16.9 - 20.0 pure white
|
|
59
|
+
========================================================================== */
|
|
60
|
+
|
|
61
|
+
const QS = new URLSearchParams(location.search), EXPORT = QS.has('export'), RENDER = QS.has('render') || EXPORT; // ?render: frame-by-frame capture; ?export: save an MP4 in the browser
|
|
62
|
+
const T = { revealIn: 0.8, revealFull: 6.5, devilGo: 2.2, devilArrive: 14.4,
|
|
63
|
+
lift0: 15.0, lift1: 16.1, blow0: 16.25, out: 16.9, end: 20.0, loop: 22.0 };
|
|
64
|
+
const Z_START = -31.0, Z_END = 0.4; // devil path
|
|
65
|
+
const CAM_Z0 = 9.0, CAM_Z1 = 2.9; // dolly path
|
|
66
|
+
const CAM_Y0 = 1.74, CAM_Y1 = 1.62;
|
|
67
|
+
const FOG_DENSITY = 0.026; // light morning haze
|
|
68
|
+
// light levels at full reveal (physical units: lux for the sun, candela for point lights)
|
|
69
|
+
const K = { sun: 3.8, hemi: 1.05, env: 0.4, candle: 1.1, pump: 0.42, ap0: 0.00025, ap1: 0.0013 };
|
|
70
|
+
|
|
71
|
+
/* ---------- deterministic randomness ---------- */
|
|
72
|
+
function mulberry32(a) { return function () { a |= 0; a = a + 0x6D2B79F5 | 0; let t = Math.imul(a ^ a >>> 15, 1 | a); t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t; return ((t ^ t >>> 14) >>> 0) / 4294967296; }; }
|
|
73
|
+
const rng = mulberry32(31102026);
|
|
74
|
+
const rand = (a = 0, b = 1) => a + (b - a) * rng();
|
|
75
|
+
function hash1(n) { const x = Math.sin(n * 127.1 + 311.7) * 43758.5453; return x - Math.floor(x); }
|
|
76
|
+
function noise1(x) { const i = Math.floor(x), f = x - i, u = f * f * (3 - 2 * f); return hash1(i) * (1 - u) + hash1(i + 1) * u; }
|
|
77
|
+
function fbm1(x) { return 0.5 * noise1(x) + 0.3 * noise1(x * 2.03 + 7.1) + 0.2 * noise1(x * 4.11 + 3.3); }
|
|
78
|
+
|
|
79
|
+
/* ---------- easing ---------- */
|
|
80
|
+
const clamp = (x, a = 0, b = 1) => Math.min(b, Math.max(a, x));
|
|
81
|
+
const lerp = (a, b, t) => a + (b - a) * t;
|
|
82
|
+
const smooth = (e0, e1, x) => { const t = clamp((x - e0) / (e1 - e0)); return t * t * (3 - 2 * t); };
|
|
83
|
+
const smoother = (e0, e1, x) => { const t = clamp((x - e0) / (e1 - e0)); return t * t * t * (t * (t * 6 - 15) + 10); };
|
|
84
|
+
// long-tailed ease: very gentle start and an extremely soft landing
|
|
85
|
+
function easeLong(u) { u = clamp(u); const p = 2.3, a = Math.pow(u, p), b = Math.pow(1 - u, p); return a / (a + b); }
|
|
86
|
+
// glide profile: speed ~ u^1.6 (1-u)^2.6, integrated once (a drifting body that bleeds speed slowly)
|
|
87
|
+
const TRAVEL = (() => { const N = 2048, arr = new Float32Array(N + 1); let s = 0;
|
|
88
|
+
for (let i = 1; i <= N; i++) { const u = (i - 0.5) / N; s += Math.pow(u, 1.6) * Math.pow(1 - u, 2.6); arr[i] = s; }
|
|
89
|
+
for (let i = 0; i <= N; i++) arr[i] /= s; return arr; })();
|
|
90
|
+
function travel(u) { u = clamp(u); const f = u * 2048, i = Math.min(2047, Math.floor(f)); return lerp(TRAVEL[i], TRAVEL[i + 1], f - i); }
|
|
91
|
+
function devilZ(t) { return lerp(Z_START, Z_END, travel((t - T.devilGo) / (T.devilArrive - T.devilGo))); }
|
|
92
|
+
|
|
93
|
+
/* ---------- bats: a small colony bursts out of the fog and flies past the lens ----------
|
|
94
|
+
Shared by the picture and the sound so every squeak follows its own bat. Story-clock times. */
|
|
95
|
+
const BATS = (() => {
|
|
96
|
+
const r = mulberry32(4242), out = [];
|
|
97
|
+
for (let i = 0; i < 14; i++) {
|
|
98
|
+
const side = i % 2 ? 1 : -1, hero = i === 0; // bat 0 skims right past the lens
|
|
99
|
+
out.push({ t0: hero ? 6.2 : 2.4 + r() * 3.0, dur: hero ? 4.4 : 4.2 + r() * 1.6, // long, unhurried flights
|
|
100
|
+
a: [(r() - 0.5) * 9, 1.8 + r() * 2.8, -34 + r() * 10],
|
|
101
|
+
b: hero ? [0.5, 0.35] : [side * (1.3 + r() * 2.4), 0.7 + r() * 1.9],
|
|
102
|
+
ph: r() * 6.28, rate: 4.5 + r() * 1.5, wob: 0.3 + r() * 0.4, span: 0.52 + r() * 0.18 });
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
})();
|
|
106
|
+
function camAt(t) { const cu = easeLong(t / T.out); return [0, lerp(CAM_Y0, CAM_Y1, cu), lerp(CAM_Z0, CAM_Z1, cu)]; }
|
|
107
|
+
function batPos(bt, t) {
|
|
108
|
+
const u = (t - bt.t0) / bt.dur; if (u < 0 || u > 1) return null;
|
|
109
|
+
const c = camAt(bt.t0 + bt.dur), e = u * u * (3 - 2 * u) * 0.35 + u * 0.65;
|
|
110
|
+
return [lerp(bt.a[0], c[0] + bt.b[0], e) + bt.wob * Math.sin(u * 11 + bt.ph),
|
|
111
|
+
lerp(bt.a[1], c[1] + bt.b[1], e) + 0.6 * bt.wob * Math.sin(u * 14 + bt.ph * 2),
|
|
112
|
+
lerp(bt.a[2], c[2] + 2.5, e)];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* ---------- sound: fully procedural, scheduled on the same timeline ----------
|
|
116
|
+
0.4 - 5.5 wind rises with the reveal; a whistle threads through the trees
|
|
117
|
+
2.6 a distant bell as the flame appears beyond the gate
|
|
118
|
+
2.0 - 16.9 the candle hisses and crackles, louder as it nears the camera
|
|
119
|
+
6.5 - 16.3 a dissonant drone gathers; the wind drains away
|
|
120
|
+
8.7 the bell again
|
|
121
|
+
10.6 - 15.0 a heartbeat, quickening, then stopping dead
|
|
122
|
+
15.3 - 16.2 it draws breath
|
|
123
|
+
16.22- 18.8 it blows the candle out: a breathy gust, the flame flutters and dies at
|
|
124
|
+
16.9, and the breath carries on into the dark, trailing off
|
|
125
|
+
1.5, 8.0 a ghostly, theremin-like wail drifts across the speakers
|
|
126
|
+
16.9 everything else stops with the light; only the breath lingers, then silence
|
|
127
|
+
------------------------------------------------------------------------- */
|
|
128
|
+
const sndCache = new WeakMap();
|
|
129
|
+
function noiseBuf(ctx, seconds, kind, seed) {
|
|
130
|
+
const sr = ctx.sampleRate, fade = Math.floor(0.05 * sr), n = Math.floor(seconds * sr) + fade, b = ctx.createBuffer(2, n, sr);
|
|
131
|
+
for (let ch = 0; ch < 2; ch++) {
|
|
132
|
+
const r = mulberry32(seed + ch * 7919), d = b.getChannelData(ch);
|
|
133
|
+
let b0 = 0, b1 = 0, b2 = 0, last = 0;
|
|
134
|
+
for (let i = 0; i < n; i++) {
|
|
135
|
+
const w = r() * 2 - 1;
|
|
136
|
+
if (kind === 'white') d[i] = w * 0.7;
|
|
137
|
+
else if (kind === 'pink') { b0 = 0.99765 * b0 + w * 0.099046; b1 = 0.963 * b1 + w * 0.2965164; b2 = 0.57 * b2 + w * 1.0526913; d[i] = (b0 + b1 + b2 + w * 0.1848) * 0.2; }
|
|
138
|
+
else { last = (last + 0.02 * w) / 1.02; d[i] = last * 3.2; }
|
|
139
|
+
}
|
|
140
|
+
// seamless loop: the head fades in from the tail that will precede it
|
|
141
|
+
for (let i = 0; i < fade; i++) { const k = i / fade; d[i] = d[i] * k + d[n - fade + i] * (1 - k); }
|
|
142
|
+
}
|
|
143
|
+
b.__loopEnd = (n - fade) / sr;
|
|
144
|
+
return b;
|
|
145
|
+
}
|
|
146
|
+
function impulseBuf(ctx, seconds, seed) {
|
|
147
|
+
const sr = ctx.sampleRate, n = Math.floor(seconds * sr), b = ctx.createBuffer(2, n, sr), pre = Math.floor(0.018 * sr);
|
|
148
|
+
for (let ch = 0; ch < 2; ch++) {
|
|
149
|
+
const r = mulberry32(seed + ch * 31), d = b.getChannelData(ch); let lp = 0;
|
|
150
|
+
for (let i = 0; i < n; i++) {
|
|
151
|
+
const x = i / n, w = r() * 2 - 1; lp += (w - lp) * (0.6 - 0.5 * x);
|
|
152
|
+
d[i] = i < pre ? 0 : lp * Math.pow(1 - x, 1.6) * Math.exp(-3.0 * x);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return b;
|
|
156
|
+
}
|
|
157
|
+
// The picture is authored on a 20 s story clock and plays in 18 s of video. The soundtrack keeps
|
|
158
|
+
// natural speed: each sound STARTS at its story moment mapped into video time (V), but plays out
|
|
159
|
+
// at its own real-time pace: bell decay, heartbeat tempo, wail, crackles, squeaks, the breath.
|
|
160
|
+
const STORY_SCALE = 20 / 18, V = s => s / STORY_SCALE;
|
|
161
|
+
const WHITE_BLANK = true; // morning: open on white, fade into the scene, cut to white at the end
|
|
162
|
+
|
|
163
|
+
const BAT_SQUEAK = 0.16, BAT_WINGS = 0.8;
|
|
164
|
+
function buildSound(ctx, t0, dest) {
|
|
165
|
+
const S = sndCache.get(ctx) || (() => { const c = { white: noiseBuf(ctx, 6, 'white', 101), pink: noiseBuf(ctx, 7, 'pink', 202), brown: noiseBuf(ctx, 8, 'brown', 303), ir: impulseBuf(ctx, 4.5, 404) }; sndCache.set(ctx, c); return c; })();
|
|
166
|
+
const R = mulberry32(1666);
|
|
167
|
+
const END = T.out + 0.2, END_V = V(END); // story / video
|
|
168
|
+
const automate = (param, a, b, fn, rate = 30) => { // a, b and fn in story time; scheduled in video time
|
|
169
|
+
const va = V(a), vb = V(b), n = Math.max(2, Math.ceil((vb - va) * rate)), v = new Float32Array(n);
|
|
170
|
+
for (let i = 0; i < n; i++) v[i] = fn((va + (vb - va) * i / (n - 1)) * STORY_SCALE);
|
|
171
|
+
param.value = v[0]; param.setValueCurveAtTime(v, t0 + va, vb - va);
|
|
172
|
+
};
|
|
173
|
+
const filter = (type, f, q) => { const x = ctx.createBiquadFilter(); x.type = type; x.frequency.value = f; if (q !== undefined) x.Q.value = q; return x; };
|
|
174
|
+
|
|
175
|
+
// master: everything dies on the frame the lights do
|
|
176
|
+
const master = ctx.createGain(); master.gain.value = 0;
|
|
177
|
+
master.gain.setValueAtTime(0.9, t0); master.gain.setValueAtTime(0.9, t0 + V(T.out) - 0.004); master.gain.linearRampToValueAtTime(0, t0 + V(T.out) + 0.025);
|
|
178
|
+
const comp = ctx.createDynamicsCompressor(); comp.threshold.value = -18; comp.knee.value = 12; comp.ratio.value = 3; comp.attack.value = 0.008; comp.release.value = 0.3;
|
|
179
|
+
master.connect(comp); comp.connect(dest);
|
|
180
|
+
const verb = ctx.createConvolver(); verb.buffer = S.ir;
|
|
181
|
+
const verbOut = ctx.createGain(); verbOut.gain.value = 0.5; verb.connect(filter('lowpass', 3000, 0.5)).connect(verbOut).connect(master);
|
|
182
|
+
const dry = ctx.createGain(); dry.connect(master);
|
|
183
|
+
const voice = (fn, send, rate) => { const g = ctx.createGain(); automate(g.gain, 0, T.out + 0.05, fn, rate); g.connect(dry); if (send) { const s = ctx.createGain(); s.gain.value = send; g.connect(s).connect(verb); } return g; };
|
|
184
|
+
const loop = (buf, offset) => { const s = ctx.createBufferSource(); s.buffer = buf; s.loop = true; s.loopEnd = buf.__loopEnd; s.start(t0, offset % (buf.__loopEnd - 0.1)); s.stop(t0 + END_V); return s; };
|
|
185
|
+
const osc = (type, f) => { const o = ctx.createOscillator(); o.type = type; o.frequency.value = f; o.start(t0); o.stop(t0 + END_V); return o; };
|
|
186
|
+
|
|
187
|
+
// shared curves, same functions the picture uses
|
|
188
|
+
const camZ = t => lerp(CAM_Z0, CAM_Z1, easeLong(t / T.out));
|
|
189
|
+
const near = t => { const d = Math.max(0.5, camZ(t) - devilZ(t) - 0.4); return 1 / (1 + (d / 2.6) ** 2); };
|
|
190
|
+
const flameLvl = t => smoother(1.5, 3.5, t) * near(t);
|
|
191
|
+
const windLvl = t => smoother(0.4, 5.5, t) * (0.65 + 0.7 * (fbm1(t * 0.45 + 3.1) - 0.3)) * (1 - 0.72 * smoother(10.5, 15.2, t));
|
|
192
|
+
|
|
193
|
+
// wind: low body + a thin whistle wandering left to right
|
|
194
|
+
{ const lp = filter('lowpass', 400, 0.5); automate(lp.frequency, 0, END, t => 240 + 460 * fbm1(t * 0.45 + 3.1), 20);
|
|
195
|
+
loop(S.brown, 1.3).connect(lp).connect(voice(t => 0.5 * windLvl(t), 0.25, 30)); }
|
|
196
|
+
{ const bp = filter('bandpass', 700, 7); automate(bp.frequency, 0, END, t => 420 + 900 * (fbm1(t * 0.35 + 11) - 0.2) + 160 * Math.sin(t * 0.7), 30);
|
|
197
|
+
const pan = ctx.createStereoPanner(); automate(pan.pan, 0, END, t => 0.65 * Math.sin(t * 0.21 + 1.0), 10);
|
|
198
|
+
loop(S.white, 2.2).connect(bp).connect(pan).connect(voice(t => 0.2 * windLvl(t) * smooth(0.5, 0.78, fbm1(t * 0.55 + 5)) * (1 - smoother(9.5, 13.5, t)), 0.7, 30)); }
|
|
199
|
+
|
|
200
|
+
// sub drone, then a dissonant cluster (A, B flat, E flat) that opens up as it nears
|
|
201
|
+
{ const lp = filter('lowpass', 170, 0.7); lp.connect(voice(t => 0.2 * smoother(2.0, 9, t) + 0.14 * smoother(9, 16.2, t), 0.3, 20));
|
|
202
|
+
[[43.65, 'sine', 1], [43.95, 'sine', 0.8], [65.41, 'triangle', 0.4], [87.31, 'triangle', 0.25]].forEach(([f, ty, a]) => { const g = ctx.createGain(); g.gain.value = a; osc(ty, f).connect(g).connect(lp); }); }
|
|
203
|
+
{ const lp = filter('lowpass', 300, 1.8); automate(lp.frequency, 0, END, t => 240 + 950 * smoother(7, 16.2, t) + 600 * smoother(16.25, 16.7, t), 20);
|
|
204
|
+
lp.connect(voice(t => 0.07 * smoother(6.5, 14.5, t) + 0.07 * smoother(15.0, 16.3, t), 0.9, 20));
|
|
205
|
+
const lfo = osc('sine', 4.7), depth = ctx.createGain(); depth.gain.value = 7; lfo.connect(depth);
|
|
206
|
+
[[110, -4, 0.5], [116.54, 3, 0.5], [155.56, -2, 0.45], [220, 5, 0.22], [233.08, -6, 0.22]].forEach(([f, det, a]) => {
|
|
207
|
+
const o = osc('sawtooth', f); o.detune.value = det; depth.connect(o.detune); const g = ctx.createGain(); g.gain.value = a; o.connect(g).connect(lp); }); }
|
|
208
|
+
|
|
209
|
+
// a far-off bell, mostly heard as its reverb
|
|
210
|
+
const bell = (tb, f, amp) => {
|
|
211
|
+
const lp = filter('lowpass', 2200), g = ctx.createGain(); g.gain.value = amp; lp.connect(g); g.connect(dry);
|
|
212
|
+
const s = ctx.createGain(); s.gain.value = 2.0; g.connect(s).connect(verb);
|
|
213
|
+
[[0.5, 0.35, 7], [1, 1, 5], [1.183, 0.55, 3.8], [1.506, 0.4, 3], [2.0, 0.45, 2.6], [2.514, 0.28, 2], [2.662, 0.22, 1.7], [3.011, 0.16, 1.4], [4.166, 0.08, 0.9]].forEach(([r, a, d]) => {
|
|
214
|
+
const o = ctx.createOscillator(); o.frequency.value = f * r; const e = ctx.createGain(); e.gain.value = 0;
|
|
215
|
+
e.gain.setValueAtTime(0, t0 + tb); e.gain.linearRampToValueAtTime(a, t0 + tb + 0.005); e.gain.exponentialRampToValueAtTime(0.0001, t0 + tb + d);
|
|
216
|
+
o.connect(e).connect(lp); o.start(t0 + tb); o.stop(t0 + Math.min(tb + d + 0.05, END_V)); });
|
|
217
|
+
};
|
|
218
|
+
bell(V(2.6), 146.83, 0.07); bell(V(8.7), 146.83, 0.055);
|
|
219
|
+
|
|
220
|
+
// heartbeat: lub-dub, quickening, stopping before the breath
|
|
221
|
+
{ const lp = filter('lowpass', 260, 0.8); lp.connect(dry);
|
|
222
|
+
for (let tb = V(10.6); tb < V(15.0);) {
|
|
223
|
+
const k = smoother(10.6, 15.0, tb * STORY_SCALE), amp = 0.55 * (0.3 + 0.7 * k);
|
|
224
|
+
for (const [off, a] of [[0, 1], [0.27, 0.6]]) {
|
|
225
|
+
const ts = t0 + tb + off;
|
|
226
|
+
for (const [mul, ty, w] of [[1, 'sine', 1], [2, 'triangle', 0.35]]) {
|
|
227
|
+
const o = ctx.createOscillator(); o.type = ty; o.frequency.setValueAtTime(72 * mul, ts); o.frequency.exponentialRampToValueAtTime(44 * mul, ts + 0.15);
|
|
228
|
+
const e = ctx.createGain(); e.gain.value = 0; e.gain.setValueAtTime(0, ts); e.gain.linearRampToValueAtTime(amp * a * w, ts + 0.012); e.gain.exponentialRampToValueAtTime(0.0001, ts + 0.24);
|
|
229
|
+
o.connect(e).connect(lp); o.start(ts); o.stop(ts + 0.26);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
tb += lerp(1.05, 0.68, k);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// candle: soft hiss with flicker, and sparse crackles
|
|
237
|
+
{ const bp = filter('bandpass', 1100, 0.7);
|
|
238
|
+
loop(S.pink, 3.7).connect(bp).connect(voice(t => 0.22 * flameLvl(t) * (0.75 + 0.25 * noise1(V(t) * 14)), 0.15, 60)); }
|
|
239
|
+
{ let tp = V(2.0);
|
|
240
|
+
while (tp < V(T.out) - 0.03) {
|
|
241
|
+
const blow = smooth(16.3, 16.6, tp * STORY_SCALE), lvl = flameLvl(tp * STORY_SCALE) * (1 + 1.5 * blow);
|
|
242
|
+
const dur = 0.004 + R() * 0.016, src = ctx.createBufferSource(); src.buffer = S.white;
|
|
243
|
+
const bp = filter('bandpass', 1800 + R() * 3200, 1.2), e = ctx.createGain(), pan = ctx.createStereoPanner(); pan.pan.value = (R() - 0.5) * 0.3;
|
|
244
|
+
const a = 0.9 * lvl * (0.25 + 0.75 * R() * R());
|
|
245
|
+
e.gain.value = 0; e.gain.setValueAtTime(0, t0 + tp); e.gain.linearRampToValueAtTime(a, t0 + tp + 0.001); e.gain.exponentialRampToValueAtTime(0.0001, t0 + tp + dur);
|
|
246
|
+
src.connect(bp).connect(e).connect(pan).connect(dry); src.start(t0 + tp, R() * 5); src.stop(t0 + tp + dur + 0.01);
|
|
247
|
+
tp += (0.08 + R() * 0.32) / (1 + 2.5 * blow);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// breath in: rising, hoarse
|
|
252
|
+
{ const bp = filter('bandpass', 500, 1.4); automate(bp.frequency, 0, END, t => 420 + 950 * smooth(15.3, 16.15, t), 40);
|
|
253
|
+
const hs = filter('highshelf', 2500); hs.gain.value = 5;
|
|
254
|
+
loop(S.pink, 0.7).connect(bp).connect(hs).connect(voice(t => 1.1 * smooth(15.3, 16.05, t) * (1 - smooth(16.05, 16.24, t)) * (0.85 + 0.15 * noise1(V(t) * 30)), 0.35, 60)); }
|
|
255
|
+
// breath out: blowing the candle out. The gust is routed past the master cut, so while the
|
|
256
|
+
// flame's flutter and everything else stop with the light, the breath itself carries on into
|
|
257
|
+
// the dark and trails off, with its own bit of room.
|
|
258
|
+
{
|
|
259
|
+
const B0 = T.blow0 - 0.03, PEAK_END = 16.6, TAIL_END = T.out + 2.3;
|
|
260
|
+
const tail = ctx.createGain(); tail.connect(comp);
|
|
261
|
+
const tailVerb = ctx.createConvolver(); tailVerb.buffer = S.ir;
|
|
262
|
+
const tailVerbOut = ctx.createGain(); tailVerbOut.gain.value = 0.45; tailVerb.connect(filter('lowpass', 3000, 0.5)).connect(tailVerbOut).connect(comp);
|
|
263
|
+
tailVerbOut.gain.setValueAtTime(0.45, t0 + V(TAIL_END) - 0.15); tailVerbOut.gain.linearRampToValueAtTime(0, t0 + V(TAIL_END) + 0.45); // echo gone before the video ends
|
|
264
|
+
const gust = t => t < B0 ? 0 : smooth(B0, B0 + 0.1, t) * (t < PEAK_END ? 1 : Math.exp(-(t - PEAK_END) / (0.55 * STORY_SCALE)))
|
|
265
|
+
* (1 - smooth(TAIL_END - 0.6, TAIL_END - 0.2, t)) * (0.85 + 0.15 * noise1(V(t) * 40)); // trails ~1.7 s past the blow-out
|
|
266
|
+
// the breath: airy "fffhhh", losing pressure (and pitch) as it fades
|
|
267
|
+
const air = ctx.createBufferSource(); air.buffer = S.white; air.loop = true; air.loopEnd = S.white.__loopEnd;
|
|
268
|
+
const bp = filter('bandpass', 2300, 0.6); automate(bp.frequency, 0, TAIL_END, t => 2300 - 900 * smooth(16.5, 18.2, t), 30);
|
|
269
|
+
const g = ctx.createGain(); automate(g.gain, 0, TAIL_END, t => 0.44 * gust(t), 120); // about 4 dB softer
|
|
270
|
+
const send = ctx.createGain(); send.gain.value = 0.35;
|
|
271
|
+
air.connect(filter('highpass', 350)).connect(bp).connect(filter('lowpass', 6500)).connect(g).connect(tail); g.connect(send).connect(tailVerb);
|
|
272
|
+
air.start(t0, 1.7); air.stop(t0 + V(TAIL_END) + 0.05);
|
|
273
|
+
// a low puff as the breath leaves
|
|
274
|
+
const puff = ctx.createBufferSource(); puff.buffer = S.brown; puff.loop = true; puff.loopEnd = S.brown.__loopEnd;
|
|
275
|
+
const pg = ctx.createGain(); automate(pg.gain, 0, 17.4, t => 0.57 * smooth(B0, B0 + 0.06, t) * Math.exp(-Math.max(0, t - B0 - 0.06) / (0.25 * STORY_SCALE)), 120);
|
|
276
|
+
puff.connect(filter('lowpass', 260)).connect(pg).connect(tail); puff.start(t0, 4.1); puff.stop(t0 + V(17.45));
|
|
277
|
+
}
|
|
278
|
+
// the flame flutters in the gust and dies with the light (inside the master, so it stops at 16.9)
|
|
279
|
+
{ const env = t => smooth(16.28, 16.4, t);
|
|
280
|
+
loop(S.pink, 2.9).connect(filter('bandpass', 750, 1.2)).connect(voice(t => 0.35 * env(t) * (0.6 + 0.4 * Math.sin(V(t) * Math.PI * 2 * 18)), 0.2, 220)); }
|
|
281
|
+
// bats: squeaks and wingbeats that follow each bat's flight past the lens
|
|
282
|
+
for (const bt of BATS) {
|
|
283
|
+
const R = mulberry32(Math.floor(bt.ph * 1000) + 17);
|
|
284
|
+
const rel = t => { const p = batPos(bt, t), c = camAt(t); return p ? [p[0] - c[0], p[1] - c[1], p[2] - c[2]] : null; };
|
|
285
|
+
const loud = t => { const q = rel(t); return q ? 1 / (1 + (Math.hypot(q[0], q[1], q[2]) / 3.5) ** 2) : 0; };
|
|
286
|
+
const panAt = t => { const q = rel(t); return q ? clamp(q[0] / (Math.abs(q[2]) * 0.6 + Math.abs(q[0]) + 0.3), -1, 1) : 0; };
|
|
287
|
+
for (let t = V(bt.t0) + 0.05; t < V(bt.t0 + bt.dur) - 0.05;) {
|
|
288
|
+
const ts = t * STORY_SCALE, f0 = 7000 + R() * 2800, d = 0.018 + R() * 0.02, a = BAT_SQUEAK * loud(ts) * (0.5 + 0.5 * R());
|
|
289
|
+
if (a > 0.002) {
|
|
290
|
+
const o = ctx.createOscillator(); o.frequency.setValueAtTime(f0, t0 + t); o.frequency.exponentialRampToValueAtTime(f0 * 0.45, t0 + t + d);
|
|
291
|
+
const g = ctx.createGain(); g.gain.value = 0; g.gain.setValueAtTime(0, t0 + t); g.gain.linearRampToValueAtTime(a, t0 + t + 0.002); g.gain.exponentialRampToValueAtTime(0.0001, t0 + t + d);
|
|
292
|
+
const p = ctx.createStereoPanner(); p.pan.value = panAt(ts);
|
|
293
|
+
o.connect(g).connect(p).connect(dry); o.start(t0 + t); o.stop(t0 + t + d + 0.01);
|
|
294
|
+
}
|
|
295
|
+
t += R() < 0.35 ? 0.03 + R() * 0.03 : 0.1 + R() * 0.2;
|
|
296
|
+
}
|
|
297
|
+
const n = ctx.createBufferSource(); n.buffer = S.white; n.loop = true; n.loopEnd = S.white.__loopEnd;
|
|
298
|
+
const g = ctx.createGain(), p = ctx.createStereoPanner();
|
|
299
|
+
automate(g.gain, bt.t0, bt.t0 + bt.dur, t => BAT_WINGS * loud(t) ** 1.5 * Math.pow(0.5 + 0.5 * Math.sin(2 * Math.PI * bt.rate * t + bt.ph), 3), 240);
|
|
300
|
+
automate(p.pan, bt.t0, bt.t0 + bt.dur, panAt, 60);
|
|
301
|
+
n.connect(filter('bandpass', 750, 0.9)).connect(g).connect(p).connect(dry); n.start(t0 + V(bt.t0), R() * 2); n.stop(t0 + V(bt.t0 + bt.dur) + 0.02);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// ghostly wail: a theremin-like voice that rises and falls with vibrato, drifting across the speakers
|
|
305
|
+
const voiceCurve = (t, dur, freqFn, ampFn, type, pan, lpf) => {
|
|
306
|
+
const n = Math.max(8, Math.ceil(dur * 120)), fq = new Float32Array(n), am = new Float32Array(n);
|
|
307
|
+
for (let i = 0; i < n; i++) { const u = i / (n - 1); fq[i] = freqFn(u); am[i] = ampFn(u); }
|
|
308
|
+
am[n - 1] = 0;
|
|
309
|
+
const o = ctx.createOscillator(); o.type = type; o.frequency.value = fq[0]; o.frequency.setValueCurveAtTime(fq, t0 + t, dur);
|
|
310
|
+
const g = ctx.createGain(); g.gain.value = 0; g.gain.setValueCurveAtTime(am, t0 + t, dur);
|
|
311
|
+
const p = ctx.createStereoPanner(); p.pan.setValueAtTime(pan[0], t0 + t); p.pan.linearRampToValueAtTime(pan[1], t0 + t + dur);
|
|
312
|
+
const send = ctx.createGain(); send.gain.value = 0.35;
|
|
313
|
+
o.connect(filter('lowpass', lpf)).connect(g).connect(p); p.connect(dry); p.connect(send).connect(verb);
|
|
314
|
+
o.start(t0 + t); o.stop(t0 + t + dur + 0.02);
|
|
315
|
+
};
|
|
316
|
+
const wail = (t, pan, k = 1) => {
|
|
317
|
+
const vib = u => 1 + 0.03 * Math.sin(u * 2.3 * 2 * Math.PI * 5.5), shape = u => Math.sin(Math.PI * Math.min(1, u * 1.3));
|
|
318
|
+
voiceCurve(t, 2.3, u => (520 + 380 * shape(u)) * vib(u), u => k * WAIL * Math.sin(Math.PI * u), 'sine', pan, 3000);
|
|
319
|
+
voiceCurve(t, 2.3, u => (260 + 190 * shape(u)) * vib(u), u => k * WAIL * 0.4 * Math.sin(Math.PI * u), 'triangle', pan, 1500);
|
|
320
|
+
};
|
|
321
|
+
const WAIL = 0.09;
|
|
322
|
+
wail(V(1.5), [0.7, -0.7]); // as the graveyard fades up, right to left
|
|
323
|
+
wail(V(8.0), [-0.7, 0.7], 1.7); // again in the middle, left to right (louder: the drone is up by then)
|
|
324
|
+
}
|
|
325
|
+
function wavBase64(buf) {
|
|
326
|
+
const ch = buf.numberOfChannels, n = buf.length, sr = buf.sampleRate, bytes = new Uint8Array(44 + n * ch * 2), v = new DataView(bytes.buffer);
|
|
327
|
+
const str = (o, s) => { for (let i = 0; i < s.length; i++) v.setUint8(o + i, s.charCodeAt(i)); };
|
|
328
|
+
str(0, 'RIFF'); v.setUint32(4, 36 + n * ch * 2, true); str(8, 'WAVE'); str(12, 'fmt '); v.setUint32(16, 16, true); v.setUint16(20, 1, true); v.setUint16(22, ch, true);
|
|
329
|
+
v.setUint32(24, sr, true); v.setUint32(28, sr * ch * 2, true); v.setUint16(32, ch * 2, true); v.setUint16(34, 16, true); str(36, 'data'); v.setUint32(40, n * ch * 2, true);
|
|
330
|
+
const data = []; for (let c = 0; c < ch; c++) data.push(buf.getChannelData(c));
|
|
331
|
+
let o = 44; for (let i = 0; i < n; i++) for (let c = 0; c < ch; c++) { const s = Math.max(-1, Math.min(1, data[c][i])); v.setInt16(o, s < 0 ? s * 0x8000 : s * 0x7fff, true); o += 2; }
|
|
332
|
+
let bin = ''; for (let i = 0; i < bytes.length; i += 0x8000) bin += String.fromCharCode.apply(null, bytes.subarray(i, i + 0x8000));
|
|
333
|
+
return btoa(bin);
|
|
334
|
+
}
|
|
335
|
+
async function renderSoundtrack() {
|
|
336
|
+
const ctx = new OfflineAudioContext(2, Math.ceil(T.end / STORY_SCALE * 48000), 48000);
|
|
337
|
+
buildSound(ctx, 0, ctx.destination);
|
|
338
|
+
return wavBase64(await ctx.startRendering());
|
|
339
|
+
}
|
|
340
|
+
/* ---------- end sound ---------- */
|
|
341
|
+
|
|
342
|
+
/* ---------- renderer ---------- */
|
|
343
|
+
let W = RENDER ? 1920 : innerWidth, H = RENDER ? 1080 : innerHeight;
|
|
344
|
+
const renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: 'high-performance', preserveDrawingBuffer: RENDER });
|
|
345
|
+
let pixelRatio = RENDER ? 1 : Math.min(devicePixelRatio, 1.5);
|
|
346
|
+
renderer.setPixelRatio(pixelRatio);
|
|
347
|
+
renderer.setSize(W, H);
|
|
348
|
+
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
|
349
|
+
renderer.toneMappingExposure = 1.0;
|
|
350
|
+
renderer.shadowMap.enabled = true;
|
|
351
|
+
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
|
352
|
+
renderer.setClearColor(0x000000, 1);
|
|
353
|
+
document.body.appendChild(renderer.domElement);
|
|
354
|
+
const maxAniso = renderer.capabilities.getMaxAnisotropy();
|
|
355
|
+
|
|
356
|
+
const scene = new THREE.Scene();
|
|
357
|
+
const FOG_BASE = new THREE.Color(0xb3c2d2); // pale blue morning haze
|
|
358
|
+
scene.fog = new THREE.FogExp2(0x000000, FOG_DENSITY);
|
|
359
|
+
scene.background = new THREE.Color(0x000000);
|
|
360
|
+
const pmrem = new THREE.PMREMGenerator(renderer);
|
|
361
|
+
scene.environment = pmrem.fromScene(new RoomEnvironment(), 0.04).texture;
|
|
362
|
+
scene.environmentIntensity = 0;
|
|
363
|
+
|
|
364
|
+
const camera = new THREE.PerspectiveCamera(32, W / H, 0.1, 100);
|
|
365
|
+
camera.position.set(0, CAM_Y0, CAM_Z0);
|
|
366
|
+
|
|
367
|
+
/* ---------- procedural textures (seeded, tileable) ---------- */
|
|
368
|
+
function tileNoise(size, cells, seed) {
|
|
369
|
+
const r = mulberry32(seed), g = new Float32Array(cells * cells);
|
|
370
|
+
for (let i = 0; i < g.length; i++) g[i] = r();
|
|
371
|
+
const out = new Float32Array(size * size);
|
|
372
|
+
for (let y = 0; y < size; y++) {
|
|
373
|
+
const fy = y / size * cells, iy = Math.floor(fy), ty = fy - iy, sy = ty * ty * (3 - 2 * ty), y0 = iy % cells, y1 = (iy + 1) % cells;
|
|
374
|
+
for (let x = 0; x < size; x++) {
|
|
375
|
+
const fx = x / size * cells, ix = Math.floor(fx), tx = fx - ix, sx = tx * tx * (3 - 2 * tx), x0 = ix % cells, x1 = (ix + 1) % cells;
|
|
376
|
+
const a = g[y0 * cells + x0], b = g[y0 * cells + x1], c = g[y1 * cells + x0], d = g[y1 * cells + x1];
|
|
377
|
+
out[y * size + x] = (a * (1 - sx) + b * sx) * (1 - sy) + (c * (1 - sx) + d * sx) * sy;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return out;
|
|
381
|
+
}
|
|
382
|
+
function fbmField(size, base, oct, seed) {
|
|
383
|
+
const acc = new Float32Array(size * size); let amp = 0.5, tot = 0;
|
|
384
|
+
for (let o = 0; o < oct; o++) { const n = tileNoise(size, base << o, seed + o * 101); for (let i = 0; i < acc.length; i++) acc[i] += n[i] * amp; tot += amp; amp *= 0.5; }
|
|
385
|
+
for (let i = 0; i < acc.length; i++) acc[i] = clamp((acc[i] / tot - 0.5) * 2.3 + 0.5);
|
|
386
|
+
return acc;
|
|
387
|
+
}
|
|
388
|
+
function canvasTex(size, fill, srgb, repeat = 1) {
|
|
389
|
+
const c = document.createElement('canvas'); c.width = c.height = size;
|
|
390
|
+
const ctx = c.getContext('2d'), img = ctx.createImageData(size, size);
|
|
391
|
+
for (let i = 0; i < size * size; i++) { const p = fill(i); img.data[i * 4] = p[0] * 255; img.data[i * 4 + 1] = p[1] * 255; img.data[i * 4 + 2] = p[2] * 255; img.data[i * 4 + 3] = 255; }
|
|
392
|
+
ctx.putImageData(img, 0, 0);
|
|
393
|
+
const t = new THREE.CanvasTexture(c);
|
|
394
|
+
t.wrapS = t.wrapT = THREE.RepeatWrapping; t.repeat.set(repeat, repeat);
|
|
395
|
+
t.colorSpace = srgb ? THREE.SRGBColorSpace : THREE.NoColorSpace; t.anisotropy = maxAniso;
|
|
396
|
+
return t;
|
|
397
|
+
}
|
|
398
|
+
const TS = 512;
|
|
399
|
+
const nA = fbmField(TS, 4, 6, 11), nB = fbmField(TS, 8, 4, 23), nC = fbmField(TS, 32, 3, 37), nD = fbmField(TS, 4, 5, 53);
|
|
400
|
+
|
|
401
|
+
const stoneMap = canvasTex(TS, i => {
|
|
402
|
+
const base = 0.34 + 0.2 * nA[i] + 0.07 * (nC[i] - 0.5);
|
|
403
|
+
let r = base, g = base * 0.985, b = base * 0.95;
|
|
404
|
+
const lichen = smooth(0.62, 0.85, nB[i]) * 0.38;
|
|
405
|
+
r = lerp(r, 0.24, lichen); g = lerp(g, 0.27, lichen); b = lerp(b, 0.15, lichen);
|
|
406
|
+
const pit = smooth(0.68, 0.88, nC[i]) * 0.22; return [r * (1 - pit), g * (1 - pit), b * (1 - pit)];
|
|
407
|
+
}, true, 1.7);
|
|
408
|
+
const stoneRough = canvasTex(TS, i => { const v = 0.7 + 0.28 * nB[i]; return [v, v, v]; }, false, 1.7);
|
|
409
|
+
const stoneBump = canvasTex(TS, i => { const v = 0.6 * nC[i] + 0.4 * nA[i]; return [v, v, v]; }, false, 1.7);
|
|
410
|
+
const soilMap = canvasTex(TS, i => {
|
|
411
|
+
const a = nD[i], c = nC[i]; let r = 0.13 + 0.07 * a, g = 0.105 + 0.055 * a, b = 0.085 + 0.04 * a;
|
|
412
|
+
const leaf = smooth(0.62, 0.8, nB[i]) * 0.6; r = lerp(r, 0.24, leaf); g = lerp(g, 0.17, leaf); b = lerp(b, 0.09, leaf);
|
|
413
|
+
const k = 0.85 + 0.3 * c; return [r * k, g * k, b * k];
|
|
414
|
+
}, true, 1);
|
|
415
|
+
soilMap.repeat.set(22, 34);
|
|
416
|
+
const soilBump = canvasTex(TS, i => { const v = 0.55 * nC[i] + 0.45 * nB[i]; return [v, v, v]; }, false, 1);
|
|
417
|
+
soilBump.repeat.set(22, 34);
|
|
418
|
+
const fabricBump = canvasTex(TS, i => {
|
|
419
|
+
const x = (i % TS) / TS; const v = 0.5 + 0.38 * Math.sin(Math.PI * 2 * (x * 11) + 3.2 * nA[i]) + 0.12 * (nC[i] - 0.5); return [v, v, v];
|
|
420
|
+
}, false, 1);
|
|
421
|
+
const waxGlow = (() => { const c = document.createElement('canvas'); c.width = 4; c.height = 128; const g = c.getContext('2d');
|
|
422
|
+
const gr = g.createLinearGradient(0, 128, 0, 0); gr.addColorStop(0, '#000'); gr.addColorStop(0.55, '#140802'); gr.addColorStop(0.85, '#7a3a14'); gr.addColorStop(1, '#ffd2a0');
|
|
423
|
+
g.fillStyle = gr; g.fillRect(0, 0, 4, 128); const t = new THREE.CanvasTexture(c); t.colorSpace = THREE.SRGBColorSpace; return t; })();
|
|
424
|
+
const haloTex = (() => { const c = document.createElement('canvas'); c.width = c.height = 128; const g = c.getContext('2d');
|
|
425
|
+
const gr = g.createRadialGradient(64, 64, 0, 64, 64, 64); gr.addColorStop(0, 'rgba(255,255,255,1)'); gr.addColorStop(0.15, 'rgba(255,255,255,0.45)');
|
|
426
|
+
gr.addColorStop(0.45, 'rgba(255,255,255,0.08)'); gr.addColorStop(1, 'rgba(255,255,255,0)');
|
|
427
|
+
g.fillStyle = gr; g.fillRect(0, 0, 128, 128); const t = new THREE.CanvasTexture(c); t.colorSpace = THREE.SRGBColorSpace; return t; })();
|
|
428
|
+
|
|
429
|
+
/* ---------- materials ---------- */
|
|
430
|
+
const P = o => new THREE.MeshPhysicalMaterial(o);
|
|
431
|
+
const stoneMats = [0xbdb8ae, 0xa8a49c, 0xc9c2b4, 0x9a9890].map((c, k) => P({
|
|
432
|
+
color: c, map: stoneMap, roughnessMap: stoneRough, roughness: 1.0, bumpMap: stoneBump, bumpScale: 0.5 + k * 0.1,
|
|
433
|
+
metalness: 0, specularIntensity: 0.35, envMapIntensity: 0.6 }));
|
|
434
|
+
const groundMat = P({ color: 0xffffff, vertexColors: true, map: soilMap, roughness: 0.97, bumpMap: soilBump, bumpScale: 3.0, specularIntensity: 0.25 });
|
|
435
|
+
const barkMat = P({ color: 0x3a322b, roughness: 0.93, bumpMap: stoneBump, bumpScale: 4.0, specularIntensity: 0.2 });
|
|
436
|
+
const ironMat = P({ color: 0x1d1c1b, metalness: 0.85, roughness: 0.58, envMapIntensity: 0.8 });
|
|
437
|
+
const grassMat = P({ color: 0xffffff, roughness: 0.85, specularIntensity: 0.3, sheen: 0.4, sheenColor: 0x8a8460, sheenRoughness: 0.6 });
|
|
438
|
+
|
|
439
|
+
/* ---------- terrain ---------- */
|
|
440
|
+
function groundH(x, z) {
|
|
441
|
+
const m = smooth(0.9, 2.4, Math.abs(x));
|
|
442
|
+
const h = 0.10 * Math.sin(0.33 * x + 0.21 * z) * Math.sin(0.27 * z - 0.13 * x + 1.0)
|
|
443
|
+
+ 0.05 * Math.sin(1.13 * x + 0.6) * Math.cos(0.87 * z + 0.3) + 0.025 * Math.sin(2.7 * x + 1.9 * z);
|
|
444
|
+
const hill = 1.4 * smooth(-26, -70, z) * (0.6 + 0.4 * Math.sin(0.13 * x + 0.8));
|
|
445
|
+
return h * m - 0.015 * (1 - m) + hill;
|
|
446
|
+
}
|
|
447
|
+
{
|
|
448
|
+
const g = new THREE.PlaneGeometry(76, 110, 152, 220); g.rotateX(-Math.PI / 2); g.translate(0, 0, -35);
|
|
449
|
+
const pos = g.attributes.position, col = new Float32Array(pos.count * 3);
|
|
450
|
+
for (let i = 0; i < pos.count; i++) {
|
|
451
|
+
const x = pos.getX(i), z = pos.getZ(i); pos.setY(i, groundH(x, z));
|
|
452
|
+
const path = 1 - smooth(0.5, 1.6, Math.abs(x)); const damp = 0.5 + 0.5 * Math.sin(x * 0.7 + z * 0.4) * Math.sin(z * 0.23);
|
|
453
|
+
col[i * 3] = lerp(0.82 + 0.1 * damp, 1.18, path); col[i * 3 + 1] = lerp(0.86 + 0.08 * damp, 1.1, path); col[i * 3 + 2] = lerp(0.8, 1.02, path);
|
|
454
|
+
}
|
|
455
|
+
g.setAttribute('color', new THREE.BufferAttribute(col, 3)); g.computeVertexNormals();
|
|
456
|
+
const ground = new THREE.Mesh(g, groundMat); ground.receiveShadow = true; scene.add(ground);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/* ---------- sky dome: morning blue overhead, hazy at the horizon, warm around a low sun ---------- */
|
|
460
|
+
const SUN_POS = new THREE.Vector3(14, 9, -40); // low, upper right of frame, behind the monument
|
|
461
|
+
const sunDir = SUN_POS.clone().normalize();
|
|
462
|
+
const ZENITH = new THREE.Color(0x6f9ccf), SUN_TINT = new THREE.Color(0xffc283);
|
|
463
|
+
const skyMat = new THREE.ShaderMaterial({
|
|
464
|
+
side: THREE.BackSide, depthWrite: false, fog: false,
|
|
465
|
+
uniforms: { uFog: { value: new THREE.Color(0) }, uZenith: { value: new THREE.Color(0) }, uSunCol: { value: new THREE.Color(0) }, uSun: { value: sunDir } },
|
|
466
|
+
vertexShader: \`varying vec3 vDir; void main(){ vec4 w = modelMatrix*vec4(position,1.0); vDir = w.xyz - cameraPosition; gl_Position = projectionMatrix*viewMatrix*w; }\`,
|
|
467
|
+
fragmentShader: \`uniform vec3 uFog, uZenith, uSunCol, uSun; varying vec3 vDir;
|
|
468
|
+
void main(){ vec3 d = normalize(vDir); float h = d.y;
|
|
469
|
+
vec3 col = mix(uFog, uZenith, smoothstep(0.0, 0.55, h));
|
|
470
|
+
float m = max(dot(d, normalize(uSun)), 0.0);
|
|
471
|
+
col += uSunCol * (0.35*pow(m, 6.0) + 0.6*pow(m, 60.0)) * smoothstep(-0.1, 0.15, h); // warm glow around the sun
|
|
472
|
+
col += uSunCol * 4.0 * smoothstep(0.99955, 0.9998, m); // the sun's disc
|
|
473
|
+
gl_FragColor = vec4(col, 1.0); }\`
|
|
474
|
+
});
|
|
475
|
+
const sky = new THREE.Mesh(new THREE.SphereGeometry(85, 32, 16), skyMat); sky.renderOrder = -1; scene.add(sky);
|
|
476
|
+
|
|
477
|
+
/* ---------- tombstones ---------- */
|
|
478
|
+
function stoneShape(w, h, type) {
|
|
479
|
+
const s = new THREE.Shape(), hw = w / 2;
|
|
480
|
+
if (type === 0) { s.moveTo(-hw, 0); s.lineTo(hw, 0); s.lineTo(hw, h - hw); s.absarc(0, h - hw, hw, 0, Math.PI, false); s.lineTo(-hw, 0); }
|
|
481
|
+
else if (type === 1) { s.moveTo(-hw, 0); s.lineTo(hw, 0); s.lineTo(hw, h * 0.62); s.quadraticCurveTo(hw, h * 0.9, 0, h); s.quadraticCurveTo(-hw, h * 0.9, -hw, h * 0.62); s.lineTo(-hw, 0); }
|
|
482
|
+
else if (type === 2) { s.moveTo(-hw, 0); s.lineTo(hw, 0); s.lineTo(hw, h * 0.8); s.quadraticCurveTo(hw * 0.95, h * 0.88, hw * 0.58, h * 0.88);
|
|
483
|
+
s.quadraticCurveTo(hw * 0.25, h * 0.88, 0, h); s.quadraticCurveTo(-hw * 0.25, h * 0.88, -hw * 0.58, h * 0.88); s.quadraticCurveTo(-hw * 0.95, h * 0.88, -hw, h * 0.8); s.lineTo(-hw, 0); }
|
|
484
|
+
else { const a = w * 0.15, y0 = h * 0.6, y1 = y0 + 2 * a;
|
|
485
|
+
s.moveTo(-a, 0); s.lineTo(a, 0); s.lineTo(a, y0); s.lineTo(hw, y0); s.lineTo(hw, y1); s.lineTo(a, y1); s.lineTo(a, h); s.lineTo(-a, h); s.lineTo(-a, y1); s.lineTo(-hw, y1); s.lineTo(-hw, y0); s.lineTo(-a, y0); s.lineTo(-a, 0); }
|
|
486
|
+
return s;
|
|
487
|
+
}
|
|
488
|
+
const stoneGroup = new THREE.Group(); scene.add(stoneGroup);
|
|
489
|
+
function addTomb(x, z, scale) {
|
|
490
|
+
const type = rng() < 0.12 ? 3 : Math.floor(rand(0, 3)), r = rng();
|
|
491
|
+
const w = (type === 3 ? rand(0.55, 0.75) : rand(0.5, 0.78)) * scale, h = (type === 3 ? rand(1.0, 1.35) : rand(0.7, 1.15)) * scale, d = rand(0.1, 0.17) * scale;
|
|
492
|
+
const geo = new THREE.ExtrudeGeometry(stoneShape(w, h, type), { depth: d, bevelEnabled: true, bevelThickness: 0.014, bevelSize: 0.014, bevelSegments: 2, curveSegments: 18 });
|
|
493
|
+
geo.translate(0, 0, -d / 2);
|
|
494
|
+
const m = new THREE.Mesh(geo, stoneMats[Math.floor(r * 4)]);
|
|
495
|
+
const grp = new THREE.Group(); grp.add(m);
|
|
496
|
+
if (type !== 3 && rng() < 0.45) { const pl = new THREE.Mesh(new THREE.BoxGeometry(w * 1.18, 0.12 * scale, d * 2.2), m.material); pl.position.y = 0.0; m.position.y = 0.05 * scale; grp.add(pl); pl.castShadow = pl.receiveShadow = true; }
|
|
497
|
+
grp.position.set(x, groundH(x, z) - rand(0.03, 0.1), z);
|
|
498
|
+
grp.rotation.set(rand(-0.11, 0.07), rand(-0.3, 0.3) + (x > 0 ? -0.08 : 0.08), rand(-0.09, 0.09));
|
|
499
|
+
m.castShadow = m.receiveShadow = true; stoneGroup.add(grp);
|
|
500
|
+
if (rng() < 0.5) { const mound = new THREE.Mesh(new THREE.SphereGeometry(1, 20, 12, 0, Math.PI * 2, 0, Math.PI / 2), groundMat);
|
|
501
|
+
mound.scale.set(w * 0.62, 0.11, 0.85); mound.position.set(x + rand(-0.05, 0.05), groundH(x, z + 0.95) - 0.05, z + 0.95); mound.receiveShadow = true; scene.add(mound); }
|
|
502
|
+
}
|
|
503
|
+
function addObelisk(x, z) {
|
|
504
|
+
const h = rand(1.5, 2.1), w = rand(0.32, 0.4), grp = new THREE.Group(), mat = stoneMats[Math.floor(rand(0, 4))];
|
|
505
|
+
const base = new THREE.Mesh(new THREE.BoxGeometry(w * 1.7, 0.28, w * 1.7), mat); base.position.y = 0.1;
|
|
506
|
+
const shaft = new THREE.Mesh(new THREE.CylinderGeometry(w * 0.48, w * 0.68, h, 4, 1), mat); shaft.rotation.y = Math.PI / 4; shaft.position.y = 0.24 + h / 2;
|
|
507
|
+
const tip = new THREE.Mesh(new THREE.ConeGeometry(w * 0.48, w * 0.7, 4), mat); tip.rotation.y = Math.PI / 4; tip.position.y = 0.24 + h + w * 0.35;
|
|
508
|
+
[base, shaft, tip].forEach(o => { o.castShadow = o.receiveShadow = true; grp.add(o); });
|
|
509
|
+
grp.position.set(x, groundH(x, z) - 0.05, z); grp.rotation.set(rand(-0.04, 0.04), rand(0, 1), rand(-0.05, 0.05)); stoneGroup.add(grp);
|
|
510
|
+
}
|
|
511
|
+
for (const side of [-1, 1]) {
|
|
512
|
+
for (let z = 6.2; z > -40; z -= rand(2.2, 3.0)) addTomb(side * rand(1.55, 2.05), z + rand(-0.4, 0.4), rand(0.9, 1.1));
|
|
513
|
+
for (let z = 5.0; z > -42; z -= rand(2.4, 3.4)) addTomb(side * rand(3.0, 4.2), z + rand(-0.5, 0.5), rand(0.85, 1.15));
|
|
514
|
+
for (let z = 3.0; z > -44; z -= rand(2.6, 4.0)) { const x = side * rand(5.2, 8.5); if (rng() < 0.18) addObelisk(x, z); else addTomb(x, z, rand(0.9, 1.25)); }
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/* ---------- dead trees ---------- */
|
|
518
|
+
function makeTree(seed, scale) {
|
|
519
|
+
const r = mulberry32(seed), geos = [], Y = new THREE.Vector3(0, 1, 0);
|
|
520
|
+
function seg(a, b, r0, r1, joint) {
|
|
521
|
+
const d = new THREE.Vector3().subVectors(b, a), L = d.length();
|
|
522
|
+
const g = new THREE.CylinderGeometry(r1, r0, L, 7, 1); g.translate(0, L / 2, 0);
|
|
523
|
+
g.applyQuaternion(new THREE.Quaternion().setFromUnitVectors(Y, d.normalize())); g.translate(a.x, a.y, a.z); geos.push(g);
|
|
524
|
+
if (joint) { const s = new THREE.SphereGeometry(r1 * 1.02, 8, 6); s.translate(b.x, b.y, b.z); geos.push(s); }
|
|
525
|
+
}
|
|
526
|
+
function branch(p, dir, len, rad, depth) {
|
|
527
|
+
let cur = p.clone(), d = dir.clone(), r0 = rad * (depth === 0 ? 1.7 : 1);
|
|
528
|
+
for (let i = 0; i < 3; i++) {
|
|
529
|
+
d.add(new THREE.Vector3((r() - 0.5) * 0.7, (r() - 0.45) * 0.35, (r() - 0.5) * 0.7)).normalize();
|
|
530
|
+
const nxt = cur.clone().addScaledVector(d, len / 3), r1 = rad * (1 - 0.5 * (i + 1) / 3);
|
|
531
|
+
seg(cur, nxt, r0, r1, depth < 3); cur = nxt; r0 = r1;
|
|
532
|
+
}
|
|
533
|
+
if (depth >= 5 || rad < 0.012) return;
|
|
534
|
+
const kids = depth < 1 ? 3 : 2 + (r() < 0.4 ? 1 : 0);
|
|
535
|
+
for (let k = 0; k < kids; k++) {
|
|
536
|
+
const nd = d.clone().add(new THREE.Vector3((r() - 0.5) * 1.7, r() * 0.7 - 0.15, (r() - 0.5) * 1.7)).normalize();
|
|
537
|
+
branch(cur, nd, len * (0.6 + r() * 0.16), r0 * 0.74, depth + 1);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
branch(new THREE.Vector3(0, -0.3, 0), new THREE.Vector3(0, 1, 0), 3.4 * scale, 0.2 * scale, 0);
|
|
541
|
+
return mergeGeometries(geos);
|
|
542
|
+
}
|
|
543
|
+
[[-5.6, -13, 1.25, 7], [6.8, -19, 1.1, 19], [-8.5, -27, 1.3, 33], [4.6, -35, 1.0, 41], [9.5, -8, 0.9, 57], [-11, -6, 1.0, 71], [12, -30, 1.2, 83]]
|
|
544
|
+
.forEach(([x, z, s, seed]) => { const t = new THREE.Mesh(makeTree(seed, s), barkMat); t.position.set(x, groundH(x, z), z); t.rotation.y = seed; t.castShadow = true; t.receiveShadow = true; scene.add(t); });
|
|
545
|
+
|
|
546
|
+
/* ---------- gate and iron fence (where the devil comes from) ---------- */
|
|
547
|
+
const GATE_Z = -27;
|
|
548
|
+
{
|
|
549
|
+
const pillarMat = stoneMats[1];
|
|
550
|
+
for (const side of [-1, 1]) {
|
|
551
|
+
const pg = new THREE.Group();
|
|
552
|
+
const p = new THREE.Mesh(new THREE.BoxGeometry(0.5, 2.5, 0.5), pillarMat); p.position.y = 1.25;
|
|
553
|
+
const cap = new THREE.Mesh(new THREE.BoxGeometry(0.64, 0.14, 0.64), pillarMat); cap.position.y = 2.57;
|
|
554
|
+
const fin = new THREE.Mesh(new THREE.SphereGeometry(0.17, 20, 14), pillarMat); fin.position.y = 2.82;
|
|
555
|
+
[p, cap, fin].forEach(o => { o.castShadow = o.receiveShadow = true; pg.add(o); });
|
|
556
|
+
pg.position.set(side * 1.65, groundH(side * 1.65, GATE_Z), GATE_Z); scene.add(pg);
|
|
557
|
+
// open gate leaf, hinged at the pillar, swung toward the camera
|
|
558
|
+
const bars = [];
|
|
559
|
+
for (let i = 0; i < 8; i++) { const b = new THREE.CylinderGeometry(0.013, 0.013, 1.9, 6); b.translate(0.08 + i * 0.16, 0.98, 0); bars.push(b);
|
|
560
|
+
const tip = new THREE.ConeGeometry(0.03, 0.12, 6); tip.translate(0.08 + i * 0.16, 1.98, 0); bars.push(tip); }
|
|
561
|
+
for (const y of [0.18, 1.55, 1.8]) { const r = new THREE.BoxGeometry(1.24, 0.035, 0.035); r.translate(0.64, y, 0); bars.push(r); }
|
|
562
|
+
const leaf = new THREE.Mesh(mergeGeometries(bars.map(g => g.toNonIndexed())), ironMat); leaf.castShadow = true;
|
|
563
|
+
const hinge = new THREE.Group(); hinge.position.set(side * 1.38, groundH(side * 1.38, GATE_Z), GATE_Z + 0.02);
|
|
564
|
+
hinge.rotation.y = side < 0 ? -1.15 : Math.PI + 1.15; hinge.add(leaf); scene.add(hinge);
|
|
565
|
+
}
|
|
566
|
+
// arch
|
|
567
|
+
const arch = new THREE.Mesh(new THREE.TorusGeometry(1.4, 0.022, 8, 48, Math.PI), ironMat);
|
|
568
|
+
arch.position.set(0, 2.45, GATE_Z); arch.castShadow = true; scene.add(arch);
|
|
569
|
+
// fence
|
|
570
|
+
const barGeo = mergeGeometries([new THREE.CylinderGeometry(0.011, 0.011, 1.5, 5).translate(0, 0.75, 0).toNonIndexed(), new THREE.ConeGeometry(0.026, 0.1, 5).translate(0, 1.55, 0).toNonIndexed()]);
|
|
571
|
+
const xs = []; for (const side of [-1, 1]) for (let x = 2.0; x < 16; x += 0.15) xs.push(side * x);
|
|
572
|
+
const fence = new THREE.InstancedMesh(barGeo, ironMat, xs.length); const m4 = new THREE.Matrix4();
|
|
573
|
+
xs.forEach((x, i) => { m4.makeRotationZ((hash1(i * 3.1) - 0.5) * 0.06); m4.setPosition(x, groundH(x, GATE_Z) - 0.05, GATE_Z); fence.setMatrixAt(i, m4); });
|
|
574
|
+
fence.castShadow = true; scene.add(fence);
|
|
575
|
+
for (const side of [-1, 1]) for (const y of [0.25, 1.32]) { const r = new THREE.Mesh(new THREE.BoxGeometry(14, 0.03, 0.03), ironMat); r.position.set(side * 9, y + 0.1, GATE_Z); r.castShadow = true; scene.add(r); }
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/* ---------- dead grass ---------- */
|
|
579
|
+
{
|
|
580
|
+
const N = 5200, blade = new THREE.ConeGeometry(0.008, 0.18, 3, 1); blade.translate(0, 0.09, 0);
|
|
581
|
+
const grass = new THREE.InstancedMesh(blade, grassMat, N), m4 = new THREE.Matrix4(), q = new THREE.Quaternion(), e = new THREE.Euler(), s = new THREE.Vector3(), p = new THREE.Vector3(), c = new THREE.Color();
|
|
582
|
+
let n = 0;
|
|
583
|
+
while (n < N) {
|
|
584
|
+
const x = rand(-13, 13), z = rand(-40, 8.5);
|
|
585
|
+
if (Math.abs(x) < 0.75 + rng() * 0.6) continue;
|
|
586
|
+
e.set(rand(-0.45, 0.45), rand(0, 6.28), rand(-0.45, 0.45)); q.setFromEuler(e);
|
|
587
|
+
s.set(rand(0.7, 1.3), rand(0.45, 1.5), rand(0.7, 1.3)); p.set(x, groundH(x, z) - 0.02, z);
|
|
588
|
+
m4.compose(p, q, s); grass.setMatrixAt(n, m4);
|
|
589
|
+
c.setRGB(lerp(0.14, 0.27, rng()), lerp(0.13, 0.23, rng()), lerp(0.08, 0.12, rng())); grass.setColorAt(n, c); n++;
|
|
590
|
+
}
|
|
591
|
+
grass.receiveShadow = true; scene.add(grass);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/* ---------- pumpkins ---------- */
|
|
595
|
+
const pumpkinColorDark = new THREE.Color(0x5a1e06), pumpkinColorLight = new THREE.Color(0xd9661c);
|
|
596
|
+
function pumpkinDeform(v) {
|
|
597
|
+
const th = Math.atan2(v.z, v.x), lobe = Math.pow(Math.abs(Math.cos(th * 4)), 0.5);
|
|
598
|
+
const rf = (1 - 0.075 * (1 - lobe)) * (1 - 0.06 * v.y * v.y);
|
|
599
|
+
let y = v.y * 0.72 - 0.2 * smooth(0.72, 1.0, v.y) + 0.08 * smooth(-0.8, -1.0, v.y);
|
|
600
|
+
return { p: new THREE.Vector3(v.x * rf, y, v.z * rf), lobe };
|
|
601
|
+
}
|
|
602
|
+
const pumpkinGeo = (() => {
|
|
603
|
+
let g = new THREE.SphereGeometry(1, 64, 40); g.deleteAttribute('uv'); g.deleteAttribute('normal'); g = mergeVertices(g);
|
|
604
|
+
const pos = g.attributes.position, col = new Float32Array(pos.count * 3), v = new THREE.Vector3(), c = new THREE.Color();
|
|
605
|
+
for (let i = 0; i < pos.count; i++) {
|
|
606
|
+
v.fromBufferAttribute(pos, i).normalize(); const d = pumpkinDeform(v); pos.setXYZ(i, d.p.x, d.p.y, d.p.z);
|
|
607
|
+
c.copy(pumpkinColorDark).lerp(pumpkinColorLight, Math.pow(d.lobe, 1.6) * (0.75 + 0.25 * (1 - Math.abs(v.y))));
|
|
608
|
+
col[i * 3] = c.r; col[i * 3 + 1] = c.g; col[i * 3 + 2] = c.b;
|
|
609
|
+
}
|
|
610
|
+
g.setAttribute('color', new THREE.BufferAttribute(col, 3)); g.computeVertexNormals(); return g;
|
|
611
|
+
})();
|
|
612
|
+
const pumpkinMat = P({ color: 0xffffff, vertexColors: true, roughness: 0.52, clearcoat: 0.35, clearcoatRoughness: 0.45, sheen: 0.3, sheenColor: 0xffa060, specularIntensity: 0.6 });
|
|
613
|
+
const stemMat = P({ color: 0x4a4426, roughness: 0.82, bumpMap: stoneBump, bumpScale: 3 });
|
|
614
|
+
function subdivide(geo, times) {
|
|
615
|
+
let g = geo.index ? geo.toNonIndexed() : geo;
|
|
616
|
+
for (let k = 0; k < times; k++) {
|
|
617
|
+
const a = g.attributes.position.array, out = [];
|
|
618
|
+
for (let i = 0; i < a.length; i += 9) {
|
|
619
|
+
const A = [a[i], a[i + 1], a[i + 2]], B = [a[i + 3], a[i + 4], a[i + 5]], C = [a[i + 6], a[i + 7], a[i + 8]];
|
|
620
|
+
const mid = (p, q) => [(p[0] + q[0]) / 2, (p[1] + q[1]) / 2, (p[2] + q[2]) / 2];
|
|
621
|
+
const ab = mid(A, B), bc = mid(B, C), ca = mid(C, A);
|
|
622
|
+
out.push(...A, ...ab, ...ca, ...ab, ...B, ...bc, ...ca, ...bc, ...C, ...ab, ...bc, ...ca);
|
|
623
|
+
}
|
|
624
|
+
g = new THREE.BufferGeometry(); g.setAttribute('position', new THREE.Float32BufferAttribute(out, 3));
|
|
625
|
+
}
|
|
626
|
+
return g;
|
|
627
|
+
}
|
|
628
|
+
const faceGeo = (() => {
|
|
629
|
+
const shapes = [];
|
|
630
|
+
const tri = pts => { const s = new THREE.Shape(); s.moveTo(...pts[0]); pts.slice(1).forEach(p => s.lineTo(...p)); s.closePath(); return s; };
|
|
631
|
+
shapes.push(tri([[0.12, 0.08], [0.42, 0.17], [0.25, 0.38]]), tri([[-0.12, 0.08], [-0.25, 0.38], [-0.42, 0.17]]), tri([[-0.06, -0.03], [0.06, -0.03], [0, 0.07]]));
|
|
632
|
+
const yu = x => -0.1 - 0.1 * (1 - (x / 0.5) ** 2), yl = x => -0.14 - 0.26 * (1 - (x / 0.5) ** 2);
|
|
633
|
+
const m = new THREE.Shape(); m.moveTo(-0.5, yu(-0.5));
|
|
634
|
+
for (let x = -0.45; x <= 0.451; x += 0.05) { const xr = Math.round(x * 100) / 100;
|
|
635
|
+
if (xr === -0.05) { m.lineTo(-0.05, yl(-0.05)); m.lineTo(-0.05, yl(-0.05) + 0.08); m.lineTo(0.05, yl(0.05) + 0.08); m.lineTo(0.05, yl(0.05)); continue; }
|
|
636
|
+
if (xr === 0 || xr === 0.05) continue; m.lineTo(xr, yl(xr)); }
|
|
637
|
+
m.lineTo(0.5, yu(0.5));
|
|
638
|
+
for (let x = 0.45; x >= -0.451; x -= 0.05) { const xr = Math.round(x * 100) / 100;
|
|
639
|
+
if (xr === 0.22) { m.lineTo(0.22, yu(0.22)); m.lineTo(0.22, yu(0.22) - 0.08); m.lineTo(0.12, yu(0.12) - 0.08); m.lineTo(0.12, yu(0.12)); continue; }
|
|
640
|
+
if (xr === -0.12) { m.lineTo(-0.12, yu(-0.12)); m.lineTo(-0.12, yu(-0.12) - 0.08); m.lineTo(-0.22, yu(-0.22) - 0.08); m.lineTo(-0.22, yu(-0.22)); continue; }
|
|
641
|
+
if ((xr > 0.12 && xr < 0.22) || (xr > -0.22 && xr < -0.12)) continue; m.lineTo(xr, yu(xr)); }
|
|
642
|
+
m.closePath(); shapes.push(m);
|
|
643
|
+
const flat = subdivide(new THREE.ShapeGeometry(shapes, 1), 3), pos = flat.attributes.position;
|
|
644
|
+
const col = new Float32Array(pos.count * 3), v = new THREE.Vector3();
|
|
645
|
+
for (let i = 0; i < pos.count; i++) {
|
|
646
|
+
const x = pos.getX(i), y = pos.getY(i); v.set(x, y, Math.sqrt(Math.max(0, 1 - x * x - y * y))).normalize();
|
|
647
|
+
const d = pumpkinDeform(v); d.p.addScaledVector(v, 0.014); pos.setXYZ(i, d.p.x, d.p.y, d.p.z);
|
|
648
|
+
const k = 0.55 + 0.45 * smooth(0.55, 0.0, Math.hypot(x * 0.8, y + 0.05)); col[i * 3] = k; col[i * 3 + 1] = k * k; col[i * 3 + 2] = k * k * k;
|
|
649
|
+
}
|
|
650
|
+
flat.setAttribute('color', new THREE.BufferAttribute(col, 3)); return flat;
|
|
651
|
+
})();
|
|
652
|
+
const pumpkins = [];
|
|
653
|
+
function addPumpkin(x, z, r, carved, withLight) {
|
|
654
|
+
const g = new THREE.Group(), body = new THREE.Mesh(pumpkinGeo, pumpkinMat); body.castShadow = body.receiveShadow = true; g.add(body);
|
|
655
|
+
const st = new THREE.CylinderGeometry(0.045, 0.08, 0.26, 7, 5); const sp = st.attributes.position;
|
|
656
|
+
for (let i = 0; i < sp.count; i++) { const yy = sp.getY(i) + 0.13; sp.setX(i, sp.getX(i) + 0.1 * yy * yy); } st.computeVertexNormals();
|
|
657
|
+
const stem = new THREE.Mesh(st, stemMat); stem.position.y = 0.58; stem.rotation.y = rand(0, 6); stem.castShadow = true; g.add(stem);
|
|
658
|
+
let face = null, faceMat = null, light = null;
|
|
659
|
+
if (carved) {
|
|
660
|
+
faceMat = new THREE.MeshBasicMaterial({ color: 0x000000, vertexColors: true, polygonOffset: true, polygonOffsetFactor: -2, polygonOffsetUnits: -2 });
|
|
661
|
+
face = new THREE.Mesh(faceGeo, faceMat); g.add(face);
|
|
662
|
+
if (withLight) { light = new THREE.PointLight(0xff7a2a, 0, 5, 2); light.position.set(0, 0.25, 0.55); g.add(light); }
|
|
663
|
+
}
|
|
664
|
+
g.scale.setScalar(r); g.position.set(x, groundH(x, z) + r * 0.62, z);
|
|
665
|
+
g.rotation.y = Math.atan2(0 - x, 3.0 - z) + rand(-0.18, 0.18); g.rotation.z = rand(-0.08, 0.08);
|
|
666
|
+
scene.add(g); pumpkins.push({ g, faceMat, light, seed: pumpkins.length * 13.7 + 2.1 });
|
|
667
|
+
}
|
|
668
|
+
addPumpkin(-0.98, -1.35, 0.3, true, true);
|
|
669
|
+
addPumpkin(1.18, -2.75, 0.34, true, true);
|
|
670
|
+
addPumpkin(1.42, -2.25, 0.2, false);
|
|
671
|
+
addPumpkin(-1.38, 4.6, 0.26, true, true);
|
|
672
|
+
addPumpkin(-2.4, 2.1, 0.22, false);
|
|
673
|
+
addPumpkin(1.55, -6.6, 0.27, true, false);
|
|
674
|
+
addPumpkin(-1.75, -11.2, 0.3, true, false);
|
|
675
|
+
addPumpkin(2.6, -8.4, 0.24, false);
|
|
676
|
+
addPumpkin(1.6, -17.5, 0.28, true, false);
|
|
677
|
+
addPumpkin(-2.9, -3.8, 0.2, false);
|
|
678
|
+
|
|
679
|
+
/* ---------- ground mist (soft sheets, never in the dolly path) ---------- */
|
|
680
|
+
const mistUniforms = { uTime: { value: 0 }, uColor: { value: new THREE.Color(0) }, uCandlePos: { value: new THREE.Vector3() },
|
|
681
|
+
uCandleCol: { value: new THREE.Color(1.0, 0.5, 0.18) }, uCandleI: { value: 0 }, uCam: { value: new THREE.Vector3() }, uFogDensity: { value: FOG_DENSITY } };
|
|
682
|
+
function mistMaterial(vertical, opacity) {
|
|
683
|
+
return new THREE.ShaderMaterial({
|
|
684
|
+
transparent: true, depthWrite: false, fog: false, side: THREE.DoubleSide,
|
|
685
|
+
uniforms: { ...mistUniforms, uVertical: { value: vertical ? 1 : 0 }, uOpacity: { value: opacity } },
|
|
686
|
+
vertexShader: \`varying vec3 vW; void main(){ vec4 w = modelMatrix*vec4(position,1.0); vW = w.xyz; gl_Position = projectionMatrix*viewMatrix*w; }\`,
|
|
687
|
+
fragmentShader: \`
|
|
688
|
+
uniform float uTime, uOpacity, uFogDensity, uVertical, uCandleI; uniform vec3 uColor, uCandlePos, uCandleCol, uCam; varying vec3 vW;
|
|
689
|
+
float hash(vec2 p){ p = fract(p*vec2(123.34,456.21)); p += dot(p, p+45.32); return fract(p.x*p.y); }
|
|
690
|
+
float vn(vec2 p){ vec2 i = floor(p), f = fract(p), u = f*f*(3.0-2.0*f);
|
|
691
|
+
return mix(mix(hash(i), hash(i+vec2(1,0)), u.x), mix(hash(i+vec2(0,1)), hash(i+vec2(1,1)), u.x), u.y); }
|
|
692
|
+
float fbm(vec2 p){ float a = 0.5, s = 0.0; for(int i=0;i<4;i++){ s += a*vn(p); p = p*2.03 + vec2(1.7,9.2); a *= 0.5; } return s; }
|
|
693
|
+
void main(){
|
|
694
|
+
vec2 q = uVertical > 0.5 ? vec2(vW.x*0.32 + uTime*0.05, vW.y*0.75 - uTime*0.012) : vec2(vW.x*0.22 + uTime*0.04, vW.z*0.2 + uTime*0.025);
|
|
695
|
+
float n = fbm(q + 1.3*fbm(q*1.6 - uTime*0.03));
|
|
696
|
+
float a = smoothstep(0.38, 0.86, n) * uOpacity;
|
|
697
|
+
if (uVertical > 0.5) { a *= smoothstep(0.0, 0.6, vW.y) * smoothstep(3.0, 1.1, vW.y) * smoothstep(13.0, 5.0, abs(vW.x)); }
|
|
698
|
+
else { a *= smoothstep(15.0, 4.0, abs(vW.x)); }
|
|
699
|
+
float dc = distance(uCam, vW); a *= smoothstep(0.9, 3.5, dc);
|
|
700
|
+
float fogF = 1.0 - exp(-pow(uFogDensity*dc, 2.0));
|
|
701
|
+
float dl = distance(vW, uCandlePos); float w = uCandleI / (0.3 + dl*dl);
|
|
702
|
+
vec3 col = uColor + uCandleCol * w * 0.22;
|
|
703
|
+
a = a * (1.0 - 0.75*fogF) * (1.0 + min(w, 3.0)*0.35);
|
|
704
|
+
gl_FragColor = vec4(col, clamp(a, 0.0, 1.0));
|
|
705
|
+
}\`
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
const mistGroup = new THREE.Group(); scene.add(mistGroup);
|
|
709
|
+
{
|
|
710
|
+
const h1 = new THREE.Mesh(new THREE.PlaneGeometry(36, 66), mistMaterial(false, 0.55)); h1.rotation.x = -Math.PI / 2; h1.position.set(0, 0.2, -24); mistGroup.add(h1);
|
|
711
|
+
const h2 = new THREE.Mesh(new THREE.PlaneGeometry(36, 60), mistMaterial(false, 0.3)); h2.rotation.x = -Math.PI / 2; h2.position.set(0, 0.55, -27); mistGroup.add(h2);
|
|
712
|
+
for (const z of [-3.5, -9, -15.5, -22.5]) { const v = new THREE.Mesh(new THREE.PlaneGeometry(26, 3.2), mistMaterial(true, 0.5)); v.position.set(0, 1.5, z); mistGroup.add(v); }
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/* ==========================================================================
|
|
716
|
+
THE DEVIL
|
|
717
|
+
========================================================================== */
|
|
718
|
+
const U = { time: { value: 0 }, drag: { value: 0 }, flutter: { value: 1 } };
|
|
719
|
+
const devil = new THREE.Group(); devil.scale.setScalar(0.9); scene.add(devil);
|
|
720
|
+
const body = new THREE.Group(); body.position.y = 1.1; devil.add(body);
|
|
721
|
+
const parts = new THREE.Group(); parts.position.y = -1.1; body.add(parts);
|
|
722
|
+
|
|
723
|
+
const clothMat = P({ color: 0x1a0a0b, roughness: 0.88, sheen: 0.55, sheenColor: 0x5e2a22, sheenRoughness: 0.55,
|
|
724
|
+
bumpMap: fabricBump, bumpScale: 1.6, specularIntensity: 0.4, envMapIntensity: 0.7 });
|
|
725
|
+
const robeMat = clothMat.clone(); robeMat.transparent = true;
|
|
726
|
+
robeMat.onBeforeCompile = sh => {
|
|
727
|
+
sh.uniforms.uTime = U.time; sh.uniforms.uDrag = U.drag; sh.uniforms.uFlutter = U.flutter;
|
|
728
|
+
sh.vertexShader = 'attribute float aW; attribute float aPhi; attribute float aFade; uniform float uTime, uDrag, uFlutter; varying float vFade;\\n' +
|
|
729
|
+
sh.vertexShader.replace('#include <begin_vertex>', \`#include <begin_vertex>
|
|
730
|
+
float w2 = aW*aW;
|
|
731
|
+
float wave = sin(position.y*7.0 - uTime*2.2 + aPhi*3.0) + 0.6*sin(position.y*13.0 - uTime*3.6 + aPhi*5.0 + 1.3);
|
|
732
|
+
transformed.xz += normalize(position.xz + 1e-4) * wave * 0.035 * w2 * uFlutter;
|
|
733
|
+
transformed.x += sin(uTime*1.7 + aPhi*2.0 + position.y*4.0) * 0.02 * w2;
|
|
734
|
+
transformed.z -= uDrag * (aW*0.22 + w2*0.3);
|
|
735
|
+
transformed.y += uDrag * w2 * 0.12;
|
|
736
|
+
vFade = aFade;\`);
|
|
737
|
+
sh.fragmentShader = 'varying float vFade;\\n' + sh.fragmentShader.replace('#include <alphamap_fragment>', '#include <alphamap_fragment>\\n diffuseColor.a *= vFade;');
|
|
738
|
+
};
|
|
739
|
+
robeMat.customProgramCacheKey = () => 'ghost-robe';
|
|
740
|
+
|
|
741
|
+
// robe: lathe from a smooth profile, then tattered hem
|
|
742
|
+
{
|
|
743
|
+
const prof = [[0.44, 0.0], [0.4, 0.14], [0.36, 0.32], [0.32, 0.56], [0.29, 0.8], [0.28, 1.0], [0.295, 1.18], [0.315, 1.32], [0.3, 1.42], [0.25, 1.5], [0.17, 1.58], [0.1, 1.64], [0.02, 1.67]];
|
|
744
|
+
const curve = new THREE.CatmullRomCurve3(prof.map(([r, y]) => new THREE.Vector3(r, y, 0)));
|
|
745
|
+
const pts = curve.getPoints(64).map(p => new THREE.Vector2(Math.max(0.01, p.x), p.y));
|
|
746
|
+
const g = new THREE.LatheGeometry(pts, 112, Math.PI, Math.PI * 2);
|
|
747
|
+
const pos = g.attributes.position, n = pos.count, aW = new Float32Array(n), aPhi = new Float32Array(n), aFade = new Float32Array(n);
|
|
748
|
+
for (let i = 0; i < n; i++) {
|
|
749
|
+
let x = pos.getX(i), y = pos.getY(i), z = pos.getZ(i); const phi = Math.atan2(x, z);
|
|
750
|
+
const w = smooth(0.6, 0.0, y);
|
|
751
|
+
const nn = 0.5 * (0.5 + 0.5 * Math.sin(phi * 7 + 0.4)) + 0.3 * (0.5 + 0.5 * Math.sin(phi * 13 + 2.1)) + 0.2 * (0.5 + 0.5 * Math.sin(phi * 23 + 0.7));
|
|
752
|
+
y -= w * w * 0.34 * Math.pow(nn, 2.4); const shrink = 1 - 0.16 * w * w * nn; x *= shrink; z *= shrink;
|
|
753
|
+
x *= 1 + 0.12 * smooth(1.0, 1.4, y) * smooth(1.62, 1.45, y); z *= 0.84;
|
|
754
|
+
pos.setXYZ(i, x, y, z); aW[i] = w; aPhi[i] = phi; aFade[i] = smooth(-0.3, 0.55, y);
|
|
755
|
+
}
|
|
756
|
+
g.setAttribute('aW', new THREE.BufferAttribute(aW, 1)); g.setAttribute('aPhi', new THREE.BufferAttribute(aPhi, 1)); g.setAttribute('aFade', new THREE.BufferAttribute(aFade, 1));
|
|
757
|
+
g.computeVertexNormals();
|
|
758
|
+
var robe = new THREE.Mesh(g, robeMat); robe.castShadow = true; robe.receiveShadow = true; parts.add(robe);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// hood, void, eyes, horns
|
|
762
|
+
const head = new THREE.Group(); head.position.set(0, 1.76, 0); parts.add(head);
|
|
763
|
+
{
|
|
764
|
+
const g = new THREE.SphereGeometry(0.2, 56, 40); g.rotateY(-Math.PI / 2);
|
|
765
|
+
const pos = g.attributes.position, v = new THREE.Vector3();
|
|
766
|
+
for (let i = 0; i < pos.count; i++) {
|
|
767
|
+
v.fromBufferAttribute(pos, i); const ny = v.y / 0.2, nz = v.z / 0.2;
|
|
768
|
+
const peak = Math.pow(Math.max(0, ny * 0.75 - nz * 0.25), 3.5);
|
|
769
|
+
v.z *= 1.12; v.y += 0.085 * peak; v.z -= 0.06 * peak; v.x *= 1 - 0.1 * peak;
|
|
770
|
+
pos.setXYZ(i, v.x, v.y, v.z);
|
|
771
|
+
}
|
|
772
|
+
g.computeVertexNormals();
|
|
773
|
+
const hood = new THREE.Mesh(g, clothMat); hood.castShadow = hood.receiveShadow = true; head.add(hood);
|
|
774
|
+
const voidMat = new THREE.MeshBasicMaterial({ color: 0x000000 });
|
|
775
|
+
const face = new THREE.Mesh(new THREE.SphereGeometry(1, 40, 28), voidMat); face.scale.set(0.108, 0.138, 0.075); face.position.set(0, -0.015, 0.165); head.add(face);
|
|
776
|
+
}
|
|
777
|
+
const eyeMat = new THREE.MeshBasicMaterial({ color: 0x000000 });
|
|
778
|
+
for (const s of [-1, 1]) {
|
|
779
|
+
const e = new THREE.Mesh(new THREE.SphereGeometry(1, 16, 10), eyeMat); e.scale.set(0.017, 0.0075, 0.006);
|
|
780
|
+
e.position.set(s * 0.04, -0.0, 0.237); e.rotation.z = s * -0.38; head.add(e);
|
|
781
|
+
}
|
|
782
|
+
const hornMat = P({ color: 0xffffff, vertexColors: true, roughness: 0.36, clearcoat: 0.6, clearcoatRoughness: 0.28, specularIntensity: 0.7, bumpMap: stoneBump, bumpScale: 0.8 });
|
|
783
|
+
for (const s of [-1, 1]) {
|
|
784
|
+
const path = new THREE.CatmullRomCurve3([[0.1, 0.08, 0.03], [0.19, 0.14, 0.01], [0.245, 0.25, -0.03], [0.225, 0.37, -0.055], [0.165, 0.445, -0.03]].map(p => new THREE.Vector3(s * p[0], p[1], p[2])));
|
|
785
|
+
const segs = 48, rad = 14, g = new THREE.TubeGeometry(path, segs, 1, rad, false);
|
|
786
|
+
const pos = g.attributes.position, col = new Float32Array(pos.count * 3), P0 = new THREE.Vector3(), v = new THREE.Vector3();
|
|
787
|
+
for (let i = 0; i <= segs; i++) {
|
|
788
|
+
const t = i / segs; path.getPointAt(t, P0);
|
|
789
|
+
const r = (0.036 * Math.pow(1 - t, 0.85) + 0.0015) * (1 + 0.07 * Math.sin(t * 46) * (1 - t));
|
|
790
|
+
const c0 = [0.46, 0.37, 0.27], c1 = [0.03, 0.02, 0.02], k = Math.pow(t, 0.8);
|
|
791
|
+
for (let j = 0; j <= rad; j++) { const idx = i * (rad + 1) + j; v.fromBufferAttribute(pos, idx).sub(P0).multiplyScalar(r).add(P0); pos.setXYZ(idx, v.x, v.y, v.z);
|
|
792
|
+
col[idx * 3] = lerp(c0[0], c1[0], k); col[idx * 3 + 1] = lerp(c0[1], c1[1], k); col[idx * 3 + 2] = lerp(c0[2], c1[2], k); }
|
|
793
|
+
}
|
|
794
|
+
g.setAttribute('color', new THREE.BufferAttribute(col, 3)); g.computeVertexNormals();
|
|
795
|
+
const horn = new THREE.Mesh(g, hornMat); horn.castShadow = true; head.add(horn);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// sleeves and hands
|
|
799
|
+
function sleeveGeometry(r0, r1, billow) { const g = new THREE.CylinderGeometry(r1, r0, 1, 24, 8, true); g.translate(0, 0.5, 0);
|
|
800
|
+
const pos = g.attributes.position;
|
|
801
|
+
for (let i = 0; i < pos.count; i++) { const y = pos.getY(i), k = 1 + billow * Math.sin(Math.PI * y) * (1 - smooth(0.85, 1.0, y)); pos.setX(i, pos.getX(i) * k); pos.setZ(i, pos.getZ(i) * k); }
|
|
802
|
+
g.computeVertexNormals(); return g; }
|
|
803
|
+
const upperGeo = sleeveGeometry(0.085, 0.08, 0.12), foreGeo = sleeveGeometry(0.072, 0.098, 0.1);
|
|
804
|
+
const sleeveMat = clothMat.clone(); sleeveMat.side = THREE.DoubleSide;
|
|
805
|
+
const skinMat = P({ color: 0x5f6b67, roughness: 0.55, sheen: 0.4, sheenColor: 0x9fb0aa, sheenRoughness: 0.5, specularIntensity: 0.5, clearcoat: 0.15 });
|
|
806
|
+
const clawMat = P({ color: 0x120c0a, roughness: 0.3, clearcoat: 0.8 });
|
|
807
|
+
const SHOULDERS = [new THREE.Vector3(-0.25, 1.43, 0.03), new THREE.Vector3(0.25, 1.43, 0.03)];
|
|
808
|
+
const sleeves = [], hands = [], elbows = [];
|
|
809
|
+
const elbowGeo = new THREE.SphereGeometry(0.084, 20, 14);
|
|
810
|
+
for (const s of [-1, 1]) {
|
|
811
|
+
const up = new THREE.Mesh(upperGeo, sleeveMat), fo = new THREE.Mesh(foreGeo, sleeveMat), el = new THREE.Mesh(elbowGeo, clothMat);
|
|
812
|
+
[up, fo, el].forEach(o => { o.castShadow = o.receiveShadow = true; parts.add(o); }); sleeves.push([up, fo]); elbows.push(el);
|
|
813
|
+
const hand = new THREE.Group();
|
|
814
|
+
const palm = new THREE.Mesh(new THREE.SphereGeometry(1, 20, 14), skinMat); palm.scale.set(0.034, 0.02, 0.048); palm.position.set(0, 0, 0.03); hand.add(palm);
|
|
815
|
+
for (let f = 0; f < 4; f++) {
|
|
816
|
+
const fx = (f - 1.5) * 0.015, len = 0.032 - Math.abs(f - 1.5) * 0.004;
|
|
817
|
+
const prox = new THREE.Mesh(new THREE.CapsuleGeometry(0.0075, len, 4, 8), skinMat); prox.rotation.x = Math.PI / 2 - 0.25; prox.position.set(fx, 0.004, 0.07 + len * 0.3); hand.add(prox);
|
|
818
|
+
const dist = new THREE.Mesh(new THREE.CapsuleGeometry(0.0065, len * 0.9, 4, 8), skinMat); dist.rotation.x = Math.PI / 2 - 1.3; dist.position.set(fx, 0.02, 0.093 + len * 0.45); hand.add(dist);
|
|
819
|
+
const claw = new THREE.Mesh(new THREE.ConeGeometry(0.0055, 0.018, 6), clawMat); claw.rotation.x = -0.35; claw.position.set(fx, 0.04, 0.099 + len * 0.5); hand.add(claw);
|
|
820
|
+
}
|
|
821
|
+
hand.traverse(o => { if (o.isMesh) o.castShadow = true; });
|
|
822
|
+
hand.rotation.set(0.15, -s * 1.15, s * 0.25); parts.add(hand); hands.push(hand);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// candle rig
|
|
826
|
+
const candleRig = new THREE.Group(); parts.add(candleRig);
|
|
827
|
+
const brassMat = P({ color: 0x7a5a36, metalness: 1.0, roughness: 0.4, clearcoat: 0.2, envMapIntensity: 1.0, bumpMap: stoneBump, bumpScale: 0.5 });
|
|
828
|
+
const waxMat = P({ color: 0xe9dcc2, roughness: 0.48, sheen: 0.5, sheenColor: 0xffd6a0, emissive: 0xff8a3a, emissiveMap: waxGlow, emissiveIntensity: 0, specularIntensity: 0.5 });
|
|
829
|
+
{
|
|
830
|
+
const dish = new THREE.Mesh(new THREE.LatheGeometry([[0.0, 0.0], [0.06, 0.0], [0.074, 0.008], [0.08, 0.019], [0.077, 0.021], [0.07, 0.012], [0.03, 0.009], [0.028, 0.03], [0.024, 0.031], [0.022, 0.012], [0.0, 0.012]].map(p => new THREE.Vector2(...p)), 48), brassMat);
|
|
831
|
+
dish.castShadow = dish.receiveShadow = true; candleRig.add(dish);
|
|
832
|
+
const ring = new THREE.Mesh(new THREE.TorusGeometry(0.022, 0.0045, 8, 24), brassMat); ring.position.set(0.09, 0.012, 0); ring.rotation.y = Math.PI / 2; candleRig.add(ring);
|
|
833
|
+
const cg = new THREE.CylinderGeometry(0.021, 0.023, 0.12, 28, 6); const cp = cg.attributes.position;
|
|
834
|
+
for (let i = 0; i < cp.count; i++) { const y = cp.getY(i), r = Math.hypot(cp.getX(i), cp.getZ(i)); if (y > 0.059 && r < 0.012) cp.setY(i, y - 0.008); }
|
|
835
|
+
cg.computeVertexNormals();
|
|
836
|
+
const candle = new THREE.Mesh(cg, waxMat); candle.position.y = 0.012 + 0.06; candle.castShadow = true; candle.receiveShadow = true; candleRig.add(candle);
|
|
837
|
+
for (let k = 0; k < 5; k++) { const a = k * 1.3 + 0.4, l = 0.012 + hash1(k) * 0.03;
|
|
838
|
+
const d = new THREE.Mesh(new THREE.CapsuleGeometry(0.0042, l, 4, 8), waxMat); d.position.set(Math.cos(a) * 0.022, 0.13 - l / 2 - 0.004, Math.sin(a) * 0.022); candleRig.add(d); }
|
|
839
|
+
const pool = new THREE.Mesh(new THREE.SphereGeometry(1, 20, 10), waxMat); pool.scale.set(0.034, 0.006, 0.032); pool.position.y = 0.012; candleRig.add(pool);
|
|
840
|
+
const wick = new THREE.Mesh(new THREE.CylinderGeometry(0.0016, 0.0018, 0.014, 6), new THREE.MeshPhysicalMaterial({ color: 0x0b0806, roughness: 0.9 }));
|
|
841
|
+
wick.position.set(0, 0.131, 0); wick.rotation.z = 0.12; candleRig.add(wick);
|
|
842
|
+
}
|
|
843
|
+
const FLAME_BASE = new THREE.Vector3(0, 0.137, 0);
|
|
844
|
+
const flameMat = new THREE.ShaderMaterial({
|
|
845
|
+
transparent: true, depthWrite: false, blending: THREE.AdditiveBlending, fog: false,
|
|
846
|
+
uniforms: { uTime: { value: 0 }, uBend: { value: new THREE.Vector3() }, uLife: { value: 0 }, uStretch: { value: 1 }, uFogDensity: { value: FOG_DENSITY } },
|
|
847
|
+
vertexShader: \`uniform float uTime, uStretch; uniform vec3 uBend; varying float vH; varying vec3 vN; varying vec3 vV; varying float vD;
|
|
848
|
+
void main(){ vec3 p = position; float h = p.y; vH = h;
|
|
849
|
+
float fl = sin(uTime*17.0 + h*5.0)*0.5 + sin(uTime*23.0 + 1.7)*0.3 + sin(uTime*9.0 + h*2.0)*0.4;
|
|
850
|
+
p.x += fl*0.07*h*h; p.z += cos(uTime*13.0 + h*4.0)*0.05*h*h;
|
|
851
|
+
p.y *= uStretch*(1.0 + 0.06*sin(uTime*21.0));
|
|
852
|
+
p += uBend*h*h;
|
|
853
|
+
vec4 mv = modelViewMatrix*vec4(p,1.0); vN = normalize(normalMatrix*normal); vV = normalize(-mv.xyz); vD = -mv.z;
|
|
854
|
+
gl_Position = projectionMatrix*mv; }\`,
|
|
855
|
+
fragmentShader: \`uniform float uLife, uFogDensity; varying float vH; varying vec3 vN; varying vec3 vV; varying float vD;
|
|
856
|
+
void main(){ float f = abs(dot(normalize(vN), normalize(vV))); float core = pow(f, 2.6);
|
|
857
|
+
vec3 blue = vec3(0.12, 0.22, 1.1), orange = vec3(2.4, 0.8, 0.14), white = vec3(4.2, 3.1, 1.7);
|
|
858
|
+
vec3 col = mix(orange, white, core*smoothstep(0.95, 0.2, vH));
|
|
859
|
+
col = mix(col, blue, smoothstep(0.22, 0.0, vH)*0.75*(1.0 - core));
|
|
860
|
+
float a = pow(f, 1.2)*smoothstep(1.0, 0.55, vH)*smoothstep(0.0, 0.06, vH);
|
|
861
|
+
float fogF = exp(-pow(uFogDensity*0.5*vD, 2.0));
|
|
862
|
+
gl_FragColor = vec4(col*a*uLife*fogF, 1.0); }\`
|
|
863
|
+
});
|
|
864
|
+
const flame = (() => { const pts = []; for (let i = 0; i <= 28; i++) { const y = i / 28; pts.push(new THREE.Vector2(0.4 * Math.sin(Math.PI * Math.pow(y, 0.55)) * Math.pow(1 - y, 0.35) + 0.0005, y)); }
|
|
865
|
+
const m = new THREE.Mesh(new THREE.LatheGeometry(pts, 28), flameMat); m.scale.set(0.042, 0.064, 0.042); m.position.copy(FLAME_BASE); m.frustumCulled = false; return m; })();
|
|
866
|
+
candleRig.add(flame);
|
|
867
|
+
const halo = new THREE.Sprite(new THREE.SpriteMaterial({ map: haloTex, color: 0xff9a50, blending: THREE.AdditiveBlending, transparent: true, depthWrite: false, fog: false, opacity: 0 }));
|
|
868
|
+
halo.position.copy(FLAME_BASE).add(new THREE.Vector3(0, 0.03, 0)); halo.scale.setScalar(0.85); candleRig.add(halo);
|
|
869
|
+
const candleLight = new THREE.PointLight(0xff9548, 0, 0, 2);
|
|
870
|
+
candleLight.position.copy(FLAME_BASE).add(new THREE.Vector3(0, 0.028, 0));
|
|
871
|
+
candleLight.castShadow = true; candleLight.shadow.mapSize.set(512, 512);
|
|
872
|
+
candleLight.shadow.camera.near = 0.03; candleLight.shadow.camera.far = 14; candleLight.shadow.bias = -0.0025; candleLight.shadow.normalBias = 0.018;
|
|
873
|
+
candleRig.add(candleLight);
|
|
874
|
+
const flameAnchor = new THREE.Object3D(); flameAnchor.position.copy(FLAME_BASE).add(new THREE.Vector3(0, 0.03, 0)); candleRig.add(flameAnchor);
|
|
875
|
+
|
|
876
|
+
/* ---------- the bats themselves: dark body, ears, scalloped membrane wings that flap ---------- */
|
|
877
|
+
const batMat = P({ color: 0x15100f, roughness: 0.75, sheen: 0.4, sheenColor: 0x4a3a36, side: THREE.DoubleSide });
|
|
878
|
+
const batWingGeo = (() => {
|
|
879
|
+
const s = new THREE.Shape(), pts = [[0, 0.06], [0.35, 0.16], [0.7, 0.14], [1.0, 0.05], [0.82, -0.04], [0.75, 0.02], [0.62, -0.1], [0.52, -0.02], [0.38, -0.16], [0.27, -0.06], [0.12, -0.14], [0, -0.08]];
|
|
880
|
+
pts.forEach(([x, y], i) => i ? s.lineTo(x, y) : s.moveTo(x, y)); s.closePath();
|
|
881
|
+
const g = new THREE.ShapeGeometry(s); g.rotateX(Math.PI / 2); return g; // span along +x, leading edge forward (+z)
|
|
882
|
+
})();
|
|
883
|
+
const batBodyGeo = new THREE.SphereGeometry(1, 12, 8), batEarGeo = new THREE.ConeGeometry(1, 2.2, 5);
|
|
884
|
+
const batRigs = BATS.map(bt => {
|
|
885
|
+
const g = new THREE.Group(), k = bt.span;
|
|
886
|
+
const bodyM = new THREE.Mesh(batBodyGeo, batMat); bodyM.scale.set(0.045 * k, 0.04 * k, 0.11 * k); g.add(bodyM);
|
|
887
|
+
const headM = new THREE.Mesh(batBodyGeo, batMat); headM.scale.setScalar(0.035 * k); headM.position.set(0, 0.012 * k, 0.1 * k); g.add(headM);
|
|
888
|
+
for (const s of [-1, 1]) { const e = new THREE.Mesh(batEarGeo, batMat); e.scale.setScalar(0.012 * k); e.position.set(s * 0.016 * k, 0.045 * k, 0.1 * k); e.rotation.z = -s * 0.3; g.add(e); }
|
|
889
|
+
const wings = [-1, 1].map(s => { const piv = new THREE.Group(); piv.position.x = s * 0.03 * k; const w = new THREE.Mesh(batWingGeo, batMat); w.scale.set(s * k * 0.5, 1, k * 0.95); piv.add(w); g.add(piv); return piv; });
|
|
890
|
+
g.visible = false; scene.add(g); return { g, wings };
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
/* ---------- lights ---------- */
|
|
894
|
+
const sun = new THREE.DirectionalLight(0xffcf9a, 0); // low warm morning sun, backlighting the scene
|
|
895
|
+
sun.position.copy(SUN_POS); sun.target.position.set(0, 0, -12);
|
|
896
|
+
sun.castShadow = true; sun.shadow.mapSize.set(2048, 2048);
|
|
897
|
+
Object.assign(sun.shadow.camera, { left: -27, right: 27, top: 27, bottom: -27, near: 1, far: 90 });
|
|
898
|
+
sun.shadow.bias = -0.0005; sun.shadow.normalBias = 0.03;
|
|
899
|
+
scene.add(sun, sun.target);
|
|
900
|
+
const hemi = new THREE.HemisphereLight(0xbcd0e8, 0x5e5040, 0); scene.add(hemi);
|
|
901
|
+
|
|
902
|
+
/* ---------- "Happy Halloween": a burning title in the top-left corner ----------
|
|
903
|
+
Screen-space pass after tone mapping, placed in the lower part of the frame so it frames
|
|
904
|
+
the monument instead of covering it. The letters ignite left to right behind a hot edge,
|
|
905
|
+
then burn: fire flowing upward inside, a charred rim, an orange halo, heat shimmer and
|
|
906
|
+
embers rising off the words. */
|
|
907
|
+
// <fire-title>
|
|
908
|
+
const TITLE_T0 = 9.8, TITLE_W = 0.42, TITLE_CY = 0.867, TITLE_LEFT = 0.05; // start, rect width, rect centre (from bottom), left margin
|
|
909
|
+
const TITLE_FRAG = \`
|
|
910
|
+
uniform sampler2D tDiffuse, tMask; uniform float uT, uAspect, uOn; uniform vec4 uRect; varying vec2 vUv;
|
|
911
|
+
float hh(vec2 p){ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
|
|
912
|
+
float vn(vec2 p){ vec2 i = floor(p), f = fract(p); f = f*f*(3.0-2.0*f);
|
|
913
|
+
return mix(mix(hh(i), hh(i+vec2(1.0,0.0)), f.x), mix(hh(i+vec2(0.0,1.0)), hh(i+vec2(1.0,1.0)), f.x), f.y); }
|
|
914
|
+
float fbm(vec2 p){ float s = 0.0, a = 0.5; for (int i = 0; i < 4; i++){ s += a*vn(p); p = p*2.03 + vec2(1.7, 9.2); a *= 0.5; } return s; }
|
|
915
|
+
void main(){
|
|
916
|
+
vec3 col = texture2D(tDiffuse, vUv).rgb;
|
|
917
|
+
vec2 q = (vUv - uRect.xy)/uRect.zw + 0.5;
|
|
918
|
+
if (uT <= 0.0 || uOn <= 0.0 || q.x < -0.12 || q.x > 1.12 || q.y < -0.5 || q.y > 2.2) { gl_FragColor = vec4(col, 1.0); return; }
|
|
919
|
+
float R = uRect.z*uAspect/uRect.w;
|
|
920
|
+
vec2 hq = q + vec2((vn(vec2(q.x*14.0, q.y*6.0 - uT*3.0)) - 0.5)*0.003, (vn(vec2(q.x*10.0 + 3.0, q.y*5.0 - uT*2.5)) - 0.5)*0.012); // heat shimmer
|
|
921
|
+
vec4 M = texture2D(tMask, hq); // r sharp, g soft, b wide halo
|
|
922
|
+
float n = fbm(q*vec2(R*2.5, 2.5) + 3.1);
|
|
923
|
+
float front = uT*0.75 - q.x*0.9 + (n - 0.5)*0.5; // ignition sweeps left to right
|
|
924
|
+
float lit = smoothstep(0.0, 0.08, front);
|
|
925
|
+
float edge = smoothstep(0.0, 0.05, front)*(1.0 - smoothstep(0.05, 0.28, front));
|
|
926
|
+
col *= 1.0 - 0.5*M.b*smoothstep(-0.2, 0.3, front); // the night darkens behind the words
|
|
927
|
+
float flick = 0.86 + 0.14*vn(vec2(uT*7.0, 1.0));
|
|
928
|
+
float fire = fbm(vec2(q.x*R*3.0, q.y*3.0 - uT*1.6));
|
|
929
|
+
vec3 deep = vec3(0.78, 0.17, 0.02), hot = vec3(1.0, 0.6, 0.1), core = vec3(1.0, 0.92, 0.66);
|
|
930
|
+
vec3 fillc = mix(deep, hot, smoothstep(0.3, 0.75, fire + 0.3*(1.0 - clamp(q.y, 0.0, 1.0))));
|
|
931
|
+
fillc = mix(fillc, core, smoothstep(0.72, 0.95, fire)*0.6);
|
|
932
|
+
float inside = smoothstep(0.38, 0.62, M.g);
|
|
933
|
+
float rim = smoothstep(0.38, 0.5, M.g)*(1.0 - smoothstep(0.55, 0.85, M.g));
|
|
934
|
+
vec3 letters = fillc*flick*(1.0 - 0.45*rim); // charred rim, burning heart
|
|
935
|
+
col += vec3(1.0, 0.4, 0.07)*(M.g*0.55 + M.b*0.55)*flick*lit*0.6; // halo
|
|
936
|
+
col = mix(col, letters, inside*lit);
|
|
937
|
+
col += vec3(1.0, 0.82, 0.5)*edge*smoothstep(0.15, 0.6, M.g)*1.3; // the igniting edge flares
|
|
938
|
+
vec2 sp = vec2(q.x*R*6.0, q.y*6.0 - uT*1.3); // embers drifting up off the words
|
|
939
|
+
vec2 cid = floor(sp), cf = fract(sp) - 0.5; float hs = hh(cid);
|
|
940
|
+
vec2 off = vec2(hh(cid + 7.1) - 0.5, hh(cid + 3.3) - 0.5)*0.6 + vec2(sin(uT*3.0 + hs*20.0)*0.15, 0.0);
|
|
941
|
+
float spark = step(0.86, hs)*smoothstep(0.09, 0.0, length(cf - off))*smoothstep(2.0, 0.7, q.y)*smoothstep(0.1, 0.6, q.y)
|
|
942
|
+
* smoothstep(0.05, 0.3, max(texture2D(tMask, vec2(q.x, 0.34)).b, texture2D(tMask, vec2(q.x, 0.66)).b))*lit;
|
|
943
|
+
col += vec3(1.0, 0.55, 0.15)*spark*(0.55 + 0.45*sin(uT*9.0 + hs*40.0));
|
|
944
|
+
gl_FragColor = vec4(min(col, vec3(1.0)), 1.0);
|
|
945
|
+
}\`;
|
|
946
|
+
function blurInto(src, CW, CH, RB, passes) {
|
|
947
|
+
const b = src.slice(), tmp = new Float32Array(src.length), cl = (v, n) => Math.min(n - 1, Math.max(0, v)), k = 1 / (2 * RB + 1);
|
|
948
|
+
for (let pass = 0; pass < passes; pass++) {
|
|
949
|
+
for (let y = 0; y < CH; y++) { const row = y * CW; let s = 0;
|
|
950
|
+
for (let x = -RB; x <= RB; x++) s += b[row + cl(x, CW)];
|
|
951
|
+
for (let x = 0; x < CW; x++) { tmp[row + x] = s * k; s += b[row + cl(x + RB + 1, CW)] - b[row + cl(x - RB, CW)]; } }
|
|
952
|
+
for (let x = 0; x < CW; x++) { let s = 0;
|
|
953
|
+
for (let y = -RB; y <= RB; y++) s += tmp[cl(y, CH) * CW + x];
|
|
954
|
+
for (let y = 0; y < CH; y++) { b[y * CW + x] = s * k; s += tmp[cl(y + RB + 1, CH) * CW + x] - tmp[cl(y - RB, CH) * CW + x]; } }
|
|
955
|
+
}
|
|
956
|
+
return b;
|
|
957
|
+
}
|
|
958
|
+
function buildTitleMask(text) {
|
|
959
|
+
const CW = 2048, CH = 512, c = document.createElement('canvas'); c.width = CW; c.height = CH;
|
|
960
|
+
const g = c.getContext('2d'), font = s => \`400 \${s}px "Creepster", "Chiller", "Papyrus", Georgia, serif\`;
|
|
961
|
+
const lines = text.split('\\n');
|
|
962
|
+
g.font = font(260); const widest = Math.max(...lines.map(l => g.measureText(l).width));
|
|
963
|
+
const fs = Math.floor(Math.min(260 * 1820 / widest, 400 / (lines.length * 1.12))); g.font = font(fs);
|
|
964
|
+
g.textAlign = 'left'; g.textBaseline = 'middle'; g.fillStyle = g.strokeStyle = '#fff'; g.lineJoin = 'round'; g.lineWidth = fs * 0.02;
|
|
965
|
+
lines.forEach((l, i) => { const y = CH / 2 + (i - (lines.length - 1) / 2) * fs * 1.12; g.fillText(l, 40, y); g.strokeText(l, 40, y); }); // left-aligned
|
|
966
|
+
const px = g.getImageData(0, 0, CW, CH).data, a = new Float32Array(CW * CH);
|
|
967
|
+
for (let i = 0; i < a.length; i++) a[i] = px[i * 4 + 3] / 255;
|
|
968
|
+
const soft = blurInto(a, CW, CH, 3, 2), wide = blurInto(a, CW, CH, 22, 3);
|
|
969
|
+
const data = new Uint8Array(CW * CH * 4);
|
|
970
|
+
for (let y = 0; y < CH; y++) for (let x = 0; x < CW; x++) {
|
|
971
|
+
const i = y * CW + x, o = ((CH - 1 - y) * CW + x) * 4; // flip: texture rows run bottom-up
|
|
972
|
+
data[o] = a[i] * 255; data[o + 1] = soft[i] * 255; data[o + 2] = Math.min(1, wide[i] * 1.8) * 255; data[o + 3] = 255;
|
|
973
|
+
}
|
|
974
|
+
return { data, CW, CH };
|
|
975
|
+
}
|
|
976
|
+
// </fire-title>
|
|
977
|
+
await Promise.race([document.fonts ? document.fonts.load('400 200px "Creepster"') : Promise.resolve(), new Promise(res => setTimeout(res, 3000))]);
|
|
978
|
+
const titleMask = buildTitleMask('Happy\\nHalloween');
|
|
979
|
+
const titleTex = new THREE.DataTexture(titleMask.data, titleMask.CW, titleMask.CH, THREE.RGBAFormat);
|
|
980
|
+
titleTex.minFilter = titleTex.magFilter = THREE.LinearFilter; titleTex.generateMipmaps = false; titleTex.needsUpdate = true;
|
|
981
|
+
const titlePass = new ShaderPass({
|
|
982
|
+
uniforms: { tDiffuse: { value: null }, tMask: { value: titleTex }, uT: { value: 0 }, uOn: { value: 0 }, uAspect: { value: W / H },
|
|
983
|
+
uRect: { value: new THREE.Vector4(TITLE_LEFT + TITLE_W / 2 - 40 / 2048 * TITLE_W, TITLE_CY, TITLE_W, TITLE_W * (W / H) / 4) } },
|
|
984
|
+
vertexShader: \`varying vec2 vUv; void main(){ vUv = uv; gl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0); }\`,
|
|
985
|
+
fragmentShader: TITLE_FRAG
|
|
986
|
+
});
|
|
987
|
+
|
|
988
|
+
/* ---------- "Applying Style....": a small loading line at the bottom centre ----------
|
|
989
|
+
Text and dots are drawn once into a mipmapped mask (red = words, green = dots) so they stay
|
|
990
|
+
crisp at a small size; the dots fill in one by one and reset, and a soft shimmer sweeps the words. */
|
|
991
|
+
// <status-line>
|
|
992
|
+
const STATUS_CY = 0.045, STATUS_H = 0.031; // centre (from bottom) and rect height, as fractions of the screen
|
|
993
|
+
const STATUS_FRAG = \`
|
|
994
|
+
uniform sampler2D tDiffuse, tStatus; uniform float uT, uOn, uDot0, uDotStep; uniform vec4 uRect; varying vec2 vUv;
|
|
995
|
+
void main(){
|
|
996
|
+
vec3 col = texture2D(tDiffuse, vUv).rgb;
|
|
997
|
+
vec2 q = (vUv - uRect.xy)/uRect.zw + 0.5;
|
|
998
|
+
if (uOn <= 0.0 || q.x < 0.0 || q.x > 1.0 || q.y < -0.2 || q.y > 1.0) { gl_FragColor = vec4(col, 1.0); return; }
|
|
999
|
+
float appear = smoothstep(1.0, 1.8, uT);
|
|
1000
|
+
vec4 m = texture2D(tStatus, q);
|
|
1001
|
+
float phase = fract(uT/1.6)*5.0, i = floor((q.x - uDot0)/uDotStep);
|
|
1002
|
+
float dotA = smoothstep(i + 0.6, i + 1.0, phase)*(1.0 - smoothstep(4.55, 4.95, phase)); // dots fill in, then reset
|
|
1003
|
+
float sweep = exp(-pow((q.x - (fract(uT/2.4)*1.6 - 0.3))*7.0, 2.0)); // soft shimmer
|
|
1004
|
+
float a = (m.r*(0.6 + 0.4*sweep) + m.g*dotA*0.85)*appear;
|
|
1005
|
+
gl_FragColor = vec4(mix(col, vec3(0.93, 0.94, 0.97), a), 1.0);
|
|
1006
|
+
}\`;
|
|
1007
|
+
function buildStatusMask(words, dots) {
|
|
1008
|
+
const CW = 1024, CH = 128, c = document.createElement('canvas'); c.width = CW; c.height = CH;
|
|
1009
|
+
const g = c.getContext('2d'); g.fillStyle = '#000'; g.fillRect(0, 0, CW, CH);
|
|
1010
|
+
g.font = '300 64px "Helvetica Neue", Helvetica, Arial, sans-serif'; if ('letterSpacing' in g) g.letterSpacing = '2px';
|
|
1011
|
+
g.textBaseline = 'middle';
|
|
1012
|
+
const ww = g.measureText(words).width, dw = g.measureText('.').width + 4, x0 = (CW - (ww + dw * dots)) / 2;
|
|
1013
|
+
g.fillStyle = '#f00'; g.fillText(words, x0, CH / 2);
|
|
1014
|
+
g.fillStyle = '#0f0'; for (let i = 0; i < dots; i++) g.fillText('.', x0 + ww + i * dw, CH / 2);
|
|
1015
|
+
const tex = new THREE.CanvasTexture(c); tex.colorSpace = THREE.NoColorSpace;
|
|
1016
|
+
tex.generateMipmaps = true; tex.minFilter = THREE.LinearMipmapLinearFilter; tex.magFilter = THREE.LinearFilter; tex.anisotropy = 4;
|
|
1017
|
+
return { tex, dot0: (x0 + ww) / CW, dotStep: dw / CW, aspect: CW / CH };
|
|
1018
|
+
}
|
|
1019
|
+
// </status-line>
|
|
1020
|
+
const status = buildStatusMask('Applying Style', 4);
|
|
1021
|
+
const statusPass = new ShaderPass({
|
|
1022
|
+
uniforms: { tDiffuse: { value: null }, tStatus: { value: status.tex }, uT: { value: 0 }, uOn: { value: 0 },
|
|
1023
|
+
uDot0: { value: status.dot0 }, uDotStep: { value: status.dotStep },
|
|
1024
|
+
uRect: { value: new THREE.Vector4(0.5, STATUS_CY, STATUS_H * status.aspect / (W / H), STATUS_H) } },
|
|
1025
|
+
vertexShader: \`varying vec2 vUv; void main(){ vUv = uv; gl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0); }\`,
|
|
1026
|
+
fragmentShader: STATUS_FRAG
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
/* ---------- post: MSAA HDR -> depth of field -> ACES -> film ---------- */
|
|
1030
|
+
const rt = new THREE.WebGLRenderTarget(W * pixelRatio, H * pixelRatio, { type: THREE.HalfFloatType, samples: 4 });
|
|
1031
|
+
const composer = new EffectComposer(renderer, rt);
|
|
1032
|
+
composer.addPass(new RenderPass(scene, camera));
|
|
1033
|
+
const bokeh = new BokehPass(scene, camera, { focus: 10, aperture: 0.0004, maxblur: 0.009 });
|
|
1034
|
+
const bokehRender = bokeh.render.bind(bokeh);
|
|
1035
|
+
bokeh.render = (...a) => { mistGroup.visible = false; halo.visible = false; sky.visible = false; bokehRender(...a); mistGroup.visible = true; halo.visible = true; sky.visible = true; };
|
|
1036
|
+
composer.addPass(bokeh);
|
|
1037
|
+
composer.addPass(new OutputPass());
|
|
1038
|
+
const filmPass = new ShaderPass({
|
|
1039
|
+
uniforms: { tDiffuse: { value: null }, uTime: { value: 0 }, uFade: { value: 0 }, uWhite: { value: 0 }, uRes: { value: new THREE.Vector2(W, H) } },
|
|
1040
|
+
vertexShader: \`varying vec2 vUv; void main(){ vUv = uv; gl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0); }\`,
|
|
1041
|
+
fragmentShader: \`uniform sampler2D tDiffuse; uniform float uTime, uFade, uWhite; uniform vec2 uRes; varying vec2 vUv;
|
|
1042
|
+
float h(vec2 p){ return fract(sin(dot(p, vec2(12.9898, 78.233)))*43758.5453); }
|
|
1043
|
+
void main(){ vec2 c = vUv - 0.5; vec2 ca = c*vec2(uRes.x/uRes.y, 1.0); float r2 = dot(ca, ca);
|
|
1044
|
+
vec2 off = c*r2*0.006;
|
|
1045
|
+
vec3 col = vec3(texture2D(tDiffuse, vUv - off).r, texture2D(tDiffuse, vUv).g, texture2D(tDiffuse, vUv + off).b);
|
|
1046
|
+
col *= 1.0 - 0.58*smoothstep(0.12, 0.95, sqrt(r2));
|
|
1047
|
+
float g = h(floor(vUv*uRes) + fract(uTime*7.31)*vec2(91.7, 37.3)) - 0.5;
|
|
1048
|
+
col *= 1.0 + g*0.11;
|
|
1049
|
+
gl_FragColor = vec4(mix(max(col, 0.0)*uFade, vec3(1.0), uWhite), 1.0); }\` // uWhite = 1 gives pure white
|
|
1050
|
+
});
|
|
1051
|
+
composer.addPass(titlePass);
|
|
1052
|
+
composer.addPass(statusPass);
|
|
1053
|
+
composer.addPass(filmPass);
|
|
1054
|
+
|
|
1055
|
+
/* ==========================================================================
|
|
1056
|
+
SEQUENCE — every value is a pure function of t
|
|
1057
|
+
========================================================================== */
|
|
1058
|
+
const tmpV = new THREE.Vector3(), flameW = new THREE.Vector3(), lookFar = new THREE.Vector3(0, 1.15, -30), lookDev = new THREE.Vector3(), Yup = new THREE.Vector3(0, 1, 0);
|
|
1059
|
+
const fogCol = new THREE.Color();
|
|
1060
|
+
const CANDLE_REST = new THREE.Vector3(0, 1.18, 0.42);
|
|
1061
|
+
|
|
1062
|
+
function update(t) {
|
|
1063
|
+
const out = t >= T.out;
|
|
1064
|
+
const on = out ? 0 : 1;
|
|
1065
|
+
|
|
1066
|
+
// world light: the morning fades up from absolute zero; thin drifting cloud barely varies the sun
|
|
1067
|
+
const reveal = (WHITE_BLANK ? 1 : smoother(T.revealIn, T.revealFull, t)) * on; // morning: full daylight under the white
|
|
1068
|
+
const cloud = 1 + 0.05 * (fbm1(t * 0.35) - 0.5);
|
|
1069
|
+
sun.intensity = K.sun * reveal * cloud;
|
|
1070
|
+
hemi.intensity = K.hemi * reveal;
|
|
1071
|
+
scene.environmentIntensity = K.env * reveal;
|
|
1072
|
+
fogCol.copy(FOG_BASE).multiplyScalar(reveal);
|
|
1073
|
+
scene.fog.color.copy(fogCol); scene.background.copy(fogCol); skyMat.uniforms.uFog.value.copy(fogCol);
|
|
1074
|
+
skyMat.uniforms.uZenith.value.copy(ZENITH).multiplyScalar(reveal); skyMat.uniforms.uSunCol.value.copy(SUN_TINT).multiplyScalar(reveal * cloud);
|
|
1075
|
+
|
|
1076
|
+
// devil glide (integrated speed profile) + floating oscillators
|
|
1077
|
+
const u = clamp((t - T.devilGo) / (T.devilArrive - T.devilGo));
|
|
1078
|
+
const s = travel(u), zD = devilZ(t);
|
|
1079
|
+
const vel = (devilZ(t + 0.05) - devilZ(t - 0.05)) / 0.1;
|
|
1080
|
+
const acc = (devilZ(t + 0.1) - 2 * zD + devilZ(t - 0.1)) / 0.01;
|
|
1081
|
+
const settle = Math.pow(1 - s, 2);
|
|
1082
|
+
const swayX = 0.16 * Math.sin(t * 0.55 + 0.4) * settle;
|
|
1083
|
+
const swayV = 0.16 * 0.55 * Math.cos(t * 0.55 + 0.4) * settle;
|
|
1084
|
+
const bob = 0.055 * Math.sin(t * 2.05) + 0.022 * Math.sin(t * 3.7 + 1.3) + 0.012 * Math.sin(t * 0.9 + 2.0);
|
|
1085
|
+
const lift = smoother(T.lift0, T.lift1, t), blow = smoother(T.blow0, T.out - 0.1, t);
|
|
1086
|
+
devil.position.set(swayX, 0.36 + bob, zD);
|
|
1087
|
+
devil.rotation.y = 0.13 * Math.sin(t * 0.38 + 1.1) * settle;
|
|
1088
|
+
body.rotation.x = 0.016 * vel - 0.012 * acc + 0.035 * lift - 0.02 * blow;
|
|
1089
|
+
body.rotation.z = -0.9 * swayV + 0.015 * Math.sin(t * 1.3);
|
|
1090
|
+
U.time.value = t; U.drag.value = clamp(vel / 6, 0, 1); U.flutter.value = 0.7 + 0.5 * clamp(vel / 4, 0, 1);
|
|
1091
|
+
|
|
1092
|
+
// breath: lift candle to the face, inhale, blow
|
|
1093
|
+
const inhale = smooth(T.lift0 + 0.3, T.blow0, t) * (1 - blow);
|
|
1094
|
+
candleRig.position.set(0, CANDLE_REST.y + 0.2 * lift + 0.01 * Math.sin(t * 2.6), CANDLE_REST.z - 0.03 * lift + 0.015 * blow);
|
|
1095
|
+
head.rotation.x = 0.06 + 0.2 * lift + 0.08 * blow + 0.012 * Math.sin(t * 1.1);
|
|
1096
|
+
head.position.z = 0.035 * blow; head.position.y = 1.76 + 0.012 * inhale;
|
|
1097
|
+
robe.scale.set(1 + 0.035 * inhale, 1, 1 + 0.05 * inhale);
|
|
1098
|
+
for (let k = 0; k < 2; k++) {
|
|
1099
|
+
const sgn = k === 0 ? -1 : 1, S = SHOULDERS[k];
|
|
1100
|
+
const Wp = tmpV.copy(candleRig.position).add(new THREE.Vector3(sgn * 0.09, -0.03, -0.05));
|
|
1101
|
+
hands[k].position.copy(Wp);
|
|
1102
|
+
const Sp = S.clone().add(new THREE.Vector3(0, 0.012 * inhale, 0));
|
|
1103
|
+
const E = new THREE.Vector3(sgn * 0.3, 1.12 + 0.12 * lift, 0.17);
|
|
1104
|
+
elbows[k].position.copy(E);
|
|
1105
|
+
const [up, fo] = sleeves[k];
|
|
1106
|
+
let dir = E.clone().sub(Sp); up.position.copy(Sp); up.scale.set(1, dir.length(), 1); up.quaternion.setFromUnitVectors(Yup, dir.normalize());
|
|
1107
|
+
dir = Wp.clone().sub(E); fo.position.copy(E); fo.scale.set(1, dir.length(), 1); fo.quaternion.setFromUnitVectors(Yup, dir.normalize());
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// candle: kindles deep in the fog, flickers, bends and gutters, then dies
|
|
1111
|
+
const kindle = smoother(1.5, 3.5, t) * on;
|
|
1112
|
+
const flick = 0.8 + 0.14 * fbm1(t * 2.3) + 0.06 * noise1(t * 14.0);
|
|
1113
|
+
const gutter = 1 - 0.65 * blow;
|
|
1114
|
+
const candleLevel = kindle * flick * gutter;
|
|
1115
|
+
candleLight.intensity = K.candle * candleLevel;
|
|
1116
|
+
flameMat.uniforms.uTime.value = t; flameMat.uniforms.uLife.value = kindle * (0.85 + 0.15 * flick) * (1 - 0.5 * blow);
|
|
1117
|
+
flameMat.uniforms.uBend.value.set(0.25 * Math.sin(t * 9) * blow, -0.25 * blow, 1.4 * blow + 0.25 * inhale * Math.sin(t * 7));
|
|
1118
|
+
flameMat.uniforms.uStretch.value = 1 + 0.25 * blow;
|
|
1119
|
+
flame.visible = !out;
|
|
1120
|
+
waxMat.emissiveIntensity = 0.9 * candleLevel;
|
|
1121
|
+
candleLight.position.x = 0.002 * Math.sin(t * 13) + 0.012 * blow;
|
|
1122
|
+
|
|
1123
|
+
// eyes wake once it is close
|
|
1124
|
+
const eyeK = smoother(6.5, 10.0, t) * on * (0.85 + 0.15 * noise1(t * 4.0)) * (1 + 0.6 * inhale);
|
|
1125
|
+
eyeMat.color.setRGB(2.1 * eyeK, 0.2 * eyeK, 0.025 * eyeK);
|
|
1126
|
+
|
|
1127
|
+
// pumpkins kindle with the reveal and cower as it passes
|
|
1128
|
+
const fear = smoother(10, 15.5, t);
|
|
1129
|
+
for (const p of pumpkins) {
|
|
1130
|
+
if (!p.faceMat) continue;
|
|
1131
|
+
const f = (0.78 + 0.22 * fbm1(t * 3.1 + p.seed) + 0.07 * noise1(t * 11 + p.seed)) * (1 - 0.45 * fear);
|
|
1132
|
+
const k = smoother(1.6, 6.0, t) * on * f;
|
|
1133
|
+
p.faceMat.color.setRGB(2.6 * k, 0.95 * k, 0.22 * k);
|
|
1134
|
+
if (p.light) p.light.intensity = K.pump * k;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
// bats: fly their paths, flapping, banking into each weave
|
|
1138
|
+
BATS.forEach((bt, i) => {
|
|
1139
|
+
const rig = batRigs[i], p = batPos(bt, t);
|
|
1140
|
+
if (!p || on === 0) { rig.g.visible = false; return; }
|
|
1141
|
+
const q = batPos(bt, Math.min(t + 0.03, bt.t0 + bt.dur)) || p;
|
|
1142
|
+
rig.g.visible = true; rig.g.position.set(p[0], p[1], p[2]); rig.g.lookAt(q[0], q[1], q[2] + (q === p ? 0.01 : 0));
|
|
1143
|
+
rig.g.rotateZ(0.5 * Math.cos(((t - bt.t0) / bt.dur) * 11 + bt.ph));
|
|
1144
|
+
rig.g.rotateX(-0.75); // nose up a little, so the camera sees the wings' undersides rather than their edges
|
|
1145
|
+
const flap = 0.2 + 0.95 * Math.sin(2 * Math.PI * bt.rate * t + bt.ph);
|
|
1146
|
+
rig.wings[0].rotation.z = flap; rig.wings[1].rotation.z = -flap;
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
// camera: one continuous dolly, eased to an almost imperceptible landing
|
|
1150
|
+
const cu = easeLong(t / T.out);
|
|
1151
|
+
camera.position.set(0, lerp(CAM_Y0, CAM_Y1, cu), lerp(CAM_Z0, CAM_Z1, cu));
|
|
1152
|
+
const wLook = smoother(3.5, 13.8, t);
|
|
1153
|
+
lookDev.set(0, 1.735, Math.max(zD, -30));
|
|
1154
|
+
camera.lookAt(tmpV.copy(lookFar).lerp(lookDev, wLook));
|
|
1155
|
+
camera.updateMatrixWorld();
|
|
1156
|
+
|
|
1157
|
+
// derived: candle world position feeds DOF, mist scattering, halo
|
|
1158
|
+
devil.updateMatrixWorld(true);
|
|
1159
|
+
flameAnchor.getWorldPosition(flameW);
|
|
1160
|
+
const camDist = camera.position.distanceTo(flameW);
|
|
1161
|
+
halo.material.opacity = 0.2 * candleLevel * Math.exp(-Math.pow(FOG_DENSITY * 0.55 * camDist, 2));
|
|
1162
|
+
mistUniforms.uTime.value = t; mistUniforms.uColor.value.copy(fogCol).multiplyScalar(1.1);
|
|
1163
|
+
mistUniforms.uCandlePos.value.copy(flameW); mistUniforms.uCandleI.value = candleLevel; mistUniforms.uCam.value.copy(camera.position);
|
|
1164
|
+
sky.position.copy(camera.position);
|
|
1165
|
+
const focus = -flameW.clone().applyMatrix4(camera.matrixWorldInverse).z;
|
|
1166
|
+
bokeh.uniforms.focus.value = focus;
|
|
1167
|
+
bokeh.uniforms.aperture.value = lerp(K.ap0, K.ap1, smoother(5, 15.5, t));
|
|
1168
|
+
|
|
1169
|
+
filmPass.uniforms.uTime.value = t;
|
|
1170
|
+
filmPass.uniforms.uFade.value = WHITE_BLANK ? 1 : on;
|
|
1171
|
+
filmPass.uniforms.uWhite.value = WHITE_BLANK ? Math.max(1 - smoother(0.6, 3.6, t), on ? 0 : 1) : 0; // fade from white; cut to white
|
|
1172
|
+
titlePass.uniforms.uT.value = t - TITLE_T0; titlePass.uniforms.uOn.value = on;
|
|
1173
|
+
statusPass.uniforms.uT.value = t; statusPass.uniforms.uOn.value = on;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
/* ---------- ?export: render every frame from the timeline and save an MP4 with sound ----------
|
|
1177
|
+
Runs entirely in the browser (Chrome or Edge): WebCodecs encodes H.264 video and AAC audio
|
|
1178
|
+
(Opus where AAC isn't available) and mp4-muxer packs them into a file that downloads. */
|
|
1179
|
+
async function exportVideo() {
|
|
1180
|
+
const FPS = 30, VW = 1920, VH = 1080, total = Math.round(T.end / STORY_SCALE * FPS), file = 'halloween_devil_morning.mp4';
|
|
1181
|
+
const ui = document.createElement('p');
|
|
1182
|
+
ui.style.cssText = 'position:fixed;left:18px;bottom:14px;margin:0;font:13px/1.4 system-ui,sans-serif;color:#a7adb5;z-index:10';
|
|
1183
|
+
document.body.appendChild(ui);
|
|
1184
|
+
renderer.domElement.style.width = '100vw'; renderer.domElement.style.height = 'auto';
|
|
1185
|
+
const say = s => { ui.textContent = s; };
|
|
1186
|
+
const breathe = () => new Promise(r => { const c = new MessageChannel(); c.port1.onmessage = r; c.port2.postMessage(0); });
|
|
1187
|
+
try {
|
|
1188
|
+
if (!('VideoEncoder' in window) || !('AudioEncoder' in window)) throw new Error('this browser cannot encode video. Open the page in a recent Chrome or Edge.');
|
|
1189
|
+
const { Muxer, ArrayBufferTarget } = await import('https://cdn.jsdelivr.net/npm/mp4-muxer@5.1.3/build/mp4-muxer.mjs');
|
|
1190
|
+
const aac = (await AudioEncoder.isConfigSupported({ codec: 'mp4a.40.2', sampleRate: 48000, numberOfChannels: 2, bitrate: 192000 })).supported;
|
|
1191
|
+
const muxer = new Muxer({ target: new ArrayBufferTarget(), fastStart: 'in-memory',
|
|
1192
|
+
video: { codec: 'avc', width: VW, height: VH, frameRate: FPS }, audio: { codec: aac ? 'aac' : 'opus', numberOfChannels: 2, sampleRate: 48000 } });
|
|
1193
|
+
let failure = null;
|
|
1194
|
+
const ve = new VideoEncoder({ output: (c, m) => muxer.addVideoChunk(c, m), error: e => { failure = e; } });
|
|
1195
|
+
ve.configure({ codec: 'avc1.640028', width: VW, height: VH, bitrate: 16e6, framerate: FPS });
|
|
1196
|
+
for (let i = 0; i < total; i++) {
|
|
1197
|
+
if (failure) throw failure;
|
|
1198
|
+
window.__renderAt(i / FPS);
|
|
1199
|
+
const frame = new VideoFrame(renderer.domElement, { timestamp: Math.round(i * 1e6 / FPS), duration: Math.round(1e6 / FPS) });
|
|
1200
|
+
ve.encode(frame, { keyFrame: i % (FPS * 2) === 0 }); frame.close();
|
|
1201
|
+
while (ve.encodeQueueSize > 4) await new Promise(r => ve.addEventListener('dequeue', r, { once: true }));
|
|
1202
|
+
if (i % 5 === 0) { say(\`Rendering video \${Math.round(100 * i / total)}%. Keep this tab open and visible.\`); await breathe(); }
|
|
1203
|
+
}
|
|
1204
|
+
await ve.flush();
|
|
1205
|
+
say('Rendering sound…');
|
|
1206
|
+
const actx = new OfflineAudioContext(2, Math.ceil(T.end / STORY_SCALE * 48000), 48000);
|
|
1207
|
+
buildSound(actx, 0, actx.destination);
|
|
1208
|
+
const sound = await actx.startRendering();
|
|
1209
|
+
const ae = new AudioEncoder({ output: (c, m) => muxer.addAudioChunk(c, m), error: e => { failure = e; } });
|
|
1210
|
+
ae.configure({ codec: aac ? 'mp4a.40.2' : 'opus', sampleRate: 48000, numberOfChannels: 2, bitrate: 192000 });
|
|
1211
|
+
for (let s = 0; s < sound.length; s += 4800) {
|
|
1212
|
+
const n = Math.min(4800, sound.length - s), planar = new Float32Array(n * 2);
|
|
1213
|
+
planar.set(sound.getChannelData(0).subarray(s, s + n), 0); planar.set(sound.getChannelData(1).subarray(s, s + n), n);
|
|
1214
|
+
const data = new AudioData({ format: 'f32-planar', sampleRate: 48000, numberOfFrames: n, numberOfChannels: 2, timestamp: Math.round(s * 1e6 / 48000), data: planar });
|
|
1215
|
+
ae.encode(data); data.close();
|
|
1216
|
+
}
|
|
1217
|
+
await ae.flush();
|
|
1218
|
+
if (failure) throw failure;
|
|
1219
|
+
muxer.finalize();
|
|
1220
|
+
const blob = new Blob([muxer.target.buffer], { type: 'video/mp4' }), url = URL.createObjectURL(blob);
|
|
1221
|
+
const a = document.createElement('a'); a.href = url; a.download = file; document.body.appendChild(a); a.click();
|
|
1222
|
+
ui.textContent = ''; const link = document.createElement('a'); link.href = url; link.download = file; link.style.color = '#d8dde3';
|
|
1223
|
+
link.textContent = \`Download \${file} (\${(blob.size / 1e6).toFixed(1)} MB)\`; ui.append('Saved. If the download didn\\'t start: ', link);
|
|
1224
|
+
} catch (e) {
|
|
1225
|
+
say(\`Export stopped: \${e.message || e}\`); console.error(e);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
/* ---------- drive ---------- */
|
|
1230
|
+
function resize() {
|
|
1231
|
+
if (RENDER) return;
|
|
1232
|
+
W = innerWidth; H = innerHeight;
|
|
1233
|
+
camera.aspect = W / H; camera.updateProjectionMatrix();
|
|
1234
|
+
renderer.setPixelRatio(pixelRatio); renderer.setSize(W, H);
|
|
1235
|
+
composer.setPixelRatio(pixelRatio); composer.setSize(W, H);
|
|
1236
|
+
bokeh.uniforms.aspect.value = camera.aspect; filmPass.uniforms.uRes.value.set(W * pixelRatio, H * pixelRatio);
|
|
1237
|
+
titlePass.uniforms.uAspect.value = W / H; titlePass.uniforms.uRect.value.w = TITLE_W * (W / H) / 4;
|
|
1238
|
+
statusPass.uniforms.uRect.value.z = STATUS_H * status.aspect / (W / H);
|
|
1239
|
+
}
|
|
1240
|
+
filmPass.uniforms.uRes.value.set(W * pixelRatio, H * pixelRatio);
|
|
1241
|
+
addEventListener('resize', resize);
|
|
1242
|
+
|
|
1243
|
+
update(0);
|
|
1244
|
+
renderer.compile(scene, camera);
|
|
1245
|
+
|
|
1246
|
+
if (RENDER) {
|
|
1247
|
+
window.__renderAt = t => { update(t * STORY_SCALE); composer.render(); return true; }; // t in video seconds
|
|
1248
|
+
window.__duration = T.end / STORY_SCALE; // 18 s
|
|
1249
|
+
window.__renderAudio = renderSoundtrack; // base64 WAV, 48 kHz stereo, same timeline
|
|
1250
|
+
document.getElementById('hint').remove();
|
|
1251
|
+
window.__ready = true;
|
|
1252
|
+
if (EXPORT) exportVideo();
|
|
1253
|
+
} else {
|
|
1254
|
+
// Live: the timeline runs on the audio clock so picture and sound stay locked.
|
|
1255
|
+
// Browsers only allow sound after a gesture, so it waits on black for a click or key.
|
|
1256
|
+
const hint = document.getElementById('hint');
|
|
1257
|
+
let actx = null, outGain = null, cycleOut = null, cycleStart = 0, nextQueued = false, started = false, clockStart = 0, muted = false;
|
|
1258
|
+
let frames = 0, acc = 0, tuned = false, last = 0;
|
|
1259
|
+
const schedule = at => { const g = actx.createGain(); g.connect(outGain); buildSound(actx, at, g); return g; };
|
|
1260
|
+
function begin() {
|
|
1261
|
+
hint.classList.add('gone');
|
|
1262
|
+
try {
|
|
1263
|
+
if (!actx) { actx = new (window.AudioContext || window.webkitAudioContext)(); outGain = actx.createGain(); outGain.connect(actx.destination); }
|
|
1264
|
+
actx.resume();
|
|
1265
|
+
} catch (e) { actx = null; }
|
|
1266
|
+
if (actx) {
|
|
1267
|
+
if (cycleOut) { const old = cycleOut; old.gain.setTargetAtTime(0, actx.currentTime, 0.04); setTimeout(() => old.disconnect(), 800); }
|
|
1268
|
+
cycleStart = actx.currentTime + 0.15; cycleOut = schedule(cycleStart); nextQueued = false;
|
|
1269
|
+
} else clockStart = performance.now();
|
|
1270
|
+
started = true;
|
|
1271
|
+
}
|
|
1272
|
+
function clock() {
|
|
1273
|
+
if (!actx) return (performance.now() - clockStart) / 1000;
|
|
1274
|
+
return actx.currentTime - cycleStart - (actx.outputLatency || actx.baseLatency || 0);
|
|
1275
|
+
}
|
|
1276
|
+
const LOOP = T.loop / STORY_SCALE; // video seconds
|
|
1277
|
+
function frame(now) {
|
|
1278
|
+
let t = 0;
|
|
1279
|
+
if (started) {
|
|
1280
|
+
t = clock();
|
|
1281
|
+
if (actx && !nextQueued && t > LOOP - 0.6) { cycleOut = schedule(cycleStart + LOOP); nextQueued = true; }
|
|
1282
|
+
if (t >= LOOP) { if (actx) { cycleStart += LOOP; nextQueued = false; } else clockStart += LOOP * 1000; t -= LOOP; }
|
|
1283
|
+
t = Math.max(0, t);
|
|
1284
|
+
}
|
|
1285
|
+
update(t * STORY_SCALE); composer.render();
|
|
1286
|
+
if (started && !tuned && t > 2) { acc += now - last; frames++;
|
|
1287
|
+
if (frames === 90) { tuned = true; if (acc / frames > 19 && pixelRatio > 1) { pixelRatio = 1; resize(); } } }
|
|
1288
|
+
last = now;
|
|
1289
|
+
requestAnimationFrame(frame);
|
|
1290
|
+
}
|
|
1291
|
+
requestAnimationFrame(frame);
|
|
1292
|
+
addEventListener('pointerdown', begin);
|
|
1293
|
+
addEventListener('keydown', e => {
|
|
1294
|
+
if (e.key === 'm' || e.key === 'M') { muted = !muted; if (outGain) outGain.gain.setTargetAtTime(muted ? 0 : 1, actx.currentTime, 0.05); }
|
|
1295
|
+
else if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); begin(); }
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
<\/script>
|
|
1299
|
+
</body>
|
|
1300
|
+
</html>
|
|
1301
|
+
`;
|
|
1302
|
+
export {
|
|
1303
|
+
halloweenDevilLightHtml as default
|
|
1304
|
+
};
|