├── .gitignore ├── README.md ├── assets ├── fonts │ ├── Nunito-Bold.ttf │ └── OFL.txt ├── icons │ ├── icon-128.png │ ├── icon-32.png │ ├── icon-48.png │ ├── icon-512.png │ ├── icon-64.png │ └── icon.ico ├── models │ ├── butterfly.bam │ ├── flower.bam │ ├── hunebed.bam │ ├── patch-low.bam │ ├── patch-potato.bam │ ├── patch.bam │ ├── rocks.bam │ ├── shrub.bam │ ├── sky.egg │ ├── star3-sub.egg │ ├── star3.egg │ ├── superflower.bam │ ├── trees.bam │ └── tuft.egg ├── music │ ├── chase.ogg │ ├── menu.ogg │ ├── pause.ogg │ └── peace.ogg ├── sfx │ ├── background.ogg │ ├── dance.ogg │ ├── flower-open-a.ogg │ ├── flower-open-b.ogg │ ├── flower-open-c.ogg │ ├── thorns.ogg │ ├── ui-a.ogg │ ├── ui-b.ogg │ ├── wind-high.ogg │ └── wind-low.ogg ├── shaders │ ├── grass.frag │ ├── grass.vert │ ├── object.frag │ ├── object.vert │ └── tree.vert └── textures │ ├── heightmap.png │ ├── skyrender0001.png │ ├── skyrender0002.png │ ├── skyrender0003.png │ ├── skyrender0004.png │ ├── skyrender0005.png │ ├── skyrender0006.png │ ├── tuft0.png │ ├── tuft1.png │ ├── tuft2.png │ └── wind.png ├── game ├── __init__.py ├── animation.py ├── audio.py ├── camera.py ├── collision.py ├── controls.py ├── general.py ├── lighting.py ├── menu.py ├── terrain.py └── world.py ├── requirements.txt ├── run_game.py ├── scripts ├── make_grass_patch.py ├── make_grass_textures.py └── process_flower.py ├── settings.prc └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.py[co] 3 | 4 | *.blend1 5 | *.blend2 6 | 7 | /build/ 8 | /.eggs/ 9 | /dist/ 10 | /screenshot-*.jpg 11 | 12 | /*.core 13 | /core 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### pollen. 2 | 3 | This is an entry in the [PyWeek 29](https://pyweek.org/29/) challenge, with the theme "Butterfly Effect". 4 | 5 | [Click here to go to the game's page on itch.io.](https://rdb.itch.io/pollen) 6 | 7 | [Click here to go to the PyWeek entry page.](https://pyweek.org/e/flowpow/) 8 | 9 | ![pollen.](https://raw.githubusercontent.com/rdb/pollen/fb66ec83b7752f791431ff197f9c6ec332d306c5/cover.gif) 10 | 11 | ## Controls 12 | 13 | Use the mouse to navigate, click (or press shift) to gain speed. 14 | 15 | Gamepad controls are also supported. The menus require a D-Pad. Use either stick to turn, and hold trigger, or A, to speed. 16 | 17 | If you want to cheat to be done quickly, press M to open a minimap. 18 | 19 | ## Running from source 20 | 21 | Requires Python 3 and Panda3D 1.10.6, plus a few other libraries. I recommend installing them via pip: 22 | 23 | ``` 24 | pip install -r requirements.txt 25 | ``` 26 | 27 | To run, simply cd to the directory containing the game and type: 28 | ``` 29 | python run_game.py 30 | ``` 31 | 32 | ## Acknowledgements 33 | 34 | Modelling by hjh 35 | 36 | Original soundtrack by hjh 37 | 38 | Sound effects by hjh 39 | 40 | Programming by rdb 41 | 42 | Skybox created by [Roel](https://reije081.home.xs4all.nl/skyboxes/) (CC BY-NC-SA) 43 | 44 | Font is Nunito Bold, licensed under the OFL. 45 | 46 | With grateful nod to the PS3 game Flower. 47 | 48 | Many thanks to @lordmauve and all the other PyWeek participants for a great challenge! 49 | -------------------------------------------------------------------------------- /assets/fonts/Nunito-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/fonts/Nunito-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2014 The Nunito Project Authors (https://github.com/googlefonts/NunitoFont) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /assets/icons/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/icons/icon-128.png -------------------------------------------------------------------------------- /assets/icons/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/icons/icon-32.png -------------------------------------------------------------------------------- /assets/icons/icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/icons/icon-48.png -------------------------------------------------------------------------------- /assets/icons/icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/icons/icon-512.png -------------------------------------------------------------------------------- /assets/icons/icon-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/icons/icon-64.png -------------------------------------------------------------------------------- /assets/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/icons/icon.ico -------------------------------------------------------------------------------- /assets/models/butterfly.bam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/models/butterfly.bam -------------------------------------------------------------------------------- /assets/models/flower.bam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/models/flower.bam -------------------------------------------------------------------------------- /assets/models/hunebed.bam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/models/hunebed.bam -------------------------------------------------------------------------------- /assets/models/patch-low.bam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/models/patch-low.bam -------------------------------------------------------------------------------- /assets/models/patch-potato.bam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/models/patch-potato.bam -------------------------------------------------------------------------------- /assets/models/patch.bam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/models/patch.bam -------------------------------------------------------------------------------- /assets/models/rocks.bam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/models/rocks.bam -------------------------------------------------------------------------------- /assets/models/shrub.bam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/models/shrub.bam -------------------------------------------------------------------------------- /assets/models/sky.egg: -------------------------------------------------------------------------------- 1 | { Z-up } 2 | bluecloud_bk { 3 | "../textures/skyrender0004.png" // bk 4 | envtype { MODULATE } 5 | minfilter { LINEAR } 6 | magfilter { LINEAR } 7 | wrap { CLAMP } 8 | } 9 | 10 | bluecloud_lf { 11 | "../textures/skyrender0002.png" // lf 12 | envtype { MODULATE } 13 | minfilter { LINEAR } 14 | magfilter { LINEAR } 15 | wrap { CLAMP } 16 | } 17 | 18 | bluecloud_dn { 19 | "../textures/skyrender0006.png" // dn 20 | envtype { MODULATE } 21 | minfilter { LINEAR } 22 | magfilter { LINEAR } 23 | wrap { CLAMP } 24 | } 25 | 26 | bluecloud_up { 27 | "../textures/skyrender0003.png" // up 28 | envtype { MODULATE } 29 | minfilter { LINEAR } 30 | magfilter { LINEAR } 31 | wrap { CLAMP } 32 | } 33 | 34 | bluecloud_rt { 35 | "../textures/skyrender0005.png" // rt 36 | envtype { MODULATE } 37 | minfilter { LINEAR } 38 | magfilter { LINEAR } 39 | wrap { CLAMP } 40 | } 41 | 42 | bluecloud_ft { 43 | "../textures/skyrender0001.png" // ft 44 | envtype { MODULATE } 45 | minfilter { LINEAR } 46 | magfilter { LINEAR } 47 | wrap { CLAMP } 48 | } 49 | 50 | Cube { 51 | { 52 | { 53 | 1.0 0.0 0.0 0.0 54 | 0.0 1.0 0.0 0.0 55 | 0.0 0.0 1.0 0.0 56 | 0.0 0.0 0.0 1.0 57 | } 58 | } 59 | 60 | Cube { 61 | 62 | 0 {-1.000000 -1.000000 -1.000000 63 | { 64 | 0.000100 0.000100 65 | } 66 | } 67 | 1 {-1.000000 1.000000 -1.000000 68 | { 69 | 0.999900 0.000100 70 | } 71 | } 72 | 2 {-1.000000 1.000000 1.000000 73 | { 74 | 0.999900 0.999900 75 | } 76 | } 77 | 3 {-1.000000 -1.000000 1.000000 78 | { 79 | 0.000100 0.999900 80 | } 81 | } 82 | 4 {-1.000000 1.000000 -1.000000 83 | { 84 | 0.000100 0.000100 85 | } 86 | } 87 | 5 {1.000000 1.000000 -1.000000 88 | { 89 | 0.999900 0.000100 90 | } 91 | } 92 | 6 {1.000000 1.000000 1.000000 93 | { 94 | 0.999900 0.999900 95 | } 96 | } 97 | 7 {-1.000000 1.000000 1.000000 98 | { 99 | 0.000100 0.999900 100 | } 101 | } 102 | 8 {1.000000 1.000000 -1.000000 103 | { 104 | 0.000100 0.000100 105 | } 106 | } 107 | 9 {1.000000 -1.000000 -1.000000 108 | { 109 | 0.999900 0.000100 110 | } 111 | } 112 | 10 {1.000000 -1.000000 1.000000 113 | { 114 | 0.999900 0.999900 115 | } 116 | } 117 | 11 {1.000000 1.000000 1.000000 118 | { 119 | 0.000100 0.999900 120 | } 121 | } 122 | 12 {1.000000 -1.000000 -1.000000 123 | { 124 | 0.000100 0.000100 125 | } 126 | } 127 | 13 {-1.000000 -1.000000 -1.000000 128 | { 129 | 0.999900 0.000100 130 | } 131 | } 132 | 14 {-1.000000 -1.000000 1.000000 133 | { 134 | 0.999900 0.999900 135 | } 136 | } 137 | 15 {1.000000 -1.000000 1.000000 138 | { 139 | 0.000100 0.999900 140 | } 141 | } 142 | 16 {-1.000000 1.000000 -1.000000 143 | { 144 | 0.999900 0.000100 145 | } 146 | } 147 | 17 {-1.000000 -1.000000 -1.000000 148 | { 149 | 0.999900 0.999900 150 | } 151 | } 152 | 18 {1.000000 -1.000000 -1.000000 153 | { 154 | 0.000100 0.999900 155 | } 156 | } 157 | 19 {1.000000 1.000000 -1.000000 158 | { 159 | 0.000100 0.000100 160 | } 161 | } 162 | 20 {1.000000 1.000000 1.000000 163 | { 164 | 0.000100 0.999900 165 | } 166 | } 167 | 21 {1.000000 -1.000000 1.000000 168 | { 169 | 0.000100 0.000100 170 | } 171 | } 172 | 22 {-1.000000 -1.000000 1.000000 173 | { 174 | 0.999900 0.000100 175 | } 176 | } 177 | 23 {-1.000000 1.000000 1.000000 178 | { 179 | 0.999900 0.999900 180 | } 181 | }} 182 | 183 | { 184 | { bluecloud_ft } 185 | {1.000000 0.000000 0.000000} 186 | { 0 1 2 3 { Cube }} 187 | } 188 | { 189 | { bluecloud_lf } 190 | {0.000000 -1.000000 0.000000} 191 | { 4 5 6 7 { Cube }} 192 | } 193 | { 194 | { bluecloud_bk } 195 | {-1.000000 0.000000 0.000000} 196 | { 8 9 10 11 { Cube }} 197 | } 198 | { 199 | { bluecloud_rt } 200 | {0.000000 1.000000 0.000000} 201 | { 12 13 14 15 { Cube }} 202 | } 203 | { 204 | { bluecloud_dn } 205 | {0.000000 0.000000 1.000000} 206 | { 16 17 18 19 { Cube }} 207 | } 208 | { 209 | { bluecloud_up } 210 | {0.000000 0.000000 -1.000000} 211 | { 20 21 22 23 { Cube }} 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /assets/models/star3-sub.egg: -------------------------------------------------------------------------------- 1 | { Z-Up } 2 | 3 | tuft0 { 4 | "../textures/tuft0.png" 5 | wrapu { clamp } 6 | wrapv { clamp } 7 | envtype { modulate } 8 | alpha { binary } 9 | minfilter { linear-mipmap-linear } 10 | } 11 | 12 | tuft1 { 13 | "../textures/tuft1.png" 14 | wrapu { clamp } 15 | wrapv { clamp } 16 | envtype { modulate } 17 | alpha { binary } 18 | minfilter { linear-mipmap-linear } 19 | } 20 | 21 | tuft2 { 22 | "../textures/tuft2.png" 23 | wrapu { clamp } 24 | wrapv { clamp } 25 | envtype { modulate } 26 | alpha { binary } 27 | minfilter { linear-mipmap-linear } 28 | } 29 | 30 | vpool { 31 | 0 { 32 | 0 0.333333 0.5 33 | { 0.333333 0.25 } 34 | { 0 0 1 } 35 | } 36 | 1 { 37 | 0 0.333333 0 38 | { 0.333333 0 } 39 | { 0 0 1 } 40 | } 41 | 2 { 42 | 0 1 0 43 | { 0 0 } 44 | { 0 0 1 } 45 | } 46 | 3 { 47 | 0 1 0.5 48 | { 0 0.25 } 49 | { 0 0 1 } 50 | } 51 | 4 { 52 | -0.288675 0.166667 0.5 53 | { 0.333333 0.25 } 54 | { 0 0 1 } 55 | } 56 | 5 { 57 | -0.288675 0.166667 0 58 | { 0.333333 0 } 59 | { 0 0 1 } 60 | } 61 | 6 { 62 | -0.866025 0.5 0 63 | { 0 0 } 64 | { 0 0 1 } 65 | } 66 | 7 { 67 | -0.866025 0.5 0.5 68 | { 0 0.25 } 69 | { 0 0 1 } 70 | } 71 | 8 { 72 | -0.288675 -0.166667 0.5 73 | { 0.333333 0.25 } 74 | { 0 0 1 } 75 | } 76 | 9 { 77 | -0.288675 -0.166667 0 78 | { 0.333333 0 } 79 | { 0 0 1 } 80 | } 81 | 10 { 82 | -0.866025 -0.5 0 83 | { 0 0 } 84 | { 0 0 1 } 85 | } 86 | 11 { 87 | -0.866025 -0.5 0.5 88 | { 0 0.25 } 89 | { 0 0 1 } 90 | } 91 | 12 { 92 | 0.866025 0.5 0.5 93 | { 1 0.25 } 94 | { 0 0 1 } 95 | } 96 | 13 { 97 | 0.866025 0.5 0 98 | { 1 0 } 99 | { 0 0 1 } 100 | } 101 | 14 { 102 | 0.288675 0.166667 0 103 | { 0.666667 0 } 104 | { 0 0 1 } 105 | } 106 | 15 { 107 | 0.288675 0.166667 0.5 108 | { 0.666667 0.25 } 109 | { 0 0 1 } 110 | } 111 | 16 { 112 | 0.288675 0.166667 0.5 113 | { 0.666667 0.25 } 114 | { 0 0 1 } 115 | } 116 | 17 { 117 | 0.288675 0.166667 0 118 | { 0.666667 0 } 119 | { 0 0 1 } 120 | } 121 | 18 { 122 | -0.288675 -0.166667 0 123 | { 0.333333 0 } 124 | { 0 0 1 } 125 | } 126 | 19 { 127 | -0.288675 -0.166667 0.5 128 | { 0.333333 0.25 } 129 | { 0 0 1 } 130 | } 131 | 20 { 132 | 0.866025 -0.5 0.5 133 | { 1 0.25 } 134 | { 0 0 1 } 135 | } 136 | 21 { 137 | 0.866025 -0.5 0 138 | { 1 0 } 139 | { 0 0 1 } 140 | } 141 | 22 { 142 | 0.288675 -0.166667 0 143 | { 0.666667 0 } 144 | { 0 0 1 } 145 | } 146 | 23 { 147 | 0.288675 -0.166667 0.5 148 | { 0.666667 0.25 } 149 | { 0 0 1 } 150 | } 151 | 24 { 152 | 0.288675 -0.166667 0.5 153 | { 0.666667 0.25 } 154 | { 0 0 1 } 155 | } 156 | 25 { 157 | 0.288675 -0.166667 0 158 | { 0.666667 0 } 159 | { 0 0 1 } 160 | } 161 | 26 { 162 | -0.288675 0.166667 0 163 | { 0.333333 0 } 164 | { 0 0 1 } 165 | } 166 | 27 { 167 | -0.288675 0.166667 0.5 168 | { 0.333333 0.25 } 169 | { 0 0 1 } 170 | } 171 | 28 { 172 | 0 -1 0.5 173 | { 1 0.25 } 174 | { 0 0 1 } 175 | } 176 | 29 { 177 | 0 -1 0 178 | { 1 0 } 179 | { 0 0 1 } 180 | } 181 | 30 { 182 | 0 -0.333333 0 183 | { 0.666667 0 } 184 | { 0 0 1 } 185 | } 186 | 31 { 187 | 0 -0.333333 0.5 188 | { 0.666667 0.25 } 189 | { 0 0 1 } 190 | } 191 | 32 { 192 | 0 -0.333333 0.5 193 | { 0.666667 0.25 } 194 | { 0 0 1 } 195 | } 196 | 33 { 197 | 0 -0.333333 0 198 | { 0.666667 0 } 199 | { 0 0 1 } 200 | } 201 | 34 { 202 | 0 0.333333 0 203 | { 0.333333 0 } 204 | { 0 0 1 } 205 | } 206 | 35 { 207 | 0 0.333333 0.5 208 | { 0.333333 0.25 } 209 | { 0 0 1 } 210 | } 211 | 36 { 212 | 0 -0.333333 1.5 213 | { 0.666667 0.75 } 214 | { 0 0 1 } 215 | } 216 | 37 { 217 | 0 -0.333333 1 218 | { 0.666667 0.5 } 219 | { 0 0 1 } 220 | } 221 | 38 { 222 | 0 0.333333 1 223 | { 0.333333 0.5 } 224 | { 0 0 1 } 225 | } 226 | 39 { 227 | 0 0.333333 1.5 228 | { 0.333333 0.75 } 229 | { 0 0 1 } 230 | } 231 | 40 { 232 | 0 -0.333333 1 233 | { 0.666667 0.5 } 234 | { 0 0 1 } 235 | } 236 | 41 { 237 | 0 -0.333333 0.5 238 | { 0.666667 0.25 } 239 | { 0 0 1 } 240 | } 241 | 42 { 242 | 0 0.333333 0.5 243 | { 0.333333 0.25 } 244 | { 0 0 1 } 245 | } 246 | 43 { 247 | 0 0.333333 1 248 | { 0.333333 0.5 } 249 | { 0 0 1 } 250 | } 251 | 44 { 252 | 0 -1 1.5 253 | { 1 0.75 } 254 | { 0 0 1 } 255 | } 256 | 45 { 257 | 0 -1 1 258 | { 1 0.5 } 259 | { 0 0 1 } 260 | } 261 | 46 { 262 | 0 -0.333333 1 263 | { 0.666667 0.5 } 264 | { 0 0 1 } 265 | } 266 | 47 { 267 | 0 -0.333333 1.5 268 | { 0.666667 0.75 } 269 | { 0 0 1 } 270 | } 271 | 48 { 272 | 0 -1 1 273 | { 1 0.5 } 274 | { 0 0 1 } 275 | } 276 | 49 { 277 | 0 -1 0.5 278 | { 1 0.25 } 279 | { 0 0 1 } 280 | } 281 | 50 { 282 | 0 -0.333333 0.5 283 | { 0.666667 0.25 } 284 | { 0 0 1 } 285 | } 286 | 51 { 287 | 0 -0.333333 1 288 | { 0.666667 0.5 } 289 | { 0 0 1 } 290 | } 291 | 52 { 292 | 0.288675 -0.166667 1.5 293 | { 0.666667 0.75 } 294 | { 0 0 1 } 295 | } 296 | 53 { 297 | 0.288675 -0.166667 1 298 | { 0.666667 0.5 } 299 | { 0 0 1 } 300 | } 301 | 54 { 302 | -0.288675 0.166667 1 303 | { 0.333333 0.5 } 304 | { 0 0 1 } 305 | } 306 | 55 { 307 | -0.288675 0.166667 1.5 308 | { 0.333333 0.75 } 309 | { 0 0 1 } 310 | } 311 | 56 { 312 | 0.288675 -0.166667 1 313 | { 0.666667 0.5 } 314 | { 0 0 1 } 315 | } 316 | 57 { 317 | 0.288675 -0.166667 0.5 318 | { 0.666667 0.25 } 319 | { 0 0 1 } 320 | } 321 | 58 { 322 | -0.288675 0.166667 0.5 323 | { 0.333333 0.25 } 324 | { 0 0 1 } 325 | } 326 | 59 { 327 | -0.288675 0.166667 1 328 | { 0.333333 0.5 } 329 | { 0 0 1 } 330 | } 331 | 60 { 332 | 0.866025 -0.5 1.5 333 | { 1 0.75 } 334 | { 0 0 1 } 335 | } 336 | 61 { 337 | 0.866025 -0.5 1 338 | { 1 0.5 } 339 | { 0 0 1 } 340 | } 341 | 62 { 342 | 0.288675 -0.166667 1 343 | { 0.666667 0.5 } 344 | { 0 0 1 } 345 | } 346 | 63 { 347 | 0.288675 -0.166667 1.5 348 | { 0.666667 0.75 } 349 | { 0 0 1 } 350 | } 351 | 64 { 352 | 0.866025 -0.5 1 353 | { 1 0.5 } 354 | { 0 0 1 } 355 | } 356 | 65 { 357 | 0.866025 -0.5 0.5 358 | { 1 0.25 } 359 | { 0 0 1 } 360 | } 361 | 66 { 362 | 0.288675 -0.166667 0.5 363 | { 0.666667 0.25 } 364 | { 0 0 1 } 365 | } 366 | 67 { 367 | 0.288675 -0.166667 1 368 | { 0.666667 0.5 } 369 | { 0 0 1 } 370 | } 371 | 68 { 372 | 0.288675 0.166667 1.5 373 | { 0.666667 0.75 } 374 | { 0 0 1 } 375 | } 376 | 69 { 377 | 0.288675 0.166667 1 378 | { 0.666667 0.5 } 379 | { 0 0 1 } 380 | } 381 | 70 { 382 | -0.288675 -0.166667 1 383 | { 0.333333 0.5 } 384 | { 0 0 1 } 385 | } 386 | 71 { 387 | -0.288675 -0.166667 1.5 388 | { 0.333333 0.75 } 389 | { 0 0 1 } 390 | } 391 | 72 { 392 | 0.288675 0.166667 1 393 | { 0.666667 0.5 } 394 | { 0 0 1 } 395 | } 396 | 73 { 397 | 0.288675 0.166667 0.5 398 | { 0.666667 0.25 } 399 | { 0 0 1 } 400 | } 401 | 74 { 402 | -0.288675 -0.166667 0.5 403 | { 0.333333 0.25 } 404 | { 0 0 1 } 405 | } 406 | 75 { 407 | -0.288675 -0.166667 1 408 | { 0.333333 0.5 } 409 | { 0 0 1 } 410 | } 411 | 76 { 412 | 0.866025 0.5 1.5 413 | { 1 0.75 } 414 | { 0 0 1 } 415 | } 416 | 77 { 417 | 0.866025 0.5 1 418 | { 1 0.5 } 419 | { 0 0 1 } 420 | } 421 | 78 { 422 | 0.288675 0.166667 1 423 | { 0.666667 0.5 } 424 | { 0 0 1 } 425 | } 426 | 79 { 427 | 0.288675 0.166667 1.5 428 | { 0.666667 0.75 } 429 | { 0 0 1 } 430 | } 431 | 80 { 432 | 0.866025 0.5 1 433 | { 1 0.5 } 434 | { 0 0 1 } 435 | } 436 | 81 { 437 | 0.866025 0.5 0.5 438 | { 1 0.25 } 439 | { 0 0 1 } 440 | } 441 | 82 { 442 | 0.288675 0.166667 0.5 443 | { 0.666667 0.25 } 444 | { 0 0 1 } 445 | } 446 | 83 { 447 | 0.288675 0.166667 1 448 | { 0.666667 0.5 } 449 | { 0 0 1 } 450 | } 451 | 84 { 452 | -0.288675 -0.166667 1.5 453 | { 0.333333 0.75 } 454 | { 0 0 1 } 455 | } 456 | 85 { 457 | -0.288675 -0.166667 1 458 | { 0.333333 0.5 } 459 | { 0 0 1 } 460 | } 461 | 86 { 462 | -0.866025 -0.5 1 463 | { 0 0.5 } 464 | { 0 0 1 } 465 | } 466 | 87 { 467 | -0.866025 -0.5 1.5 468 | { 0 0.75 } 469 | { 0 0 1 } 470 | } 471 | 88 { 472 | -0.288675 -0.166667 1 473 | { 0.333333 0.5 } 474 | { 0 0 1 } 475 | } 476 | 89 { 477 | -0.288675 -0.166667 0.5 478 | { 0.333333 0.25 } 479 | { 0 0 1 } 480 | } 481 | 90 { 482 | -0.866025 -0.5 0.5 483 | { 0 0.25 } 484 | { 0 0 1 } 485 | } 486 | 91 { 487 | -0.866025 -0.5 1 488 | { 0 0.5 } 489 | { 0 0 1 } 490 | } 491 | 92 { 492 | -0.288675 0.166667 1.5 493 | { 0.333333 0.75 } 494 | { 0 0 1 } 495 | } 496 | 93 { 497 | -0.288675 0.166667 1 498 | { 0.333333 0.5 } 499 | { 0 0 1 } 500 | } 501 | 94 { 502 | -0.866025 0.5 1 503 | { 0 0.5 } 504 | { 0 0 1 } 505 | } 506 | 95 { 507 | -0.866025 0.5 1.5 508 | { 0 0.75 } 509 | { 0 0 1 } 510 | } 511 | 96 { 512 | -0.288675 0.166667 1 513 | { 0.333333 0.5 } 514 | { 0 0 1 } 515 | } 516 | 97 { 517 | -0.288675 0.166667 0.5 518 | { 0.333333 0.25 } 519 | { 0 0 1 } 520 | } 521 | 98 { 522 | -0.866025 0.5 0.5 523 | { 0 0.25 } 524 | { 0 0 1 } 525 | } 526 | 99 { 527 | -0.866025 0.5 1 528 | { 0 0.5 } 529 | { 0 0 1 } 530 | } 531 | 100 { 532 | 0 0.333333 1.5 533 | { 0.333333 0.75 } 534 | { 0 0 1 } 535 | } 536 | 101 { 537 | 0 0.333333 1 538 | { 0.333333 0.5 } 539 | { 0 0 1 } 540 | } 541 | 102 { 542 | 0 1 1 543 | { 0 0.5 } 544 | { 0 0 1 } 545 | } 546 | 103 { 547 | 0 1 1.5 548 | { 0 0.75 } 549 | { 0 0 1 } 550 | } 551 | 104 { 552 | 0 0.333333 1 553 | { 0.333333 0.5 } 554 | { 0 0 1 } 555 | } 556 | 105 { 557 | 0 0.333333 0.5 558 | { 0.333333 0.25 } 559 | { 0 0 1 } 560 | } 561 | 106 { 562 | 0 1 0.5 563 | { 0 0.25 } 564 | { 0 0 1 } 565 | } 566 | 107 { 567 | 0 1 1 568 | { 0 0.5 } 569 | { 0 0 1 } 570 | } 571 | } 572 | root { 573 | { 574 | { 1 } 575 | { 0 0 1 } 576 | { tuft0 } 577 | { 0 1 2 3 { vpool } } 578 | } 579 | { 580 | { 1 } 581 | { 0 0 1 } 582 | { tuft1 } 583 | { 4 5 6 7 { vpool } } 584 | } 585 | { 586 | { 1 } 587 | { 0 0 1 } 588 | { tuft2 } 589 | { 8 9 10 11 { vpool } } 590 | } 591 | { 592 | { 1 } 593 | { 0 0 1 } 594 | { tuft2 } 595 | { 12 13 14 15 { vpool } } 596 | } 597 | { 598 | { 1 } 599 | { 0 0 1 } 600 | { tuft2 } 601 | { 16 17 18 19 { vpool } } 602 | } 603 | { 604 | { 1 } 605 | { 0 0 1 } 606 | { tuft1 } 607 | { 20 21 22 23 { vpool } } 608 | } 609 | { 610 | { 1 } 611 | { 0 0 1 } 612 | { tuft1 } 613 | { 24 25 26 27 { vpool } } 614 | } 615 | { 616 | { 1 } 617 | { 0 0 1 } 618 | { tuft0 } 619 | { 28 29 30 31 { vpool } } 620 | } 621 | { 622 | { 1 } 623 | { 0 0 1 } 624 | { tuft0 } 625 | { 32 33 34 35 { vpool } } 626 | } 627 | { 628 | { 1 } 629 | { 0 0 1 } 630 | { tuft0 } 631 | { 36 37 38 39 { vpool } } 632 | } 633 | { 634 | { 1 } 635 | { 0 0 1 } 636 | { tuft0 } 637 | { 40 41 42 43 { vpool } } 638 | } 639 | { 640 | { 1 } 641 | { 0 0 1 } 642 | { tuft0 } 643 | { 44 45 46 47 { vpool } } 644 | } 645 | { 646 | { 1 } 647 | { 0 0 1 } 648 | { tuft0 } 649 | { 48 49 50 51 { vpool } } 650 | } 651 | { 652 | { 1 } 653 | { 0 0 1 } 654 | { tuft1 } 655 | { 52 53 54 55 { vpool } } 656 | } 657 | { 658 | { 1 } 659 | { 0 0 1 } 660 | { tuft1 } 661 | { 56 57 58 59 { vpool } } 662 | } 663 | { 664 | { 1 } 665 | { 0 0 1 } 666 | { tuft1 } 667 | { 60 61 62 63 { vpool } } 668 | } 669 | { 670 | { 1 } 671 | { 0 0 1 } 672 | { tuft1 } 673 | { 64 65 66 67 { vpool } } 674 | } 675 | { 676 | { 1 } 677 | { 0 0 1 } 678 | { tuft2 } 679 | { 68 69 70 71 { vpool } } 680 | } 681 | { 682 | { 1 } 683 | { 0 0 1 } 684 | { tuft2 } 685 | { 72 73 74 75 { vpool } } 686 | } 687 | { 688 | { 1 } 689 | { 0 0 1 } 690 | { tuft2 } 691 | { 76 77 78 79 { vpool } } 692 | } 693 | { 694 | { 1 } 695 | { 0 0 1 } 696 | { tuft2 } 697 | { 80 81 82 83 { vpool } } 698 | } 699 | { 700 | { 1 } 701 | { 0 0 1 } 702 | { tuft2 } 703 | { 84 85 86 87 { vpool } } 704 | } 705 | { 706 | { 1 } 707 | { 0 0 1 } 708 | { tuft2 } 709 | { 88 89 90 91 { vpool } } 710 | } 711 | { 712 | { 1 } 713 | { 0 0 1 } 714 | { tuft1 } 715 | { 92 93 94 95 { vpool } } 716 | } 717 | { 718 | { 1 } 719 | { 0 0 1 } 720 | { tuft1 } 721 | { 96 97 98 99 { vpool } } 722 | } 723 | { 724 | { 1 } 725 | { 0 0 1 } 726 | { tuft0 } 727 | { 100 101 102 103 { vpool } } 728 | } 729 | { 730 | { 1 } 731 | { 0 0 1 } 732 | { tuft0 } 733 | { 104 105 106 107 { vpool } } 734 | } 735 | } 736 | -------------------------------------------------------------------------------- /assets/models/star3.egg: -------------------------------------------------------------------------------- 1 | { Z-Up } 2 | 3 | tuft0 { 4 | "../textures/tuft0.png" 5 | wrapu { clamp } 6 | wrapv { clamp } 7 | envtype { modulate } 8 | alpha { binary } 9 | minfilter { linear-mipmap-linear } 10 | } 11 | 12 | tuft1 { 13 | "../textures/tuft1.png" 14 | wrapu { clamp } 15 | wrapv { clamp } 16 | envtype { modulate } 17 | alpha { binary } 18 | minfilter { linear-mipmap-linear } 19 | } 20 | 21 | tuft2 { 22 | "../textures/tuft2.png" 23 | wrapu { clamp } 24 | wrapv { clamp } 25 | envtype { modulate } 26 | alpha { binary } 27 | minfilter { linear-mipmap-linear } 28 | } 29 | 30 | vpool { 31 | 0 { 32 | 0 -1 1.5 33 | { 1 0.75 } 34 | { 0 0 1 } 35 | } 36 | 1 { 37 | 0 -1 0 38 | { 1 0 } 39 | { 0 0 1 } 40 | } 41 | 2 { 42 | 0 1 0 43 | { 0 0 } 44 | { 0 0 1 } 45 | } 46 | 3 { 47 | 0 1 1.5 48 | { 0 0.75 } 49 | { 0 0 1 } 50 | } 51 | 4 { 52 | 0.866025 -0.5 1.5 53 | { 1 0.75 } 54 | { 0 0 1 } 55 | } 56 | 5 { 57 | 0.866025 -0.5 0 58 | { 1 0 } 59 | { 0 0 1 } 60 | } 61 | 6 { 62 | -0.866025 0.5 0 63 | { 0 0 } 64 | { 0 0 1 } 65 | } 66 | 7 { 67 | -0.866025 0.5 1.5 68 | { 0 0.75 } 69 | { 0 0 1 } 70 | } 71 | 8 { 72 | 0.866025 0.5 1.5 73 | { 1 0.75 } 74 | { 0 0 1 } 75 | } 76 | 9 { 77 | 0.866025 0.5 0 78 | { 1 0 } 79 | { 0 0 1 } 80 | } 81 | 10 { 82 | -0.866025 -0.5 0 83 | { 0 0 } 84 | { 0 0 1 } 85 | } 86 | 11 { 87 | -0.866025 -0.5 1.5 88 | { 0 0.75 } 89 | { 0 0 1 } 90 | } 91 | 12 { 92 | 0 -1 1.5 93 | { 1 0.75 } 94 | { 0 0 1 } 95 | } 96 | 13 { 97 | 0 1 1.5 98 | { 0 0.75 } 99 | { 0 0 1 } 100 | } 101 | 14 { 102 | 0 1 0 103 | { 0 0 } 104 | { 0 0 1 } 105 | } 106 | 15 { 107 | 0 -1 0 108 | { 1 0 } 109 | { 0 0 1 } 110 | } 111 | 16 { 112 | 0.866025 -0.5 1.5 113 | { 1 0.75 } 114 | { 0 0 1 } 115 | } 116 | 17 { 117 | -0.866025 0.5 1.5 118 | { 0 0.75 } 119 | { 0 0 1 } 120 | } 121 | 18 { 122 | -0.866025 0.5 0 123 | { 0 0 } 124 | { 0 0 1 } 125 | } 126 | 19 { 127 | 0.866025 -0.5 0 128 | { 1 0 } 129 | { 0 0 1 } 130 | } 131 | 20 { 132 | 0.866025 0.5 1.5 133 | { 1 0.75 } 134 | { 0 0 1 } 135 | } 136 | 21 { 137 | -0.866025 -0.5 1.5 138 | { 0 0.75 } 139 | { 0 0 1 } 140 | } 141 | 22 { 142 | -0.866025 -0.5 0 143 | { 0 0 } 144 | { 0 0 1 } 145 | } 146 | 23 { 147 | 0.866025 0.5 0 148 | { 1 0 } 149 | { 0 0 1 } 150 | } 151 | } 152 | root { 153 | { 154 | { 0 1 2 3 { vpool } } 155 | { tuft0 } 156 | } 157 | { 158 | { 12 13 14 15 { vpool } } 159 | { tuft0 } 160 | } 161 | { 162 | { 4 5 6 7 { vpool } } 163 | { tuft1 } 164 | } 165 | { 166 | { 16 17 18 19 { vpool } } 167 | { tuft1 } 168 | } 169 | { 170 | { 8 9 10 11 { vpool } } 171 | { tuft2 } 172 | } 173 | { 174 | { 20 21 22 23 { vpool } } 175 | { tuft2 } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /assets/models/superflower.bam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/models/superflower.bam -------------------------------------------------------------------------------- /assets/models/trees.bam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/models/trees.bam -------------------------------------------------------------------------------- /assets/models/tuft.egg: -------------------------------------------------------------------------------- 1 | { Z-Up } 2 | 3 | { 4 | "obj2egg tuft.obj -o tuft.egg" 5 | } 6 | vpool { 7 | 0 { 8 | -0.412411 -0.66844 0.43483 9 | { 0.75 0 } 10 | { 0.826636186791132 -0.562024603165517 -0.0283012389138508 } 11 | } 12 | 1 { 13 | -0.412116 -0.668666 0 14 | { 1 0 } 15 | { 0.8285411173732 -0.55992778710592 0.000700034740085987 } 16 | } 17 | 2 { 18 | -0.345646 -0.570228 0 19 | { 1 1 } 20 | { 0.8285411173732 -0.55992778710592 0.000700034740085987 } 21 | } 22 | 3 { 23 | -0.345961 -0.570189 0.434695 24 | { 0.75 1 } 25 | { 0.826854685843032 -0.56183715832924 -0.0255016866098178 } 26 | } 27 | 4 { 28 | -0.503958 -1.164619 1.298553 29 | { 0 0 } 30 | { 0.660751031772852 -0.492838063353506 -0.566143724968384 } 31 | } 32 | 5 { 33 | -0.457136 -0.887046 1.11156 34 | { 0.25 0 } 35 | { 0.711813766611371 -0.52121008030043 -0.470809105536152 } 36 | } 37 | 6 { 38 | -0.41509 -0.827772 1.109032 39 | { 0.25 1 } 40 | { 0.775448897471894 -0.553234885325576 -0.304319189451505 } 41 | } 42 | 7 { 43 | -0.42532 -0.72383 0.820126 44 | { 0.5 0 } 45 | { 0.81000324001944 -0.564402257613546 -0.159200636803821 } 46 | } 47 | 8 { 48 | -0.364652 -0.635957 0.818458 49 | { 0.5 1 } 50 | { 0.816621216094795 -0.564814674075851 -0.118803086544283 } 51 | } 52 | 9 { 53 | -0.142487 0.397447 0.499293 54 | { 0.75 0 } 55 | { 0.637713819408202 0.768116645268056 -0.057601248232574 } 56 | } 57 | 10 { 58 | -0.144318 0.396699 0 59 | { 1 0 } 60 | { 0.638723537396038 0.769428353957274 -0.00350012898212953 } 61 | } 62 | 11 { 63 | -0.235709 0.472567 0 64 | { 1 1 } 65 | { 0.638723537396038 0.769428353957274 -0.00350012898212953 } 66 | } 67 | 12 { 68 | -0.233878 0.473315 0.499293 69 | { 0.75 1 } 70 | { 0.637740567967834 0.768248869864968 -0.0555035306918845 } 71 | } 72 | 13 { 73 | 0.211249 0.598648 1.844948 74 | { 0 0 } 75 | { 0.575714868028453 0.693517910331304 -0.43311118524079 } 76 | } 77 | 14 { 78 | 0.029431 0.488215 1.426468 79 | { 0.25 0 } 80 | { 0.595833659594173 0.717740546308724 -0.360320355071803 } 81 | } 82 | 15 { 83 | -0.028871 0.536614 1.426468 84 | { 0.25 1 } 85 | { 0.600507221142752 0.723308697839388 -0.340904099396443 } 86 | } 87 | 16 { 88 | -0.090804 0.423312 0.977843 89 | { 0.5 0 } 90 | { 0.626394246595267 0.754593069094491 -0.195498204357239 } 91 | } 92 | 17 { 93 | -0.174543 0.492828 0.977843 94 | { 0.5 1 } 95 | { 0.627689410968955 0.756187243228809 -0.184896880815931 } 96 | } 97 | 18 { 98 | 0.174612 0.333821 0.499776 99 | { 0.75 0 } 100 | { 0.889425811511581 -0.456813256913076 -0.0156004527317075 } 101 | } 102 | 19 { 103 | 0.173817 0.333368 0 104 | { 1 0 } 105 | { 0.889575359103825 -0.456787347165723 -0.000999972301150882 } 106 | } 107 | 20 { 108 | 0.228079 0.439028 0 109 | { 1 1 } 110 | { 0.889575359103825 -0.456787347165723 -0.000999972301150882 } 111 | } 112 | 21 { 113 | 0.228873 0.439481 0.499776 114 | { 0.75 1 } 115 | { 0.889460818962816 -0.45673123006557 -0.0160010941122162 } 116 | } 117 | 22 { 118 | 0.375029 0.485433 1.950945 119 | { 0 0 } 120 | { 0.883078757795969 -0.453489091451106 -0.120497101477086 } 121 | } 122 | 23 { 123 | 0.266196 0.399557 1.476736 124 | { 0.25 0 } 125 | { 0.885564772933985 -0.454781909135475 -0.0945962370365346 } 126 | } 127 | 24 { 128 | 0.300811 0.466961 1.476736 129 | { 0.25 1 } 130 | { 0.885181765443446 -0.454590635529361 -0.0989979606630153 } 131 | } 132 | 25 { 133 | 0.200965 0.351972 0.99299 134 | { 0.5 0 } 135 | { 0.888397916709328 -0.456198930214763 -0.0512998797019232 } 136 | } 137 | 26 { 138 | 0.250683 0.448785 0.99299 139 | { 0.5 1 } 140 | { 0.88826967054989 -0.456184423848767 -0.0535981699217315 } 141 | } 142 | 27 { 143 | -0.471102 0.70533 0.498997 144 | { 0.75 0 } 145 | { 0.997272605297794 0.0713980387228141 0.0186994863321656 } 146 | } 147 | 28 { 148 | -0.47039 0.703595 0 149 | { 1 0 } 150 | { 0.997446801301901 0.0714033503237976 0.00120005630796299 } 151 | } 152 | 29 { 153 | -0.478872 0.822071 0 154 | { 1 1 } 155 | { 0.997446801301901 0.0714033503237976 0.00120005630796299 } 156 | } 157 | 30 { 158 | -0.479584 0.823805 0.498997 159 | { 0.75 1 } 160 | { 0.997244971776026 0.0714032200008106 0.0201009064708164 } 161 | } 162 | 31 { 163 | -0.630652 1.142936 1.780225 164 | { 0 0 } 165 | { 0.982409406615103 0.0703006731321679 0.173001656498792 } 166 | } 167 | 32 { 168 | -0.545916 0.905302 1.395774 169 | { 0.25 0 } 170 | { 0.989639185539343 0.0708028034925075 0.12490494570924 } 171 | } 172 | 33 { 173 | -0.551327 0.980882 1.395774 174 | { 0.25 1 } 175 | { 0.987106978871011 0.070700499854301 0.143601015262767 } 176 | } 177 | 34 { 178 | -0.49304 0.762871 0.968595 179 | { 0.5 0 } 180 | { 0.995412298394921 0.0713008809278258 0.0638007882636085 } 181 | } 182 | 35 { 183 | -0.500812 0.871427 0.968595 184 | { 0.5 1 } 185 | { 0.994866613524237 0.0712047676748349 0.0719048145480425 } 186 | } 187 | 36 { 188 | 0.587952 0.513706 0.386707 189 | { 0.75 0 } 190 | { 0.344997990392559 0.937094541440194 -0.0531996901127076 } 191 | } 192 | 37 { 193 | 0.585885 0.512773 0 194 | { 1 0 } 195 | { 0.346898052172906 0.937894733735855 -0.00389997810168444 } 196 | } 197 | 38 { 198 | 0.474505 0.55404 0 199 | { 1 1 } 200 | { 0.346898052172906 0.937894733735855 -0.00389997810168444 } 201 | } 202 | 39 { 203 | 0.476388 0.554901 0.386786 204 | { 0.75 1 } 205 | { 0.345197084822928 0.937192085446257 -0.0500995769108595 } 206 | } 207 | 40 { 208 | 0.860498 0.689914 1.431539 209 | { 0 0 } 210 | { 0.309200717346496 0.871602022119037 -0.380400882531071 } 211 | } 212 | 41 { 213 | 0.725048 0.595514 1.105156 214 | { 0.25 0 } 215 | { 0.318617490987265 0.89204897037238 -0.320517595296354 } 216 | } 217 | 42 { 218 | 0.652794 0.621371 1.105671 219 | { 0.25 1 } 220 | { 0.322316396652157 0.89994578140638 -0.293614936571745 } 221 | } 222 | 43 { 223 | 0.630854 0.537816 0.757011 224 | { 0.5 0 } 225 | { 0.335997160835986 0.925292181314102 -0.175898513663839 } 226 | } 227 | 44 { 228 | 0.527943 0.575292 0.757378 229 | { 0.5 1 } 230 | { 0.33742315983339 0.927463658652892 -0.161111058236986 } 231 | } 232 | 45 { 233 | -0.337539 0.058055 0.499186 234 | { 0.75 0 } 235 | { 0.196404418167081 0.978722016599398 0.0594013362480885 } 236 | } 237 | 46 { 238 | -0.337152 0.059872 0 239 | { 1 0 } 240 | { 0.195903246143683 0.980616248945869 0.00370006131052388 } 241 | } 242 | 47 { 243 | -0.453633 0.083117 0 244 | { 1 1 } 245 | { 0.195903246143683 0.980616248945869 0.00370006131052388 } 246 | } 247 | 48 { 248 | -0.453842 0.081318 0.499315 249 | { 0.75 1 } 250 | { 0.196405583890126 0.978727825627629 0.0593016859708986 } 251 | } 252 | 49 { 253 | -0.459987 -0.324709 1.836275 254 | { 0 0 } 255 | { 0.184800085008059 0.871000400660276 0.455200209392144 } 256 | } 257 | 50 { 258 | -0.388407 -0.123771 1.422689 259 | { 0.25 0 } 260 | { 0.190210813792155 0.909951732226507 0.368520951011614 } 261 | } 262 | 51 { 263 | -0.461343 -0.108812 1.423673 264 | { 0.25 1 } 265 | { 0.190207520002958 0.910035978983658 0.368314561604045 } 266 | } 267 | 52 { 268 | -0.351365 0.004218 0.976527 269 | { 0.5 0 } 270 | { 0.195702877831978 0.960114118581921 0.199702936653275 } 271 | } 272 | 53 { 273 | -0.457129 0.025608 0.97722 274 | { 0.5 1 } 275 | { 0.195695803348495 0.960179409173353 0.199395724004548 } 276 | } 277 | 54 { 278 | 0.36063 -0.448604 0.499401 279 | { 0.75 0 } 280 | { 0.868562873199197 0.493735740355145 -0.0427030911751361 } 281 | } 282 | 55 { 283 | 0.358295 -0.448281 0 284 | { 1 0 } 285 | { 0.870566818567586 0.492037765347791 -0.00350026865592941 } 286 | } 287 | 56 { 288 | 0.299961 -0.344814 0 289 | { 1 1 } 290 | { 0.870566818567586 0.492037765347791 -0.00350026865592941 } 291 | } 292 | 57 { 293 | 0.302005 -0.34514 0.499467 294 | { 0.75 1 } 295 | { 0.868649232090383 0.493727982826412 -0.0410023238725601 } 296 | } 297 | 58 { 298 | 0.685634 -0.468921 1.904267 299 | { 0 0 } 300 | { 0.821809742612248 0.486105762817978 -0.297203523368654 } 301 | } 302 | 59 { 303 | 0.519765 -0.463842 1.45392 304 | { 0.25 0 } 305 | { 0.835169898293805 0.490641063468974 -0.248520799576111 } 306 | } 307 | 60 { 308 | 0.480874 -0.397854 1.454305 309 | { 0.25 1 } 310 | { 0.838741678109504 0.491824439363603 -0.233711613418613 } 311 | } 312 | 61 { 313 | 0.409592 -0.454263 0.985679 314 | { 0.5 0 } 315 | { 0.857705236306452 0.495303023834191 -0.13790084188721 } 316 | } 317 | 62 { 318 | 0.354924 -0.359472 0.985958 319 | { 0.5 1 } 320 | { 0.858951822126691 0.495329884153394 -0.129807831542723 } 321 | } 322 | 63 { 323 | -0.6122 -0.087198 0.410575 324 | { 0.75 0 } 325 | { -0.630929155909958 0.774135773640669 -0.0514023753586493 } 326 | } 327 | 64 { 328 | -0.611898 -0.088442 0 329 | { 1 0 } 330 | { -0.629995489248446 0.776594439603719 -0.00259998138419993 } 331 | } 332 | 65 { 333 | -0.704184 -0.163218 0 334 | { 1 1 } 335 | { -0.629995489248446 0.776594439603719 -0.00259998138419993 } 336 | } 337 | 66 { 338 | -0.704377 -0.16207 0.410628 339 | { 0.75 1 } 340 | { -0.630918773267402 0.774223037349219 -0.0502014937676709 } 341 | } 342 | 67 { 343 | -0.848672 0.067509 1.433023 344 | { 0 0 } 345 | { -0.575097636353572 0.668897250837949 -0.470998064201934 } 346 | } 347 | 68 { 348 | -0.694699 0.018481 1.175419 349 | { 0.25 0 } 350 | { -0.604983036513481 0.715779929812148 -0.348790220059342 } 351 | } 352 | 69 { 353 | -0.751727 -0.029204 1.177327 354 | { 0.25 1 } 355 | { -0.602112147735122 0.711014344859113 -0.363207327781758 } 356 | } 357 | 70 { 358 | -0.629589 -0.04877 0.814627 359 | { 0.5 0 } 360 | { -0.627422496636933 0.758827208237336 -0.174706264205407 } 361 | } 362 | 71 { 363 | -0.713404 -0.117938 0.81499 364 | { 0.5 1 } 365 | { -0.627620871879147 0.759325251781129 -0.171805713494005 } 366 | } 367 | 72 { 368 | -0.253329 -0.229259 0.500613 369 | { 0.75 0 } 370 | { 0.620642400633948 0.782653468798144 -0.0475032452950573 } 371 | } 372 | 73 { 373 | -0.253516 -0.229295 0 374 | { 1 0 } 375 | { 0.622069674842509 0.782961831541046 -0.000299985376069366 } 376 | } 377 | 74 { 378 | -0.346514 -0.155405 0 379 | { 1 1 } 380 | { 0.622069674842509 0.782961831541046 -0.000299985376069366 } 381 | } 382 | 75 { 383 | -0.346327 -0.155369 0.500613 384 | { 0.75 1 } 385 | { 0.62063352752876 0.782642279477937 -0.0478025823652509 } 386 | } 387 | 76 { 388 | -0.021211 0.248733 2.184838 389 | { 0 0 } 390 | { 0.54933228520713 0.704641412992798 -0.449126395933956 } 391 | } 392 | 77 { 393 | -0.151922 -0.031846 1.584736 394 | { 0.25 0 } 395 | { 0.573675249315291 0.733368359504679 -0.364784261722535 } 396 | } 397 | 78 { 398 | -0.211608 0.014638 1.58466 399 | { 0.25 1 } 400 | { 0.57264193037727 0.73225361757289 -0.368626991856552 } 401 | } 402 | 79 { 403 | -0.229146 -0.184249 1.02396 404 | { 0.5 0 } 405 | { 0.607746989775092 0.771259632243789 -0.189214629694664 } 406 | } 407 | 80 { 408 | -0.314578 -0.116946 1.023914 409 | { 0.5 1 } 410 | { 0.607443880293597 0.770955691995939 -0.191413827277238 } 411 | } 412 | 81 { 413 | -0.48194 -0.432518 0.496307 414 | { 0.75 0 } 415 | { 0.107304170994191 0.988938440784304 0.102403980520086 } 416 | } 417 | 82 { 418 | -0.483993 -0.418632 0 419 | { 1 0 } 420 | { 0.117900225779149 0.99260190083446 0.0290000555351595 } 421 | } 422 | 83 { 423 | -0.601837 -0.403765 0 424 | { 1 1 } 425 | { 0.117900225779149 0.99260190083446 0.0290000555351595 } 426 | } 427 | 84 { 428 | -0.599448 -0.41941 0.495958 429 | { 0.75 1 } 430 | { 0.107505458190672 0.989050215354183 0.101105133237925 } 431 | } 432 | 85 { 433 | -0.597609 -0.818659 1.713474 434 | { 0 0 } 435 | { 0.0409997365775387 0.894294254177875 0.445597137047592 } 436 | } 437 | 86 { 438 | -0.524517 -0.644427 1.357126 439 | { 0.25 0 } 440 | { 0.0539030582767695 0.923652404905831 0.379421527091027 } 441 | } 442 | 87 { 443 | -0.598635 -0.640483 1.356027 444 | { 0.25 1 } 445 | { 0.0533020260485159 0.922535066224313 0.382214528250333 } 446 | } 447 | 88 { 448 | -0.490012 -0.511399 0.952081 449 | { 0.5 0 } 450 | { 0.0810974073573295 0.965669128051457 0.246792110182354 } 451 | } 452 | 89 { 453 | -0.59714 -0.502214 0.951201 454 | { 0.5 1 } 455 | { 0.0810995726063786 0.965794910274234 0.246298702009261 } 456 | } 457 | 90 { 458 | 0.053513 0.665152 0.500054 459 | { 0.75 0 } 460 | { -0.851529782774988 0.524218334856898 -0.00960033577761584 } 461 | } 462 | 91 { 463 | 0.05353 0.664592 0 464 | { 1 0 } 465 | { -0.8515963551754 0.524197756438404 -0.000599997432016487 } 466 | } 467 | 92 { 468 | -0.008735 0.563442 0 469 | { 1 1 } 470 | { -0.8515963551754 0.524197756438404 -0.000599997432016487 } 471 | } 472 | 93 { 473 | -0.008753 0.564002 0.500054 474 | { 0.75 1 } 475 | { -0.851530596043984 0.524218835521146 -0.00950034135339735 } 476 | } 477 | 94 { 478 | 0.018666 0.736762 2.011787 479 | { 0 0 } 480 | { -0.849516705910284 0.522910283131828 -0.0699013746240481 } 481 | } 482 | 95 { 483 | 0.040489 0.704491 1.50559 484 | { 0.25 0 } 485 | { -0.850209484139694 0.523305837509176 -0.057400640307714 } 486 | } 487 | 96 { 488 | 0.000767 0.639964 1.50559 489 | { 0.25 1 } 490 | { -0.850232479501087 0.523419995025722 -0.056002139322584 } 491 | } 492 | 97 { 493 | 0.050391 0.677898 1.001684 494 | { 0.5 0 } 495 | { -0.851164984671477 0.523940001726456 -0.0317024204136832 } 496 | } 497 | 98 { 498 | -0.006662 0.585217 1.001684 499 | { 0.5 1 } 500 | { -0.851213849361995 0.52390852406103 -0.0309005027552698 } 501 | } 502 | 99 { 503 | 0.331137 0.912824 0.414591 504 | { 0.75 0 } 505 | { 0.707478952814226 0.699379193778473 -0.10169697456001 } 506 | } 507 | 100 { 508 | 0.327259 0.911876 0 509 | { 1 0 } 510 | { 0.715238151820554 0.698837276974557 -0.00780041608529128 } 511 | } 512 | 101 { 513 | 0.244406 0.996987 0 514 | { 1 1 } 515 | { 0.715238151820554 0.698837276974557 -0.00780041608529128 } 516 | } 517 | 102 { 518 | 0.247805 0.997806 0.415372 519 | { 0.75 1 } 520 | { 0.708211423542393 0.699511283207998 -0.0955015404522714 } 521 | } 522 | 103 { 523 | 0.837729 1.082039 1.261934 524 | { 0 0 } 525 | { 0.425392364275591 0.504290948058723 -0.751486510938192 } 526 | } 527 | 104 { 528 | 0.578578 0.989033 1.052808 529 | { 0.25 0 } 530 | { 0.505807652927684 0.569308613704488 -0.648109805975547 } 531 | } 532 | 105 { 533 | 0.522986 1.042593 1.057275 534 | { 0.25 1 } 535 | { 0.544506811822824 0.599307497383689 -0.586807341005754 } 536 | } 537 | 106 { 538 | 0.407525 0.934983 0.770446 539 | { 0.5 0 } 540 | { 0.650538704951661 0.67244000800845 -0.353021003609433 } 541 | } 542 | 107 { 543 | 0.329597 1.012427 0.77373 544 | { 0.5 1 } 545 | { 0.661600552436692 0.67890056688221 -0.318400265864333 } 546 | } 547 | 108 { 548 | 0.08496 0.061693 0.49939 549 | { 0.75 0 } 550 | { 0.982471852584629 0.176794934897672 -0.0590983068577625 } 551 | } 552 | 109 { 553 | 0.083113 0.061643 0 554 | { 1 0 } 555 | { 0.984186708648249 0.17709760831295 -0.00359995138298486 } 556 | } 557 | 110 { 558 | 0.062077 0.178544 0 559 | { 1 1 } 560 | { 0.984186708648249 0.17709760831295 -0.00359995138298486 } 561 | } 562 | 111 { 563 | 0.063923 0.178593 0.49939 564 | { 0.75 1 } 565 | { 0.982506514039782 0.176801172195658 -0.0585003878588573 } 566 | } 567 | 112 { 568 | 0.47733 0.130986 1.866358 569 | { 0 0 } 570 | { 0.883906960794725 0.159001252139791 -0.439803463465912 } 571 | } 572 | 113 { 573 | 0.271246 0.087971 1.436622 574 | { 0.25 0 } 575 | { 0.917896287117028 0.165199331770055 -0.360798540572855 } 576 | } 577 | 114 { 578 | 0.257826 0.162547 1.436622 579 | { 0.25 1 } 580 | { 0.920037092043064 0.165606676567751 -0.355114316722274 } 581 | } 582 | 115 { 583 | 0.140069 0.068093 0.980903 584 | { 0.5 0 } 585 | { 0.964725343667672 0.173604560651713 -0.197905199037869 } 586 | } 587 | 116 { 588 | 0.120794 0.175206 0.980903 589 | { 0.5 1 } 590 | { 0.9653560550268 0.173710086769041 -0.194711306240255 } 591 | } 592 | 117 { 593 | 0.696571 0.268174 0.397525 594 | { 0.75 0 } 595 | { -0.0219014577000275 0.982265376847807 0.186212393778316 } 596 | } 597 | 118 { 598 | 0.702595 0.301067 0 599 | { 1 0 } 600 | { -0.0597024875559671 0.9953414717664 0.0757031542376334 } 601 | } 602 | 119 { 603 | 0.584188 0.291682 0 604 | { 1 1 } 605 | { -0.0597024875559671 0.9953414717664 0.0757031542376334 } 606 | } 607 | 120 { 608 | 0.578957 0.263246 0.399655 609 | { 0.75 1 } 610 | { -0.024000876888056 0.983335926834392 0.18020658396782 } 611 | } 612 | 121 { 613 | 1.04698 -0.201701 1.090564 614 | { 0 0 } 615 | { 0.218802824762702 0.68050878542513 0.69930902813783 } 616 | } 617 | 122 { 618 | 0.863283 0.006944 0.944998 619 | { 0.25 0 } 620 | { 0.192306126009218 0.749023860535122 0.63402019703507 } 621 | } 622 | 123 { 623 | 0.790142 0.020164 0.955014 624 | { 0.25 1 } 625 | { 0.159798810302286 0.818593905591059 0.551695892639369 } 626 | } 627 | 124 { 628 | 0.742546 0.165073 0.715674 629 | { 0.5 0 } 630 | { 0.0848008170598086 0.908808756414552 0.408503935954385 } 631 | } 632 | 125 { 633 | 0.635856 0.17057 0.722988 634 | { 0.5 1 } 635 | { 0.0632021918900228 0.925832108414289 0.372612922440229 } 636 | } 637 | 126 { 638 | -0.121455 0.841136 0.436359 639 | { 0.75 0 } 640 | { 0.953741254970245 0.292412648582678 -0.0698030193949073 } 641 | } 642 | 127 { 643 | -0.123104 0.839936 0 644 | { 1 0 } 645 | { 0.95607410030343 0.293092060243631 -0.00439988080884331 } 646 | } 647 | 128 { 648 | -0.157925 0.953495 0 649 | { 1 1 } 650 | { 0.95607410030343 0.293092060243631 -0.00439988080884331 } 651 | } 652 | 129 { 653 | -0.156276 0.954696 0.436359 654 | { 0.75 1 } 655 | { 0.953590306803797 0.292397027799319 -0.0718992691476436 } 656 | } 657 | 130 { 658 | 0.221062 1.159864 1.633825 659 | { 0 0 } 660 | { 0.823627159553387 0.252508326599357 -0.50781674553328 } 661 | } 662 | 131 { 663 | 0.042066 0.985288 1.256719 664 | { 0.25 0 } 665 | { 0.872232338613442 0.267409914406368 -0.409515183056873 } 666 | } 667 | 132 { 668 | 0.019852 1.057732 1.256719 669 | { 0.25 1 } 670 | { 0.864924105770746 0.265207391433 -0.426111875903474 } 671 | } 672 | 133 { 673 | -0.072893 0.882293 0.85751 674 | { 0.5 0 } 675 | { 0.930697631377542 0.285399273659773 -0.228799417706223 } 676 | } 677 | 134 { 678 | -0.104799 0.986345 0.85751 679 | { 0.5 1 } 680 | { 0.928401425097281 0.284600436862006 -0.238900366712344 } 681 | } 682 | 135 { 683 | -0.544984 0.406467 0.367245 684 | { 0.75 0 } 685 | { 0.670028460063275 0.739631416511639 0.0633026888388139 } 686 | } 687 | 136 { 688 | -0.540522 0.405894 0 689 | { 1 0 } 690 | { 0.666539376976827 0.745444038407392 0.00620036629745885 } 691 | } 692 | 137 { 693 | -0.62924 0.484871 0 694 | { 1 1 } 695 | { 0.666539376976827 0.745444038407392 0.00620036629745885 } 696 | } 697 | 138 { 698 | -0.632517 0.485074 0.36774 699 | { 0.75 1 } 700 | { 0.670184163735314 0.73908253568602 0.0678983955798685 } 701 | } 702 | 139 { 703 | -0.92868 0.373292 1.227191 704 | { 0 0 } 705 | { 0.612190602946367 0.580491089530163 0.536891758774754 } 706 | } 707 | 140 { 708 | -0.731588 0.38973 0.984669 709 | { 0.25 0 } 710 | { 0.648730014188426 0.645729875383793 0.402718632208539 } 711 | } 712 | 141 { 713 | -0.779949 0.437546 0.98811 714 | { 0.25 1 } 715 | { 0.63862984706337 0.626429276856397 0.446920887335766 } 716 | } 717 | 142 { 718 | -0.604574 0.402391 0.698619 719 | { 0.5 0 } 720 | { 0.671090265906289 0.711189684268444 0.209396962719083 } 721 | } 722 | 143 { 723 | -0.679869 0.472887 0.701124 724 | { 0.5 1 } 725 | { 0.670124198621726 0.704925455317796 0.232408392418578 } 726 | } 727 | 144 { 728 | 0.200876 -0.139292 0.500361 729 | { 0.75 0 } 730 | { -0.544096991151958 0.83799536589844 -0.0414997705069036 } 731 | } 732 | 145 { 733 | 0.201121 -0.140315 0 734 | { 1 0 } 735 | { -0.543921857939051 0.839133721266147 -0.00190007635610259 } 736 | } 737 | 146 { 738 | 0.101442 -0.20491 0 739 | { 1 1 } 740 | { -0.543921857939051 0.839133721266147 -0.00190007635610259 } 741 | } 742 | 147 { 743 | 0.101154 -0.203955 0.500275 744 | { 0.75 1 } 745 | { -0.544086825097043 0.838079706145621 -0.0398990338565926 } 746 | } 747 | 148 { 748 | 0.205996 0.248408 2.199434 749 | { 0 0 } 750 | { -0.524808961189519 0.805913761094957 -0.274004678669833 } 751 | } 752 | 149 { 753 | 0.205147 0.040659 1.590122 754 | { 0.25 0 } 755 | { -0.53068859297129 0.814482492886971 -0.234494959585015 } 756 | } 757 | 150 { 758 | 0.140777 -0.001792 1.588542 759 | { 0.25 1 } 760 | { -0.53231253610784 0.816919238674609 -0.22200522828469 } 761 | } 762 | 151 { 763 | 0.201572 -0.088638 1.024897 764 | { 0.5 0 } 765 | { -0.540628413472925 0.830243634600855 -0.13570713227576 } 766 | } 767 | 152 { 768 | 0.109728 -0.148638 1.023865 769 | { 0.5 1 } 770 | { -0.541102721753536 0.831204180967546 -0.127700642335846 } 771 | } 772 | 153 { 773 | 0.698434 -0.353285 0.350268 774 | { 0.75 0 } 775 | { 0.281511032633558 0.959137589338704 0.0284011130614318 } 776 | } 777 | 154 { 778 | 0.697836 -0.352477 0 779 | { 1 0 } 780 | { 0.28160820899894 0.959527970648023 0.0017000495571669 } 781 | } 782 | 155 { 783 | 0.583865 -0.319029 0 784 | { 1 1 } 785 | { 0.28160820899894 0.959527970648023 0.0017000495571669 } 786 | } 787 | 156 { 788 | 0.584463 -0.319836 0.350268 789 | { 0.75 1 } 790 | { 0.281493422983007 0.959177589077444 0.0271993644942728 } 791 | } 792 | 157 { 793 | 0.771919 -0.512797 1.316931 794 | { 0 0 } 795 | { 0.274492807010237 0.93537548880647 0.222994156514692 } 796 | } 797 | 158 { 798 | 0.739362 -0.430383 1.011332 799 | { 0.25 0 } 800 | { 0.276811525287788 0.943239272584688 0.183507640499672 } 801 | } 802 | 159 { 803 | 0.666655 -0.409045 1.011332 804 | { 0.25 1 } 805 | { 0.277502970685202 0.94551012174003 0.170301823090774 } 806 | } 807 | 160 { 808 | 0.711794 -0.376376 0.689071 809 | { 0.5 0 } 810 | { 0.280294263836585 0.954980456524934 0.0971980108630613 } 811 | } 812 | 161 { 813 | 0.607365 -0.345728 0.689071 814 | { 0.5 1 } 815 | { 0.280396914248938 0.955589483795596 0.0906990018629767 } 816 | } 817 | 162 { 818 | 0.541856 0.0362 0.367919 819 | { 0.75 0 } 820 | { -0.98568074491472 0.159296888165684 0.055298919746154 } 821 | } 822 | 163 { 823 | 0.540564 0.036206 0 824 | { 1 0 } 825 | { -0.987176525221342 0.159596204847373 0.00339991915088388 } 826 | } 827 | 164 { 828 | 0.525719 -0.05562 0 829 | { 1 1 } 830 | { -0.987176525221342 0.159596204847373 0.00339991915088388 } 831 | } 832 | 165 { 833 | 0.527012 -0.055625 0.367919 834 | { 0.75 1 } 835 | { -0.985707875837392 0.159301272822255 0.0548004378572477 } 836 | } 837 | 166 { 838 | 0.816405 -0.010951 1.415698 839 | { 0 0 } 840 | { -0.90523985858848 0.146306442014466 0.398917564727071 } 841 | } 842 | 167 { 843 | 0.672211 0.018992 1.077497 844 | { 0.25 0 } 845 | { -0.932464743274766 0.150710464190806 0.328322796243142 } 846 | } 847 | 168 { 848 | 0.662741 -0.039587 1.077497 849 | { 0.25 1 } 850 | { -0.934058866244684 0.151009516919644 0.323620395199978 } 851 | } 852 | 169 { 853 | 0.58042 0.032184 0.72821 854 | { 0.5 0 } 855 | { -0.970605716884509 0.156900924149165 0.182501074934497 } 856 | } 857 | 170 { 858 | 0.566819 -0.051953 0.72821 859 | { 0.5 1 } 860 | { -0.97111150773955 0.15700186048307 0.179702129482852 } 861 | } 862 | 171 { 863 | 0.140553 -0.628803 0.328953 864 | { 0.75 0 } 865 | { -0.105102277065499 0.983521308219966 0.147103187025071 } 866 | } 867 | 172 { 868 | 0.13066 -0.62412 0 869 | { 1 0 } 870 | { -0.102507334149592 0.994571159139212 0.0179012807929531 } 871 | } 872 | 173 { 873 | 0.025497 -0.634731 0 874 | { 1 1 } 875 | { -0.102507334149592 0.994571159139212 0.0179012807929531 } 876 | } 877 | 174 { 878 | 0.036579 -0.639751 0.328643 879 | { 0.75 1 } 880 | { -0.104997563559806 0.984977143870562 0.137096818705232 } 881 | } 882 | 175 { 883 | 0.222953 -1.150029 1.011227 884 | { 0 0 } 885 | { -0.0768016954161397 0.51801143522865 0.851918806315226 } 886 | } 887 | 176 { 888 | 0.19948 -0.876767 0.842975 889 | { 0.25 0 } 890 | { -0.0893020445937167 0.662615170748003 0.743617025306694 } 891 | } 892 | 177 { 893 | 0.135466 -0.884605 0.841969 894 | { 0.25 1 } 895 | { -0.0905022395056252 0.677516765359791 0.729918062045921 } 896 | } 897 | 178 { 898 | 0.169774 -0.70435 0.614604 899 | { 0.5 0 } 900 | { -0.10420063093673 0.885705362962209 0.45240273930688 } 901 | } 902 | 179 { 903 | 0.076027 -0.714937 0.613795 904 | { 0.5 1 } 905 | { -0.104601945091254 0.896216665303839 0.431108016528102 } 906 | } 907 | 180 { 908 | -0.001997 -0.419739 0.442937 909 | { 0.75 0 } 910 | { -0.600832719236613 0.79614335516689 0.0718039101883969 } 911 | } 912 | 181 { 913 | -0.002801 -0.417799 0 914 | { 1 0 } 915 | { -0.60232719869875 0.798236045162447 0.00460020772706998 } 916 | } 917 | 182 { 918 | -0.097612 -0.489346 0 919 | { 1 1 } 920 | { -0.60232719869875 0.798236045162447 0.00460020772706998 } 921 | } 922 | 183 { 923 | -0.096809 -0.491287 0.442937 924 | { 0.75 1 } 925 | { -0.600742431936504 0.796056227437086 0.0735051918550576 } 926 | } 927 | 184 { 928 | 0.125993 -0.878938 1.701165 929 | { 0 0 } 930 | { -0.521504792651067 0.691106351296553 0.500404598739394 } 931 | } 932 | 185 { 933 | 0.063596 -0.632475 1.2957 934 | { 0.25 0 } 935 | { -0.549717629727083 0.728423360911783 0.408913114053855 } 936 | } 937 | 186 { 938 | 0.003112 -0.678118 1.2957 939 | { 0.25 1 } 940 | { -0.546530765082674 0.724140763030859 0.420623677573235 } 941 | } 942 | 187 { 943 | 0.018409 -0.481578 0.876256 944 | { 0.5 0 } 945 | { -0.585888557708201 0.776384837352188 0.232295463313902 } 946 | } 947 | 188 { 948 | -0.068465 -0.547135 0.876256 949 | { 0.5 1 } 950 | { -0.58481020795127 0.774913526233652 0.239804185818595 } 951 | } 952 | } 953 | root { 954 | { 955 | { 0 1 2 3 { vpool } } 956 | } 957 | { 958 | { 4 5 6 { vpool } } 959 | } 960 | { 961 | { 5 7 8 6 { vpool } } 962 | } 963 | { 964 | { 7 0 3 8 { vpool } } 965 | } 966 | { 967 | { 9 10 11 12 { vpool } } 968 | } 969 | { 970 | { 13 14 15 { vpool } } 971 | } 972 | { 973 | { 14 16 17 15 { vpool } } 974 | } 975 | { 976 | { 16 9 12 17 { vpool } } 977 | } 978 | { 979 | { 18 19 20 21 { vpool } } 980 | } 981 | { 982 | { 22 23 24 { vpool } } 983 | } 984 | { 985 | { 23 25 26 24 { vpool } } 986 | } 987 | { 988 | { 25 18 21 26 { vpool } } 989 | } 990 | { 991 | { 27 28 29 30 { vpool } } 992 | } 993 | { 994 | { 31 32 33 { vpool } } 995 | } 996 | { 997 | { 32 34 35 33 { vpool } } 998 | } 999 | { 1000 | { 34 27 30 35 { vpool } } 1001 | } 1002 | { 1003 | { 36 37 38 39 { vpool } } 1004 | } 1005 | { 1006 | { 40 41 42 { vpool } } 1007 | } 1008 | { 1009 | { 41 43 44 42 { vpool } } 1010 | } 1011 | { 1012 | { 43 36 39 44 { vpool } } 1013 | } 1014 | { 1015 | { 45 46 47 48 { vpool } } 1016 | } 1017 | { 1018 | { 49 50 51 { vpool } } 1019 | } 1020 | { 1021 | { 50 52 53 51 { vpool } } 1022 | } 1023 | { 1024 | { 52 45 48 53 { vpool } } 1025 | } 1026 | { 1027 | { 54 55 56 57 { vpool } } 1028 | } 1029 | { 1030 | { 58 59 60 { vpool } } 1031 | } 1032 | { 1033 | { 59 61 62 60 { vpool } } 1034 | } 1035 | { 1036 | { 61 54 57 62 { vpool } } 1037 | } 1038 | { 1039 | { 63 64 65 66 { vpool } } 1040 | } 1041 | { 1042 | { 67 68 69 { vpool } } 1043 | } 1044 | { 1045 | { 68 70 71 69 { vpool } } 1046 | } 1047 | { 1048 | { 70 63 66 71 { vpool } } 1049 | } 1050 | { 1051 | { 72 73 74 75 { vpool } } 1052 | } 1053 | { 1054 | { 76 77 78 { vpool } } 1055 | } 1056 | { 1057 | { 77 79 80 78 { vpool } } 1058 | } 1059 | { 1060 | { 79 72 75 80 { vpool } } 1061 | } 1062 | { 1063 | { 81 82 83 84 { vpool } } 1064 | } 1065 | { 1066 | { 85 86 87 { vpool } } 1067 | } 1068 | { 1069 | { 86 88 89 87 { vpool } } 1070 | } 1071 | { 1072 | { 88 81 84 89 { vpool } } 1073 | } 1074 | { 1075 | { 90 91 92 93 { vpool } } 1076 | } 1077 | { 1078 | { 94 95 96 { vpool } } 1079 | } 1080 | { 1081 | { 95 97 98 96 { vpool } } 1082 | } 1083 | { 1084 | { 97 90 93 98 { vpool } } 1085 | } 1086 | { 1087 | { 99 100 101 102 { vpool } } 1088 | } 1089 | { 1090 | { 103 104 105 { vpool } } 1091 | } 1092 | { 1093 | { 104 106 107 105 { vpool } } 1094 | } 1095 | { 1096 | { 106 99 102 107 { vpool } } 1097 | } 1098 | { 1099 | { 108 109 110 111 { vpool } } 1100 | } 1101 | { 1102 | { 112 113 114 { vpool } } 1103 | } 1104 | { 1105 | { 113 115 116 114 { vpool } } 1106 | } 1107 | { 1108 | { 115 108 111 116 { vpool } } 1109 | } 1110 | { 1111 | { 117 118 119 120 { vpool } } 1112 | } 1113 | { 1114 | { 121 122 123 { vpool } } 1115 | } 1116 | { 1117 | { 122 124 125 123 { vpool } } 1118 | } 1119 | { 1120 | { 124 117 120 125 { vpool } } 1121 | } 1122 | { 1123 | { 126 127 128 129 { vpool } } 1124 | } 1125 | { 1126 | { 130 131 132 { vpool } } 1127 | } 1128 | { 1129 | { 131 133 134 132 { vpool } } 1130 | } 1131 | { 1132 | { 133 126 129 134 { vpool } } 1133 | } 1134 | { 1135 | { 135 136 137 138 { vpool } } 1136 | } 1137 | { 1138 | { 139 140 141 { vpool } } 1139 | } 1140 | { 1141 | { 140 142 143 141 { vpool } } 1142 | } 1143 | { 1144 | { 142 135 138 143 { vpool } } 1145 | } 1146 | { 1147 | { 144 145 146 147 { vpool } } 1148 | } 1149 | { 1150 | { 148 149 150 { vpool } } 1151 | } 1152 | { 1153 | { 149 151 152 150 { vpool } } 1154 | } 1155 | { 1156 | { 151 144 147 152 { vpool } } 1157 | } 1158 | { 1159 | { 153 154 155 156 { vpool } } 1160 | } 1161 | { 1162 | { 157 158 159 { vpool } } 1163 | } 1164 | { 1165 | { 158 160 161 159 { vpool } } 1166 | } 1167 | { 1168 | { 160 153 156 161 { vpool } } 1169 | } 1170 | { 1171 | { 162 163 164 165 { vpool } } 1172 | } 1173 | { 1174 | { 166 167 168 { vpool } } 1175 | } 1176 | { 1177 | { 167 169 170 168 { vpool } } 1178 | } 1179 | { 1180 | { 169 162 165 170 { vpool } } 1181 | } 1182 | { 1183 | { 171 172 173 174 { vpool } } 1184 | } 1185 | { 1186 | { 175 176 177 { vpool } } 1187 | } 1188 | { 1189 | { 176 178 179 177 { vpool } } 1190 | } 1191 | { 1192 | { 178 171 174 179 { vpool } } 1193 | } 1194 | { 1195 | { 180 181 182 183 { vpool } } 1196 | } 1197 | { 1198 | { 184 185 186 { vpool } } 1199 | } 1200 | { 1201 | { 185 187 188 186 { vpool } } 1202 | } 1203 | { 1204 | { 187 180 183 188 { vpool } } 1205 | } 1206 | } 1207 | -------------------------------------------------------------------------------- /assets/music/chase.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/music/chase.ogg -------------------------------------------------------------------------------- /assets/music/menu.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/music/menu.ogg -------------------------------------------------------------------------------- /assets/music/pause.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/music/pause.ogg -------------------------------------------------------------------------------- /assets/music/peace.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/music/peace.ogg -------------------------------------------------------------------------------- /assets/sfx/background.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/sfx/background.ogg -------------------------------------------------------------------------------- /assets/sfx/dance.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/sfx/dance.ogg -------------------------------------------------------------------------------- /assets/sfx/flower-open-a.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/sfx/flower-open-a.ogg -------------------------------------------------------------------------------- /assets/sfx/flower-open-b.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/sfx/flower-open-b.ogg -------------------------------------------------------------------------------- /assets/sfx/flower-open-c.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/sfx/flower-open-c.ogg -------------------------------------------------------------------------------- /assets/sfx/thorns.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/sfx/thorns.ogg -------------------------------------------------------------------------------- /assets/sfx/ui-a.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/sfx/ui-a.ogg -------------------------------------------------------------------------------- /assets/sfx/ui-b.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/sfx/ui-b.ogg -------------------------------------------------------------------------------- /assets/sfx/wind-high.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/sfx/wind-high.ogg -------------------------------------------------------------------------------- /assets/sfx/wind-low.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/sfx/wind-low.ogg -------------------------------------------------------------------------------- /assets/shaders/grass.frag: -------------------------------------------------------------------------------- 1 | // Based on code from https://github.com/KhronosGroup/glTF-Sample-Viewer 2 | 3 | #version 120 4 | 5 | #define MAX_LIGHTS 2 6 | 7 | uniform struct p3d_MaterialParameters { 8 | vec4 baseColor; 9 | float roughness; 10 | float metallic; 11 | float refractiveIndex; 12 | } p3d_Material; 13 | 14 | uniform struct p3d_LightSourceParameters { 15 | vec4 position; 16 | vec4 diffuse; 17 | vec4 specular; 18 | vec3 attenuation; 19 | vec3 spotDirection; 20 | float spotCosCutoff; 21 | float spotExponent; 22 | //sampler2DShadow shadowMap; 23 | //mat4 shadowMatrix; 24 | } p3d_LightSource[MAX_LIGHTS]; 25 | 26 | uniform struct p3d_LightModelParameters { 27 | vec4 ambient; 28 | } p3d_LightModel; 29 | 30 | struct FunctionParamters { 31 | float n_dot_l; 32 | float n_dot_v; 33 | float n_dot_h; 34 | float l_dot_h; 35 | float v_dot_h; 36 | float roughness; 37 | float metallic; 38 | vec3 reflection0; 39 | vec3 reflection90; 40 | vec3 diffuse_color; 41 | vec3 specular_color; 42 | }; 43 | 44 | // Give texture slots names 45 | #define p3d_TextureBaseColor p3d_Texture0 46 | #define p3d_TextureMetalRoughness p3d_Texture1 47 | 48 | uniform sampler2D p3d_TextureBaseColor; 49 | uniform sampler2D p3d_TextureMetalRoughness; 50 | uniform vec4 p3d_ColorScale; 51 | 52 | //const vec3 fog_color = vec3(0.31, 0.42, 0.53); 53 | //const vec3 fog_color = vec3(0.6, 1.2, 1.4); 54 | const vec3 fog_color = vec3(0.8, 0.8, 0.9); 55 | 56 | const vec3 F0 = vec3(0.04); 57 | const float PI = 3.141592653589793; 58 | const float SPOTSMOOTH = 0.001; 59 | const float LIGHT_CUTOFF = 0.001; 60 | 61 | varying vec3 v_position; 62 | varying vec4 v_color; 63 | varying vec3 v_normal; 64 | varying vec2 v_texcoord; 65 | 66 | // Schlick's Fresnel approximation 67 | vec3 specular_reflection(FunctionParamters func_params) { 68 | return func_params.reflection0 + (func_params.reflection90 - func_params.reflection0) * pow(clamp(1.0 - func_params.v_dot_h, 0.0, 1.0), 5.0); 69 | } 70 | 71 | // Smith GGX with optional fast sqrt approximation (see https://google.github.io/filament/Filament.md.html#materialsystem/specularbrdf/geometricshadowing(specularg)) 72 | float visibility_occlusion(FunctionParamters func_params) { 73 | float r = func_params.roughness; 74 | float r2 = r * r; 75 | float n_dot_l = func_params.n_dot_l; 76 | float n_dot_v = func_params.n_dot_v; 77 | float ggxv = n_dot_l * (n_dot_v * (1.0 - r) + r); 78 | float ggxl = n_dot_v * (n_dot_l * (1.0 - r) + r); 79 | 80 | return max(0.0, 0.5 / (ggxv + ggxl)); 81 | } 82 | 83 | // GGX/Trowbridge-Reitz 84 | float microfacet_distribution(FunctionParamters func_params) { 85 | float roughness2 = func_params.roughness * func_params.roughness; 86 | float f = (func_params.n_dot_h * roughness2 - func_params.n_dot_h) * func_params.n_dot_h + 1.0; 87 | return roughness2 / (PI * f * f); 88 | } 89 | 90 | // Lambert 91 | vec3 diffuse_function(FunctionParamters func_params) { 92 | return func_params.diffuse_color / PI; 93 | } 94 | 95 | void main() { 96 | vec4 tex_color = texture2D(p3d_TextureBaseColor, v_texcoord); 97 | if (tex_color.a < 0.5) { 98 | discard; 99 | } 100 | 101 | vec4 base_color = p3d_Material.baseColor * tex_color; 102 | 103 | float metallic = clamp(p3d_Material.metallic, 0.0, 1.0); 104 | float perceptual_roughness = clamp(p3d_Material.roughness, 0.0, 1.0); 105 | float alpha_roughness = perceptual_roughness * perceptual_roughness; 106 | 107 | base_color.rgb *= v_color.g; 108 | 109 | base_color.rgb *= min(1.0, v_color.r * v_color.r) * vec3(10, 15, 8) + vec3(1, 1, 1); 110 | 111 | vec3 diffuse_color = (base_color.rgb * (vec3(1.0) - F0)) * (1.0 - metallic); 112 | vec3 spec_color = mix(F0, base_color.rgb, metallic); 113 | vec3 reflection90 = vec3(clamp(max(max(spec_color.r, spec_color.g), spec_color.b) * 50.0, 0.0, 1.0)); 114 | vec3 n = v_normal; 115 | vec3 v = normalize(-v_position); 116 | 117 | vec4 color = vec4(vec3(0.0), base_color.a); 118 | 119 | for (int i = 0; i < MAX_LIGHTS; ++i) { 120 | //const int i = 0; 121 | vec3 lightcol = p3d_LightSource[i].diffuse.rgb; 122 | 123 | //if (dot(lightcol, lightcol) < LIGHT_CUTOFF) { 124 | // continue; 125 | //} 126 | 127 | vec3 l = normalize(p3d_LightSource[i].position.xyz - v_position * p3d_LightSource[i].position.w); 128 | vec3 h = normalize(l + v); 129 | vec3 r = -normalize(reflect(l, n)); 130 | 131 | FunctionParamters func_params; 132 | func_params.n_dot_l = clamp(dot(n, l), 0.001, 1.0); 133 | func_params.n_dot_v = clamp(abs(dot(n, v)), 0.001, 1.0); 134 | func_params.n_dot_h = clamp(dot(n, h), 0.0, 1.0); 135 | func_params.l_dot_h = clamp(dot(l, h), 0.0, 1.0); 136 | func_params.v_dot_h = clamp(dot(v, h), 0.0, 1.0); 137 | func_params.roughness = alpha_roughness; 138 | func_params.metallic = metallic; 139 | func_params.reflection0 = spec_color; 140 | func_params.reflection90 = reflection90; 141 | func_params.diffuse_color = diffuse_color; 142 | func_params.specular_color = spec_color; 143 | 144 | vec3 F = specular_reflection(func_params); 145 | float V = visibility_occlusion(func_params); // V = G / (4 * n_dot_l * n_dot_v) 146 | float D = microfacet_distribution(func_params); 147 | 148 | vec3 diffuse_contrib = (1.0 - F) * diffuse_function(func_params); 149 | vec3 spec_contrib = vec3(F * V * D); 150 | color.rgb += func_params.n_dot_l * lightcol * (diffuse_contrib + spec_contrib * 0.5); 151 | } 152 | 153 | //color.rgb += p3d_LightModel.ambient.rgb; 154 | //color.rgb = mix(color.ggg * vec3(234/255.0, 213/255.0, 105/255.0), color.rgb, v_color.b); 155 | color.rgb = mix(color.ggg * vec3(200/255.0, 152/255.0, 130/255.0), color.rgb, v_color.b); 156 | //color.rgb = mix(color.ggg * 0.75, color.rgb, v_color.b); 157 | 158 | color.a *= v_color.a; 159 | color.rgb *= p3d_ColorScale.rgb; 160 | color.a *= p3d_ColorScale.a; 161 | 162 | color.rgb = mix(fog_color, color.rgb, clamp(exp2(0.0001 * (-v_position.z - 10) * (-v_position.z - 10) * -1.442695f), 0, 1)); 163 | //color.rgb = mix(fog_color, color.rgb, clamp(exp2(0.005 * (-v_position.z - 10) * -1.442695f), 0, 1)); 164 | 165 | gl_FragColor = color; 166 | } 167 | -------------------------------------------------------------------------------- /assets/shaders/grass.vert: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | uniform mat4 p3d_ModelMatrix; 4 | uniform mat4 p3d_ViewMatrix; 5 | uniform mat4 p3d_ModelViewMatrix; 6 | uniform mat4 p3d_ProjectionMatrix; 7 | 8 | attribute vec4 p3d_Vertex; 9 | attribute vec4 p3d_Tangent; 10 | attribute vec2 p3d_MultiTexCoord0; 11 | 12 | uniform float osg_FrameTime; 13 | 14 | uniform vec3 player; 15 | uniform vec3 scale; 16 | uniform sampler2D terrainmap; 17 | uniform sampler2D windmap; 18 | uniform sampler2D satmap; 19 | 20 | varying vec3 v_position; 21 | varying vec4 v_color; 22 | varying vec3 v_normal; 23 | varying vec2 v_texcoord; 24 | 25 | void main() { 26 | mat4 modelmat = p3d_ModelMatrix; 27 | modelmat[3][2] = 0; 28 | vec3 wspos = (modelmat * p3d_Vertex).xyz; 29 | 30 | // normal is stored in xyz, height in alpha 31 | vec4 sample = texture2D(terrainmap, wspos.xy * scale.xy); 32 | float hval = sample.a; 33 | vec3 normal = sample.xyz * 2.0 - 1.0; 34 | 35 | float t = p3d_MultiTexCoord0.y; 36 | float wind = texture2D(windmap, wspos.xy * scale.xy * 4 + vec2(osg_FrameTime * 0.06, 0)).r - 0.5; 37 | float wind_offset = wind * (t * t) * 5; 38 | 39 | v_color.g = hval * 0.333; 40 | v_color.a = 1; 41 | 42 | float sat = texture2D(satmap, wspos.xy * scale.xy).r; 43 | v_color.b = (sat > 0.5) ? 1.0 : sat*2; 44 | 45 | vec2 shove = vec2(0); 46 | float factor = 0; 47 | 48 | vec2 delta = wspos.xy - player.xy; 49 | 50 | //delta *= (p3d_ViewMatrix * vec4(0.13, 0, 0, 0)).xy; 51 | //delta *= (1.0 - dot(normalize(delta), normalize(-playerdir.xy))) * 1; 52 | 53 | //delta.y *= 0.13; 54 | float dist = length(delta); 55 | if (dist < 3) { 56 | delta = normalize(delta); 57 | shove = delta * ((t * t) * (3 - dist) / 3) * 2; 58 | factor = (1.0 - (dist / 3)) * player.z; 59 | 60 | // Hide grass too close to player 61 | if (dist < 2) { 62 | v_color.a = mix(1, dist * 0.8, player.z); 63 | } 64 | } 65 | 66 | wspos.xy += v_color.b * mix(vec2(wind_offset, 0), shove, factor); 67 | normal.x += v_color.b * mix(wind_offset * 0.8, 0, factor); 68 | 69 | wspos.z *= (0.5 + v_color.b * 0.5); 70 | 71 | //wspos.x += wind_offset; 72 | //normal.x += wind_offset * 0.8; 73 | 74 | wspos.z += hval * scale.z; 75 | 76 | v_position = vec3(p3d_ViewMatrix * vec4(wspos, 1)); 77 | v_normal = normalize((p3d_ViewMatrix * vec4(normal, 0)).xyz); 78 | v_texcoord = p3d_MultiTexCoord0; 79 | gl_Position = p3d_ProjectionMatrix * vec4(v_position, 1); 80 | 81 | v_color.r = t; 82 | } 83 | -------------------------------------------------------------------------------- /assets/shaders/object.frag: -------------------------------------------------------------------------------- 1 | // Based on code from https://github.com/KhronosGroup/glTF-Sample-Viewer 2 | 3 | #version 120 4 | 5 | #define MAX_LIGHTS 2 6 | 7 | uniform struct p3d_MaterialParameters { 8 | vec4 baseColor; 9 | float roughness; 10 | float metallic; 11 | float refractiveIndex; 12 | } p3d_Material; 13 | 14 | uniform struct p3d_LightSourceParameters { 15 | vec4 position; 16 | vec4 diffuse; 17 | vec4 specular; 18 | vec3 attenuation; 19 | vec3 spotDirection; 20 | float spotCosCutoff; 21 | float spotExponent; 22 | //sampler2DShadow shadowMap; 23 | //mat4 shadowMatrix; 24 | } p3d_LightSource[MAX_LIGHTS]; 25 | 26 | uniform struct p3d_LightModelParameters { 27 | vec4 ambient; 28 | } p3d_LightModel; 29 | 30 | uniform vec4 p3d_ColorScale; 31 | 32 | struct FunctionParamters { 33 | float n_dot_l; 34 | float n_dot_v; 35 | float n_dot_h; 36 | float l_dot_h; 37 | float v_dot_h; 38 | float roughness; 39 | float metallic; 40 | vec3 reflection0; 41 | vec3 reflection90; 42 | vec3 diffuse_color; 43 | vec3 specular_color; 44 | }; 45 | 46 | //const vec3 fog_color = vec3(0.6, 1.2, 1.4); 47 | const vec3 fog_color = vec3(0.8, 0.8, 0.9); 48 | 49 | // Give texture slots names 50 | #define p3d_TextureBaseColor p3d_Texture0 51 | #define p3d_TextureMetalRoughness p3d_Texture1 52 | #define p3d_TextureNormal p3d_Texture2 53 | 54 | uniform sampler2D p3d_TextureBaseColor; 55 | uniform sampler2D p3d_TextureMetalRoughness; 56 | uniform sampler2D p3d_TextureNormal; 57 | 58 | const vec3 F0 = vec3(0.04); 59 | const float PI = 3.141592653589793; 60 | const float SPOTSMOOTH = 0.001; 61 | const float LIGHT_CUTOFF = 0.001; 62 | 63 | varying vec3 v_position; 64 | varying vec4 v_color; 65 | varying vec2 v_texcoord; 66 | varying vec3 v_normal; 67 | 68 | // Schlick's Fresnel approximation 69 | vec3 specular_reflection(FunctionParamters func_params) { 70 | return func_params.reflection0 + (func_params.reflection90 - func_params.reflection0) * pow(clamp(1.0 - func_params.v_dot_h, 0.0, 1.0), 5.0); 71 | } 72 | 73 | // Smith GGX with optional fast sqrt approximation (see https://google.github.io/filament/Filament.md.html#materialsystem/specularbrdf/geometricshadowing(specularg)) 74 | float visibility_occlusion(FunctionParamters func_params) { 75 | float r = func_params.roughness; 76 | float r2 = r * r; 77 | float n_dot_l = func_params.n_dot_l; 78 | float n_dot_v = func_params.n_dot_v; 79 | float ggxv = n_dot_l * (n_dot_v * (1.0 - r) + r); 80 | float ggxl = n_dot_v * (n_dot_l * (1.0 - r) + r); 81 | 82 | return max(0.0, 0.5 / (ggxv + ggxl)); 83 | } 84 | 85 | // GGX/Trowbridge-Reitz 86 | float microfacet_distribution(FunctionParamters func_params) { 87 | float roughness2 = func_params.roughness * func_params.roughness; 88 | float f = (func_params.n_dot_h * roughness2 - func_params.n_dot_h) * func_params.n_dot_h + 1.0; 89 | return roughness2 / (PI * f * f); 90 | } 91 | 92 | // Lambert 93 | vec3 diffuse_function(FunctionParamters func_params) { 94 | return func_params.diffuse_color / PI; 95 | } 96 | 97 | void main() { 98 | if (v_color.a < 0.5) { 99 | discard; 100 | } 101 | 102 | vec4 metal_rough = texture2D(p3d_TextureMetalRoughness, v_texcoord); 103 | float metallic = clamp(p3d_Material.metallic * metal_rough.b, 0.0, 1.0); 104 | float perceptual_roughness = clamp(p3d_Material.roughness * metal_rough.g, 0.0, 1.0); 105 | float alpha_roughness = perceptual_roughness * perceptual_roughness; 106 | vec4 base_color = p3d_Material.baseColor * v_color * p3d_ColorScale * texture2D(p3d_TextureBaseColor, v_texcoord); 107 | vec3 diffuse_color = (base_color.rgb * (vec3(1.0) - F0)) * (1.0 - metallic); 108 | vec3 spec_color = mix(F0, base_color.rgb, metallic); 109 | vec3 reflection90 = vec3(clamp(max(max(spec_color.r, spec_color.g), spec_color.b) * 50.0, 0.0, 1.0)); 110 | vec3 n = v_normal; 111 | vec3 v = normalize(-v_position); 112 | 113 | vec4 color = vec4(vec3(0.0), base_color.a); 114 | 115 | for (int i = 0; i < 2; ++i) { 116 | vec3 lightcol = p3d_LightSource[i].diffuse.rgb; 117 | 118 | //if (dot(lightcol, lightcol) < LIGHT_CUTOFF) { 119 | // continue; 120 | //} 121 | 122 | vec3 l = normalize(p3d_LightSource[i].position.xyz - v_position * p3d_LightSource[i].position.w); 123 | vec3 h = normalize(l + v); 124 | vec3 r = -normalize(reflect(l, n)); 125 | 126 | FunctionParamters func_params; 127 | func_params.n_dot_l = clamp(dot(n, l), 0.001, 1.0); 128 | func_params.n_dot_v = clamp(abs(dot(n, v)), 0.001, 1.0); 129 | func_params.n_dot_h = clamp(dot(n, h), 0.0, 1.0); 130 | func_params.l_dot_h = clamp(dot(l, h), 0.0, 1.0); 131 | func_params.v_dot_h = clamp(dot(v, h), 0.0, 1.0); 132 | func_params.roughness = alpha_roughness; 133 | func_params.metallic = metallic; 134 | func_params.reflection0 = spec_color; 135 | func_params.reflection90 = reflection90; 136 | func_params.diffuse_color = diffuse_color; 137 | func_params.specular_color = spec_color; 138 | 139 | vec3 F = specular_reflection(func_params); 140 | float V = visibility_occlusion(func_params); // V = G / (4 * n_dot_l * n_dot_v) 141 | float D = microfacet_distribution(func_params); 142 | 143 | vec3 diffuse_contrib = (1.0 - F) * diffuse_function(func_params); 144 | vec3 spec_contrib = vec3(F * V * D); 145 | color.rgb += func_params.n_dot_l * lightcol * (diffuse_contrib + spec_contrib); 146 | } 147 | 148 | //color.rgb = mix(fog_color, color.rgb, clamp(exp2(0.005 * (-v_position.z - 10) * -1.442695f), 0, 1)); 149 | color.rgb = mix(fog_color, color.rgb, clamp(exp2(0.0001 * (-v_position.z - 10) * (-v_position.z - 10) * -1.442695f), 0, 1)); 150 | 151 | gl_FragColor = color; 152 | } 153 | -------------------------------------------------------------------------------- /assets/shaders/object.vert: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | uniform mat4 p3d_ModelViewProjectionMatrix; 4 | uniform mat4 p3d_ModelViewMatrix; 5 | uniform mat3 p3d_NormalMatrix; 6 | 7 | attribute vec4 p3d_Vertex; 8 | attribute vec4 p3d_Color; 9 | attribute vec3 p3d_Normal; 10 | attribute vec2 p3d_MultiTexCoord0; 11 | 12 | varying vec3 v_position; 13 | varying vec4 v_color; 14 | varying vec2 v_texcoord; 15 | varying vec3 v_normal; 16 | 17 | void main() { 18 | v_position = vec3(p3d_ModelViewMatrix * p3d_Vertex); 19 | v_color = p3d_Color; 20 | v_normal = normalize(p3d_NormalMatrix * p3d_Normal); 21 | v_texcoord = p3d_MultiTexCoord0; 22 | 23 | gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; 24 | } 25 | -------------------------------------------------------------------------------- /assets/shaders/tree.vert: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | uniform mat4 p3d_ProjectionMatrix; 4 | uniform mat4 p3d_ModelMatrix; 5 | uniform mat4 p3d_ViewMatrix; 6 | uniform mat3 p3d_NormalMatrix; 7 | 8 | uniform vec3 scale; 9 | uniform sampler2D windmap; 10 | uniform sampler2D satmap; 11 | 12 | uniform float osg_FrameTime; 13 | 14 | attribute vec4 p3d_Vertex; 15 | attribute vec4 p3d_Color; 16 | attribute vec3 p3d_Normal; 17 | 18 | varying vec3 v_position; 19 | varying vec4 v_color; 20 | varying vec2 v_texcoord; 21 | varying vec3 v_normal; 22 | 23 | void main() { 24 | vec3 wspos_model = p3d_ModelMatrix[3].xyz; 25 | vec3 wspos = (p3d_ModelMatrix * p3d_Vertex).xyz; 26 | 27 | float sat = texture2D(satmap, wspos_model.xy * scale.xy).r; 28 | sat = (sat > 0.5) ? 1.0 : sat*2; 29 | 30 | v_color.a = 1; 31 | 32 | if (p3d_Color.g > 0.7) { 33 | v_color.a = sat + 0.25 - mod(wspos.x + wspos.y * 10 + wspos.z * 100, 0.5); 34 | } 35 | 36 | 37 | float t = p3d_Vertex.z / 5; 38 | 39 | float wind = texture2D(windmap, wspos.xy * scale.xy * 4 + vec2(osg_FrameTime * 0.06, 0)).r - 0.5; 40 | float wind_offset = wind * t * t; 41 | wspos.x += wind_offset; 42 | 43 | v_position = vec3(p3d_ViewMatrix * vec4(wspos, 1)); 44 | v_normal = normalize(p3d_NormalMatrix * p3d_Normal); 45 | v_texcoord = vec2(0, 0); 46 | 47 | v_color.rgb = p3d_Color.rgb; 48 | v_color.rgb = mix(v_color.ggg * vec3(188/255.0, 152/255.0, 101/255.0), v_color.rgb, sat); 49 | 50 | 51 | gl_Position = p3d_ProjectionMatrix * vec4(v_position, 1); 52 | } 53 | -------------------------------------------------------------------------------- /assets/textures/heightmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/textures/heightmap.png -------------------------------------------------------------------------------- /assets/textures/skyrender0001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/textures/skyrender0001.png -------------------------------------------------------------------------------- /assets/textures/skyrender0002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/textures/skyrender0002.png -------------------------------------------------------------------------------- /assets/textures/skyrender0003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/textures/skyrender0003.png -------------------------------------------------------------------------------- /assets/textures/skyrender0004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/textures/skyrender0004.png -------------------------------------------------------------------------------- /assets/textures/skyrender0005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/textures/skyrender0005.png -------------------------------------------------------------------------------- /assets/textures/skyrender0006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/textures/skyrender0006.png -------------------------------------------------------------------------------- /assets/textures/tuft0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/textures/tuft0.png -------------------------------------------------------------------------------- /assets/textures/tuft1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/textures/tuft1.png -------------------------------------------------------------------------------- /assets/textures/tuft2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/textures/tuft2.png -------------------------------------------------------------------------------- /assets/textures/wind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdb/pollen/8ea27df00340c86eab895c5c0032a045a19a92f9/assets/textures/wind.png -------------------------------------------------------------------------------- /game/__init__.py: -------------------------------------------------------------------------------- 1 | from direct.showbase.ShowBase import ShowBase 2 | import direct.gui.DirectGuiGlobals as DGG 3 | from direct.actor.Actor import Actor 4 | from direct.interval.IntervalGlobal import Sequence, Parallel, Wait, Func, LerpFunctionInterval 5 | from panda3d import core 6 | import sys 7 | 8 | import simplepbr 9 | 10 | 11 | from .audio import Music 12 | from .menu import Menu 13 | from .world import World 14 | 15 | 16 | QUALITY_HIGH = 3 17 | QUALITY_MEDIUM = 2 18 | QUALITY_LOW = 1 19 | QUALITY_POTATO = 0 20 | 21 | 22 | class Game(ShowBase): 23 | def __init__(self): 24 | main_dir = core.ExecutionEnvironment.get_environment_variable("MAIN_DIR") 25 | main_dir = core.Filename.from_os_specific(main_dir) 26 | core.load_prc_file(core.Filename(main_dir, "settings.prc")) 27 | 28 | ShowBase.__init__(self) 29 | self.disable_mouse() 30 | 31 | # Workaround for failure to load simplepbr shaders on macOS 32 | if getattr(sys, 'frozen', False) and sys.platform == 'darwin': 33 | simplepbr.__file__ = self.main_dir + '/' 34 | 35 | self.assume_gamepad = False 36 | 37 | DGG.setDefaultRolloverSound(loader.load_sfx('sfx/ui-a.ogg')) 38 | DGG.setDefaultClickSound(loader.load_sfx('sfx/ui-b.ogg')) 39 | 40 | base.setBackgroundColor((0, 0, 0, 1)) 41 | 42 | self.quality_menu = Menu('quality.', [ 43 | ('high.', self.setup_high), 44 | ('medium.', self.setup_medium), 45 | ('low.', self.setup_low), 46 | ('none.', self.setup_potato), 47 | ]) 48 | self.quality_menu.show() 49 | 50 | self.gamepads = set() 51 | 52 | for dev in self.devices.get_devices(core.InputDevice.DeviceClass.gamepad): 53 | self._device_connected(dev) 54 | self.assume_gamepad = False 55 | 56 | self.accept('connect-device', self._device_connected) 57 | self.accept('disconnect-device', self._device_disconnected) 58 | 59 | def _device_connected(self, device): 60 | if device.device_class == core.InputDevice.DeviceClass.gamepad and device not in self.gamepads: 61 | print("Detected", device) 62 | self.gamepads.add(device) 63 | base.attach_input_device(device, "gamepad") 64 | 65 | # User plugged in gamepad during play, assume they intend to use it 66 | self.assume_gamepad = True 67 | messenger.send("switch-gamepad-controls") 68 | 69 | def _device_disconnected(self, device): 70 | if device in self.gamepads: 71 | print("Disconnected", device) 72 | self.gamepads.discard(device) 73 | base.detach_input_device(device) 74 | 75 | if not self.gamepads: 76 | self.assume_gamepad = False 77 | messenger.send("switch-mouse-controls") 78 | 79 | def setup_high(self): 80 | base.camLens.set_far(128) 81 | core.load_prc_file_data("", "lod-fade-time 2.0") 82 | self.setup(QUALITY_HIGH) 83 | 84 | def setup_medium(self): 85 | base.camLens.set_far(125) 86 | core.load_prc_file_data("", "lod-fade-time 2.0") 87 | self.setup(QUALITY_MEDIUM) 88 | 89 | def setup_low(self): 90 | base.camLens.set_far(100) 91 | core.load_prc_file_data("", "lod-fade-time 1.5") 92 | self.setup(QUALITY_LOW) 93 | 94 | def setup_potato(self): 95 | base.camLens.set_far(75) 96 | core.load_prc_file_data("", "lod-fade-time 1.0") 97 | self.setup(QUALITY_POTATO) 98 | 99 | def setup(self, quality): 100 | self.quality_menu.hide() 101 | base.quality = quality 102 | 103 | is_fullscreen = self.win.get_properties().fullscreen 104 | 105 | self.main_menu = Menu('pollen.', [ 106 | ('begin.', self.start_game), 107 | ('no music.', self.toggle_music), 108 | ('window.' if is_fullscreen else 'fullscreen.', self.toggle_fullscreen), 109 | ('leave.', self.stop_game), 110 | ]) 111 | 112 | self.pause_menu = Menu('paused.', [ 113 | ('resume.', self.resume), 114 | ('no music.', self.toggle_music), 115 | ('stop.', self.stop_game), 116 | ]) 117 | 118 | base.paused = False 119 | 120 | #base.cam.set_pos(-3, 0, 10) 121 | base.cam.set_pos(0, 0, 10) 122 | base.cam.set_hpr(0, 5, 0) 123 | 124 | if base.quality >= QUALITY_HIGH: 125 | samples = 8 126 | elif base.quality >= QUALITY_MEDIUM: 127 | samples = 4 128 | else: 129 | samples = 0 130 | simplepbr.init(msaa_samples=samples, max_lights=2) 131 | 132 | self.world = World() 133 | 134 | self.accept('f12', self.screenshot) 135 | #self.accept('1', self.oobeCull) 136 | self.starting = False 137 | self.started = False 138 | self.music_on = True 139 | 140 | self.main_menu.show() 141 | self.accept('gamepad-start', self.start_game) 142 | 143 | self.setBackgroundColor((0.6, 1.0, 1.4, 1.0)) 144 | 145 | def stop_game(self): 146 | self.main_menu.hide() 147 | self.pause_menu.hide() 148 | base.transitions.setFadeColor(0, 0, 0) 149 | base.transitions.getFadeOutIval(1.0).start() 150 | taskMgr.doMethodLater(1.5, lambda task: sys.exit(), 'exit') 151 | 152 | def start_game(self): 153 | if self.starting: 154 | return 155 | 156 | self.ignore('gamepad-start') 157 | 158 | self.starting = True 159 | 160 | self.main_menu.hide() 161 | #self.world.start_game() 162 | 163 | Sequence( 164 | base.cam.hprInterval(2, hpr=(0, 30, 0), blendType="easeInOut"), 165 | Func(self.start_starting), 166 | #Func(self.finish_starting), 167 | ).start() 168 | 169 | def start_starting(self): 170 | self.world.finish_loading() 171 | 172 | old_hpr = base.cam.get_hpr() 173 | base.cam.look_at(base.camera.get_parent(), (0, 0, 0)) 174 | base.cam.set_h(old_hpr[0]) 175 | cam_hpr = base.cam.get_hpr() 176 | base.cam.set_hpr(old_hpr) 177 | 178 | old_hpr = base.camera.get_hpr() 179 | base.camera.look_at(0, 0, 0) 180 | camera_hpr = base.camera.get_hpr() 181 | base.camera.set_hpr(old_hpr) 182 | 183 | #self.world.activate() 184 | self.started = True 185 | base.graphicsEngine.render_frame() 186 | base.graphicsEngine.render_frame() 187 | Sequence( 188 | Wait(0.5), 189 | Parallel( 190 | LerpFunctionInterval(base.camLens.set_fov, 2.0, fromData=base.camLens.get_fov()[0], toData=80, blendType='easeIn'), 191 | base.cam.hprInterval(2, hpr=(0, 0, 0), blendType="easeInOut"), 192 | base.camera.hprInterval(2, hpr=camera_hpr, blendType="easeInOut"), 193 | ), 194 | Func(self.finish_starting), 195 | ).start() 196 | 197 | def finish_starting(self): 198 | self.started = True 199 | self.world.activate() 200 | #self.accept('2', self.world.press_2) 201 | #self.accept('3', self.world.press_3) 202 | #self.accept('4', self.world.ending) 203 | #self.accept('p', self.world.print_pos) 204 | 205 | self.accept('pause', self.pause) 206 | self.accept('escape', self.pause) 207 | self.accept('gamepad-back', self.pause) 208 | self.accept('gamepad-start', self.resume) 209 | 210 | def pause(self): 211 | if self.paused: 212 | return 213 | self.world.music[Music].play('pause') 214 | self.paused = True 215 | self.pause_menu.show() 216 | 217 | self.win.request_properties(core.WindowProperties( 218 | mouse_mode=core.WindowProperties.M_absolute, 219 | cursor_hidden=False, 220 | )) 221 | 222 | def resume(self): 223 | if self.paused: 224 | self.pause_menu.hide() 225 | self.paused = False 226 | 227 | # Switch to whatever method was used when navigating the UI 228 | if self.assume_gamepad: 229 | messenger.send('switch-gamepad-controls') 230 | else: 231 | messenger.send('switch-mouse-controls') 232 | 233 | def toggle_fullscreen(self): 234 | is_fullscreen = self.win.get_properties().fullscreen 235 | if is_fullscreen: 236 | print("Disabling fullscreen") 237 | size = core.WindowProperties.get_default().size 238 | self.win.request_properties(core.WindowProperties(fullscreen=False, origin=(-2, -2), size=size)) 239 | self.main_menu.buttons[2]['text'] = 'fullscreen.' 240 | else: 241 | print("Enabling fullscreen") 242 | size = self.pipe.get_display_width(), self.pipe.get_display_height() 243 | self.win.request_properties(core.WindowProperties(fullscreen=True, size=size)) 244 | self.main_menu.buttons[2]['text'] = 'window.' 245 | 246 | def toggle_music(self): 247 | if self.music_on: 248 | print("Disabling music") 249 | self.enable_music(False) 250 | self.main_menu.buttons[1]['text'] = 'yes music.' 251 | self.pause_menu.buttons[1]['text'] = 'yes music.' 252 | self.music_on = False 253 | else: 254 | print("Enabling music") 255 | self.enable_music(True) 256 | self.main_menu.buttons[1]['text'] = 'no music.' 257 | self.pause_menu.buttons[1]['text'] = 'no music.' 258 | self.music_on = True 259 | 260 | 261 | def main(): 262 | game = Game() 263 | #game.movie(duration=15, fps=30) 264 | game.run() 265 | -------------------------------------------------------------------------------- /game/animation.py: -------------------------------------------------------------------------------- 1 | from wecs.core import Component, System, and_filter 2 | from panda3d import core 3 | from dataclasses import field 4 | 5 | from direct.interval.IntervalGlobal import Sequence, Parallel, Func 6 | 7 | 8 | @Component() 9 | class Character: 10 | state: str 11 | 12 | states: dict = field(default_factory=dict) 13 | play_rates: dict = field(default_factory=dict) 14 | transitions: dict = field(default_factory=dict) 15 | subparts: dict = field(default_factory=dict) 16 | 17 | _state = None 18 | _state_paths: dict = field(default_factory=dict) 19 | _state_actors: dict = field(default_factory=dict) 20 | 21 | 22 | class AnimationPlayer(System): 23 | entity_filters = { 24 | 'character': and_filter([Character]), 25 | } 26 | 27 | def change_state(self, char, old_state, new_state): 28 | old_state_def = char.states.get(old_state, {}) 29 | new_state_def = char.states[new_state] 30 | 31 | #char._actor.stop() 32 | if old_state is not None: 33 | char._state_paths[old_state].stash() 34 | 35 | transition = char.transitions.get((old_state, new_state)) 36 | if transition: 37 | char._actor.unstash() 38 | 39 | for part, anim in transition.items(): 40 | Sequence( 41 | char._actor.actor_interval(anim, partName=part, playRate=char.play_rates[new_state]), 42 | Func(lambda: char._actor.stash()), 43 | Func(lambda: char._state_paths[new_state].unstash()), 44 | ).start() 45 | else: 46 | char._actor.stash() 47 | char._state_paths[new_state].unstash() 48 | 49 | def update(self, entities_by_filter): 50 | if base.paused: 51 | return 52 | 53 | for entity in entities_by_filter['character']: 54 | char = entity[Character] 55 | 56 | if char._state != char.state: 57 | self.change_state(char, char._state, char.state) 58 | char._state = char.state 59 | -------------------------------------------------------------------------------- /game/audio.py: -------------------------------------------------------------------------------- 1 | from direct.showbase.Audio3DManager import Audio3DManager 2 | from direct.interval.SoundInterval import SoundInterval 3 | from wecs.core import Component, System, and_filter 4 | from panda3d import core 5 | from dataclasses import field 6 | 7 | from .camera import Camera 8 | from .terrain import TerrainObject 9 | 10 | 11 | MUSIC_FADE_TIME = 2.0 12 | 13 | 14 | @Component() 15 | class Listener: 16 | pass 17 | 18 | 19 | @Component() 20 | class Music: 21 | songs: list 22 | current: str = None 23 | 24 | _playing = None 25 | _active = None 26 | 27 | def play(self, song): 28 | assert song in self.songs 29 | self.current = song 30 | 31 | 32 | @Component() 33 | class SfxPlayer: 34 | sounds: list 35 | loop: bool = False 36 | volume: float = 1.0 37 | 38 | _queued: set = field(default_factory=set) 39 | 40 | def play(self, sound): 41 | self._queued.add(sound) 42 | 43 | 44 | class AudioSystem(System): 45 | entity_filters = { 46 | 'listener': and_filter([Listener]), 47 | 'sfx': and_filter([SfxPlayer]), 48 | 'sfx3d': and_filter([SfxPlayer, TerrainObject]), 49 | 'music': and_filter([Music]), 50 | } 51 | 52 | def __init__(self): 53 | System.__init__(self) 54 | 55 | self.audio3d = Audio3DManager(base.sfxManagerList[0], None) 56 | #self.audio3d.set_distance_factor(1.0) 57 | #self.audio3d.set_drop_off_factor(0.0) 58 | 59 | # We need cross fading. 60 | base.musicManager.set_concurrent_sound_limit(2) 61 | 62 | def init_entity(self, filter, entity): 63 | if filter == 'listener': 64 | self.init_listener(entity) 65 | elif filter == 'sfx': 66 | self.init_sfx(entity) 67 | elif filter == 'music': 68 | self.init_music(entity) 69 | 70 | def init_listener(self, entity): 71 | if Camera in entity: 72 | root = entity[Camera]._root 73 | elif TerrainObject in entity: 74 | root = entity[TerrainObject]._root 75 | else: 76 | return 77 | 78 | listener = entity[Listener] 79 | listener._root = root 80 | listener._prev_pos = root.get_net_transform().get_pos() 81 | self.audio3d.attach_listener(root) 82 | 83 | def init_sfx(self, entity): 84 | player = entity[SfxPlayer] 85 | player._sfx = {} 86 | 87 | if TerrainObject in entity: 88 | root = entity[TerrainObject]._root 89 | else: 90 | root = None 91 | 92 | for sound in player.sounds: 93 | path = core.Filename('sfx', sound + '.ogg') 94 | sfx = self.audio3d.audio_manager.get_sound(path, positional=(root is not None)) 95 | if player.loop: 96 | sfx.set_loop(True) 97 | player._sfx[sound] = sfx 98 | player._root = root 99 | 100 | def init_music(self, entity): 101 | music = entity[Music] 102 | music._songs = {} 103 | music._prev_songs = set() 104 | 105 | for song in music.songs: 106 | music._songs[song] = loader.load_music(core.Filename('music', song + '.ogg')) 107 | music._songs[song].set_loop(True) 108 | music._songs[song].set_volume(0) 109 | 110 | def play_sfx(self, entity, sound): 111 | player = entity[SfxPlayer] 112 | 113 | player._sfx[sound].set_volume(player.volume) 114 | player._sfx[sound].play() 115 | 116 | if player._root: 117 | pos = player._root.get_pos(self.audio3d.root) 118 | player._sfx[sound].set_3d_attributes(pos[0], pos[1], pos[2], 0, 0, 0) 119 | 120 | #print("Playing sound", sound) 121 | 122 | def change_music(self, entity, old_song, new_song): 123 | music = entity[Music] 124 | 125 | old = music._songs.get(old_song) 126 | if old: 127 | music._prev_songs.add(old) 128 | 129 | new = music._songs.get(new_song) 130 | if old and new: 131 | new.set_time(old.get_time()) 132 | 133 | if new: 134 | new.play() 135 | music._active = new 136 | music._prev_songs.discard(new) 137 | 138 | print("Changing music to", new_song) 139 | 140 | def update(self, entities_by_filter): 141 | #for entity in entities_by_filter['listener']: 142 | # listener = entity[Listener] 143 | # pos = listener._root.get_net_transform().get_pos() 144 | # vel = (pos - listener._prev_pos) / globalClock.dt 145 | # self.audio3d.set_listener_velocity(vel) 146 | # listener._prev_pos = pos 147 | 148 | for entity in entities_by_filter['sfx']: 149 | player = entity[SfxPlayer] 150 | for sound in player._queued: 151 | self.play_sfx(entity, sound) 152 | player._queued.clear() 153 | 154 | for entity in entities_by_filter['music']: 155 | music = entity[Music] 156 | 157 | # Fade in active song 158 | if music._active: 159 | volume = music._active.get_volume() 160 | if volume < 1.0: 161 | volume = min(1.0, volume + globalClock.dt / MUSIC_FADE_TIME) 162 | music._active.set_volume(volume) 163 | 164 | # Fade out inactive songs 165 | for song in tuple(music._prev_songs): 166 | volume = song.get_volume() 167 | if volume > 0.0: 168 | volume = max(0.0, volume - globalClock.dt / MUSIC_FADE_TIME) 169 | song.set_volume(volume) 170 | if volume <= 0.0: 171 | song.stop() 172 | music._prev_songs.discard(song) 173 | 174 | if music._playing != music.current: 175 | self.change_music(entity, music._playing, music.current) 176 | music._playing = music.current 177 | 178 | def destroy_entity(self, filter, entity): 179 | pass 180 | -------------------------------------------------------------------------------- /game/camera.py: -------------------------------------------------------------------------------- 1 | from wecs.core import Entity, Component, System 2 | from wecs.core import and_filter 3 | 4 | from panda3d import core 5 | 6 | from .terrain import TerrainObject 7 | from .general import Speed 8 | 9 | import math 10 | 11 | 12 | def smoothstep(x): 13 | return x * x * x * (x * (x * 6 - 15) + 10) 14 | 15 | 16 | @Component() 17 | class Camera: 18 | target: Entity 19 | distance: float = 5.0 20 | elevation: float = 2.5 21 | fov: float = 80 22 | fast_fov: float = 110 23 | far: float = 128 24 | active: bool = False 25 | 26 | 27 | class CameraSystem(System): 28 | entity_filters = { 29 | 'camera': and_filter([Camera]), 30 | } 31 | 32 | def __init__(self): 33 | System.__init__(self) 34 | 35 | def init_entity(self, filter_name, entity): 36 | camera = entity[Camera] 37 | target_obj = camera.target[TerrainObject] 38 | 39 | #cam_node = core.Camera(entity._uid.name) 40 | cam_node = base.cam.node() 41 | camera._lens = cam_node.get_lens() 42 | #camera._lens.set_fov(camera.fov) 43 | #camera._lens.set_far(camera.far) 44 | #camera._root = target_obj._root.attach_new_node(cam_node) 45 | camera._root = base.camera 46 | camera._root.wrt_reparent_to(target_obj._root) 47 | camera._root.set_pos(0, -camera.distance, camera.elevation) 48 | #print(-camera.distance, camera.elevation) 49 | #camera._root.look_at(0, 0, 0) 50 | 51 | def button_pressed(self, entity, action): 52 | controls = entity[Controls] 53 | controls._states[action] = 1.0 54 | 55 | def button_released(self, entity, action): 56 | controls = entity[Controls] 57 | controls._states[action] = 0.0 58 | 59 | def update(self, entities_by_filter): 60 | if base.paused: 61 | return 62 | 63 | for entity in entities_by_filter['camera']: 64 | camera = entity[Camera] 65 | 66 | if not camera.active: 67 | continue 68 | 69 | fov = camera.fov 70 | 71 | if Speed in camera.target: 72 | speed = camera.target[Speed] 73 | if speed.max is not None: 74 | t = (speed.current - speed.min) / (speed.max - speed.min) 75 | t = smoothstep(t) 76 | fov = t * camera.fast_fov + (1 - t) * fov 77 | 78 | if base.cam.parent.name != "dolly" and base.started: 79 | if fov > 0: 80 | camera._lens.set_fov(fov) 81 | h = base.cam.get_h() 82 | base.cam.look_at(camera._root.get_parent(), (0, 0, 0)) 83 | base.cam.set_h(h) 84 | -------------------------------------------------------------------------------- /game/collision.py: -------------------------------------------------------------------------------- 1 | from wecs.core import Component, System, and_filter 2 | from panda3d import core 3 | from dataclasses import field 4 | 5 | from .terrain import TerrainObject 6 | from .animation import Character 7 | from .camera import Camera 8 | from .controls import Controls 9 | 10 | 11 | GRAVITY = 1.0 12 | 13 | 14 | @Component() 15 | class Collider: 16 | solid: core.CollisionSolid = None 17 | from_mask: int = 0 18 | joint_from_mask: int = 0 19 | into_mask: int = 1 20 | tangible: bool = True 21 | 22 | 23 | @Component() 24 | class GeomCollider: 25 | into_mask: int = 1 26 | 27 | 28 | class CollisionDetectionSystem(System): 29 | entity_filters = { 30 | 'collider': and_filter([Collider]), 31 | 'geomcollider': and_filter([GeomCollider]), 32 | } 33 | 34 | def __init__(self): 35 | System.__init__(self) 36 | 37 | self.traverser = core.CollisionTraverser() 38 | #self.traverser.set_respect_prev_transform(True) 39 | #self.traverser.show_collisions(base.render) 40 | 41 | self.handler = core.CollisionHandlerPusher() 42 | self.handler.add_in_pattern('%fn-into-%in') 43 | self.handler.add_out_pattern('%fn-outof-%in') 44 | self.handler.add_in_pattern('%fn-into-any') 45 | self.handler.add_out_pattern('%fn-outof-any') 46 | self.handler.horizontal = False 47 | 48 | self.queue = core.CollisionHandlerQueue() 49 | 50 | base.accept('player-into-any', self._enter_swarm) 51 | base.accept('player-outof-any', self._leave_swarm) 52 | 53 | base.accept('butterfly-into-any', self._enter_butterfly) 54 | base.accept('butterfly-outof-any', self._leave_butterfly) 55 | 56 | self.player_obj = None 57 | 58 | self.joint_colliders = [] 59 | 60 | self._times_swarm_activated = 0 61 | 62 | def init_entity(self, filter, entity): 63 | if TerrainObject in entity: 64 | path = entity[TerrainObject]._root 65 | elif Camera in entity: 66 | path = entity[Camera]._root 67 | else: 68 | path = None 69 | 70 | if filter == 'geomcollider': 71 | path.get_child(0).set_collide_mask(entity[GeomCollider].into_mask) 72 | return 73 | 74 | collider = entity[Collider] 75 | 76 | if not collider.solid: 77 | bounds = path.get_child(0).get_bounds() 78 | collider.solid = core.CollisionSphere(bounds.center, bounds.radius) 79 | 80 | collider.solid.tangible = collider.tangible 81 | 82 | cnode = core.CollisionNode(entity._uid.name) 83 | cnode.add_solid(collider.solid) 84 | cnode.set_from_collide_mask(collider.from_mask) 85 | cnode.set_into_collide_mask(collider.into_mask) 86 | cpath = path.attach_new_node(cnode) 87 | #cpath.show() 88 | 89 | collider._cpath = cpath 90 | 91 | cnode.set_python_tag('entity', entity) 92 | 93 | if collider.from_mask: 94 | if entity._uid.name == "camera": 95 | self.traverser.add_collider(cpath, self.queue) 96 | else: 97 | self.traverser.add_collider(cpath, self.handler) 98 | 99 | if collider.tangible: 100 | self.handler.add_collider(cpath, path) 101 | 102 | if TerrainObject in entity: 103 | self.player_obj = entity[TerrainObject] 104 | 105 | if collider.joint_from_mask and Character in entity: 106 | actor = entity[Character]._state_actors['fly'] 107 | self.actor = actor 108 | root = actor.expose_joint(None, "butterfly", "root") 109 | self.root = root 110 | 111 | for joint in actor.get_joints(): 112 | if joint.name.startswith("butterfly."): 113 | np = actor.expose_joint(root.attach_new_node(joint.name), "butterfly", joint.name, localTransform=True) 114 | cpath = np.attach_new_node(core.CollisionNode("butterfly")) 115 | cpath.node().set_from_collide_mask(collider.joint_from_mask) 116 | cpath.node().set_into_collide_mask(collider.into_mask) 117 | cpath.node().add_solid(core.CollisionSphere((0, 0.4, 0.3), 0.7)) 118 | cpath.node().set_tag("joint", joint.name) 119 | cpath.node().modify_solid(0).set_tangible(False) 120 | #cpath.show() 121 | self.joint_colliders.append(cpath) 122 | #self.traverser.add_collider(cpath, self.handler) 123 | 124 | def destroy_entity(self, filter, entity, removed={}): 125 | collider = removed.get(Collider) 126 | if not collider: 127 | return 128 | 129 | cpath = collider._cpath 130 | 131 | if collider.from_mask: 132 | if entity._uid.name == "camera": 133 | self.traverser.remove_collider(cpath) 134 | else: 135 | self.traverser.remove_collider(cpath) 136 | 137 | def _enter_butterfly(self, entry): 138 | cpath = entry.get_from_node_path() 139 | exposed = cpath.get_parent() 140 | cpath.node().modify_solid(0).set_tangible(True) 141 | #saved_transform = exposed.get_transform() 142 | self.actor.control_joint(exposed, "butterfly", cpath.node().get_tag("joint")) 143 | #cpath.set_transform(saved_transform) 144 | self.handler.add_collider(cpath, exposed) 145 | 146 | #cpath.show() 147 | 148 | def _leave_butterfly(self, entry): 149 | cpath = entry.get_from_node_path() 150 | exposed = cpath.get_parent() 151 | cpath.node().modify_solid(0).set_tangible(False) 152 | self.actor.release_joint("butterfly", cpath.node().get_tag("joint")) 153 | self.handler.remove_collider(cpath) 154 | 155 | #cpath.hide() 156 | 157 | def _enter_swarm(self, entry): 158 | name = entry.get_into_node().name 159 | if name == 'flower': 160 | return 161 | 162 | if self._times_swarm_activated == 0: 163 | print("Activating swarm colliders due to proximity of", name) 164 | for cpath in self.joint_colliders: 165 | self.traverser.add_collider(cpath, self.handler) 166 | 167 | self._times_swarm_activated += 1 168 | 169 | def _leave_swarm(self, entry): 170 | name = entry.get_into_node().name 171 | if name == 'flower': 172 | return 173 | 174 | self._times_swarm_activated -= 1 175 | 176 | if self._times_swarm_activated == 0: 177 | print("Deactivating swarm colliders") 178 | for cpath in self.joint_colliders: 179 | exposed = cpath.get_parent() 180 | cpath.node().modify_solid(0).set_tangible(False) 181 | self.actor.release_joint("butterfly", cpath.node().get_tag("joint")) 182 | self.handler.remove_collider(cpath) 183 | self.traverser.remove_collider(cpath) 184 | 185 | def update(self, entities_by_filter): 186 | if base.paused: 187 | return 188 | 189 | if self.player_obj: 190 | old_pos = self.player_obj._root.get_pos() 191 | 192 | self.traverser.traverse(base.render) 193 | 194 | if self.player_obj: 195 | pos = self.player_obj.position 196 | new_pos = self.player_obj._root.get_pos() 197 | z_delta = new_pos[2] - old_pos[2] 198 | gravity = globalClock.dt * GRAVITY 199 | self.player_obj.position = (new_pos[0], new_pos[1], max(pos[2] + z_delta - gravity, 1.0)) 200 | 201 | if self.queue.get_num_entries() > 0: 202 | self.queue.sort_entries() 203 | best_entry = self.queue.get_entry(0) 204 | best_entry_z = best_entry.get_surface_point(base.cam.get_parent()).get_z() 205 | 206 | for entry in list(self.queue.entries)[1:]: 207 | entry_z = entry.get_surface_point(base.cam.get_parent()).get_z() 208 | if entry_z > best_entry_z: 209 | best_entry = entry 210 | best_entry_z = entry_z 211 | 212 | rock_z = best_entry_z + 2 213 | diff = rock_z - base.cam.get_z() 214 | 215 | if Controls in base.world.player: 216 | base.world.player[Controls]._collision = diff 217 | else: 218 | if Controls in base.world.player: 219 | base.world.player[Controls]._collision = 0.0 220 | -------------------------------------------------------------------------------- /game/controls.py: -------------------------------------------------------------------------------- 1 | from wecs.core import Component, System 2 | from wecs.core import and_filter 3 | 4 | from direct.showbase.DirectObject import DirectObject 5 | from panda3d import core 6 | 7 | from .terrain import TerrainObject 8 | from .general import Speed 9 | from .animation import Character 10 | from .audio import Music 11 | 12 | import math 13 | import sys 14 | 15 | 16 | RESPONSIVENESS = 1.5 17 | 18 | 19 | @Component() 20 | class Controls: 21 | forward: str = 'mouse1' 22 | 23 | acceleration: float = 3.0 24 | deceleration: float = 2.0 25 | 26 | # degrees per second 27 | turn_speed: float = 150 28 | 29 | enabled: bool = True 30 | 31 | _collision = 0.0 32 | 33 | 34 | class PlayerController(System, DirectObject): 35 | entity_filters = { 36 | 'player': and_filter([Controls, TerrainObject, Speed]), 37 | } 38 | 39 | def __init__(self): 40 | System.__init__(self) 41 | 42 | self.last_ptr = None 43 | 44 | base.win.request_properties(core.WindowProperties(mouse_mode=core.WindowProperties.M_confined)) 45 | 46 | self._control_mode = 'mouse' 47 | self._current_vec = core.Vec2(0) 48 | self._speed_target = 0.0 49 | 50 | self.accept('switch-mouse-controls', self.ensure_control_mode, ['mouse', True]) 51 | self.accept('switch-gamepad-controls', self.ensure_control_mode, ['gamepad', True]) 52 | 53 | def init_entity(self, filter_name, entity): 54 | controls = entity[Controls] 55 | controls._states = {} 56 | 57 | controls._states['forward'] = 0.0 58 | 59 | for key in 'mouse1', 'shift', 'gamepad-ltrigger', 'gamepad-rtrigger', 'gamepad-lshoulder', 'gamepad-rshoulder', 'gamepad-face_a', 'gamepad-lgrip', 'gamepad-rgrip', 'gamepad-lstick', 'gamepad-rstick': 60 | self.accept(key, self._button_pressed, [entity, 'forward']) 61 | self.accept(key + '-up', self._button_released, [entity, 'forward']) 62 | 63 | if base.assume_gamepad: 64 | self.ensure_control_mode('gamepad') 65 | 66 | def destroy_entity(self, filter_name, entity, components={}): 67 | controls = components.get(Controls) 68 | if not controls: 69 | return 70 | 71 | del controls._states 72 | 73 | self.ignore_all() 74 | base.world.music[Music].play('peace') 75 | 76 | if Speed in entity: 77 | entity[Speed].current = 0.0 78 | 79 | def _button_pressed(self, entity, action): 80 | if base.started and not base.paused: 81 | controls = entity[Controls] 82 | controls._states[action] = 1.0 83 | 84 | def _button_released(self, entity, action): 85 | controls = entity[Controls] 86 | controls._states[action] = 0.0 87 | 88 | def ensure_control_mode(self, mode, force=False): 89 | if mode == self._control_mode and not force: 90 | return 91 | 92 | if mode != self._control_mode: 93 | print("Switched to", mode, "controls") 94 | 95 | self._control_mode = mode 96 | base.win.move_pointer(0, base.win.get_x_size() // 2, base.win.get_y_size() // 2) 97 | 98 | if mode == 'mouse': 99 | base.assume_gamepad = False 100 | base.win.request_properties(core.WindowProperties( 101 | mouse_mode=core.WindowProperties.M_confined, 102 | cursor_hidden=False, 103 | )) 104 | else: 105 | base.assume_gamepad = True 106 | base.win.request_properties(core.WindowProperties( 107 | mouse_mode=core.WindowProperties.M_absolute, 108 | cursor_hidden=True, 109 | )) 110 | 111 | def update(self, entities_by_filter): 112 | if not base.started or base.paused: 113 | return 114 | 115 | for entity in entities_by_filter['player']: 116 | obj = entity[TerrainObject] 117 | controls = entity[Controls] 118 | speed = entity[Speed] 119 | 120 | dt = globalClock.dt 121 | 122 | input = core.Vec2(0) 123 | 124 | ptr = base.win.get_pointer(0) 125 | if ptr.in_window: 126 | if self.last_ptr: 127 | lean = ptr.x - base.win.get_x_size() / 2 128 | lean_norm = (lean / base.win.get_x_size()) * 2 129 | elevate = ((ptr.y / base.win.get_y_size()) - 0.5) * 2 130 | input.set(lean_norm, -elevate) 131 | 132 | if input.length_squared() > 0.02: 133 | self.ensure_control_mode("mouse") 134 | else: 135 | base.win.move_pointer(0, base.win.get_x_size() // 2, base.win.get_y_size() // 2) 136 | 137 | self.last_ptr = ptr 138 | elif sys.platform == 'win32': 139 | if self.last_ptr: 140 | ptr = self.last_ptr 141 | lean = ptr.x - base.win.get_x_size() / 2 142 | lean_norm = (lean / base.win.get_x_size()) * 2 143 | elevate = ((ptr.y / base.win.get_y_size()) - 0.5) * 2 144 | input.set(lean_norm, -elevate) 145 | else: 146 | self.last_ptr = None 147 | 148 | boost_input = 0.0 149 | 150 | for gamepad in base.gamepads: 151 | x_axis = gamepad.find_axis(core.InputDevice.Axis.right_x) 152 | y_axis = gamepad.find_axis(core.InputDevice.Axis.right_y) 153 | z_axis = gamepad.find_axis(core.InputDevice.Axis.right_trigger) 154 | xy = core.Vec2(x_axis.value, y_axis.value) 155 | if xy.length_squared() > 0.5: 156 | self.ensure_control_mode("gamepad") 157 | xy *= xy.length() 158 | if xy.length_squared() > input.length_squared(): 159 | input.set(*xy) 160 | 161 | if z_axis.value > boost_input: 162 | boost_input = z_axis.value 163 | 164 | x_axis = gamepad.find_axis(core.InputDevice.Axis.left_x) 165 | y_axis = gamepad.find_axis(core.InputDevice.Axis.left_y) 166 | z_axis = gamepad.find_axis(core.InputDevice.Axis.left_trigger) 167 | xy = core.Vec2(x_axis.value, y_axis.value) 168 | if xy.length_squared() > 0.5: 169 | self.ensure_control_mode("gamepad") 170 | xy *= xy.length() 171 | if xy.length_squared() > input.length_squared(): 172 | input.set(*xy) 173 | 174 | if z_axis.value > boost_input: 175 | boost_input = z_axis.value 176 | 177 | input.y *= abs(input.y) 178 | 179 | if controls._collision > input.y: 180 | input.y = controls._collision 181 | 182 | cur = core.Vec2(self._current_vec) 183 | delta = input.xy - cur 184 | dist = delta.length() 185 | if dist > 0: 186 | delta /= dist 187 | cur += delta * min(dt * RESPONSIVENESS, dist) 188 | 189 | if base.cam.parent.name != "dolly" and base.started: 190 | base.cam.set_z(cur.y) 191 | base.cam.set_x(cur.x * abs(cur.x) * 1.5) 192 | obj.direction -= cur.x * 0.5 * controls.turn_speed * dt 193 | 194 | self._current_vec = cur 195 | 196 | if not base.started: 197 | self._speed_target = 0.0 198 | elif (core.Vec2(base.world.dolmen[TerrainObject].position[0], base.world.dolmen[TerrainObject].position[1]) - core.Vec2(base.world.player[TerrainObject].position[0], base.world.player[TerrainObject].position[1])).length_squared() < 600: 199 | self._speed_target = speed.min 200 | elif controls._states['forward'] and base.cam.parent.name != 'dolly': 201 | self._speed_target = speed.max 202 | else: 203 | target_speed = boost_input * (speed.max - speed.min) + speed.min 204 | diff = target_speed - self._speed_target 205 | responsiveness = (speed.max - speed.min) 206 | if diff > 0: 207 | self._speed_target += min(dt * responsiveness, diff) 208 | else: 209 | self._speed_target -= min(dt * responsiveness, -diff) 210 | 211 | if self._speed_target < speed.min: 212 | self._speed_target = speed.min 213 | if self._speed_target > speed.max: 214 | self._speed_target = speed.max 215 | 216 | if controls.enabled and speed.current <= self._speed_target: 217 | speed.accelerate(controls.acceleration, max=self._speed_target) 218 | else: 219 | speed.accelerate(-controls.deceleration, min=self._speed_target) 220 | 221 | speed_t = (speed.current - speed.min) / (speed.max - speed.min) 222 | if speed_t > 0.8: 223 | base.world.music[Music].play('chase') 224 | elif speed_t < 0.2: 225 | base.world.music[Music].play('peace') 226 | 227 | base.world.player[Character]._state_actors['fly'].set_play_rate(speed_t*0.5+0.5, 'flap', partName='morphs') 228 | 229 | dir_rad = math.radians(obj.direction) 230 | obj.position = ( 231 | obj.position[0] - math.sin(dir_rad) * speed.current * dt, 232 | obj.position[1] + math.cos(dir_rad) * speed.current * dt, 233 | obj.position[2], 234 | ) 235 | -------------------------------------------------------------------------------- /game/general.py: -------------------------------------------------------------------------------- 1 | from wecs.core import Component 2 | 3 | 4 | @Component() 5 | class Speed: 6 | current: float = 0.0 7 | min: float = 0.0 8 | max: float = None 9 | 10 | def accelerate(self, by, max=None, min=None): 11 | self.current += by * globalClock.dt 12 | 13 | if max is not None and self.current > max: 14 | self.current = max 15 | 16 | if min is not None and self.current < min: 17 | self.current = min 18 | 19 | if self.max is not None and self.current > self.max: 20 | self.current = self.max 21 | elif self.current < self.min: 22 | self.current = self.min 23 | -------------------------------------------------------------------------------- /game/lighting.py: -------------------------------------------------------------------------------- 1 | from wecs.core import Component, System 2 | from wecs.core import and_filter 3 | 4 | from panda3d import core 5 | import math 6 | 7 | 8 | @Component() 9 | class Sun: 10 | azimuth: float = 90 11 | elevation: float = 45 12 | color_temperature: float = 6500 13 | intensity: float = 5 14 | priority: float = 0 15 | 16 | 17 | @Component() 18 | class AmbientLight: 19 | color: tuple 20 | intensity: float = 1 21 | 22 | 23 | class LightingSystem(System): 24 | entity_filters = { 25 | 'sun': and_filter([Sun]), 26 | 'ambient': and_filter([AmbientLight]), 27 | } 28 | 29 | def __init__(self): 30 | System.__init__(self) 31 | 32 | self.lights = {} 33 | 34 | def init_entity(self, filter_name, entity): 35 | if filter_name == 'sun': 36 | sun = entity[Sun] 37 | light = core.DirectionalLight(entity._uid.name) 38 | light.priority = sun.priority 39 | path = base.render.attach_new_node(light) 40 | path.set_h(sun.azimuth) 41 | self.lights[entity] = path 42 | 43 | elif filter_name == 'ambient': 44 | ambient = entity[AmbientLight] 45 | light = core.AmbientLight(entity._uid.name) 46 | light.color = core.LVecBase4(*ambient.color, 0) * ambient.intensity 47 | path = base.render.attach_new_node(light) 48 | self.lights[entity] = path 49 | 50 | base.render.set_light(path) 51 | 52 | def update(self, entities_by_filter): 53 | for entity in entities_by_filter['sun']: 54 | sun = entity[Sun] 55 | elev_rad = math.radians(sun.elevation) 56 | 57 | light = self.lights[entity].node() 58 | light.direction = (0, math.cos(elev_rad), -math.sin(elev_rad)) 59 | light.color_temperature = sun.color_temperature 60 | light.color = light.color * sun.intensity 61 | self.lights[entity].set_h(entity[Sun].azimuth) 62 | 63 | def destroy_entity(self, filter_name, entity): 64 | path = self.lights.pop(entity) 65 | path.remove_node() 66 | -------------------------------------------------------------------------------- /game/menu.py: -------------------------------------------------------------------------------- 1 | from direct.showbase.DirectObject import DirectObject 2 | from direct.gui.OnscreenText import OnscreenText 3 | from direct.gui.DirectButton import DirectButton 4 | import direct.gui.DirectGuiGlobals as DGG 5 | from panda3d import core 6 | from direct.interval.IntervalGlobal import Sequence, Func 7 | 8 | 9 | FADE_TIME = 1.0 10 | 11 | 12 | class Menu(DirectObject): 13 | def __init__(self, title, button_defs): 14 | self.root = base.a2dBottomLeft.attach_new_node("menu") 15 | 16 | self.title = OnscreenText(parent=self.root, text=title, align=core.TextNode.A_left, pos=(0.07, 0.625), scale=0.1, fg=(1, 1, 1, 1)) 17 | 18 | text_fg = (1, 1, 1, 0.6) 19 | 20 | #x = (0.9 + 0.4) / 2 21 | x = 0.08 22 | spacing = 0.08 23 | scale=0.05 24 | y = 0.51 25 | btns = [] 26 | for text, command in button_defs: 27 | btns.append(DirectButton(text=text, command=command, parent=self.root, text_align=core.TextNode.A_left, pos=(x, 0.0, y), scale=scale, text_fg=(1, 1, 1, 1), relief=None)) 28 | y -= spacing 29 | self.buttons = btns 30 | 31 | for i, btn in enumerate(btns): 32 | btn.set_color_scale(text_fg) 33 | btn.bind(DGG.ENTER, self._focus_button, [i]) 34 | btn.bind(DGG.EXIT, self._unfocus_button, [i]) 35 | 36 | self._selected = None 37 | 38 | self.root.stash() 39 | self._fade_interval = None 40 | 41 | def _press_down(self, gamepad=False): 42 | base.assume_gamepad = gamepad 43 | if self._selected is None: 44 | self._selected = 0 45 | else: 46 | self._unfocus_button(self._selected) 47 | self._selected = (self._selected + 1) % len(self.buttons) 48 | self._focus_button(self._selected) 49 | DGG.getDefaultRolloverSound().play() 50 | 51 | def _press_up(self, gamepad=False): 52 | base.assume_gamepad = gamepad 53 | if self._selected is None: 54 | self._selected = len(self.buttons) - 1 55 | else: 56 | self._unfocus_button(self._selected) 57 | self._selected = (self._selected - 1) % len(self.buttons) 58 | self._focus_button(self._selected) 59 | DGG.getDefaultRolloverSound().play() 60 | 61 | def _press_select(self, gamepad=False): 62 | base.assume_gamepad = gamepad 63 | if self._selected is None: 64 | self._press_down() 65 | else: 66 | self.buttons[self._selected]['command']() 67 | DGG.getDefaultClickSound().play() 68 | 69 | def _focus_button(self, i, param=None): 70 | btn = self.buttons[i] 71 | btn.colorScaleInterval(0.2, (1, 1, 1, 1), blendType='easeInOut').start() 72 | 73 | def _unfocus_button(self, i, param=None): 74 | btn = self.buttons[i] 75 | btn.colorScaleInterval(0.2, (1, 1, 1, 0.6), blendType='easeInOut').start() 76 | 77 | def show(self): 78 | self.accept('gamepad-dpad_down', self._press_down, [True]) 79 | self.accept('gamepad-dpad_up', self._press_up, [True]) 80 | self.accept('gamepad-face_a', self._press_select, [True]) 81 | self.accept('arrow_down', self._press_down, [False]) 82 | self.accept('arrow_up', self._press_up, [False]) 83 | self.accept('enter', self._press_select, [False]) 84 | 85 | self.root.set_color_scale((1, 1, 1, 0)) 86 | self.root.unstash() 87 | 88 | if self._fade_interval: 89 | self._fade_interval.pause() 90 | 91 | self._fade_interval = self.root.colorScaleInterval(FADE_TIME, (1, 1, 1, 1)) 92 | self._fade_interval.start() 93 | 94 | if base.assume_gamepad and self.buttons: 95 | self._selected = 0 96 | self._focus_button(0) 97 | 98 | def hide(self): 99 | self.ignoreAll() 100 | if self._selected is not None: 101 | self._unfocus_button(self._selected) 102 | self._selected = None 103 | 104 | if self._fade_interval: 105 | self._fade_interval.pause() 106 | 107 | self._fade_interval = Sequence(self.root.colorScaleInterval(FADE_TIME, (1, 1, 1, 0)), Func(self.root.stash)) 108 | self._fade_interval.start() 109 | -------------------------------------------------------------------------------- /game/terrain.py: -------------------------------------------------------------------------------- 1 | from wecs.core import Entity, Component, System 2 | from wecs.core import and_filter 3 | 4 | from direct.actor.Actor import Actor 5 | from panda3d import core 6 | 7 | from .animation import Character 8 | from .general import Speed 9 | 10 | from dataclasses import field 11 | 12 | 13 | @Component() 14 | class Terrain: 15 | # pixels per meter, should be power of 2 16 | resolution: float = 1 / 2 17 | 18 | size: float = 256 19 | seed: int = 100 20 | octaves: tuple = ( 21 | # bump height, bump width in metres 22 | (10, 32), 23 | (2, 10), 24 | (0.1, 3), 25 | ) 26 | 27 | 28 | @Component() 29 | class TerrainObject: 30 | """Components for objects that are positioned relative to the terrain.""" 31 | 32 | terrain: Entity 33 | model: core.NodePath = None 34 | position: tuple = (0, 0, 0) 35 | scale: float = 1.0 36 | direction: float = 0.0 37 | material: core.Material = None 38 | path: str = None 39 | shadeless: bool = False 40 | shader: core.Shader = None 41 | wraparound: float = 0 42 | draw_distance: float = None 43 | 44 | 45 | class TerrainSystem(System): 46 | entity_filters = { 47 | 'terrain': and_filter([Terrain]), 48 | 'object': and_filter([TerrainObject]), 49 | 'movable': and_filter([TerrainObject, Speed]), 50 | } 51 | 52 | def __init__(self): 53 | System.__init__(self) 54 | 55 | self.objects = {} 56 | self.actor_states = {} 57 | 58 | def init_entity(self, filter_name, entity): 59 | if filter_name == 'terrain': 60 | return self.init_terrain(entity) 61 | elif filter_name == 'object': 62 | return self.init_terrain_object(entity) 63 | 64 | def init_terrain(self, entity): 65 | component = entity[Terrain] 66 | 67 | noise = core.StackedPerlinNoise2() 68 | 69 | scaled_size = int(component.size * component.resolution) 70 | heightmap_size = scaled_size# + 1 71 | 72 | print(f"Terrain complexity: {scaled_size}") 73 | 74 | max_mag = 0.0 75 | for mag, scale in component.octaves: 76 | if mag > max_mag: 77 | max_mag = mag 78 | 79 | for mag, scale in component.octaves: 80 | scale /= component.size 81 | noise.add_level(core.PerlinNoise2(scale, scale, 256, component.seed), mag / max_mag) 82 | 83 | heightfield = core.PNMImage(heightmap_size, heightmap_size, 1) 84 | heightfield.set_maxval(0xffff) 85 | #heightfield.perlin_noise_fill(noise) 86 | status = heightfield.read(core.Filename.expand_from('$MAIN_DIR/assets/textures/heightmap.png')) 87 | assert status, "Could not read heightmap" 88 | #heightfield.make_grayscale() 89 | #assert heightfield.is_grayscale() 90 | 91 | # We use GeoMipTerrain just for determining the normals at the moment. 92 | heightfield2 = core.PNMImage(heightmap_size + 1, heightmap_size + 1, 1) 93 | heightfield2.set_maxval(0xffff) 94 | heightfield2.copy_sub_image(heightfield, 0, 0) 95 | 96 | terrain = core.GeoMipTerrain(entity._uid.name) 97 | terrain.set_heightfield(heightfield2) 98 | terrain.set_bruteforce(True) 99 | terrain.set_block_size(min(32, scaled_size)) 100 | #terrain.set_color_map(heightfield) 101 | 102 | # Store both height and normal in the same texture. 103 | nimg = core.PNMImage(heightmap_size, heightmap_size, 4, color_space=core.CS_linear) 104 | 105 | for x in range(heightmap_size): 106 | for y in range(heightmap_size): 107 | normal = terrain.get_normal(x, y) 108 | normal.x *= component.resolution 109 | normal.y *= component.resolution 110 | normal.z /= max_mag 111 | normal.normalize() 112 | nimg.set_xel(x, y, normal * 0.5 + 0.5) 113 | #nimg.set_alpha(x, y, terrain.get_elevation(x, y)) 114 | nimg.set_alpha(x, y, heightfield.get_gray(x, y)) 115 | 116 | ntex = core.Texture("normal") 117 | ntex.load(nimg) 118 | ntex.wrap_u = core.SamplerState.WM_repeat 119 | ntex.wrap_v = core.SamplerState.WM_repeat 120 | ntex.set_keep_ram_image(True) 121 | component._peeker = ntex.peek() 122 | 123 | root = terrain.get_root() 124 | root.set_scale(1.0 / component.resolution, 1.0 / component.resolution, max_mag) 125 | #root.reparent_to(base.render) 126 | #root.set_texture(htex) 127 | #terrain.generate() 128 | 129 | mat = core.Material() 130 | #mat.base_color = (0.16, 0.223, 0.076, 1) 131 | mat.base_color = (0.4, 0.6, 0.4, 1) 132 | mat.base_color = ( 133 | 41.6/255.0, 134 | 73.7/255.0, 135 | 29.0/255.0, 136 | 1) 137 | #print(mat.base_color) 138 | mat.roughness = 1 139 | #root.set_material(mat) 140 | 141 | simg = core.PNMImage(64, 64, 1) 142 | simg.fill(1) 143 | component._sat_img = simg 144 | component._sat_tex = core.Texture() 145 | component._sat_tex.load(simg) 146 | 147 | component._scale = core.VBase3(component.resolution / scaled_size, component.resolution / scaled_size, max_mag) 148 | 149 | component._wind_map = loader.load_texture("assets/textures/wind.png") 150 | component._wind_sound = loader.load_sfx("assets/sfx/wind-low.ogg") 151 | component._wind_sound.set_loop(True) 152 | 153 | grass_root = render.attach_new_node("grass") 154 | grass_root.set_shader(core.Shader.load(core.Shader.SL_GLSL, "assets/shaders/grass.vert", "assets/shaders/grass.frag"), 20) 155 | render.set_shader_input("scale", component.resolution / scaled_size, component.resolution / scaled_size, max_mag) 156 | grass_root.set_shader_input("terrainmap", ntex) 157 | render.set_shader_input("windmap", component._wind_map) 158 | grass_root.set_material(mat) 159 | grass_root.set_shader_input("player", 0, 0, 0) 160 | 161 | render.set_shader_input("satmap", component._sat_tex) 162 | 163 | #grass_root.node().set_bounds(core.BoundingSphere()) 164 | #grass_root.node().set_final(True) 165 | 166 | grass_root.set_bin('background', 10) 167 | 168 | if base.quality == 0: 169 | patch = loader.load_model("assets/models/patch-potato.bam") 170 | elif base.quality == 1: 171 | patch = loader.load_model("assets/models/patch-low.bam") 172 | else: 173 | patch = loader.load_model("assets/models/patch.bam") 174 | patch_size = int(patch.get_tag('patch_size')) 175 | 176 | self._r_build_grass_octree(grass_root, patch, patch_size, component.size * 2) 177 | grass_root.set_pos(-0.5 * component.size, -0.5 * component.size, 8) 178 | 179 | component._grass_root = grass_root 180 | 181 | def _r_build_grass_octree(self, parent, patch, patch_size, total_size): 182 | num_patches = int(total_size // patch_size) 183 | 184 | if num_patches >= 4: 185 | half_size = total_size // 2 186 | 187 | node = parent.attach_new_node("1") 188 | node.node().set_bounds_type(core.BoundingVolume.BT_box) 189 | node.set_pos(0, 0, 0) 190 | self._r_build_grass_octree(node, patch, patch_size, half_size) 191 | 192 | node = parent.attach_new_node("2") 193 | node.node().set_bounds_type(core.BoundingVolume.BT_box) 194 | node.set_pos(0, half_size, 0) 195 | self._r_build_grass_octree(node, patch, patch_size, half_size) 196 | 197 | node = parent.attach_new_node("3") 198 | node.node().set_bounds_type(core.BoundingVolume.BT_box) 199 | node.set_pos(half_size, 0, 0) 200 | self._r_build_grass_octree(node, patch, patch_size, half_size) 201 | 202 | node = parent.attach_new_node("4") 203 | node.node().set_bounds_type(core.BoundingVolume.BT_box) 204 | node.set_pos(half_size, half_size, 0) 205 | self._r_build_grass_octree(node, patch, patch_size, half_size) 206 | return 207 | 208 | for x in range(num_patches): 209 | for y in range(num_patches): 210 | inst = patch.copy_to(parent) 211 | inst.set_pos(x * patch_size, y * patch_size, 0) 212 | #inst.node().set_final(True) 213 | #inst.node().set_bounds(core.BoundingBox((x, y, 0), (x+patch_size, y+patch_size, 100))) 214 | 215 | def init_terrain_object(self, entity): 216 | obj = entity[TerrainObject] 217 | 218 | if obj.draw_distance is not None: 219 | path = base.render.attach_new_node(core.FadeLODNode(entity._uid.name)) 220 | path.node().add_switch(obj.draw_distance, 0) 221 | else: 222 | path = base.render.attach_new_node(entity._uid.name) 223 | 224 | obj._root = path 225 | 226 | if obj.model: 227 | model = loader.load_model(obj.model) 228 | 229 | if obj.path: 230 | model = model.find(obj.path) 231 | model.clear_transform() 232 | 233 | #model.flatten_strong() 234 | 235 | if model.find("**/+Character"): 236 | if Character in entity: 237 | model = core.NodePath("states") 238 | model.set_transparency(1) 239 | 240 | actor = Actor(obj.model) 241 | char = entity[Character] 242 | char._actor = actor 243 | actor.reparent_to(model) 244 | #actor.stash() 245 | 246 | for part, joints in char.subparts.items(): 247 | actor.make_subpart(part, joints) 248 | 249 | for state, part_anims in char.states.items(): 250 | actor = self.actor_states.get((obj.model, state)) 251 | if not actor: 252 | print("Creating actor", obj.model, "in state", state) 253 | 254 | actor = Actor(obj.model) 255 | self.actor_states[(obj.model, state)] = actor 256 | 257 | for part, joints in char.subparts.items(): 258 | actor.make_subpart(part, joints) 259 | 260 | for part, anim in part_anims.items(): 261 | actor.set_play_rate(char.play_rates[state], anim, partName=part) 262 | actor.loop(anim, partName=part) 263 | #actor.pose(anim, -1, partName=part) 264 | 265 | char._state_actors[state] = actor 266 | 267 | if entity._uid.name == 'player' or entity._uid.name == 'superflower': 268 | # No instancing needed for singletons, and in fact breaks some stuff 269 | actor.reparent_to(model) 270 | inst = actor 271 | else: 272 | inst = actor.instance_under_node(model, state) 273 | char._state_paths[state] = inst 274 | inst.stash() 275 | 276 | #if obj.shadeless: 277 | # model.set_light_off(1) 278 | 279 | if entity._uid.name == 'player': 280 | model.set_color((2.0, 0.5, 1.0, 1), 10000) 281 | model.set_color_scale((1, 1, 1, 1), 10000) 282 | model.set_shader_off(2000) 283 | model.set_h(10) 284 | 285 | if obj.material: 286 | model.set_material(obj.material, 1) 287 | 288 | if obj.shader: 289 | model.set_shader(obj.shader, 10) 290 | model.reparent_to(path) 291 | 292 | component = obj.terrain[Terrain] 293 | 294 | pos = obj.position 295 | res = component.resolution 296 | col = core.LColor() 297 | component._peeker.lookup_bilinear( 298 | col, 299 | (pos[0] * component._scale.x) % 1.0, 300 | (pos[1] * component._scale.y) % 1.0, 301 | ) 302 | height = col.w * component._scale.z 303 | obj._root.set_fluid_pos(pos[0], pos[1], pos[2] + height) 304 | obj._root.get_child(0).set_scale(obj.scale) 305 | obj._root.set_h(obj.direction) 306 | 307 | for tex in obj._root.find_all_textures(): 308 | tex.wrap_u = core.SamplerState.WM_clamp 309 | tex.wrap_v = core.SamplerState.WM_clamp 310 | 311 | if obj.wraparound: 312 | border = obj.wraparound 313 | if pos[0] < border: 314 | inst = model.instance_under_node(render, "wrap") 315 | inst.set_transform(obj._root.get_transform()) 316 | inst.set_pos(inst.get_pos() + (256, 0, 0)) 317 | if pos[1] < border: 318 | inst = model.instance_under_node(render, "wrap") 319 | inst.set_transform(obj._root.get_transform()) 320 | inst.set_pos(inst.get_pos() + (0, 256, 0)) 321 | 322 | if pos[0] > 256 - border: 323 | inst = model.instance_under_node(render, "wrap") 324 | inst.set_transform(obj._root.get_transform()) 325 | inst.set_pos(inst.get_pos() - (256, 0, 0)) 326 | if pos[1] > 256 - border: 327 | inst = model.instance_under_node(render, "wrap") 328 | inst.set_transform(obj._root.get_transform()) 329 | inst.set_pos(inst.get_pos() - (0, 256, 0)) 330 | 331 | if pos[0] < border and pos[1] < border: 332 | inst = model.instance_under_node(render, "wrap") 333 | inst.set_transform(obj._root.get_transform()) 334 | inst.set_pos(inst.get_pos() + (256, 256, 0)) 335 | 336 | if pos[0] > 256 - border and pos[1] > 256 - border: 337 | inst = model.instance_under_node(render, "wrap") 338 | inst.set_transform(obj._root.get_transform()) 339 | inst.set_pos(inst.get_pos() + (-256, -256, 0)) 340 | 341 | if pos[0] > 256 - border and pos[1] < border: 342 | inst = model.instance_under_node(render, "wrap") 343 | inst.set_transform(obj._root.get_transform()) 344 | inst.set_pos(inst.get_pos() + (-256, 256, 0)) 345 | 346 | if pos[0] < border and pos[1] > 256 - border: 347 | inst = model.instance_under_node(render, "wrap") 348 | inst.set_transform(obj._root.get_transform()) 349 | inst.set_pos(inst.get_pos() + (256, -256, 0)) 350 | 351 | def update(self, entities_by_filter): 352 | if base.paused: 353 | return 354 | 355 | for entity in entities_by_filter['movable']: 356 | obj = entity[TerrainObject] 357 | 358 | component = obj.terrain[Terrain] 359 | while obj.position[0] < 0: 360 | obj.position = (obj.position[0] + component.size, obj.position[1], obj.position[2]) 361 | while obj.position[1] < 0: 362 | obj.position = (obj.position[0], obj.position[1] + component.size, obj.position[2]) 363 | 364 | while obj.position[0] > component.size: 365 | obj.position = (obj.position[0] - component.size, obj.position[1], obj.position[2]) 366 | while obj.position[1] > component.size: 367 | obj.position = (obj.position[0], obj.position[1] - component.size, obj.position[2]) 368 | 369 | pos = obj.position 370 | res = component.resolution 371 | col = core.LColor() 372 | component._peeker.lookup_bilinear( 373 | col, 374 | (pos[0] * component._scale.x) % 1.0, 375 | (pos[1] * component._scale.y) % 1.0, 376 | ) 377 | height = col.w * component._scale.z 378 | obj._root.set_pos(pos[0], pos[1], pos[2] + height) 379 | obj._root.get_child(0).set_scale(obj.scale) 380 | obj._root.set_h(obj.direction) 381 | 382 | #obj._root.set_texture_off(10) 383 | 384 | if entity._uid.name == "player": #haack 385 | t = 0 386 | if Speed in entity: 387 | speed = entity[Speed] 388 | if speed.max is not None: 389 | #t = (speed.current - speed.min) / (speed.max - speed.min) 390 | t = speed.current / speed.max 391 | 392 | obj.terrain[Terrain]._grass_root.set_shader_input("player", pos[0], pos[1], t) 393 | 394 | wspos = base.cam.get_pos(render).xy 395 | peeker = component._wind_map.peek() 396 | wind_coord = core.Vec2(pos[0], pos[1]) * component._scale[0] * 4 + core.Vec2(globalClock.frame_time * 0.06, 0) 397 | wind = core.LColor() 398 | peeker.lookup(wind, wind_coord.x, wind_coord.y) 399 | #wind = max(1.0 - wind.x - 0.5, 0.0) + 0.5 400 | wind = abs(1.0 - wind.x - 0.5) + 0.5 401 | component._wind_sound.set_play_rate(wind * 2 + 0.5) 402 | component._wind_sound.set_volume(max(0, wind - 0.35)) 403 | 404 | def destroy_entity(self, filter_name, entity): 405 | if filter_name == 'terrain': 406 | pass 407 | elif filter_name == 'object': 408 | entity[TerrainObject]._root.remove_node() 409 | -------------------------------------------------------------------------------- /game/world.py: -------------------------------------------------------------------------------- 1 | from wecs.core import World as ECSWorld 2 | from direct.showbase.DirectObject import DirectObject 3 | from direct.interval.IntervalGlobal import Sequence, Parallel, Wait, Func 4 | from panda3d import core 5 | 6 | from random import random, choice 7 | import math 8 | 9 | from .controls import Controls, PlayerController 10 | from .terrain import Terrain, TerrainObject, TerrainSystem 11 | from .lighting import Sun, AmbientLight, LightingSystem 12 | from .camera import Camera, CameraSystem 13 | from .general import Speed 14 | from .animation import Character, AnimationPlayer 15 | from .collision import Collider, GeomCollider, CollisionDetectionSystem 16 | from .audio import SfxPlayer, Music, Listener, AudioSystem 17 | 18 | 19 | PAINT_THRESHOLD = 0.78 20 | 21 | 22 | class World(ECSWorld, DirectObject): 23 | def __init__(self): 24 | ECSWorld.__init__(self) 25 | 26 | mat = core.Material() 27 | mat.twoside = True 28 | mat.base_color = (0.2, 0.2, 0.2, 1) 29 | mat.roughness = 0 30 | 31 | self.terrain = self.create_entity(Terrain(), name="Terrain") 32 | self.sun = self.create_entity(Sun(priority=10, intensity=3, color_temperature=7000)) 33 | self.sun2 = self.create_entity(Sun(priority=0, azimuth=-90, elevation=20, intensity=0.5, color_temperature=10000)) 34 | 35 | self.music = self.create_entity(Music(songs=["menu", "peace", "chase", "pause"], current="menu"), name="music") 36 | self.ambient = self.create_entity(SfxPlayer(sounds=['background'], loop=True)) 37 | self.ambient[SfxPlayer].play('background') 38 | 39 | sky = loader.load_model("models/sky") 40 | sky.set_bin('background', 1) 41 | sky.set_depth_write(False) 42 | sky.set_depth_test(False) 43 | sky.reparent_to(base.cam) 44 | sky.set_compass() 45 | sky.set_scale(5) 46 | sky.set_shader_off(10) 47 | sky.set_light_off(10) 48 | sky.set_color_scale((0.7, 0.8, 1.0, 1.0)) 49 | 50 | self.tree_shader = core.Shader.load(core.Shader.SL_GLSL, "assets/shaders/tree.vert", "assets/shaders/object.frag") 51 | 52 | self.trees = [] 53 | 54 | pos = (251.88694392730682, 43.61265902663462, 0) 55 | sub = choice([ 56 | '**/tree', 57 | '**/tree.001', 58 | '**/tree.002', 59 | '**/tree.003', 60 | ]) 61 | #pos = (pos[0], pos[1], -random()) 62 | tree = self.create_entity( 63 | TerrainObject( 64 | self.terrain, 65 | model='models/trees.bam', 66 | path='**/tree.001', 67 | scale=1, 68 | position=pos, 69 | direction=0, 70 | material=mat, 71 | shader=self.tree_shader, 72 | wraparound=120, 73 | ), 74 | Collider(solid=core.CollisionCapsule((0, 0, -1), (0, 0, 5), 0.5), into_mask=0b11), 75 | name="tree", 76 | ) 77 | 78 | #pos = (251.887+3, 43.6127, 0) 79 | pos = (251.887-256+3.5, 43.6127, 0) 80 | self.lone_flower = self.create_entity( 81 | TerrainObject( 82 | self.terrain, 83 | model='models/flower.bam', 84 | position=pos, 85 | direction=random()*360, 86 | scale=0.5, 87 | material=mat, 88 | ), 89 | Character( 90 | state="closed", 91 | states={ 92 | "locked": {"flower": "closed_idle"}, 93 | "closed": {"flower": "closed_idle"}, 94 | "open": {"flower": ""}, 95 | }, 96 | transitions={ 97 | #(None, "closed"): {"vine": "vine"}, 98 | ("locked", "open"): {"flower": "open", "vine": "vine"}, 99 | ("closed", "open"): {"flower": "open"}, 100 | }, 101 | subparts={ 102 | "flower": ["petal", "petal.001", "petal.002", "petal.003", "petal.004", "petal.005", "petal.006", "petal.007"], 103 | "vine": ["vine"], 104 | }, 105 | play_rates={ 106 | "locked": 2.5, 107 | "closed": 1.0, 108 | "open": 2.5, 109 | }, 110 | ), 111 | name="lone_flower", 112 | ) 113 | 114 | self.add_system(TerrainSystem(), sort=0) 115 | self.add_system(LightingSystem(), sort=1) 116 | self.add_system(AnimationPlayer(), sort=3) 117 | self.add_system(AudioSystem(), sort=5) 118 | 119 | render.set_shader(core.Shader.load(core.Shader.SL_GLSL, "assets/shaders/object.vert", "assets/shaders/object.frag"), 10) 120 | 121 | def finish_loading(self): 122 | self.terrain[Terrain]._wind_sound.play() 123 | self.terrain[Terrain]._grass_root.set_z(5) 124 | self.fill_map(0) 125 | 126 | self.lone_flower[TerrainObject]._root.remove_node() 127 | 128 | mat = core.Material() 129 | mat.twoside = True 130 | mat.base_color = (0.2, 0.2, 0.2, 1) 131 | mat.roughness = 0 132 | 133 | self.player = self.create_entity( 134 | #TerrainObject(self.terrain, model='models/butterfly.bam', position=(128, 64, 1), scale=0.09), 135 | TerrainObject(self.terrain, model='models/butterfly.bam', position=(128, 60, 1), scale=0.2, shadeless=True, material=mat), 136 | Character( 137 | state="fly", 138 | states={ 139 | "fly": {"butterfly": "forward", "morphs": "flap"}, 140 | "end": {"morphs": "flap"}, 141 | }, 142 | play_rates={ 143 | "fly": 0.5, 144 | "end": 1.0, 145 | }, 146 | transitions={ 147 | ("fly", "end"): {"butterfly": "dance"}, 148 | }, 149 | subparts={ 150 | "butterfly": ["root", "butterfly.000", "butterfly.001", "butterfly.002", "butterfly.003", "butterfly.004", "butterfly.005", "butterfly.006", "butterfly.007", "butterfly.008", "butterfly.009", "butterfly.010", "butterfly.011", "butterfly.012", "butterfly.013", "butterfly.014", "butterfly.015", "butterfly.016", "butterfly.017", "butterfly.018", "butterfly.019", "butterfly.020", "butterfly.021", "butterfly.022", "butterfly.023", "butterfly.024", "butterfly.025", "butterfly.026", "butterfly.027", "butterfly.028", "butterfly.029", "butterfly.030", "butterfly.031", "butterfly.032", "butterfly.033", "butterfly.034", "butterfly.035", "butterfly.036", "butterfly.037", "butterfly.038", "butterfly.039", "butterfly.040", "butterfly.041", "butterfly.042", "butterfly.043", "butterfly.044", "butterfly.045", "butterfly.046", "butterfly.047", "butterfly.048", "butterfly.049", "butterfly.050", "butterfly.051", "butterfly.052", "butterfly.053", "butterfly.054", "butterfly.055", "butterfly.056", "butterfly.057", "butterfly.058", "butterfly.059", "butterfly.060", "butterfly.061", "butterfly.062", "butterfly.063"], 151 | "morphs": ["Key 1", "Key 2", "Key 3", "Key 4", "Key 5", "Key 6", "Key 7", "Key 8"], 152 | }, 153 | ), 154 | Controls(acceleration=2.0), 155 | Speed(min=3.0, max=6.0), 156 | Collider(solid=core.CollisionSphere((0, 0.5, 0), 2), from_mask=0b01, joint_from_mask=0b10, into_mask=0, tangible=False), 157 | name="player", 158 | ) 159 | self.flush_component_updates() 160 | 161 | self.camera = self.create_entity( 162 | Camera(target=self.player), 163 | Collider(solid=core.CollisionSphere((0, 0, 0), 3), from_mask=0b10, tangible=False), 164 | Listener(), 165 | name="camera", 166 | ) 167 | self.camera[Camera]._root = base.camera 168 | 169 | mat = core.Material() 170 | mat.twoside = True 171 | mat.roughness = 1 172 | mat.base_color = (1, 1, 1, 1) 173 | 174 | self.dolmen = self.create_entity( 175 | TerrainObject( 176 | self.terrain, 177 | model='models/hunebed.bam', 178 | scale=1, 179 | position=(164.38, 238.85, 0), 180 | direction=0, 181 | material=mat, 182 | wraparound=100, 183 | ), 184 | #Collider(into_mask=0b01, tangible=False), 185 | #GeomCollider(into_mask=0b10), 186 | name="dolmen", 187 | ) 188 | 189 | self.superflower = self.create_entity( 190 | TerrainObject( 191 | self.terrain, 192 | model='models/superflower.bam', 193 | position=(160.6078540733596, 234.90864229056726, 0), 194 | #position=(120.64369917139119, 83.10867386895148, 0), 195 | scale=0.75, 196 | material=mat, 197 | ), 198 | Character( 199 | state="locked", 200 | states={ 201 | "locked": {"flower": "closed_idle"}, 202 | "closed": {"flower": "closed_idle"}, 203 | "open": {"flower": ""}, 204 | }, 205 | transitions={ 206 | ("locked", "closed"): {"vine": "vine"}, 207 | ("locked", "open"): {"flower": "open", "vine": "vine"}, 208 | ("closed", "open"): {"flower": "open"}, 209 | }, 210 | subparts={ 211 | "flower": ["petal", "petal.001", "petal.002", "petal.003", "petal.004", "petal.005", "petal.006", "petal.007"], 212 | "vine": ["vine"], 213 | }, 214 | play_rates={ 215 | "locked": 2.5, 216 | "closed": 2.5, 217 | "open": 2.5, 218 | }, 219 | ), 220 | SfxPlayer(sounds=['flower-open-a', 'flower-open-b', 'flower-open-c', 'dance'], volume=1), 221 | Collider(solid=core.CollisionTube((0, 0, 0), (0, 0, 3), 0.5), into_mask=0b11, tangible=True), 222 | name="superflower", 223 | ) 224 | 225 | self.flowers = [] 226 | for pos in [ 227 | (128.24077881498945, 72.77485431180376, 0), 228 | (132.32008294369948, 80.04317696001722, 0), 229 | #(133.4009193432467, 79.83727034306206, 0), 230 | (138.97300808378307, 83.48684150117761, 0), 231 | #(145.89853302506023, 86.95707780906609, 0), 232 | (149.16924933624284, 93.18188292973764, 0), 233 | #(150.0381232901063, 91.2378202069885, 0), 234 | #(151.64899737599134, 97.48784982878404, 0), 235 | (150.03599905444173, 104.24586880067328, 0), 236 | #(146.76541251927688, 109.26866765636274, 0), 237 | (141.92663334350647, 114.0527358251485, 0), 238 | #(138.0546674357987, 120.01713618007396, 0), 239 | (137.43444515430608, 126.13130609854254, 0), 240 | #(139.27571895998688, 132.99633636935727, 0), 241 | (143.52169508528715, 138.78468462166003, 0), 242 | #(148.73723546177817, 142.71347034058024, 0), 243 | (155.46392914504787, 146.3215586709661, 0), 244 | #(161.19854639608613, 148.76522856414377, 0), 245 | (167.95230362806765, 149.65691705621924, 0), 246 | #(174.8956392426905, 147.34021636467332, 0), 247 | (180.4638821990831, 141.78063329568153, 0), 248 | #(183.54504965305318, 135.17796679895395, 0), 249 | (185.1086409590219, 128.92359600898587, 0), 250 | #(185.88186453483573, 122.74109480644104, 0), 251 | (187.49091467183828, 115.80290034083976, 0), 252 | #(190.00928424087985, 110.05787248633183, 0), 253 | (192.90475252264096, 104.34093522818362, 0), 254 | #(196.43646486107863, 98.80226570669744, 0), 255 | (200.35085821316372, 92.8568112843837, 0), 256 | #(202.3025868332382, 86.15294531813072, 0), 257 | (200.54392348488892, 78.95528965692367, 0), 258 | #(195.53559988417797, 74.65488784784873, 0), 259 | (189.8881664928147, 72.7373621513797, 0), 260 | #(183.5152002073849, 69.16919563141558, 0), 261 | (178.23871176836195, 63.733684609286065, 0), 262 | #(173.64252913896135, 56.88738322396486, 0), 263 | (168.91910754972935, 52.06427141526403, 0), 264 | #(162.1546191660268, 48.20588629414677, 0), 265 | (154.1682546093517, 46.49877184012427, 0), 266 | 267 | 268 | # Ring 269 | (224.25324068648297, 147.9524436758256, 0), 270 | (224.74846935937805, 144.1994349074313, 0), 271 | (226.02997098165073, 139.62188542012845, 0), 272 | (228.18526695794196, 136.89766430060075, 0), 273 | (231.28666027454278, 135.22954291835057, 0), 274 | #(234.8748116077093, 134.9775092908075, 0), 275 | #(238.21323153334666, 136.2878374259144, 0), 276 | (240.3925758087457, 138.62419160387685, 0), 277 | (241.42026634187215, 142.49463653061184, 0), 278 | (240.62472305622435, 146.1531031333106, 0), 279 | (238.36847157752044, 149.70438600794074, 0), 280 | (235.14486071395635, 151.91685016815046, 0), 281 | (231.1394656805694, 152.65415495009626, 0), 282 | (227.01753644437755, 151.5663151505824, 0), 283 | 284 | # Connection path 285 | (240.86699107197063, 125.82443995874955, 0), 286 | (241.29491671615156, 113.96245240498043, 0), 287 | (241.16597028566738, 108.45314032105024, 0), 288 | (240.11180577525508, 102.39336690523075, 0), 289 | (237.87211696591172, 97.34319370981748, 0), 290 | (234.33422190572497, 92.52596271515502, 0), 291 | #(225.18437917898285, 86.50554445427109, 0), 292 | (214.0419069842869, 82.20714784400558, 0), 293 | (227.0915502805343, 83.31047993039502, 0), 294 | 295 | (144.40541250063532, 35.422677889928536, 0), 296 | #(142.53576810882134, 29.225344858336957, 0), 297 | (138.8383229410278, 23.635697266638296, 0), 298 | #(134.11276628992894, 19.024569743949158, 0), 299 | (128.7855626387505, 15.990961152144838, 0), 300 | #(123.13528183681751, 14.747388049120119, 0), 301 | (117.34601449001879, 17.177702219031023, 0), 302 | #(115.3575744144685, 23.178636274155057, 0), 303 | (145.5476220423136, 43.97049146590003, 0), 304 | 305 | # Another ring 306 | (108.19437718515435, 24.58976171768642, 0), 307 | #(101.31484050741551, 25.567191720598508, 0), 308 | (95.26477256154789, 28.050311831704228, 0), 309 | #(89.44097511047566, 32.106964269374195, 0), 310 | (84.11303008752341, 37.804850080198456, 0), 311 | #(80.20422271747601, 43.81329931232724, 0), 312 | (77.3130242761945, 51.484231578003715, 0), 313 | (79.75548777142643, 55.40912507443539, 0), 314 | (81.85357174767407, 59.273140495690384, 0), 315 | (78.12489121500599, 63.32190377160861, 0), 316 | (71.98775315369242, 61.611005612352926, 0), 317 | (70.13420211243033, 56.38959284874809, 0), 318 | (75.31712540816625, 52.40333503562683, 0), 319 | (76.08579656459118, 70.39394585510323, 0), 320 | (76.83312450826354, 81.81511986798962, 0), 321 | (77.53064238332415, 92.22656953703483, 0), 322 | (76.97514029795386, 99.77443737162557, 0), 323 | (77.10428934020467, 108.46659600799124, 0), 324 | 325 | (79.27446828297043, 135.62930646908575, 0), 326 | (86.82430599042767, 142.9109391314034, 0), 327 | (97.21348719858959, 151.34973196779322, 0), 328 | (98.78740261374203, 164.7445577144036, 0), 329 | (100.83834890367783, 178.6139922863031, 0), 330 | (102.87866006380524, 192.40579236670126, 0), 331 | (104.320626460479, 205.22144514121095, 0), 332 | (106.87500959760752, 237.038629505919, 0), 333 | (102.20281233432881, 222.18309831240288, 0), 334 | 335 | (57.72114627705365, 23.13809485169263, 0), 336 | (54.83568570922412, 14.708424929273024, 0), 337 | (54.27767980201529, 5.3782874419989755, 0), 338 | (53.272139304689944, 252.46196150562707, 0), 339 | (51.01050041599985, 243.83820524954257, 0), 340 | (48.80560174800455, 241.33605087004472, 0), 341 | (39.329678836652214, 236.1429311063105, 0), 342 | (31.14546466199422, 230.70760410053472, 0), 343 | (21.611261251579762, 223.60075249286575, 0), 344 | (11.843669443919978, 217.0793381945025, 0), 345 | (5.536457891524848, 208.61876431656694, 0), 346 | (255.09809425539206, 195.8263185678207, 0), 347 | (251.39547612381514, 184.7141102190906, 0), 348 | (248.58198579152662, 171.77483939009534, 0), 349 | (246.53870804720572, 161.98961466808638, 0), 350 | (243.8368990519704, 151.31974655348137, 0), 351 | 352 | 353 | #(149.2144335705672, 14.578011501734217, 0), 354 | #(156.94364947478599, 9.58994436478866, 0), 355 | #(164.03014099142175, 6.00799063985961, 0), 356 | #(171.07765062675256, 3.334500417197855, 0), 357 | #(179.2444548665613, 1.1123368945131376, 0), 358 | #(215.27848986739903, 1.031311104213051, 0), 359 | #(222.01174636794326, 3.670624741845923, 0), 360 | (229.94915633552202, 8.25574379953187, 0), 361 | (236.18989881642997, 14.191817719770912, 0), 362 | (241.20794871012063, 22.411911443845636, 0), 363 | (243.55571339998644, 30.353414815761774, 0), 364 | (243.56905632042873, 37.76905228541563, 0), 365 | (240.58237847884453, 43.96967140496043, 0), 366 | (232.72858330553848, 48.48786286723826, 0), 367 | (224.81109773500796, 47.861669040492494, 0), 368 | (221.03124861041616, 41.109409619364044, 0), 369 | (226.95840539191792, 35.410150756790976, 0), 370 | 371 | # A trio 372 | 373 | (89.90026699678978, 113.69701450486184, 0), 374 | (96.54471054383173, 102.66577856382757, 0), 375 | (90.30983739216052, 95.53393310320703, 0), 376 | 377 | (136.37815410409448, 184.2896357815367, 0), 378 | (185.55730135432339, 189.91360389837496, 0), 379 | (204.09846544959535, 188.7803899075964, 0), 380 | (215.90876652182146, 198.16845079019424, 0), 381 | (221.4337231710032, 212.24022789691972, 0), 382 | (216.93893100503382, 228.37042317059561, 0), 383 | 384 | (29.350722419175685, 241.3910478606554, 0), 385 | (20.133346821568303, 249.61823994600195, 0), 386 | (10.346773845022398, 248.06873447397248, 0), 387 | (8.687520306138117, 234.0175857929473, 0), 388 | (180.19359198916518, 167.46072283728094, 0), 389 | 390 | (153.53915644535368, 27.754359273640638, 0), 391 | (161.38353718142577, 29.757919414068702, 0), 392 | (170.0677590490259, 30.130271092380312, 0), 393 | (179.59339329972286, 28.597703363452794, 0), 394 | (189.4279392765973, 28.067489001699336, 0), 395 | (198.33499750013354, 29.65868343871907, 0), 396 | (207.16257103300913, 33.525396320724234, 0), 397 | (214.26991964342764, 37.89603016703796, 0), 398 | 399 | # Slalom 400 | 401 | (243.01162964101823, 53.29815236126152, 0), 402 | (251.60862542062415, 60.85632002783746, 0), 403 | (2.735007844058824, 69.53734218273168, 0), 404 | (14.095482024606563, 77.69082801005295, 0), 405 | (23.471972039490677, 81.91639930162111, 0), 406 | (32.30166252501987, 87.55152763025269, 0), 407 | (37.65790702312686, 94.66520162513443, 0), 408 | (39.7626447039763, 105.40147597228956, 0), 409 | (36.69907274701938, 115.07697813648598, 0), 410 | (30.113742242883983, 124.51823041776727, 0), 411 | (24.911534200174604, 135.1609474815683, 0), 412 | (28.147477481464907, 145.84393781692188, 0), 413 | (36.43823838904034, 150.21278243608475, 0), 414 | (45.96640551063055, 147.5180452017424, 0), 415 | (51.980574624105074, 148.36291831180316, 0), 416 | (59.795262439512925, 153.2373224274378, 0), 417 | (63.95897676752707, 161.86351987200433, 0), 418 | (63.097615671776516, 172.58966025813183, 0), 419 | (61.538210896745355, 182.6335184444167, 0), 420 | (64.86929596875241, 192.97914576198494, 0), 421 | (74.59525860771423, 197.6119924807432, 0), 422 | (87.00369706327608, 194.2260882310563, 0), 423 | (103.1733320858391, 250.00764526828317, 0), 424 | (94.15939093923622, 255.62837785668094, 0), 425 | #(83.67401654337496, 2.7149590232081624, 0), 426 | #(70.60850191243787, 1.789399027573447, 0), 427 | (59.59424926217645, 253.8583515280184, 0), 428 | 429 | (48.25640806258084, 31.462588487650162, 0), 430 | (37.25979121729276, 35.13604875662652, 0), 431 | (28.273100212972505, 40.58901664369962, 0), 432 | (24.242033957115755, 52.87812053035312, 0), 433 | (24.42399217723482, 67.60412822483853, 0), 434 | 435 | 436 | (23.11302685143048, 177.24147101040091, 0), 437 | (29.917117500964366, 186.47212327273195, 0), 438 | (36.97620642816884, 195.17909739273503, 0), 439 | (43.31169944703755, 204.43899210882407, 0), 440 | (47.94207758981136, 216.11495863692357, 0), 441 | (46.6431495617351, 229.2515768394893, 0), 442 | 443 | (79.07814213913232, 116.05322856954518, 0), 444 | (77.9722924574722, 123.58716911561608, 0), 445 | (76.53528353884747, 132.9635164541018, 0), 446 | 447 | (79.70955065957168, 255.9534151970939, 0), 448 | 449 | (113.38319537719572, 173.90327438610638, 0), 450 | (147.372899053233, 186.2782833936401, 0), 451 | (159.38698064080037, 185.01081456322132, 0), 452 | (169.96448788947714, 185.0386365677651, 0), 453 | (219.89807924012396, 236.9262004051333, 0), 454 | (225.55292310898577, 248.69549737791857, 0), 455 | 456 | (109.92257598784407, 152.99223398636448, 0), 457 | (122.41572428622283, 144.0090112324044, 0), 458 | 459 | (70.01113153984998, 35.50622703985409, 0), 460 | ]: 461 | flower = self.create_entity( 462 | TerrainObject( 463 | self.terrain, 464 | model='models/flower.bam', 465 | position=pos, 466 | direction=random()*360, 467 | scale=0.5, 468 | material=mat, 469 | wraparound=64, 470 | ), 471 | Character( 472 | state="closed", 473 | states={ 474 | "locked": {"flower": "closed_idle"}, 475 | "closed": {"flower": "closed_idle"}, 476 | "open": {"flower": ""}, 477 | }, 478 | transitions={ 479 | #(None, "closed"): {"vine": "vine"}, 480 | ("locked", "open"): {"flower": "open", "vine": "vine"}, 481 | ("closed", "open"): {"flower": "open"}, 482 | }, 483 | subparts={ 484 | "flower": ["petal", "petal.001", "petal.002", "petal.003", "petal.004", "petal.005", "petal.006", "petal.007"], 485 | "vine": ["vine"], 486 | }, 487 | play_rates={ 488 | "locked": 2.5, 489 | "closed": 2.5, 490 | "open": 2.5, 491 | }, 492 | ), 493 | SfxPlayer(sounds=['flower-open-a', 'flower-open-b', 'flower-open-c'], volume=1), 494 | Collider(solid=core.CollisionSphere((0, 0, 1.25), 0.2), into_mask=0b01, tangible=False), 495 | name="flower", 496 | ) 497 | self.flowers.append(flower) 498 | 499 | self.rocks = [] 500 | for pos in [ 501 | (179.75304215854243, 149.04975430815733, 0), 502 | (180.93273624802038, 150.24433709387412, 0), 503 | (178.29043263410358, 158.34346624513768, 0), 504 | (177.6256832438217, 159.30518126483173, 0), 505 | (134.3215914171175, 173.988652351578, 0), 506 | (133.30302347421252, 174.64165330846927, 0), 507 | (80.94144112248188, 191.93282441406913, 0), 508 | (77.20476468809473, 194.00487365548406, 0), 509 | (79.57873359185078, 130.15171515500876, 0), 510 | (80.73863022680965, 126.839283605625, 0), 511 | (80.88561502641723, 124.01427131421939, 0), 512 | (79.79887931974496, 69.48015211000896, 0), 513 | (149.63835358731924, 33.8311999428527, 0), 514 | (150.18732678110973, 32.502881064685326, 0), 515 | (158.15873024290195, 27.68981896657041, 0), 516 | (179.8089155563131, 79.04630674494886, 0), 517 | (224.41306290164056, 88.3782599620616, 0), 518 | (226.2407723765043, 90.9309224570788, 0), 519 | (237.0522376142232, 136.17456140019024, 0), 520 | (132.47247814108326, 122.27847218528836, 0), 521 | (152.06672484882824, 133.8405784581478, 0), 522 | (178.86309414515873, 155.04155835228747, 0), 523 | (180.53078168107396, 153.37379785519641, 0), 524 | (137.43537002389613, 89.95124523848395, 0), 525 | (148.5603756930313, 117.4550714412239, 0), 526 | (138.37479640212072, 105.78828222093686, 0), 527 | (182.3238016476745, 125.01421610660083, 0), 528 | (205.3607911535825, 133.28229434734854, 0), 529 | (163.34641171619177, 200.67520097839738, 0), 530 | (158.4226171487318, 201.34250868968968, 0), 531 | (189.14832964429186, 126.82113829517645, 0), 532 | (244.0424530741286, 116.5356348437022, 0), 533 | (237.22882578369487, 157.1559093046709, 0), 534 | (196.15256885114437, 71.72051480474003, 0), 535 | (163.51313851478395, 45.48997053881406, 0), 536 | (141.5132813870503, 61.53002774191786, 0), 537 | (120.72316910563102, 20.294243109646484, 0), 538 | (122.24272007683149, 21.33200783788481, 0), 539 | (208.70198736559144, 229.25940733032687, 0), 540 | (86.73497131930671, 102.13899208990811, 0), 541 | (53.61271092518869, 24.949344475089486, 0), 542 | (56.05233170943425, 0.484183767313744, 0), 543 | (235.80029146524143, 41.958280144946315, 0), 544 | (40.27292605760751, 79.57967833095057, 0), 545 | (18.709533272278772, 241.59275210676637, 0), 546 | (144.31505779068812, 250.57853808283957, 0), 547 | ]: 548 | sub = choice([ 549 | '**/rock.000', 550 | '**/rock.001', 551 | '**/rock.002', 552 | '**/rock.003', 553 | '**/rock.004', 554 | '**/rock.005', 555 | '**/rock.006', 556 | ]) 557 | rock = self.create_entity( 558 | TerrainObject( 559 | self.terrain, 560 | model='models/rocks.bam', 561 | path=sub, 562 | scale=random()*2+2.5, 563 | position=pos, 564 | direction=random()*360, 565 | material=mat, 566 | wraparound=100, 567 | ), 568 | Collider(into_mask=0b01, tangible=False), 569 | GeomCollider(into_mask=0b10), 570 | name="rock", 571 | ) 572 | self.rocks.append(rock) 573 | 574 | shrub_positions = [ 575 | (145.1654531609378, 84.56205859354166, 0), 576 | (133.99739124052564, 95.74738159686336, 0), 577 | (188.5086361801647, 81.75343430304068, 0), 578 | (120.84985539195297, 25.173721786609676, 0), 579 | (113.72210369152982, 86.11736839885505, 0), 580 | (118.45254222807526, 130.11084531968396, 0), 581 | (105.90369980618854, 62.35300278787522, 0), 582 | (132.81316677395853, 54.375810660374704, 0), 583 | (159.66544847341189, 75.52744392827569, 0), 584 | (166.1590325302975, 120.11470288184765, 0), 585 | (200.25444817153718, 151.5906149350707, 0), 586 | (213.10247047346476, 156.3558456952619, 0), 587 | (233.2862580111869, 171.50089134617275, 0), 588 | (249.92281566048962, 177.27985320523442, 0), 589 | (3.9003201741741074, 169.4261505038755, 0), 590 | (12.707674765333122, 128.76098271730848, 0), 591 | (12.646384986309279, 95.51199824924912, 0), 592 | (10.050388457165626, 91.02504625192826, 0), 593 | (5.892282737657839, 79.47690069742129, 0), 594 | (15.188800587787489, 58.6806465252775, 0), 595 | (20.264281891824584, 43.15770845437191, 0), 596 | (11.565858020142342, 31.969478989863532, 0), 597 | (9.226745131047528, 30.45448498551221, 0), 598 | (243.60031874826592, 5.971163450485722, 0), 599 | (240.80898729102034, 0.9199952083555893, 0), 600 | (236.93673779892953, 209.96322605886854, 0), 601 | (230.20474611141256, 179.84192001708206, 0), 602 | (107.51525220879986, 254.1160487773983, 0), 603 | (82.67618586737444, 240.58257896138812, 0), 604 | (79.89228747973159, 236.22924514278054, 0), 605 | (57.787041859678276, 230.07526546529877, 0), 606 | (41.065727410696546, 216.72682489633542, 0), 607 | (36.459922155495775, 204.9283085806328, 0), 608 | (53.23451081174334, 165.643973419102, 0), 609 | (39.13047237358696, 144.00193382852962, 0), 610 | (43.450422436725525, 114.2936127733581, 0), 611 | (38.40750632176204, 108.82287304161925, 0), 612 | (223.65100226296684, 111.22770794568294, 0), 613 | (208.2963932548011, 108.06589481179485, 0), 614 | (183.41914894051658, 103.59768071774427, 0), 615 | (248.79661291648947, 70.35523492614341, 0), 616 | (198.17874225438027, 121.72120637025128, 0), 617 | (175.51503316879152, 174.29220958021978, 0), 618 | (164.40558435819503, 173.09115365242212, 0), 619 | (134.67220837947667, 161.44469876496265, 0), 620 | (89.51731449354446, 168.58697374325217, 0), 621 | (71.12139972322547, 182.84411836889686, 0), 622 | (28.24641123311496, 214.30461995647966, 0), 623 | (243.95284739415047, 237.09047824903968, 0), 624 | (229.65242057585226, 240.47426114245573, 0), 625 | (217.05179232392942, 21.28722249456183, 0), 626 | (18.926434323735435, 108.06204409748898, 0), 627 | (70.37356286571604, 123.7372719120398, 0), 628 | (92.16250483615904, 143.28778753990068, 0), 629 | (144.7786389609179, 121.91396692159228, 0), 630 | (196.95297022878256, 198.07484091148734, 0), 631 | (193.13275512203094, 16.591565950078163, 0), 632 | (188.25510033641814, 50.06646394365584, 0), 633 | (197.5745381255224, 46.908633370961695, 0), 634 | (218.05442077615604, 27.88806244037831, 0), 635 | (155.33119764893593, 187.22615080867038, 0), 636 | (83.3053123099234, 31.260258049062095, 0), 637 | (58.022967222165775, 70.75000957914604, 0), 638 | (129.76709802126658, 205.23256930110296, 0), 639 | (109.87584066549704, 196.23776666594298, 0), 640 | (69.66903092290568, 2.340512632039162, 0), 641 | (82.49145313340462, 28.238068870234255, 0), 642 | ] 643 | self.shrubs = [] 644 | for pos in shrub_positions: 645 | scale = random()*0.125+0.25 646 | dir = random()*360 647 | sub = choice([ 648 | '**/shrub', 649 | '**/shrub.001', 650 | '**/shrub.002', 651 | '**/shrub.003', 652 | ]) 653 | shrub = self.create_entity( 654 | TerrainObject( 655 | self.terrain, 656 | model='models/shrub.bam', 657 | path=sub, 658 | scale=scale, 659 | position=pos, 660 | direction=dir, 661 | material=mat, 662 | shader=self.tree_shader, 663 | wraparound=64, 664 | draw_distance=64, 665 | ), 666 | Collider(solid=core.CollisionSphere((0, 0, 1), 1.3), into_mask=0b11), 667 | name="shrub", 668 | ) 669 | self.shrubs.append(shrub) 670 | 671 | tree_positions = [ 672 | (141.44997331973642, 90.01814911779846, 0), 673 | (140.939259035704, 104.62580330682461, 0), 674 | (145.43459463675472, 120.0549962850645, 0), 675 | (174.96956247302137, 152.8717330910715, 0), 676 | (190.145361010965, 181.457325047707, 0), 677 | (201.05797662398624, 185.63245228897478, 0), 678 | (210.82617761726922, 183.22408375244615, 0), 679 | (232.11213740634656, 145.0250765825596, 0), 680 | (168.49540300577198, 43.917871248449586, 0), 681 | (148.15340704194222, 40.82486758175469, 0), 682 | (180.8336831977601, 72.20419613504203, 0), 683 | (186.68997325028943, 78.32692096719747, 0), 684 | (195.39788697159972, 81.90468045730204, 0), 685 | (213.25849217928504, 53.10199070005116, 0), 686 | (114.66943149608485, 15.391007594793304, 0), 687 | (52.98666565712674, 23.21233977848427, 0), 688 | (75.4807246783207, 58.36780909880394, 0), 689 | (36.67038516706891, 78.0532194542566, 0), 690 | (80.52830178248198, 89.48209477996595, 0), 691 | (80.69478875859468, 107.38463114589469, 0), 692 | (71.49999731337809, 152.53717564605435, 0), 693 | (113.74923390426176, 171.87223098072752, 0), 694 | (155.88697741926651, 203.82782631618855, 0), 695 | (160.8053323613594, 204.3887265874588, 0), 696 | (167.13062975700038, 209.98871913338877, 0), 697 | (116.12119500751355, 159.71674422503244, 0), 698 | (121.89508999517197, 171.6454485532199, 0), 699 | (125.33541961548639, 180.55647815406377, 0), 700 | (225.44289989186166, 94.26375479526874, 0), 701 | (242.03323714737428, 92.30854507678191, 0), 702 | (226.78261885364577, 46.699038760172556, 0), 703 | (236.31441122722777, 35.811792309990395, 0), 704 | (244.0675453062362, 41.461836300440766, 0), 705 | (0.9360229815382952, 118.35309110859266, 0), 706 | (2.9367472705065185, 110.5310509314511, 0), 707 | (80.51599951596246, 97.06288773442499, 0), 708 | (3.984805460625611, 139.2271346715085, 0), 709 | (250.4588108563478, 140.61375465097203, 0), 710 | (28.25598332004428, 168.6564255412971, 0), 711 | (33.05219488106874, 176.59245635008432, 0), 712 | (39.33471815426912, 181.00305492759665, 0), 713 | (45.57911836533825, 188.0264031340052, 0), 714 | (49.84149714167223, 201.87253094669632, 0), 715 | (20.992753249862538, 228.1386104651079, 0), 716 | (14.683335768444953, 235.34901263819225, 0), 717 | (12.231489481186854, 241.9423527111655, 0), 718 | (11.8621285444361, 250.52279820373835, 0), 719 | (108.12901491632508, 21.768645663016716, 0), 720 | (120.74501892422101, 9.173819912513748, 0), 721 | 722 | (130.93986109342399, 245.03627995327133, 0), 723 | (123.47617711611365, 236.5177887693719, 0), 724 | (118.54018894903761, 230.31552873125054, 0), 725 | (112.11442258649323, 229.15232753210634, 0), 726 | (115.33554257802335, 235.12176260428103, 0), 727 | #(188.00154226065294, 244.918254643617, 0), 728 | ] 729 | 730 | for pos in tree_positions: 731 | scale = random()*0.5+0.5 732 | dir = random()*360 733 | sub = choice([ 734 | '**/tree', 735 | '**/tree.001', 736 | '**/tree.002', 737 | '**/tree.003', 738 | ]) 739 | #pos = (pos[0], pos[1], -random()) 740 | tree = self.create_entity( 741 | TerrainObject( 742 | self.terrain, 743 | model='models/trees.bam', 744 | path=sub, 745 | scale=scale, 746 | position=pos, 747 | direction=dir, 748 | material=mat, 749 | shader=self.tree_shader, 750 | wraparound=120, 751 | ), 752 | Collider(solid=core.CollisionCapsule((0, 0, -1), (0, 0, 5), 0.5), into_mask=0b11), 753 | name="tree", 754 | ) 755 | self.trees.append(tree) 756 | 757 | #self.create_entity(AmbientLight(intensity=0.1, color=(0.5, 0.7, 0.5))) 758 | self.flush_component_updates() 759 | 760 | ctrl = PlayerController() 761 | self.add_system(ctrl, sort=-1) 762 | self.add_system(CameraSystem(), sort=2) 763 | 764 | self.update_system(ctrl) 765 | 766 | self.accept('player-into-flower', self.handle_collision) 767 | 768 | #render.find("**/butterfly").set_color((2.0, 0.5, 1.0, 1), 10000) 769 | #render.find("**/butterfly").set_color_scale((1, 1, 1, 1), 10000) 770 | #render.find("**/butterfly").set_shader_off(20) 771 | 772 | self.num_flowers = len(self.flowers) 773 | 774 | #sattr = render.get_attrib(core.ShaderAttrib) 775 | #sattr = sattr.set_flag(core.ShaderAttrib.F_hardware_skinning, True) 776 | #render.set_attrib(sattr) 777 | 778 | #print(self.camList) 779 | 780 | radius = 12 781 | size = math.ceil(radius * 2) 782 | half_size = size * 0.5 783 | r = half_size / radius 784 | self.spot = core.PNMImage(size, size, 2) 785 | self.spot.render_spot((1, 1, 1, 1), (1, 1, 1, 0), 0, r) 786 | self.spot.apply_exponent(2, 2, 2, 2) 787 | 788 | self._last_note = 'flower-open-c' 789 | 790 | cm = core.CardMaker("minimap") 791 | cm.set_frame(-1, 0, 0, 1) 792 | cm.set_uv_range((0, 0), (1, 1)) 793 | self.minimap = base.a2dBottomRight.attach_new_node(cm.generate()) 794 | self.minimap.set_texture(self.terrain[Terrain]._sat_tex) 795 | self.minimap.set_scale(2/3.0) 796 | self.minimap.hide() 797 | self.minimap.set_transparency(1) 798 | self.minimap.set_alpha_scale(0.75) 799 | 800 | sz = 0.025 801 | cm = core.CardMaker("dolmen") 802 | cm.set_frame(-sz, sz, -sz, sz) 803 | cm.set_color((0.5, 0.5, 0.5, 1)) 804 | 805 | pos = self.dolmen[TerrainObject].position 806 | minimap_icon = self.minimap.attach_new_node(cm.generate()) 807 | minimap_icon.set_texture_off(10) 808 | minimap_icon.set_pos(pos[0] / 256 - 1, 0, pos[1] / 256) 809 | minimap_icon.set_r(45) 810 | 811 | sz = 0.005 812 | cm = core.CardMaker("flower") 813 | cm.set_frame(-sz, sz, -sz, sz) 814 | cm.set_color((0.9, 0.2, 0.2, 1)) 815 | 816 | self.flower_icons = {} 817 | 818 | for flower in self.flowers: 819 | pos = flower[TerrainObject].position 820 | minimap_icon = self.minimap.attach_new_node(cm.generate()) 821 | minimap_icon.set_texture_off(10) 822 | minimap_icon.set_pos(pos[0] / 256 - 1, 0, pos[1] / 256) 823 | self.flower_icons[flower._uid] = minimap_icon 824 | 825 | sz = 0.0075 826 | cm = core.CardMaker("superflower") 827 | cm.set_frame(-sz, sz, -sz, sz) 828 | cm.set_color((0.9, 1.0, 0.0, 1)) 829 | 830 | pos = self.superflower[TerrainObject].position 831 | minimap_icon = self.minimap.attach_new_node(cm.generate()) 832 | minimap_icon.set_texture_off(10) 833 | minimap_icon.set_pos(pos[0] / 256 - 1, 0, pos[1] / 256) 834 | self.flower_icons[self.superflower._uid] = minimap_icon 835 | 836 | sz = 0.005 837 | cm = core.CardMaker("tree") 838 | cm.set_frame(-sz, sz, -sz, sz) 839 | cm.set_color((0.0, 0.5, 0.0, 1)) 840 | 841 | for tree in self.trees: 842 | pos = tree[TerrainObject].position 843 | minimap_icon = self.minimap.attach_new_node(cm.generate()) 844 | minimap_icon.set_texture_off(10) 845 | minimap_icon.set_pos(pos[0] / 256 - 1, 0, pos[1] / 256) 846 | 847 | sz = 0.01 848 | cm = core.CardMaker("cursor") 849 | cm.set_frame(-sz, sz, -sz, sz) 850 | cm.set_color((0.4, 0.2, 0.7, 1)) 851 | minimap_icon = self.minimap.attach_new_node(cm.generate()) 852 | minimap_icon.set_texture_off(10) 853 | arrow = minimap_icon.attach_new_node(cm.generate()) 854 | arrow.set_r(45) 855 | arrow.set_scale(0.707) 856 | arrow.set_z(0.01) 857 | self.minimap_icon = minimap_icon 858 | 859 | self.accept('m', self.toggle_minimap) 860 | 861 | self._goodenough_activated = False 862 | 863 | #for flower in self.flowers: 864 | # self.paint_at(flower[TerrainObject].position) 865 | 866 | vine = self.superflower[Character]._state_actors['locked'].find('**/vine') 867 | vine.set_color((188/430.0, 152/430.0, 101/430.0, 1.0), 1000) 868 | 869 | vine = self.superflower[Character]._state_actors['closed'].find('**/vine') 870 | vine.remove_node() 871 | 872 | vine = self.superflower[Character]._state_actors['open'].find('**/vine') 873 | vine.remove_node() 874 | 875 | self.player[Character]._actor.loop('flap', partName='morphs') 876 | self.player[Character]._state_actors['end'].hide() 877 | 878 | def activate(self): 879 | self.add_system(CollisionDetectionSystem(), sort=4) 880 | 881 | self.camera[Camera].active = True 882 | 883 | def add_system(self, system, sort): 884 | ECSWorld.add_system(self, system, sort) 885 | task = base.task_mgr.add( 886 | self.run_system, 887 | repr(system), 888 | extraArgs=[system], 889 | appendTask=True, 890 | sort=sort, 891 | ) 892 | return task 893 | 894 | def run_system(self, system, task): 895 | self.update_system(system) 896 | return task.cont 897 | 898 | def toggle_minimap(self): 899 | if not self.minimap.is_hidden(): 900 | self.minimap.hide() 901 | base.taskMgr.remove('update-minimap') 902 | return 903 | 904 | self.minimap.show() 905 | base.taskMgr.add(self._update_minimap, 'update-minimap') 906 | 907 | def _update_minimap(self, task): 908 | pos = self.player[TerrainObject].position 909 | self.minimap_icon.set_pos(pos[0] / 256 - 1, 0, pos[1] / 256) 910 | self.minimap_icon.set_r(-self.player[TerrainObject].direction) 911 | return task.cont 912 | 913 | def print_pos(self): 914 | pos = self.player[TerrainObject].position 915 | print(' ' + str((pos[0], pos[1], 0)) + ',') 916 | 917 | def press_2(self): 918 | for flower in self.flowers: 919 | self.paint_at(flower[TerrainObject].position) 920 | 921 | terrain = self.terrain[Terrain] 922 | 923 | def press_3(self): 924 | self.enough_paint() 925 | terrain = self.terrain[Terrain] 926 | 927 | def handle_collision(self, entry): 928 | flower = entry.into_node.get_python_tag('entity') 929 | self.pollinate_flower(flower) 930 | 931 | def pollinate_flower(self, flower): 932 | if Character in flower: 933 | if flower[Character].state == 'open': 934 | return 935 | 936 | flower[Character].state = 'open' 937 | 938 | if flower._uid in self.flower_icons: 939 | self.flower_icons[flower._uid].remove_node() 940 | 941 | if Collider in flower: 942 | del flower[Collider] 943 | 944 | if flower._uid.name == 'flower': 945 | notes = set(['flower-open-a', 'flower-open-b', 'flower-open-c']) 946 | notes.discard(self._last_note) 947 | note = choice(list(notes)) 948 | flower[SfxPlayer].play(note) 949 | self._last_note = note 950 | elif flower._uid.name == 'superflower': 951 | flower[SfxPlayer].play('flower-open-a') 952 | flower[SfxPlayer].play('flower-open-b') 953 | flower[SfxPlayer].play('flower-open-c') 954 | 955 | #self.player[Speed].current = 0.0 956 | #self.player[Controls].enabled = False 957 | 958 | obj = flower[TerrainObject] 959 | pos = obj.position 960 | 961 | self.paint_at(pos) 962 | 963 | #self.player[TerrainObject]._root.hprInterval(4, (360, 0, 0), blendType='easeInOut').start() 964 | if flower._uid.name == "flower": 965 | self.num_flowers -= 1 966 | 967 | if not self._goodenough_activated: 968 | print("%d flowers to go!" % (self.num_flowers)) 969 | 970 | elif flower._uid.name == "superflower": 971 | # Superflower. 972 | self.ending() 973 | 974 | def enough_paint(self): 975 | if self._goodenough_activated: 976 | return 977 | self._goodenough_activated = True 978 | 979 | # Pollinate all remaining flowers. 980 | for flower in self.flowers: 981 | if Character in flower and flower[Character].state == 'closed': 982 | self.pollinate_flower(flower) 983 | 984 | print("Good enough!") 985 | dolly = self.player[TerrainObject]._root.attach_new_node("dolly") 986 | dolly.set_compass() 987 | saved_xform = base.cam.get_transform() 988 | saved_parent = base.cam.get_parent() 989 | base.cam.wrt_reparent_to(dolly) 990 | 991 | dolmen_pos = self.dolmen[TerrainObject]._root.get_pos() 992 | 993 | # Which way around is quicker? 994 | pos = self.player[TerrainObject].position 995 | if pos[1] < dolmen_pos[1] - 128: 996 | dolmen_pos.y -= 256 997 | 998 | old_hpr = base.cam.get_hpr() 999 | base.cam.set_pos(base.cam.get_pos() + (0, 0, 10)) 1000 | base.cam.look_at(render, dolmen_pos) 1001 | look_hpr = base.cam.get_hpr() 1002 | base.cam.set_hpr(old_hpr) 1003 | base.cam.set_pos(base.cam.get_pos() - (0, 0, 10)) 1004 | 1005 | while look_hpr[0] > old_hpr[0] + 180: 1006 | look_hpr[0] -= 360 1007 | while look_hpr[0] < old_hpr[0] - 180: 1008 | look_hpr[0] += 360 1009 | 1010 | Sequence( 1011 | Parallel( 1012 | dolly.posInterval(3, (0, 0, 10), blendType='easeInOut'), 1013 | base.cam.hprInterval(3, look_hpr, blendType='easeInOut'), 1014 | ), 1015 | Wait(1.5), 1016 | Parallel( 1017 | dolly.posInterval(1.5, (0, 0, 0), blendType='easeInOut'), 1018 | base.cam.hprInterval(1.5, old_hpr, blendType='easeInOut'), 1019 | ), 1020 | Func(lambda: (base.cam.reparent_to(saved_parent) or base.cam.set_transform(saved_xform))), 1021 | ).start() 1022 | 1023 | if Character in self.superflower: 1024 | self.superflower[Character].state = 'closed' 1025 | 1026 | self.accept('player-into-superflower', self.handle_collision) 1027 | 1028 | def ending(self): 1029 | self._goodenough_activated = True 1030 | 1031 | if Controls in self.player: 1032 | del self.player[Controls] 1033 | 1034 | pos = self.superflower[TerrainObject].position 1035 | pos = (pos[0], pos[1], 2.5) 1036 | self.player[TerrainObject].position = pos 1037 | 1038 | pos = (pos[0], pos[1], self.superflower[TerrainObject]._root.get_z() + 2.5) 1039 | self.player[TerrainObject]._root.set_pos(pos) 1040 | 1041 | self.player[Character].state = 'end' 1042 | self.superflower[SfxPlayer].play('dance') 1043 | self.player[Character]._actor.set_play_rate(0.5, 'flap', partName='morphs') 1044 | 1045 | dolly = render.attach_new_node("dolly") 1046 | dolly.set_pos(pos[0], pos[1], pos[2] + 2.5) 1047 | saved_xform = base.cam.get_transform() 1048 | saved_parent = base.cam.get_parent() 1049 | base.cam.wrt_reparent_to(dolly) 1050 | 1051 | old_hpr = base.cam.get_hpr() 1052 | look_hpr = core.VBase3(0, -30, 0) 1053 | 1054 | while look_hpr[0] > old_hpr[0] + 180: 1055 | look_hpr[0] -= 360 1056 | while look_hpr[0] < old_hpr[0] - 180: 1057 | look_hpr[0] += 360 1058 | 1059 | base.transitions.setFadeColor(1, 1, 1) 1060 | Sequence( 1061 | Parallel( 1062 | Sequence(Wait(2.0), Func(self.fill_map)), 1063 | base.cam.posInterval(3, (0, -10, 5), blendType='easeInOut'), 1064 | base.cam.hprInterval(4, look_hpr, blendType='easeInOut'), 1065 | dolly.scaleInterval(14, 1.5, blendType='easeInOut'), 1066 | ), 1067 | base.transitions.getFadeOutIval(3.0), 1068 | ).start() 1069 | 1070 | print("Thank you for playing our game!") 1071 | 1072 | def fill_map(self, v=1): 1073 | terrain = self.terrain[Terrain] 1074 | terrain._sat_img.fill(v) 1075 | terrain._sat_tex.load(terrain._sat_img) 1076 | 1077 | def paint_at(self, pos): 1078 | terrain = self.terrain[Terrain] 1079 | bound = terrain._sat_img.get_x_size() - 1 1080 | 1081 | #num_steps = int(math.ceil(2.5 / globalClock.dt)) 1082 | num_steps = 80 1083 | per_step = 0.333 / num_steps 1084 | 1085 | point = pos[0] * terrain._scale.x * bound, (bound - pos[1] * terrain._scale.y * bound) 1086 | 1087 | def paint_more(task): 1088 | px_x = int(round(point[0] - self.spot.get_x_size() / 2)) 1089 | px_y = int(round(point[1] - self.spot.get_y_size() / 2)) 1090 | terrain._sat_img.blend_sub_image( 1091 | self.spot, 1092 | px_x, 1093 | px_y, 1094 | 0, 0, -1, -1, 1095 | (per_step * 0.1) * (task.get_elapsed_frames() + 1) 1096 | ) 1097 | 1098 | if px_x < self.spot.get_x_size(): 1099 | terrain._sat_img.blend_sub_image( 1100 | self.spot, 1101 | px_x + terrain._sat_img.get_x_size(), 1102 | px_y, 1103 | 0, 0, -1, -1, 1104 | (per_step * 0.1) * (task.get_elapsed_frames() + 1) 1105 | ) 1106 | 1107 | if px_y < self.spot.get_y_size(): 1108 | terrain._sat_img.blend_sub_image( 1109 | self.spot, 1110 | px_x, 1111 | px_y + terrain._sat_img.get_y_size(), 1112 | 0, 0, -1, -1, 1113 | (per_step * 0.1) * (task.get_elapsed_frames() + 1) 1114 | ) 1115 | 1116 | if px_x > terrain._sat_img.get_x_size() - self.spot.get_x_size(): 1117 | terrain._sat_img.blend_sub_image( 1118 | self.spot, 1119 | px_x - terrain._sat_img.get_x_size(), 1120 | px_y, 1121 | 0, 0, -1, -1, 1122 | (per_step * 0.1) * (task.get_elapsed_frames() + 1) 1123 | ) 1124 | 1125 | if px_y > terrain._sat_img.get_y_size() - self.spot.get_y_size(): 1126 | terrain._sat_img.blend_sub_image( 1127 | self.spot, 1128 | px_x, 1129 | px_y - terrain._sat_img.get_y_size(), 1130 | 0, 0, -1, -1, 1131 | (per_step * 0.1) * (task.get_elapsed_frames() + 1) 1132 | ) 1133 | 1134 | terrain._sat_tex.load(terrain._sat_img) 1135 | 1136 | if task.get_elapsed_frames() < num_steps: 1137 | return task.cont 1138 | 1139 | if not self._goodenough_activated: 1140 | gray = terrain._sat_img.get_average_gray() 1141 | print("%.1f %% pollinated" % (gray * 100)) 1142 | if gray >= PAINT_THRESHOLD: 1143 | self.enough_paint() 1144 | 1145 | taskMgr.add(paint_more) 1146 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | panda3d~=1.10.6 2 | panda3d-simplepbr>=0.4 3 | wecs==0.1.11a0 4 | -------------------------------------------------------------------------------- /run_game.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from game import main 4 | 5 | 6 | if __name__ == '__main__': 7 | main() 8 | -------------------------------------------------------------------------------- /scripts/make_grass_patch.py: -------------------------------------------------------------------------------- 1 | from panda3d.core import * 2 | from random import random 3 | 4 | 5 | assets_dir = Filename(ExecutionEnvironment.expand_string("$MAIN_DIR/../assets")) 6 | models_dir = Filename(assets_dir, "models") 7 | 8 | 9 | def generate(model, size, ground_density, grass_density, min_scale=1.0, max_scale=1.0): 10 | root = NodePath("patch") 11 | 12 | model = NodePath(ModelPool.load_model(Filename(models_dir, model))) 13 | model.clear_model_nodes() 14 | model.flatten_strong() 15 | 16 | ground_dimension = int(round(size * ground_density)) 17 | if ground_dimension == 0: 18 | ground_dimension = 1 19 | ground_spacing = size / ground_dimension 20 | 21 | for x in range(-1, ground_dimension + 1): 22 | for y in range(-1, ground_dimension + 1): 23 | cm = CardMaker("ground") 24 | cm.set_frame( 25 | ((x - 0.5) * ground_spacing, (y - 0.5) * ground_spacing, 0), 26 | ((x + 0.5) * ground_spacing, (y - 0.5) * ground_spacing, 0), 27 | ((x + 0.5) * ground_spacing, (y + 0.5) * ground_spacing, 0), 28 | ((x - 0.5) * ground_spacing, (y + 0.5) * ground_spacing, 0), 29 | ) 30 | cm.set_uv_range((0, 0), (1, 0)) 31 | root.attach_new_node(cm.generate()) 32 | 33 | grass_dimension = int(round(size * grass_density)) 34 | if grass_dimension > 0: 35 | grass_spacing = size / grass_dimension 36 | 37 | for x in range(grass_dimension): 38 | for y in range(grass_dimension): 39 | inst = model.copy_to(root) 40 | inst.set_pos((x + random() - 0.5) * grass_spacing, (y + random() - 0.5) * grass_spacing, 0) 41 | inst.set_h(random() * 360) 42 | 43 | t = random() 44 | inst.set_scale(min_scale * t + max_scale * (1 - t)) 45 | inst.set_sz(inst.get_sz() * 1.5) 46 | 47 | root.set_pos(-size * 0.5, -size * 0.5, 0) 48 | root.flatten_strong() 49 | root = root.find("**/+GeomNode") 50 | root.set_tag('patch_size', str(size)) 51 | 52 | bmin, bmax = root.get_tight_bounds() 53 | #root.node().set_bounds_type(BoundingVolume.BT_box) 54 | #root.node().set_final(True) 55 | #root.node().set_bounds(BoundingBox((size * -0.5 - 5, size * -0.5 - 5, 0), (size * 0.5 + 5, size * 0.5 + 5, 20))) 56 | return root 57 | 58 | 59 | if __name__ == '__main__': 60 | size = 16 61 | ul = generate("star3-sub.egg", size=size, ground_density=1.0, grass_density=2.0, min_scale=0.8*0.5, max_scale=1.2*0.5) 62 | hi = generate("star3.egg", size=size, ground_density=1.0, grass_density=1.75, min_scale=0.8*0.5, max_scale=1.2*0.5) 63 | md = generate("star3.egg", size=size, ground_density=1.0, grass_density=1.0, min_scale=0.8*0.5, max_scale=1.2*0.5) 64 | no = generate("star3.egg", size=size, ground_density=1.0, grass_density=0) 65 | 66 | #ul.set_color_scale((1, 0, 1, 1), 100000) 67 | #hi.set_color_scale((1, 0, 0, 1), 100000) 68 | #md.set_color_scale((1, 0, 0, 1), 100000) 69 | 70 | lod = FadeLODNode("patch") 71 | lod.add_child(no.node()) 72 | lod.add_switch(1000, 120) 73 | 74 | lod.add_child(md.node()) 75 | lod.add_switch(120, 80) 76 | 77 | lod.add_child(hi.node()) 78 | lod.add_switch(80, 32) 79 | 80 | lod.add_child(ul.node()) 81 | lod.add_switch(32, 0) 82 | 83 | lod.set_tag('patch_size', str(size)) 84 | 85 | #lod.set_bounds(BoundingBox((0, 0, 0), (size, size, 1))) 86 | #lod.set_final(True) 87 | 88 | sga = SceneGraphAnalyzer() 89 | sga.add_node(lod) 90 | print(sga) 91 | 92 | NodePath(lod).write_bam_file(Filename(assets_dir, "models/patch.bam")) 93 | print("Written to assets/models/patch.bam") 94 | 95 | # Make a potato version. 96 | size = 32 97 | md = generate("star3.egg", size=size, ground_density=1.0, grass_density=1.0, min_scale=0.8*0.5, max_scale=1.2*0.5) 98 | lo = generate("star3.egg", size=size, ground_density=0.75, grass_density=0.5, min_scale=0.8*0.5, max_scale=1.2*0.5) 99 | no = generate("star3.egg", size=size, ground_density=0.5, grass_density=0) 100 | 101 | lod = FadeLODNode("patch") 102 | lod.add_child(no.node()) 103 | lod.add_switch(1000, 64) 104 | 105 | lod.add_child(lo.node()) 106 | lod.add_switch(64, 32) 107 | 108 | lod.add_child(md.node()) 109 | lod.add_switch(32, 0) 110 | 111 | lod.set_tag('patch_size', str(size)) 112 | 113 | sga = SceneGraphAnalyzer() 114 | sga.add_node(lod) 115 | 116 | NodePath(lod).write_bam_file(Filename(assets_dir, "models/patch-potato.bam")) 117 | print("Written to assets/models/patch-potato.bam") 118 | -------------------------------------------------------------------------------- /scripts/make_grass_textures.py: -------------------------------------------------------------------------------- 1 | from panda3d.core import * 2 | from direct.showbase.ShowBase import ShowBase 3 | 4 | load_prc_file_data("", "win-size 512 512") 5 | 6 | base = ShowBase(windowType='offscreen') 7 | base.disable_mouse() 8 | 9 | assets_dir = Filename(ExecutionEnvironment.expand_string("$MAIN_DIR/../assets")) 10 | 11 | tuft = base.loader.load_model(Filename(assets_dir, "/models/tuft.egg")) 12 | tuft.set_two_sided(True) 13 | tuft.reparent_to(base.render) 14 | 15 | pmin, pmax = tuft.get_tight_bounds() 16 | radius = tuft.get_bounds().get_radius() 17 | 18 | dim = max(pmax[2] - pmin[2], radius * 2) 19 | 20 | lens = OrthographicLens() 21 | lens.set_near_far(-dim, dim * 3) 22 | lens.set_film_size(dim, dim) 23 | lens.film_offset = (0, dim * 0.5) 24 | base.cam.node().set_lens(lens) 25 | 26 | base.cam.set_y(-dim) 27 | 28 | tex = Texture() 29 | base.win.add_render_texture(tex, GraphicsOutput.RTM_copy_ram) 30 | 31 | base.camera.set_h(0) 32 | base.graphicsEngine.render_frame() 33 | base.graphicsEngine.render_frame() 34 | tex.write(Filename(assets_dir, "/textures/tuft0.png")) 35 | 36 | base.camera.set_h(60) 37 | base.graphicsEngine.render_frame() 38 | base.graphicsEngine.render_frame() 39 | tex.write(Filename(assets_dir, "/textures/tuft1.png")) 40 | 41 | base.camera.set_h(120) 42 | base.graphicsEngine.render_frame() 43 | base.graphicsEngine.render_frame() 44 | tex.write(Filename(assets_dir, "/textures/tuft2.png")) 45 | -------------------------------------------------------------------------------- /scripts/process_flower.py: -------------------------------------------------------------------------------- 1 | from panda3d import core 2 | 3 | mat = core.Material() 4 | mat.base_color = (2.0, 0.75, 0.6, 1) 5 | model = core.NodePath(core.ModelPool.load_model("assets/models/flower.bam")) 6 | model.find('**/petals').set_material(mat, 10000000) 7 | model.find('**/petals').set_light_off(100) 8 | model.find('**/leafs').remove_node() 9 | model.find('**/vine').remove_node() 10 | 11 | # Work around blend2bam bug that manifests when flattening 12 | for gnode in model.find_all_matches("**/+GeomNode"): 13 | for geom in gnode.node().modify_geoms(): 14 | vdata = geom.modify_vertex_data() 15 | if not vdata.get_format().has_column("transform_blend") and vdata.get_transform_blend_table(): 16 | vdata.clear_transform_blend_table() 17 | 18 | model.flatten_strong() 19 | 20 | gnode = model.find("**/+GeomNode") 21 | gnode.name = "flower" 22 | 23 | #character = model.get_child(0) 24 | #model = core.NodePath(core.LODNode("flower")) 25 | #model.node().add_child(character.node()) 26 | #model.node().add_switch(50, 0) 27 | 28 | model.write_bam_file("assets/models/flower.bam") 29 | 30 | sga = core.SceneGraphAnalyzer() 31 | sga.add_node(model.node()) 32 | print(sga) 33 | model.ls() 34 | -------------------------------------------------------------------------------- /settings.prc: -------------------------------------------------------------------------------- 1 | # To show frames, comment this out: 2 | #show-frame-rate-meter true 3 | 4 | # If you get crashes, comment out these two lines: 5 | threading-model /Draw 6 | x-init-threads true 7 | 8 | model-path $MAIN_DIR/assets 9 | 10 | window-title pollen. 11 | fullscreen false 12 | #win-fixed-size true 13 | win-size 1022 766 14 | win-origin -2 -2 15 | 16 | icon-filename icons/icon.ico 17 | 18 | textures-power-2 none 19 | 20 | bounds-type best 21 | 22 | text-default-font fonts/Nunito-Bold.ttf 23 | text-pixels-per-unit 64 24 | 25 | audio-library-name p3openal_audio 26 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name='pollen.', 5 | version='1.0.0', 6 | options={ 7 | 'build_apps': { 8 | 'include_patterns': { 9 | 'assets/**', 10 | 'settings.prc', 11 | 'README.md', 12 | }, 13 | 'package_data_dirs': { 14 | 'simplepbr': [ 15 | ('simplepbr/*.vert', '', {}), 16 | ('simplepbr/*.frag', '', {}), 17 | ], 18 | }, 19 | 'gui_apps': { 20 | 'run_game': 'run_game.py', 21 | }, 22 | 'icons': { 23 | 'run_game': [ 24 | 'assets/icons/icon-512.png', 25 | 'assets/icons/icon-128.png', 26 | 'assets/icons/icon-64.png', 27 | 'assets/icons/icon-48.png', 28 | 'assets/icons/icon-32.png' 29 | ], 30 | }, 31 | 'log_filename': "$USER_APPDATA/pollen/output.log", 32 | 'log_append': False, 33 | 'plugins': [ 34 | 'pandagl', 35 | 'p3openal_audio', 36 | ], 37 | }, 38 | } 39 | ) 40 | --------------------------------------------------------------------------------