diff --git a/README.md b/README.md index 9aebe7244..6a80a168c 100644 --- a/README.md +++ b/README.md @@ -53,5 +53,5 @@ Thank you to all the people who have contributed to CosmosJourneyer! ## Special Thanks -- Bilal Molli for his fearless refactoring of the messy code base in its early days +- Martin Molli for his fearless refactoring of the messy code base in its early days - The people from [BabylonJS](https://www.babylonjs.com/) for their amazing work on the BabylonJS framework and their help on the forum \ No newline at end of file diff --git a/src/html/mainMenu.html b/src/html/mainMenu.html new file mode 100644 index 000000000..7935243a1 --- /dev/null +++ b/src/html/mainMenu.html @@ -0,0 +1,87 @@ + \ No newline at end of file diff --git a/src/html/pauseMenu.html b/src/html/pauseMenu.html index 0140583fd..8f7c26b6c 100644 --- a/src/html/pauseMenu.html +++ b/src/html/pauseMenu.html @@ -11,6 +11,8 @@

Paused

Share System
+
Save
+
Resume
diff --git a/src/shaders/matterjet.glsl b/src/shaders/matterjet.glsl index a4459dc7d..50dd3c6ee 100644 --- a/src/shaders/matterjet.glsl +++ b/src/shaders/matterjet.glsl @@ -24,10 +24,12 @@ uniform sampler2D depthSampler;// the depth map of the camera uniform float time; -#include "./utils/camera.glsl" +#include "./utils/camera.glsl"; #include "./utils/object.glsl"; +#include "./utils/rotateAround.glsl"; + #include "./utils/remap.glsl"; #include "./utils/worldFromUV.glsl"; @@ -97,12 +99,12 @@ float spiralDensity(vec3 pointOnCone, vec3 coneAxis, float coneMaxHeight) { float density = 1.0; // smoothstep fadeout when the height is too much (outside of cone) or too low (too close to the star) - density *= smoothstep(0.0, 1.0, 1.0 - heightFraction) * smoothstep(0.0, 0.05, heightFraction); + density *= smoothstep(1.0, 0.0, heightFraction) * smoothstep(0.0, 0.05, heightFraction); - float d = spiralSDF(theta + time, 0.2 + heightFraction) / (0.3 + heightFraction * 2.0); + float d = spiralSDF(theta + time, 0.2 + sqrt(heightFraction) / 2.0) / (0.3 + heightFraction * 2.0); //d = pow(d, 4.0); - density *= smoothstep(0.85, 1.0, 1.0 - d); + density *= smoothstep(0.6, 1.0, pow(1.0 - d, 8.0)) * 2.0; //smoothstep(0.85, 1.0, 1.0 - d) * 2.0; //density *= d * 500.0; @@ -124,11 +126,11 @@ void main() { vec4 finalColor = screenColor; const float jetHeight = 10000000e3; - const vec3 jetColor = vec3(0.2, 0.2, 1.0); + const vec3 jetColor = vec3(0.5, 0.5, 1.0); float t1, t2; - if (rayIntersectCone(camera_position, rayDir, object_position, object_rotationAxis, 0.9, t1, t2)) { + if (rayIntersectCone(camera_position, rayDir, object_position, object_rotationAxis, 0.95, t1, t2)) { if (t2 > 0.0 && t2 < maximumDistance) { vec3 jetPointPosition2 = camera_position + t2 * rayDir - object_position; diff --git a/src/shaders/rings/ringsDensity.glsl b/src/shaders/rings/ringsDensity.glsl index d325c7048..47d06a505 100644 --- a/src/shaders/rings/ringsDensity.glsl +++ b/src/shaders/rings/ringsDensity.glsl @@ -29,7 +29,12 @@ float ringDensityAtPoint(vec3 samplePoint) { if (relativeDistance < rings_start || relativeDistance > rings_end) return 0.0; float uvX = remap(relativeDistance, rings_start, rings_end, 0.0, 1.0); - float lutDensity = texture2D(rings_lut, vec2(uvX, 0.0)).x; + vec2 uv = vec2(uvX, 0.0); + + // trick from https://www.shadertoy.com/view/3dVSzm to avoid Greenwich artifacts + vec2 df = fwidth(uv); + if(df.x > 0.5) df.x = 0.0; + float lutDensity = textureLod(rings_lut, uv, log2(max(df.x, df.y) * 1024.0)).r; return lutDensity; } \ No newline at end of file diff --git a/src/styles/index.scss b/src/styles/index.scss index 9cb2fe950..18d057c71 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -15,6 +15,11 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +@font-face { + font-family: 'Nasalization'; + src: url('./nasalization/nasalization-rg.otf'); +} + *, *::before, *::after { @@ -35,6 +40,8 @@ body { @import "pauseMenu/index.scss"; + @import "mainMenu.scss"; + #renderer { z-index: 1; float: left; diff --git a/src/styles/mainMenu.scss b/src/styles/mainMenu.scss new file mode 100644 index 000000000..f98a50409 --- /dev/null +++ b/src/styles/mainMenu.scss @@ -0,0 +1,149 @@ +#mainMenu { + position: absolute; + width: 100vw; + height: 100vh; + + h1 { + font-family: 'Nasalization', sans-serif; + font-size: 8em; + text-align: center; + color: white; + text-shadow: 0 0 20px rgba(255, 255, 255, 0.5); + transition: 0.1s; + } + + ul#menuItems { + list-style-type: none; + padding: 0; + row-gap: 10px; + display: flex; + flex-direction: column; + width: fit-content; + position: absolute; + top: 65vh; + left: 5%; + transform: translateY(-50%); + transition: 0.2s ease-in-out; + + li { + color: white; + font-family: Nasalization, sans-serif; + font-size: 2em; + line-height: 1.5em; + cursor: pointer; + padding: 5px 20px; + transition: 0.1s; + + &:hover { + background: #002b6a; + } + + &.disabled { + color: grey; + cursor: not-allowed; + } + + &.disabled:hover { + background: black; + } + } + } + + .rightPanel { + position: absolute; + max-width: 1400px; + width: 72%; + right: 2.5vw; + height: 65vh; + bottom: 2.5vh; + transition: 0.2s ease-in-out; + + transform: scale(0); + opacity: 0; + + &.visible { + transform: scale(1); + opacity: 1; + } + + box-shadow: 0 0 20px black; + background: rgba(0, 0, 0, 0.8); + + h2 { + color: white; + text-align: center; + font-family: Nasalization, sans-serif; + font-size: 3em; + margin-block: 0.6em; + } + + h3 { + color: white; + padding: 0 20px; + font-family: Nasalization, sans-serif; + font-size: 1.5em; + } + + #dropFileZone { + height: 100%; + width: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + cursor: pointer; + transition: 0.1s; + + &.dragover { + background: rgba(255, 255, 255, 0.1); + } + + &:hover { + background: rgba(255, 255, 255, 0.05); + } + + &.invalid { + background: rgba(255, 0, 0, 0.1); + } + + p { + color: white; + font-family: Nasalization, sans-serif; + font-size: 1.5em; + text-align: center; + line-height: 1.5em; + padding: 0 20px; + } + } + + &#credits { + p { + text-align: center; + } + } + + p { + color: white; + font-family: Nasalization, sans-serif; + line-height: 1.5em; + font-size: 1.25em; + padding: 0 20px; + + &.signature { + text-align: right; + font-style: italic; + padding-right: 60px; + } + } + + a { + color: white; + transition: 0.1s; + text-decoration: underline solid rgba(255, 255, 255, 0.5); + + &:hover { + text-decoration: underline solid white; + } + } + } +} \ No newline at end of file diff --git a/src/styles/nasalization/Typodermic Desktop EULA 2023.pdf b/src/styles/nasalization/Typodermic Desktop EULA 2023.pdf new file mode 100644 index 000000000..c5936fe81 Binary files /dev/null and b/src/styles/nasalization/Typodermic Desktop EULA 2023.pdf differ diff --git a/src/styles/nasalization/nasalization-rg.otf b/src/styles/nasalization/nasalization-rg.otf new file mode 100644 index 000000000..ce4625f27 Binary files /dev/null and b/src/styles/nasalization/nasalization-rg.otf differ diff --git a/src/styles/nasalization/read-this.html b/src/styles/nasalization/read-this.html new file mode 100644 index 000000000..b5fca4ebc --- /dev/null +++ b/src/styles/nasalization/read-this.html @@ -0,0 +1,1699 @@ + + +Typodermic Fonts Inc. Free Font Instructions 2023 + + + + + +
+ +

+Deutsch +Español +Français +العربية +中国简体 +簡體字 +Čeština +Ελληνικά +עִבְרִית +हिन्दी +Magyar +Íslenska +Indonesia +Italiano +한국말 +日本語 +Norsk +Język Polski +Português +ਪੰਜਾਬੀ +Română +Русский +Svenska +தமிழ் +ภาษาไทย +Türkçe +Українська мова +Tiếng Việt +

+ +

Thank you

+ +

Thanks for downloading a free font from Typodermic Fonts Inc. These fonts included in this ZIP include a free desktop license agreement which permits commercial use. Read the included license agreement for details.

+ + +

Installation

+ + +

Allowed

+ + + +

Not allowed

+ + + +

It’s easy to get a different license agreement. Read the this page for details.

+ +

Other styles

+ +

Many of Typodermic's free fonts have other styles available. Please visit Typodermic Fonts and search for the name of this font in the search bar.

+ +
+ +

Danke dir

+ +

Vielen Dank, dass Sie eine kostenlose Schriftart von heruntergeladen haben Typodermic Fonts Inc. Diese in diesem ZIP enthaltenen Schriftarten beinhalten eine kostenlose Desktop-Lizenzvereinbarung, die eine kommerzielle Nutzung erlaubt. Lesen Sie die beigefügte Lizenzvereinbarung für Details.

+ + +

Installation

+ + +

Erlaubt

+ + + +

Nicht erlaubt

+ + + +

Es ist einfach, eine andere Lizenzvereinbarung zu erhalten. Lesen Sie diese Seite für Details.

+ +

Andere Stile

+ +

Für viele der kostenlosen Schriftarten von Typodermic sind andere Stile verfügbar. Bitte besuchen Sie Typodermic Fonts und suchen Sie in der Suchleiste nach dem Namen dieser Schriftart.

+ + +
+ +

Gracias

+ +

Gracias por descargar una fuente gratuita de Typodermic Fonts Inc. Estas fuentes incluidas en este ZIP incluyen un acuerdo de licencia de escritorio gratuito que permite el uso comercial. Lea el acuerdo de licencia incluido para más detalles.

+ + +

Instalación

+ + +

Permitido

+ + + + +

No permitido

+ + + +

Es fácil obtener un acuerdo de licencia diferente. Lea esta página para más detalles.

+ +

Otros estilos

+ +

Muchas de las fuentes gratuitas de Typodermic tienen otros estilos disponibles. Visite Typodermic Fonts y busque el nombre de esta fuente en la barra de búsqueda.

+ + +
+ +

Merci

+ +

Merci d'avoir téléchargé une police gratuite sur Typodermic Fonts Inc. Ces polices incluses dans ce ZIP incluent un accord de licence de bureau gratuit qui permet une utilisation commerciale. Lisez le contrat de licence inclus pour plus de détails.

+ + +

Installation

+ + + + + +

Autorisé

+ + + + + +

Interdit

+ + + +

Il est facile d'obtenir un autre contrat de licence. Lisez cette page pour plus de détails.

+ +

Autres modèles

+ +

De nombreuses polices gratuites de Typodermic proposent d'autres styles. Veuillez visiter Typodermic Fonts et rechercher le nom de cette police dans la barre de recherche.

+ + +
+ +

شكرًا لك

+ +

+ +شكرا لتحميل الخط المجاني من Typodermic Fonts Inc. تتضمن هذه الخطوط المضمنة في ملف ZIP هذا اتفاقية ترخيص سطح مكتب مجانية تسمح بالاستخدام التجاري. اقرأ اتفاقية الترخيص المضمنة للحصول على التفاصيل.

+ + +

التركيب

+ + +

مسموح

+ + + +

غير مسموح

+ + + +

من السهل للحصول على هذه الصفحة اقرأ هذه الصفحة للحصول على التفاصي

+ + +

أنماط أخرى

+ +

العديد من الخطوط المجانية في Typodermic لها أنماط أخرى متاحة. يرجى زيارة TTypodermic Fonts والبحث عن اسم هذا الخط في شريط البحث.

+ + +
+ +

谢谢

+ +

感谢您从以下位置下载免费字体Typodermic Fonts Inc.此 ZIP 中包含的这些字体包括允许商业使用的免费桌面许可协议。阅读随附的许可协议了解详细信息。

+ + +

安装

+ + +

允许

+ + + + + + + +

不允许

+ + + +

很容易获得不同的许可协议。阅读此页面了解详情。

+ +

其他款式

+ +

Typodermic 的许多免费字体都有其他可用的样式。请访问Typodermic Fonts并在搜索栏中搜索此字体的名称。

+ + +
+ +

謝謝

+ +

感謝您從以下位置下載免費字體Typodermic Fonts Inc。 此 ZIP 中包含的這些字體包括允許商業使用的免費桌面許可協議。閱讀隨附的許可協議了解詳細信息。

+ + +

安裝

+ + +

允許

+ + + +

不允許

+ + + +

很容易獲得不同的許可協議。閱讀此頁面了解詳情。

+ +

其他款式

+ +

Typodermic 的許多免費字體都有其他可用的樣式。請訪問Typodermic Fonts並在搜索欄中搜索此字體的名稱。

+ + +
+ +

Děkuju

+ +

Děkujeme, že jste si zdarma stáhli písmo z Typodermic Fonts Inc. Tato písma obsažená v tomto ZIP zahrnují bezplatnou licenční smlouvu pro stolní počítače, která umožňuje komerční použití. Podrobnosti naleznete v přiložené licenční smlouvě.

+ + +

Instalace

+ + +

Povoleno

+ + + +

Nepovoleno

+ + + +

Je snadné získat jinou licenční smlouvu. Podrobnosti si přečtěte na této stránce. + +

Jiné styly

+ +

Mnoho bezplatných písem Typodermic má k dispozici další styly. Navštivte prosím Typodermic Fonts a vyhledejte název tohoto písma ve vyhledávací liště.

+ +
+ +

Σας ευχαριστώ

+ +

Ευχαριστούμε για τη λήψη μιας δωρεάν γραμματοσειράς από Typodermic Fonts Inc. Αυτές οι γραμματοσειρές που περιλαμβάνονται σε αυτό το ZIP περιλαμβάνουν μια δωρεάν άδεια χρήσης επιτραπέζιου υπολογιστή που επιτρέπει την εμπορική χρήση. Διαβάστε τη συμφωνία άδειας χρήσης που περιλαμβάνεται για λεπτομέρειες.

+ + +

Εγκατάσταση

+ + +

Επιτρέπεται

+ + + +

Δεν επιτρέπεται

+ + + +

Είναι εύκολο να αποκτήσετε μια διαφορετική συμφωνία άδειας χρήσης. Διαβάστε αυτήν τη σελίδα για λεπτομέρειες.

+ +

Άλλα στυλ

+ +

Πολλές από τις δωρεάν γραμματοσειρές του Typodermic έχουν άλλα στυλ διαθέσιμα. Επισκεφτείτε το Typodermic Fonts και αναζητήστε το όνομα αυτής της γραμματοσειράς στη γραμμή αναζήτησης.

+ + +
+ +

תודה

+ +

תודה שהורדת פונט בחינם מTypodermic Fonts Inc.גופנים אלה הכלולים ב-ZIP זה כוללים הסכם רישיון חינם לשולחן העבודה המאפשר שימוש מסחרי. קרא את הסכם הרישיון הכלול לפרטים.

+ + +

הַתקָנָה

+ + + +

מוּתָר

+ + + +

לא מורשה

+ + + +

קל להשיג הסכם רישיון אחר. קרא את הדף הזה לפרטים.

+ +

סגנונות אחרים

+ +

לרבים מהגופנים החינמיים של Typodermic יש סגנונות אחרים זמינים. אנא בקר בגופנים מסוג Typodermic וחפש את השם של גופן זה בשורת החיפוש.

+ +
+ +

शुक्रिया

+ +

से निःशुल्क फ़ॉन्ट डाउनलोड करने के लिए धन्यवादTypodermic Fonts Inc.इस जिप में शामिल इन फोंट में एक मुफ्त डेस्कटॉप लाइसेंस समझौता शामिल है जो व्यावसायिक उपयोग की अनुमति देता है। विवरण के लिए शामिल लाइसेंस समझौते को पढ़ें।

+ + +

इंस्टालेशन

+ + +

अनुमत

+ + + +

अनुमति नहीं

+ + + +

एक अलग लाइसेंस अनुबंध प्राप्त करना आसान है। विवरण के लिए इस पेज को पढ़ें ।

+ +

अन्य शैलियाँ

+ +

टाइपोडर्मिक के कई मुफ्त फोंट में अन्य शैलियाँ उपलब्ध हैं। कृपया टाइपोडर्मिक फ़ॉन्ट्स पर जाएं और सर्च बार में इस फ़ॉन्ट का नाम खोजें।

+ + +
+ +

Köszönöm

+ +

Köszönjük, hogy letöltött egy ingyenes betűtípust innen Typodermic Fonts Inc. A ZIP-ben található betűtípusok ingyenes asztali licencszerződést tartalmaznak, amely lehetővé teszi a kereskedelmi felhasználást. A részletekért olvassa el a mellékelt licencszerződést.

+ + +

Telepítés

+ + +

Engedélyezett

+ + + +

Nem megengedett

+ + + +

Könnyű más licencszerződést kötni. Olvassa el ezt az oldalt a részletekért.

+ +

Egyéb stílusok

+ +

A Typodermic ingyenes betűtípusai közül sok más stílus is elérhető. Kérjük, látogasson el a Typodermic Fonts oldalra , és keresse meg ennek a betűtípusnak a nevét a keresősávban.

+ + +
+ +

Þakka þér fyrir

+ +

Takk fyrir að hlaða niður ókeypis leturgerð frá Typodermic Fonts Inc. Þessar leturgerðir sem fylgja með í þessari ZIP innihalda ókeypis leyfissamning fyrir skrifborð sem leyfir notkun í atvinnuskyni. Lestu meðfylgjandi leyfissamning fyrir frekari upplýsingar.

+ +

Uppsetning

+ + +

Leyfilegt

+ + + +

Ekki leyft

+ + + +

Það er auðvelt að fá annan leyfissamning. Lestu þessa síðu fyrir nánari upplýsingar.

+ +

Aðrir stílar

+ +

Margar af ókeypis leturgerðum Typodermic eru með aðra stíla í boði. Vinsamlegast farðu á Typodermic Fonts og leitaðu að nafni þessarar leturgerðar í leitarstikunni.

+ + +
+ +

Terima kasih

+ +

Terima kasih telah mengunduh font gratis dari Typodermic Fonts Inc. Font-font yang disertakan dalam ZIP ini menyertakan perjanjian lisensi desktop gratis yang mengizinkan penggunaan komersial. Baca perjanjian lisensi yang disertakan untuk detailnya.

+ + +

Instalasi

+ + +

Diizinkan

+ + + +

Tidak diizinkan

+ + + +

Sangat mudah untuk mendapatkan perjanjian lisensi yang berbeda. Baca halaman ini untuk detailnya.

+ +

Gaya lain

+ +

Banyak font gratis Typodermic memiliki gaya lain yang tersedia. Silakan kunjungi Typodermic Fonts dan cari nama font ini di bilah pencarian.

+ + +
+ +

Grazie

+ +

Grazie per aver scaricato un font gratuito daTypodermic Fonts Inc.Questi font inclusi in questo ZIP includono un accordo di licenza desktop gratuito che ne consente l'uso commerciale. Leggere il contratto di licenza incluso per i dettagli.

+ + +

Installazione

+ + +

Permesso

+ + + +

Non autorizzato

+ + + +

È facile ottenere un contratto di licenza diverso. Leggi questa pagina per i dettagli.

+ +

Altri stili

+ +

Molti dei font gratuiti di Typodermic hanno altri stili disponibili. Si prega di visitare Typodermic Fonts e cercare il nome di questo font nella barra di ricerca.

+ + +
+ +

고맙습니다

+ +

에서 무료 글꼴을 다운로드해 주셔서 감사합니다 Typodermic Fonts Inc. 이 ZIP에 포함된 이러한 글꼴에는 상업적 사용을 허용하는 무료 데스크톱 라이선스 계약이 포함되어 있습니다. 자세한 내용은 포함된 라이센스 계약을 읽으십시오.

+ + +

설치

+ + +

허용된

+ + + +

허용되지 않음

+ + + +

+다른 라이센스 계약을 얻는 것은 쉽습니다. 자세한 내용은 이 페이지 를 읽으십시오.

+ +

다른 스타일

+ +

많은 Typodermic의 무료 글꼴에는 다른 스타일을 사용할 수 있습니다. Typodermic Fonts 를 방문 하여 검색창에서 이 글꼴의 이름을 검색하십시오.

+ + +
+ +

ありがとうございます

+ +

から無料フォントをダウンロードしていただきありがとうございますTypodermic Fonts 株式会社。 この ZIP に含まれるこれらのフォントには、商用利用を許可する無料のデスクトップ ライセンス契約が含まれています。詳細については、同梱のライセンス契約をお読みください。

+ + +

インストール

+ + +

許可

+ + + +

禁止

+ + + +

別のライセンス契約を取得するのは簡単です。詳しくはこちらのページをお読みください。

+ +

その他のスタイル

+ +

Typodermic のフリー フォントの多くには、他のスタイルが用意されています。Typodermic Fontsにアクセスし、検索バーでこのフォントの名前を検索してください。 弊社は日本の会社ですので、お問い合わせフォームに日本語で入力してください。

+ + +
+ +

Takk skal du ha

+ +

Takk for at du lastet ned en gratis font fra Typodermic Fonts Inc. Disse skriftene som er inkludert i denne ZIP-filen inkluderer en gratis lisensavtale for skrivebord som tillater kommersiell bruk. Les den medfølgende lisensavtalen for detaljer.

+ + +

Installasjon

+ + +

Tillatt

+ + + +

Ikke tillatt

+ + + +

Det er enkelt å få en annen lisensavtale. Les denne siden for detaljer.

+ +

Andre stiler

+ +

Mange av Typodermics gratis fonter har andre stiler tilgjengelig. Gå til Typodermic Fonts og søk etter navnet på denne fonten i søkefeltet.

+ + +
+ +

Dziękuję Ci

+ +

Dziękujemy za pobranie darmowej czcionki z Typodermic Fonts Inc. Czcionki zawarte w tym pliku ZIP zawierają umowę licencyjną dotyczącą bezpłatnego komputera stacjonarnego, która zezwala na wykorzystanie komercyjne. Przeczytaj dołączoną umowę licencyjną, aby poznać szczegóły.

+ + +

Dozwolony

+ + +

Allowed

+ + + +

Nie dozwolony

+ + + +

Uzyskanie innej umowy licencyjnej jest łatwe. Przeczytaj tę stronę, aby uzyskać szczegółowe informacje.

+ +

Inne style

+ +

Wiele darmowych czcionek Typodermic ma dostępne inne style. Odwiedź stronę Typodermic Fonts i wyszukaj nazwę tej czcionki w pasku wyszukiwania.

+ + +
+ +

Obrigada

+ +

Obrigado por baixar uma fonte gratuita de Typodermic Fonts Inc. Essas fontes incluídas neste ZIP incluem um contrato de licença de desktop gratuito que permite o uso comercial. Leia o contrato de licença incluído para obter detalhes.

+ + +

Instalação

+ + +

Permitido

+ + + +

Não permitido

+ + + +

É fácil obter um contrato de licença diferente. Leia esta página para obter detalhes.

+ +

Outros estilos

+ +

Muitas das fontes gratuitas do Typodermic têm outros estilos disponíveis. Por favor, visite Typodermic Fonts e procure o nome desta fonte na barra de pesquisa.

+ + +
+ +

ਤੁਹਾਡਾ ਧੰਨਵਾਦ

+ +

ਤੋਂ ਇੱਕ ਮੁਫਤ ਫੌਂਟ ਡਾਊਨਲੋਡ ਕਰਨ ਲਈ ਧੰਨਵਾਦ Typodermic Fonts Inc. ਇਸ ਜ਼ਿਪ ਵਿੱਚ ਸ਼ਾਮਲ ਇਹਨਾਂ ਫੌਂਟਾਂ ਵਿੱਚ ਇੱਕ ਮੁਫਤ ਡੈਸਕਟੌਪ ਲਾਇਸੈਂਸ ਸਮਝੌਤਾ ਸ਼ਾਮਲ ਹੈ ਜੋ ਵਪਾਰਕ ਵਰਤੋਂ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦਾ ਹੈ। ਵੇਰਵਿਆਂ ਲਈ ਸ਼ਾਮਲ ਲਾਇਸੰਸ ਸਮਝੌਤੇ ਨੂੰ ਪੜ੍ਹੋ।

+ + +

ਇੰਸਟਾਲੇਸ਼ਨ

+ + +

ਦੀ ਇਜਾਜ਼ਤ ਹੈ

+ + + +

ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ

+ + + +

ਇੱਕ ਵੱਖਰਾ ਲਾਇਸੰਸ ਸਮਝੌਤਾ ਪ੍ਰਾਪਤ ਕਰਨਾ ਆਸਾਨ ਹੈ। ਵੇਰਵਿਆਂ ਲਈ ਇਸ ਪੰਨੇ ਨੂੰ ਪੜ੍ਹੋ ।

+ +

ਹੋਰ ਸਟਾਈਲ

+ +

ਟਾਈਪੋਡਰਮਿਕ ਦੇ ਬਹੁਤ ਸਾਰੇ ਮੁਫਤ ਫੌਂਟਾਂ ਦੀਆਂ ਹੋਰ ਸ਼ੈਲੀਆਂ ਉਪਲਬਧ ਹਨ। ਕਿਰਪਾ ਕਰਕੇ Typodermic Fonts 'ਤੇ ਜਾਓ ਅਤੇ ਖੋਜ ਪੱਟੀ ਵਿੱਚ ਇਸ ਫੌਂਟ ਦੇ ਨਾਮ ਦੀ ਖੋਜ ਕਰੋ।

+ + +
+ +

Mulțumesc

+ +

Vă mulțumim pentru descărcarea unui font gratuit de la Typodermic Fonts Inc. Aceste fonturi incluse în acest ZIP includ un acord de licență gratuit pentru desktop, care permite utilizarea comercială. Citiți acordul de licență inclus pentru detalii.

+ + +

Instalare

+ + +

Permis

+ + + +

Nepermis

+ + + +

Este ușor să obțineți un alt acord de licență. Citiți această pagină pentru detalii.

+ +

Alte stiluri

+ +

Multe dintre fonturile gratuite ale Typodermic au alte stiluri disponibile. Vă rugăm să vizitați Typodermic Fonts și să căutați numele acestui font în bara de căutare.

+ + +
+ +

Спасибо

+ +

Спасибо за загрузку бесплатного шрифта с Typodermic Fonts Inc. Эти шрифты, включенные в этот ZIP-файл, включают бесплатное лицензионное соглашение для настольных компьютеров, которое разрешает коммерческое использование. Подробнее читайте в прилагаемом лицензионном соглашении.

+ + +

Установка

+ + +

Допустимый

+ + + +

Не положено

+ + + +

Легко получить другое лицензионное соглашение. Подробности читайте на этой странице .

+ +

Другие стили

+ +

Для многих бесплатных шрифтов Typodermic доступны другие стили. Пожалуйста, посетите Typodermic Fonts и найдите название этого шрифта в строке поиска.

+ + +
+ +

Tack

+ +

Tack för att du laddar ner ett gratis typsnitt från Typodermic Fonts Inc. Dessa typsnitt som ingår i denna ZIP inkluderar ett gratis licensavtal för skrivbordet som tillåter kommersiell användning. Läs det medföljande licensavtalet för detaljer.

+ + +

Installation

+ + +

Tillåten

+ + + +

Inte tillåtet

+ + + +

Det är lätt att få ett annat licensavtal. Läs den här sidan för detaljer.

+ +

Andra stilar

+ +

Många av Typodermics gratis typsnitt har andra stilar tillgängliga. Besök Typodermic Fonts och sök efter namnet på detta teckensnitt i sökfältet.

+ + +
+ +

நன்றி

+ +

இதிலிருந்து இலவச எழுத்துருவைப் பதிவிறக்கியதற்கு நன்றி Typodermic Fonts Inc. இந்த ஜிப்பில் சேர்க்கப்பட்டுள்ள இந்த எழுத்துருக்கள் வணிகரீதியான பயன்பாட்டை அனுமதிக்கும் இலவச டெஸ்க்டாப் உரிம ஒப்பந்தத்தை உள்ளடக்கியது. விவரங்களுக்கு சேர்க்கப்பட்ட உரிம ஒப்பந்தத்தைப் படிக்கவும்.

+ + +

நிறுவல்

+ + +

அனுமதிக்கப்பட்டது

+ + + +

அனுமதி இல்லை

+ + + +

வேறுபட்ட உரிம ஒப்பந்தத்தைப் பெறுவது எளிது. விவரங்களுக்கு இந்தப் பக்கத்தைப் படிக்கவும் .

+ +

பிற பாணிகள்

+ +

Typodermic இன் இலவச எழுத்துருக்கள் பல மற்ற பாணிகளைக் கொண்டுள்ளன. தயவு செய்து Typodermic எழுத்துருக்களைப் பார்வையிடவும் மற்றும் தேடல் பட்டியில் இந்த எழுத்துருவின் பெயரைத் தேடவும்.

+ + +
+ +

ขอขอบคุณ

+ +

Tขอขอบคุณที่ดาวน์โหลดฟอนต์ฟรีจากTypodermic Fonts Inc. แบบอักษรเหล่านี้ที่รวมอยู่ใน ZIP นี้รวมถึงข้อตกลงสิทธิ์การใช้งานเดสก์ท็อปฟรีซึ่งอนุญาตให้ใช้ในเชิงพาณิชย์ อ่านข้อตกลงใบอนุญาตรวมสำหรับรายละเอียด

+ + +

การติดตั้ง

+ + +

อนุญาต

+ + + +

ไม่ได้รับอนุญาต

+ + + +

ง่ายที่จะได้รับข้อตกลงสิทธิ์การใช้งานอื่น อ่านหน้านี้เพื่อดูรายละเอียด

+ +

สไตล์อื่นๆ

+ +

ฟอนต์ฟรีมากมายของ Typodermic มีสไตล์อื่นๆ ให้เลือกใช้ โปรดไปที่ Typodermic Fontsและค้นหาชื่อของแบบอักษรนี้ในแถบค้นหา.

+ + +
+ +

Teşekkür ederim

+ +

Typodermic Fonts Inc.'den ücretsiz bir yazı tipi indirdiğiniz için teşekkür ederiz. Bu ZIP'te bulunan bu yazı tipleri, ticari kullanıma izin veren ücretsiz bir masaüstü lisans sözleşmesi içerir. Ayrıntılar için verilen lisans sözleşmesini okuyun.

+ + +

Kurulum

+ + +

İzin verilmiş

+ + + +

İzin verilmedi

+ + + +

Farklı bir lisans sözleşmesi almak kolaydır. Ayrıntılar için bu sayfayı okuyun .

+ +

Diğer stiller

+ +

Typodermic'in ücretsiz yazı tiplerinin birçoğunda başka stiller mevcuttur. Lütfen Typodermic Fonts'u ziyaret edin ve arama çubuğunda bu fontun adını arayın.

+ +
+ +

Дуже дякую

+ +

Дякуємо за завантаження безкоштовного шрифту з Typodermic Fonts Inc. Ці шрифти, включені в цей ZIP, включають безкоштовну ліцензійну угоду для комп’ютера, яка дозволяє комерційне використання. Прочитайте включену ліцензійну угоду, щоб дізнатися більше.

+ + +

монтаж

+ + +

Дозволено

+ + + +

Не дозволено

+ + + +

Отримати іншу ліцензійну угоду легко. Прочитайте цю сторінку для отримання деталей.

+ +

Інші стилі

+ +

Багато безкоштовних шрифтів Typodermic мають інші доступні стилі. Відвідайте Typodermic Fonts і знайдіть назву цього шрифту в рядку пошуку.

+ + +
+ + +

Cảm ơn bạn

+ +

Cảm ơn bạn đã tải xuống một phông chữ miễn phí từ Typodermic Fonts Inc. Những phông chữ có trong ZIP này bao gồm thỏa thuận cấp phép máy tính để bàn miễn phí cho phép sử dụng thương mại. Đọc thỏa thuận cấp phép đi kèm để biết chi tiết.

+ + +

Cài đặt

+ + + + + + +

Không cho phép

+ + + +

Thật dễ dàng để có được một thỏa thuận cấp phép khác. Đọc trang này để biết chi tiết.

+ +

phong cách khác

+ +

Nhiều phông chữ miễn phí của Typodermic có sẵn các kiểu khác. Vui lòng truy cập Typodermic Fonts và tìm kiếm tên của phông chữ này trong thanh tìm kiếm.

+ +
+ + \ No newline at end of file diff --git a/src/ts/alphaTestis.ts b/src/ts/alphaTestis.ts new file mode 100644 index 000000000..1dffabd99 --- /dev/null +++ b/src/ts/alphaTestis.ts @@ -0,0 +1,205 @@ +// This file is part of CosmosJourneyer +// +// Copyright (C) 2024 Barthélemy Paléologue +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import "../styles/index.scss"; + +import { StarSystemController } from "./starSystem/starSystemController"; + +import { Settings } from "./settings"; +import { Assets } from "./assets"; +import { positionNearObjectBrightSide } from "./utils/positionNearObject"; +import { CosmosJourneyer } from "./cosmosJourneyer"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { Color3 } from "@babylonjs/core/Maths/math.color"; +import { PostProcessType } from "./postProcesses/postProcessTypes"; +import { TelluricPlanetModel } from "./planets/telluricPlanet/telluricPlanetModel"; +import { GasPlanetModel } from "./planets/gasPlanet/gasPlanetModel"; +import { getForwardDirection, getRotationQuaternion, setRotationQuaternion, translate } from "./uberCore/transforms/basicTransform"; +import { StarSystemHelper } from "./starSystem/starSystemHelper"; +import { StarModel } from "./stellarObjects/star/starModel"; +import { RingsUniforms } from "./postProcesses/rings/ringsUniform"; +import { getMoonSeed } from "./planets/common"; +import { SystemSeed } from "./utils/systemSeed"; + +const engine = new CosmosJourneyer(); + +await engine.setup(); + +const starSystemView = engine.getStarSystemView(); + +const spaceshipController = starSystemView.getSpaceshipControls(); + +const characterController = starSystemView.getCharacterControls(); + +// const physicsViewer = new PhysicsViewer(); +// physicsViewer.showBody(spaceshipController.aggregate.body); + +console.log(`Time is going ${Settings.TIME_MULTIPLIER} time${Settings.TIME_MULTIPLIER > 1 ? "s" : ""} faster than in reality`); + +const starSystemSeed = new SystemSeed(0, 0, 0, 0); +const starSystem = new StarSystemController(starSystemSeed, starSystemView.scene); +starSystem.model.setName("Alpha Testis"); + +engine.getStarSystemView().setStarSystem(starSystem, false); + +const sunModel = new StarModel(0.51); +const sun = StarSystemHelper.makeStar(starSystem, sunModel); +sun.model.orbit.period = 60 * 60 * 24; + +/*const secundaModel = new StarModel(-672446, sunModel); +secundaModel.orbit.radius = 30 * sunModel.radius; +secundaModel.orbit.period = 60 * 60; +const secunda = StarSystemHelper.makeStar(starSystem, secundaModel); + +const terminaModel = new StarModel(756263, sunModel); +terminaModel.orbit.radius = 50 * sunModel.radius; +terminaModel.orbit.period = 60 * 60; +const termina = StarSystemHelper.makeStar(starSystem, terminaModel);*/ + +const planetModel = new TelluricPlanetModel(0.4233609183800225, sunModel); +planetModel.physicalProperties.minTemperature = -55; +planetModel.physicalProperties.maxTemperature = 30; + +planetModel.orbit.period = 60 * 60 * 24 * 365.25; +planetModel.orbit.radius = 4000 * planetModel.radius; +planetModel.orbit.normalToPlane = Vector3.Up(); + +const planet = StarSystemHelper.makeTelluricPlanet(starSystem, planetModel); +planet.model.ringsUniforms = new RingsUniforms(planet.model.rng); +planet.postProcesses.push(PostProcessType.RING); + +//const spacestation = new SpaceStation(starSystemView.scene, planet); +//starSystemView.getStarSystem().addSpaceStation(spacestation); + +const moonModel = new TelluricPlanetModel(getMoonSeed(planetModel, 0), planetModel); +moonModel.physicalProperties.mass = 2; +moonModel.physicalProperties.rotationPeriod = 7 * 60 * 60; +moonModel.physicalProperties.minTemperature = -180; +moonModel.physicalProperties.maxTemperature = 200; +moonModel.physicalProperties.waterAmount = 0.9; + +moonModel.orbit.period = moonModel.physicalProperties.rotationPeriod; +moonModel.orbit.radius = 8 * planet.getRadius(); +moonModel.orbit.normalToPlane = Vector3.Up(); + +const moon = StarSystemHelper.makeSatellite(starSystem, planet, moonModel); + +moon.material.colorSettings.plainColor.copyFromFloats(0.67, 0.67, 0.67); +moon.material.colorSettings.desertColor.copyFrom(new Color3(116, 134, 121).scale(1 / 255)); +moon.material.colorSettings.steepColor.copyFrom(new Color3(92, 92, 92).scale(1 / 255)); + +moon.material.setTexture("plainNormalMap", Assets.DirtNormalMap); +moon.material.setTexture("bottomNormalMap", Assets.DirtNormalMap); +moon.material.updateConstants(); + +const aresModel = new TelluricPlanetModel(0.3725, sunModel); +aresModel.physicalProperties.mass = 7; +aresModel.physicalProperties.rotationPeriod = (24 * 60 * 60) / 30; +aresModel.physicalProperties.minTemperature = -30; +aresModel.physicalProperties.maxTemperature = 20; +aresModel.physicalProperties.pressure = 0.5; +aresModel.physicalProperties.waterAmount = 0.2; +aresModel.physicalProperties.oceanLevel = 0; + +aresModel.orbit.period = 60 * 60 * 24 * 365.24; +aresModel.orbit.radius = 4020 * planet.getRadius(); +aresModel.orbit.normalToPlane = Vector3.Up(); + +//aresModel.terrainSettings.continents_fragmentation = 0.0; +//aresModel.terrainSettings.continent_base_height = 10e3; +//aresModel.terrainSettings.max_mountain_height = 20e3; + +const ares = StarSystemHelper.makeTelluricPlanet(starSystem, aresModel); +ares.postProcesses.splice(ares.postProcesses.indexOf(PostProcessType.OCEAN), 1); +ares.postProcesses.splice(ares.postProcesses.indexOf(PostProcessType.CLOUDS), 1); + +ares.material.colorSettings.plainColor.copyFromFloats(139 / 255, 59 / 255, 24 / 255); +ares.material.colorSettings.desertColor.copyFromFloats(178 / 255, 107 / 255, 42 / 255); +ares.material.colorSettings.beachColor.copyFrom(ares.material.colorSettings.plainColor); +ares.material.colorSettings.bottomColor.copyFrom(ares.material.colorSettings.plainColor.scale(0.9)); + +ares.material.updateConstants(); + +const andromaqueModel = new GasPlanetModel(0.28711440474126226, sunModel); +andromaqueModel.orbit.period = 60 * 60 * 24 * 365.25; +andromaqueModel.orbit.radius = 4300 * ares.getRadius(); +andromaqueModel.orbit.normalToPlane = Vector3.Up(); + +const andromaque = StarSystemHelper.makeGasPlanet(starSystem, andromaqueModel); + +/*const blackHoleModel = new BlackHoleModel(0.5, sunModel); +blackHoleModel.orbit.period = 60 * 60 * 24 * 365.25; +blackHoleModel.orbit.radius = 100 * ares.getRadius(); +const blackHole = starSystem.makeBlackHole(blackHoleModel);*/ + +engine.init(true); + +positionNearObjectBrightSide(starSystemView.scene.getActiveController(), planet, starSystem, 2); + +spaceshipController.toggleWarpDrive(); + +const aresAtmosphere = starSystem.postProcessManager.getAtmosphere(ares); +if (aresAtmosphere) { + aresAtmosphere.atmosphereUniforms.redWaveLength = 500; + aresAtmosphere.atmosphereUniforms.greenWaveLength = 680; + aresAtmosphere.atmosphereUniforms.blueWaveLength = 670; +} else { + console.warn("No atmosphere found for Ares"); +} + +document.addEventListener("keydown", (e) => { + if (engine.isPaused()) return; + + if (e.key === "x") { + let nbVertices = 0; + let nbInstances = 0; + planet.sides.forEach((side) => { + side.executeOnEveryChunk((chunk) => { + nbVertices += Settings.VERTEX_RESOLUTION * Settings.VERTEX_RESOLUTION; + chunk.instancePatches.forEach((patch) => { + nbInstances += patch.getNbInstances(); + }); + }); + }); + console.log("Vertices", nbVertices, "Instances", nbInstances); + } + + if (e.key === "y") { + if (starSystemView.scene.getActiveController() === spaceshipController) { + console.log("disembark"); + + characterController.getTransform().setEnabled(true); + characterController.getTransform().setAbsolutePosition(spaceshipController.getTransform().absolutePosition); + translate(characterController.getTransform(), getForwardDirection(spaceshipController.getTransform()).scale(10)); + + setRotationQuaternion(characterController.getTransform(), getRotationQuaternion(spaceshipController.getTransform()).clone()); + + starSystemView.scene.setActiveController(characterController); + starSystemView.getStarSystem().postProcessManager.rebuild(); + } else if (starSystemView.scene.getActiveController() === characterController) { + console.log("embark"); + + characterController.getTransform().setEnabled(false); + starSystemView.scene.setActiveController(spaceshipController); + starSystemView.getStarSystem().postProcessManager.rebuild(); + } + } +}); + +starSystemView.ui.setEnabled(true); +starSystemView.showUI(); +starSystemView.getSpaceshipControls().enableWarpDrive(); diff --git a/src/ts/blackHoleDemo.ts b/src/ts/blackHoleDemo.ts index e28597cc7..ee65617f7 100644 --- a/src/ts/blackHoleDemo.ts +++ b/src/ts/blackHoleDemo.ts @@ -19,19 +19,9 @@ import "../styles/index.scss"; import { StarSystemController } from "./starSystem/starSystemController"; -import { randRange } from "extended-random"; -import { Settings } from "./settings"; -import { DefaultControls } from "./defaultController/defaultControls"; -import { positionNearObject } from "./utils/positionNearObject"; +import { positionNearObjectBrightSide } from "./utils/positionNearObject"; import { CosmosJourneyer } from "./cosmosJourneyer"; -import { ShipControls } from "./spaceship/shipControls"; -import { getRotationQuaternion, setRotationQuaternion } from "./uberCore/transforms/basicTransform"; -import { parsePercentageFrom01, parseSpeed } from "./utils/parseToStrings"; import { StarSystemHelper } from "./starSystem/starSystemHelper"; -import { Mouse } from "./inputs/mouse"; -import { Keyboard } from "./inputs/keyboard"; -import { Gamepad } from "./inputs/gamepad"; -import { Vector3 } from "@babylonjs/core/Maths/math.vector"; import { SystemSeed } from "./utils/systemSeed"; const engine = new CosmosJourneyer(); @@ -42,45 +32,7 @@ const starSystemView = engine.getStarSystemView(); const scene = starSystemView.scene; -const mouse = new Mouse(engine.canvas, 100); -const keyboard = new Keyboard(); -const gamepad = new Gamepad(); - -const player = new DefaultControls(scene); -player.speed = 0.2 * Settings.EARTH_RADIUS; -player.getActiveCamera().maxZ = Settings.EARTH_RADIUS * 100000; -player.addInput(keyboard); -player.addInput(gamepad); - -const spaceshipController = new ShipControls(scene); -spaceshipController.getActiveCamera().maxZ = Settings.EARTH_RADIUS * 100000; -spaceshipController.addInput(keyboard); -spaceshipController.addInput(mouse); -spaceshipController.addInput(gamepad); - -scene.setActiveController(spaceshipController); - -engine.registerStarSystemUpdateCallback(() => { - if (scene.getActiveController() != spaceshipController) return; - - const shipPosition = spaceshipController.getTransform().getAbsolutePosition(); - const nearestBody = starSystemView.getStarSystem().getNearestOrbitalObject(); - const distance = nearestBody.getTransform().getAbsolutePosition().subtract(shipPosition).length(); - const radius = nearestBody.getBoundingRadius(); - spaceshipController.registerClosestObject(distance, radius); - - const warpDrive = spaceshipController.getWarpDrive(); - const shipInternalThrottle = warpDrive.getInternalThrottle(); - const shipTargetThrottle = warpDrive.getTargetThrottle(); - - const throttleString = warpDrive.isEnabled() - ? `${parsePercentageFrom01(shipInternalThrottle)}/${parsePercentageFrom01(shipTargetThrottle)}` - : `${parsePercentageFrom01(spaceshipController.getThrottle())}/100%`; - - (document.querySelector("#speedometer") as HTMLElement).innerHTML = `${throttleString} | ${parseSpeed(spaceshipController.getSpeed())}`; -}); - -const starSystemSeed = new SystemSeed(Vector3.Zero(), 0); +const starSystemSeed = new SystemSeed(0, 0, 0, 0); const starSystem = new StarSystemController(starSystemSeed, scene); starSystemView.setStarSystem(starSystem, false); @@ -90,27 +42,10 @@ BH.model.orbit.radius = 0; const planet = StarSystemHelper.makeTelluricPlanet(starSystem); planet.model.orbit.radius = 45 * planet.getRadius(); -document.addEventListener("keydown", (e) => { - if (engine.isPaused()) return; - if (e.key === "g") { - if (scene.getActiveController() === spaceshipController) { - scene.setActiveController(player); - setRotationQuaternion(player.getTransform(), getRotationQuaternion(spaceshipController.getTransform()).clone()); - starSystemView.getStarSystem().postProcessManager.rebuild(); - - spaceshipController.setEnabled(false, engine.getHavokPlugin()); - } else { - scene.setActiveController(spaceshipController); - setRotationQuaternion(spaceshipController.getTransform(), getRotationQuaternion(player.getTransform()).clone()); - starSystemView.getStarSystem().postProcessManager.rebuild(); - - spaceshipController.setEnabled(true, engine.getHavokPlugin()); - } - } -}); - -engine.init(); +engine.init(true); -positionNearObject(scene.getActiveController(), BH, starSystem, 20); +positionNearObjectBrightSide(scene.getActiveController(), BH, starSystem, 20); -engine.toggleStarMap(); +starSystemView.ui.setEnabled(true); +starSystemView.showUI(); +starSystemView.getSpaceshipControls().enableWarpDrive(); diff --git a/src/ts/cosmosJourneyer.ts b/src/ts/cosmosJourneyer.ts index 625d35b1d..6fcd2feac 100644 --- a/src/ts/cosmosJourneyer.ts +++ b/src/ts/cosmosJourneyer.ts @@ -15,7 +15,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -import { Assets } from "./assets"; +import projectInfo from "../../package.json"; + import { StarSystemController } from "./starSystem/starSystemController"; import { Engine } from "@babylonjs/core/Engines/engine"; import { Tools } from "@babylonjs/core/Misc/tools"; @@ -25,16 +26,21 @@ import { StarMap } from "./starmap/starMap"; import { Scene } from "@babylonjs/core/scene"; import "@babylonjs/core/Physics/physicsEngineComponent"; -import { HavokPlugin } from "@babylonjs/core/Physics/v2/Plugins/havokPlugin"; import HavokPhysics from "@babylonjs/havok"; import "@babylonjs/core/Engines/WebGPU/Extensions/"; -import { setMaxLinVel } from "./utils/havok"; import { Observable } from "@babylonjs/core/Misc/observable"; import { PauseMenu } from "./ui/pauseMenu"; import { StarSystemView } from "./starSystem/StarSystemView"; import { EngineFactory } from "@babylonjs/core/Engines/engineFactory"; +import { MainMenu } from "./mainMenu/mainMenu"; import { SystemSeed } from "./utils/systemSeed"; +import { SaveFileData } from "./saveFile/saveFileData"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { Quaternion } from "@babylonjs/core/Maths/math"; +import { setRotationQuaternion } from "./uberCore/transforms/basicTransform"; +import { positionNearObjectBrightSide } from "./utils/positionNearObject"; +import { ShipControls } from "./spaceship/shipControls"; enum EngineState { RUNNING, @@ -52,8 +58,8 @@ export class CosmosJourneyer { readonly canvas: HTMLCanvasElement; private engine: Engine | null = null; - private havokPlugin: HavokPlugin | null = null; + private mainMenu: MainMenu | null = null; private starSystemView: StarSystemView | null = null; private starMap: StarMap | null = null; @@ -69,21 +75,27 @@ export class CosmosJourneyer { this.pauseMenu.onScreenshot.add(() => this.takeScreenshot()); this.pauseMenu.onShare.add(() => { const seed = this.getStarSystemView().getStarSystem().model.seed; - const payload = `starMapX=${seed.starSectorCoordinates.x}&starMapY=${seed.starSectorCoordinates.y}&starMapZ=${seed.starSectorCoordinates.z}&index=${seed.index}`; + const payload = `starMapX=${seed.starSectorX}&starMapY=${seed.starSectorY}&starMapZ=${seed.starSectorZ}&index=${seed.index}`; const url = new URL(`https://barthpaleologue.github.io/CosmosJourneyer/random.html?${payload}`); navigator.clipboard.writeText(url.toString()).then(() => console.log("Copied to clipboard")); }); + this.pauseMenu.onSave.add(() => this.downloadSaveFile()); this.canvas = document.getElementById("renderer") as HTMLCanvasElement; this.canvas.width = window.innerWidth; this.canvas.height = window.innerHeight; window.addEventListener("blur", () => { - if (!this.isPaused()) this.pause(); + if (!this.isPaused() && this.activeScene !== this.mainMenu?.scene) this.pause(); + }); + + window.addEventListener("mouseleave", () => { + if (!this.mainMenu?.isVisible()) this.pause(); }); //TODO: use the keyboard class document.addEventListener("keydown", (e) => { + if (this.mainMenu?.isVisible()) return; if (e.key === "p") this.takeScreenshot(); if (e.key === "v") this.takeVideoCapture(); if (e.key === "m") this.toggleStarMap(); @@ -123,26 +135,41 @@ export class CosmosJourneyer { // Init Havok physics engine const havokInstance = await HavokPhysics(); - this.havokPlugin = new HavokPlugin(true, havokInstance); - setMaxLinVel(this.havokPlugin, 10000, 10000); console.log(`Havok initialized`); // Init starmap view this.starMap = new StarMap(this.engine); this.starMap.onWarpObservable.add((seed: SystemSeed) => { this.getStarSystemView().setStarSystem(new StarSystemController(seed, this.getStarSystemView().scene), true); - this.getStarSystemView().init(); + this.getStarSystemView().initStarSystem(); this.toggleStarMap(); + + const activeControls = this.getStarSystemView().scene.getActiveController(); + if (activeControls instanceof ShipControls) { + activeControls.enableWarpDrive(); + activeControls.thirdPersonCamera.radius = 30; + } }); // Init star system view - this.starSystemView = new StarSystemView(this.engine, this.havokPlugin); + this.starSystemView = new StarSystemView(this.engine, havokInstance); + await this.starSystemView.initAssets(); + + this.mainMenu = new MainMenu(this.starSystemView); + this.mainMenu.onStartObservable.add(() => { + this.getStarMap().setCurrentStarSystem(this.getStarSystemView().getStarSystem().model.seed); + this.getStarSystemView().switchToSpaceshipControls(); + this.getStarSystemView().getSpaceshipControls().enableWarpDrive(); + this.getStarSystemView().showUI(); + this.getStarSystemView().ui.setEnabled(true); + }); - // Init assets used in star system view - await Assets.Init(this.getStarSystemView().scene); + this.mainMenu.onLoadSaveObservable.add((saveData: SaveFileData) => { + this.loadSaveData(saveData); + }); // Starmap is the active scene by default - this.activeScene = this.starMap.scene; + this.activeScene = this.mainMenu.scene; } public pause(): void { @@ -162,8 +189,9 @@ export class CosmosJourneyer { /** * Inits the current star system */ - public init(): void { - this.getStarSystemView().init(); + public init(skipMainMenu = false): void { + if (!skipMainMenu) this.getMainMenu().init(); + this.getStarSystemView().initStarSystem(); this.getEngine().runRenderLoop(() => { if (this.isPaused()) return; @@ -179,6 +207,11 @@ export class CosmosJourneyer { this.getStarSystemView().scene.onBeforeRenderObservable.add(callback); } + public getMainMenu(): MainMenu { + if (this.mainMenu === null) throw new Error("Main menu is null"); + return this.mainMenu; + } + public getStarSystemView(): StarSystemView { if (this.starSystemView === null) throw new Error("Star system view is null"); return this.starSystemView; @@ -196,14 +229,14 @@ export class CosmosJourneyer { public toggleStarMap(): void { if (this.activeScene === this.getStarSystemView().scene) { this.getStarSystemView().unZoom(() => { - this.getStarSystemView().scene.detachControl(); + if (this.activeScene !== null) this.activeScene.detachControl(); this.getStarMap().scene.attachControl(); const starMap = this.getStarMap(); this.activeScene = starMap.scene; starMap.focusOnCurrentSystem(); }); } else { - this.getStarMap().scene.detachControl(); + if (this.activeScene !== null) this.activeScene.detachControl(); this.getStarSystemView().scene.attachControl(); this.activeScene = this.getStarSystemView().scene; this.getStarSystemView().showUI(); @@ -232,16 +265,6 @@ export class CosmosJourneyer { return this.engine; } - /** - * Returns the Havok plugin - * @returns the Havok plugin - * @throws Error if the Havok plugin is null - */ - public getHavokPlugin(): HavokPlugin { - if (this.havokPlugin === null) throw new Error("Havok plugin is null"); - return this.havokPlugin; - } - /** * Takes a screenshot of the current scene. By default, the screenshot is taken at a 4x the resolution of the canvas * @param precision The resolution multiplier of the screenshot @@ -272,4 +295,96 @@ export class CosmosJourneyer { this.videoRecorder.startRecording("planetEngine.webm", Number(prompt("Enter video duration in seconds", "10"))).then(); } } + + /** + * Generates a save file data object from the current star system and the player's position + */ + public generateSaveData(): SaveFileData { + const currentStarSystem = this.getStarSystemView().getStarSystem(); + const seed = currentStarSystem.model.seed; + + // Finding the index of the nearest orbital object + const nearestOrbitalObject = currentStarSystem.getNearestOrbitalObject(); + const nearestOrbitalObjectIndex = currentStarSystem.getOrbitalObjects().indexOf(nearestOrbitalObject); + if (nearestOrbitalObjectIndex === -1) throw new Error("Nearest orbital object not found"); + + // Finding the position of the player in the nearest orbital object's frame of reference + const currentWorldPosition = this.getStarSystemView().scene.getActiveController().getTransform().getAbsolutePosition(); + const nearestOrbitalObjectInverseWorld = nearestOrbitalObject.getTransform().getWorldMatrix().clone().invert(); + const currentLocalPosition = Vector3.TransformCoordinates(currentWorldPosition, nearestOrbitalObjectInverseWorld); + + // Finding the rotation of the player in the nearest orbital object's frame of reference + const currentWorldRotation = this.getStarSystemView().scene.getActiveController().getTransform().absoluteRotationQuaternion; + const nearestOrbitalObjectInverseRotation = nearestOrbitalObject.getTransform().absoluteRotationQuaternion.clone().invert(); + const currentLocalRotation = currentWorldRotation.multiply(nearestOrbitalObjectInverseRotation); + + return { + version: projectInfo.version, + starSystem: { + starSectorX: seed.starSectorX, + starSectorY: seed.starSectorY, + starSectorZ: seed.starSectorZ, + starSectorIndex: seed.index + }, + nearestOrbitalObjectIndex: nearestOrbitalObjectIndex, + positionX: currentLocalPosition.x, + positionY: currentLocalPosition.y, + positionZ: currentLocalPosition.z, + rotationQuaternionX: currentLocalRotation.x, + rotationQuaternionY: currentLocalRotation.y, + rotationQuaternionZ: currentLocalRotation.z, + rotationQuaternionW: currentLocalRotation.w + }; + } + + /** + * Generates save file data and downloads it as a json file + */ + public downloadSaveFile(): void { + const saveData = this.generateSaveData(); + const blob = new Blob([JSON.stringify(saveData)], { type: "application/json" }); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = "save.json"; + link.click(); + } + + /** + * Loads a save file and apply it. This will generate the requested star system and position the player at the requested position around the requested orbital object. + * @param saveData The save file data to load + */ + public loadSaveData(saveData: SaveFileData): void { + const seed = new SystemSeed(saveData.starSystem.starSectorX, saveData.starSystem.starSectorY, saveData.starSystem.starSectorZ, saveData.starSystem.starSectorIndex); + + this.getStarMap().setCurrentStarSystem(seed); + this.getStarSystemView().setStarSystem(new StarSystemController(seed, this.getStarSystemView().scene), true); + + this.getStarSystemView().onInitStarSystem.addOnce(() => { + this.getStarSystemView().switchToSpaceshipControls(); + + this.getStarSystemView().ui.setEnabled(true); + this.getStarSystemView().showUI(); + + const playerTransform = this.getStarSystemView().scene.getActiveController().getTransform(); + + const nearestOrbitalObject = this.getStarSystemView().getStarSystem().getOrbitalObjects()[saveData.nearestOrbitalObjectIndex]; + const nearestOrbitalObjectWorld = nearestOrbitalObject.getTransform().getWorldMatrix(); + const currentLocalPosition = new Vector3(saveData.positionX, saveData.positionY, saveData.positionZ); + const currentWorldPosition = Vector3.TransformCoordinates(currentLocalPosition, nearestOrbitalObjectWorld); + playerTransform.setAbsolutePosition(currentWorldPosition); + + const nearestOrbitalObjectWorldRotation = nearestOrbitalObject.getTransform().absoluteRotationQuaternion; + const currentLocalRotationQuaternion = new Quaternion( + saveData.rotationQuaternionX, + saveData.rotationQuaternionY, + saveData.rotationQuaternionZ, + saveData.rotationQuaternionW + ); + const currentWorldRotationQuaternion = currentLocalRotationQuaternion.multiply(nearestOrbitalObjectWorldRotation); + setRotationQuaternion(playerTransform, currentWorldRotationQuaternion); + }); + + this.getStarSystemView().initStarSystem(); + } } diff --git a/src/ts/index.ts b/src/ts/index.ts index cfb732b96..fee5855e6 100644 --- a/src/ts/index.ts +++ b/src/ts/index.ts @@ -17,31 +17,9 @@ import "../styles/index.scss"; -import { StarSystemController } from "./starSystem/starSystemController"; - -import { Settings } from "./settings"; -import { Assets } from "./assets"; -import { DefaultControls } from "./defaultController/defaultControls"; -import { positionNearObject } from "./utils/positionNearObject"; import { CosmosJourneyer } from "./cosmosJourneyer"; -import { Vector3 } from "@babylonjs/core/Maths/math.vector"; -import { Color3 } from "@babylonjs/core/Maths/math.color"; -import { ShipControls } from "./spaceship/shipControls"; -import { PostProcessType } from "./postProcesses/postProcessTypes"; -import { TelluricPlanetModel } from "./planets/telluricPlanet/telluricPlanetModel"; -import { GasPlanetModel } from "./planets/gasPlanet/gasPlanetModel"; import { getForwardDirection, getRotationQuaternion, setRotationQuaternion, translate } from "./uberCore/transforms/basicTransform"; -import { parsePercentageFrom01, parseSpeed } from "./utils/parseToStrings"; - -import { StarSystemHelper } from "./starSystem/starSystemHelper"; -import { Mouse } from "./inputs/mouse"; -import { Keyboard } from "./inputs/keyboard"; -import { StarModel } from "./stellarObjects/star/starModel"; -import { RingsUniforms } from "./postProcesses/rings/ringsUniform"; -import { getMoonSeed } from "./planets/common"; - -import { Gamepad } from "./inputs/gamepad"; -import { CharacterControls } from "./spacelegs/characterControls"; +import { StarSystemController } from "./starSystem/starSystemController"; import { SystemSeed } from "./utils/systemSeed"; const engine = new CosmosJourneyer(); @@ -50,200 +28,18 @@ await engine.setup(); const starSystemView = engine.getStarSystemView(); -const mouse = new Mouse(engine.canvas, 100); -const keyboard = new Keyboard(); -const gamepad = new Gamepad(); - -const maxZ = Settings.EARTH_RADIUS * 1e5; - -const defaultController = new DefaultControls(starSystemView.scene); -defaultController.speed = 0.2 * Settings.EARTH_RADIUS; -defaultController.getActiveCamera().maxZ = maxZ; -defaultController.addInput(keyboard); -defaultController.addInput(gamepad); - -const spaceshipController = new ShipControls(starSystemView.scene); -spaceshipController.getActiveCamera().maxZ = maxZ; -spaceshipController.addInput(keyboard); -spaceshipController.addInput(gamepad); -spaceshipController.addInput(mouse); - -const characterController = new CharacterControls(starSystemView.scene); -characterController.getTransform().setEnabled(false); -characterController.getActiveCamera().maxZ = maxZ; -characterController.addInput(keyboard); -characterController.addInput(gamepad); - -// const physicsViewer = new PhysicsViewer(); -// physicsViewer.showBody(spaceshipController.aggregate.body); - -mouse.onMouseLeaveObservable.add(() => { - if (starSystemView.scene.getActiveController() === spaceshipController) engine.pause(); -}); - -starSystemView.scene.setActiveController(spaceshipController); - -engine.registerStarSystemUpdateCallback(() => { - if (starSystemView.scene.getActiveController() != spaceshipController) return; - - const shipPosition = spaceshipController.getTransform().getAbsolutePosition(); - const nearestBody = starSystemView.getStarSystem().getNearestOrbitalObject(); - const distance = nearestBody.getTransform().getAbsolutePosition().subtract(shipPosition).length(); - const radius = nearestBody.getBoundingRadius(); - spaceshipController.registerClosestObject(distance, radius); - - const warpDrive = spaceshipController.getWarpDrive(); - const shipInternalThrottle = warpDrive.getInternalThrottle(); - const shipTargetThrottle = warpDrive.getTargetThrottle(); - - const throttleString = warpDrive.isEnabled() - ? `${parsePercentageFrom01(shipInternalThrottle)}/${parsePercentageFrom01(shipTargetThrottle)}` - : `${parsePercentageFrom01(spaceshipController.getThrottle())}/100%`; - - (document.querySelector("#speedometer") as HTMLElement).innerHTML = `${throttleString} | ${parseSpeed(spaceshipController.getSpeed())}`; - - characterController.setClosestWalkableObject(nearestBody); - spaceshipController.setClosestWalkableObject(nearestBody); -}); - -engine.getStarMap().onWarpObservable.add(() => { - spaceshipController.thirdPersonCamera.radius = 30; -}); - -engine.onToggleStarMapObservable.add((isStarMapOpen) => { - if (!isStarMapOpen) spaceshipController.thirdPersonCamera.radius = 30; -}); - -console.log(`Time is going ${Settings.TIME_MULTIPLIER} time${Settings.TIME_MULTIPLIER > 1 ? "s" : ""} faster than in reality`); - -const starSystemSeed = new SystemSeed(Vector3.Zero(), 0); -const starSystem = new StarSystemController(starSystemSeed, starSystemView.scene); -starSystem.model.setName("Alpha Testis"); - -engine.getStarSystemView().setStarSystem(starSystem, false); - -const sunModel = new StarModel(0.51); -const sun = StarSystemHelper.makeStar(starSystem, sunModel); -sun.model.orbit.period = 60 * 60 * 24; - -/*const secundaModel = new StarModel(-672446, sunModel); -secundaModel.orbit.radius = 30 * sunModel.radius; -secundaModel.orbit.period = 60 * 60; -const secunda = StarSystemHelper.makeStar(starSystem, secundaModel); - -const terminaModel = new StarModel(756263, sunModel); -terminaModel.orbit.radius = 50 * sunModel.radius; -terminaModel.orbit.period = 60 * 60; -const termina = StarSystemHelper.makeStar(starSystem, terminaModel);*/ - -const planetModel = new TelluricPlanetModel(0.4233609183800225, sunModel); -planetModel.physicalProperties.minTemperature = -55; -planetModel.physicalProperties.maxTemperature = 30; - -planetModel.orbit.period = 60 * 60 * 24 * 365.25; -planetModel.orbit.radius = 4000 * planetModel.radius; -planetModel.orbit.normalToPlane = Vector3.Up(); - -const planet = StarSystemHelper.makeTelluricPlanet(starSystem, planetModel); -planet.model.ringsUniforms = new RingsUniforms(planet.model.rng); -planet.postProcesses.push(PostProcessType.RING); - -//const spacestation = new SpaceStation(starSystemView.scene, planet); -//starSystemView.getStarSystem().addSpaceStation(spacestation); - -const moonModel = new TelluricPlanetModel(getMoonSeed(planetModel, 0), planetModel); -moonModel.physicalProperties.mass = 2; -moonModel.physicalProperties.rotationPeriod = 7 * 60 * 60; -moonModel.physicalProperties.minTemperature = -180; -moonModel.physicalProperties.maxTemperature = 200; -moonModel.physicalProperties.waterAmount = 0.9; - -moonModel.orbit.period = moonModel.physicalProperties.rotationPeriod; -moonModel.orbit.radius = 8 * planet.getRadius(); -moonModel.orbit.normalToPlane = Vector3.Up(); - -const moon = StarSystemHelper.makeSatellite(starSystem, planet, moonModel); - -moon.material.colorSettings.plainColor.copyFromFloats(0.67, 0.67, 0.67); -moon.material.colorSettings.desertColor.copyFrom(new Color3(116, 134, 121).scale(1 / 255)); -moon.material.colorSettings.steepColor.copyFrom(new Color3(92, 92, 92).scale(1 / 255)); - -moon.material.setTexture("plainNormalMap", Assets.DirtNormalMap); -moon.material.setTexture("bottomNormalMap", Assets.DirtNormalMap); -moon.material.updateConstants(); - -const aresModel = new TelluricPlanetModel(0.3725, sunModel); -aresModel.physicalProperties.mass = 7; -aresModel.physicalProperties.rotationPeriod = (24 * 60 * 60) / 30; -aresModel.physicalProperties.minTemperature = -30; -aresModel.physicalProperties.maxTemperature = 20; -aresModel.physicalProperties.pressure = 0.5; -aresModel.physicalProperties.waterAmount = 0.2; -aresModel.physicalProperties.oceanLevel = 0; - -aresModel.orbit.period = 60 * 60 * 24 * 365.24; -aresModel.orbit.radius = 4020 * planet.getRadius(); -aresModel.orbit.normalToPlane = Vector3.Up(); - -//aresModel.terrainSettings.continents_fragmentation = 0.0; -//aresModel.terrainSettings.continent_base_height = 10e3; -//aresModel.terrainSettings.max_mountain_height = 20e3; - -const ares = StarSystemHelper.makeTelluricPlanet(starSystem, aresModel); -ares.postProcesses.splice(ares.postProcesses.indexOf(PostProcessType.OCEAN), 1); -ares.postProcesses.splice(ares.postProcesses.indexOf(PostProcessType.CLOUDS), 1); - -ares.material.colorSettings.plainColor.copyFromFloats(139 / 255, 59 / 255, 24 / 255); -ares.material.colorSettings.desertColor.copyFromFloats(178 / 255, 107 / 255, 42 / 255); -ares.material.colorSettings.beachColor.copyFrom(ares.material.colorSettings.plainColor); -ares.material.colorSettings.bottomColor.copyFrom(ares.material.colorSettings.plainColor.scale(0.9)); - -ares.material.updateConstants(); - -const andromaqueModel = new GasPlanetModel(0.28711440474126226, sunModel); -andromaqueModel.orbit.period = 60 * 60 * 24 * 365.25; -andromaqueModel.orbit.radius = 4300 * ares.getRadius(); -andromaqueModel.orbit.normalToPlane = Vector3.Up(); - -const andromaque = StarSystemHelper.makeGasPlanet(starSystem, andromaqueModel); - -/*const blackHoleModel = new BlackHoleModel(0.5, sunModel); -blackHoleModel.orbit.period = 60 * 60 * 24 * 365.25; -blackHoleModel.orbit.radius = 100 * ares.getRadius(); -const blackHole = starSystem.makeBlackHole(blackHoleModel);*/ +//const starSystem = new StarSystemController(new SystemSeed(0, 0, 0, 0), starSystemView.scene); +//starSystemView.setStarSystem(starSystem, true); engine.init(); -positionNearObject(starSystemView.scene.getActiveController(), planet, starSystem, 2); - -spaceshipController.toggleWarpDrive(); - -const aresAtmosphere = starSystem.postProcessManager.getAtmosphere(ares); -if (aresAtmosphere) { - aresAtmosphere.atmosphereUniforms.redWaveLength = 500; - aresAtmosphere.atmosphereUniforms.greenWaveLength = 680; - aresAtmosphere.atmosphereUniforms.blueWaveLength = 670; -} else { - console.warn("No atmosphere found for Ares"); -} +const spaceshipController = starSystemView.getSpaceshipControls(); +const characterController = starSystemView.getCharacterControls(); +const defaultController = starSystemView.getDefaultControls(); document.addEventListener("keydown", (e) => { if (engine.isPaused()) return; - if (e.key === "x") { - let nbVertices = 0; - let nbInstances = 0; - planet.sides.forEach((side) => { - side.executeOnEveryChunk((chunk) => { - nbVertices += Settings.VERTEX_RESOLUTION * Settings.VERTEX_RESOLUTION; - chunk.instancePatches.forEach((patch) => { - nbInstances += patch.getNbInstances(); - }); - }); - }); - console.log("Vertices", nbVertices, "Instances", nbInstances); - } - if (e.key === "y") { if (starSystemView.scene.getActiveController() === spaceshipController) { console.log("disembark"); @@ -264,29 +60,4 @@ document.addEventListener("keydown", (e) => { starSystemView.getStarSystem().postProcessManager.rebuild(); } } - - if (e.key === "g") { - if (starSystemView.scene.getActiveController() === spaceshipController) { - starSystemView.scene.setActiveController(defaultController); - setRotationQuaternion(defaultController.getTransform(), getRotationQuaternion(spaceshipController.getTransform()).clone()); - starSystemView.getStarSystem().postProcessManager.rebuild(); - - spaceshipController.setEnabled(false, engine.getHavokPlugin()); - } else if (starSystemView.scene.getActiveController() === defaultController) { - characterController.getTransform().setEnabled(true); - characterController.getTransform().setAbsolutePosition(defaultController.getTransform().absolutePosition); - starSystemView.scene.setActiveController(characterController); - setRotationQuaternion(characterController.getTransform(), getRotationQuaternion(defaultController.getTransform()).clone()); - starSystemView.getStarSystem().postProcessManager.rebuild(); - - spaceshipController.setEnabled(false, engine.getHavokPlugin()); - } else if (starSystemView.scene.getActiveController() === characterController) { - characterController.getTransform().setEnabled(false); - starSystemView.scene.setActiveController(spaceshipController); - setRotationQuaternion(spaceshipController.getTransform(), getRotationQuaternion(defaultController.getTransform()).clone()); - starSystemView.getStarSystem().postProcessManager.rebuild(); - - spaceshipController.setEnabled(true, engine.getHavokPlugin()); - } - } }); diff --git a/src/ts/mainMenu/mainMenu.ts b/src/ts/mainMenu/mainMenu.ts new file mode 100644 index 000000000..1ef4eb21a --- /dev/null +++ b/src/ts/mainMenu/mainMenu.ts @@ -0,0 +1,323 @@ +import { UberScene } from "../uberCore/uberScene"; +import { DefaultControls } from "../defaultController/defaultControls"; +import { StarSystemView } from "../starSystem/StarSystemView"; +import { StarSystemController } from "../starSystem/starSystemController"; +import { positionNearObjectWithStarVisible } from "../utils/positionNearObject"; +import { BODY_TYPE } from "../model/common"; +import { EditorVisibility } from "../ui/bodyEditor/bodyEditor"; +import mainMenuHTML from "../../html/mainMenu.html"; +import { getForwardDirection } from "../uberCore/transforms/basicTransform"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { TransformRotationAnimation } from "../uberCore/transforms/animations/rotation"; +import { TransformTranslationAnimation } from "../uberCore/transforms/animations/translation"; +import { Observable } from "@babylonjs/core/Misc/observable"; +import { SystemSeed } from "../utils/systemSeed"; +import { parseSaveFileData, SaveFileData } from "../saveFile/saveFileData"; + +export class MainMenu { + readonly scene: UberScene; + readonly controls: DefaultControls; + + readonly starSystemView: StarSystemView; + readonly starSystemController: StarSystemController; + + readonly onStartObservable = new Observable(); + readonly onLoadSaveObservable = new Observable(); + readonly onContributeObservable = new Observable(); + readonly onCreditsObservable = new Observable(); + readonly onAboutObservable = new Observable(); + + private htmlRoot: HTMLElement | null = null; + private title: HTMLElement | null = null; + + private activeRightPanel: HTMLElement | null = null; + private loadSavePanel: HTMLElement | null = null; + private contributePanel: HTMLElement | null = null; + private creditsPanel: HTMLElement | null = null; + private aboutPanel: HTMLElement | null = null; + + constructor(starSystemView: StarSystemView) { + this.starSystemView = starSystemView; + + this.scene = this.starSystemView.scene; + this.controls = this.starSystemView.getDefaultControls(); + + const allowedSeeds = [ + new SystemSeed(-600, 955, -68, 0), + new SystemSeed(576, -192, 480, 0), + new SystemSeed(-760, -856, 60, 0), + new SystemSeed(-238, 254, -675, 0), + new SystemSeed(-312, 314, 736, 0), + new SystemSeed(43 - 503, -427, 532, 0), + new SystemSeed(-866, 71, -294, 0), + new SystemSeed(-249, 706, 631, 0), + new SystemSeed(-433, 945, -693, 0), + new SystemSeed(-430, -767, -670, 0), + new SystemSeed(61, 376, -389, 0), + new SystemSeed(-499, -114, 377, 0), + new SystemSeed(-596, 339, -571, 0), + new SystemSeed(-319, 253, 30, 0), + new SystemSeed(709, 570, 285, 0), + new SystemSeed(-939, -753, -401, 0), + new SystemSeed(-516, -140, -2, 0), + new SystemSeed(984, -155, 179, 0), + new SystemSeed(728, 691, -883, 0), + new SystemSeed(-673, -545, 753, 0), + new SystemSeed(-218, 213, 765, 0), + new SystemSeed(-47, 97, -20, 0), + new SystemSeed(817, 750, -983, 0) + ]; + + /*const randomSeed = new SystemSeed( + Math.trunc((Math.random() * 2 - 1) * 1000), + Math.trunc((Math.random() * 2 - 1) * 1000), + Math.trunc((Math.random() * 2 - 1) * 1000), + 0 + );*/ + + const seed = allowedSeeds[Math.floor(Math.random() * allowedSeeds.length)]; + console.log(seed.starSectorX + ", " + seed.starSectorY + ", " + seed.starSectorZ + ", " + seed.index); + this.starSystemController = new StarSystemController(seed, this.scene); + } + + init() { + this.starSystemView.setStarSystem(this.starSystemController, true); + + this.starSystemView.onInitStarSystem.addOnce(() => { + this.starSystemView.switchToDefaultControls(); + const nbRadius = this.starSystemController.model.getBodyTypeOfStar(0) === BODY_TYPE.BLACK_HOLE ? 8 : 2; + positionNearObjectWithStarVisible( + this.controls, + this.starSystemController.planets.length > 0 ? this.starSystemController.getBodies()[1] : this.starSystemController.stellarObjects[0], + this.starSystemController, + nbRadius + ); + }); + + this.starSystemView.ui.setEnabled(false); + + this.starSystemView.bodyEditor.setVisibility(EditorVisibility.HIDDEN); + + document.body.insertAdjacentHTML("beforeend", mainMenuHTML); + + const htmlRoot = document.getElementById("mainMenu"); + if (htmlRoot === null) throw new Error("#mainMenu does not exist!"); + this.htmlRoot = htmlRoot; + + const title = document.querySelector("#mainMenu h1"); + if (title === null) throw new Error("#mainMenu h1 does not exist!"); + this.title = title as HTMLElement; + + document.getElementById("startButton")?.addEventListener("click", () => { + this.startAnimation(() => this.onStartObservable.notifyObservers()); + }); + + const loadSaveButton = document.getElementById("loadSaveButton"); + if (loadSaveButton === null) throw new Error("#loadSaveButton does not exist!"); + + const loadSavePanel = document.getElementById("loadSavePanel"); + if (loadSavePanel === null) throw new Error("#loadSavePanel does not exist!"); + this.loadSavePanel = loadSavePanel; + + const dropFileZone = document.getElementById("dropFileZone"); + if (dropFileZone === null) throw new Error("#dropFileZone does not exist!"); + + dropFileZone.addEventListener("dragover", (event) => { + event.preventDefault(); + event.stopPropagation(); + dropFileZone.classList.add("dragover"); + dropFileZone.classList.remove("invalid"); + if (event.dataTransfer === null) throw new Error("event.dataTransfer is null"); + event.dataTransfer.dropEffect = "copy"; + }); + + dropFileZone.addEventListener("dragleave", (event) => { + event.preventDefault(); + event.stopPropagation(); + dropFileZone.classList.remove("dragover"); + }); + + dropFileZone.addEventListener("drop", (event) => { + event.preventDefault(); + event.stopPropagation(); + dropFileZone.classList.remove("dragover"); + + if (event.dataTransfer === null) throw new Error("event.dataTransfer is null"); + if (event.dataTransfer.files.length === 0) throw new Error("event.dataTransfer.files is empty"); + + const file = event.dataTransfer.files[0]; + if (file.type !== "application/json") { + dropFileZone.classList.add("invalid"); + alert("File is not a JSON file"); + return; + } + + const reader = new FileReader(); + reader.onload = (event) => { + if (event.target === null) throw new Error("event.target is null"); + const data = event.target.result as string; + try { + const saveFileData = parseSaveFileData(data); + this.startAnimation(() => this.onLoadSaveObservable.notifyObservers(saveFileData)); + } catch (e) { + dropFileZone.classList.add("invalid"); + alert("Invalid save file"); + } + }; + reader.readAsText(file); + }); + + dropFileZone.addEventListener("click", () => { + const fileInput = document.createElement("input"); + fileInput.type = "file"; + fileInput.accept = "application/json"; + fileInput.onchange = () => { + if (fileInput.files === null) throw new Error("fileInput.files is null"); + if (fileInput.files.length === 0) throw new Error("fileInput.files is empty"); + const file = fileInput.files[0]; + const reader = new FileReader(); + reader.onload = (event) => { + if (event.target === null) throw new Error("event.target is null"); + const data = event.target.result as string; + const saveFileData = parseSaveFileData(data); + this.startAnimation(() => this.onLoadSaveObservable.notifyObservers(saveFileData)); + }; + reader.readAsText(file); + }; + fileInput.click(); + }); + + loadSaveButton.addEventListener("click", () => { + this.toggleActivePanel(loadSavePanel); + }); + + const contributeButton = document.getElementById("contributeButton"); + if (contributeButton === null) throw new Error("#contributeButton does not exist!"); + + const contributePanel = document.getElementById("contribute"); + if (contributePanel === null) throw new Error("#contribute does not exist!"); + this.contributePanel = contributePanel; + + contributeButton.addEventListener("click", () => { + this.toggleActivePanel(contributePanel); + this.onContributeObservable.notifyObservers(); + }); + + const creditsButton = document.getElementById("creditsButton"); + if (creditsButton === null) throw new Error("#creditsButton does not exist!"); + const creditsPanel = document.getElementById("credits"); + if (creditsPanel === null) throw new Error("#credits does not exist!"); + this.creditsPanel = creditsPanel; + + creditsButton.addEventListener("click", () => { + this.toggleActivePanel(creditsPanel); + this.onCreditsObservable.notifyObservers(); + }); + + const aboutButton = document.getElementById("aboutButton"); + if (aboutButton === null) throw new Error("#aboutButton does not exist!"); + const aboutPanel = document.getElementById("about"); + if (aboutPanel === null) throw new Error("#about does not exist!"); + this.aboutPanel = aboutPanel; + + aboutButton.addEventListener("click", () => { + this.toggleActivePanel(aboutPanel); + this.onAboutObservable.notifyObservers(); + }); + } + + private startAnimation(onAnimationFinished: () => void) { + this.hideActivePanel(); + + const currentForward = getForwardDirection(this.controls.getTransform()); + + const planet = this.starSystemController.planets[0]; + const newForward = planet.getTransform().getAbsolutePosition().subtract(this.controls.getTransform().getAbsolutePosition()).normalize(); + const axis = Vector3.Cross(currentForward, newForward); + const angle = Vector3.GetAngleBetweenVectors(currentForward, newForward, axis); + const duration = 2; + + const rotationAnimation = new TransformRotationAnimation(this.controls.getTransform(), axis, angle, duration); + const translationAnimation = new TransformTranslationAnimation( + this.controls.getTransform(), + this.controls + .getTransform() + .getAbsolutePosition() + .add(newForward.scale(-planet.model.radius * 2)), + duration + ); + + if (this.title === null) throw new Error("Title is null"); + + this.title.animate( + [ + { + marginTop: this.title.style.marginTop, + opacity: 1 + }, + { + marginTop: "30vh", + opacity: 0 + } + ], + { + duration: duration * 1000, + easing: "ease-in-out", + fill: "forwards" + } + ); + + const animationCallback = () => { + const deltaTime = this.scene.getEngine().getDeltaTime() / 1000; + + if (!translationAnimation.isFinished()) translationAnimation.update(deltaTime); + if (!rotationAnimation.isFinished()) rotationAnimation.update(deltaTime); + else { + this.scene.onBeforePhysicsObservable.removeCallback(animationCallback); + if (this.htmlRoot === null) throw new Error("MainMenu is null"); + this.htmlRoot.style.display = "none"; + onAnimationFinished(); + } + + this.controls.getActiveCamera().getViewMatrix(); + + this.starSystemController.applyFloatingOrigin(); + this.starSystemController.updateShaders(0.0); + }; + + this.scene.onBeforePhysicsObservable.add(animationCallback); + + this.hideMenu(); + } + + private toggleActivePanel(newPanel: HTMLElement) { + if (this.activeRightPanel === newPanel) { + this.hideActivePanel(); + return; + } + + if (this.activeRightPanel !== null) { + this.hideActivePanel(); + } + + this.activeRightPanel = newPanel; + newPanel.classList.add("visible"); + } + + private hideActivePanel() { + if (this.activeRightPanel !== null) { + this.activeRightPanel.classList.remove("visible"); + this.activeRightPanel = null; + } + } + + private hideMenu() { + const menuItems = document.getElementById("menuItems"); + if (menuItems === null) throw new Error("#menuItems does not exist!"); + menuItems.style.left = "-20%"; + } + + public isVisible() { + return this.htmlRoot !== null && this.htmlRoot.style.display !== "none"; + } +} diff --git a/src/ts/model/common.ts b/src/ts/model/common.ts index 1ce9e182f..f9f84c02a 100644 --- a/src/ts/model/common.ts +++ b/src/ts/model/common.ts @@ -42,5 +42,6 @@ export enum BODY_TYPE { TELLURIC_PLANET, GAS_PLANET, MANDELBULB, - BLACK_HOLE + BLACK_HOLE, + NEUTRON_STAR } diff --git a/src/ts/randomizer.ts b/src/ts/randomizer.ts index 85fdf699a..e9cc3f8e4 100644 --- a/src/ts/randomizer.ts +++ b/src/ts/randomizer.ts @@ -19,21 +19,10 @@ import "../styles/index.scss"; import { StarSystemController } from "./starSystem/starSystemController"; -import { centeredRand, randRange } from "extended-random"; -import { Settings } from "./settings"; -import { DefaultControls } from "./defaultController/defaultControls"; -import { positionNearObject } from "./utils/positionNearObject"; +import { positionNearObjectBrightSide } from "./utils/positionNearObject"; import { CosmosJourneyer } from "./cosmosJourneyer"; import { BODY_TYPE } from "./model/common"; -import { ShipControls } from "./spaceship/shipControls"; -import { EditorVisibility } from "./ui/bodyEditor/bodyEditor"; -import { getRotationQuaternion, setRotationQuaternion } from "./uberCore/transforms/basicTransform"; -import { parsePercentageFrom01, parseSpeed } from "./utils/parseToStrings"; -import { Mouse } from "./inputs/mouse"; -import { Keyboard } from "./inputs/keyboard"; -import { Gamepad } from "./inputs/gamepad"; import { SystemSeed } from "./utils/systemSeed"; -import { Vector3 } from "@babylonjs/core/Maths/math.vector"; const engine = new CosmosJourneyer(); @@ -43,52 +32,6 @@ const starSystemView = engine.getStarSystemView(); const scene = starSystemView.scene; -const mouse = new Mouse(engine.canvas, 100); -const keyboard = new Keyboard(); -const gamepad = new Gamepad(); - -const player = new DefaultControls(scene); -player.speed = 0.2 * Settings.EARTH_RADIUS; -player.getActiveCamera().maxZ = Settings.EARTH_RADIUS * 100000; -player.addInput(keyboard); -player.addInput(gamepad); - -const spaceshipController = new ShipControls(scene); -spaceshipController.getActiveCamera().maxZ = Settings.EARTH_RADIUS * 100000; -spaceshipController.addInput(keyboard); -spaceshipController.addInput(mouse); -spaceshipController.addInput(gamepad); - -scene.setActiveController(spaceshipController); - -engine.registerStarSystemUpdateCallback(() => { - if (scene.getActiveController() != spaceshipController) return; - - const shipPosition = spaceshipController.getTransform().getAbsolutePosition(); - const nearestBody = starSystemView.getStarSystem().getNearestOrbitalObject(); - const distance = nearestBody.getTransform().getAbsolutePosition().subtract(shipPosition).length(); - const radius = nearestBody.getBoundingRadius(); - spaceshipController.registerClosestObject(distance, radius); - - const warpDrive = spaceshipController.getWarpDrive(); - const shipInternalThrottle = warpDrive.getInternalThrottle(); - const shipTargetThrottle = warpDrive.getTargetThrottle(); - - const throttleString = warpDrive.isEnabled() - ? `${parsePercentageFrom01(shipInternalThrottle)}/${parsePercentageFrom01(shipTargetThrottle)}` - : `${parsePercentageFrom01(spaceshipController.getThrottle())}/100%`; - - (document.querySelector("#speedometer") as HTMLElement).innerHTML = `${throttleString} | ${parseSpeed(spaceshipController.getSpeed())}`; -}); - -engine.getStarMap().onWarpObservable.add(() => { - spaceshipController.thirdPersonCamera.radius = 30; -}); - -engine.onToggleStarMapObservable.add((isStarMapOpen) => { - if (!isStarMapOpen) spaceshipController.thirdPersonCamera.radius = 30; -}); - //check if url contains a seed const urlParams = new URLSearchParams(window.location.search); const urlStarMapX = urlParams.get("starMapX"); @@ -103,38 +46,19 @@ const starMapZ = urlStarMapZ !== null ? Number(urlStarMapZ) : Math.trunc((Math.r const index = urlIndex !== null ? Number(urlIndex) : 0; const bodyIndex = urlBodyIndex !== null ? Number(urlBodyIndex) : 0; -const seed = new SystemSeed(new Vector3(starMapX, starMapY, starMapZ), index); +const seed = new SystemSeed(starMapX, starMapY, starMapZ, index); const starSystem = new StarSystemController(seed, scene); starSystemView.setStarSystem(starSystem, true); engine.getStarMap().setCurrentStarSystem(seed); -document.addEventListener("keydown", (e) => { - if (engine.isPaused()) return; - if (e.key === "g") { - if (scene.getActiveController() === spaceshipController) { - scene.setActiveController(player); - setRotationQuaternion(player.getTransform(), getRotationQuaternion(spaceshipController.getTransform()).clone()); - starSystemView.getStarSystem().postProcessManager.rebuild(); - - spaceshipController.setEnabled(false, engine.getHavokPlugin()); - } else { - scene.setActiveController(spaceshipController); - setRotationQuaternion(spaceshipController.getTransform(), getRotationQuaternion(player.getTransform()).clone()); - starSystemView.getStarSystem().postProcessManager.rebuild(); - - spaceshipController.setEnabled(true, engine.getHavokPlugin()); - } - } -}); - -engine.init(); +engine.init(true); const nbRadius = starSystem.model.getBodyTypeOfStar(0) === BODY_TYPE.BLACK_HOLE ? 8 : 3; if (bodyIndex >= starSystem.getBodies().length) throw new Error(`Body index (${bodyIndex}) out of bound (0 - ${starSystem.getBodies().length - 1})!`); -positionNearObject(scene.getActiveController(), starSystem.planets.length > 0 ? starSystem.getBodies()[bodyIndex] : starSystem.stellarObjects[0], starSystem, nbRadius); - -engine.getStarSystemView().bodyEditor.setVisibility(EditorVisibility.NAVBAR); +positionNearObjectBrightSide(scene.getActiveController(), starSystem.planets.length > 0 ? starSystem.getBodies()[bodyIndex] : starSystem.stellarObjects[0], starSystem, nbRadius); -engine.toggleStarMap(); +starSystemView.ui.setEnabled(true); +starSystemView.showUI(); +starSystemView.getSpaceshipControls().enableWarpDrive(); diff --git a/src/ts/saveFile/saveFileData.ts b/src/ts/saveFile/saveFileData.ts new file mode 100644 index 000000000..ccc4e31d3 --- /dev/null +++ b/src/ts/saveFile/saveFileData.ts @@ -0,0 +1,121 @@ +/** + * Data structure for the save file to allow restoring current star system and position. + */ +export type SaveFileData = { + /** + * The version of CosmosJourneyer that created this save file. + */ + version: string; + + /** + * The seed of the current star system. + */ + starSystem: { + starSectorX: number; + starSectorY: number; + starSectorZ: number; + starSectorIndex: number; + }; + + /** + * The index of the nearest orbital object. + */ + nearestOrbitalObjectIndex: number; + + /** + * The x coordinate of the player's position in the nearest orbital object's frame of reference. + */ + positionX: number; + + /** + * The y coordinate of the player's position in the nearest orbital object's frame of reference. + */ + positionY: number; + + /** + * The z coordinate of the player's position in the nearest orbital object's frame of reference. + */ + positionZ: number; + + /** + * The x component of the player's rotation quaternion in the nearest orbital object's frame of reference. + */ + rotationQuaternionX: number; + + /** + * The y component of the player's rotation quaternion in the nearest orbital object's frame of reference. + */ + rotationQuaternionY: number; + + /** + * The z component of the player's rotation quaternion in the nearest orbital object's frame of reference. + */ + rotationQuaternionZ: number; + + /** + * The w component of the player's rotation quaternion in the nearest orbital object's frame of reference. + */ + rotationQuaternionW: number; +}; + +/** + * Checks if a string is a valid save file json data. + * @param jsonString The string to check. + */ +export function isJsonStringValidSaveFileData(jsonString: string): boolean { + try { + const data = JSON.parse(jsonString); + if (typeof data !== "object") return false; + + if (typeof data.version !== "string") return false; + + if (typeof data.starSystem !== "object") return false; + if (typeof data.starSystem.starSectorX !== "number") return false; + if (typeof data.starSystem.starSectorY !== "number") return false; + if (typeof data.starSystem.starSectorZ !== "number") return false; + if (typeof data.starSystem.starSectorIndex !== "number") return false; + + if (typeof data.nearestOrbitalObjectIndex !== "number") return false; + + if (typeof data.positionX !== "number") return false; + if (typeof data.positionY !== "number") return false; + if (typeof data.positionZ !== "number") return false; + + if (typeof data.rotationQuaternionX !== "number") return false; + if (typeof data.rotationQuaternionY !== "number") return false; + if (typeof data.rotationQuaternionZ !== "number") return false; + + return true; + } catch (e) { + return false; + } +} + +/** + * Parses a string into a SaveFileData object. Throws an error if the string is not a valid save file data. + * @param jsonString The string to parse. + * @throws Error if the string is not a valid save file data. + */ +export function parseSaveFileData(jsonString: string): SaveFileData { + if (!isJsonStringValidSaveFileData(jsonString)) throw new Error("Invalid save file data"); + + const data = JSON.parse(jsonString); + + return { + version: data.version, + starSystem: { + starSectorX: data.starSystem.starSectorX, + starSectorY: data.starSystem.starSectorY, + starSectorZ: data.starSystem.starSectorZ, + starSectorIndex: data.starSystem.starSectorIndex + }, + nearestOrbitalObjectIndex: data.nearestOrbitalObjectIndex, + positionX: data.positionX, + positionY: data.positionY, + positionZ: data.positionZ, + rotationQuaternionX: data.rotationQuaternionX, + rotationQuaternionY: data.rotationQuaternionY, + rotationQuaternionZ: data.rotationQuaternionZ, + rotationQuaternionW: data.rotationQuaternionW + }; +} diff --git a/src/ts/starSystem/StarSystemView.ts b/src/ts/starSystem/StarSystemView.ts index 9e3770a90..8a687fc8c 100644 --- a/src/ts/starSystem/StarSystemView.ts +++ b/src/ts/starSystem/StarSystemView.ts @@ -30,19 +30,36 @@ import { HemisphericLight } from "@babylonjs/core/Lights/hemisphericLight"; import { Vector3 } from "@babylonjs/core/Maths/math"; import { Settings } from "../settings"; import { StarSystemHelper } from "./starSystemHelper"; -import { positionNearObject } from "../utils/positionNearObject"; +import { positionNearObjectBrightSide } from "../utils/positionNearObject"; import { ShipControls } from "../spaceship/shipControls"; import { OrbitRenderer } from "../orbit/orbitRenderer"; import { BlackHole } from "../stellarObjects/blackHole/blackHole"; import { ChunkForgeWorkers } from "../planets/telluricPlanet/terrain/chunks/chunkForgeWorkers"; import "@babylonjs/core/Loading/loadingScreen"; +import { setMaxLinVel } from "../utils/havok"; +import { HavokPhysicsWithBindings } from "@babylonjs/havok"; import { ChunkForge } from "../planets/telluricPlanet/terrain/chunks/chunkForge"; +import { Mouse } from "../inputs/mouse"; +import { Keyboard } from "../inputs/keyboard"; +import { Gamepad } from "../inputs/gamepad"; +import { DefaultControls } from "../defaultController/defaultControls"; +import { CharacterControls } from "../spacelegs/characterControls"; +import { parsePercentageFrom01, parseSpeed } from "../utils/parseToStrings"; +import { Assets } from "../assets"; +import { getRotationQuaternion, setRotationQuaternion } from "../uberCore/transforms/basicTransform"; +import { Observable } from "@babylonjs/core/Misc/observable"; export class StarSystemView { private readonly helmetOverlay: HelmetOverlay; readonly bodyEditor: BodyEditor; readonly scene: UberScene; + readonly havokPlugin: HavokPlugin; + + private defaultControls: DefaultControls | null = null; + private spaceshipControls: ShipControls | null = null; + private characterControls: CharacterControls | null = null; + private readonly orbitRenderer: OrbitRenderer = new OrbitRenderer(); private readonly axisRenderer: AxisRenderer = new AxisRenderer(); @@ -54,7 +71,9 @@ export class StarSystemView { private readonly chunkForge: ChunkForge = new ChunkForgeWorkers(Settings.VERTEX_RESOLUTION); - constructor(engine: Engine, havokPlugin: HavokPlugin) { + readonly onInitStarSystem = new Observable(); + + constructor(engine: Engine, havokInstance: HavokPhysicsWithBindings) { this.helmetOverlay = new HelmetOverlay(); this.bodyEditor = new BodyEditor(EditorVisibility.HIDDEN); @@ -87,35 +106,32 @@ export class StarSystemView { if (e.key === "t") { this.ui.setTarget(this.getStarSystem().getClosestToScreenCenterOrbitalObject()); } + + if (e.key === "g") { + if (this.scene.getActiveController() === this.getSpaceshipControls()) { + this.switchToDefaultControls(); + } else if (this.scene.getActiveController() === this.getDefaultControls()) { + this.switchToCharacterControls(); + } else if (this.scene.getActiveController() === this.getCharacterControls()) { + this.switchToSpaceshipControls(); + } + } }); this.scene = new UberScene(engine, ScenePerformancePriority.Intermediate); this.scene.clearColor = new Color4(0, 0, 0, 0); this.scene.useRightHandedSystem = true; - this.scene.enablePhysics(Vector3.Zero(), havokPlugin); + this.havokPlugin = new HavokPlugin(true, havokInstance); + setMaxLinVel(this.havokPlugin, 10000, 10000); + this.scene.enablePhysics(Vector3.Zero(), this.havokPlugin); const ambientLight = new HemisphericLight("ambientLight", Vector3.Zero(), this.scene); ambientLight.intensity = 0.3; this.scene.onBeforePhysicsObservable.add(() => { - const starSystem = this.getStarSystem(); - const deltaTime = engine.getDeltaTime() / 1000; - - this.chunkForge.update(); - starSystem.update(deltaTime * Settings.TIME_MULTIPLIER, this.chunkForge); - - this.ui.update(this.scene.getActiveCamera()); - - const nearestOrbitalObject = starSystem.getNearestOrbitalObject(); - const nearestCelestialBody = starSystem.getNearestCelestialBody(this.scene.getActiveCamera().globalPosition); - - this.bodyEditor.update(nearestCelestialBody, starSystem.postProcessManager, this.scene); - - this.helmetOverlay.update(nearestOrbitalObject); - - this.orbitRenderer.update(); + this.update(deltaTime); }); window.addEventListener("resize", () => { @@ -129,6 +145,158 @@ export class StarSystemView { this.ui = new SystemUI(this.scene); } + initStarSystem() { + this.scene.getEngine().loadingScreen.displayLoadingUI(); + this.scene.getEngine().loadingScreen.loadingUIText = `Warping to ${this.getStarSystem().model.getName()}`; + + this.getStarSystem().initPositions(100, this.chunkForge); + this.ui.createObjectOverlays(this.getStarSystem().getOrbitalObjects()); + + const firstBody = this.getStarSystem().getBodies()[0]; + if (firstBody === undefined) throw new Error("No bodies in star system"); + + this.orbitRenderer.setOrbitalObjects(this.getStarSystem().getBodies()); + this.axisRenderer.setObjects(this.getStarSystem().getBodies()); + + const activeController = this.scene.getActiveController(); + positionNearObjectBrightSide(activeController, firstBody, this.getStarSystem(), firstBody instanceof BlackHole ? 7 : 5); + + this.getStarSystem() + .initPostProcesses() + .then(() => { + this.scene.getEngine().loadingScreen.hideLoadingUI(); + this.onInitStarSystem.notifyObservers(); + }); + } + + async initAssets() { + await Assets.Init(this.scene); + + const canvas = this.scene.getEngine().getRenderingCanvas(); + if (canvas === null) throw new Error("Canvas is null"); + + const mouse = new Mouse(canvas, 100); + const keyboard = new Keyboard(); + const gamepad = new Gamepad(); + + const maxZ = Settings.EARTH_RADIUS * 1e5; + + this.defaultControls = new DefaultControls(this.scene); + this.defaultControls.speed = 0.2 * Settings.EARTH_RADIUS; + this.defaultControls.getActiveCamera().maxZ = maxZ; + this.defaultControls.addInput(keyboard); + this.defaultControls.addInput(gamepad); + + this.spaceshipControls = new ShipControls(this.scene); + this.spaceshipControls.getActiveCamera().maxZ = maxZ; + this.spaceshipControls.addInput(keyboard); + this.spaceshipControls.addInput(gamepad); + this.spaceshipControls.addInput(mouse); + + this.characterControls = new CharacterControls(this.scene); + this.characterControls.getTransform().setEnabled(false); + this.characterControls.getActiveCamera().maxZ = maxZ; + this.characterControls.addInput(keyboard); + this.characterControls.addInput(gamepad); + + this.scene.setActiveController(this.spaceshipControls); + } + + update(deltaTime: number) { + const starSystem = this.getStarSystem(); + + this.chunkForge.update(); + starSystem.update(deltaTime * Settings.TIME_MULTIPLIER, this.chunkForge); + + if (this.spaceshipControls === null) throw new Error("Spaceship controls is null"); + if (this.characterControls === null) throw new Error("Character controls is null"); + + const shipPosition = this.spaceshipControls.getTransform().getAbsolutePosition(); + const nearestBody = starSystem.getNearestOrbitalObject(); + const distance = nearestBody.getTransform().getAbsolutePosition().subtract(shipPosition).length(); + const radius = nearestBody.getBoundingRadius(); + this.spaceshipControls.registerClosestObject(distance, radius); + + const warpDrive = this.spaceshipControls.getWarpDrive(); + const shipInternalThrottle = warpDrive.getInternalThrottle(); + const shipTargetThrottle = warpDrive.getTargetThrottle(); + + const throttleString = warpDrive.isEnabled() + ? `${parsePercentageFrom01(shipInternalThrottle)}/${parsePercentageFrom01(shipTargetThrottle)}` + : `${parsePercentageFrom01(this.spaceshipControls.getThrottle())}/100%`; + + (document.querySelector("#speedometer") as HTMLElement).innerHTML = `${throttleString} | ${parseSpeed(this.spaceshipControls.getSpeed())}`; + + this.characterControls.setClosestWalkableObject(nearestBody); + this.spaceshipControls.setClosestWalkableObject(nearestBody); + + this.ui.update(this.scene.getActiveCamera()); + + const nearestOrbitalObject = starSystem.getNearestOrbitalObject(); + const nearestCelestialBody = starSystem.getNearestCelestialBody(this.scene.getActiveCamera().globalPosition); + + this.bodyEditor.update(nearestCelestialBody, starSystem.postProcessManager, this.scene); + + this.helmetOverlay.update(nearestOrbitalObject); + + this.orbitRenderer.update(); + } + + getSpaceshipControls() { + if (this.spaceshipControls === null) throw new Error("Spaceship controls is null"); + return this.spaceshipControls; + } + + getCharacterControls() { + if (this.characterControls === null) throw new Error("Character controls is null"); + return this.characterControls; + } + + getDefaultControls() { + if (this.defaultControls === null) throw new Error("Default controls is null"); + return this.defaultControls; + } + + switchToSpaceshipControls() { + const shipControls = this.getSpaceshipControls(); + const characterControls = this.getCharacterControls(); + const defaultControls = this.getDefaultControls(); + + characterControls.getTransform().setEnabled(false); + this.scene.setActiveController(shipControls); + setRotationQuaternion(shipControls.getTransform(), getRotationQuaternion(defaultControls.getTransform()).clone()); + this.getStarSystem().postProcessManager.rebuild(); + + shipControls.setEnabled(true, this.havokPlugin); + } + + switchToCharacterControls() { + const shipControls = this.getSpaceshipControls(); + const characterControls = this.getCharacterControls(); + const defaultControls = this.getDefaultControls(); + + characterControls.getTransform().setEnabled(true); + characterControls.getTransform().setAbsolutePosition(defaultControls.getTransform().absolutePosition); + this.scene.setActiveController(characterControls); + setRotationQuaternion(characterControls.getTransform(), getRotationQuaternion(defaultControls.getTransform()).clone()); + this.getStarSystem().postProcessManager.rebuild(); + + shipControls.setEnabled(false, this.havokPlugin); + } + + switchToDefaultControls() { + const shipControls = this.getSpaceshipControls(); + const characterControls = this.getCharacterControls(); + const defaultControls = this.getDefaultControls(); + + characterControls.getTransform().setEnabled(false); + shipControls.setEnabled(false, this.havokPlugin); + + this.scene.setActiveController(defaultControls); + setRotationQuaternion(defaultControls.getTransform(), getRotationQuaternion(shipControls.getTransform()).clone()); + this.getStarSystem().postProcessManager.rebuild(); + } + /** * Returns the star system * @returns the star system @@ -151,30 +319,6 @@ export class StarSystemView { if (needsGenerating) StarSystemHelper.generate(this.starSystem); } - init() { - this.scene.getEngine().loadingScreen.displayLoadingUI(); - this.scene.getEngine().loadingScreen.loadingUIText = `Warping to ${this.getStarSystem().model.getName()}`; - - this.getStarSystem().initPositions(100, this.chunkForge); - this.ui.createObjectOverlays(this.getStarSystem().getOrbitalObjects()); - - const firstBody = this.getStarSystem().getBodies()[0]; - if (firstBody === undefined) throw new Error("No bodies in star system"); - - this.orbitRenderer.setOrbitalObjects(this.getStarSystem().getBodies()); - this.axisRenderer.setObjects(this.getStarSystem().getBodies()); - - const activeController = this.scene.getActiveController(); - positionNearObject(activeController, firstBody, this.getStarSystem(), firstBody instanceof BlackHole ? 7 : 5); - if (activeController instanceof ShipControls) activeController.enableWarpDrive(); - - this.getStarSystem() - .initPostProcesses() - .then(() => { - this.scene.getEngine().loadingScreen.hideLoadingUI(); - }); - } - hideUI() { this.bodyEditor.setVisibility(EditorVisibility.HIDDEN); this.helmetOverlay.setVisibility(false); @@ -186,11 +330,19 @@ export class StarSystemView { } unZoom(callback: () => void) { - this.scene.getActiveController().getActiveCamera().animations = [StarSystemView.unZoomAnimation]; + const activeControls = this.scene.getActiveController(); + if (activeControls != this.getSpaceshipControls()) { + callback(); + return; + } + activeControls.getActiveCamera().animations = [StarSystemView.unZoomAnimation]; this.scene.beginAnimation(this.scene.getActiveController().getActiveCamera(), 0, 60, false, 2.0, () => { this.scene.getActiveController().getActiveCamera().animations = []; this.hideUI(); callback(); + this.scene.onAfterRenderObservable.addOnce(() => { + (activeControls as ShipControls).thirdPersonCamera.radius = 30; + }); }); } } diff --git a/src/ts/starSystem/starSystemController.ts b/src/ts/starSystem/starSystemController.ts index 7f81d446a..41f16ed24 100644 --- a/src/ts/starSystem/starSystemController.ts +++ b/src/ts/starSystem/starSystemController.ts @@ -423,13 +423,18 @@ export class StarSystemController { } // floating origin + this.applyFloatingOrigin(); + + this.updateShaders(deltaTime); + } + + public applyFloatingOrigin() { + const controller = this.scene.getActiveController(); if (controller.getActiveCamera().globalPosition.length() > 500) { const displacementTranslation = controller.getTransform().getAbsolutePosition().negate(); this.translateEverythingNow(displacementTranslation); translate(controller.getTransform(), displacementTranslation); } - - this.updateShaders(deltaTime); } /** diff --git a/src/ts/starSystem/starSystemHelper.ts b/src/ts/starSystem/starSystemHelper.ts index b353507e4..f3cd1a5aa 100644 --- a/src/ts/starSystem/starSystemHelper.ts +++ b/src/ts/starSystem/starSystemHelper.ts @@ -63,7 +63,7 @@ export class StarSystemHelper { */ public static makeBlackHole(starsystem: StarSystemController, model: number | BlackHoleModel = starsystem.model.getStarSeed(starsystem.stellarObjects.length)): BlackHole { const name = starName(starsystem.model.getName(), starsystem.stellarObjects.length); - const blackHole = new BlackHole(name, starsystem.scene, model, starsystem.stellarObjects[0]); + const blackHole = new BlackHole(name, starsystem.scene, model, starsystem.stellarObjects.length > 0 ? starsystem.stellarObjects[0] : null); starsystem.addStellarObject(blackHole); return blackHole; } @@ -91,7 +91,11 @@ export class StarSystemHelper { public static makeStellarObject(starsystem: StarSystemController, seed: number = starsystem.model.getStarSeed(starsystem.stellarObjects.length)): StellarObject { const isStellarObjectBlackHole = starsystem.model.getBodyTypeOfStar(starsystem.stellarObjects.length) === BODY_TYPE.BLACK_HOLE; if (isStellarObjectBlackHole) return StarSystemHelper.makeBlackHole(starsystem, seed); - else return this.makeStar(starsystem, seed); + + const isStellarObjectNeutronStar = starsystem.model.getBodyTypeOfStar(starsystem.stellarObjects.length) === BODY_TYPE.NEUTRON_STAR; + if (isStellarObjectNeutronStar) return StarSystemHelper.makeNeutronStar(starsystem, seed); + + return this.makeStar(starsystem, seed); } /** diff --git a/src/ts/starSystem/starSystemModel.ts b/src/ts/starSystem/starSystemModel.ts index ad5d82662..ef058fcd5 100644 --- a/src/ts/starSystem/starSystemModel.ts +++ b/src/ts/starSystem/starSystemModel.ts @@ -71,6 +71,8 @@ export class StarSystemModel { if (index > this.getNbStars()) throw new Error("Star out of bound! " + index); if (uniformRandBool(0.002, this.rng, GENERATION_STEPS.GENERATE_STARS + index)) return BODY_TYPE.BLACK_HOLE; + if (uniformRandBool(0.05, this.rng, GENERATION_STEPS.GENERATE_STARS + index)) return BODY_TYPE.NEUTRON_STAR; + return BODY_TYPE.STAR; } diff --git a/src/ts/starmap/starMap.ts b/src/ts/starmap/starMap.ts index 0a72512a4..a451d6cfb 100644 --- a/src/ts/starmap/starMap.ts +++ b/src/ts/starmap/starMap.ts @@ -292,16 +292,18 @@ export class StarMap { this.currentSystemSeed = starSystemSeed; this.selectedSystemSeed = starSystemSeed; - if (this.loadedStarSectors.has(Vector3ToString(starSystemSeed.starSectorCoordinates))) { + const sectorCoordinates = new Vector3(starSystemSeed.starSectorX, starSystemSeed.starSectorY, starSystemSeed.starSectorZ); + + if (this.loadedStarSectors.has(Vector3ToString(sectorCoordinates))) { this.starMapUI.setCurrentStarSystemMesh(this.seedToInstanceMap.get(this.currentSystemSeed.toString()) as InstancedMesh); this.focusOnCurrentSystem(); return; } - this.registerStarSector(starSystemSeed.starSectorCoordinates, true); + this.registerStarSector(sectorCoordinates, true); this.starMapUI.setCurrentStarSystemMesh(this.seedToInstanceMap.get(this.currentSystemSeed.toString()) as InstancedMesh); - const translation = starSystemSeed.starSectorCoordinates.subtract(this.currentStarSectorPosition).scaleInPlace(StarSector.SIZE); + const translation = sectorCoordinates.subtract(this.currentStarSectorPosition).scaleInPlace(StarSector.SIZE); translate(this.controls.getTransform(), translation); this.controls.getActiveCamera().getViewMatrix(true); this.acknowledgeCameraMovement(); diff --git a/src/ts/starmap/starMapUI.ts b/src/ts/starmap/starMapUI.ts index c70557e33..0ccc833f2 100644 --- a/src/ts/starmap/starMapUI.ts +++ b/src/ts/starmap/starMapUI.ts @@ -58,7 +58,7 @@ export class StarMapUI { this.systemUI.alpha = 0.95; this.namePlate = new TextBlock(); - this.namePlate.text = "Vesperia Gamma"; + this.namePlate.text = ""; this.namePlate.fontSize = 24; this.namePlate.height = "50px"; this.namePlate.textHorizontalAlignment = TextBlock.HORIZONTAL_ALIGNMENT_LEFT; diff --git a/src/ts/starmap/starSector.ts b/src/ts/starmap/starSector.ts index fbdfe439b..257a591e0 100644 --- a/src/ts/starmap/starSector.ts +++ b/src/ts/starmap/starSector.ts @@ -70,7 +70,7 @@ export class StarSector { constructor(positionInStarMap: Vector3) { this.position = positionInStarMap; - this.rng = seededSquirrelNoise(hashVec3(positionInStarMap)); + this.rng = seededSquirrelNoise(hashVec3(positionInStarMap.x, positionInStarMap.y, positionInStarMap.z)); this.density = UniverseDensity(positionInStarMap.x, positionInStarMap.y, positionInStarMap.z); @@ -81,7 +81,7 @@ export class StarSector { const sectorString = this.getKey(); const data: BuildData[] = []; for (let i = 0; i < this.nbStars; i++) { - const systemSeed = new SystemSeed(this.position, i); + const systemSeed = new SystemSeed(this.position.x, this.position.y, this.position.z, i); data.push({ name: `starInstance|${this.position.x}|${this.position.y}|${this.position.z}|${i}`, seed: systemSeed, diff --git a/src/ts/stellarObjects/blackHole/blackHole.ts b/src/ts/stellarObjects/blackHole/blackHole.ts index 663285d0b..427d997b8 100644 --- a/src/ts/stellarObjects/blackHole/blackHole.ts +++ b/src/ts/stellarObjects/blackHole/blackHole.ts @@ -100,7 +100,7 @@ export class BlackHole implements StellarObject, Cullable { } public getBoundingRadius(): number { - return this.getRadius() + this.model.physicalProperties.accretionDiskRadius; + return this.getRadius(); } public dispose(): void { diff --git a/src/ts/ui/bodyEditor/bodyEditor.ts b/src/ts/ui/bodyEditor/bodyEditor.ts index 1f59ab039..cefaf4436 100644 --- a/src/ts/ui/bodyEditor/bodyEditor.ts +++ b/src/ts/ui/bodyEditor/bodyEditor.ts @@ -67,6 +67,16 @@ export class BodyEditor { private readonly panels: EditorPanel[]; constructor(visibility: EditorVisibility = EditorVisibility.FULL) { + if (document.querySelector("#editorPanelContainer") !== null) { + document.querySelector("#editorPanelContainer")?.remove(); + } + if (document.querySelector("#toolbar") !== null) { + document.querySelector("#toolbar")?.remove(); + } + if (document.querySelector("#navBar") !== null) { + document.querySelector("#navBar")?.remove(); + } + document.body.insertAdjacentHTML("beforeend", editorHTML); this.navBar = document.getElementById("navBar") as HTMLElement; diff --git a/src/ts/ui/helmetOverlay.ts b/src/ts/ui/helmetOverlay.ts index f265ecf70..3eb641863 100644 --- a/src/ts/ui/helmetOverlay.ts +++ b/src/ts/ui/helmetOverlay.ts @@ -19,11 +19,13 @@ import overlayHTML from "../../html/helmetOverlay.html"; import { OrbitalObject } from "../architecture/orbitalObject"; export class HelmetOverlay { - private readonly parentNode: HTMLElement; - private readonly bodyNamePlate: HTMLElement; + private parentNode: HTMLElement; + private bodyNamePlate: HTMLElement; constructor() { - document.body.insertAdjacentHTML("beforeend", overlayHTML); + if (document.querySelector("#helmetOverlay") === null) { + document.body.insertAdjacentHTML("beforeend", overlayHTML); + } this.parentNode = document.getElementById("helmetOverlay") as HTMLElement; this.bodyNamePlate = document.getElementById("bodyName") as HTMLElement; } diff --git a/src/ts/ui/objectOverlay.ts b/src/ts/ui/objectOverlay.ts index 387620476..0664d52ba 100644 --- a/src/ts/ui/objectOverlay.ts +++ b/src/ts/ui/objectOverlay.ts @@ -37,11 +37,13 @@ export class ObjectOverlay { private lastDistance: number = 0; + static WIDTH = 300; + constructor(object: OrbitalObject) { this.object = object; this.textRoot = new StackPanel(object.name + "OverlayTextRoot"); - this.textRoot.width = "150px"; + this.textRoot.width = `${ObjectOverlay.WIDTH}px`; this.textRoot.height = "130px"; this.textRoot.background = "transparent"; this.textRoot.zIndex = 6; @@ -127,7 +129,7 @@ export class ObjectOverlay { const alphaText = Math.max(0, distance / (3 * this.object.getBoundingRadius()) - 1.0); this.textRoot.alpha = alphaText; - this.textRoot.linkOffsetXInPixels = 0.5 * Math.max(scale, screenRatio) * window.innerWidth + 75 + 20; + this.textRoot.linkOffsetXInPixels = 0.5 * Math.max(scale, screenRatio) * window.innerWidth + ObjectOverlay.WIDTH / 2 + 20; this.distanceText.text = parseDistance(distance); diff --git a/src/ts/ui/pauseMenu.ts b/src/ts/ui/pauseMenu.ts index 9afad9668..d28e077ef 100644 --- a/src/ts/ui/pauseMenu.ts +++ b/src/ts/ui/pauseMenu.ts @@ -24,10 +24,12 @@ export class PauseMenu { private readonly screenshotButton: HTMLElement; private readonly shareButton: HTMLElement; + private readonly saveButton: HTMLElement; private readonly resumeButton: HTMLElement; readonly onScreenshot = new Observable(); readonly onShare = new Observable(); + readonly onSave = new Observable(); readonly onResume = new Observable(); constructor() { @@ -41,6 +43,9 @@ export class PauseMenu { this.shareButton = document.getElementById("shareButton") as HTMLElement; this.shareButton.addEventListener("click", () => this.onShare.notifyObservers()); + this.saveButton = document.getElementById("saveButton") as HTMLElement; + this.saveButton.addEventListener("click", () => this.onSave.notifyObservers()); + this.resumeButton = document.getElementById("resumeButton") as HTMLElement; this.resumeButton.addEventListener("click", () => this.onResume.notifyObservers()); diff --git a/src/ts/utils/algebra.ts b/src/ts/utils/algebra.ts index 0595e6a9e..0413ee2fe 100644 --- a/src/ts/utils/algebra.ts +++ b/src/ts/utils/algebra.ts @@ -65,3 +65,7 @@ export function flattenVector4Array(vector4Array: Vector4[]): number[] { } return result; } + +export function MapVector3(v: Vector3, f: (x: number) => number): Vector3 { + return new Vector3(f(v.x), f(v.y), f(v.z)); +} diff --git a/src/ts/utils/hashVec3.ts b/src/ts/utils/hashVec3.ts index fff1db89f..29c06b60b 100644 --- a/src/ts/utils/hashVec3.ts +++ b/src/ts/utils/hashVec3.ts @@ -15,10 +15,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +export function hashVec3(x: number, y: number, z: number): number { + const hash = (x * 73856093) ^ (y * 19349663) ^ (z * 83492791); -export function hashVec3(v: Vector3): number { - const hash = (v.x * 73856093) ^ (v.y * 19349663) ^ (v.z * 83492791); const n = 1000000000; return hash % n; } diff --git a/src/ts/utils/nameGenerator.ts b/src/ts/utils/nameGenerator.ts index 06ce70544..b0b18a752 100644 --- a/src/ts/utils/nameGenerator.ts +++ b/src/ts/utils/nameGenerator.ts @@ -15,100 +15,132 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +/** + * @see https://en.wikipedia.org/wiki/List_of_proper_names_of_stars + */ const constellationNames: string[] = [ - "Andromeda", - "Antlia", - "Apus", - "Aquarius", - "Aquila", - "Ara", - "Aries", - "Ashton", - "Auriga", - "Boötes", + "Andromedae", + "Antliae", + "Apodis", + "Aquarii", + "Aquilae", + "Arae", + "Archimediae", + "Arii", + "Aurigae", + "Azophi", "Barnard", "Berenices", - "Bode", - "Bottlinger", + "Boötis", "Botes", - "Caela", "Caeli", - "Caelum", - "Camelopardalis", - "Cancer", - "Canes Venatici", + "Cameliae", + "Cancri", + "Canesis", + "Cannonis", "Canis", - "Carter", - "Capricornus", - "Cassiopeia", - "Centaurus", - "Cepheus", - "Cetus", - "Chamaeleon", - "Circinus", - "Columba", - "Corona", - "Corvus", - "Crater", - "Crux", - "Cygnus", - "Delphinus", - "Dorado", - "Draco", - "Equuleus", - "Eridanus", - "Fornax", - "Gemini", - "Grus", - "Hades", + "Carteri", + "Capricorni", + "Carinae", + "Cassini", + "Cassiopeiae", + "Centauri", + "Cephei", + "Ceti", + "Circini", + "Columbae", + "Comae", + "Conway", + "Copernici", + "Coronae", + "Corvi", + "Crucis", + "Curi", + "Cygni", + "Darwini", + "Da Vinci", + "Delphini", + "Descartes", + "Dijkstrae", + "Draconis", + "Drakes", + "Eich", + "Einsteini", + "Equulei", + "Eratosthenis", + "Eridani", + "Euclidae", + "Euleris", + "Europae", + "Fornacis", + "Gaussi", + "Galilei", + "Geminorum", + "Hamiltoni", + "Hawking", "Hercules", + "Herschel", + "Hopperae", "Horologium", - "Hydra", - "Hydrus", - "Indus", - "Kozuch", - "Lacerta", - "Laval", - "Leo", - "Lepus", - "Libra", - "Lupus", - "Lynx", - "Lyra", - "Mensa", - "Microscopium", - "Monoceros", - "Musca", + "Hubblis", + "Huygentis", + "Hyades", + "Hydri", + "Hypatiae", + "Indi", + "Kapteyni", + "Kepleris", + "Lacertae", + "Leavitis", + "Leonis", + "Lepi", + "Librae", + "Lovelace", + "Lupi", + "Lyncis", + "Lyrae", + "Mensae", + "Messier", + "Mobii", + "Muscae", + "Neumanni", + "Newtonis", + "Normae", "Norma", - "Octans", - "Ophiuchus", - "Orion", - "Pavo", - "Pegasus", - "Perseus", - "Phoenix", - "Pictor", - "Pisces", + "Octantis", + "Ophiuchi", + "Orionis", + "Pavonis", + "Pegasi", + "Persei", + "Phoenicis", + "Pictoris", + "Piscium", + "Ptolemæi", "Puppis", - "Pyxis", - "Reticulum", - "Sagitta", - "Sagittarius", - "Scorpius", - "Sculptor", - "Scutum", - "Serpens", - "Sextans", - "Taurus", - "Telescopium", - "Triangulum", - "Tucana", - "Ursa", - "Vela", - "Virgo", - "Volans", + "Pythagorae", + "Reticuli", + "Saganis", + "Sagittae", + "Sagittarii", + "Scorpii", + "Sculptoris", + "Scuti", + "Serpentinis", + "Sextantis", + "Tauri", + "Telescopii", + "Torvaldi", + "Trianguli", + "Tucanae", + "Turingi", + "Ursae", + "Velae", + "Virginis", + "Volantis", "Vulpecula", "Weierstrass", + "Zeno", "Zimmerman" ]; @@ -169,5 +201,5 @@ export function romanNumeral(n: number): string { export function generateName(rng: (step: number) => number, baseStep = 0): string { const constellation = constellationNames[Math.floor(rng(baseStep) * constellationNames.length)]; const greekLetter = greekLetters[Math.floor(rng(baseStep + 1) * greekLetters.length)]; - return `${constellation} ${greekLetter}`; + return `${greekLetter} ${constellation}`; } diff --git a/src/ts/utils/positionNearObject.ts b/src/ts/utils/positionNearObject.ts index 4f630bd89..de887f74b 100644 --- a/src/ts/utils/positionNearObject.ts +++ b/src/ts/utils/positionNearObject.ts @@ -20,8 +20,10 @@ import { StarSystemController } from "../starSystem/starSystemController"; import { nearestBody } from "./nearestBody"; import { Transformable } from "../architecture/transformable"; import { BoundingSphere } from "../architecture/boundingSphere"; +import { Controls } from "../uberCore/controls"; +import { getUpwardDirection, roll, rotateAround } from "../uberCore/transforms/basicTransform"; -export function positionNearObject(transformable: Transformable, object: Transformable & BoundingSphere, starSystem: StarSystemController, nRadius = 3): void { +export function positionNearObjectBrightSide(transformable: Transformable, object: Transformable & BoundingSphere, starSystem: StarSystemController, nRadius = 3): void { // go from the nearest star to be on the sunny side of the object const nearestStar = nearestBody(object.getTransform().getAbsolutePosition(), starSystem.stellarObjects); @@ -50,3 +52,53 @@ export function positionNearObject(transformable: Transformable, object: Transfo transformable.getTransform().lookAt(object.getTransform().getAbsolutePosition()); } + +export function positionNearObjectWithStarVisible(transformable: Controls, object: Transformable & BoundingSphere, starSystem: StarSystemController, nRadius = 3): void { + // go from the nearest star to be on the sunny side of the object + const nearestStar = nearestBody(object.getTransform().getAbsolutePosition(), starSystem.stellarObjects); + + if (nearestStar === object) { + // the object is the nearest star + transformable.getTransform().setAbsolutePosition( + object + .getTransform() + .getAbsolutePosition() + .add(new Vector3(0, 0.2, 1).scaleInPlace(object.getBoundingRadius() * nRadius)) + ); + } else { + const dirBodyToStar = object.getTransform().getAbsolutePosition().subtract(nearestStar.getTransform().getAbsolutePosition()); + const distBodyToStar = dirBodyToStar.length(); + dirBodyToStar.scaleInPlace(1 / distBodyToStar); + + const upDirection = getUpwardDirection(object.getTransform()); + const lateralDirection = Vector3.Cross(dirBodyToStar, upDirection); + + const displacement = nearestStar + .getTransform() + .getAbsolutePosition() + .add(dirBodyToStar.scale(distBodyToStar + 1.5 * object.getBoundingRadius())) + .add(lateralDirection.scale(3 * object.getBoundingRadius())); + //.add(upDirection.scale(1 * object.getBoundingRadius())); + transformable.getTransform().setAbsolutePosition(displacement); + + rotateAround(transformable.getTransform(), object.getTransform().getAbsolutePosition(), dirBodyToStar, -Math.PI / 16); + } + + starSystem.translateEverythingNow(transformable.getTransform().getAbsolutePosition().negate()); + transformable.getTransform().setAbsolutePosition(Vector3.Zero()); + + const starDirection = nearestStar.getTransform().getAbsolutePosition().subtract(object.getTransform().getAbsolutePosition()).normalize(); + + const halfway = object + .getTransform() + .getAbsolutePosition() + .add(starDirection.scale(object.getBoundingRadius() * 4)); + transformable.getTransform().lookAt(halfway); + + transformable.getTransform().computeWorldMatrix(true); + + roll(transformable.getTransform(), -Math.PI / 8); + + transformable.getActiveCamera().getViewMatrix(true); + transformable.getActiveCamera().getProjectionMatrix(true); +} diff --git a/src/ts/utils/systemSeed.ts b/src/ts/utils/systemSeed.ts index f77b90755..947168749 100644 --- a/src/ts/utils/systemSeed.ts +++ b/src/ts/utils/systemSeed.ts @@ -15,31 +15,34 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -import { Vector3 } from "@babylonjs/core/Maths/math.vector"; import { seededSquirrelNoise } from "squirrel-noise"; import { hashVec3 } from "./hashVec3"; import { centeredRand } from "extended-random"; import { Settings } from "../settings"; export class SystemSeed { - readonly starSectorCoordinates: Vector3; + readonly starSectorX: number; + readonly starSectorY: number; + readonly starSectorZ: number; readonly index: number; readonly hash: number; - constructor(starSectorCoordinates: Vector3, index: number) { - this.starSectorCoordinates = starSectorCoordinates; + constructor(starSectorX: number, starSectorY: number, starSectorZ: number, index: number) { + this.starSectorX = starSectorX; + this.starSectorY = starSectorY; + this.starSectorZ = starSectorZ; this.index = index; - if (!Number.isSafeInteger(this.starSectorCoordinates.x)) throw new Error("x coordinate of star sector is not a safe integer"); - if (!Number.isSafeInteger(this.starSectorCoordinates.y)) throw new Error("y coordinate of star sector is not a safe integer"); - if (!Number.isSafeInteger(this.starSectorCoordinates.z)) throw new Error("z coordinate of star sector is not a safe integer"); + if (!Number.isSafeInteger(this.starSectorX)) throw new Error("x coordinate of star sector is not a safe integer"); + if (!Number.isSafeInteger(this.starSectorY)) throw new Error("y coordinate of star sector is not a safe integer"); + if (!Number.isSafeInteger(this.starSectorZ)) throw new Error("z coordinate of star sector is not a safe integer"); - const cellRNG = seededSquirrelNoise(hashVec3(starSectorCoordinates)); + const cellRNG = seededSquirrelNoise(hashVec3(starSectorX, starSectorY, starSectorZ)); this.hash = centeredRand(cellRNG, 1 + index) * Settings.SEED_HALF_RANGE; } toString(): string { - return `${this.starSectorCoordinates.x},${this.starSectorCoordinates.y},${this.starSectorCoordinates.z},${this.index}`; + return `${this.starSectorX},${this.starSectorY},${this.starSectorZ},${this.index}`; } } diff --git a/tsconfig.json b/tsconfig.json index 1e0189bd7..1d70cc3fc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,6 +11,7 @@ "name": "typescript-plugin-css-modules" } ], + "resolveJsonModule": true, "noImplicitReturns": true, //"allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ diff --git a/webpack.config.js b/webpack.config.js index 68b3f8c80..b9357793d 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -9,7 +9,8 @@ const htmlPath = path.join(__dirname, "/src/html/"); const config = { entry: { - showcase: "./src/ts/index.ts", + main: "./src/ts/index.ts", + alphaTestis: "./src/ts/alphaTestis.ts", random: "./src/ts/randomizer.ts", blackHole: "./src/ts/blackHoleDemo.ts", physicSpaceship: "./src/ts/physicSpaceship.ts", @@ -36,7 +37,13 @@ const config = { title: "Planet Engine", filename: "index.html", template: path.join(htmlPath, "index.html"), - chunks: ["showcase"] + chunks: ["main"] + }), + new HtmlWebpackPlugin({ + title: "Alpha Testis", + filename: "alphaTestis.html", + template: path.join(htmlPath, "index.html"), + chunks: ["alphaTestis"] }), new HtmlWebpackPlugin({ title: "Randomizer",