Compare commits

..
278 Commits
Author SHA1 Message Date
Vladislav Khorev efc696de09 Final adjustments 2026-04-22 11:08:25 +03:00
Vladislav Khorev 074c586edf Final version 2026-04-20 01:11:24 +03:00
Vladislav Khorev d64659c8d6 Now we can win or loose, also restart 2026-04-20 00:36:32 +03:00
Vladislav Khorev d557c4b530 Working on fog, dialogs, etc 2026-04-20 00:21:51 +03:00
Vladislav Khorev fd098e8346 working on the game 2026-04-19 23:25:18 +03:00
Vladislav Khorev 3622f2b888 Merge branch 'escape001' of gitea.fishrungames.com:salmon-engine-projects/space-game001 into escape001 2026-04-19 22:04:26 +03:00
Vladislav Khorev ce8bc2d142 Added bandit chase, change dialogs 2026-04-19 22:03:56 +03:00
Vlad e293f58413 Merge branch 'escape001' of gitea.fishrungames.com:salmon-engine-projects/space-game001 into escape001 2026-04-20 01:03:15 +06:00
Vlad 0495c3946c path forest loc 2026-04-20 01:02:49 +06:00
Vladislav Khorev 28a3ace187 Add more dialogs logic 2026-04-19 20:28:22 +03:00
Vladislav Khorev 61d806a7a1 Added more dialogs, some minor tweaks 2026-04-19 20:00:15 +03:00
Vladislav Khorev e4bcf62afa Merge branch 'escape001' of gitea.fishrungames.com:salmon-engine-projects/space-game001 into escape001 2026-04-19 18:46:23 +03:00
Vladislav Khorev 709327b3af Adapt for web 2026-04-19 18:46:06 +03:00
Vladislav Khorev 920376d686 Working on web version 2026-04-19 17:13:48 +03:00
Vlad a6d9716ddf Merge branch 'escape001' of gitea.fishrungames.com:salmon-engine-projects/space-game001 into escape001 2026-04-19 20:11:20 +06:00
Vlad 1a6410dc12 camera in barn 2026-04-19 20:10:28 +06:00
Vladislav Khorev 360d6999c2 merge 2026-04-19 15:41:49 +03:00
Vladislav Khorev 7fe425bf07 Adjustments 2026-04-19 15:33:54 +03:00
Vlad c9324bd30b fix menu 2026-04-19 17:24:37 +06:00
Vlad 777ee30a24 loca 3 2026-04-19 15:39:44 +06:00
Vlad c9e9755e28 added music 2026-04-19 14:13:27 +06:00
Vlad f8b4174eea Merge branch 'escape001' of gitea.fishrungames.com:salmon-engine-projects/space-game001 into escape001 2026-04-19 13:47:11 +06:00
Vlad 9d4da63e36 added menu 2026-04-19 13:46:44 +06:00
Vladislav Khorev a0b0db6f6a Working on village 2026-04-19 10:38:40 +03:00
Vladislav Khorev 60a1422232 Adding quests 2026-04-19 09:28:28 +03:00
Vladislav Khorev 25bb79fb42 police chase 2026-04-19 08:22:03 +03:00
Vladislav Khorev 8e0936836e Some quests 2026-04-19 07:56:42 +03:00
Vladislav Khorev 542939207f Change girlfriend behavior, set some dialogs 2026-04-19 06:37:35 +03:00
Vladislav Khorev 2195542974 Bug fixing 2026-04-18 22:27:23 +03:00
Vladislav Khorev c6bd5385e3 Merge branch 'escape001' of gitea.fishrungames.com:salmon-engine-projects/space-game001 into escape001 2026-04-18 22:15:41 +03:00
Vladislav Khorev abd3da30e2 Car logic, dialog logic 2026-04-18 22:15:34 +03:00
Vlad bc62c34cf7 fix some / loca 1 2026-04-19 00:57:22 +06:00
Vladislav Khorev 0d7d430909 Added 3rd person view 2026-04-18 19:58:44 +03:00
Vladislav Khorev 57789eed24 working 2026-04-18 17:30:41 +03:00
Vladislav Khorev 8efe063749 Working on chars and anim 2026-04-18 17:26:14 +03:00
Vlad e6f332244c Merge branch 'escape001' of gitea.fishrungames.com:salmon-engine-projects/space-game001 into escape001 2026-04-18 20:18:20 +06:00
Vlad 77827af150 added forbidden zones 2026-04-18 20:17:09 +06:00
Vladislav Khorev 66da012d5a Merge branch 'escape001' of gitea.fishrungames.com:salmon-engine-projects/space-game001 into escape001 2026-04-18 16:41:24 +03:00
Vladislav Khorev b3a5e36e1b New type of anim 2026-04-18 16:41:17 +03:00
Vlad b633aa01f5 Merge branch 'escape001' of gitea.fishrungames.com:salmon-engine-projects/space-game001 into escape001 2026-04-18 19:23:38 +06:00
Vlad 8191db025f added loca 2 2026-04-18 19:21:17 +06:00
Vladislav Khorev c198f39515 Working on car physics 2026-04-18 14:14:04 +03:00
Vlad 01be8852c6 added loca2 2026-04-18 17:02:20 +06:00
Vlad 1bf54b5367 added locations 2026-04-18 14:45:50 +06:00
Vladislav Khorev fe01a5d1b2 Working on audio in web part 2 2026-04-17 17:14:44 +03:00
Vladislav Khorev b6e0422d71 Working on audio in web part 1 2026-04-17 17:13:55 +03:00
Vladislav Khorev 3778a603f2 merge 2026-04-17 16:30:09 +03:00
Vladislav Khorev e310822462 Working on web version 2026-04-17 16:13:56 +03:00
Vladislav Khorev 3d3c3b2a36 merge 2026-04-17 13:42:45 +03:00
Vlad b5bcd3a812 added music 2026-04-17 14:23:56 +06:00
vottozi 16e7580418 Merge branch 'witcher001-cutscene' of gitea.fishrungames.com:salmon-engine-projects/space-game001 into witcher001-cutscene 2026-04-17 02:33:56 +06:00
vottozi be4dcd4cf9 fix: make choice options the only clickable area in dialogue choices 2026-04-17 02:04:36 +06:00
Vladislav Khorev a1dda3fd50 Major refactoring 2026-04-16 20:24:27 +03:00
Vladislav Khorev 92ba3f2b60 Some refactoring 2026-04-16 20:06:03 +03:00
Vladislav Khorev f2f56125f0 Refactoring 2026-04-16 18:07:22 +03:00
Vladislav Khorev 2ef2e456f7 GPU skinning refactoring 2026-04-16 17:48:23 +03:00
Vladislav Khorev 83cceecccd Refactoring 2026-04-16 15:37:48 +03:00
Vladislav Khorev 24851a89e8 Merge 2026-04-16 15:16:45 +03:00
Vladislav Khorev d47cfd1b6b Minor refactoring 2026-04-16 15:07:59 +03:00
Vladislav Khorev 71346b4227 Remove unused files 2026-04-16 14:03:52 +03:00
Vladislav Khorev 08e1174eaf Fixing minor bugs 2026-04-16 13:44:40 +03:00
Vladislav Khorev cc2eba1352 merge 2026-04-16 13:24:04 +03:00
Vladislav Khorev cfee60d71a merge 2026-04-16 13:11:01 +03:00
Vladislav Khorev dc76986cd3 Added path showing 2026-04-16 11:39:14 +03:00
Vladislav Khorev 61665434ab Fixing minor bugs 2026-04-16 09:37:36 +03:00
vottozi 2494a44e4a refactor: switch cutscene camera to viewport/UV-based system 2026-04-16 01:29:01 +06:00
Vladislav Khorev d8795ec076 Added shadow maps 2026-04-15 19:54:21 +03:00
vottozi ceebb13719 improve dialogue advance and add flexible cutscene camera system 2026-04-15 20:42:41 +06:00
Vladislav Khorev a47306533e Working on stuff 2026-04-15 16:51:55 +03:00
Vladislav Khorev f708843747 Working on optimization: GPU skinning and binary animations 2026-04-15 16:39:33 +03:00
Vladislav Khorev a589765b7e prepare to add anims 2026-04-15 16:38:30 +03:00
anime 4eaefd25d4 added finding path with A* and json file to know where character can walk 2026-04-15 18:36:27 +06:00
Vladislav Khorev eff7322140 Working on dialog portraits 2026-04-15 11:56:38 +03:00
Vlad 44dd09fcb5 fix inventory 2026-04-15 14:50:46 +06:00
Vladislav Khorev de2e18e38f Fixing rotation issues in battle mode 2026-04-15 10:34:55 +03:00
Vladislav Khorev 984d7489d5 Working on enemies and attack 2026-04-15 10:00:16 +03:00
Vlad f7e6f063d1 added dialog start radius and background by time in cutscenes 2026-04-14 18:07:46 +06:00
Vlad db8b9b7a18 fix timetyper and dialog start by click on npc 2026-04-14 17:41:28 +06:00
Vladislav Khorev 493eb5a5d7 Working on battle animation 2026-04-13 23:19:03 +03:00
Vlad 422ff1fbe3 merge 2026-04-13 15:08:38 +06:00
vottozi 6465549c3b fix: dialogue text now displays correctly 2026-04-13 02:16:25 +06:00
Vlad 34759326b9 added npc's gift 2026-04-12 14:52:37 +06:00
vottozi 7125674308 add dialogue and cutscene system 2026-04-12 01:46:33 +06:00
Vlad bc2997d86f added activeFunc for active obj 2026-04-11 16:18:59 +06:00
Vlad 943b6b4d21 added load npcs from json 2026-04-11 15:30:29 +06:00
Vlad b3fda88784 added inventory & active object 2026-04-09 15:33:55 +06:00
Vlad e1b491be73 added load from json game objects 2026-04-07 20:17:04 +06:00
Vladislav Khorev 178f69967b Working on web, change python to lua 2026-04-04 14:29:20 +03:00
Vladislav Khorev 90e2a369f4 Added attack animation for ghots 2026-04-03 21:54:40 +03:00
Vladislav Khorev 403b93e7f5 Added more scripts 2026-04-03 20:06:33 +03:00
Vladislav Khorev 63c9d7b35b Adding scripts 2026-04-03 19:46:43 +03:00
Vladislav Khorev 995e2c8e5b NPC code 2026-04-02 21:03:55 +03:00
Vladislav Khorev 2e4a7ab467 Some cleanup 2026-04-02 19:24:15 +03:00
Vladislav Khorev a96d0b207a Added ai generated animation 2026-04-01 19:07:21 +03:00
Vladislav Khorev 7840bfdb4e Added viola 2026-04-01 13:02:39 +03:00
Vladislav Khorev 56ce826205 Remove unused references 2026-04-01 12:03:35 +03:00
Vladislav Khorev 5364b97235 Working on new scene 2026-04-01 12:03:16 +03:00
Vladislav Khorev beac06fd45 Production-ready, fixing server bugs 2026-03-12 11:36:47 +03:00
Vladislav Khorev ca98fea2bd Fixing stupid bug 2026-03-10 22:09:07 +03:00
Vladislav Khorev a4d5017270 Remove log 2026-03-10 21:40:58 +03:00
Vladislav Khorev 45826ab5b5 Minor changes in config 2026-03-10 21:39:05 +03:00
Vladislav Khorev e5296a41b9 Added help and about, minor improvements 2026-03-10 21:16:58 +03:00
Vladislav Khorev 18674efd8e Working on minor bug fixing 2026-03-10 18:03:16 +03:00
Vladislav Khorev 5e7c050c68 Merge branch 'spark' 2026-03-10 11:39:02 +03:00
Vladislav Khorev c88784a669 Added inverse, fixing server bug 2026-03-10 11:38:16 +03:00
Vlad 181132e092 merge 2026-03-10 14:36:35 +06:00
Vlad 6fe10eacfc Merge remote-tracking branch 'origin/main' into spark 2026-03-10 14:26:51 +06:00
Vlad 9e692c9ec3 added enter nicknamw 2026-03-10 14:25:00 +06:00
Vladislav Khorev 785b96ce82 Working on list of players 2026-03-09 23:04:48 +03:00
Vladislav Khorev a8ded217df Fixing ui bugs 2026-03-09 20:52:16 +03:00
Vladislav Khorev 3e8dba36a4 fixing minor bugs, fixing velocity not changed after death, fixing server crash 2026-03-09 19:27:20 +03:00
Vladislav Khorev 84a5e888a0 Added pickup boxes 2026-03-09 18:36:04 +03:00
Vladislav Khorev 86a9f38c3b Some UI changes 2026-03-09 13:50:59 +03:00
Vladislav Khorev 24aa7007bb Added trails to other ships toos 2026-03-08 21:27:45 +03:00
Vladislav Khorev 59b5bea540 Fixing particles 2026-03-08 19:07:02 +03:00
Vladislav Khorev 9da3cc4401 Fixing bug in particle emitter 2026-03-08 00:22:13 +03:00
Vladislav Khorev 4a542fd6c8 Added multi-touch, added on press down 2026-03-07 22:41:25 +03:00
Vladislav Khorev a06cb77a66 Fixing minor stuff, refactoring 2026-03-07 20:51:34 +03:00
Vladislav Khorev 3c2c06ca40 Working on refactoring 2026-03-07 20:03:16 +03:00
Vladislav Khorev 928600acd4 Refactoring 2026-03-07 19:41:15 +03:00
Vladislav Khorev 7ccda081b0 Fixing bugs 2026-03-06 23:15:56 +03:00
Vladislav Khorev 41f370f2ee Minor fixing 2026-03-06 22:34:03 +03:00
Vladislav Khorev 4af20c65e6 Make menu to state machine 2026-03-06 21:52:00 +03:00
Vladislav Khorev ac551122d9 Added force disconnect from server 2026-03-06 20:05:55 +03:00
Vladislav Khorev 8528b4dc90 Clean up server 2026-03-06 19:08:55 +03:00
Vladislav Khorev c35e093cc5 Working on encoding 2026-03-06 17:51:46 +03:00
Vladislav Khorev a8c76dee3c Working on fixing minor bugs 2026-03-05 23:58:35 +03:00
Vladislav Khorev e84a094ad9 Merge branch 'spark' 2026-03-05 19:54:24 +03:00
Vladislav Khorev b0b7391939 Fixed low quality 2026-03-05 19:53:27 +03:00
Vlad 3e2632c5b5 added textures 2026-03-05 20:14:02 +06:00
Vladislav Khorev 70ef730e86 Merge branch 'main' into ShipSpawn 2026-03-05 10:24:17 +03:00
Vladislav Khorev 92b288d978 fixing bugs 2026-03-05 10:15:55 +03:00
Vladislav Khorev cca1d0ece2 Merge branch 'spark' 2026-03-05 09:10:38 +03:00
Vladislav Khorev 8effdd5dfa Merge branch 'sergey' 2026-03-05 09:10:00 +03:00
Vladislav Khorev e78429b600 Working on running the game even in Telegram 2026-03-04 21:41:51 +03:00
anime 05a13a40e7 fixed: Box destroition 2026-03-04 16:37:00 +06:00
vottozi ef7617701e fix server collision radius mismatch 2026-03-04 03:37:58 +06:00
vottozi 2f50dc1522 fix Lead Indicator rendering when target off-screen 2026-03-04 01:42:57 +06:00
vottozi 9eafcd27fb Fix lead indicator prediction & align projectile lifetime (client/server) 2026-03-03 19:42:08 +06:00
Vlad 3879eb1d75 fixed web ship types boxes / local boxes collision and projectiles 2026-03-03 19:17:07 +06:00
anime 78046e5e2d Merge remote-tracking branch 'origin/main' into ShipSpawn 2026-03-02 17:49:09 +06:00
anime 26233f934f fixed: spawning ships 2026-03-02 17:47:46 +06:00
Vlad 167c04b107 install 2026-03-02 14:05:23 +06:00
Vladislav Khorev 5fcb1d1234 server changes 2026-03-01 22:35:17 +03:00
Vladislav Khorev 70a617b688 More changes 2026-03-01 22:34:12 +03:00
Vladislav Khorev bb0f584bf6 Working on UI and web game 2026-03-01 21:22:57 +03:00
Vladislav Khorev 6b4b549b3c Working on UI for web 2026-02-28 23:01:26 +03:00
Vladislav Khorev ffbecbbcde Working on ui 2026-02-28 22:21:08 +03:00
Vladislav Khorev 2728ca9646 Adapting web version 2026-02-28 20:37:47 +03:00
Vladislav Khorev 7e5aa22fee merge 2026-02-28 13:33:33 +03:00
Vlad c20e82634a fix game over 2026-02-28 12:07:40 +06:00
Vladislav Khorev 9f82e7a2e6 Adapt for web 2026-02-27 22:31:07 +03:00
Vlad 7418bbbe27 fix spaceship rotation fix sync boxes 2026-02-27 17:37:00 +06:00
Vladislav Khorev be2aa76f8b Working on server optimization 2026-02-26 22:26:43 +03:00
Vladislav Khorev e8f1786a2a merge 2026-02-26 21:22:38 +03:00
Vladislav Khorev e30f8a4c70 merge 2026-02-26 21:10:27 +03:00
Vladislav Khorev f8642efa23 merge 2026-02-26 21:02:16 +03:00
vottozi 5da5a754fe add configurable HUD crosshair 2026-02-26 01:49:49 +06:00
vottozi fb1b0a1c2e Lead Indicator fix 2026-02-25 22:18:47 +06:00
Vlad 74c2f786a1 added choising spaceship type for multiplayer and some fix in respawn 2026-02-25 19:17:42 +06:00
Vlad 5216965496 added choosing spaceship in singleplay 2026-02-24 20:34:01 +06:00
Vlad ad7294ceea added random spaceship type 2026-02-24 19:51:33 +06:00
Vlad 3893038d9a Merge remote-tracking branch 'origin/main' into spark 2026-02-23 15:30:01 +06:00
Vlad cb2e8318e7 added multi_menu 2026-02-23 15:29:05 +06:00
Vladislav Khorev 5cca2413fe changes 2026-02-23 11:46:57 +03:00
Vladislav Khorev db4254e205 working on web 2026-02-23 11:45:55 +03:00
vottozi fa3a4c66c9 add lead indicator 2026-02-23 14:43:23 +06:00
Vladislav Khorev 84006380db Added loading screen 2026-02-23 09:55:01 +03:00
Vladislav Khorev f6cc30a30c Try out Cursor 2026-02-22 21:20:39 +03:00
Vladislav Khorev d883b58260 Adapt for web 2026-02-22 20:04:18 +03:00
Vladislav Khorev b59a10b7e6 Major refactoring for game menu 2026-02-22 19:50:13 +03:00
Vladislav Khorev 5b57696acf Refactoring major 2026-02-22 19:15:25 +03:00
Vladislav Khorev 1265d87bc5 Updated UI 2026-02-22 18:23:27 +03:00
Vladislav Khorev 46d7a2a25d Merge branch 'spark' 2026-02-22 17:02:40 +03:00
Vlad 0babfff28b added multiplayer menu 2026-02-22 19:21:12 +06:00
Vladislav Khorev ed6e1bacc7 merge 2026-02-22 16:03:11 +03:00
Vladislav Khorev 9c39782729 Fixing bug with collisions 2026-02-22 15:53:04 +03:00
Vladislav Khorev efad2dde3e Restore as it was before 2026-02-22 14:08:12 +03:00
Vladislav Khorev 84d8ee7eee Merge branch 'spark' 2026-02-22 13:30:33 +03:00
Vladislav Khorev 7693237aa5 Added websocket 2026-02-22 13:29:06 +03:00
vottozi 528c94e921 Add enemy target brackets + off-screen arrows 2026-02-19 01:13:08 +06:00
Vlad a8700af1d0 added npc destroy 2026-02-13 15:40:40 +06:00
Vlad 9793c5bf06 fix npc 2026-02-12 18:13:19 +06:00
vottozi b56fafa0e0 Add mesh caching for unchanging text labels 2026-02-12 13:23:16 +06:00
Vladislav Khorev 22ee99418d Working on web version 2026-02-11 21:48:25 +03:00
vottozi 4eda57b4e4 single texture atlas for all font glyphs in TextRenderer 2026-02-12 00:08:52 +06:00
Vladislav Khorev 2ffd8124f2 Change the way fire happens 2026-02-11 20:02:15 +03:00
Vladislav Khorev 210c191d41 Added different hash func 2026-02-10 21:06:21 +03:00
Vladislav Khorev 251a59ddbe added hash 2026-02-10 11:42:23 +03:00
Vlad 5df2216da6 added npc 2026-02-10 12:42:19 +06:00
Vladislav Khorev 2c1c077611 Added time offset sync, added web version multiplayer 2026-02-09 22:56:48 +03:00
Vlad 629c9ba7b1 add local server 2026-02-09 18:42:07 +06:00
Vladislav Khorev a59bcc0c4b Adapting for web 2026-02-08 22:38:24 +03:00
Vladislav Khorev cf1ec1e76f merge 2026-02-08 15:53:55 +03:00
Vladislav Khorev 8a374a4231 Merge with main 2026-02-08 15:31:25 +03:00
Vlad da2b6e5577 some fix/added sync boxes 2026-02-06 19:16:17 +06:00
vottozi aa9590e2f4 Add velocity HUD display (Velocity: 0) in top-left corner 2026-02-06 18:04:33 +06:00
vottozi a1d54b01b0 Add TextView UI component 2026-02-06 15:11:27 +06:00
Vlad 65a2066727 size 2026-02-05 14:00:20 +06:00
Vladislav Khorev c49c4626d0 merge 2026-02-05 10:43:24 +03:00
Vlad ec156bd679 add sync projectiles 2026-02-05 13:29:21 +06:00
Vladislav Khorev 7b1d9363a0 Fixing lag compensation and make more smooth 2026-02-05 09:00:54 +03:00
Vladislav Khorev 82ca994fac merge 2026-02-05 07:33:13 +03:00
Vladislav Khorev 0d2f705d42 merge 2026-02-05 07:30:12 +03:00
vottozi e799b4ec79 conflict resolution 2026-02-05 01:43:45 +06:00
vottozi c8076b6018 adjust label scale: constant far, smooth growth near 2026-02-05 01:34:14 +06:00
Vlad 88f68b20ea some fix 2026-02-02 14:45:36 +06:00
vottozi d249f17d22 everything uselles 2026-02-02 13:50:43 +06:00
vottozi bee3545196 change to permanent visibility of ship labels 2026-02-02 13:48:28 +06:00
vottozi e050d7d0ec Improve ship nickname rendering 2026-02-02 04:44:18 +06:00
vottozi e18484ea62 add player ID labels for ships 2026-02-01 23:34:21 +06:00
Vlad 204b79bf06 add main menu 2026-01-31 19:25:40 +06:00
Vlad 50232c0816 fix shooting 2026-01-31 16:44:00 +06:00
Vlad e8fb14b809 added sync projectiles/boxes 2026-01-27 19:38:18 +06:00
Vladislav Khorev d425167dce Added planet physics and simple base 2026-01-25 20:55:36 +03:00
vottozi f41228acf8 add freetype-based text rendering and box labels 2026-01-22 15:54:21 +06:00
Vlad cacc18dc7e added boxes from server 2026-01-19 22:04:33 +06:00
Vladislav Khorev 96d879076a Merge branch 'main' into sergey 2026-01-18 13:35:06 +03:00
Vladislav Khorev 8f3528f9a1 merge 2026-01-18 13:29:48 +03:00
Vladislav Khorev 67c0696061 By default make offline 2026-01-18 13:18:59 +03:00
Vladislav Khorev 1a7e424085 Major clean up 2026-01-18 13:13:25 +03:00
Vladislav Khorev 6aff22ec53 Working on cleanup 2026-01-18 11:29:15 +03:00
Vladislav Khorev 579f4886d1 Finally fixed bugs, now need to do cleanup 2026-01-17 20:06:57 +03:00
Vladislav Khorev 64385ba15c Fixing bug, implementing delays, remove WORLD_UPDATE 2026-01-17 19:16:06 +03:00
Vladislav Khorev 3c55b59c8d Fixing bugs, more freq updated 2026-01-17 15:03:37 +03:00
Vladislav Khorev e0a60dcd6f Change clock to system clock, fix bugs 2026-01-17 13:59:02 +03:00
Vladislav Khorev f967807296 Fixing bugs, unify velocity 2026-01-17 12:43:41 +03:00
Vladislav Khorev fbc420e894 Propagate ROT and VEL 2026-01-17 11:05:04 +03:00
Vladislav Khorev ff95163bcd Yee render works 2026-01-16 18:11:52 +03:00
Vladislav Khorev e0453db063 Working on implementing server side physics on gam 2026-01-16 00:01:44 +03:00
vottozi 0f415b6ed6 added SDL_ttf and FreeType libs 2026-01-16 00:58:22 +06:00
Vladislav Khorev 1348efb5fe Working on server 2026-01-15 21:54:37 +03:00
Vladislav Khorev ea26d6fb23 Working on local and websocket client 2026-01-15 13:41:00 +03:00
Vlad 52a52ef960 added projectile collision and some fixed 2026-01-15 15:49:32 +06:00
Vladislav Khorev 90ad3392cb Merge branch 'main' into spark 2026-01-15 09:32:35 +03:00
Vladislav Khorev dc5c76f888 Merge branch 'spark' of gitea.fishrungames.com:salmon-engine-projects/space-game001 into spark 2026-01-15 09:32:29 +03:00
Vladislav Khorev 483757f8ca first version 2026-01-14 23:17:52 +03:00
Vlad bdce619e68 fix 2026-01-14 17:06:53 +06:00
Vladislav Khorev dca38c986d Minor refactoring 2026-01-14 13:24:07 +03:00
Vlad 13af968a04 added collision for stones 2026-01-14 16:06:32 +06:00
Vladislav Khorev 4cc6971542 Merge, adding ship vertical shift 2026-01-14 12:32:42 +03:00
Vladislav Khorev 8d58e41459 Merge branch 'spark' 2026-01-14 11:13:29 +03:00
Vladislav Khorev dffbc6029d Merge branch 'spark' of gitea.fishrungames.com:salmon-engine-projects/space-game001 into spark 2026-01-14 11:12:50 +03:00
Vladislav Khorev 988e0fefa8 Adding new ship to test, temporary remove ui 2026-01-14 11:12:04 +03:00
Vladislav Khorev f0ad32476e some move 2026-01-14 11:08:27 +03:00
Vladislav Khorev deedec7833 merge 2026-01-14 10:53:05 +03:00
Vlad cda1c0de20 added collision box/planet 2026-01-13 19:09:06 +06:00
Vlad ec30cbe810 merge 2026-01-12 14:55:16 +06:00
Vladislav Khorev d610f1dea1 Merge branch 'spark' of gitea.fishrungames.com:salmon-engine-projects/space-game001 into spark 2026-01-12 09:44:54 +03:00
Vladislav Khorev c347545d0e Major refactoring to multi-platform code 2026-01-11 19:59:58 +03:00
Vladislav Khorev a08ee412a6 Added android version too 2026-01-11 12:08:48 +03:00
Vlad 528e7ae5a6 add animation buttons 2026-01-10 15:06:12 +06:00
Vladislav Khorev 8089bcc0f3 porting web to emcmake 2026-01-09 23:19:39 +03:00
Vladislav Khorev d1cab1f3b3 refactoring 2026-01-09 22:33:46 +03:00
Vladislav Khorev 9fc0adf210 Major refactoring + web adaptation 2026-01-09 20:49:03 +03:00
Vladislav Khorev 197423b572 Merge branch 'main' into spark 2026-01-09 17:17:11 +03:00
Vlad 112684b40a add read from zip 2026-01-09 20:08:09 +06:00
Vladislav Khorev 648acf8938 Working on multi-threading, including web 2026-01-09 15:24:01 +03:00
Vladislav Khorev b055faf882 Working on web version 2026-01-08 23:23:39 +03:00
Vladislav Khorev 723b973c7f Porting to web 2026-01-07 15:14:13 +03:00
Vladislav Khorev 6961152d34 Added mipmapping partially 2026-01-07 13:30:21 +03:00
Vladislav Khorev 34fee1005a Lighting and clouds are somehow working 2026-01-04 18:11:10 +03:00
Vladislav Khorev 8168734fbb fixing bug with stones baking 2026-01-03 20:58:18 +03:00
Vladislav Khorev 144978bfa2 Added Eigen, replacing ZMath 2026-01-03 20:38:36 +03:00
Vladislav Khorev 9bfe2bef89 Working on adding Eigen 2026-01-03 19:52:44 +03:00
Vladislav Khorev c553780b6e Make IcoSphere 2026-01-03 17:29:40 +03:00
Vladislav Khorev 570c96a628 working on colors on the planet 2026-01-02 22:01:33 +03:00
Vladislav Khorev c9178cff3a Working on fog shader 2026-01-02 21:16:44 +03:00
Vladislav Khorev 2b40c13c9d Finally somehow planet is done 2025-12-31 23:02:13 +03:00
Vladislav Khorev 592e008914 Minor refactoring 2025-12-30 16:16:50 +03:00
Vladislav Khorev 0a5a3b653d merge 2025-12-30 15:52:26 +03:00
Vlad ff3946a019 added projectile 2025-12-30 16:47:32 +06:00
Vlad d57112be35 add UiSlider and some fix 2025-12-23 13:37:54 +06:00
Vlad bee2fff538 add menu 2025-12-22 15:40:45 +06:00
Vlad 068c319890 add ui.json and del default values from SparkEmitter 2025-12-19 17:35:34 +06:00
Vlad 53ea1f768c merge 2025-12-18 12:33:37 +06:00
Vlad b369c52270 error 2025-12-18 12:26:41 +06:00
Vlad 01d362615f add loading from json 2025-12-17 17:05:40 +06:00
2479 changed files with 111682 additions and 339663 deletions

No files matched your search

+4
View File
@@ -1,3 +1,7 @@
*.bmp filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.anim filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text
+5 -1
View File
@@ -400,4 +400,8 @@ jumpingbird.*
jumpingbird.data
build
build-emcmake
thirdparty1
thirdparty
proj-web/build
proj-windows/build
public
-93
View File
@@ -1,93 +0,0 @@
#ifdef AUDIO
#include "AudioPlayerAsync.h"
AudioPlayerAsync::AudioPlayerAsync() : worker(&AudioPlayerAsync::workerThread, this) {}
AudioPlayerAsync::~AudioPlayerAsync() {
{
std::unique_lock<std::mutex> lock(mtx);
stop = true;
cv.notify_all();
}
worker.join();
}
void AudioPlayerAsync::stopAsync() {
std::unique_lock<std::mutex> lock(mtx);
taskQueue.push([this]() {
//audioPlayerMutex.lock();
audioPlayer->stop();
std::this_thread::sleep_for(std::chrono::seconds(1));
//audioPlayerMutex.unlock();
});
cv.notify_one();
}
void AudioPlayerAsync::resetAsync() {
std::unique_lock<std::mutex> lock(mtx);
taskQueue.push([this]() {
//audioPlayerMutex.lock();
audioPlayer.reset();
audioPlayer = std::make_unique<AudioPlayer>();
//audioPlayerMutex.unlock();
});
cv.notify_one();
}
void AudioPlayerAsync::playSoundAsync(std::string soundName) {
soundNameMutex.lock();
latestSoundName = soundName;
soundNameMutex.unlock();
std::unique_lock<std::mutex> lock(mtx);
taskQueue.push([this]() {
//audioPlayerMutex.lock();
if (audioPlayer) {
audioPlayer->playSound(latestSoundName);
}
//audioPlayerMutex.unlock();
});
cv.notify_one();
}
void AudioPlayerAsync::playMusicAsync(std::string musicName) {
musicNameMutex.lock();
latestMusicName = musicName;
musicNameMutex.unlock();
std::unique_lock<std::mutex> lock(mtx);
taskQueue.push([this]() {
//audioPlayerMutex.lock();
if (audioPlayer) {
audioPlayer->playMusic(latestMusicName);
}
//audioPlayerMutex.unlock();
});
cv.notify_one();
}
void AudioPlayerAsync::workerThread() {
while (true) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this]() { return !taskQueue.empty() || stop; });
if (stop && taskQueue.empty()) {
break;
}
task = taskQueue.front();
taskQueue.pop();
}
task();
}
}
#endif
-53
View File
@@ -1,53 +0,0 @@
#pragma once
#ifdef AUDIO
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <functional>
#include "cmakeaudioplayer/include/AudioPlayer.hpp"
class AudioPlayerAsync {
public:
AudioPlayerAsync();
~AudioPlayerAsync();
void resetAsync();
void playSoundAsync(std::string soundName);
void playMusicAsync(std::string musicName);
void stopAsync();
void exit()
{
stop = true;
}
std::thread worker;
private:
std::unique_ptr<AudioPlayer> audioPlayer;
//std::mutex audioPlayerMutex;
std::mutex soundNameMutex;
std::mutex musicNameMutex;
std::string latestSoundName;
std::string latestMusicName;
std::mutex mtx;
std::condition_variable cv;
std::queue<std::function<void()>> taskQueue;
bool stop = false;
void workerThread();
};
#endif
-719
View File
@@ -1,719 +0,0 @@
#include "BoneAnimatedModel.h"
#include <regex>
#include <string>
#include <fstream>
#include <iostream>
#include <sstream>
namespace ZL
{
int getIndexByValue(const std::string& name, const std::vector<std::string>& words)
{
for (int i = 0; i < words.size(); i++)
{
if (words[i] == name)
{
return i;
}
}
return -1;
}
void BoneSystem::LoadFromFile(const std::string& fileName, const std::string& ZIPFileName)
{
std::ifstream filestream;
std::istringstream zipStream;
if (!ZIPFileName.empty())
{
std::vector<char> fileData = readFileFromZIP(fileName, ZIPFileName);
std::string fileContents(fileData.begin(), fileData.end());
zipStream.str(fileContents);
}
else
{
filestream.open(fileName);
}
std::istream& f = (!ZIPFileName.empty()) ? static_cast<std::istream&>(zipStream) : static_cast<std::istream&>(filestream);
//Skip first 5 lines
std::string tempLine;
for (int i = 0; i < 5; i++)
{
std::getline(f, tempLine);
}
std::getline(f, tempLine);
static const std::regex pattern_count(R"(\d+)");
static const std::regex pattern_float(R"([-]?\d+\.\d+)");
static const std::regex pattern_int(R"([-]?\d+)");
static const std::regex pattern_boneChildren(R"(\'([^\']+)\')");
static const std::regex pattern_bone_weight(R"(\'([^\']+)\'.*?([-]?\d+\.\d+))");
std::smatch match;
int numberBones;
if (std::regex_search(tempLine, match, pattern_count)) {
std::string number_str = match.str();
numberBones = std::stoi(number_str);
}
else {
throw std::runtime_error("No number found in the input string.");
}
std::vector<Bone> bones;
std::vector<std::string> boneNames;
std::vector<std::string> boneParentNames;
std::unordered_map<std::string, std::vector<std::string>> boneChildren;
bones.resize(numberBones);
boneNames.resize(numberBones);
boneParentNames.resize(numberBones);
for (int i = 0; i < numberBones; i++)
{
std::getline(f, tempLine);
std::string boneName = tempLine.substr(6);
boneNames[i] = boneName;
std::getline(f, tempLine);
std::vector<float> floatValues;
auto b = tempLine.cbegin();
auto e = tempLine.cend();
while (std::regex_search(b, e, match, pattern_float)) {
floatValues.push_back(std::stof(match.str()));
b = match.suffix().first;
}
bones[i].boneStartWorld = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
std::getline(f, tempLine); //skip tail
std::getline(f, tempLine); //len
if (std::regex_search(tempLine, match, pattern_float)) {
std::string len_str = match.str();
bones[i].boneLength = std::stof(len_str);
}
else {
throw std::runtime_error("No number found in the input string.");
}
//---------- matrix begin
std::getline(f, tempLine);
b = tempLine.cbegin();
e = tempLine.cend();
floatValues.clear();
while (std::regex_search(b, e, match, pattern_float)) {
floatValues.push_back(std::stof(match.str()));
b = match.suffix().first;
}
bones[i].boneMatrixWorld.m[0] = floatValues[0];
bones[i].boneMatrixWorld.m[0 + 1 * 3] = floatValues[1];
bones[i].boneMatrixWorld.m[0 + 2 * 3] = floatValues[2];
std::getline(f, tempLine);
b = tempLine.cbegin();
e = tempLine.cend();
floatValues.clear();
while (std::regex_search(b, e, match, pattern_float)) {
floatValues.push_back(std::stof(match.str()));
b = match.suffix().first;
}
bones[i].boneMatrixWorld.m[1] = floatValues[0];
bones[i].boneMatrixWorld.m[1 + 1 * 3] = floatValues[1];
bones[i].boneMatrixWorld.m[1 + 2 * 3] = floatValues[2];
std::getline(f, tempLine);
b = tempLine.cbegin();
e = tempLine.cend();
floatValues.clear();
while (std::regex_search(b, e, match, pattern_float)) {
floatValues.push_back(std::stof(match.str()));
b = match.suffix().first;
}
bones[i].boneMatrixWorld.m[2] = floatValues[0];
bones[i].boneMatrixWorld.m[2 + 1 * 3] = floatValues[1];
bones[i].boneMatrixWorld.m[2 + 2 * 3] = floatValues[2];
//----------- matrix end
std::getline(f, tempLine); //parent
if (tempLine == " Parent: None")
{
bones[i].parent = -1;
}
else
{
std::string boneParent = tempLine.substr(10);
boneParentNames[i] = boneParent;
}
std::getline(f, tempLine); //children
b = tempLine.cbegin();
e = tempLine.cend();
while (std::regex_search(b, e, match, pattern_boneChildren)) {
boneChildren[boneName].push_back(match.str(1));
b = match.suffix().first;
}
}
//Now process all the bones:
for (int i = 0; i < numberBones; i++)
{
std::string boneName = boneNames[i];
std::string boneParent = boneParentNames[i];
if (boneParent == "")
{
bones[i].parent = -1;
}
else
{
bones[i].parent = getIndexByValue(boneParent, boneNames);
}
for (int j = 0; j < boneChildren[boneName].size(); j++)
{
bones[i].children.push_back(getIndexByValue(boneChildren[boneName][j], boneNames));
}
/*if (boneName == "Bone.020")
{
std::cout << i << std::endl;
}*/
}
startBones = bones;
currentBones = bones;
///std::cout << "Hello!" << std::endl;
std::getline(f, tempLine); //vertice count
int numberVertices;
if (std::regex_search(tempLine, match, pattern_count)) {
std::string number_str = match.str();
numberVertices = std::stoi(number_str);
}
else {
throw std::runtime_error("No number found in the input string.");
}
std::vector<Vector3f> vertices;
vertices.resize(numberVertices);
for (int i = 0; i < numberVertices; i++)
{
std::getline(f, tempLine);
std::vector<float> floatValues;
auto b = tempLine.cbegin();
auto e = tempLine.cend();
while (std::regex_search(b, e, match, pattern_float)) {
floatValues.push_back(std::stof(match.str()));
b = match.suffix().first;
}
vertices[i] = Vector3f{floatValues[0], floatValues[1], floatValues[2]};
}
//==== process uv and normals begin
std::cout << "Hello x1" << std::endl;
std::getline(f, tempLine); //===UV Coordinates:
std::getline(f, tempLine); //triangle count
int numberTriangles;
if (std::regex_search(tempLine, match, pattern_count)) {
std::string number_str = match.str();
numberTriangles = std::stoi(number_str);
}
else {
throw std::runtime_error("No number found in the input string.");
}
// Now process UVs
std::vector<std::array<Vector2f, 3>> uvCoords;
uvCoords.resize(numberTriangles);
for (int i = 0; i < numberTriangles; i++)
{
std::getline(f, tempLine); //Face 0
int uvCount;
std::getline(f, tempLine);
if (std::regex_search(tempLine, match, pattern_count)) {
std::string number_str = match.str();
uvCount = std::stoi(number_str);
}
else {
throw std::runtime_error("No number found in the input string.");
}
if (uvCount != 3)
{
throw std::runtime_error("more than 3 uvs");
}
std::vector<float> floatValues;
for (int j = 0; j < 3; j++)
{
std::getline(f, tempLine); //UV <Vector (-0.3661, -1.1665)>
auto b = tempLine.cbegin();
auto e = tempLine.cend();
floatValues.clear();
while (std::regex_search(b, e, match, pattern_float)) {
floatValues.push_back(std::stof(match.str()));
b = match.suffix().first;
}
if (floatValues.size() != 2)
{
throw std::runtime_error("more than 2 uvs---");
}
uvCoords[i][j] = Vector2f{ floatValues[0],floatValues[1] };
}
}
std::cout << "Hello eee" << std::endl;
std::getline(f, tempLine); //===Normals:
std::vector<Vector3f> normals;
normals.resize(numberVertices);
for (int i = 0; i < numberVertices; i++)
{
std::getline(f, tempLine);
std::vector<float> floatValues;
auto b = tempLine.cbegin();
auto e = tempLine.cend();
while (std::regex_search(b, e, match, pattern_float)) {
floatValues.push_back(std::stof(match.str()));
b = match.suffix().first;
}
normals[i] = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
}
//==== process uv and normals end
std::getline(f, tempLine); //triangle count.
//numberTriangles; //Need to check if new value is the same as was read before
if (std::regex_search(tempLine, match, pattern_count)) {
std::string number_str = match.str();
numberTriangles = std::stoi(number_str);
}
else {
throw std::runtime_error("No number found in the input string.");
}
std::vector<std::array<int, 3>> triangles;
triangles.resize(numberTriangles);
for (int i = 0; i < numberTriangles; i++)
{
std::getline(f, tempLine);
std::vector<int> intValues;
auto b = tempLine.cbegin();
auto e = tempLine.cend();
while (std::regex_search(b, e, match, pattern_int)) {
intValues.push_back(std::stoi(match.str()));
b = match.suffix().first;
}
triangles[i] = { intValues[0], intValues[1], intValues[2] };
}
std::getline(f, tempLine);//=== Vertex Weights ===
std::vector<std::array<BoneWeight, MAX_BONE_COUNT>> localVerticesBoneWeight;
localVerticesBoneWeight.resize(numberVertices);
for (int i = 0; i < numberVertices; i++)
{
std::getline(f, tempLine); //skip Vertex 0:
std::getline(f, tempLine); //vertex group count
int boneCount;
if (std::regex_search(tempLine, match, pattern_count)) {
std::string number_str = match.str();
boneCount = std::stoi(number_str);
}
else {
throw std::runtime_error("No number found in the input string.");
}
if (boneCount > MAX_BONE_COUNT)
{
throw std::runtime_error("more than 5 bones");
}
float sumWeights = 0;
for (int j = 0; j < boneCount; j++)
{
std::getline(f, tempLine); //Group: 'Bone', Weight: 0.9929084181785583
if (std::regex_search(tempLine, match, pattern_bone_weight)) {
// ��������� ����� (��� �������)
std::string word = match.str(1);
double weight = std::stod(match.str(2));
int boneNumber = getIndexByValue(word, boneNames);
localVerticesBoneWeight[i][j].boneIndex = boneNumber;
localVerticesBoneWeight[i][j].weight = weight;
sumWeights += weight;
}
else {
throw std::runtime_error("No match found in the input string.");
}
}
//Normalize weights:
for (int j = 0; j < boneCount; j++)
{
localVerticesBoneWeight[i][j].weight = localVerticesBoneWeight[i][j].weight / sumWeights;
}
}
std::getline(f, tempLine);//=== Animation Keyframes ===
std::getline(f, tempLine);//=== Bone Transforms per Keyframe ===
std::getline(f, tempLine);
int numberKeyFrames;
if (std::regex_search(tempLine, match, pattern_count)) {
std::string number_str = match.str();
numberKeyFrames = std::stoi(number_str);
}
else {
throw std::runtime_error("No number found in the input string.");
}
animations.resize(1);
animations[0].keyFrames.resize(numberKeyFrames);
for (int i = 0; i < numberKeyFrames; i++)
{
std::getline(f, tempLine);
int numberFrame;
if (std::regex_search(tempLine, match, pattern_count)) {
std::string number_str = match.str();
numberFrame = std::stoi(number_str);
}
else {
throw std::runtime_error("No number found in the input string.");
}
animations[0].keyFrames[i].frame = numberFrame;
animations[0].keyFrames[i].bones.resize(numberBones);
for (int j = 0; j < numberBones; j++)
{
std::getline(f, tempLine);
std::string boneName = tempLine.substr(8);
int boneNumber = getIndexByValue(boneName, boneNames);
animations[0].keyFrames[i].bones[boneNumber] = startBones[boneNumber];
std::getline(f, tempLine); // Location: <Vector (0.0000, 0.0000, -0.0091)>
std::vector<float> floatValues;
auto b = tempLine.cbegin();
auto e = tempLine.cend();
while (std::regex_search(b, e, match, pattern_float)) {
floatValues.push_back(std::stof(match.str()));
b = match.suffix().first;
}
animations[0].keyFrames[i].bones[boneNumber].boneStartWorld = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
std::getline(f, tempLine); // Rotation
std::getline(f, tempLine); // Matrix
//=============== Matrix begin ==================
std::getline(f, tempLine);
b = tempLine.cbegin();
e = tempLine.cend();
floatValues.clear();
while (std::regex_search(b, e, match, pattern_float)) {
floatValues.push_back(std::stof(match.str()));
b = match.suffix().first;
}
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[0] = floatValues[0];
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[0 + 1 * 4] = floatValues[1];
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[0 + 2 * 4] = floatValues[2];
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[0 + 3 * 4] = floatValues[3];
std::getline(f, tempLine);
b = tempLine.cbegin();
e = tempLine.cend();
floatValues.clear();
while (std::regex_search(b, e, match, pattern_float)) {
floatValues.push_back(std::stof(match.str()));
b = match.suffix().first;
}
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[1] = floatValues[0];
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[1 + 1 * 4] = floatValues[1];
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[1 + 2 * 4] = floatValues[2];
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[1 + 3 * 4] = floatValues[3];
std::getline(f, tempLine);
b = tempLine.cbegin();
e = tempLine.cend();
floatValues.clear();
while (std::regex_search(b, e, match, pattern_float)) {
floatValues.push_back(std::stof(match.str()));
b = match.suffix().first;
}
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[2] = floatValues[0];
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[2 + 1 * 4] = floatValues[1];
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[2 + 2 * 4] = floatValues[2];
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[2 + 3 * 4] = floatValues[3];
std::getline(f, tempLine);
b = tempLine.cbegin();
e = tempLine.cend();
floatValues.clear();
while (std::regex_search(b, e, match, pattern_float)) {
floatValues.push_back(std::stof(match.str()));
b = match.suffix().first;
}
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[3] = floatValues[0];
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[3 + 1 * 4] = floatValues[1];
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[3 + 2 * 4] = floatValues[2];
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.m[3 + 3 * 4] = floatValues[3];
//std::getline(f, tempLine);// ignore last matrix line
//=============== Matrix end ==================
}
}
// Now let's process bone weights and vertices
for (int i = 0; i < numberTriangles; i++)
{
mesh.PositionData.push_back(vertices[triangles[i][0]]);
mesh.PositionData.push_back(vertices[triangles[i][1]]);
mesh.PositionData.push_back(vertices[triangles[i][2]]);
verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][0]]);
verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][1]]);
verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][2]]);
mesh.TexCoordData.push_back(uvCoords[i][0]);
mesh.TexCoordData.push_back(uvCoords[i][1]);
mesh.TexCoordData.push_back(uvCoords[i][2]);
}
startMesh = mesh;
}
void BoneSystem::Interpolate(int frame)
{
int startingFrame = -1;
for (int i = 0; i < animations[0].keyFrames.size() - 1; i++)
{
int oldFrame = animations[0].keyFrames[i].frame;
int nextFrame = animations[0].keyFrames[i + 1].frame;
if (frame >= oldFrame && frame < nextFrame)
{
startingFrame = i;
break;
}
}
if (startingFrame == -1)
{
throw std::runtime_error("Exception here");
}
int modifiedFrameNumber = frame - animations[0].keyFrames[startingFrame].frame;
int diffFrames = animations[0].keyFrames[startingFrame + 1].frame - animations[0].keyFrames[startingFrame].frame;
float t = (modifiedFrameNumber + 0.f) / diffFrames;
std::vector<Bone>& oneFrameBones = animations[0].keyFrames[startingFrame].bones;
std::vector<Bone>& nextFrameBones = animations[0].keyFrames[startingFrame+1].bones;
std::vector<Matrix4f> skinningMatrixForEachBone;
//std::vector<Matrix3f> skinningMatrixForEachBone;
skinningMatrixForEachBone.resize(currentBones.size());
for (int i = 0; i < currentBones.size(); i++)
{
currentBones[i].boneStartWorld.v[0] = oneFrameBones[i].boneStartWorld.v[0] + t * (nextFrameBones[i].boneStartWorld.v[0] - oneFrameBones[i].boneStartWorld.v[0]);
currentBones[i].boneStartWorld.v[1] = oneFrameBones[i].boneStartWorld.v[1] + t * (nextFrameBones[i].boneStartWorld.v[1] - oneFrameBones[i].boneStartWorld.v[1]);
currentBones[i].boneStartWorld.v[2] = oneFrameBones[i].boneStartWorld.v[2] + t * (nextFrameBones[i].boneStartWorld.v[2] - oneFrameBones[i].boneStartWorld.v[2]);
Matrix3f oneFrameBonesMatrix;
oneFrameBonesMatrix.m[0] = oneFrameBones[i].boneMatrixWorld.m[0];
oneFrameBonesMatrix.m[1] = oneFrameBones[i].boneMatrixWorld.m[1];
oneFrameBonesMatrix.m[2] = oneFrameBones[i].boneMatrixWorld.m[2];
oneFrameBonesMatrix.m[3] = oneFrameBones[i].boneMatrixWorld.m[0 + 1*4];
oneFrameBonesMatrix.m[4] = oneFrameBones[i].boneMatrixWorld.m[1 + 1*4];
oneFrameBonesMatrix.m[5] = oneFrameBones[i].boneMatrixWorld.m[2 + 1*4];
oneFrameBonesMatrix.m[6] = oneFrameBones[i].boneMatrixWorld.m[0 + 2*4];
oneFrameBonesMatrix.m[7] = oneFrameBones[i].boneMatrixWorld.m[1 + 2*4];
oneFrameBonesMatrix.m[8] = oneFrameBones[i].boneMatrixWorld.m[2 + 2*4];
Matrix3f nextFrameBonesMatrix;
nextFrameBonesMatrix.m[0] = nextFrameBones[i].boneMatrixWorld.m[0];
nextFrameBonesMatrix.m[1] = nextFrameBones[i].boneMatrixWorld.m[1];
nextFrameBonesMatrix.m[2] = nextFrameBones[i].boneMatrixWorld.m[2];
nextFrameBonesMatrix.m[3] = nextFrameBones[i].boneMatrixWorld.m[0 + 1 * 4];
nextFrameBonesMatrix.m[4] = nextFrameBones[i].boneMatrixWorld.m[1 + 1 * 4];
nextFrameBonesMatrix.m[5] = nextFrameBones[i].boneMatrixWorld.m[2 + 1 * 4];
nextFrameBonesMatrix.m[6] = nextFrameBones[i].boneMatrixWorld.m[0 + 2 * 4];
nextFrameBonesMatrix.m[7] = nextFrameBones[i].boneMatrixWorld.m[1 + 2 * 4];
nextFrameBonesMatrix.m[8] = nextFrameBones[i].boneMatrixWorld.m[2 + 2 * 4];
Vector4f q1 = MatrixToQuat(oneFrameBonesMatrix);
Vector4f q2 = MatrixToQuat(nextFrameBonesMatrix);
Vector4f q1_norm = q1.normalized();
Vector4f q2_norm = q2.normalized();
Vector4f result = slerp(q1_norm, q2_norm, t);
Matrix3f boneMatrixWorld3 = QuatToMatrix(result);
currentBones[i].boneMatrixWorld = MakeMatrix4x4(boneMatrixWorld3, currentBones[i].boneStartWorld);
Matrix4f currentBoneMatrixWorld4 = currentBones[i].boneMatrixWorld;
Matrix4f startBoneMatrixWorld4 = animations[0].keyFrames[0].bones[i].boneMatrixWorld;
Matrix4f inverstedStartBoneMatrixWorld4 = InverseMatrix(startBoneMatrixWorld4);
skinningMatrixForEachBone[i] = MultMatrixMatrix(currentBoneMatrixWorld4, inverstedStartBoneMatrixWorld4);
}
/*
for (int i = 0; i < currentBones.size(); i++)
{
currentBones[i].boneStartWorld = oneFrameBones[i].boneStartWorld;
currentBones[i].boneMatrixWorld = oneFrameBones[i].boneMatrixWorld;
//Matrix4f currentBoneMatrixWorld4 = MakeMatrix4x4(currentBones[i].boneMatrixWorld, currentBones[i].boneStartWorld);
//Matrix4f startBoneMatrixWorld4 = MakeMatrix4x4(animations[0].keyFrames[0].bones[i].boneMatrixWorld, animations[0].keyFrames[0].bones[i].boneStartWorld);
Matrix4f currentBoneMatrixWorld4 = currentBones[i].boneMatrixWorld;
Matrix4f startBoneMatrixWorld4 = animations[0].keyFrames[0].bones[i].boneMatrixWorld;
Matrix4f inverstedStartBoneMatrixWorld4 = InverseMatrix(startBoneMatrixWorld4);
skinningMatrixForEachBone[i] = MultMatrixMatrix(currentBoneMatrixWorld4, inverstedStartBoneMatrixWorld4);
if (i == 10)
{
std::cout << i << std::endl;
}
}*/
for (int i = 0; i < mesh.PositionData.size(); i++)
{
Vector4f originalPos = {
startMesh.PositionData[i].v[0],
startMesh.PositionData[i].v[1],
startMesh.PositionData[i].v[2], 1.0};
Vector4f finalPos = Vector4f{0.f, 0.f, 0.f, 0.f};
bool vMoved = false;
//Vector3f finalPos = Vector3f{ 0.f, 0.f, 0.f };
for (int j = 0; j < MAX_BONE_COUNT; j++)
{
if (verticesBoneWeight[i][j].weight != 0)
{
vMoved = true;
//finalPos = finalPos + MultVectorMatrix(originalPos, skinningMatrixForEachBone[verticesBoneWeight[i][j].boneIndex]) * verticesBoneWeight[i][j].weight;
finalPos = finalPos + MultMatrixVector(skinningMatrixForEachBone[verticesBoneWeight[i][j].boneIndex], originalPos) * verticesBoneWeight[i][j].weight;
}
}
if (abs(finalPos.v[0] - originalPos.v[0]) > 1 || abs(finalPos.v[1] - originalPos.v[1]) > 1 || abs(finalPos.v[2] - originalPos.v[2]) > 1)
{
}
if (!vMoved)
{
finalPos = originalPos;
}
mesh.PositionData[i].v[0] = finalPos.v[0];
mesh.PositionData[i].v[1] = finalPos.v[1];
mesh.PositionData[i].v[2] = finalPos.v[2];
}
}
}
-60
View File
@@ -1,60 +0,0 @@
#pragma once
#include "ZLMath.h"
#include "Renderer.h"
#include <unordered_map>
namespace ZL
{
constexpr int MAX_BONE_COUNT = 6;
struct Bone
{
Vector3f boneStartWorld;
float boneLength;
Matrix4f boneMatrixWorld;
// boneVector = boneLength * (0, 1, 0) � ���� ��������
// Then multiply by boneMatrixWorld � �� �������� �������� �����
int parent;
std::vector<int> children;
};
struct BoneWeight
{
int boneIndex = -1;
float weight = 0;
};
struct AnimationKeyFrame
{
int frame;
std::vector<Bone> bones;
};
struct Animation
{
std::vector<AnimationKeyFrame> keyFrames;
};
struct BoneSystem
{
VertexDataStruct mesh;
VertexDataStruct startMesh;
std::vector<std::array<BoneWeight, MAX_BONE_COUNT>> verticesBoneWeight;
Matrix4f armatureMatrix;
std::vector<Bone> startBones;
std::vector<Bone> currentBones;
std::vector<Animation> animations;
void LoadFromFile(const std::string& fileName, const std::string& ZIPFileName = "");
void Interpolate(int frame);
};
};
-544
View File
@@ -1,544 +0,0 @@
cmake_minimum_required(VERSION 3.16)
project(space-game001 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(BUILD_CONFIGS Debug Release)
# ==============================
# Папка для всех сторонних либ
# ==============================
set(THIRDPARTY_DIR "${CMAKE_SOURCE_DIR}/thirdparty1")
file(MAKE_DIRECTORY "${THIRDPARTY_DIR}")
macro(log msg)
message(STATUS "${msg}")
endmacro()
# ===========================================
# 1) ZLIB (zlib131.zip → zlib-1.3.1) - без изменений
# ===========================================
set(ZLIB_ARCHIVE "${THIRDPARTY_DIR}/zlib131.zip")
set(ZLIB_SRC_DIR "${THIRDPARTY_DIR}/zlib-1.3.1")
set(ZLIB_BUILD_DIR "${ZLIB_SRC_DIR}/build")
set(ZLIB_INSTALL_DIR "${ZLIB_SRC_DIR}/install")
if(NOT EXISTS "${ZLIB_ARCHIVE}")
log("Downloading zlib131.zip ...")
file(DOWNLOAD
"https://www.zlib.net/zlib131.zip"
"${ZLIB_ARCHIVE}"
SHOW_PROGRESS
)
endif()
if(NOT EXISTS "${ZLIB_SRC_DIR}/CMakeLists.txt")
log("Extracting zlib131.zip to zlib-1.3.1 ...")
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xvf "${ZLIB_ARCHIVE}"
WORKING_DIRECTORY "${THIRDPARTY_DIR}"
RESULT_VARIABLE _zlib_extract_res
)
if(NOT _zlib_extract_res EQUAL 0)
message(FATAL_ERROR "Failed to extract zlib archive")
endif()
endif()
file(MAKE_DIRECTORY "${ZLIB_BUILD_DIR}")
# проверяем, собран ли уже zlib
set(_have_zlib FALSE)
foreach(candidate
"${ZLIB_INSTALL_DIR}/lib/zlibstatic.lib"
)
if(EXISTS "${candidate}")
set(_have_zlib TRUE)
break()
endif()
endforeach()
if(NOT _have_zlib)
log("Configuring zlib ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
-G "${CMAKE_GENERATOR}"
-S "${ZLIB_SRC_DIR}"
-B "${ZLIB_BUILD_DIR}"
-DCMAKE_INSTALL_PREFIX=${ZLIB_INSTALL_DIR}
RESULT_VARIABLE _zlib_cfg_res
)
if(NOT _zlib_cfg_res EQUAL 0)
message(FATAL_ERROR "zlib configure failed")
endif()
foreach(cfg IN LISTS BUILD_CONFIGS)
log("Building ZLIB (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--build "${ZLIB_BUILD_DIR}" --config ${cfg}
RESULT_VARIABLE _zlib_build_res
)
if(NOT _zlib_build_res EQUAL 0)
message(FATAL_ERROR "ZLIB build failed for configuration ${cfg}")
endif()
log("Installing ZLIB (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--install "${ZLIB_BUILD_DIR}" --config ${cfg}
RESULT_VARIABLE _zlib_inst_res
)
if(NOT _zlib_inst_res EQUAL 0)
message(FATAL_ERROR "ZLIB install failed for configuration ${cfg}")
endif()
endforeach()
endif()
# ИСПРАВЛЕНИЕ: Используем свойства для конкретных конфигураций
add_library(zlib_external_lib UNKNOWN IMPORTED GLOBAL)
set_target_properties(zlib_external_lib PROPERTIES
# Динамическая линковка (если zlib.lib - это импорт-библиотека для zlibd.dll)
#IMPORTED_LOCATION_DEBUG "${ZLIB_INSTALL_DIR}/lib/zlibd.lib"
#IMPORTED_LOCATION_RELEASE "${ZLIB_INSTALL_DIR}/lib/zlib.lib"
# Можно также указать статические библиотеки, если вы хотите их использовать
IMPORTED_LOCATION_DEBUG "${ZLIB_INSTALL_DIR}/lib/zlibstaticd.lib"
IMPORTED_LOCATION_RELEASE "${ZLIB_INSTALL_DIR}/lib/zlibstatic.lib"
INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INSTALL_DIR}/include"
)
# ===========================================
# 2) SDL2 (release-2.32.10.zip → SDL-release-2.32.10) - без изменений
# ===========================================
set(SDL2_ARCHIVE "${THIRDPARTY_DIR}/release-2.32.10.zip")
set(SDL2_SRC_DIR "${THIRDPARTY_DIR}/SDL-release-2.32.10")
set(SDL2_BUILD_DIR "${SDL2_SRC_DIR}/build")
set(SDL2_INSTALL_DIR "${SDL2_SRC_DIR}/install")
if(NOT EXISTS "${SDL2_ARCHIVE}")
log("Downloading SDL2 release-2.32.10.zip ...")
file(DOWNLOAD
"https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.32.10.zip"
"${SDL2_ARCHIVE}"
SHOW_PROGRESS
)
endif()
if(NOT EXISTS "${SDL2_SRC_DIR}/CMakeLists.txt")
log("Extracting SDL2 archive to SDL-release-2.32.10 ...")
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xvf "${SDL2_ARCHIVE}"
WORKING_DIRECTORY "${THIRDPARTY_DIR}"
RESULT_VARIABLE _sdl_extract_res
)
if(NOT _sdl_extract_res EQUAL 0)
message(FATAL_ERROR "Failed to extract SDL2 archive")
endif()
endif()
file(MAKE_DIRECTORY "${SDL2_BUILD_DIR}")
set(_have_sdl2 FALSE)
foreach(candidate
"${SDL2_INSTALL_DIR}/lib/SDL2.lib"
"${SDL2_INSTALL_DIR}/lib/SDL2-static.lib"
"${SDL2_INSTALL_DIR}/lib/SDL2d.lib"
)
if(EXISTS "${candidate}")
set(_have_sdl2 TRUE)
break()
endif()
endforeach()
if(NOT _have_sdl2)
log("Configuring SDL2 ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
-G "${CMAKE_GENERATOR}"
-S "${SDL2_SRC_DIR}"
-B "${SDL2_BUILD_DIR}"
-DCMAKE_INSTALL_PREFIX=${SDL2_INSTALL_DIR}
-DCMAKE_PREFIX_PATH=${ZLIB_INSTALL_DIR} # путь к zlib для SDL2
RESULT_VARIABLE _sdl_cfg_res
)
if(NOT _sdl_cfg_res EQUAL 0)
message(FATAL_ERROR "SDL2 configure failed")
endif()
# --- ИЗМЕНЕНИЕ: Цикл по конфигурациям Debug и Release ---
foreach(cfg IN LISTS BUILD_CONFIGS)
log("Building SDL2 (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--build "${SDL2_BUILD_DIR}" --config ${cfg}
RESULT_VARIABLE _sdl_build_res
)
if(NOT _sdl_build_res EQUAL 0)
message(FATAL_ERROR "SDL2 build failed for configuration ${cfg}")
endif()
log("Installing SDL2 (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--install "${SDL2_BUILD_DIR}" --config ${cfg}
RESULT_VARIABLE _sdl_inst_res
)
if(NOT _sdl_inst_res EQUAL 0)
message(FATAL_ERROR "SDL2 install failed for configuration ${cfg}")
endif()
endforeach()
# ------------------------------------------------------
endif()
# ИСПРАВЛЕНИЕ: SDL2: Используем свойства для конкретных конфигураций
add_library(SDL2_external_lib UNKNOWN IMPORTED GLOBAL)
set_target_properties(SDL2_external_lib PROPERTIES
# Динамическая линковка SDL2
IMPORTED_LOCATION_DEBUG "${SDL2_INSTALL_DIR}/lib/SDL2d.lib"
IMPORTED_LOCATION_RELEASE "${SDL2_INSTALL_DIR}/lib/SDL2.lib"
# Оба include-пути: и include, и include/SDL2
INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INSTALL_DIR}/include;${SDL2_INSTALL_DIR}/include/SDL2"
)
# SDL2main (обычно статическая)
add_library(SDL2main_external_lib UNKNOWN IMPORTED GLOBAL)
set_target_properties(SDL2main_external_lib PROPERTIES
# ИСПРАВЛЕНО: Указываем пути для Debug и Release, используя
# соглашение, что Debug имеет суффикс 'd', а Release — нет.
IMPORTED_LOCATION_DEBUG "${SDL2_INSTALL_DIR}/lib/SDL2maind.lib"
IMPORTED_LOCATION_RELEASE "${SDL2_INSTALL_DIR}/lib/SDL2main.lib"
INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INSTALL_DIR}/include"
)
log("-----${SDL2_INSTALL_DIR}/lib/SDL2maind.lib")
# ===========================================
# 3) libpng (v1.6.51.zip → libpng-1.6.51) - без изменений
# ===========================================
set(LIBPNG_ARCHIVE "${THIRDPARTY_DIR}/v1.6.51.zip")
set(LIBPNG_SRC_DIR "${THIRDPARTY_DIR}/libpng-1.6.51")
set(LIBPNG_BUILD_DIR "${LIBPNG_SRC_DIR}/build")
set(LIBPNG_INSTALL_DIR "${LIBPNG_SRC_DIR}/install") # на будущее
if(NOT EXISTS "${LIBPNG_ARCHIVE}")
log("Downloading libpng v1.6.51.zip ...")
file(DOWNLOAD
"https://github.com/pnggroup/libpng/archive/refs/tags/v1.6.51.zip"
"${LIBPNG_ARCHIVE}"
SHOW_PROGRESS
)
endif()
if(NOT EXISTS "${LIBPNG_SRC_DIR}/CMakeLists.txt")
log("Extracting libpng v1.6.51.zip to libpng-1.6.51 ...")
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xvf "${LIBPNG_ARCHIVE}"
WORKING_DIRECTORY "${THIRDPARTY_DIR}"
RESULT_VARIABLE _png_extract_res
)
if(NOT _png_extract_res EQUAL 0)
message(FATAL_ERROR "Failed to extract libpng archive")
endif()
endif()
file(MAKE_DIRECTORY "${LIBPNG_BUILD_DIR}")
# Проверяем, есть ли уже .lib (build/Debug или install/lib)
set(_libpng_candidates
"${LIBPNG_BUILD_DIR}/Debug/libpng16_staticd.lib"
"${LIBPNG_BUILD_DIR}/Release/libpng16_static.lib"
)
set(_have_png FALSE)
foreach(candidate IN LISTS _libpng_candidates)
if(EXISTS "${candidate}")
set(_have_png TRUE)
break()
endif()
endforeach()
if(NOT _have_png)
log("Configuring libpng ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
-G "${CMAKE_GENERATOR}"
-S "${LIBPNG_SRC_DIR}"
-B "${LIBPNG_BUILD_DIR}"
-DCMAKE_INSTALL_PREFIX=${LIBPNG_INSTALL_DIR}
-DCMAKE_PREFIX_PATH=${ZLIB_INSTALL_DIR}
-DZLIB_ROOT=${ZLIB_INSTALL_DIR}
RESULT_VARIABLE _png_cfg_res
)
if(NOT _png_cfg_res EQUAL 0)
message(FATAL_ERROR "libpng configure failed")
endif()
# --- ИЗМЕНЕНИЕ: Цикл по конфигурациям Debug и Release ---
foreach(cfg IN LISTS BUILD_CONFIGS)
log("Building libpng (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--build "${LIBPNG_BUILD_DIR}" --config ${cfg}
RESULT_VARIABLE _png_build_res
)
if(NOT _png_build_res EQUAL 0)
message(FATAL_ERROR "libpng build failed for configuration ${cfg}")
endif()
# Поскольку вы не используете "cmake --install" для libpng,
# здесь нет необходимости в дополнительном шаге установки.
# Файлы .lib будут сгенерированы в подкаталоге ${LIBPNG_BUILD_DIR}/${cfg} (например, build/Debug или build/Release).
endforeach()
# ------------------------------------------------------
endif()
add_library(libpng_external_lib UNKNOWN IMPORTED GLOBAL)
set_target_properties(libpng_external_lib PROPERTIES
# Предполагая, что libpng использует статический вариант
IMPORTED_LOCATION_DEBUG "${LIBPNG_BUILD_DIR}/Debug/libpng16_staticd.lib"
IMPORTED_LOCATION_RELEASE "${LIBPNG_BUILD_DIR}/Release/libpng16_static.lib"
# png.h, pngconf.h – в SRC, pnglibconf.h – в BUILD
INTERFACE_INCLUDE_DIRECTORIES "${LIBPNG_SRC_DIR};${LIBPNG_BUILD_DIR}"
)
# ===========================================
# 4) libzip (v1.11.4.zip → libzip-1.11.4) - НОВАЯ ЗАВИСИМОСТЬ
# ===========================================
set(LIBZIP_ARCHIVE "${THIRDPARTY_DIR}/v1.11.4.zip")
set(LIBZIP_SRC_DIR "${THIRDPARTY_DIR}/libzip-1.11.4")
set(LIBZIP_BUILD_DIR "${LIBZIP_SRC_DIR}/build")
#set(LIBZIP_INSTALL_DIR "${LIBZIP_SRC_DIR}/install")
set(LIBZIP_BASE_DIR "${LIBZIP_SRC_DIR}/install")
if(NOT EXISTS "${LIBZIP_ARCHIVE}")
log("Downloading libzip v1.11.4.zip ...")
file(DOWNLOAD
"https://github.com/nih-at/libzip/archive/refs/tags/v1.11.4.zip"
"${LIBZIP_ARCHIVE}"
SHOW_PROGRESS
)
endif()
if(NOT EXISTS "${LIBZIP_SRC_DIR}/CMakeLists.txt")
log("Extracting libzip v1.11.4.zip to libzip-1.11.4 ...")
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xvf "${LIBZIP_ARCHIVE}"
WORKING_DIRECTORY "${THIRDPARTY_DIR}"
RESULT_VARIABLE _zip_extract_res
)
if(NOT _zip_extract_res EQUAL 0)
message(FATAL_ERROR "Failed to extract libzip archive")
endif()
endif()
file(MAKE_DIRECTORY "${LIBZIP_BUILD_DIR}")
# Проверяем, собран ли уже libzip
set(_have_zip FALSE)
foreach(candidate
"${LIBZIP_BASE_DIR}-Debug/lib/zip.lib"
"${LIBZIP_BASE_DIR}-Release/lib/zip.lib"
)
if(EXISTS "${candidate}")
set(_have_zip TRUE)
break()
endif()
endforeach()
if(NOT _have_zip)
foreach(cfg IN LISTS BUILD_CONFIGS)
log("Configuring libzip (${cfg})...")
execute_process(
COMMAND ${CMAKE_COMMAND}
-G "${CMAKE_GENERATOR}"
-S "${LIBZIP_SRC_DIR}"
-B "${LIBZIP_SRC_DIR}/build-${cfg}"
-DCMAKE_INSTALL_PREFIX=${LIBZIP_BASE_DIR}-${cfg}
-DCMAKE_PREFIX_PATH=${ZLIB_INSTALL_DIR}
-DZLIB_ROOT=${ZLIB_INSTALL_DIR}
-DENABLE_COMMONCRYPTO=OFF
-DENABLE_GNUTLS=OFF
-DENABLE_MBEDTLS=OFF
-DENABLE_OPENSSL=OFF
-DENABLE_WINDOWS_CRYPTO=OFF
-DENABLE_FUZZ=OFF
RESULT_VARIABLE _zip_cfg_res
)
if(NOT _zip_cfg_res EQUAL 0)
message(FATAL_ERROR "libzip configure failed")
endif()
log("Building libzip (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND} --build "${LIBZIP_SRC_DIR}/build-${cfg}" --config ${cfg} -v
RESULT_VARIABLE _zip_build_res
)
if(NOT _zip_build_res EQUAL 0)
message(FATAL_ERROR "libzip build failed for configuration ${cfg}")
endif()
log("Installing libzip (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND} --install "${LIBZIP_SRC_DIR}/build-${cfg}" --config ${cfg} -v
RESULT_VARIABLE _zip_inst_res
)
if(NOT _zip_inst_res EQUAL 0)
message(FATAL_ERROR "libzip install failed for configuration ${cfg}")
endif()
endforeach()
endif()
add_library(libzip_external_lib UNKNOWN IMPORTED GLOBAL)
set_target_properties(libzip_external_lib PROPERTIES
IMPORTED_LOCATION_DEBUG "${LIBZIP_BASE_DIR}-Debug/lib/zip.lib" # ИСПРАВЛЕНО
IMPORTED_LOCATION_RELEASE "${LIBZIP_BASE_DIR}-Release/lib/zip.lib" # ИСПРАВЛЕНО
INTERFACE_INCLUDE_DIRECTORIES "$<IF:$<CONFIG:Debug>,${LIBZIP_BASE_DIR}-Debug/include,${LIBZIP_BASE_DIR}-Release/include>"
# libzip требует zlib для линковки
INTERFACE_LINK_LIBRARIES zlib_external_lib
)
# ===========================================
# Основной проект space-game001
# ===========================================
add_executable(space-game001
main.cpp
Game.cpp
Game.h
Environment.cpp
Environment.h
Renderer.cpp
Renderer.h
ShaderManager.cpp
ShaderManager.h
TextureManager.cpp
TextureManager.h
TextModel.cpp
TextModel.h
AudioPlayerAsync.cpp
AudioPlayerAsync.h
BoneAnimatedModel.cpp
BoneAnimatedModel.h
ZLMath.cpp
ZLMath.h
OpenGlExtensions.cpp
OpenGlExtensions.h
Utils.cpp
Utils.h
SparkEmitter.cpp
SparkEmitter.h
PlanetObject.cpp
PlanetObject.h
PlanetData.cpp
PlanetData.h
Perlin.cpp
Perlin.h
StoneObject.cpp
StoneObject.h
FrameBuffer.cpp
FrameBuffer.h
)
# Установка проекта по умолчанию для Visual Studio
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT space-game001)
# include-пути проекта
target_include_directories(space-game001 PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}"
#"${CMAKE_CURRENT_SOURCE_DIR}/gl"
#"${CMAKE_CURRENT_SOURCE_DIR}/cmakeaudioplayer/include"
#"${SDL2_INSTALL_DIR}/include"
#"${SDL2_INSTALL_DIR}/include/SDL2"
#"${LIBZIP_INSTALL_DIR}-Release/include" # Добавил include-путь для libzip
)
set_target_properties(space-game001 PROPERTIES
OUTPUT_NAME "space-game001"
)
# Определения препроцессора:
# PNG_ENABLED – включает код PNG в TextureManager
# SDL_MAIN_HANDLED – отключает переопределение main -> SDL_main
target_compile_definitions(space-game001 PRIVATE
PNG_ENABLED
SDL_MAIN_HANDLED
)
# Линкуем с SDL2main, если он вообще установлен
target_link_libraries(space-game001 PRIVATE SDL2main_external_lib)
# Линкуем сторонние библиотеки
target_link_libraries(space-game001 PRIVATE
SDL2_external_lib
libpng_external_lib
zlib_external_lib
libzip_external_lib
)
# Линкуем OpenGL (Windows)
if(WIN32)
target_link_libraries(space-game001 PRIVATE opengl32)
endif()
# ===========================================
# Копирование SDL2d.dll и zlibd.dll рядом с exe
# ===========================================
if (WIN32)
# SDL2: в Debug - SDL2d.dll, в Release - SDL2.dll
set(SDL2_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2_INSTALL_DIR}/bin/SDL2d.dll,${SDL2_INSTALL_DIR}/bin/SDL2.dll>")
set(SDL2_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:space-game001>/SDL2d.dll,$<TARGET_FILE_DIR:space-game001>/SDL2.dll>")
set(LIBZIP_DLL_SRC "$<IF:$<CONFIG:Debug>,${LIBZIP_BASE_DIR}-Debug/bin/zip.dll,${LIBZIP_BASE_DIR}-Release/bin/zip.dll>")
add_custom_command(TARGET space-game001 POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Copying DLLs to output folder..."
# Копируем SDL2 (целевое имя всегда SDL2.dll)
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${SDL2_DLL_SRC}"
"${SDL2_DLL_DST}"
# Копируем LIBZIP (целевое имя всегда zip.dll)
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${LIBZIP_DLL_SRC}"
"$<TARGET_FILE_DIR:space-game001>/zip.dll"
)
endif()
# ===========================================
# Копирование ресурсов после сборки
# ===========================================
# Какие папки с ресурсами нужно копировать
set(RUNTIME_RESOURCE_DIRS
"resources"
"shaders"
)
# Копируем ресурсы и шейдеры в папку exe и в корень build/
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
add_custom_command(TARGET space-game001 POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Copying ${resdir} to runtime folders..."
# 1) туда, где лежит exe (build/Debug, build/Release и т.п.)
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_SOURCE_DIR}/${resdir}"
"$<TARGET_FILE_DIR:space-game001>/${resdir}"
# 2) в корень build, если захочешь запускать из этой папки
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_SOURCE_DIR}/${resdir}"
"${CMAKE_BINARY_DIR}/${resdir}"
)
endforeach()
-43
View File
@@ -1,43 +0,0 @@
#include "Environment.h"
#include "Utils.h"
#include <GL/gl.h>
namespace ZL {
int Environment::windowHeaderHeight = 0;
int Environment::width = 0;
int Environment::height = 0;
float Environment::zoom = 18.f;
bool Environment::leftPressed = false;
bool Environment::rightPressed = false;
bool Environment::upPressed = false;
bool Environment::downPressed = false;
bool Environment::settings_inverseVertical = false;
SDL_Window* Environment::window = nullptr;
bool Environment::showMouse = false;
bool Environment::exitGameLoop = false;
Matrix3f Environment::shipMatrix = Matrix3f::Identity();
Matrix3f Environment::inverseShipMatrix = Matrix3f::Identity();
bool Environment::tapDownHold = false;
Vector2f Environment::tapDownStartPos = { 0, 0 };
Vector2f Environment::tapDownCurrentPos = { 0, 0 };
Vector3f Environment::shipPosition = {0,0,45000.f};
float Environment::shipVelocity = 0.f;
const float Environment::CONST_Z_NEAR = 5.f;
const float Environment::CONST_Z_FAR = 5000.f;
} // namespace ZL
-47
View File
@@ -1,47 +0,0 @@
#pragma once
#include "ZLMath.h"
#ifdef __linux__
#include <SDL2/SDL.h>
#endif
#include "OpenGlExtensions.h"
namespace ZL {
class Environment {
public:
static int windowHeaderHeight;
static int width;
static int height;
static float zoom;
static bool leftPressed;
static bool rightPressed;
static bool upPressed;
static bool downPressed;
static bool settings_inverseVertical;
static Matrix3f shipMatrix;
static Matrix3f inverseShipMatrix;
static SDL_Window* window;
static bool showMouse;
static bool exitGameLoop;
static bool tapDownHold;
static Vector2f tapDownStartPos;
static Vector2f tapDownCurrentPos;
static Vector3f shipPosition;
static float shipVelocity;
static const float CONST_Z_NEAR;
static const float CONST_Z_FAR;
};
} // namespace ZL
-49
View File
@@ -1,49 +0,0 @@
#include "FrameBuffer.h"
#include <iostream>
#include "Environment.h"
namespace ZL {
FrameBuffer::FrameBuffer(int w, int h) : width(w), height(h) {
// 1. Ñîçäàåì FBO
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// 2. Ñîçäàåì òåêñòóðó, êóäà áóäåì "ôîòîãðàôèðîâàòü"
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
// Óñòàíàâëèâàåì ïàðàìåòðû òåêñòóðû
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 3. Ïðèâÿçûâàåì òåêñòóðó ê FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0);
// Ïðîâåðêà ãîòîâíîñòè
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
std::cerr << "Error: Framebuffer is not complete!" << std::endl;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
FrameBuffer::~FrameBuffer() {
glDeleteFramebuffers(1, &fbo);
glDeleteTextures(1, &textureID);
}
void FrameBuffer::Bind() {
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glViewport(0, 0, width, height); // Âàæíî: óñòàíàâëèâàåì âüþïîðò ïîä ðàçìåð òåêñòóðû
}
void FrameBuffer::Unbind() {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Çäåñü æåëàòåëüíî âîçâðàùàòü âüþïîðò ê ðàçìåðàì ýêðàíà,
// íàïðèìåð, ÷åðåç Environment::width/height
glViewport(0, 0, Environment::width, Environment::height);
}
} // namespace ZL
-29
View File
@@ -1,29 +0,0 @@
#pragma once
#include "OpenGlExtensions.h"
#include <memory>
namespace ZL {
class FrameBuffer {
private:
GLuint fbo = 0;
GLuint textureID = 0;
int width, height;
public:
FrameBuffer(int w, int h);
~FrameBuffer();
// Çàïðåùàåì êîïèðîâàíèå, êàê â VBOHolder
FrameBuffer(const FrameBuffer&) = delete;
FrameBuffer& operator=(const FrameBuffer&) = delete;
void Bind(); // Íà÷àòü ðåíäåð â ýòîò áóôåð
void Unbind(); // Âåðíóòüñÿ ê îáû÷íîìó ðåíäåðó â ýêðàí
GLuint getTextureID() const { return textureID; }
int getWidth() const { return width; }
int getHeight() const { return height; }
};
} // namespace ZL
-655
View File
@@ -1,655 +0,0 @@
#include "Game.h"
#include "AnimatedModel.h"
#include "BoneAnimatedModel.h"
#include "Utils.h"
#include "OpenGlExtensions.h"
#include <iostream>
#include "TextureManager.h"
#include "TextModel.h"
#include "StoneObject.h"
#include <random>
#include <cmath>
namespace ZL
{
#ifdef EMSCRIPTEN
const char* CONST_ZIP_FILE = "space-game001.zip";
#else
const char* CONST_ZIP_FILE = "";
#endif
Vector4f generateRandomQuaternion(std::mt19937& gen)
{
std::normal_distribution<> distrib(0.0, 1.0);
Vector4f randomQuat = {
(float)distrib(gen),
(float)distrib(gen),
(float)distrib(gen),
(float)distrib(gen)
};
return randomQuat.normalized();
}
std::vector<BoxCoords> generateRandomBoxCoords(int N)
{
const float MIN_DISTANCE = 3.0f;
const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE;
const float MIN_COORD = -100.0f;
const float MAX_COORD = 100.0f;
const int MAX_ATTEMPTS = 1000;
std::vector<BoxCoords> boxCoordsArr;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<> distrib(MIN_COORD, MAX_COORD);
int generatedCount = 0;
while (generatedCount < N)
{
bool accepted = false;
int attempts = 0;
while (!accepted && attempts < MAX_ATTEMPTS)
{
Vector3f newPos(
(float)distrib(gen),
(float)distrib(gen),
(float)distrib(gen)
);
accepted = true;
for (const auto& existingBox : boxCoordsArr)
{
Vector3f diff = newPos - existingBox.pos;
float distanceSquared = diff.squaredNorm();
if (distanceSquared < MIN_DISTANCE_SQUARED)
{
accepted = false;
break;
}
}
if (accepted)
{
Vector4f randomQuat = generateRandomQuaternion(gen);
Matrix3f randomMatrix = QuatToMatrix(randomQuat);
boxCoordsArr.emplace_back(BoxCoords{ newPos, randomMatrix });
generatedCount++;
}
attempts++;
}
if (!accepted) {
std::cerr << "Ïðåäóïðåæäåíèå: Íå óäàëîñü ñãåíåðèðîâàòü " << N << " îáúåêòîâ. Ñãåíåðèðîâàíî: " << generatedCount << std::endl;
break;
}
}
return boxCoordsArr;
}
Game::Game()
: window(nullptr)
, glContext(nullptr)
, newTickCount(0)
, lastTickCount(0)
{
std::vector<Vector3f> emissionPoints = {
Vector3f{-2.1f, 0.9f, 5.0f},
Vector3f{2.1f, 0.9f, 5.0f}
};
sparkEmitter = SparkEmitter(emissionPoints, 100.0f);
}
Game::~Game() {
if (glContext) {
SDL_GL_DeleteContext(glContext);
}
if (window) {
SDL_DestroyWindow(window);
}
SDL_Quit();
}
void Game::setup() {
glContext = SDL_GL_CreateContext(ZL::Environment::window);
ZL::BindOpenGlFunctions();
ZL::CheckGlError();
// Initialize renderer
#ifdef EMSCRIPTEN
renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env.vertex", "./shaders/env_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "./shaders/defaultAtmosphere.vertex", "./shaders/defaultAtmosphere.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultColorPlanet", "./shaders/defaultColorPlanet.vertex", "./shaders/defaultColorPlanet_web.fragment", CONST_ZIP_FILE);
#else
renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor_fog.vertex", "./shaders/defaultColor_fog_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env_sky.vertex", "./shaders/env_sky_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "./shaders/defaultAtmosphere.vertex", "./shaders/defaultAtmosphere.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultColor2", "./shaders/defaultColor_fog2.vertex", "./shaders/defaultColor_fog2_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultColorStones", "./shaders/defaultColor_fog_stones.vertex", "./shaders/defaultColor_fog_stones_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("planetBake", "./shaders/planet_bake.vertex", "./shaders/planet_bake_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("planetStone", "./shaders/planet_stone.vertex", "./shaders/planet_stone_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("planetLand", "./shaders/planet_land.vertex", "./shaders/planet_land_desktop.fragment", CONST_ZIP_FILE);
#endif
cubemapTexture = std::make_shared<Texture>(
std::array<TextureDataStruct, 6>{
CreateTextureDataFromBmp24("./resources/sky/space_rt.bmp", CONST_ZIP_FILE),
CreateTextureDataFromBmp24("./resources/sky/space_lf.bmp", CONST_ZIP_FILE),
CreateTextureDataFromBmp24("./resources/sky/space_up.bmp", CONST_ZIP_FILE),
CreateTextureDataFromBmp24("./resources/sky/space_dn.bmp", CONST_ZIP_FILE),
CreateTextureDataFromBmp24("./resources/sky/space_bk.bmp", CONST_ZIP_FILE),
CreateTextureDataFromBmp24("./resources/sky/space_ft.bmp", CONST_ZIP_FILE)
});
cubemap.data = ZL::CreateCubemap(500);
cubemap.RefreshVBO();
//Load texture
spaceshipTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/DefaultMaterial_BaseColor_shine.png", CONST_ZIP_FILE));
spaceshipBase = LoadFromTextFile02("./resources/spaceship006.txt", CONST_ZIP_FILE);
spaceshipBase.RotateByMatrix(QuatToMatrix(QuatFromRotateAroundY(M_PI / 2.0)));
//spaceshipBase.Move(Vector3f{ -0.52998, -13, 0 });
//spaceshipBase.Move(Vector3f{ -0.52998, -10, 10 });
spaceship.AssignFrom(spaceshipBase);
spaceship.RefreshVBO();
//Boxes
boxTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/box/box.png", CONST_ZIP_FILE));
boxBase = LoadFromTextFile02("./resources/box/box.txt", CONST_ZIP_FILE);
boxCoordsArr = generateRandomBoxCoords(50);
boxRenderArr.resize(boxCoordsArr.size());
for (int i = 0; i < boxCoordsArr.size(); i++)
{
boxRenderArr[i].AssignFrom(boxBase);
//boxRenderArr[i].data = CreateBaseConvexPolyhedron(1999);
boxRenderArr[i].RefreshVBO();
}
sparkTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/spark.png", CONST_ZIP_FILE));
sparkEmitter.setTexture(sparkTexture);
buttonTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/button.png", CONST_ZIP_FILE));
button.data.PositionData.push_back({ 100, 100, 0 });
button.data.PositionData.push_back({ 100, 150, 0 });
button.data.PositionData.push_back({ 300, 150, 0 });
button.data.PositionData.push_back({ 100, 100, 0 });
button.data.PositionData.push_back({ 300, 150, 0 });
button.data.PositionData.push_back({ 300, 100, 0 });
button.data.TexCoordData.push_back({ 0,0 });
button.data.TexCoordData.push_back({ 0,1 });
button.data.TexCoordData.push_back({ 1,1 });
button.data.TexCoordData.push_back({ 0,0 });
button.data.TexCoordData.push_back({ 1,1 });
button.data.TexCoordData.push_back({ 1,0 });
button.RefreshVBO();
musicVolumeBarTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/musicVolumeBarTexture.png", CONST_ZIP_FILE));
musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 });
musicVolumeBar.data.PositionData.push_back({ 1190, 600, 0 });
musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 });
musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 });
musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 });
musicVolumeBar.data.PositionData.push_back({ 1200, 100, 0 });
musicVolumeBar.data.TexCoordData.push_back({ 0,0 });
musicVolumeBar.data.TexCoordData.push_back({ 0,1 });
musicVolumeBar.data.TexCoordData.push_back({ 1,1 });
musicVolumeBar.data.TexCoordData.push_back({ 0,0 });
musicVolumeBar.data.TexCoordData.push_back({ 1,1 });
musicVolumeBar.data.TexCoordData.push_back({ 1,0 });
musicVolumeBar.RefreshVBO();
musicVolumeBarButtonTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/musicVolumeBarButton.png", CONST_ZIP_FILE));
float musicVolumeBarButtonButtonCenterY = 350.0f;
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 });
musicVolumeBarButton.data.TexCoordData.push_back({ 0,1 });
musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 });
musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 });
musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 });
musicVolumeBarButton.data.TexCoordData.push_back({ 1,0 });
musicVolumeBarButton.RefreshVBO();
renderer.InitOpenGL();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
planetObject.init();
rockTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/rock.png", ""));
}
void Game::drawCubemap(float skyPercent)
{
static const std::string defaultShaderName = "default";
static const std::string envShaderName = "env";
static const std::string vPositionName = "vPosition";
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
static const std::string skyPercentUniformName = "skyPercent";
renderer.shaderManager.PushShader(envShaderName);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.RenderUniform1f(skyPercentUniformName, skyPercent);
renderer.EnableVertexAttribArray(vPositionName);
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
renderer.PushMatrix();
renderer.LoadIdentity();
renderer.RotateMatrix(Environment::inverseShipMatrix);
CheckGlError();
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture->getTexID());
renderer.DrawVertexRenderStruct(cubemap);
CheckGlError();
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.DisableVertexAttribArray(vPositionName);
renderer.shaderManager.PopShader();
CheckGlError();
}
void Game::drawShip()
{
static const std::string defaultShaderName = "default";
static const std::string envShaderName = "env";
static const std::string vPositionName = "vPosition";
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
renderer.shaderManager.PushShader(defaultShaderName);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vTexCoordName);
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
renderer.PushMatrix();
renderer.LoadIdentity();
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
renderer.DrawVertexRenderStruct(spaceship);
sparkEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.DisableVertexAttribArray(vPositionName);
renderer.DisableVertexAttribArray(vTexCoordName);
renderer.shaderManager.PopShader();
CheckGlError();
}
void Game::drawBoxes()
{
static const std::string defaultShaderName = "default";
static const std::string envShaderName = "env";
static const std::string vPositionName = "vPosition";
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
renderer.shaderManager.PushShader(defaultShaderName);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vTexCoordName);
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
for (int i = 0; i < boxCoordsArr.size(); i++)
{
renderer.PushMatrix();
renderer.LoadIdentity();
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
renderer.RotateMatrix(Environment::inverseShipMatrix);
renderer.TranslateMatrix(-Environment::shipPosition);
renderer.TranslateMatrix({ 0.f, 0.f, 45000.f });
renderer.TranslateMatrix(boxCoordsArr[i].pos);
renderer.RotateMatrix(boxCoordsArr[i].m);
glBindTexture(GL_TEXTURE_2D, boxTexture->getTexID());
//glBindTexture(GL_TEXTURE_2D, rockTexture->getTexID());
renderer.DrawVertexRenderStruct(boxRenderArr[i]);
renderer.PopMatrix();
}
renderer.PopProjectionMatrix();
renderer.DisableVertexAttribArray(vPositionName);
renderer.DisableVertexAttribArray(vTexCoordName);
renderer.shaderManager.PopShader();
CheckGlError();
}
void Game::UpdateVolumeKnob() {
float musicVolumeBarButtonButtonCenterY = volumeBarMinY + musicVolume * (volumeBarMaxY - volumeBarMinY);
auto& pos = musicVolumeBarButton.data.PositionData;
pos[0] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 };
pos[1] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 };
pos[2] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 };
pos[3] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 };
pos[4] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 };
pos[5] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 };
musicVolumeBarButton.RefreshVBO();
}
void Game::UpdateVolumeFromMouse(int mouseX, int mouseY) {
int uiX = mouseX;
int uiY = Environment::height - mouseY;
Environment::shipVelocity = (musicVolume) * (20.0);
if (uiY < volumeBarMinY || uiY > volumeBarMaxY)
{
return;
}
float t = (uiY - volumeBarMinY) / (volumeBarMaxY - volumeBarMinY);
if (t < 0.0f) t = 0.0f;
if (t > 1.0f) t = 1.0f;
musicVolume = t;
UpdateVolumeKnob();
}
void Game::drawUI()
{
static const std::string defaultShaderName = "default";
static const std::string envShaderName = "env";
static const std::string vPositionName = "vPosition";
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
glClear(GL_DEPTH_BUFFER_BIT);
renderer.shaderManager.PushShader(defaultShaderName);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vTexCoordName);
renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1);
renderer.PushMatrix();
renderer.LoadIdentity();
glBindTexture(GL_TEXTURE_2D, buttonTexture->getTexID());
renderer.DrawVertexRenderStruct(button);
glBindTexture(GL_TEXTURE_2D, musicVolumeBarTexture->getTexID());
renderer.DrawVertexRenderStruct(musicVolumeBar);
glBindTexture(GL_TEXTURE_2D, musicVolumeBarButtonTexture->getTexID());
renderer.DrawVertexRenderStruct(musicVolumeBarButton);
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.DisableVertexAttribArray(vPositionName);
renderer.DisableVertexAttribArray(vTexCoordName);
renderer.shaderManager.PopShader();
CheckGlError();
}
void Game::drawScene() {
static const std::string defaultShaderName = "default";
static const std::string envShaderName = "env";
static const std::string vPositionName = "vPosition";
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glViewport(0, 0, Environment::width, Environment::height);
CheckGlError();
float skyPercent = 0.0;
float distance = planetObject.distanceToPlanetSurface(Environment::shipPosition);
if (distance > 1900.f)
{
skyPercent = 0.0f;
}
else if (distance < 1000.f)
{
skyPercent = 1.0f;
}
else
{
skyPercent = (1900.f - distance) / 900.f;
}
drawCubemap(skyPercent);
planetObject.draw(renderer);
if (planetObject.distanceToPlanetSurface(Environment::shipPosition) > 100.f)
{
glClear(GL_DEPTH_BUFFER_BIT);
}
drawShip();
drawBoxes();
drawUI();
CheckGlError();
}
void Game::processTickCount() {
if (lastTickCount == 0) {
lastTickCount = SDL_GetTicks64();
return;
}
newTickCount = SDL_GetTicks64();
if (newTickCount - lastTickCount > CONST_TIMER_INTERVAL) {
size_t delta = (newTickCount - lastTickCount > CONST_MAX_TIME_INTERVAL) ?
CONST_MAX_TIME_INTERVAL : newTickCount - lastTickCount;
//gameObjects.updateScene(delta);
sparkEmitter.update(static_cast<float>(delta));
planetObject.update(static_cast<float>(delta));
if (Environment::tapDownHold) {
float diffx = Environment::tapDownCurrentPos.v[0] - Environment::tapDownStartPos.v[0];
float diffy = Environment::tapDownCurrentPos.v[1] - Environment::tapDownStartPos.v[1];
if (abs(diffy) > 5.0 || abs(diffx) > 5.0) //threshold
{
float rotationPower = sqrtf(diffx * diffx + diffy * diffy);
//std::cout << rotationPower << std::endl;
float deltaAlpha = rotationPower * delta * M_PI / 500000.f;
Vector3f rotationDirection = { diffy, diffx, 0 };
rotationDirection = rotationDirection.normalized();
Vector4f rotateQuat = {
rotationDirection.v[0] * sin(deltaAlpha * 0.5f),
rotationDirection.v[1] * sin(deltaAlpha * 0.5f),
rotationDirection.v[2] * sin(deltaAlpha * 0.5f),
cos(deltaAlpha * 0.5f) };
Matrix3f rotateMat = QuatToMatrix(rotateQuat);
Environment::shipMatrix = MultMatrixMatrix(Environment::shipMatrix, rotateMat);
Environment::inverseShipMatrix = InverseMatrix(Environment::shipMatrix);
}
}
if (fabs(Environment::shipVelocity) > 0.01f)
{
Vector3f velocityDirection = { 0,0, -Environment::shipVelocity * delta / 1000.f };
Vector3f velocityDirectionAdjusted = MultMatrixVector(Environment::shipMatrix, velocityDirection);
Environment::shipPosition = Environment::shipPosition + velocityDirectionAdjusted;
}
lastTickCount = newTickCount;
}
}
void Game::render() {
SDL_GL_MakeCurrent(ZL::Environment::window, glContext);
ZL::CheckGlError();
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
drawScene();
processTickCount();
SDL_GL_SwapWindow(ZL::Environment::window);
}
void Game::update() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
Environment::exitGameLoop = true;
}
else if (event.type == SDL_MOUSEBUTTONDOWN) {
// 1. Îáðàáîòêà íàæàòèÿ êíîïêè ìûøè
int mx = event.button.x;
int my = event.button.y;
std::cout << mx << " " << my << '\n';
int uiX = mx;
int uiY = Environment::height - my;
if (uiX >= volumeBarMinX - 40 && uiX <= volumeBarMaxX + 40 &&
uiY >= volumeBarMinY - 40 && uiY <= volumeBarMaxY + 40) {
isDraggingVolume = true;
UpdateVolumeFromMouse(mx, my);
}
else {
Environment::tapDownHold = true;
// Êîîðäèíàòû íà÷àëüíîãî íàæàòèÿ
Environment::tapDownStartPos.v[0] = event.button.x;
Environment::tapDownStartPos.v[1] = event.button.y;
// Íà÷àëüíàÿ ïîçèöèÿ òàêæå ñòàíîâèòñÿ òåêóùåé
Environment::tapDownCurrentPos.v[0] = event.button.x;
Environment::tapDownCurrentPos.v[1] = event.button.y;
}
}
else if (event.type == SDL_MOUSEBUTTONUP) {
// 2. Îáðàáîòêà îòïóñêàíèÿ êíîïêè ìûøè
isDraggingVolume = false;
Environment::tapDownHold = false;
}
else if (event.type == SDL_MOUSEMOTION) {
// 3. Îáðàáîòêà ïåðåìåùåíèÿ ìûøè
int mx = event.motion.x;
int my = event.motion.y;
if (isDraggingVolume) {
// Äâèãàåì ìûøü ïî ñëàéäåðó — ìåíÿåì ãðîìêîñòü è ïîçèöèþ êðóæêà
UpdateVolumeFromMouse(mx, my);
}
if (Environment::tapDownHold) {
// Îáíîâëåíèå òåêóùåé ïîçèöèè, åñëè êíîïêà óäåðæèâàåòñÿ
Environment::tapDownCurrentPos.v[0] = event.motion.x;
Environment::tapDownCurrentPos.v[1] = event.motion.y;
}
}
else if (event.type == SDL_MOUSEWHEEL) {
static const float zoomstep = 2.0f;
if (event.wheel.y > 0) {
Environment::zoom -= zoomstep;
}
else if (event.wheel.y < 0) {
Environment::zoom += zoomstep;
}
if (Environment::zoom < zoomstep) {
Environment::zoom = zoomstep;
}
}
else if (event.type == SDL_KEYUP)
{
if (event.key.keysym.sym == SDLK_i)
{
Environment::shipVelocity += 500.f;
}
if (event.key.keysym.sym == SDLK_k)
{
Environment::shipVelocity -= 500.f;
}
if (event.key.keysym.sym == SDLK_o)
{
Environment::shipVelocity += 50.f;
}
if (event.key.keysym.sym == SDLK_l)
{
Environment::shipVelocity -= 50.f;
}
}
}
render();
}
} // namespace ZL
-95
View File
@@ -1,95 +0,0 @@
#pragma once
#include "OpenGlExtensions.h"
#include "Renderer.h"
#include "Environment.h"
#include "TextureManager.h"
#include "SparkEmitter.h"
#include "PlanetObject.h"
namespace ZL {
struct BoxCoords
{
Vector3f pos;
Matrix3f m;
};
class Game {
public:
Game();
~Game();
void setup();
void update();
void render();
bool shouldExit() const { return Environment::exitGameLoop; }
private:
void processTickCount();
void drawScene();
void drawCubemap(float skyPercent);
void drawShip();
void drawBoxes();
void drawUI();
SDL_Window* window;
SDL_GLContext glContext;
Renderer renderer;
size_t newTickCount;
size_t lastTickCount;
std::shared_ptr<Texture> rockTexture;
std::vector<BoxCoords> boxCoordsArr;
std::vector<VertexRenderStruct> boxRenderArr;
std::shared_ptr<Texture> buttonTexture;
VertexRenderStruct button;
std::shared_ptr<Texture> musicVolumeBarTexture;
VertexRenderStruct musicVolumeBar;
std::shared_ptr<Texture> musicVolumeBarButtonTexture;
VertexRenderStruct musicVolumeBarButton;
bool isDraggingVolume = false;
float musicVolume = 0.0f;
float volumeBarMinX = 1190.0f;
float volumeBarMaxX = 1200.0f;
float volumeBarMinY = 100.0f;
float volumeBarMaxY = 600.0f;
float musicVolumeBarButtonButtonCenterX = 1195.0f;
float musicVolumeBarButtonButtonRadius = 25.0f;
void UpdateVolumeFromMouse(int mouseX, int mouseY);
void UpdateVolumeKnob();
static const size_t CONST_TIMER_INTERVAL = 10;
static const size_t CONST_MAX_TIME_INTERVAL = 1000;
std::shared_ptr<Texture> sparkTexture;
std::shared_ptr<Texture> spaceshipTexture;
std::shared_ptr<Texture> cubemapTexture;
VertexDataStruct spaceshipBase;
VertexRenderStruct spaceship;
VertexRenderStruct cubemap;
std::shared_ptr<Texture> boxTexture;
VertexDataStruct boxBase;
SparkEmitter sparkEmitter;
PlanetObject planetObject;
};
} // namespace ZL
-493
View File
@@ -1,493 +0,0 @@
#include "PlanetData.h"
#include <iostream>
#include <numeric>
#include <cmath>
#include <algorithm>
namespace ZL {
const float PlanetData::PLANET_RADIUS = 20000.f;
const Vector3f PlanetData::PLANET_CENTER_OFFSET = Vector3f{ 0.f, 0.f, 0.0f };
// --- Êîíñòàíòû äèàïàçîíîâ (ïåðåíåñåíû èç PlanetObject.cpp) ---
VertexID generateEdgeID(const VertexID& id1, const VertexID& id2) {
return id1 < id2 ? id1 + "_" + id2 : id2 + "_" + id1;
}
// Âñïîìîãàòåëüíàÿ ôóíêöèÿ äëÿ ïðîåêöèè (ëîêàëüíàÿ)
static Vector3f projectPointOnPlane(const Vector3f& P, const Vector3f& A, const Vector3f& B, const Vector3f& C) {
Vector3f AB = B + A * (-1.0f);
Vector3f AC = C + A * (-1.0f);
Vector3f N = AB.cross(AC).normalized();
Vector3f AP = P + A * (-1.0f);
float distance = N.dot(AP);
return P + N * (-distance);
}
PlanetData::PlanetData()
: perlin(77777)
, colorPerlin(123123)
, currentLod(0)
{
currentLod = planetMeshLods.size() - 1; // Start with max LOD
initialVertexMap = {
{{ 0.0f, 1.0f, 0.0f}, "A"},
{{ 0.0f, -1.0f, 0.0f}, "B"},
{{ 1.0f, 0.0f, 0.0f}, "C"},
{{-1.0f, 0.0f, 0.0f}, "D"},
{{ 0.0f, 0.0f, 1.0f}, "E"},
{{ 0.0f, 0.0f, -1.0f}, "F"}
};
}
void PlanetData::init() {
for (int i = 0; i < planetMeshLods.size(); i++) {
planetMeshLods[i] = generateSphere(i, 0.025f);
planetMeshLods[i].Scale(PLANET_RADIUS);
planetMeshLods[i].Move(PLANET_CENTER_OFFSET);
}
for (int i = 0; i < planetMeshLodsNoDist.size(); i++) {
planetMeshLodsNoDist[i] = generateSphere(i, 0);
planetMeshLodsNoDist[i].Scale(PLANET_RADIUS);
planetMeshLodsNoDist[i].Move(PLANET_CENTER_OFFSET);
}
planetAtmosphereLod = generateSphere(5, 0);
planetAtmosphereLod.Scale(PLANET_RADIUS * 1.03);
planetAtmosphereLod.Move(PLANET_CENTER_OFFSET);
}
const LodLevel& PlanetData::getLodLevel(int level) const {
return planetMeshLods.at(level);
}
const LodLevel& PlanetData::getLodLevelNoDist(int level) const {
return planetMeshLodsNoDist.at(level);
}
const LodLevel& PlanetData::getAtmosphereLod() const {
return planetAtmosphereLod;
}
int PlanetData::getCurrentLodIndex() const {
return currentLod;
}
int PlanetData::getMaxLodIndex() const {
return static_cast<int>(planetMeshLods.size() - 1);
}
std::pair<float, float> PlanetData::calculateZRange(float dToPlanetSurface) {
float currentZNear;
float currentZFar;
float alpha;
if (dToPlanetSurface > 2000)
{
currentZNear = 1000;
currentZFar = currentZNear * 100;
}
else if (dToPlanetSurface > 1200)
{
currentZNear = 500;
currentZFar = currentZNear * 100;
}
else if (dToPlanetSurface > 650)
{
currentZNear = 250;
currentZFar = currentZNear * 100;
}
else if (dToPlanetSurface > 160)
{
currentZNear = 125;
currentZFar = currentZNear * 150;
}
else if (dToPlanetSurface > 100)
{
currentZNear = 65;
currentZFar = currentZNear * 170;
}
else if (dToPlanetSurface > 40)
{
currentZNear = 32;
currentZFar = 10000.f;
}
else
{
currentZNear = 16;
currentZFar = 5000.f;
}
return { currentZNear, currentZFar };
}
float PlanetData::distanceToPlanetSurface(const Vector3f& viewerPosition) {
Vector3f shipLocalPosition = viewerPosition - PLANET_CENTER_OFFSET;
// Èñïîëüçóåì getTrianglesUnderCamera äëÿ ïîèñêà
// ÂÍÈÌÀÍÈÅ: Çäåñü ðåêóðñèÿ ëîãèêè. Ìåòîä getTrianglesUnderCamera èñïîëüçóåò âíóòðè viewerPosition.
// ×òîáû íå äóáëèðîâàòü êîä, ïåðåïèøåì ëîãèêó ïîèñêà çäåñü èëè áóäåì èñïîëüçîâàòü óæå íàéäåííûé ðàíåå.
// Äëÿ ïðîñòîòû îñòàâèì ëîãèêó êàê â îðèãèíàëå, íî àäàïòèðîâàííóþ.
// ÂÀÆÍÎ: getTrianglesUnderCamera - ýòî "òÿæåëàÿ" ôóíêöèÿ. Â îðèãèíàëå îíà âûçûâàëàñü âíóòðè distanceToPlanetSurface.
// Íî îíà òðåáóåò LOD. Èñïîëüçóåì MAX LOD äëÿ òî÷íîñòè.
// Ðåêóðñèâíûé ïîèñê òðåóãîëüíèêîâ ïîä êàìåðîé.
// Çäåñü íàì íóæíî ðåàëèçîâàòü àíàëîã triangleUnderCamera, íî íå çàâèñÿùèé îò ãëîáàëüíîãî ñîñòîÿíèÿ.
// Â îðèãèíàëå ôóíêöèÿ triangleUnderCamera áûëà ðåêóðñèâíîé.
// Íèæå ÿ ðåàëèçóþ èòåðàòèâíûé èëè ðåêóðñèâíûé ïîäõîä âíóòðè getTrianglesUnderCamera.
// Âðåìåííîå ðåøåíèå: ïîëíàÿ êîïèÿ ëîãèêè ïîèñêà òðåóãîëüíèêà
// Íî íàì íóæåí äîñòóï ê ìåòîäó.
std::vector<int> targetTriangles = getTrianglesUnderCamera(viewerPosition);
if (targetTriangles.empty()) {
return (shipLocalPosition.length() - PLANET_RADIUS);
}
float lowestDistance;
int tri_index = targetTriangles[0];
const auto& posData = planetMeshLods[currentLod].vertexData.PositionData;
size_t data_index = tri_index * 3;
if (data_index + 2 >= posData.size()) {
return (shipLocalPosition.length() - PLANET_RADIUS);
}
const Vector3f& A = posData[data_index];
const Vector3f& B = posData[data_index + 1];
const Vector3f& C = posData[data_index + 2];
Vector3f P_proj = projectPointOnPlane(shipLocalPosition, A, B, C);
Vector3f P_closest = P_proj;
lowestDistance = (shipLocalPosition - P_closest).length();
if (targetTriangles.size() <= 1)
{
return lowestDistance;
}
else
{
for (int i = 0; i < targetTriangles.size(); i++)
{
int tri_index = targetTriangles[i];
const auto& posData = planetMeshLods[currentLod].vertexData.PositionData;
size_t data_index = tri_index * 3;
if (data_index + 2 >= posData.size()) {
return (shipLocalPosition.length() - PLANET_RADIUS);
}
const Vector3f& A = posData[data_index];
const Vector3f& B = posData[data_index + 1];
const Vector3f& C = posData[data_index + 2];
Vector3f P_proj = projectPointOnPlane(shipLocalPosition, A, B, C);
Vector3f P_closest = P_proj;
if (lowestDistance < (shipLocalPosition - P_closest).length())
{
lowestDistance = (shipLocalPosition - P_closest).length();
}
}
return lowestDistance;
}
}
// --- Ðåàëèçàöèÿ getTrianglesUnderCamera (áûâøàÿ triangleUnderCamera) ---
// Âñïîìîãàòåëüíàÿ ðåêóðñèâíàÿ ôóíêöèÿ, ñêðûòàÿ îò ïóáëè÷íîãî API
static std::vector<int> recursiveTriangleSearch(int lod, const Vector3f& pos, const std::array<LodLevel, MAX_LOD_LEVELS>& meshes) {
std::vector<int> r;
// Ëîãèêà óðîâíÿ 0 (áàçîâûé îêòàýäð)
if (lod == 0) {
if (pos.v[1] >= 0) {
if (pos.v[0] >= 0 && pos.v[2] >= 0) r.push_back(0);
if (pos.v[0] >= 0 && pos.v[2] <= 0) r.push_back(1);
if (pos.v[0] <= 0 && pos.v[2] <= 0) r.push_back(2);
if (pos.v[0] <= 0 && pos.v[2] >= 0) r.push_back(3);
}
if (pos.v[1] <= 0) {
if (pos.v[0] >= 0 && pos.v[2] >= 0) r.push_back(4);
if (pos.v[0] <= 0 && pos.v[2] >= 0) r.push_back(5);
if (pos.v[0] <= 0 && pos.v[2] <= 0) r.push_back(6);
if (pos.v[0] >= 0 && pos.v[2] <= 0) r.push_back(7);
}
}
else {
// Ðåêóðñèâíûé øàã
std::vector<int> r0 = recursiveTriangleSearch(lod - 1, pos, meshes);
for (int tri0 : r0) {
Vector3f a = meshes[lod - 1].vertexData.PositionData[tri0 * 3];
Vector3f b = meshes[lod - 1].vertexData.PositionData[tri0 * 3 + 1];
Vector3f c = meshes[lod - 1].vertexData.PositionData[tri0 * 3 + 2];
std::vector<int> result = PlanetData::find_sub_triangle_spherical(a, b, c, pos);
for (int trix : result) {
r.push_back(tri0 * 4 + trix);
}
}
}
return r;
}
std::vector<int> PlanetData::getTrianglesUnderCamera(const Vector3f& viewerPosition) {
// Âûçûâàåì ðåêóðñèþ ñ òåêóùèì LOD
return recursiveTriangleSearch(currentLod, viewerPosition, planetMeshLods);
}
// --- Îñòàëüíûå ìåòîäû (check_spherical_side, generateSphere è ò.ä.) ---
// (Êîïèðóéòå ðåàëèçàöèþ èç ñòàðîãî PlanetObject.cpp,
// çàìåíÿÿ îáðàùåíèÿ ê Environment::shipPosition íà àðãóìåíòû, åñëè íóæíî,
// è èñïîëüçóÿ this->planetMeshLods)
float PlanetData::check_spherical_side(const Vector3f& V1, const Vector3f& V2, const Vector3f& P, const Vector3f& V3, float epsilon) {
Vector3f N_plane = V1.cross(V2);
float sign_P = P.dot(N_plane);
float sign_V3 = V3.dot(N_plane);
return sign_P * sign_V3;
}
bool PlanetData::is_inside_spherical_triangle(const Vector3f& P, const Vector3f& V1, const Vector3f& V2, const Vector3f& V3, float epsilon) {
if (check_spherical_side(V1, V2, P, V3, epsilon) < -epsilon) return false;
if (check_spherical_side(V2, V3, P, V1, epsilon) < -epsilon) return false;
if (check_spherical_side(V3, V1, P, V2, epsilon) < -epsilon) return false;
return true;
}
std::vector<int> PlanetData::find_sub_triangle_spherical(const Vector3f& a_orig, const Vector3f& b_orig, const Vector3f& c_orig, const Vector3f& px_orig) {
const float EPSILON = 1e-6f;
std::vector<int> result;
const Vector3f a = a_orig.normalized();
const Vector3f b = b_orig.normalized();
const Vector3f c = c_orig.normalized();
const Vector3f pxx_normalized = px_orig.normalized();
const Vector3f m_ab = ((a + b) * 0.5f).normalized();
const Vector3f m_bc = ((b + c) * 0.5f).normalized();
const Vector3f m_ac = ((a + c) * 0.5f).normalized();
if (is_inside_spherical_triangle(pxx_normalized, a, m_ab, m_ac, EPSILON)) result.push_back(0);
if (is_inside_spherical_triangle(pxx_normalized, m_ab, b, m_bc, EPSILON)) result.push_back(1);
if (is_inside_spherical_triangle(pxx_normalized, m_ac, m_bc, c, EPSILON)) result.push_back(2);
if (is_inside_spherical_triangle(pxx_normalized, m_ab, m_bc, m_ac, EPSILON)) result.push_back(3);
return result;
}
std::vector<Triangle> PlanetData::subdivideTriangles(const std::vector<Triangle>& input, float noiseCoeff) {
std::vector<Triangle> output;
for (const auto& t : input) {
// Âåðøèíû è èõ ID
const Vector3f& a = t.data[0];
const Vector3f& b = t.data[1];
const Vector3f& c = t.data[2];
const VertexID& id_a = t.ids[0];
const VertexID& id_b = t.ids[1];
const VertexID& id_c = t.ids[2];
// 1. Âû÷èñëÿåì ñåðåäèíû (êîîðäèíàòû)
Vector3f m_ab = ((a + b) * 0.5f).normalized();
Vector3f m_bc = ((b + c) * 0.5f).normalized();
Vector3f m_ac = ((a + c) * 0.5f).normalized();
Vector3f pm_ab = m_ab * perlin.getSurfaceHeight(m_ab, noiseCoeff);
Vector3f pm_bc = m_bc * perlin.getSurfaceHeight(m_bc, noiseCoeff);
Vector3f pm_ac = m_ac * perlin.getSurfaceHeight(m_ac, noiseCoeff);
// 2. Âû÷èñëÿåì ID íîâûõ âåðøèí
VertexID id_mab = generateEdgeID(id_a, id_b);
VertexID id_mbc = generateEdgeID(id_b, id_c);
VertexID id_mac = generateEdgeID(id_a, id_c);
// 3. Ôîðìèðóåì 4 íîâûõ òðåóãîëüíèêà
output.emplace_back(Triangle{ {a, pm_ab, pm_ac}, {id_a, id_mab, id_mac} }); // 0
output.emplace_back(Triangle{ {pm_ab, b, pm_bc}, {id_mab, id_b, id_mbc} }); // 1
output.emplace_back(Triangle{ {pm_ac, pm_bc, c}, {id_mac, id_mbc, id_c} }); // 2
output.emplace_back(Triangle{ {pm_ab, pm_bc, pm_ac}, {id_mab, id_mbc, id_mac} }); // 3
}
return output;
}
LodLevel PlanetData::trianglesToVertices(const std::vector<Triangle>& geometry) {
LodLevel result;
result.triangles = geometry;
size_t vertexCount = geometry.size() * 3;
result.vertexData.PositionData.reserve(vertexCount);
result.vertexData.NormalData.reserve(vertexCount);
result.vertexData.TexCoordData.reserve(vertexCount);
result.vertexData.TangentData.reserve(vertexCount); // Äîáàâëÿåì ðåçåðâ
result.vertexData.BinormalData.reserve(vertexCount);
result.VertexIDs.reserve(vertexCount);
const std::array<Vector2f, 3> triangleUVs = {
Vector2f(0.5f, 1.0f),
Vector2f(0.0f, 0.0f),
Vector2f(1.0f, 0.0f)
};
for (const auto& t : geometry) {
// --- Âû÷èñëÿåì ëîêàëüíûé áàçèñ òðåóãîëüíèêà (êàê â GetRotationForTriangle) ---
Vector3f vA = t.data[0];
Vector3f vB = t.data[1];
Vector3f vC = t.data[2];
Vector3f x_axis = (vC - vB).normalized(); // Íàïðàâëåíèå U
Vector3f edge1 = vB - vA;
Vector3f edge2 = vC - vA;
Vector3f z_axis = edge1.cross(edge2).normalized(); // Íîðìàëü ïëîñêîñòè
// Ïðîâåðêà íàïðàâëåíèÿ íîðìàëè íàðóæó (îò öåíòðà ïëàíåòû)
Vector3f centerToTri = (vA + vB + vC).normalized();
if (z_axis.dot(centerToTri) < 0) {
z_axis = z_axis * -1.0f;
}
Vector3f y_axis = z_axis.cross(x_axis).normalized(); // Íàïðàâëåíèå V
for (int i = 0; i < 3; ++i) {
result.vertexData.PositionData.push_back(t.data[i]);
result.vertexData.NormalData.push_back(z_axis); // Ïëîñêàÿ íîðìàëü ãðàíè äëÿ Parallax
result.vertexData.TexCoordData.push_back(triangleUVs[i]);
// Çàïèñûâàåì âû÷èñëåííûé áàçèñ â êàæäóþ âåðøèíó òðåóãîëüíèêà
result.vertexData.TangentData.push_back(x_axis);
result.vertexData.BinormalData.push_back(y_axis);
result.VertexIDs.push_back(t.ids[i]);
}
}
return result;
}
LodLevel PlanetData::generateSphere(int subdivisions, float noiseCoeff) {
// 1. Èñõîäíûé îêòàýäð è ïðèñâîåíèå ID
std::vector<Triangle> geometry = {
// Âåðõíÿÿ ïîëóñôåðà (Y > 0)
{{ 0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}}, // 0
{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, { 1.0f, 0.0f, 0.0f}}, // 1
{{ 0.0f, 1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // 2
{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}}, // 3
// Íèæíÿÿ ïîëóñôåðà (Y < 0)
{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, { 1.0f, 0.0f, 0.0f}}, // 4
{{ 0.0f, -1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // 5
{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}}, // 6
{{ 0.0f, -1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}} // 7
};
// Ïðèñâîåíèå ID èñõîäíûì âåðøèíàì
for (auto& t : geometry) {
for (int i = 0; i < 3; i++) {
// Èñïîëüçóåì map äëÿ ïîëó÷åíèÿ ID ïî ÷èñòûì êîîðäèíàòàì (íîðì. == ÷èñòûå)
t.ids[i] = initialVertexMap[t.data[i].normalized()];
}
}
// 3. Ðàçáèâàåì N ðàç (â subdivideTriangles ãåíåðèðóþòñÿ ID íîâûõ âåðøèí)
for (int i = 0; i < subdivisions; i++) {
geometry = subdivideTriangles(geometry, noiseCoeff);
}
// 4. Ãåíåðèðóåì PositionData, NormalData è VertexIDs
LodLevel lodLevel = trianglesToVertices(geometry);
// 5. Ñîçäàíèå V2T-Map íà îñíîâå VertexIDs (ÒÎÏÎËÎÃÈ×ÅÑÊÈÉ ÊËÞ×)
V2TMap v2tMap;
const auto& finalVertexIDs = lodLevel.VertexIDs;
size_t num_triangles = geometry.size();
for (size_t i = 0; i < num_triangles; ++i) {
for (int j = 0; j < 3; ++j) {
VertexID v_id = finalVertexIDs[i * 3 + j];
// Åñëè êëþ÷ óæå åñòü, ïðîñòî äîáàâëÿåì èíäåêñ
v2tMap[v_id].push_back((int)i);
}
}
lodLevel.v2tMap = v2tMap;
// 6. Ïðèìåíåíèå ôèíàëüíîãî øóìà (åñëè âû õîòåëè, ÷òîáû øóì áûë òîëüêî çäåñü)
// Çäåñü ìû äîëæíû áûëè áû ïðèìåíèòü øóì ê buffer.PositionData,
// íî â âàøåì èñõîäíîì êîäå øóì ïðèìåíÿëñÿ ðàíåå.
// Ïðåäïîëàãàåì, ÷òî øóì áóäåò ïðèìåíåí çäåñü (èíà÷å v2tMap íå áóäåò ñîîòâåòñòâîâàòü).
// ÂÎÑÑÒÀÍÎÂÈÌ ØÀÃ 2, íî äëÿ ôèíàëüíîé ãåîìåòðèè:
for (size_t i = 0; i < lodLevel.vertexData.PositionData.size(); i++) {
Vector3f dir = lodLevel.vertexData.PositionData[i].normalized();
lodLevel.vertexData.PositionData[i] = dir * perlin.getSurfaceHeight(dir, noiseCoeff);
}
// 7. Ãåíåðàöèÿ ColorData
lodLevel.vertexData.ColorData.reserve(geometry.size() * 3);
const Vector3f baseColor = { 0.498f, 0.416f, 0.0f };
const float colorFrequency = 5.0f;
const float colorAmplitude = 0.2f;
const Vector3f offsetR = { 0.1f, 0.2f, 0.3f };
const Vector3f offsetG = { 0.5f, 0.4f, 0.6f };
const Vector3f offsetB = { 0.9f, 0.8f, 0.7f };
for (size_t i = 0; i < geometry.size(); i++) {
for (int j = 0; j < 3; j++) {
// Èñïîëüçóåì íîðìàëèçîâàííûé âåêòîð èç PositionData (êîòîðûé ðàâåí NormalData)
Vector3f dir = lodLevel.vertexData.NormalData[i * 3 + j];
// Âû÷èñëåíèå öâåòîâîãî øóìà
float noiseR = colorPerlin.noise(
(dir.v[0] + offsetR.v[0]) * colorFrequency,
(dir.v[1] + offsetR.v[1]) * colorFrequency,
(dir.v[2] + offsetR.v[2]) * colorFrequency
);
// ... (àíàëîãè÷íî äëÿ noiseG è noiseB)
// Çäåñü ìû èñïîëüçóåì çàãëóøêè, òàê êàê íåò ïîëíîãî îïðåäåëåíèÿ PerlinNoise
float noiseG = 0.0f;
float noiseB = 0.0f;
Vector3f colorOffset = {
noiseR * colorAmplitude,
noiseG * colorAmplitude,
noiseB * colorAmplitude
};
Vector3f finalColor = baseColor + colorOffset;
lodLevel.vertexData.ColorData.push_back(finalColor);
}
}
return lodLevel;
}
// Ïðèìåð findNeighbors:
std::vector<int> PlanetData::findNeighbors(int index, int lod) {
const V2TMap& v2tMap = planetMeshLods[lod].v2tMap;
const auto& vertexIDs = planetMeshLods[lod].VertexIDs;
if ((index * 3 + 2) >= vertexIDs.size()) return {};
std::set<int> neighbors;
for (int i = 0; i < 3; ++i) {
VertexID v_id = vertexIDs[index * 3 + i];
auto it = v2tMap.find(v_id);
if (it != v2tMap.end()) {
for (int tri_index : it->second) {
if (tri_index != index) {
neighbors.insert(tri_index);
}
}
}
}
return std::vector<int>(neighbors.begin(), neighbors.end());
}
}
-117
View File
@@ -1,117 +0,0 @@
#pragma once
#include "ZLMath.h"
#include "Perlin.h"
#include "Renderer.h" // Äëÿ VertexDataStruct
#include <vector>
#include <array>
#include <string>
#include <map>
#include <set>
namespace ZL {
using VertexID = std::string;
using V2TMap = std::map<VertexID, std::vector<int>>;
VertexID generateEdgeID(const VertexID& id1, const VertexID& id2);
constexpr static int MAX_LOD_LEVELS = 6;
//constexpr static int MAX_LOD_LEVELS = 3;
struct Triangle
{
std::array<Vector3f, 3> data;
std::array<VertexID, 3> ids;
Triangle(Vector3f p1, Vector3f p2, Vector3f p3)
: data{ p1, p2, p3 }
{
}
Triangle(std::array<Vector3f, 3> idata, std::array<VertexID, 3> iids)
: data{ idata }
, ids{ iids }
{
}
};
struct LodLevel
{
std::vector<Triangle> triangles;
VertexDataStruct vertexData;
std::vector<VertexID> VertexIDs;
V2TMap v2tMap;
void Scale(float s)
{
vertexData.Scale(s);
for (auto& t : triangles) {
for (int i = 0; i < 3; i++) {
t.data[i] = t.data[i] * s;
}
}
}
void Move(Vector3f pos)
{
vertexData.Move(pos);
for (auto& t : triangles) {
for (int i = 0; i < 3; i++) {
t.data[i] = t.data[i] + pos;
}
}
}
};
class PlanetData {
public:
static const float PLANET_RADIUS;
static const Vector3f PLANET_CENTER_OFFSET;
private:
PerlinNoise perlin;
PerlinNoise colorPerlin;
std::array<LodLevel, MAX_LOD_LEVELS> planetMeshLods;
std::array<LodLevel, MAX_LOD_LEVELS> planetMeshLodsNoDist;
LodLevel planetAtmosphereLod;
int currentLod; // Ëîãè÷åñêèé òåêóùèé óðîâåíü äåòàëèçàöèè
std::map<Vector3f, VertexID> initialVertexMap;
// Âíóòðåííèå ìåòîäû ãåíåðàöèè
std::vector<Triangle> subdivideTriangles(const std::vector<Triangle>& inputTriangles, float noiseCoeff);
LodLevel trianglesToVertices(const std::vector<Triangle>& triangles);
LodLevel generateSphere(int subdivisions, float noiseCoeff);
// Âñïîìîãàòåëüíûå ìåòîäû ìàòåìàòèêè
static float check_spherical_side(const Vector3f& V1, const Vector3f& V2, const Vector3f& P, const Vector3f& V3, float epsilon = 1e-6f);
static bool is_inside_spherical_triangle(const Vector3f& P, const Vector3f& V1, const Vector3f& V2, const Vector3f& V3, float epsilon);
public:
PlanetData();
void init();
// Ìåòîäû äîñòóïà ê äàííûì (äëÿ ðåíäåðåðà)
const LodLevel& getLodLevel(int level) const;
const LodLevel& getLodLevelNoDist(int level) const;
const LodLevel& getAtmosphereLod() const;
int getCurrentLodIndex() const;
int getMaxLodIndex() const;
// Ëîãèêà
std::pair<float, float> calculateZRange(float distanceToSurface);
float distanceToPlanetSurface(const Vector3f& viewerPosition);
// Âîçâðàùàåò èíäåêñû òðåóãîëüíèêîâ, âèäèìûõ êàìåðîé
std::vector<int> getTrianglesUnderCamera(const Vector3f& viewerPosition);
std::vector<int> findNeighbors(int index, int lod);
static std::vector<int> find_sub_triangle_spherical(const Vector3f& a_orig, const Vector3f& b_orig, const Vector3f& c_orig, const Vector3f& px_orig);
};
} // namespace ZL
-570
View File
@@ -1,570 +0,0 @@
#include "PlanetObject.h"
#include <random>
#include <cmath>
#include "OpenGlExtensions.h"
#include "Environment.h"
#include "StoneObject.h"
namespace ZL {
Matrix3f GetRotationForTriangle(const Triangle& tri) {
// Для треугольника №0:
// tri.data[0] = {0, 20000, 0} (A)
// tri.data[1] = {0, 0, 20000} (B)
// tri.data[2] = {20000, 0, 0} (C)
Vector3f vA = tri.data[0];
Vector3f vB = tri.data[1];
Vector3f vC = tri.data[2];
// 1. Вычисляем ось X (горизонталь).
// Нам нужна грань BC: от (0, 0, 20000) до (20000, 0, 0)
Vector3f x_axis = (vC - vB).normalized();
// 2. Вычисляем нормаль (ось Z).
// Порядок cross product (AB x AC) определит "лицевую" сторону.
Vector3f edge1 = vB - vA;
Vector3f edge2 = vC - vA;
Vector3f z_axis = edge1.cross(edge2).normalized();
// 3. Вычисляем ось Y (вертикаль).
// В ортонормированном базисе Y всегда перпендикулярна Z и X.
Vector3f y_axis = z_axis.cross(x_axis).normalized();
// 4. Формируем прямую матрицу поворота (Rotation/World Matrix).
Matrix3f rot;
// Столбец 0: Ось X
rot.m[0] = x_axis.v[0];
rot.m[3] = x_axis.v[1];
rot.m[6] = x_axis.v[2];
// Столбец 1: Ось Y
rot.m[1] = y_axis.v[0];
rot.m[4] = y_axis.v[1];
rot.m[7] = y_axis.v[2];
// Столбец 2: Ось Z
rot.m[2] = z_axis.v[0];
rot.m[5] = z_axis.v[1];
rot.m[8] = z_axis.v[2];
return rot;
}
PlanetObject::PlanetObject()
{
}
void PlanetObject::init() {
// 1. Инициализируем данные (генерация мешей)
planetData.init();
// 2. Забираем данные для VBO
// Берем максимальный LOD для начальной отрисовки
int lodIndex = planetData.getMaxLodIndex();
planetRenderStruct.data = planetData.getLodLevel(lodIndex).vertexData;
planetRenderStruct.RefreshVBO();
planetRenderStructCut.data = planetData.getLodLevelNoDist(lodIndex).vertexData;
planetRenderStructCut.data.PositionData.resize(3);
planetRenderStructCut.RefreshVBO();
//sandTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/sand2.png", ""));
sandTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/sand2.png", ""));
stoneTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/rock.png", ""));
// Атмосфера
planetAtmosphereRenderStruct.data = planetData.getAtmosphereLod().vertexData;
planetAtmosphereRenderStruct.RefreshVBO();
planetStones = CreateStoneGroupData(778, planetData.getLodLevel(lodIndex));
planetStones.inflate({ 0/*,1,2,3,4,5,6,7*/ });
planetStonesToBakeRenderStruct.AssignFrom(planetStones.mesh);
planetStonesToBakeRenderStruct.RefreshVBO();
}
void PlanetObject::prepareDrawData() {
if (!drawDataDirty) return;
drawDataDirty = false;
}
void PlanetObject::update(float deltaTimeMs) {
// 1. Получаем базовые треугольники под камерой
auto lr = planetData.getTrianglesUnderCamera(Environment::shipPosition);
int currentLod = planetData.getCurrentLodIndex();
// Временный вектор для сбора новых индексов
std::vector<int> newIndices;
std::set<int> used;
// Рекурсивно (или итеративно, как у тебя) собираем индексы видимых зон
for (int i : lr) {
if (used.insert(i).second) newIndices.push_back(i);
auto neighbors = planetData.findNeighbors(i, currentLod);
for (int n : neighbors) {
if (used.insert(n).second) newIndices.push_back(n);
auto neighbors2 = planetData.findNeighbors(n, currentLod);
for (int n2 : neighbors2) {
if (used.insert(n2).second) newIndices.push_back(n2);
auto neighbors3 = planetData.findNeighbors(n, currentLod);
for (int n3 : neighbors3) {
if (used.insert(n3).second) newIndices.push_back(n3);
}
}
}
}
// 2. Сортируем новый список, чтобы порядок не влиял на сравнение
std::sort(newIndices.begin(), newIndices.end());
// 3. Сравниваем с тем, что было нарисовано в прошлый раз
if (newIndices != triangleIndicesToDraw) {
// Обновляем список индексов (используем move для эффективности)
triangleIndicesToDraw = std::move(newIndices);
// --- ОБНОВЛЯЕМ ЖЕЛТУЮ ЗОНУ (только когда изменился состав треугольников) ---
const auto& fullMesh = planetData.getLodLevel(currentLod).vertexData;
planetRenderYellowStruct.data.PositionData.clear();
planetRenderYellowStruct.data.TexCoordData.clear();
for (int i : triangleIndicesToDraw) {
// Копируем геометрию для подсветки
for (int j = 0; j < 3; ++j) {
planetRenderYellowStruct.data.PositionData.push_back(fullMesh.PositionData[i * 3 + j]);
planetRenderYellowStruct.data.TexCoordData.push_back(fullMesh.TexCoordData[i * 3 + j]);
}
}
if (planetRenderYellowStruct.data.PositionData.size() > 0)
{
planetRenderYellowStruct.RefreshVBO();
}
// --- ОБНОВЛЯЕМ КАМНИ (через новую структуру StoneGroup) ---
if (triangleIndicesToDraw.size() > 0)
{
planetStones.inflate(triangleIndicesToDraw);
// Используем AssignFrom, он внутри сам вызывает RefreshVBO
planetStonesRenderStruct.AssignFrom(planetStones.mesh);
}
}
}
void PlanetObject::bakeStoneTexture(Renderer& renderer) {
glViewport(0, 0, 512, 512);
glClearColor(224 / 255.f, 201 / 255.f, 167 / 255.f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
static const std::string defaultShaderName2 = "planetBake";
static const std::string vPositionName = "vPosition";
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
renderer.shaderManager.PushShader(defaultShaderName2);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vTexCoordName);
float dist = planetData.distanceToPlanetSurface(Environment::shipPosition);
auto zRange = planetData.calculateZRange(dist);
const float currentZNear = zRange.first;
const float currentZFar = zRange.second;
Triangle tr = planetData.getLodLevelNoDist(planetData.getCurrentLodIndex()).triangles[0];
// 1. Получаем матрицу вращения (оси в столбцах)
Matrix3f mr = GetRotationForTriangle(tr);
// 2. Трансформируем вершины в локальное пространство экрана, чтобы найти габариты
// Используем MultMatrixVector(Matrix, Vector).
// Если ваша функция считает V * M, то передайте Inverse(mr).
Vector3f rA = MultMatrixVector(mr, tr.data[0]);
Vector3f rB = MultMatrixVector(mr, tr.data[1]);
Vector3f rC = MultMatrixVector(mr, tr.data[2]);
// 3. Вычисляем реальные границы треугольника после поворота
float minX = min(rA.v[0], min(rB.v[0], rC.v[0]));
float maxX = max(rA.v[0], max(rB.v[0], rC.v[0]));
float minY = min(rA.v[1], min(rB.v[1], rC.v[1]));
float maxY = max(rA.v[1], max(rB.v[1], rC.v[1]));
// Находим центр и размеры
float width = maxX - minX;
float height = maxY - minY;
float centerX = (minX + maxX) * 0.5f;
float centerY = (minY + maxY) * 0.5f;
width = width * 0.995;
height = height * 0.995;
renderer.PushProjectionMatrix(
centerX - width*0.5, centerX + width * 0.5,
centerY - height * 0.5, centerY + height * 0.5,
150, 200000);
renderer.PushMatrix();
renderer.LoadIdentity();
// Сдвигаем камеру по Z
renderer.TranslateMatrix(Vector3f{ 0, 0, -45000 });
// Применяем вращение
renderer.RotateMatrix(mr);
// Извлекаем нормаль треугольника (это 3-й столбец нашей матрицы вращения)
Vector3f planeNormal = { mr.m[2], mr.m[5], mr.m[8] };
renderer.RenderUniform3fv("uPlanePoint", &tr.data[0].v[0]);
renderer.RenderUniform3fv("uPlaneNormal", &planeNormal.v[0]);
renderer.RenderUniform1f("uMaxHeight", StoneParams::BASE_SCALE * 1.1f); // Соответствует BASE_SCALE + perturbation
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID());
renderer.PushMatrix();
renderer.DrawVertexRenderStruct(planetRenderStructCut);
renderer.PopMatrix();
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK); // Отсекаем задние грани
if (planetStonesToBakeRenderStruct.data.PositionData.size() > 0)
{
glBindTexture(GL_TEXTURE_2D, stoneTexture->getTexID());
renderer.DrawVertexRenderStruct(planetStonesToBakeRenderStruct);
CheckGlError();
}
glDisable(GL_CULL_FACE); // Не забываем выключить, чтобы не сломать остальной рендер
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.DisableVertexAttribArray(vTexCoordName);
renderer.DisableVertexAttribArray(vPositionName);
renderer.shaderManager.PopShader();
CheckGlError();
}
void PlanetObject::draw(Renderer& renderer) {
prepareDrawData();
{
if (stoneMapFB == nullptr)
{
stoneMapFB = std::make_unique<FrameBuffer>(512, 512);
}
stoneMapFB->Bind();
bakeStoneTexture(renderer);
stoneMapFB->Unbind();
}
//bakeStoneTexture(renderer);
glViewport(0, 0, Environment::width, Environment::height);
//--------------------------
drawPlanet(renderer);
//drawYellowZone(renderer);
drawStones(renderer);
drawAtmosphere(renderer);
}
void PlanetObject::drawPlanet(Renderer& renderer)
{
static const std::string defaultShaderName = "planetLand";
static const std::string vPositionName = "vPosition";
static const std::string vColorName = "vColor";
static const std::string vNormalName = "vNormal";
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
renderer.shaderManager.PushShader(defaultShaderName);
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vColorName);
renderer.EnableVertexAttribArray(vNormalName);
renderer.EnableVertexAttribArray("vTangent");
renderer.EnableVertexAttribArray("vBinormal");
renderer.EnableVertexAttribArray(vTexCoordName);
float dist = planetData.distanceToPlanetSurface(Environment::shipPosition);
auto zRange = planetData.calculateZRange(dist);
const float currentZNear = zRange.first;
const float currentZFar = zRange.second;
// 2. Применяем динамическую матрицу проекции
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
currentZNear, currentZFar);
renderer.PushMatrix();
renderer.LoadIdentity();
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
renderer.RotateMatrix(Environment::inverseShipMatrix);
renderer.TranslateMatrix(-Environment::shipPosition);
const Matrix4f viewMatrix = renderer.GetCurrentModelViewMatrix();
renderer.RenderUniform1i("Texture", 0);
renderer.RenderUniform1i("BakedTexture", 1);
Triangle tr = planetData.getLodLevel(planetData.getCurrentLodIndex()).triangles[0]; // Берем базовый треугольник
Matrix3f mr = GetRotationForTriangle(tr); // Та же матрица, что и при запекании
// Позиция камеры (корабля) в мире
renderer.RenderUniform3fv("uViewPos", &Environment::shipPosition.v[0]);
//renderer.RenderUniform1f("uHeightScale", 0.03f);
renderer.RenderUniform1f("uHeightScale", 0.0f);
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, stoneMapFB->getTextureID());
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID());
renderer.DrawVertexRenderStruct(planetRenderStruct);
CheckGlError();
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.DisableVertexAttribArray(vTexCoordName);
renderer.DisableVertexAttribArray(vNormalName);
renderer.DisableVertexAttribArray("vTangent");
renderer.DisableVertexAttribArray("vBinormal");
renderer.DisableVertexAttribArray(vColorName);
renderer.DisableVertexAttribArray(vPositionName);
renderer.shaderManager.PopShader();
CheckGlError();
}
void PlanetObject::drawStones(Renderer& renderer)
{
static const std::string defaultShaderName2 = "planetStone";
static const std::string vPositionName = "vPosition";
static const std::string vColorName = "vColor";
static const std::string vNormalName = "vNormal";
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
renderer.shaderManager.PushShader(defaultShaderName2);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vColorName);
renderer.EnableVertexAttribArray(vNormalName);
renderer.EnableVertexAttribArray(vTexCoordName);
float dist = planetData.distanceToPlanetSurface(Environment::shipPosition);
auto zRange = planetData.calculateZRange(dist);
const float currentZNear = zRange.first;
const float currentZFar = zRange.second;
// 2. Применяем динамическую матрицу проекции
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
currentZNear, currentZFar);
renderer.PushMatrix();
renderer.LoadIdentity();
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
renderer.RotateMatrix(Environment::inverseShipMatrix);
renderer.TranslateMatrix(-Environment::shipPosition);
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
renderer.RenderUniform3fv("uViewPos", &Environment::shipPosition.v[0]);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (planetStonesRenderStruct.data.PositionData.size() > 0)
{
glBindTexture(GL_TEXTURE_2D, stoneTexture->getTexID());
renderer.DrawVertexRenderStruct(planetStonesRenderStruct);
CheckGlError();
}
glDisable(GL_BLEND);
glDisable(GL_CULL_FACE);
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.DisableVertexAttribArray(vTexCoordName);
renderer.DisableVertexAttribArray(vNormalName);
renderer.DisableVertexAttribArray(vColorName);
renderer.DisableVertexAttribArray(vPositionName);
renderer.shaderManager.PopShader();
CheckGlError();
glClear(GL_DEPTH_BUFFER_BIT);
}
void PlanetObject::drawYellowZone(Renderer& renderer)
{
static const std::string defaultShaderName = "planetLand";
static const std::string vPositionName = "vPosition";
static const std::string vColorName = "vColor";
static const std::string vNormalName = "vNormal";
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
float dist = planetData.distanceToPlanetSurface(Environment::shipPosition);
auto zRange = planetData.calculateZRange(dist);
const float currentZNear = zRange.first;
const float currentZFar = zRange.second;
glClear(GL_DEPTH_BUFFER_BIT);
if (planetRenderYellowStruct.data.PositionData.size() > 0)
{
renderer.shaderManager.PushShader(defaultShaderName);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vColorName);
renderer.EnableVertexAttribArray(vNormalName);
renderer.EnableVertexAttribArray("vTangent");
renderer.EnableVertexAttribArray("vBinormal");
renderer.EnableVertexAttribArray(vTexCoordName);
// 2. Применяем динамическую матрицу проекции
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
currentZNear, currentZFar);
renderer.PushMatrix();
renderer.LoadIdentity();
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
renderer.RotateMatrix(Environment::inverseShipMatrix);
renderer.TranslateMatrix(-Environment::shipPosition);
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
renderer.RenderUniform3fv("uViewPos", &Environment::shipPosition.v[0]);
Vector3f color2 = { 1.0, 1.0, 0.0 };
glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID());
renderer.RenderUniform3fv("uColor", &color2.v[0]);
renderer.DrawVertexRenderStruct(planetRenderYellowStruct);
//glDisable(GL_BLEND);
CheckGlError();
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.DisableVertexAttribArray(vTexCoordName);
renderer.DisableVertexAttribArray(vNormalName);
renderer.DisableVertexAttribArray("vTangent");
renderer.DisableVertexAttribArray("vBinormal");
renderer.DisableVertexAttribArray(vColorName);
renderer.DisableVertexAttribArray(vPositionName);
renderer.shaderManager.PopShader();
CheckGlError();
}
}
void PlanetObject::drawAtmosphere(Renderer& renderer) {
static const std::string defaultShaderName = "defaultAtmosphere";
static const std::string vPositionName = "vPosition";
static const std::string vNormalName = "vNormal";
//glClear(GL_DEPTH_BUFFER_BIT);
glDepthMask(GL_FALSE);
renderer.shaderManager.PushShader(defaultShaderName);
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vNormalName);
float dist = planetData.distanceToPlanetSurface(Environment::shipPosition);
auto zRange = planetData.calculateZRange(dist);
const float currentZNear = zRange.first;
const float currentZFar = zRange.second;
// 2. Применяем динамическую матрицу проекции
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
currentZNear, currentZFar);
renderer.PushMatrix();
renderer.LoadIdentity();
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
renderer.RotateMatrix(Environment::inverseShipMatrix);
renderer.TranslateMatrix(-Environment::shipPosition);
const Matrix4f viewMatrix = renderer.GetCurrentModelViewMatrix();
Vector3f color = { 0.0, 0.5, 1.0 };
renderer.RenderUniform3fv("uColor", &color.v[0]);
renderer.RenderUniformMatrix4fv("ModelViewMatrix", false, &viewMatrix.m[0]);
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения
renderer.DrawVertexRenderStruct(planetAtmosphereRenderStruct);
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.DisableVertexAttribArray(vNormalName);
renderer.DisableVertexAttribArray(vPositionName);
renderer.shaderManager.PopShader();
CheckGlError();
}
float PlanetObject::distanceToPlanetSurface(const Vector3f& viewerPosition)
{
return planetData.distanceToPlanetSurface(viewerPosition);
}
} // namespace ZL
-63
View File
@@ -1,63 +0,0 @@
#pragma once
#include "ZLMath.h"
#include "Renderer.h"
#include "TextureManager.h"
#include <vector>
#include <chrono>
#include <iostream>
#include <string>
#include <array>
#include <numeric>
#include <random>
#include <algorithm>
#include <map>
#include <set>
#include "Perlin.h"
#include "PlanetData.h"
#include "StoneObject.h"
#include "FrameBuffer.h"
namespace ZL {
class PlanetObject {
public:
// Агрегация: логика и данные теперь здесь
PlanetData planetData;
// Данные только для рендеринга (OpenGL specific)
VertexRenderStruct planetRenderStruct;
VertexRenderStruct planetRenderStructCut;
VertexRenderStruct planetRenderYellowStruct;
VertexRenderStruct planetAtmosphereRenderStruct;
VertexRenderStruct planetStonesRenderStruct;
VertexRenderStruct planetStonesToBakeRenderStruct;
StoneGroup planetStones;
std::vector<int> triangleIndicesToDraw;
std::shared_ptr<Texture> sandTexture;
std::shared_ptr<Texture> stoneTexture;
bool drawDataDirty = true;
void prepareDrawData();
std::unique_ptr<FrameBuffer> stoneMapFB;
public:
PlanetObject();
void init();
void update(float deltaTimeMs);
void bakeStoneTexture(Renderer& renderer);
void draw(Renderer& renderer);
void drawStones(Renderer& renderer);
void drawPlanet(Renderer& renderer);
void drawYellowZone(Renderer& renderer);
void drawAtmosphere(Renderer& renderer);
float distanceToPlanetSurface(const Vector3f& viewerPosition);
};
} // namespace ZL
+6
View File
@@ -154,6 +154,12 @@ emcc main.cpp Game.cpp Environment.cpp BoneAnimatedModel.cpp ZLMath.cpp Renderer
emrun --no_browser --port 8080 .
```
# Emscripten new
```
emcc src/main.cpp src/Game.cpp src/Environment.cpp src/BoneAnimatedModel.cpp src/TextModel.cpp src/Projectile.cpp src/SparkEmitter.cpp src/UiManager.cpp src/render/Renderer.cpp src/render/ShaderManager.cpp src/render/TextureManager.cpp src/render/FrameBuffer.cpp src/render/OpenGlExtensions.cpp src/utils/Utils.cpp src/utils/TaskManager.cpp src/utils/Perlin.cpp src/planet/PlanetData.cpp src/planet/PlanetObject.cpp src/planet/StoneObject.cpp -O2 -std=c++17 -pthread -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE=4 -sTOTAL_MEMORY=4294967296 -sINITIAL_MEMORY=3221225472 -sMAXIMUM_MEMORY=4294967296 -sALLOW_MEMORY_GROWTH=1 -fexceptions -I./thirdparty1/eigen-5.0.0 -I./src -I./thirdparty/libzip-1.11.3/build-emcmake/install/include -IC:/Boost/include/boost-1_84 -L./thirdparty/libzip-1.11.3/build-emcmake/install/lib -lzip -lz -sUSE_SDL_IMAGE=2 -sUSE_SDL=2 -sUSE_LIBPNG=1 --preload-file space-game001.zip -o space-game001.html
```
# License
Code: MIT
-215
View File
@@ -1,215 +0,0 @@
#include "ShaderManager.h"
#include <iostream>
namespace ZL {
ShaderResource::ShaderResource(const std::string& vertexCode, const std::string& fragmentCode)
{
const int CONST_INFOLOG_LENGTH = 256;
char infoLog[CONST_INFOLOG_LENGTH];
int infoLogLength;
char infoLog2[CONST_INFOLOG_LENGTH];
int infoLogLength2;
int vertexShaderCompiled;
int fragmentShaderCompiled;
int programLinked;
GLuint vertexShader;
GLuint fragmentShader;
int vertexCodeLength = static_cast<int>(strlen(vertexCode.c_str()));
int fragmentCodeLength = static_cast<int>(strlen(fragmentCode.c_str()));
const char* vc = &vertexCode[0];
const char* fc = &fragmentCode[0];
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &(vc), &vertexCodeLength);
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &(fc), &fragmentCodeLength);
glCompileShader(vertexShader);
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &vertexShaderCompiled);
glGetShaderInfoLog(vertexShader, CONST_INFOLOG_LENGTH, &infoLogLength, infoLog);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &fragmentShaderCompiled);
glGetShaderInfoLog(fragmentShader, CONST_INFOLOG_LENGTH, &infoLogLength2, infoLog2);
if (!vertexShaderCompiled)
{
throw std::runtime_error("Failed to compile vertex shader code!");
}
if (!fragmentShaderCompiled)
{
throw std::runtime_error("Failed to compile fragment shader code!");
}
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &programLinked);
glGetProgramInfoLog(shaderProgram, CONST_INFOLOG_LENGTH, &infoLogLength, infoLog);
if (!programLinked)
{
shaderProgram = 0;
throw std::runtime_error("Failed to link shader program!");
}
int dummySize; //Dummy
int dummyLen; //Dummy
GLenum dummyEnum;
//================= Parsing all uniforms ================
int activeUniforms;
const int CONST_UNIFORM_NAME_LENGTH = 256;
char uniformName[CONST_UNIFORM_NAME_LENGTH];
glGetProgramiv(shaderProgram, GL_ACTIVE_UNIFORMS, &activeUniforms);
for (int i = 0; i < activeUniforms; i++)
{
glGetActiveUniform(shaderProgram, i, CONST_UNIFORM_NAME_LENGTH, &dummyLen, &dummySize, &dummyEnum, uniformName);
uniformList[uniformName] = glGetUniformLocation(shaderProgram, uniformName);
}
//================= Parsing all attributes ================
int activeAttribs;
const int CONST_ATTRIB_NAME_LENGTH = 256;
char attribName[CONST_ATTRIB_NAME_LENGTH];
glGetProgramiv(shaderProgram, GL_ACTIVE_ATTRIBUTES, &activeAttribs);
for (int i = 0; i < activeAttribs; i++)
{
glGetActiveAttrib(shaderProgram, i, CONST_ATTRIB_NAME_LENGTH, &dummyLen, &dummySize, &dummyEnum, attribName);
attribList[attribName] = glGetAttribLocation(shaderProgram, attribName);
}
}
ShaderResource::~ShaderResource()
{
if (shaderProgram != 0)
{
glDeleteProgram(shaderProgram);
shaderProgram = 0;
}
}
GLuint ShaderResource::getShaderProgram()
{
return shaderProgram;
}
void ShaderManager::AddShaderFromFiles(const std::string& shaderName, const std::string& vertexShaderFileName, const std::string& fragmentShaderFileName, const std::string& ZIPFileName)
{
std::string vertexShader;
std::string fragmentShader;
if (!ZIPFileName.empty()){
std::vector<char> vertexShaderData;
std::vector<char> fragmentShaderData;
vertexShaderData = readFileFromZIP(vertexShaderFileName, ZIPFileName);
fragmentShaderData = readFileFromZIP(fragmentShaderFileName, ZIPFileName);
vertexShader = std::string(vertexShaderData.begin(), vertexShaderData.end());
fragmentShader = std::string(fragmentShaderData.begin(), fragmentShaderData.end());
}else{
vertexShader = readTextFile(vertexShaderFileName);
fragmentShader = readTextFile(fragmentShaderFileName);
}
///std::cout << "Shader: "<< vertexShader << std::endl;
shaderResourceMap[shaderName] = std::make_shared<ShaderResource>(vertexShader, fragmentShader);
}
void ShaderManager::PushShader(const std::string& shaderName)
{
if (shaderStack.size() >= CONST_MAX_SHADER_STACK_SIZE)
{
throw std::runtime_error("Shader stack overflow!");
}
if (shaderResourceMap.find(shaderName) == shaderResourceMap.end())
{
throw std::runtime_error("Shader does not exist!");
}
shaderStack.push(shaderName);
glUseProgram(shaderResourceMap[shaderName]->getShaderProgram());
}
void ShaderManager::PopShader()
{
if (shaderStack.size() == 0)
{
throw std::runtime_error("Shader stack underflow!");
}
shaderStack.pop();
if (shaderStack.size() == 0)
{
glUseProgram(0);
}
else
{
glUseProgram(shaderResourceMap[shaderStack.top()]->getShaderProgram());
}
}
std::shared_ptr<ShaderResource> ShaderManager::GetCurrentShader()
{
if (shaderStack.size() == 0)
{
throw std::runtime_error("Shader stack underflow!");
}
return shaderResourceMap[shaderStack.top()];
}
ShaderSetter::ShaderSetter(ShaderManager& inShaderManager, const std::string& shaderName)
: shaderManager(shaderManager)
{
shaderManager.PushShader(shaderName);
}
ShaderSetter::~ShaderSetter()
{
shaderManager.PopShader();
}
}
-299
View File
@@ -1,299 +0,0 @@
#include "SparkEmitter.h"
#include <random>
#include <cmath>
#include "OpenGlExtensions.h"
#include "Environment.h"
namespace ZL {
SparkEmitter::SparkEmitter()
: emissionRate(100.0f), isActive(true), drawDataDirty(true), maxParticles(200) {
particles.resize(maxParticles);
drawPositions.reserve(maxParticles * 6);
drawTexCoords.reserve(maxParticles * 6);
lastEmissionTime = std::chrono::steady_clock::now();
sparkQuad.data = VertexDataStruct();
}
SparkEmitter::SparkEmitter(const std::vector<Vector3f>& positions, float rate)
: emissionPoints(positions), emissionRate(rate), isActive(true),
drawDataDirty(true), maxParticles(positions.size() * 100) {
particles.resize(maxParticles);
drawPositions.reserve(maxParticles * 6);
drawTexCoords.reserve(maxParticles * 6);
lastEmissionTime = std::chrono::steady_clock::now();
sparkQuad.data = VertexDataStruct();
}
SparkEmitter::SparkEmitter(const std::vector<Vector3f>& positions,
std::shared_ptr<Texture> tex,
float rate)
: emissionPoints(positions), texture(tex), emissionRate(rate),
isActive(true), drawDataDirty(true), maxParticles(positions.size() * 100) {
particles.resize(maxParticles);
drawPositions.reserve(maxParticles * 6);
drawTexCoords.reserve(maxParticles * 6);
lastEmissionTime = std::chrono::steady_clock::now();
sparkQuad.data = VertexDataStruct();
}
void SparkEmitter::setTexture(std::shared_ptr<Texture> tex) {
texture = tex;
}
void SparkEmitter::prepareDrawData() {
if (!drawDataDirty) return;
drawPositions.clear();
drawTexCoords.clear();
if (getActiveParticleCount() == 0) {
drawDataDirty = false;
return;
}
std::vector<std::pair<const SparkParticle*, float>> sortedParticles;
sortedParticles.reserve(getActiveParticleCount());
for (const auto& particle : particles) {
if (particle.active) {
sortedParticles.push_back({ &particle, particle.position.v[2] });
}
}
std::sort(sortedParticles.begin(), sortedParticles.end(),
[](const auto& a, const auto& b) {
return a.second > b.second;
});
for (const auto& [particlePtr, depth] : sortedParticles) {
const auto& particle = *particlePtr;
Vector3f pos = particle.position;
float size = 0.04f * particle.scale;
drawPositions.push_back({ pos.v[0] - size, pos.v[1] - size, pos.v[2] });
drawTexCoords.push_back({ 0.0f, 0.0f });
drawPositions.push_back({ pos.v[0] - size, pos.v[1] + size, pos.v[2] });
drawTexCoords.push_back({ 0.0f, 1.0f });
drawPositions.push_back({ pos.v[0] + size, pos.v[1] + size, pos.v[2] });
drawTexCoords.push_back({ 1.0f, 1.0f });
drawPositions.push_back({ pos.v[0] - size, pos.v[1] - size, pos.v[2] });
drawTexCoords.push_back({ 0.0f, 0.0f });
drawPositions.push_back({ pos.v[0] + size, pos.v[1] + size, pos.v[2] });
drawTexCoords.push_back({ 1.0f, 1.0f });
drawPositions.push_back({ pos.v[0] + size, pos.v[1] - size, pos.v[2] });
drawTexCoords.push_back({ 1.0f, 0.0f });
}
drawDataDirty = false;
}
void SparkEmitter::draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight) {
if (getActiveParticleCount() == 0) {
return;
}
if (!texture) {
return;
}
prepareDrawData();
if (drawPositions.empty()) {
return;
}
sparkQuad.data.PositionData = drawPositions;
sparkQuad.data.TexCoordData = drawTexCoords;
sparkQuad.RefreshVBO();
static const std::string defaultShaderName = "default";
static const std::string vPositionName = "vPosition";
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
renderer.shaderManager.PushShader(defaultShaderName);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vTexCoordName);
float aspectRatio = static_cast<float>(screenWidth) / static_cast<float>(screenHeight);
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, aspectRatio, Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения
renderer.PushMatrix();
renderer.LoadIdentity();
renderer.TranslateMatrix({ 0, 0, -1.0f * zoom });
renderer.DrawVertexRenderStruct(sparkQuad);
renderer.PopMatrix();
renderer.PopProjectionMatrix();
glDisable(GL_BLEND);
renderer.DisableVertexAttribArray(vPositionName);
renderer.DisableVertexAttribArray(vTexCoordName);
renderer.shaderManager.PopShader();
}
void SparkEmitter::update(float deltaTimeMs) {
auto currentTime = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
currentTime - lastEmissionTime).count();
if (isActive && elapsed >= emissionRate) {
emit();
lastEmissionTime = currentTime;
drawDataDirty = true;
}
bool anyChanged = false;
for (auto& particle : particles) {
if (particle.active) {
Vector3f oldPosition = particle.position;
float oldScale = particle.scale;
particle.position.v[0] += particle.velocity.v[0] * deltaTimeMs / 1000.0f;
particle.position.v[1] += particle.velocity.v[1] * deltaTimeMs / 1000.0f;
particle.position.v[2] += particle.velocity.v[2] * deltaTimeMs / 1000.0f;
particle.lifeTime += deltaTimeMs;
if (particle.lifeTime >= particle.maxLifeTime) {
particle.active = false;
anyChanged = true;
}
else {
float lifeRatio = particle.lifeTime / particle.maxLifeTime;
particle.scale = 1.0f - lifeRatio * 0.8f;
if (oldPosition.v[0] != particle.position.v[0] ||
oldPosition.v[1] != particle.position.v[1] ||
oldPosition.v[2] != particle.position.v[2] ||
oldScale != particle.scale) {
anyChanged = true;
}
}
}
}
if (anyChanged) {
drawDataDirty = true;
}
}
void SparkEmitter::emit() {
if (emissionPoints.empty()) return;
bool emitted = false;
for (int i = 0; i < emissionPoints.size(); ++i) {
bool particleFound = false;
for (auto& particle : particles) {
if (!particle.active) {
initParticle(particle, i);
particle.active = true;
particle.lifeTime = 0;
particle.position = emissionPoints[i];
particle.emitterIndex = i;
particleFound = true;
emitted = true;
break;
}
}
if (!particleFound && !particles.empty()) {
size_t oldestIndex = 0;
float maxLifeTime = 0;
for (size_t j = 0; j < particles.size(); ++j) {
if (particles[j].lifeTime > maxLifeTime) {
maxLifeTime = particles[j].lifeTime;
oldestIndex = j;
}
}
initParticle(particles[oldestIndex], i);
particles[oldestIndex].active = true;
particles[oldestIndex].lifeTime = 0;
particles[oldestIndex].position = emissionPoints[i];
particles[oldestIndex].emitterIndex = i;
emitted = true;
}
}
if (emitted) {
drawDataDirty = true;
}
}
void SparkEmitter::setEmissionPoints(const std::vector<Vector3f>& positions) {
emissionPoints = positions;
maxParticles = positions.size() * 100;
particles.resize(maxParticles);
drawDataDirty = true;
}
void SparkEmitter::initParticle(SparkParticle& particle, int emitterIndex) {
particle.velocity = getRandomVelocity(emitterIndex);
particle.scale = 1.0f;
particle.maxLifeTime = 800.0f + (rand() % 400);
particle.emitterIndex = emitterIndex;
}
Vector3f SparkEmitter::getRandomVelocity(int emitterIndex) {
static std::random_device rd;
static std::mt19937 gen(rd());
static std::uniform_real_distribution<> angleDist(0, 2 * M_PI);
static std::uniform_real_distribution<> speedDist(0.5f, 2.0f);
static std::uniform_real_distribution<> zSpeedDist(1.0f, 3.0f);
float angle = angleDist(gen);
float speed = speedDist(gen);
float zSpeed = zSpeedDist(gen);
if (emitterIndex == 0) {
return Vector3f{
cosf(angle) * speed - 0.3f,
sinf(angle) * speed,
zSpeed
};
}
else {
return Vector3f{
cosf(angle) * speed + 0.3f,
sinf(angle) * speed,
zSpeed
};
}
}
const std::vector<SparkParticle>& SparkEmitter::getParticles() const {
return particles;
}
size_t SparkEmitter::getActiveParticleCount() const {
size_t count = 0;
for (const auto& particle : particles) {
if (particle.active) {
count++;
}
}
return count;
}
} // namespace ZL
-66
View File
@@ -1,66 +0,0 @@
#pragma once
#include "ZLMath.h"
#include "Renderer.h"
#include "TextureManager.h"
#include <vector>
#include <chrono>
namespace ZL {
struct SparkParticle {
Vector3f position;
Vector3f velocity;
float scale;
float lifeTime;
float maxLifeTime;
bool active;
int emitterIndex;
SparkParticle() : position({ 0,0,0 }), velocity({ 0,0,0 }), scale(1.0f),
lifeTime(0), maxLifeTime(1000.0f), active(false), emitterIndex(0) {
}
};
class SparkEmitter {
private:
std::vector<SparkParticle> particles;
std::vector<Vector3f> emissionPoints;
std::chrono::steady_clock::time_point lastEmissionTime;
float emissionRate;
bool isActive;
std::vector<Vector3f> drawPositions;
std::vector<Vector2f> drawTexCoords;
bool drawDataDirty;
VertexRenderStruct sparkQuad;
std::shared_ptr<Texture> texture;
int maxParticles;
void prepareDrawData();
public:
SparkEmitter();
SparkEmitter(const std::vector<Vector3f>& positions, float rate = 100.0f);
SparkEmitter(const std::vector<Vector3f>& positions,
std::shared_ptr<Texture> tex,
float rate = 100.0f);
void setEmissionPoints(const std::vector<Vector3f>& positions);
void setTexture(std::shared_ptr<Texture> tex);
void update(float deltaTimeMs);
void emit();
void draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight);
const std::vector<SparkParticle>& getParticles() const;
size_t getActiveParticleCount() const;
private:
void initParticle(SparkParticle& particle, int emitterIndex);
Vector3f getRandomVelocity(int emitterIndex);
};
} // namespace ZL
-276
View File
@@ -1,276 +0,0 @@
#include "StoneObject.h"
#include "Utils.h"
#include <GL/gl.h>
#include <random>
#include <cmath>
#include "Renderer.h"
#include "PlanetData.h"
namespace ZL {
// --- ÊÎÍÑÒÀÍÒÛ ÏÀÐÀÌÅÒÐÎÂ (êàê âû ïðîñèëè) ---
const float StoneParams::BASE_SCALE = 17.0f; // Îáùèé ðàçìåð êàìíÿ
//const float StoneParams::BASE_SCALE = 5000.0f; // Îáùèé ðàçìåð êàìíÿ
//const float StoneParams::MIN_AXIS_SCALE = 1.0f; // Ìèíèìàëüíîå ðàñòÿæåíèå/ñæàòèå ïî îñè
//const float StoneParams::MAX_AXIS_SCALE = 1.0f; // Ìàêñèìàëüíîå ðàñòÿæåíèå/ñæàòèå ïî îñè
//const float StoneParams::MIN_PERTURBATION = 0.0f; // Ìèíèìàëüíîå ðàäèàëüíîå âîçìóùåíèå âåðøèíû
//const float StoneParams::MAX_PERTURBATION = 0.0f; // Ìàêñèìàëüíîå ðàäèàëüíîå âîçìóùåíèå âåðøèíû
const float StoneParams::MIN_AXIS_SCALE = 0.5f; // Ìèíèìàëüíîå ðàñòÿæåíèå/ñæàòèå ïî îñè
const float StoneParams::MAX_AXIS_SCALE = 1.5f; // Ìàêñèìàëüíîå ðàñòÿæåíèå/ñæàòèå ïî îñè
const float StoneParams::MIN_PERTURBATION = 0.05f; // Ìèíèìàëüíîå ðàäèàëüíîå âîçìóùåíèå âåðøèíû
const float StoneParams::MAX_PERTURBATION = 0.25f; // Ìàêñèìàëüíîå ðàäèàëüíîå âîçìóùåíèå âåðøèíû
const int StoneParams::STONES_PER_TRIANGLE = 100;
// Âñïîìîãàòåëüíàÿ ôóíêöèÿ äëÿ ïîëó÷åíèÿ ñëó÷àéíîãî ÷èñëà â äèàïàçîíå [min, max]
float getRandomFloat(std::mt19937& gen, float min, float max) {
std::uniform_real_distribution<> distrib(min, max);
return static_cast<float>(distrib(gen));
}
// Âñïîìîãàòåëüíàÿ ôóíêöèÿ äëÿ ãåíåðàöèè ñëó÷àéíîé òî÷êè íà òðåóãîëüíèêå
// Èñïîëüçóåò áàðèöåíòðè÷åñêèå êîîðäèíàòû
Vector3f GetRandomPointOnTriangle(const Triangle& t, std::mt19937& engine) {
std::uniform_real_distribution<> distrib(0.0f, 1.0f);
float r1 = getRandomFloat(engine, 0.0f, 1.0f);
float r2 = getRandomFloat(engine, 0.0f, 1.0f);
// Ïðåîáðàçîâàíèå r1, r2 äëÿ ïîëó÷åíèÿ ðàâíîìåðíîãî ðàñïðåäåëåíèÿ
float a = 1.0f - std::sqrt(r1);
float b = std::sqrt(r1) * r2;
float c = 1.0f - a - b; // c = sqrt(r1) * (1 - r2)
// Áàðèöåíòðè÷åñêèå êîîðäèíàòû
// P = a*p1 + b*p2 + c*p3
Vector3f p1_term = t.data[0] * a;
Vector3f p2_term = t.data[1] * b;
Vector3f p3_term = t.data[2] * c;
return p1_term + p2_term + p3_term;
}
// Èêîñàýäð (íà îñíîâå çîëîòîãî ñå÷åíèÿ phi)
// Êîîðäèíàòû ìîãóò áûòü âû÷èñëåíû çàðàíåå äëÿ êîíñòàíòíîãî èêîñàýäðà.
// Çäåñü òîëüêî îáúÿâëåíèå, ÷òîáû ïîêàçàòü èäåþ.
VertexDataStruct CreateBaseConvexPolyhedron(uint64_t seed) {
// const size_t SUBDIVISION_LEVEL = 1; // Óðîâåíü ïîäðàçäåëåíèÿ (äëÿ áîëåå êðóãëîãî êàìíÿ, ïîêà îïóñòèì)
std::mt19937 engine(static_cast<unsigned int>(seed));
// Çîëîòîå ñå÷åíèå
const float t = (1.0f + std::sqrt(5.0f)) / 2.0f;
// 12 âåðøèí èêîñàýäðà
std::vector<Vector3f> initialVertices = {
{ -1, t, 0 }, { 1, t, 0 }, { -1, -t, 0 }, { 1, -t, 0 },
{ 0, -1, t }, { 0, 1, t }, { 0, -1, -t }, { 0, 1, -t },
{ t, 0, -1 }, { t, 0, 1 }, { -t, 0, -1 }, { -t, 0, 1 }
};
// 20 òðåóãîëüíûõ ãðàíåé (èíäåêñû âåðøèí)
std::vector<std::array<int, 3>> faces = {
// 5 òðåóãîëüíèêîâ âîêðóã âåðøèíû 0
{0, 11, 5}, {0, 5, 1}, {0, 1, 7}, {0, 7, 10}, {0, 10, 11},
// 5 ñìåæíûõ ïîëîñ
{1, 5, 9}, {5, 11, 4}, {11, 10, 2}, {10, 7, 6}, {7, 1, 8},
// 5 òðåóãîëüíèêîâ âîêðóã âåðøèíû 3
{3, 9, 4}, {3, 4, 2}, {3, 2, 6}, {3, 6, 8}, {3, 8, 9},
// 5 ñìåæíûõ ïîëîñ
{4, 9, 5}, {2, 4, 11}, {6, 2, 10}, {8, 6, 7}, {9, 8, 1}
};
// 1. Íîðìàëèçàöèÿ è Âîçìóùåíèå (Perturbation)
for (Vector3f& v : initialVertices) {
v = v.normalized() * StoneParams::BASE_SCALE; // Íîðìàëèçàöèÿ ê ñôåðå ðàäèóñà BASE_SCALE
// Ðàäèàëüíîå âîçìóùåíèå:
float perturbation = getRandomFloat(engine, StoneParams::MIN_PERTURBATION, StoneParams::MAX_PERTURBATION);
v = v * (1.0f + perturbation);
}
// 2. Òðàíñôîðìàöèÿ (Ìàñøòàáèðîâàíèå è Ïîâîðîò)
// Ñëó÷àéíûå ìàñøòàáû ïî îñÿì
Vector3f scaleFactors = {
getRandomFloat(engine, StoneParams::MIN_AXIS_SCALE, StoneParams::MAX_AXIS_SCALE),
getRandomFloat(engine, StoneParams::MIN_AXIS_SCALE, StoneParams::MAX_AXIS_SCALE),
getRandomFloat(engine, StoneParams::MIN_AXIS_SCALE, StoneParams::MAX_AXIS_SCALE)
};
// Ïðèìåíÿåì ìàñøòàáèðîâàíèå
for (Vector3f& v : initialVertices) {
v.v[0] *= scaleFactors.v[0];
v.v[1] *= scaleFactors.v[1];
v.v[2] *= scaleFactors.v[2];
}
// Ñëó÷àéíûé ïîâîðîò (íàïðèìåð, âîêðóã òðåõ îñåé)
Vector4f qx = QuatFromRotateAroundX(getRandomFloat(engine, 0.0f, 360.0f));
Vector4f qy = QuatFromRotateAroundY(getRandomFloat(engine, 0.0f, 360.0f));
Vector4f qz = QuatFromRotateAroundZ(getRandomFloat(engine, 0.0f, 360.0f));
Vector4f qFinal = slerp(qx, qy, 0.5f); // Ïðîñòîé ïðèìåð êîìáèíèðîâàíèÿ
qFinal = slerp(qFinal, qz, 0.5f).normalized();
Matrix3f rotationMatrix = QuatToMatrix(qFinal);
for (Vector3f& v : initialVertices) {
v = MultMatrixVector(rotationMatrix, v);
}
// 3. Ñãëàæåííûå Íîðìàëè è Ôîðìèðîâàíèå Mesh
VertexDataStruct result;
// Êàðòà äëÿ íàêîïëåíèÿ íîðìàëåé ïî óíèêàëüíûì ïîçèöèÿì âåðøèí
// (Òðåáóåò îïðåäåëåííîãî îïåðàòîðà < äëÿ Vector3f â ZLMath.h, êîòîðûé ó âàñ åñòü)
std::map<Vector3f, Vector3f> smoothNormalsMap;
// Ïðåäâàðèòåëüíîå çàïîëíåíèå êàðòû íîðìàëÿìè
for (const auto& face : faces) {
Vector3f p1 = initialVertices[face[0]];
Vector3f p2 = initialVertices[face[1]];
Vector3f p3 = initialVertices[face[2]];
// Íîðìàëü ãðàíè: (p2 - p1) x (p3 - p1)
Vector3f faceNormal = (p2 - p1).cross(p3 - p1).normalized();
smoothNormalsMap[p1] = smoothNormalsMap[p1] + faceNormal;
smoothNormalsMap[p2] = smoothNormalsMap[p2] + faceNormal;
smoothNormalsMap[p3] = smoothNormalsMap[p3] + faceNormal;
}
// Íîðìàëèçàöèÿ íàêîïëåííûõ íîðìàëåé
for (auto& pair : smoothNormalsMap) {
pair.second = pair.second.normalized();
}
// 4. Ôèíàëüíîå çàïîëíåíèå VertexDataStruct è Òåêñòóðíûå Êîîðäèíàòû
for (const auto& face : faces) {
Vector3f p1 = initialVertices[face[0]];
Vector3f p2 = initialVertices[face[1]];
Vector3f p3 = initialVertices[face[2]];
// Ïîçèöèè
result.PositionData.push_back(p1);
result.PositionData.push_back(p2);
result.PositionData.push_back(p3);
// Ñãëàæåííûå Íîðìàëè (èç êàðòû)
result.NormalData.push_back(smoothNormalsMap[p1]);
result.NormalData.push_back(smoothNormalsMap[p2]);
result.NormalData.push_back(smoothNormalsMap[p3]);
// Òåêñòóðíûå Êîîðäèíàòû (Ïëàíàðíàÿ ïðîåêöèÿ íà ïëîñêîñòü ãðàíè)
// p1 -> (0, 0), p2 -> (L_12, 0), p3 -> (L_13 * cos(angle), L_13 * sin(angle))
// Ãäå L_xy - äëèíà âåêòîðà, angle - óãîë ìåæäó p2-p1 è p3-p1
Vector3f uAxis = (p2 - p1).normalized();
Vector3f vRaw = p3 - p1;
// Ïðîåêöèÿ vRaw íà uAxis
float uProjLen = vRaw.dot(uAxis);
// Âåêòîð V ïåðïåíäèêóëÿðíûé U: vRaw - uProj
Vector3f vAxisRaw = vRaw - (uAxis * uProjLen);
// Äëèíà îñè V
float vLen = vAxisRaw.length();
// Íîðìàëèçîâàííàÿ îñü V
Vector3f vAxis = vAxisRaw.normalized();
// Êîîðäèíàòû (u, v) äëÿ p1, p2, p3 îòíîñèòåëüíî p1
Vector2f uv1 = { 0.0f, 0.0f };
Vector2f uv2 = { (p2 - p1).length(), 0.0f }; // p2-p1 âäîëü îñè U
Vector2f uv3 = { uProjLen, vLen }; // p3-p1: u-êîìïîíåíòà = uProjLen, v-êîìïîíåíòà = vLen
// Íàõîäèì ìàêñèìàëüíûé ðàçìåð, ÷òîáû ìàñøòàáèðîâàòü â [0, 1]
float maxUV = max(uv2.v[0], max(uv3.v[0], uv3.v[1]));
if (maxUV > 0.000001f) {
// Ìàñøòàáèðóåì:
result.TexCoordData.push_back(uv1);
result.TexCoordData.push_back(uv2 * (1.f / maxUV));
result.TexCoordData.push_back(uv3 * (1.f / maxUV));
}
else {
// Ïðåäîòâðàùåíèå äåëåíèÿ íà íîëü äëÿ âûðîæäåííûõ ãðàíåé
result.TexCoordData.push_back({ 0.0f, 0.0f });
result.TexCoordData.push_back({ 0.0f, 0.0f });
result.TexCoordData.push_back({ 0.0f, 0.0f });
}
}
return result;
}
StoneGroup CreateStoneGroupData(uint64_t globalSeed, const LodLevel& planetLodLevel) {
StoneGroup group;
// Ðåçåðâèðóåì ìåñòî ïîä âñå òðåóãîëüíèêè òåêóùåãî LOD
group.allInstances.resize(planetLodLevel.triangles.size());
for (size_t tIdx = 0; tIdx < planetLodLevel.triangles.size(); ++tIdx) {
const Triangle& tri = planetLodLevel.triangles[tIdx];
std::mt19937 engine(static_cast<unsigned int>(globalSeed));
for (int i = 0; i < StoneParams::STONES_PER_TRIANGLE; ++i) {
StoneInstance instance;
instance.seed = globalSeed;// + tIdx * 1000 + i; // Óíèêàëüíûé ñèä äëÿ êàæäîãî êàìíÿ
instance.position = GetRandomPointOnTriangle(tri, engine);
//instance.position = Vector3f(5000.0f, 5000.0f, 5000.0f);
// Ãåíåðèðóåì ñëó÷àéíûå ïàðàìåòðû îäèí ðàç
instance.scale = {
getRandomFloat(engine, 0.5f, 1.5f),
getRandomFloat(engine, 0.5f, 1.5f),
getRandomFloat(engine, 0.5f, 1.5f)
};
Vector4f qx = QuatFromRotateAroundX(getRandomFloat(engine, 0.0f, 360.0f));
Vector4f qy = QuatFromRotateAroundY(getRandomFloat(engine, 0.0f, 360.0f));
Vector4f qz = QuatFromRotateAroundZ(getRandomFloat(engine, 0.0f, 360.0f));
instance.rotation = slerp(slerp(qx, qy, 0.5f), qz, 0.5f).normalized();
group.allInstances[tIdx].push_back(instance);
}
}
return group;
}
void StoneGroup::inflate(const std::vector<int>& triangleIndices) {
// 1. Î÷èùàåì òåêóùèé ìåø ïåðåä çàïîëíåíèåì
mesh.PositionData.clear();
mesh.NormalData.clear();
mesh.TexCoordData.clear();
static VertexDataStruct baseStone = CreateBaseConvexPolyhedron(1337);
// 2. Íàïîëíÿåì ìåø òîëüêî äëÿ âèäèìûõ òðåóãîëüíèêîâ
for (int tIdx : triangleIndices) {
if (tIdx >= allInstances.size()) continue;
for (const auto& inst : allInstances[tIdx]) {
Matrix3f rotMat = QuatToMatrix(inst.rotation);
for (size_t j = 0; j < baseStone.PositionData.size(); ++j) {
Vector3f p = baseStone.PositionData[j];
Vector3f n = baseStone.NormalData[j];
// Ìàñøòàá -> Ïîâîðîò -> Ñìåùåíèå
p.v[0] *= inst.scale.v[0];
p.v[1] *= inst.scale.v[1];
p.v[2] *= inst.scale.v[2];
mesh.PositionData.push_back(MultMatrixVector(rotMat, p) + inst.position);
mesh.NormalData.push_back(MultMatrixVector(rotMat, n));
mesh.TexCoordData.push_back(baseStone.TexCoordData[j]);
}
}
}
}
} // namespace ZL
-41
View File
@@ -1,41 +0,0 @@
#pragma once
#include "ZLMath.h"
#include "Renderer.h"
#include "PlanetData.h"
namespace ZL {
struct StoneParams
{
static const float BASE_SCALE; // Îáùèé ðàçìåð êàìíÿ
static const float MIN_AXIS_SCALE; // Ìèíèìàëüíîå ðàñòÿæåíèå/ñæàòèå ïî îñè
static const float MAX_AXIS_SCALE; // Ìàêñèìàëüíîå ðàñòÿæåíèå/ñæàòèå ïî îñè
static const float MIN_PERTURBATION; // Ìèíèìàëüíîå ðàäèàëüíîå âîçìóùåíèå âåðøèíû
static const float MAX_PERTURBATION; // Ìàêñèìàëüíîå ðàäèàëüíîå âîçìóùåíèå âåðøèíû
static const int STONES_PER_TRIANGLE;
};
struct StoneInstance {
uint64_t seed;
Vector3f position;
Vector3f scale;
Vector4f rotation;
};
struct StoneGroup {
// mesh.PositionData è ïðî÷èå áóäóò çàïîëíÿòüñÿ â inflate()
VertexDataStruct mesh;
// Âíåøíèé âåêòîð — èíäåêñ òðåóãîëüíèêà ïëàíåòû,
// âíóòðåííèé — ñïèñîê êàìíåé íà ýòîì òðåóãîëüíèêå
std::vector<std::vector<StoneInstance>> allInstances;
// Î÷èùàåò ñòàðóþ ãåîìåòðèþ è ãåíåðèðóåò íîâóþ äëÿ óêàçàííûõ èíäåêñîâ
void inflate(const std::vector<int>& triangleIndices);
};
// Òåïåðü âîçâðàùàåò çàãîòîâêó ñî âñåìè ïàðàìåòðàìè, íî áåç òÿæåëîãî ìåøà
StoneGroup CreateStoneGroupData(uint64_t globalSeed, const LodLevel& planetLodLevel);
} // namespace ZL
-105
View File
@@ -1,105 +0,0 @@
#include "Utils.h"
#include <cstring>
#include <iterator>
#include <vector>
#include <iostream>
#include <algorithm>
#include <fstream>
#ifdef EMSCRIPTEN
#include <zip.h>
#endif
namespace ZL
{
std::string readTextFile(const std::string& filename)
{
std::ifstream f(filename);
std::string str((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
return str;
}
std::vector<char> readFile(const std::string& filename)
{
std::ifstream file(filename, std::ios::binary);
file.unsetf(std::ios::skipws);
std::streampos fileSize;
file.seekg(0, std::ios::end);
fileSize = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<char> vec;
vec.reserve(fileSize);
vec.insert(vec.begin(),
std::istream_iterator<char>(file),
std::istream_iterator<char>());
return vec;
}
std::vector<char> readFileFromZIP(const std::string& filename, const std::string& zipfilename) {
#ifdef EMSCRIPTEN
const std::string zipPath = zipfilename;
int zipErr;
zip_t* archive = zip_open(zipPath.c_str(), ZIP_RDONLY, &zipErr);
if (!archive) {
throw std::runtime_error("Ошибка открытия ZIP: " + zipPath);
}
std::string cleanFilename = filename;
if (cleanFilename.rfind("./", 0) == 0) {
cleanFilename = cleanFilename.substr(2);
}
std::cout << "Ищем в ZIP: " << cleanFilename << std::endl;
zip_file_t* zipFile = zip_fopen(archive, cleanFilename.c_str(), 0);
if (!zipFile) {
zip_close(archive);
throw std::runtime_error("Файл не найден в ZIP: " + cleanFilename);
}
zip_stat_t fileStat;
if (zip_stat(archive, cleanFilename.c_str(), 0, &fileStat) != 0) {
zip_fclose(zipFile);
zip_close(archive);
throw std::runtime_error("Ошибка чтения ZIP-статистики.");
}
std::vector<char> fileData;
fileData.resize(fileStat.size);
zip_fread(zipFile, fileData.data(), fileData.size());
zip_fclose(zipFile);
zip_close(archive);
return fileData;
#else
return {};
#endif
}
bool findString(const char* in, char* list)
{
size_t thisLength = strlen(in);
while (*list != 0)
{
size_t length = strcspn(list, " ");
if (thisLength == length)
if (!strncmp(in, list, length))
return true;
list += length;
list += 1;
}
return false;
}
};
-799
View File
@@ -1,799 +0,0 @@
#include "ZLMath.h"
#include <exception>
#include <cmath>
namespace ZL {
Vector2f operator+(const Vector2f& x, const Vector2f& y)
{
Vector2f result;
result.v[0] = x.v[0] + y.v[0];
result.v[1] = x.v[1] + y.v[1];
return result;
}
Vector2f operator-(const Vector2f& x, const Vector2f& y)
{
Vector2f result;
result.v[0] = x.v[0] - y.v[0];
result.v[1] = x.v[1] - y.v[1];
return result;
}
Vector3f operator+(const Vector3f& x, const Vector3f& y)
{
Vector3f result;
result.v[0] = x.v[0] + y.v[0];
result.v[1] = x.v[1] + y.v[1];
result.v[2] = x.v[2] + y.v[2];
return result;
}
Vector3f operator-(const Vector3f& x, const Vector3f& y)
{
Vector3f result;
result.v[0] = x.v[0] - y.v[0];
result.v[1] = x.v[1] - y.v[1];
result.v[2] = x.v[2] - y.v[2];
return result;
}
Vector3f operator-(const Vector3f& x)
{
Vector3f result;
result.v[0] = -x.v[0];
result.v[1] = -x.v[1];
result.v[2] = -x.v[2];
return result;
}
Vector4f operator+(const Vector4f& x, const Vector4f& y)
{
Vector4f result;
result.v[0] = x.v[0] + y.v[0];
result.v[1] = x.v[1] + y.v[1];
result.v[2] = x.v[2] + y.v[2];
result.v[3] = x.v[3] + y.v[3];
return result;
}
Vector4f operator-(const Vector4f& x, const Vector4f& y)
{
Vector4f result;
result.v[0] = x.v[0] - y.v[0];
result.v[1] = x.v[1] - y.v[1];
result.v[2] = x.v[2] - y.v[2];
result.v[3] = x.v[3] - y.v[3];
return result;
}
Matrix3f Matrix3f::Identity()
{
Matrix3f r;
r.m[0] = 1.f;
r.m[1] = 0.f;
r.m[2] = 0.f;
r.m[3] = 0.f;
r.m[4] = 1.f;
r.m[5] = 0.f;
r.m[6] = 0.f;
r.m[7] = 0.f;
r.m[8] = 1.f;
return r;
}
Matrix4f Matrix4f::Identity()
{
Matrix4f r;
r.m[0] = 1.f;
r.m[1] = 0.f;
r.m[2] = 0.f;
r.m[3] = 0.f;
r.m[4] = 0.f;
r.m[5] = 1.f;
r.m[6] = 0.f;
r.m[7] = 0.f;
r.m[8] = 0.f;
r.m[9] = 0.f;
r.m[10] = 1.f;
r.m[11] = 0.f;
r.m[12] = 0.f;
r.m[13] = 0.f;
r.m[14] = 0.f;
r.m[15] = 1.f;
return r;
}
Matrix4f operator*(const Matrix4f& m1, const Matrix4f& m2)
{
Matrix4f r;
r.m[0] = m1.m[0] * m2.m[0] + m1.m[4] * m2.m[1] + m1.m[8] * m2.m[2] + m1.m[12] * m2.m[3];
r.m[1] = m1.m[1] * m2.m[0] + m1.m[5] * m2.m[1] + m1.m[9] * m2.m[2] + m1.m[13] * m2.m[3];
r.m[2] = m1.m[2] * m2.m[0] + m1.m[6] * m2.m[1] + m1.m[10] * m2.m[2] + m1.m[14] * m2.m[3];
r.m[3] = m1.m[3] * m2.m[0] + m1.m[7] * m2.m[1] + m1.m[11] * m2.m[2] + m1.m[15] * m2.m[3];
r.m[4] = m1.m[0] * m2.m[4] + m1.m[4] * m2.m[5] + m1.m[8] * m2.m[6] + m1.m[12] * m2.m[7];
r.m[5] = m1.m[1] * m2.m[4] + m1.m[5] * m2.m[5] + m1.m[9] * m2.m[6] + m1.m[13] * m2.m[7];
r.m[6] = m1.m[2] * m2.m[4] + m1.m[6] * m2.m[5] + m1.m[10] * m2.m[6] + m1.m[14] * m2.m[7];
r.m[7] = m1.m[3] * m2.m[4] + m1.m[7] * m2.m[5] + m1.m[11] * m2.m[6] + m1.m[15] * m2.m[7];
r.m[8] = m1.m[0] * m2.m[8] + m1.m[4] * m2.m[9] + m1.m[8] * m2.m[10] + m1.m[12] * m2.m[11];
r.m[9] = m1.m[1] * m2.m[8] + m1.m[5] * m2.m[9] + m1.m[9] * m2.m[10] + m1.m[13] * m2.m[11];
r.m[10] = m1.m[2] * m2.m[8] + m1.m[6] * m2.m[9] + m1.m[10] * m2.m[10] + m1.m[14] * m2.m[11];
r.m[11] = m1.m[3] * m2.m[8] + m1.m[7] * m2.m[9] + m1.m[11] * m2.m[10] + m1.m[15] * m2.m[11];
r.m[12] = m1.m[0] * m2.m[12] + m1.m[4] * m2.m[13] + m1.m[8] * m2.m[14] + m1.m[12] * m2.m[15];
r.m[13] = m1.m[1] * m2.m[12] + m1.m[5] * m2.m[13] + m1.m[9] * m2.m[14] + m1.m[13] * m2.m[15];
r.m[14] = m1.m[2] * m2.m[12] + m1.m[6] * m2.m[13] + m1.m[10] * m2.m[14] + m1.m[14] * m2.m[15];
r.m[15] = m1.m[3] * m2.m[12] + m1.m[7] * m2.m[13] + m1.m[11] * m2.m[14] + m1.m[15] * m2.m[15];
return r;
}
Matrix4f MakeOrthoMatrix(float width, float height, float zNear, float zFar)
{
float depthRange = zFar - zNear;
if (depthRange <= 0)
{
throw std::runtime_error("zFar must be greater than zNear");
}
Matrix4f r;
r.m[0] = 2.f / width;
r.m[1] = 0;
r.m[2] = 0;
r.m[3] = 0;
r.m[4] = 0;
r.m[5] = 2.f / height;
r.m[6] = 0;
r.m[7] = 0;
r.m[8] = 0;
r.m[9] = 0;
r.m[10] = -1 / depthRange;
r.m[11] = 0;
r.m[12] = -1;
r.m[13] = -1;
r.m[14] = zNear / depthRange;
r.m[15] = 1;
return r;
}
Matrix4f MakeOrthoMatrix(float xmin, float xmax, float ymin, float ymax, float zNear, float zFar)
{
float width = xmax - xmin;
float height = ymax - ymin;
float depthRange = zFar - zNear;
if (width <= 0 || height <= 0 || depthRange <= 0)
{
throw std::runtime_error("Invalid dimensions for orthogonal matrix");
}
Matrix4f r;
// Масштабирование
r.m[0] = 2.f / width;
r.m[5] = 2.f / height;
r.m[10] = -1.f / depthRange;
// Обнуление неиспользуемых компонентов
r.m[1] = r.m[2] = r.m[3] = 0;
r.m[4] = r.m[6] = r.m[7] = 0;
r.m[8] = r.m[9] = r.m[11] = 0;
// Трансляция (смещение)
r.m[12] = -(xmax + xmin) / width;
r.m[13] = -(ymax + ymin) / height;
r.m[14] = zNear / depthRange;
r.m[15] = 1.f;
return r;
}
Matrix4f MakePerspectiveMatrix(float fovY, float aspectRatio, float zNear, float zFar)
{
float tanHalfFovy = tan(fovY / 2.f);
Matrix4f r;
if (zNear >= zFar || aspectRatio == 0)
{
throw std::runtime_error("Invalid perspective parameters");
}
r.m[0] = 1.f / (aspectRatio * tanHalfFovy);
r.m[1] = 0;
r.m[2] = 0;
r.m[3] = 0;
r.m[4] = 0;
r.m[5] = 1.f / (tanHalfFovy);
r.m[6] = 0;
r.m[7] = 0;
r.m[8] = 0;
r.m[9] = 0;
r.m[10] = -(zFar + zNear) / (zFar - zNear);
r.m[11] = -1;
r.m[12] = 0;
r.m[13] = 0;
r.m[14] = -(2.f * zFar * zNear) / (zFar - zNear);
r.m[15] = 0;
return r;
}
Matrix3f QuatToMatrix(const Vector4f& q)
{
Matrix3f m;
float wx, wy, wz, xx, yy, yz, xy, xz, zz, s, x2, y2, z2;
s = 2.0f / (q.v[0] * q.v[0] + q.v[1] * q.v[1] + q.v[2] * q.v[2] + q.v[3] * q.v[3]);
x2 = q.v[0] * s;
y2 = q.v[1] * s;
z2 = q.v[2] * s;
wx = q.v[3] * x2; wy = q.v[3] * y2; wz = q.v[3] * z2;
xx = q.v[0] * x2; xy = q.v[1] * x2; xz = q.v[2] * x2;
yy = q.v[1] * y2; yz = q.v[2] * y2;
zz = q.v[2] * z2;
m.m[0] = 1.0f - (yy + zz);
m.m[1] = xy + wz;
m.m[2] = xz - wy;
m.m[3] = xy - wz;
m.m[4] = 1.0f - (xx + zz);
m.m[5] = yz + wx;
m.m[6] = xz + wy;
m.m[7] = yz - wx;
m.m[8] = 1.0f - (xx + yy);
return m;
}
Vector4f MatrixToQuat(const Matrix3f& m)
{
Vector4f r;
float trace = m.m[0] + m.m[4] + m.m[8];
if (trace > 0)
{
float s = 0.5f / sqrtf(trace + 1.0f);
r.v[3] = 0.25f / s;
r.v[0] = (m.m[5] - m.m[7]) * s;
r.v[1] = (m.m[6] - m.m[2]) * s;
r.v[2] = (m.m[1] - m.m[3]) * s;
}
else if (m.m[0] > m.m[4] && m.m[0] > m.m[8])
{
float s = 2.0f * sqrtf(1.0f + m.m[0] - m.m[4] - m.m[8]);
r.v[3] = (m.m[5] - m.m[7]) / s;
r.v[0] = 0.25f * s;
r.v[1] = (m.m[1] + m.m[3]) / s;
r.v[2] = (m.m[6] + m.m[2]) / s;
}
else if (m.m[4] > m.m[8])
{
float s = 2.0f * sqrtf(1.0f + m.m[4] - m.m[0] - m.m[8]);
r.v[3] = (m.m[6] - m.m[2]) / s;
r.v[0] = (m.m[1] + m.m[3]) / s;
r.v[1] = 0.25f * s;
r.v[2] = (m.m[5] + m.m[7]) / s;
}
else
{
float s = 2.0f * sqrtf(1.0f + m.m[8] - m.m[0] - m.m[4]);
r.v[3] = (m.m[1] - m.m[3]) / s;
r.v[0] = (m.m[6] + m.m[2]) / s;
r.v[1] = (m.m[5] + m.m[7]) / s;
r.v[2] = 0.25f * s;
}
return r.normalized();
}
Vector4f QuatFromRotateAroundX(float angle)
{
Vector4f result;
result.v[0] = sinf(angle * 0.5f);
result.v[1] = 0.f;
result.v[2] = 0.f;
result.v[3] = cosf(angle * 0.5f);
return result;
}
Vector4f QuatFromRotateAroundY(float angle)
{
Vector4f result;
result.v[0] = 0.f;
result.v[1] = sinf(angle * 0.5f);
result.v[2] = 0.f;
result.v[3] = cosf(angle * 0.5f);
return result;
}
Vector4f QuatFromRotateAroundZ(float angle)
{
Vector4f result;
result.v[0] = 0.f;
result.v[1] = 0.f;
result.v[2] = sinf(angle * 0.5f);
result.v[3] = cosf(angle * 0.5f);
return result;
}
Matrix3f TransposeMatrix(const Matrix3f& m)
{
Matrix3f r;
r.m[0] = m.m[0];
r.m[1] = m.m[3];
r.m[2] = m.m[6];
r.m[3] = m.m[1];
r.m[4] = m.m[4];
r.m[5] = m.m[7];
r.m[6] = m.m[2];
r.m[7] = m.m[5];
r.m[8] = m.m[8];
return r;
}
Matrix3f InverseMatrix(const Matrix3f& m)
{
float d;
Matrix3f r;
d = m.m[0] * (m.m[4] * m.m[8] - m.m[5] * m.m[7]);
d -= m.m[1] * (m.m[3] * m.m[8] - m.m[6] * m.m[5]);
d += m.m[2] * (m.m[3] * m.m[7] - m.m[6] * m.m[4]);
if (fabs(d) < 0.01f)
{
throw std::runtime_error("Error: matrix cannot be inversed!");
}
else
{
r.m[0] = (m.m[4] * m.m[8] - m.m[5] * m.m[7]) / d;
r.m[1] = -(m.m[1] * m.m[8] - m.m[2] * m.m[7]) / d;
r.m[2] = (m.m[1] * m.m[5] - m.m[2] * m.m[4]) / d;
r.m[3] = -(m.m[3] * m.m[8] - m.m[5] * m.m[6]) / d;
r.m[4] = (m.m[0] * m.m[8] - m.m[2] * m.m[6]) / d;
r.m[5] = -(m.m[0] * m.m[5] - m.m[2] * m.m[3]) / d;
r.m[6] = (m.m[3] * m.m[7] - m.m[6] * m.m[4]) / d;
r.m[7] = -(m.m[0] * m.m[7] - m.m[6] * m.m[1]) / d;
r.m[8] = (m.m[0] * m.m[4] - m.m[1] * m.m[3]) / d;
};
return r;
}
Matrix4f InverseMatrix(const Matrix4f& mat)
{
Matrix4f inv;
float det;
inv.m[0] = mat.m[5] * mat.m[10] * mat.m[15] -
mat.m[5] * mat.m[11] * mat.m[14] -
mat.m[9] * mat.m[6] * mat.m[15] +
mat.m[9] * mat.m[7] * mat.m[14] +
mat.m[13] * mat.m[6] * mat.m[11] -
mat.m[13] * mat.m[7] * mat.m[10];
inv.m[4] = -mat.m[4] * mat.m[10] * mat.m[15] +
mat.m[4] * mat.m[11] * mat.m[14] +
mat.m[8] * mat.m[6] * mat.m[15] -
mat.m[8] * mat.m[7] * mat.m[14] -
mat.m[12] * mat.m[6] * mat.m[11] +
mat.m[12] * mat.m[7] * mat.m[10];
inv.m[8] = mat.m[4] * mat.m[9] * mat.m[15] -
mat.m[4] * mat.m[11] * mat.m[13] -
mat.m[8] * mat.m[5] * mat.m[15] +
mat.m[8] * mat.m[7] * mat.m[13] +
mat.m[12] * mat.m[5] * mat.m[11] -
mat.m[12] * mat.m[7] * mat.m[9];
inv.m[12] = -mat.m[4] * mat.m[9] * mat.m[14] +
mat.m[4] * mat.m[10] * mat.m[13] +
mat.m[8] * mat.m[5] * mat.m[14] -
mat.m[8] * mat.m[6] * mat.m[13] -
mat.m[12] * mat.m[5] * mat.m[10] +
mat.m[12] * mat.m[6] * mat.m[9];
inv.m[1] = -mat.m[1] * mat.m[10] * mat.m[15] +
mat.m[1] * mat.m[11] * mat.m[14] +
mat.m[9] * mat.m[2] * mat.m[15] -
mat.m[9] * mat.m[3] * mat.m[14] -
mat.m[13] * mat.m[2] * mat.m[11] +
mat.m[13] * mat.m[3] * mat.m[10];
inv.m[5] = mat.m[0] * mat.m[10] * mat.m[15] -
mat.m[0] * mat.m[11] * mat.m[14] -
mat.m[8] * mat.m[2] * mat.m[15] +
mat.m[8] * mat.m[3] * mat.m[14] +
mat.m[12] * mat.m[2] * mat.m[11] -
mat.m[12] * mat.m[3] * mat.m[10];
inv.m[9] = -mat.m[0] * mat.m[9] * mat.m[15] +
mat.m[0] * mat.m[11] * mat.m[13] +
mat.m[8] * mat.m[1] * mat.m[15] -
mat.m[8] * mat.m[3] * mat.m[13] -
mat.m[12] * mat.m[1] * mat.m[11] +
mat.m[12] * mat.m[3] * mat.m[9];
inv.m[13] = mat.m[0] * mat.m[9] * mat.m[14] -
mat.m[0] * mat.m[10] * mat.m[13] -
mat.m[8] * mat.m[1] * mat.m[14] +
mat.m[8] * mat.m[2] * mat.m[13] +
mat.m[12] * mat.m[1] * mat.m[10] -
mat.m[12] * mat.m[2] * mat.m[9];
inv.m[2] = mat.m[1] * mat.m[6] * mat.m[15] -
mat.m[1] * mat.m[7] * mat.m[14] -
mat.m[5] * mat.m[2] * mat.m[15] +
mat.m[5] * mat.m[3] * mat.m[14] +
mat.m[13] * mat.m[2] * mat.m[7] -
mat.m[13] * mat.m[3] * mat.m[6];
inv.m[6] = -mat.m[0] * mat.m[6] * mat.m[15] +
mat.m[0] * mat.m[7] * mat.m[14] +
mat.m[4] * mat.m[2] * mat.m[15] -
mat.m[4] * mat.m[3] * mat.m[14] -
mat.m[12] * mat.m[2] * mat.m[7] +
mat.m[12] * mat.m[3] * mat.m[6];
inv.m[10] = mat.m[0] * mat.m[5] * mat.m[15] -
mat.m[0] * mat.m[7] * mat.m[13] -
mat.m[4] * mat.m[1] * mat.m[15] +
mat.m[4] * mat.m[3] * mat.m[13] +
mat.m[12] * mat.m[1] * mat.m[7] -
mat.m[12] * mat.m[3] * mat.m[5];
inv.m[14] = -mat.m[0] * mat.m[5] * mat.m[14] +
mat.m[0] * mat.m[6] * mat.m[13] +
mat.m[4] * mat.m[1] * mat.m[14] -
mat.m[4] * mat.m[2] * mat.m[13] -
mat.m[12] * mat.m[1] * mat.m[6] +
mat.m[12] * mat.m[2] * mat.m[5];
inv.m[3] = -mat.m[1] * mat.m[6] * mat.m[11] +
mat.m[1] * mat.m[7] * mat.m[10] +
mat.m[5] * mat.m[2] * mat.m[11] -
mat.m[5] * mat.m[3] * mat.m[10] -
mat.m[9] * mat.m[2] * mat.m[7] +
mat.m[9] * mat.m[3] * mat.m[6];
inv.m[7] = mat.m[0] * mat.m[6] * mat.m[11] -
mat.m[0] * mat.m[7] * mat.m[10] -
mat.m[4] * mat.m[2] * mat.m[11] +
mat.m[4] * mat.m[3] * mat.m[10] +
mat.m[8] * mat.m[2] * mat.m[7] -
mat.m[8] * mat.m[3] * mat.m[6];
inv.m[11] = -mat.m[0] * mat.m[5] * mat.m[11] +
mat.m[0] * mat.m[7] * mat.m[9] +
mat.m[4] * mat.m[1] * mat.m[11] -
mat.m[4] * mat.m[3] * mat.m[9] -
mat.m[8] * mat.m[1] * mat.m[7] +
mat.m[8] * mat.m[3] * mat.m[5];
inv.m[15] = mat.m[0] * mat.m[5] * mat.m[10] -
mat.m[0] * mat.m[6] * mat.m[9] -
mat.m[4] * mat.m[1] * mat.m[10] +
mat.m[4] * mat.m[2] * mat.m[9] +
mat.m[8] * mat.m[1] * mat.m[6] -
mat.m[8] * mat.m[2] * mat.m[5];
// ��������� �����������
det = mat.m[0] * inv.m[0] + mat.m[1] * inv.m[4] + mat.m[2] * inv.m[8] + mat.m[3] * inv.m[12];
if (std::fabs(det) < 0.01f)
{
throw std::runtime_error("Error: matrix cannot be inversed!");
}
// ����� ��� �������� �� �����������
det = 1.0f / det;
for (int i = 0; i < 16; i++)
{
inv.m[i] *= det;
}
return inv;
}
Matrix3f CreateZRotationMatrix(float angle)
{
Matrix3f result = Matrix3f::Identity();
result.m[0] = cosf(angle);
result.m[1] = -sinf(angle);
result.m[3] = sinf(angle);
result.m[4] = cosf(angle);
return result;
}
Matrix4f MultMatrixMatrix(const Matrix4f& m1, const Matrix4f& m2)
{
Matrix4f rx;
rx.m[0] = m1.m[0] * m2.m[0] + m1.m[4] * m2.m[1] + m1.m[8] * m2.m[2] + m1.m[12] * m2.m[3];
rx.m[1] = m1.m[1] * m2.m[0] + m1.m[5] * m2.m[1] + m1.m[9] * m2.m[2] + m1.m[13] * m2.m[3];
rx.m[2] = m1.m[2] * m2.m[0] + m1.m[6] * m2.m[1] + m1.m[10] * m2.m[2] + m1.m[14] * m2.m[3];
rx.m[3] = m1.m[3] * m2.m[0] + m1.m[7] * m2.m[1] + m1.m[11] * m2.m[2] + m1.m[15] * m2.m[3];
rx.m[4] = m1.m[0] * m2.m[4] + m1.m[4] * m2.m[5] + m1.m[8] * m2.m[6] + m1.m[12] * m2.m[7];
rx.m[5] = m1.m[1] * m2.m[4] + m1.m[5] * m2.m[5] + m1.m[9] * m2.m[6] + m1.m[13] * m2.m[7];
rx.m[6] = m1.m[2] * m2.m[4] + m1.m[6] * m2.m[5] + m1.m[10] * m2.m[6] + m1.m[14] * m2.m[7];
rx.m[7] = m1.m[3] * m2.m[4] + m1.m[7] * m2.m[5] + m1.m[11] * m2.m[6] + m1.m[15] * m2.m[7];
rx.m[8] = m1.m[0] * m2.m[8] + m1.m[4] * m2.m[9] + m1.m[8] * m2.m[10] + m1.m[12] * m2.m[11];
rx.m[9] = m1.m[1] * m2.m[8] + m1.m[5] * m2.m[9] + m1.m[9] * m2.m[10] + m1.m[13] * m2.m[11];
rx.m[10] = m1.m[2] * m2.m[8] + m1.m[6] * m2.m[9] + m1.m[10] * m2.m[10] + m1.m[14] * m2.m[11];
rx.m[11] = m1.m[3] * m2.m[8] + m1.m[7] * m2.m[9] + m1.m[11] * m2.m[10] + m1.m[15] * m2.m[11];
rx.m[12] = m1.m[0] * m2.m[12] + m1.m[4] * m2.m[13] + m1.m[8] * m2.m[14] + m1.m[12] * m2.m[15];
rx.m[13] = m1.m[1] * m2.m[12] + m1.m[5] * m2.m[13] + m1.m[9] * m2.m[14] + m1.m[13] * m2.m[15];
rx.m[14] = m1.m[2] * m2.m[12] + m1.m[6] * m2.m[13] + m1.m[10] * m2.m[14] + m1.m[14] * m2.m[15];
rx.m[15] = m1.m[3] * m2.m[12] + m1.m[7] * m2.m[13] + m1.m[11] * m2.m[14] + m1.m[15] * m2.m[15];
return rx;
}
Matrix3f MultMatrixMatrix(const Matrix3f& m1, const Matrix3f& m2)
{
Matrix3f r;
r.m[0] = m1.m[0] * m2.m[0] + m1.m[3] * m2.m[1] + m1.m[6] * m2.m[2];
r.m[1] = m1.m[1] * m2.m[0] + m1.m[4] * m2.m[1] + m1.m[7] * m2.m[2];
r.m[2] = m1.m[2] * m2.m[0] + m1.m[5] * m2.m[1] + m1.m[8] * m2.m[2];
r.m[3] = m1.m[0] * m2.m[3] + m1.m[3] * m2.m[4] + m1.m[6] * m2.m[5];
r.m[4] = m1.m[1] * m2.m[3] + m1.m[4] * m2.m[4] + m1.m[7] * m2.m[5];
r.m[5] = m1.m[2] * m2.m[3] + m1.m[5] * m2.m[4] + m1.m[8] * m2.m[5];
r.m[6] = m1.m[0] * m2.m[6] + m1.m[3] * m2.m[7] + m1.m[6] * m2.m[8] ;
r.m[7] = m1.m[1] * m2.m[6] + m1.m[4] * m2.m[7] + m1.m[7] * m2.m[8];
r.m[8] = m1.m[2] * m2.m[6] + m1.m[5] * m2.m[7] + m1.m[8] * m2.m[8];
return r;
}
Matrix3f MakeTranslationMatrix(const Vector3f& p)
{
Matrix3f r = Matrix3f::Identity();
r.m[12] = p.v[0];
r.m[13] = p.v[1];
r.m[14] = p.v[2];
return r;
}
Matrix3f MakeScaleMatrix(float scale)
{
Matrix3f r = Matrix3f::Identity();
r.m[0] = scale;
r.m[5] = scale;
r.m[10] = scale;
return r;
}
Matrix3f MakeRotationMatrix(const Vector3f& p)
{
Matrix3f r = Matrix3f::Identity();
r.m[12] = p.v[0];
r.m[13] = p.v[1];
r.m[14] = p.v[2];
return r;
}
Vector2f operator*(Vector2f v, float scale)
{
Vector2f r = v;
r.v[0] = v.v[0] * scale;
r.v[1] = v.v[1] * scale;
return r;
}
Vector3f operator*(Vector3f v, float scale)
{
Vector3f r = v;
r.v[0] = v.v[0] * scale;
r.v[1] = v.v[1] * scale;
r.v[2] = v.v[2] * scale;
return r;
}
Vector4f operator*(Vector4f v, float scale)
{
Vector4f r = v;
r.v[0] = v.v[0] * scale;
r.v[1] = v.v[1] * scale;
r.v[2] = v.v[2] * scale;
r.v[3] = v.v[3] * scale;
return r;
}
Vector3f MultVectorMatrix(Vector3f v, Matrix3f mt)
{
Vector3f r;
r.v[0] = v.v[0] * mt.m[0] + v.v[1] * mt.m[1] + v.v[2] * mt.m[2];
r.v[1] = v.v[0] * mt.m[3] + v.v[1] * mt.m[4] + v.v[2] * mt.m[5];
r.v[2] = v.v[0] * mt.m[6] + v.v[1] * mt.m[7] + v.v[2] * mt.m[8];
return r;
}
Vector4f MultVectorMatrix(Vector4f v, Matrix4f mt)
{
Vector4f r;
r.v[0] = v.v[0] * mt.m[0] + v.v[1] * mt.m[1] + v.v[2] * mt.m[2] + v.v[3] * mt.m[3];
r.v[1] = v.v[0] * mt.m[4] + v.v[1] * mt.m[5] + v.v[2] * mt.m[6] + v.v[3] * mt.m[7];
r.v[2] = v.v[0] * mt.m[8] + v.v[1] * mt.m[9] + v.v[2] * mt.m[10] + v.v[3] * mt.m[11];
r.v[3] = v.v[0] * mt.m[12] + v.v[1] * mt.m[13] + v.v[2] * mt.m[14] + v.v[3] * mt.m[15];
return r;
}
Vector4f MultMatrixVector(Matrix4f mt, Vector4f v)
{
Vector4f r;
r.v[0] = v.v[0] * mt.m[0] + v.v[1] * mt.m[4] + v.v[2] * mt.m[8] + v.v[3] * mt.m[12];
r.v[1] = v.v[0] * mt.m[1] + v.v[1] * mt.m[5] + v.v[2] * mt.m[9] + v.v[3] * mt.m[13];
r.v[2] = v.v[0] * mt.m[2] + v.v[1] * mt.m[6] + v.v[2] * mt.m[10] + v.v[3] * mt.m[14];
r.v[3] = v.v[0] * mt.m[3] + v.v[1] * mt.m[7] + v.v[2] * mt.m[11] + v.v[3] * mt.m[15];
return r;
}
Vector3f MultMatrixVector(Matrix3f mt, Vector3f v)
{
Vector3f r;
r.v[0] = v.v[0] * mt.m[0] + v.v[1] * mt.m[3] + v.v[2] * mt.m[6];
r.v[1] = v.v[0] * mt.m[1] + v.v[1] * mt.m[4] + v.v[2] * mt.m[7];
r.v[2] = v.v[0] * mt.m[2] + v.v[1] * mt.m[5] + v.v[2] * mt.m[8];
return r;
}
Vector4f slerp(const Vector4f& q1, const Vector4f& q2, float t)
{
const float epsilon = 1e-6f;
// Нормализация входных кватернионов
Vector4f q1_norm = q1.normalized();
Vector4f q2_norm = q2.normalized();
float cosTheta = q1_norm.dot(q2_norm);
// Если q1 и q2 близки к противоположным направлениям, корректируем q2
Vector4f q2_adjusted = q2_norm;
if (cosTheta < 0.0f) {
q2_adjusted.v[0] = -q2_adjusted.v[0];
q2_adjusted.v[1] = -q2_adjusted.v[1];
q2_adjusted.v[2] = -q2_adjusted.v[2];
q2_adjusted.v[3] = -q2_adjusted.v[3];
cosTheta = -cosTheta;
}
// Если кватернионы близки, используем линейную интерполяцию
if (cosTheta > 1.0f - epsilon) {
Vector4f result;
result.v[0] = q1_norm.v[0] + t * (q2_adjusted.v[0] - q1_norm.v[0]);
result.v[1] = q1_norm.v[1] + t * (q2_adjusted.v[1] - q1_norm.v[1]);
result.v[2] = q1_norm.v[2] + t * (q2_adjusted.v[2] - q1_norm.v[2]);
result.v[3] = q1_norm.v[3] + t * (q2_adjusted.v[3] - q1_norm.v[3]);
return result.normalized();
}
// Иначе используем сферическую интерполяцию
float theta = std::acos(cosTheta);
float sinTheta = std::sin(theta);
float coeff1 = std::sin((1.0f - t) * theta) / sinTheta;
float coeff2 = std::sin(t * theta) / sinTheta;
Vector4f result;
result.v[0] = coeff1 * q1_norm.v[0] + coeff2 * q2_adjusted.v[0];
result.v[1] = coeff1 * q1_norm.v[1] + coeff2 * q2_adjusted.v[1];
result.v[2] = coeff1 * q1_norm.v[2] + coeff2 * q2_adjusted.v[2];
result.v[3] = coeff1 * q1_norm.v[3] + coeff2 * q2_adjusted.v[3];
return result.normalized();
}
Matrix4f MakeMatrix4x4(const Matrix3f& m, const Vector3f pos)
{
Matrix4f r;
r.m[0] = m.m[0];
r.m[1] = m.m[1];
r.m[2] = m.m[2];
r.m[3] = 0;
r.m[4] = m.m[3];
r.m[5] = m.m[4];
r.m[6] = m.m[5];
r.m[7] = 0;
r.m[8] = m.m[6];
r.m[9] = m.m[7];
r.m[10] = m.m[8];
r.m[11] = 0;
r.m[12] = pos.v[0];
r.m[13] = pos.v[1];
r.m[14] = pos.v[2];
r.m[15] = 1.0;
return r;
}
};
-179
View File
@@ -1,179 +0,0 @@
#pragma once
#include <array>
#include <exception>
#include <stdexcept>
#include <cmath>
namespace ZL {
struct Vector4f
{
std::array<float, 4> v = { 0.f, 0.f, 0.f, 0.f };
Vector4f normalized() const {
double norm = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]);
if (norm <= 0.000001f) {
return { 0,0, 0, 0};
}
Vector4f r;
r.v[0] = v[0] / norm;
r.v[1] = v[1] / norm;
r.v[2] = v[2] / norm;
r.v[3] = v[3] / norm;
return r;
}
double dot(const Vector4f& other) const {
return v[0] * other.v[0] + v[1] * other.v[1] + v[2] * other.v[2] + v[3] * other.v[3];
}
};
struct Vector3f
{
std::array<float, 3> v = { 0.f, 0.f, 0.f };
Vector3f()
{
}
Vector3f(float x, float y, float z)
: v{x,y,z}
{
}
Vector3f normalized() const {
double norm = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
if (norm <= 0.000001f) {
return { 0,0,0 };
}
Vector3f r;
r.v[0] = v[0] / norm;
r.v[1] = v[1] / norm;
r.v[2] = v[2] / norm;
return r;
}
float squaredNorm() const {
return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
}
float length() const
{
return sqrt(squaredNorm());
}
float dot(const Vector3f& other) const {
return v[0] * other.v[0] + v[1] * other.v[1] + v[2] * other.v[2];
}
Vector3f cross(const Vector3f& other) const {
return Vector3f(
v[1] * other.v[2] - v[2] * other.v[1],
v[2] * other.v[0] - v[0] * other.v[2],
v[0] * other.v[1] - v[1] * other.v[0]
);
}
bool operator<(const Vector3f& other) const {
if (v[0] != other.v[0]) return v[0] < other.v[0];
if (v[1] != other.v[1]) return v[1] < other.v[1];
return v[2] < other.v[2];
}
};
struct Vector2f
{
std::array<float, 2> v = {0.f, 0.f};
Vector2f()
{
}
Vector2f(float x, float y)
: v{ x,y }
{
}
};
Vector2f operator+(const Vector2f& x, const Vector2f& y);
Vector2f operator-(const Vector2f& x, const Vector2f& y);
Vector3f operator+(const Vector3f& x, const Vector3f& y);
Vector3f operator-(const Vector3f& x, const Vector3f& y);
Vector4f operator+(const Vector4f& x, const Vector4f& y);
Vector4f operator-(const Vector4f& x, const Vector4f& y);
Vector3f operator-(const Vector3f& x);
struct Matrix3f
{
std::array<float, 9> m = { 0.f, 0.f, 0.f,
0.f, 0.f, 0.f,
0.f, 0.f, 0.f, };
static Matrix3f Identity();
};
struct Matrix4f
{
std::array<float, 16> m = { 0.f, 0.f, 0.f, 0.f,
0.f, 0.f, 0.f, 0.f,
0.f, 0.f, 0.f, 0.f,
0.f, 0.f, 0.f, 0.f };
static Matrix4f Identity();
float& operator()(int row, int col) {
//return m[row * 4 + col]; //OpenGL specific
return m[col * 4 + row];
}
const float& operator()(int row, int col) const {
//return m[row * 4 + col];
return m[col * 4 + row];
}
};
Matrix4f operator*(const Matrix4f& m1, const Matrix4f& m2);
Matrix4f MakeOrthoMatrix(float width, float height, float zNear, float zFar);
Matrix4f MakeOrthoMatrix(float xmin, float xmax, float ymin, float ymax, float zNear, float zFar);
Matrix4f MakePerspectiveMatrix(float fovY, float aspectRatio, float zNear, float zFar);
Matrix3f QuatToMatrix(const Vector4f& q);
Vector4f MatrixToQuat(const Matrix3f& m);
Vector4f QuatFromRotateAroundX(float angle);
Vector4f QuatFromRotateAroundY(float angle);
Vector4f QuatFromRotateAroundZ(float angle);
Vector2f operator*(Vector2f v, float scale);
Vector3f operator*(Vector3f v, float scale);
Vector4f operator*(Vector4f v, float scale);
Vector3f MultVectorMatrix(Vector3f v, Matrix3f mt);
Vector4f MultVectorMatrix(Vector4f v, Matrix4f mt);
Vector4f MultMatrixVector(Matrix4f mt, Vector4f v);
Vector3f MultMatrixVector(Matrix3f mt, Vector3f v);
Vector4f slerp(const Vector4f& q1, const Vector4f& q2, float t);
Matrix3f InverseMatrix(const Matrix3f& m);
Matrix4f InverseMatrix(const Matrix4f& m);
Matrix3f MultMatrixMatrix(const Matrix3f& m1, const Matrix3f& m2);
Matrix4f MultMatrixMatrix(const Matrix4f& m1, const Matrix4f& m2);
Matrix4f MakeMatrix4x4(const Matrix3f& m, const Vector3f pos);
};
Binary file not shown.
BIN
View File
Binary file not shown.
+65
View File
@@ -0,0 +1,65 @@
import bpy
def append_layered_action_5_0(source_obj_name, target_obj_name):
source_obj = bpy.data.objects.get(source_obj_name)
target_obj = bpy.data.objects.get(target_obj_name)
if not (source_obj and target_obj):
print("Ошибка: Объекты не найдены")
return
src_action = source_obj.animation_data.action
tgt_action = target_obj.animation_data.action
# 1. Получаем слои (обычно первый)
src_layer = src_action.layers[0]
tgt_layer = tgt_action.layers[0]
# 2. Получаем стрипы
src_strip = src_layer.strips[0]
tgt_strip = tgt_layer.strips[0]
# Смещение (опираемся на конец диапазона целевого экшена)
offset = tgt_action.frame_range[1]
# 3. Итерируемся по channelbags в исходном стрипе
for src_bag in src_strip.channelbags:
# Ищем или создаем соответствующий bag в целевом стрипе
# Обычно они сопоставляются по имени или типу (например, 'Keyframe Channel Bag')
# В простейшем случае берем первый или сопоставляем по индексу
dst_bag = None
if len(tgt_strip.channelbags) > 0:
# Пытаемся найти по названию (если оно есть) или берем тот же индекс
dst_bag = tgt_strip.channelbags[0]
if not dst_bag:
# Если в целевом стрипе нет сумок, это странно, но можно создать
# (Метод создания может зависеть от конкретного подтипа стрипа в 5.0)
continue
#print(f"Обработка channelbag: {src_bag.name}, кривых: {len(src_bag.fcurves)}")
# 4. Итерируемся по fcurves внутри сумки
for src_fcurve in src_bag.fcurves:
dst_fcurve = dst_bag.fcurves.find(src_fcurve.data_path, index=src_fcurve.array_index)
if not dst_fcurve:
dst_fcurve = dst_bag.fcurves.new(data_path=src_fcurve.data_path, index=src_fcurve.array_index)
# 5. Копируем ключи с офсетом
for keyframe in src_fcurve.keyframe_points:
new_frame = keyframe.co[0] + offset
new_value = keyframe.co[1]
new_kp = dst_fcurve.keyframe_points.insert(new_frame, new_value, options={'FAST'})
new_kp.interpolation = keyframe.interpolation
# Обновляем интерполяцию
for fc in dst_bag.fcurves:
fc.update()
print(f"Анимация успешно дозаписана. Новый конец: {tgt_action.frame_range[1]}")
append_layered_action_5_0('Armature.001', 'Armature')
+192
View File
@@ -0,0 +1,192 @@
import bpy
import bmesh
# Имена mesh и арматуры
mesh_name = "arm"
armature_name = "Reference"
# Находим объект mesh по имени
mesh_obj = bpy.data.objects.get(mesh_name)
# Находим объект арматуры по имени
armature_obj = bpy.data.objects.get(armature_name)
# Устанавливаем текущий кадр на 0
bpy.context.scene.frame_set(0)
# Принудительно обновляем сцену, чтобы применить анимацию
bpy.context.view_layer.update()
# Открываем файл для записи
with open("C:\\Work\\Projects\\witcher001\\resources\\w\\zombie002.txt", "w") as file:
# Обработка арматуры и анимации
if armature_obj and armature_obj.type == 'ARMATURE':
file.write("=== Armature Matrix ===\n")
for row in armature_obj.matrix_world:
file.write(f"{row}\n")
file.write(f"=== Armature Bones: {len(armature_obj.data.bones)}\n")
for bone in armature_obj.data.bones:
# Записываем имя кости, длину и связи
file.write(f"Bone: {bone.name}\n")
file.write(f" HEAD_LOCAL: {bone.head_local}\n")
file.write(f" TAIL_LOCAL: {bone.tail_local}\n")
file.write(f" Length: {(bone.tail_local - bone.head_local).length}\n")
for row in bone.matrix:
file.write(f" {row}\n")
file.write(f" Parent: {bone.parent.name if bone.parent else 'None'}\n")
file.write(f" Children: {[child.name for child in bone.children]}\n")
# Обработка mesh
if mesh_obj and mesh_obj.type == 'MESH':
# Создаем копию mesh, чтобы не изменять оригинал
mesh_copy = mesh_obj.copy()
mesh_copy.data = mesh_obj.data.copy()
bpy.context.collection.objects.link(mesh_copy)
# Убедимся, что объект активен
bpy.context.view_layer.objects.active = mesh_copy
mesh_copy.select_set(True)
# Применяем модификатор Armature (если он есть)
for modifier in mesh_copy.modifiers:
if modifier.type == 'ARMATURE':
# Включаем модификатор, если он отключен
if not modifier.show_viewport:
modifier.show_viewport = True
if not modifier.show_render:
modifier.show_render = True
# Проверяем, что модификатор связан с арматурой
if modifier.object is None:
print(f"Модификатор Armature на объекте {mesh_copy.name} не связан с арматурой. Пропускаем.")
continue
# Временно применяем модификатор, чтобы получить правильные координаты вершин
try:
bpy.ops.object.modifier_apply(modifier=modifier.name)
except RuntimeError as e:
print(f"Ошибка при применении модификатора Armature: {e}")
continue
# Переходим в режим редактирования
bpy.ops.object.mode_set(mode='EDIT')
# Получаем BMesh представление mesh
bm = bmesh.from_edit_mesh(mesh_copy.data)
# Записываем список вершин
file.write(f"===Vertices: {len(bm.verts)}\n")
for vertex in bm.verts:
file.write(f"Vertex {vertex.index}: {vertex.co}\n")
# Убедимся, что у меша есть UV слой
uv_layer = bm.loops.layers.uv.active
if not uv_layer:
file.write("UV слой не найден.\n")
if uv_layer:
file.write(f"===UV Coordinates:\n")
file.write(f"Face count: {len(bm.faces)}\n")
for face in bm.faces:
file.write(f"Face {face.index}\n")
file.write(f"UV Count: {len(face.loops)}\n")
for loop in face.loops:
uv_coords = loop[uv_layer].uv
file.write(f" UV {uv_coords}\n")
# Записываем нормали
file.write(f"===Normals:\n")
for vertex in bm.verts:
file.write(f"Vertex {vertex.index}: Normal {vertex.normal}\n")
# Записываем треугольники (индексы вершин)
file.write(f"===Triangles: {len(bm.faces)}\n")
for face in bm.faces:
if len(face.verts) == 3: # Проверяем, что это треугольник
verts_indices = [vert.index for vert in face.verts]
file.write(f"Triangle: {verts_indices}\n")
# Возвращаемся в объектный режим
bpy.ops.object.mode_set(mode='OBJECT')
# Записываем веса вершин
file.write("=== Vertex Weights ===\n")
for vertex in mesh_copy.data.vertices:
file.write(f"Vertex {vertex.index}:\n")
file.write(f"Vertex groups: {len(vertex.groups)}\n")
for group in vertex.groups:
group_name = mesh_copy.vertex_groups[group.group].name
file.write(f" Group: '{group_name}', Weight: {group.weight}\n")
# Удаляем временную копию mesh
bpy.data.objects.remove(mesh_copy)
else:
file.write(f"Объект с именем '{mesh_name}' не найден или не является mesh.\n")
# Обработка арматуры и анимации
if armature_obj and armature_obj.type == 'ARMATURE':
# Получаем все ключевые кадры для арматуры
file.write("=== Animation Keyframes ===\n")
if armature_obj.animation_data and armature_obj.animation_data.action:
action = armature_obj.animation_data.action
# Собираем все уникальные ключевые кадры
keyframes = set()
# Логика для Blender 5.0 (Strip-based / ChannelBag structure)
if hasattr(action, "layers"):
for layer in action.layers:
if hasattr(layer, "strips"):
for strip in layer.strips:
# Проверяем наличие channelbags (согласно вашему dir(strip))
if hasattr(strip, "channelbags"):
for bag in strip.channelbags:
for fcurve in bag.fcurves:
for keyframe in fcurve.keyframe_points:
keyframes.add(int(keyframe.co[0]))
# На случай, если в этой версии используется единственное число
elif hasattr(strip, "channelbag") and strip.channelbag:
for fcurve in strip.channelbag.fcurves:
for keyframe in fcurve.keyframe_points:
keyframes.add(int(keyframe.co[0]))
# Фоллбек для Legacy экшенов
if not keyframes and hasattr(action, "fcurves"):
for fcurve in action.fcurves:
for keyframe in fcurve.keyframe_points:
keyframes.add(int(keyframe.co[0]))
keyframes = sorted(keyframes)
# Сортируем ключевые кадры
keyframes = sorted(keyframes)
# Сохраняем координаты и матрицы поворота для каждой кости на каждом ключевом кадре
file.write("=== Bone Transforms per Keyframe ===\n")
file.write(f"Keyframes: {len(keyframes)}\n")
for frame in keyframes:
# Устанавливаем текущий кадр
bpy.context.scene.frame_set(frame)
bpy.context.view_layer.update() # Обновляем сцену
file.write(f"Frame: {frame}\n")
for bone in armature_obj.pose.bones:
# Получаем координаты и матрицу поворота кости в мировом пространстве
matrix = bone.matrix
location = matrix.translation
rotation = matrix.to_euler()
# Записываем данные
file.write(f" Bone: {bone.name}\n")
file.write(f" Location: {location}\n")
file.write(f" Rotation: {rotation}\n")
file.write(f" Matrix:\n")
for row in matrix:
file.write(f" {row}\n")
else:
file.write(f"Объект с именем '{armature_name}' не найден или не является арматурой.\n")
print("Данные сохранены в файл 'mesh_armature_and_animation_data.txt'")
+228
View File
@@ -0,0 +1,228 @@
import bpy
import bmesh
#!
# Имена mesh и арматуры
mesh_name = "Joined"
armature_name = "Armature"
# Находим объект mesh по имени
mesh_obj = bpy.data.objects.get(mesh_name)
# Находим объект арматуры по имени
armature_obj = bpy.data.objects.get(armature_name)
# Устанавливаем текущий кадр на 0
bpy.context.scene.frame_set(0)
# Принудительно обновляем сцену, чтобы применить анимацию
bpy.context.view_layer.update()
# Открываем файл для записи
with open("C:\\Work\\Media\\witcher\\2026-04-13\\output\\gg_stand_idle001.txt", "w") as file:
# Обработка арматуры и анимации
if armature_obj and armature_obj.type == 'ARMATURE':
file.write("=== Armature Matrix ===\n")
for row in armature_obj.matrix_world:
file.write(f"{row}\n")
file.write(f"=== Armature Bones: {len(armature_obj.data.bones)}\n")
for bone in armature_obj.data.bones:
# Записываем имя кости, длину и связи
file.write(f"Bone: {bone.name}\n")
file.write(f" HEAD_LOCAL: {bone.head_local}\n")
file.write(f" TAIL_LOCAL: {bone.tail_local}\n")
file.write(f" Length: {(bone.tail_local - bone.head_local).length}\n")
for row in bone.matrix:
file.write(f" {row}\n")
file.write(f" Parent: {bone.parent.name if bone.parent else 'None'}\n")
file.write(f" Children: {[child.name for child in bone.children]}\n")
# Обработка mesh
if mesh_obj and mesh_obj.type == 'MESH':
# Создаем копию mesh, чтобы не изменять оригинал
mesh_copy = mesh_obj.copy()
mesh_copy.data = mesh_obj.data.copy()
bpy.context.collection.objects.link(mesh_copy)
# Убедимся, что объект активен
bpy.context.view_layer.objects.active = mesh_copy
mesh_copy.select_set(True)
# Применяем модификатор Armature (если он есть)
for modifier in mesh_copy.modifiers:
if modifier.type == 'ARMATURE':
# Включаем модификатор, если он отключен
if not modifier.show_viewport:
modifier.show_viewport = True
if not modifier.show_render:
modifier.show_render = True
# Проверяем, что модификатор связан с арматурой
if modifier.object is None:
print(f"Модификатор Armature на объекте {mesh_copy.name} не связан с арматурой. Пропускаем.")
continue
# Временно применяем модификатор, чтобы получить правильные координаты вершин
try:
bpy.ops.object.modifier_apply(modifier=modifier.name)
except RuntimeError as e:
print(f"Ошибка при применении модификатора Armature: {e}")
continue
# Переходим в режим редактирования
bpy.ops.object.mode_set(mode='EDIT')
# Получаем BMesh представление mesh
bm = bmesh.from_edit_mesh(mesh_copy.data)
# Записываем список вершин
file.write(f"===Vertices: {len(bm.verts)}\n")
for vertex in bm.verts:
file.write(f"Vertex {vertex.index}: {vertex.co}\n")
# Убедимся, что у меша есть UV слой
uv_layer = bm.loops.layers.uv.active
if not uv_layer:
file.write("UV слой не найден.\n")
if uv_layer:
file.write(f"===UV Coordinates:\n")
file.write(f"Face count: {len(bm.faces)}\n")
for face in bm.faces:
file.write(f"Face {face.index}\n")
file.write(f"UV Count: {len(face.loops)}\n")
for loop in face.loops:
uv_coords = loop[uv_layer].uv
file.write(f" UV {uv_coords}\n")
# Записываем нормали
file.write(f"===Normals:\n")
for vertex in bm.verts:
file.write(f"Vertex {vertex.index}: Normal {vertex.normal}\n")
# Записываем треугольники (индексы вершин)
file.write(f"===Triangles: {len(bm.faces)}\n")
for face in bm.faces:
if len(face.verts) == 3: # Проверяем, что это треугольник
verts_indices = [vert.index for vert in face.verts]
file.write(f"Triangle: {verts_indices}\n")
# Возвращаемся в объектный режим
bpy.ops.object.mode_set(mode='OBJECT')
# Записываем веса вершин
file.write("=== Vertex Weights (Max 5 bones per vertex) ===\n")
MAX_BONES = 5
for vertex in mesh_copy.data.vertices:
# Извлекаем все группы и веса для текущей вершины
all_weights = []
for group_element in vertex.groups:
all_weights.append({
'index': group_element.group,
'weight': group_element.weight
})
# Если костей больше лимита, фильтруем и перераспределяем
if len(all_weights) > MAX_BONES:
# Сортируем по весу (от большего к меньшему)
all_weights.sort(key=lambda x: x['weight'], reverse=True)
# Берем только топ-5
kept_weights = all_weights[:MAX_BONES]
# Считаем сумму весов оставшихся костей для нормализации
total_weight = sum(gw['weight'] for gw in kept_weights)
if total_weight > 0:
for gw in kept_weights:
gw['weight'] /= total_weight
else:
# На случай, если у всех веса были по 0.0 (редкий баг меша)
kept_weights[0]['weight'] = 1.0
final_weights = kept_weights
else:
final_weights = all_weights
file.write(f"Vertex {vertex.index}:\n")
file.write(f"Vertex groups: {len(final_weights)}\n")
for gw in final_weights:
group_name = mesh_copy.vertex_groups[gw['index']].name
file.write(f" Group: '{group_name}', Weight: {gw['weight']:.6f}\n")
# Удаляем временную копию mesh
bpy.data.objects.remove(mesh_copy)
else:
file.write(f"Объект с именем '{mesh_name}' не найден или не является mesh.\n")
# Обработка арматуры и анимации
if armature_obj and armature_obj.type == 'ARMATURE':
# Получаем все ключевые кадры для арматуры
file.write("=== Animation Keyframes ===\n")
if armature_obj.animation_data and armature_obj.animation_data.action:
action = armature_obj.animation_data.action
# Собираем все уникальные ключевые кадры
keyframes = set()
# Логика для Blender 5.0 (Strip-based / ChannelBag structure)
if hasattr(action, "layers"):
for layer in action.layers:
if hasattr(layer, "strips"):
for strip in layer.strips:
# Проверяем наличие channelbags (согласно вашему dir(strip))
if hasattr(strip, "channelbags"):
for bag in strip.channelbags:
for fcurve in bag.fcurves:
for keyframe in fcurve.keyframe_points:
keyframes.add(int(keyframe.co[0]))
# На случай, если в этой версии используется единственное число
elif hasattr(strip, "channelbag") and strip.channelbag:
for fcurve in strip.channelbag.fcurves:
for keyframe in fcurve.keyframe_points:
keyframes.add(int(keyframe.co[0]))
# Фоллбек для Legacy экшенов
if not keyframes and hasattr(action, "fcurves"):
for fcurve in action.fcurves:
for keyframe in fcurve.keyframe_points:
keyframes.add(int(keyframe.co[0]))
keyframes = sorted(keyframes)
# Сортируем ключевые кадры
keyframes = sorted(keyframes)
# Сохраняем координаты и матрицы поворота для каждой кости на каждом ключевом кадре
file.write("=== Bone Transforms per Keyframe ===\n")
file.write(f"Keyframes: {len(keyframes)}\n")
for frame in keyframes:
# Устанавливаем текущий кадр
bpy.context.scene.frame_set(frame)
bpy.context.view_layer.update() # Обновляем сцену
file.write(f"Frame: {frame}\n")
for bone in armature_obj.pose.bones:
# Получаем координаты и матрицу поворота кости в мировом пространстве
matrix = bone.matrix
location = matrix.translation
rotation = matrix.to_euler()
# Записываем данные
file.write(f" Bone: {bone.name}\n")
file.write(f" Location: {location}\n")
file.write(f" Rotation: {rotation}\n")
file.write(f" Matrix:\n")
for row in matrix:
file.write(f" {row}\n")
else:
file.write(f"Объект с именем '{armature_name}' не найден или не является арматурой.\n")
print("Данные сохранены в файл 'mesh_armature_and_animation_data.txt'")
+111
View File
@@ -0,0 +1,111 @@
import bpy
import bmesh
import mathutils
import random
import math
class SolidTreeGenerator:
def __init__(self, levels=5, length=3.0, radius=0.3):
self.levels = levels
self.base_length = length
self.base_radius = radius
# Хранилище для данных: (start_pos, end_pos, radius_start, radius_end)
self.branches_data = []
def calculate_tree(self, start_pos, direction, length, radius, level):
if level <= 0 or length < 0.1:
return
end_pos = start_pos + direction * length
# Сохраняем данные сегмента
self.branches_data.append({
'start': start_pos.copy(),
'end': end_pos.copy(),
'r_start': radius,
'r_end': radius * 0.7
})
# 1. Основной ствол (продолжение)
trunk_dir = (direction + self.get_random_vector(0.1)).normalized()
self.calculate_tree(end_pos, trunk_dir, length * 0.8, radius * 0.7, level - 1)
# 2. Боковые ветки (ветвление)
if level > 1:
num_sides = random.randint(2, 3) # Минимум 2 ветки для видимости
for _ in range(num_sides):
# Создаем вектор, сильно отклоненный от ствола (30-60 градусов)
axis = self.get_random_vector(1.0).normalized()
angle = math.radians(random.uniform(30, 60))
side_dir = direction.copy()
side_dir.rotate(mathutils.Quaternion(axis, angle))
# Боковые ветки короче
self.calculate_tree(end_pos, side_dir, length * 0.6, radius * 0.5, level - 1)
def get_random_vector(self, intensity):
return mathutils.Vector((
random.uniform(-intensity, intensity),
random.uniform(-intensity, intensity),
random.uniform(-intensity, intensity)
))
def build_mesh(self):
mesh = bpy.data.meshes.new("TreeMesh")
obj = bpy.data.objects.new("Tree", mesh)
bpy.context.collection.objects.link(obj)
bm = bmesh.new()
skin_layer = bm.verts.layers.skin.verify()
# Словарь для предотвращения дублирования вершин в одной точке
# Ключ - кортеж координат, Значение - объект вершины BMesh
vert_map = {}
for b in self.branches_data:
# Превращаем координаты в кортежи для словаря
s_key = tuple(round(v, 4) for v in b['start'])
e_key = tuple(round(v, 4) for v in b['end'])
# Получаем или создаем начальную вершину
if s_key not in vert_map:
v_start = bm.verts.new(b['start'])
v_start[skin_layer].radius = (b['r_start'], b['r_start'])
vert_map[s_key] = v_start
else:
v_start = vert_map[s_key]
# Получаем или создаем конечную вершину
if e_key not in vert_map:
v_end = bm.verts.new(b['end'])
v_end[skin_layer].radius = (b['r_end'], b['r_end'])
vert_map[e_key] = v_end
else:
v_end = vert_map[e_key]
# Создаем ребро, если его еще нет
if not bm.edges.get((v_start, v_end)):
bm.edges.new((v_start, v_end))
# Находим корень (самую нижнюю точку) и помечаем его
root_v = min(bm.verts, key=lambda v: v.co.z)
root_v[skin_layer].use_root = True
bm.to_mesh(mesh)
bm.free()
# Модификаторы
obj.modifiers.new(name="Skin", type='SKIN')
sub = obj.modifiers.new(name="Subdiv", type='SUBSURF')
sub.levels = 1 # Для начала 1, чтобы не тормозило
# Очистка сцены
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
# Запуск
generator = SolidTreeGenerator(levels=5, length=3.0, radius=0.4)
generator.calculate_tree(mathutils.Vector((0,0,0)), mathutils.Vector((0,0,1)), 3.0, 0.4, 5)
generator.build_mesh()
File diff suppressed because it is too large. Load diff
+58
View File
@@ -0,0 +1,58 @@
# cmake/FetchDependencies.cmake
set(THIRDPARTY_DIR "${CMAKE_CURRENT_LIST_DIR}/../thirdparty")
if(NOT EXISTS "${THIRDPARTY_DIR}")
file(MAKE_DIRECTORY "${THIRDPARTY_DIR}")
endif()
macro(check_and_download URL ARCHIVE_NAME EXTRACTED_DIR_NAME CHECK_FILE)
set(ARCHIVE_PATH "${THIRDPARTY_DIR}/${ARCHIVE_NAME}")
set(SRC_PATH "${THIRDPARTY_DIR}/${EXTRACTED_DIR_NAME}")
if(NOT EXISTS "${ARCHIVE_PATH}")
message(STATUS "Downloading ${ARCHIVE_NAME}...")
file(DOWNLOAD "${URL}" "${ARCHIVE_PATH}" SHOW_PROGRESS)
endif()
if(NOT EXISTS "${SRC_PATH}/${CHECK_FILE}")
message(STATUS "Extracting ${ARCHIVE_NAME}...")
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xvf "${ARCHIVE_PATH}"
WORKING_DIRECTORY "${THIRDPARTY_DIR}"
)
endif()
endmacro()
# 1) ZLIB (Нужна только для инклудов, если не используете emscripten порты)
check_and_download("https://www.zlib.net/zlib132.zip" "zlib132.zip" "zlib-1.3.2" "CMakeLists.txt")
# 2) SDL2
check_and_download("https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.32.10.zip" "release-2.32.10.zip" "SDL-release-2.32.10" "CMakeLists.txt")
# 3) LibPNG
check_and_download("https://github.com/pnggroup/libpng/archive/refs/tags/v1.6.51.zip" "v1.6.51.zip" "libpng-1.6.51" "CMakeLists.txt")
# 4) LibZip
check_and_download("https://github.com/nih-at/libzip/archive/refs/tags/v1.11.4.zip" "v1.11.4.zip" "libzip-1.11.4" "CMakeLists.txt")
# 5) Eigen
check_and_download("https://gitlab.com/libeigen/eigen/-/archive/5.0.0/eigen-5.0.0.zip" "eigen-5.0.0.zip" "eigen-5.0.0" "signature_of_eigen3_matrix_library")
# 6) Boost
check_and_download("https://archives.boost.io/release/1.90.0/source/boost_1_90_0.zip" "boost_1_90_0.zip" "boost_1_90_0" "boost")
# 7) FreeType
check_and_download("https://download.savannah.gnu.org/releases/freetype/freetype-2.14.1.tar.gz" "freetype-2.14.1.tar.gz" "freetype-2.14.1" "CMakeLists.txt")
# 8) SDL_ttf
check_and_download("https://github.com/libsdl-org/SDL_ttf/archive/refs/tags/release-2.24.0.zip" "release-2.24.0.zip" "SDL_ttf-release-2.24.0" "CMakeLists.txt")
# 9) Lua
check_and_download("https://github.com/lua/lua/archive/refs/tags/v5.4.8.zip" "lua-v5.4.8.zip" "lua-5.4.8" "lapi.c")
# 10) sol2 (header-only C++ bindings for Lua)
check_and_download("https://github.com/ThePhD/sol2/archive/refs/tags/v3.3.0.zip" "sol2-v3.3.0.zip" "sol2-3.3.0" "include/sol/sol.hpp")
# 11) SDL2_mixer
check_and_download("https://github.com/libsdl-org/SDL_mixer/archive/refs/tags/release-2.8.0.zip" "SDL_mixer-release-2.8.0.zip" "SDL_mixer-release-2.8.0" "CMakeLists.txt")
+728
View File
@@ -0,0 +1,728 @@
# cmake/ThirdParty.cmake
include("${CMAKE_CURRENT_LIST_DIR}/FetchDependencies.cmake")
macro(log msg)
message(STATUS "[ThirdParty] ${msg}")
endmacro()
set(BUILD_CONFIGS Debug Release)
# Map MinSizeRel and RelWithDebInfo to Release libs for all imported targets.
# Without this CMake warns that IMPORTED_LOCATION is missing for those configs.
set(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL Release)
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release)
# ===========================================
# 1) ZLIB (zlib131.zip → zlib-1.3.2) - без изменений
# ===========================================
set(ZLIB_SRC_DIR "${THIRDPARTY_DIR}/zlib-1.3.2")
set(ZLIB_BUILD_DIR "${ZLIB_SRC_DIR}/build")
set(ZLIB_INSTALL_DIR "${ZLIB_SRC_DIR}/install")
file(MAKE_DIRECTORY "${ZLIB_BUILD_DIR}")
# проверяем, собран ли уже zlib
set(_have_zlib FALSE)
foreach(candidate
"${ZLIB_INSTALL_DIR}/lib/zlibstatic.lib"
)
if(EXISTS "${candidate}")
set(_have_zlib TRUE)
break()
endif()
endforeach()
if(NOT _have_zlib)
log("Configuring zlib ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
-G "${CMAKE_GENERATOR}"
-S "${ZLIB_SRC_DIR}"
-B "${ZLIB_BUILD_DIR}"
-DCMAKE_INSTALL_PREFIX=${ZLIB_INSTALL_DIR}
RESULT_VARIABLE _zlib_cfg_res
)
if(NOT _zlib_cfg_res EQUAL 0)
message(FATAL_ERROR "zlib configure failed")
endif()
foreach(cfg IN LISTS BUILD_CONFIGS)
log("Building ZLIB (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--build "${ZLIB_BUILD_DIR}" --config ${cfg}
RESULT_VARIABLE _zlib_build_res
)
if(NOT _zlib_build_res EQUAL 0)
message(FATAL_ERROR "ZLIB build failed for configuration ${cfg}")
endif()
log("Installing ZLIB (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--install "${ZLIB_BUILD_DIR}" --config ${cfg}
RESULT_VARIABLE _zlib_inst_res
)
if(NOT _zlib_inst_res EQUAL 0)
message(FATAL_ERROR "ZLIB install failed for configuration ${cfg}")
endif()
endforeach()
endif()
# ИСПРАВЛЕНИЕ: Используем свойства для конкретных конфигураций
add_library(zlib_external_lib UNKNOWN IMPORTED GLOBAL)
set_target_properties(zlib_external_lib PROPERTIES
# Динамическая линковка (если zlib.lib - это импорт-библиотека для zlibd.dll)
#IMPORTED_LOCATION_DEBUG "${ZLIB_INSTALL_DIR}/lib/zlibd.lib"
#IMPORTED_LOCATION_RELEASE "${ZLIB_INSTALL_DIR}/lib/zlib.lib"
# Можно также указать статические библиотеки, если вы хотите их использовать
IMPORTED_LOCATION_DEBUG "${ZLIB_INSTALL_DIR}/lib/zsd.lib"
IMPORTED_LOCATION_RELEASE "${ZLIB_INSTALL_DIR}/lib/zs.lib"
INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INSTALL_DIR}/include"
)
# ===========================================
# 2) SDL2 (release-2.32.10.zip → SDL-release-2.32.10) - без изменений
# ===========================================
set(SDL2_SRC_DIR "${THIRDPARTY_DIR}/SDL-release-2.32.10")
set(SDL2_BUILD_DIR "${SDL2_SRC_DIR}/build")
set(SDL2_INSTALL_DIR "${SDL2_SRC_DIR}/install")
file(MAKE_DIRECTORY "${SDL2_BUILD_DIR}")
set(_have_sdl2 FALSE)
foreach(candidate
"${SDL2_INSTALL_DIR}/lib/SDL2.lib"
"${SDL2_INSTALL_DIR}/lib/SDL2-static.lib"
"${SDL2_INSTALL_DIR}/lib/SDL2d.lib"
)
if(EXISTS "${candidate}")
set(_have_sdl2 TRUE)
break()
endif()
endforeach()
if(NOT _have_sdl2)
log("Configuring SDL2 ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
-G "${CMAKE_GENERATOR}"
-S "${SDL2_SRC_DIR}"
-B "${SDL2_BUILD_DIR}"
-DCMAKE_INSTALL_PREFIX=${SDL2_INSTALL_DIR}
-DCMAKE_PREFIX_PATH=${ZLIB_INSTALL_DIR} # путь к zlib для SDL2
RESULT_VARIABLE _sdl_cfg_res
)
if(NOT _sdl_cfg_res EQUAL 0)
message(FATAL_ERROR "SDL2 configure failed")
endif()
# --- ИЗМЕНЕНИЕ: Цикл по конфигурациям Debug и Release ---
foreach(cfg IN LISTS BUILD_CONFIGS)
log("Building SDL2 (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--build "${SDL2_BUILD_DIR}" --config ${cfg}
RESULT_VARIABLE _sdl_build_res
)
if(NOT _sdl_build_res EQUAL 0)
message(FATAL_ERROR "SDL2 build failed for configuration ${cfg}")
endif()
log("Installing SDL2 (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--install "${SDL2_BUILD_DIR}" --config ${cfg}
RESULT_VARIABLE _sdl_inst_res
)
if(NOT _sdl_inst_res EQUAL 0)
message(FATAL_ERROR "SDL2 install failed for configuration ${cfg}")
endif()
endforeach()
# ------------------------------------------------------
endif()
# ИСПРАВЛЕНИЕ: SDL2: Используем свойства для конкретных конфигураций
add_library(SDL2_external_lib UNKNOWN IMPORTED GLOBAL)
set_target_properties(SDL2_external_lib PROPERTIES
# Динамическая линковка SDL2
IMPORTED_LOCATION_DEBUG "${SDL2_INSTALL_DIR}/lib/SDL2d.lib"
IMPORTED_LOCATION_RELEASE "${SDL2_INSTALL_DIR}/lib/SDL2.lib"
# Оба include-пути: и include, и include/SDL2
INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INSTALL_DIR}/include;${SDL2_INSTALL_DIR}/include/SDL2"
)
# SDL2main (обычно статическая)
add_library(SDL2main_external_lib UNKNOWN IMPORTED GLOBAL)
set_target_properties(SDL2main_external_lib PROPERTIES
# ИСПРАВЛЕНО: Указываем пути для Debug и Release, используя
# соглашение, что Debug имеет суффикс 'd', а Release — нет.
IMPORTED_LOCATION_DEBUG "${SDL2_INSTALL_DIR}/lib/SDL2maind.lib"
IMPORTED_LOCATION_RELEASE "${SDL2_INSTALL_DIR}/lib/SDL2main.lib"
INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INSTALL_DIR}/include"
)
log("-----${SDL2_INSTALL_DIR}/lib/SDL2maind.lib")
# ===========================================
# 3) libpng (v1.6.51.zip → libpng-1.6.51) - без изменений
# ===========================================
set(LIBPNG_SRC_DIR "${THIRDPARTY_DIR}/libpng-1.6.51")
set(LIBPNG_BUILD_DIR "${LIBPNG_SRC_DIR}/build")
set(LIBPNG_INSTALL_DIR "${LIBPNG_SRC_DIR}/install") # на будущее
file(MAKE_DIRECTORY "${LIBPNG_BUILD_DIR}")
# Проверяем, есть ли уже .lib (build/Debug или install/lib)
set(_libpng_candidates
"${LIBPNG_BUILD_DIR}/Debug/libpng16_staticd.lib"
"${LIBPNG_BUILD_DIR}/Release/libpng16_static.lib"
)
set(_have_png FALSE)
foreach(candidate IN LISTS _libpng_candidates)
if(EXISTS "${candidate}")
set(_have_png TRUE)
break()
endif()
endforeach()
if(NOT _have_png)
log("Configuring libpng ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
-G "${CMAKE_GENERATOR}"
-S "${LIBPNG_SRC_DIR}"
-B "${LIBPNG_BUILD_DIR}"
-DCMAKE_INSTALL_PREFIX=${LIBPNG_INSTALL_DIR}
-DCMAKE_PREFIX_PATH=${ZLIB_INSTALL_DIR}
-DZLIB_ROOT=${ZLIB_INSTALL_DIR}
RESULT_VARIABLE _png_cfg_res
)
if(NOT _png_cfg_res EQUAL 0)
message(FATAL_ERROR "libpng configure failed")
endif()
# --- ИЗМЕНЕНИЕ: Цикл по конфигурациям Debug и Release ---
foreach(cfg IN LISTS BUILD_CONFIGS)
log("Building libpng (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--build "${LIBPNG_BUILD_DIR}" --config ${cfg}
RESULT_VARIABLE _png_build_res
)
if(NOT _png_build_res EQUAL 0)
message(FATAL_ERROR "libpng build failed for configuration ${cfg}")
endif()
# Поскольку вы не используете "cmake --install" для libpng,
# здесь нет необходимости в дополнительном шаге установки.
# Файлы .lib будут сгенерированы в подкаталоге ${LIBPNG_BUILD_DIR}/${cfg} (например, build/Debug или build/Release).
endforeach()
# ------------------------------------------------------
endif()
add_library(libpng_external_lib UNKNOWN IMPORTED GLOBAL)
set_target_properties(libpng_external_lib PROPERTIES
# Предполагая, что libpng использует статический вариант
IMPORTED_LOCATION_DEBUG "${LIBPNG_BUILD_DIR}/Debug/libpng16_staticd.lib"
IMPORTED_LOCATION_RELEASE "${LIBPNG_BUILD_DIR}/Release/libpng16_static.lib"
# png.h, pngconf.h – в SRC, pnglibconf.h – в BUILD
INTERFACE_INCLUDE_DIRECTORIES "${LIBPNG_SRC_DIR};${LIBPNG_BUILD_DIR}"
)
# ===========================================
# 4) libzip (v1.11.4.zip → libzip-1.11.4) - НОВАЯ ЗАВИСИМОСТЬ
# ===========================================
set(LIBZIP_SRC_DIR "${THIRDPARTY_DIR}/libzip-1.11.4")
set(LIBZIP_BUILD_DIR "${LIBZIP_SRC_DIR}/build")
#set(LIBZIP_INSTALL_DIR "${LIBZIP_SRC_DIR}/install")
set(LIBZIP_BASE_DIR "${LIBZIP_SRC_DIR}/install")
file(MAKE_DIRECTORY "${LIBZIP_BUILD_DIR}")
# Проверяем, собран ли уже libzip
set(_have_zip FALSE)
foreach(candidate
"${LIBZIP_BASE_DIR}-Debug/lib/zip.lib"
"${LIBZIP_BASE_DIR}-Release/lib/zip.lib"
)
if(EXISTS "${candidate}")
set(_have_zip TRUE)
break()
endif()
endforeach()
if(NOT _have_zip)
foreach(cfg IN LISTS BUILD_CONFIGS)
log("Configuring libzip (${cfg})...")
execute_process(
COMMAND ${CMAKE_COMMAND}
-G "${CMAKE_GENERATOR}"
-S "${LIBZIP_SRC_DIR}"
-B "${LIBZIP_SRC_DIR}/build-${cfg}"
-DCMAKE_INSTALL_PREFIX=${LIBZIP_BASE_DIR}-${cfg}
-DCMAKE_PREFIX_PATH=${ZLIB_INSTALL_DIR}
-DZLIB_ROOT=${ZLIB_INSTALL_DIR}
-DENABLE_COMMONCRYPTO=OFF
-DENABLE_GNUTLS=OFF
-DENABLE_MBEDTLS=OFF
-DENABLE_OPENSSL=OFF
-DENABLE_WINDOWS_CRYPTO=OFF
-DENABLE_FUZZ=OFF
RESULT_VARIABLE _zip_cfg_res
)
if(NOT _zip_cfg_res EQUAL 0)
message(FATAL_ERROR "libzip configure failed")
endif()
log("Building libzip (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND} --build "${LIBZIP_SRC_DIR}/build-${cfg}" --config ${cfg} -v
RESULT_VARIABLE _zip_build_res
)
if(NOT _zip_build_res EQUAL 0)
message(FATAL_ERROR "libzip build failed for configuration ${cfg}")
endif()
log("Installing libzip (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND} --install "${LIBZIP_SRC_DIR}/build-${cfg}" --config ${cfg} -v
RESULT_VARIABLE _zip_inst_res
)
if(NOT _zip_inst_res EQUAL 0)
message(FATAL_ERROR "libzip install failed for configuration ${cfg}")
endif()
endforeach()
endif()
add_library(libzip_external_lib UNKNOWN IMPORTED GLOBAL)
set_target_properties(libzip_external_lib PROPERTIES
IMPORTED_LOCATION_DEBUG "${LIBZIP_BASE_DIR}-Debug/lib/zip.lib" # ИСПРАВЛЕНО
IMPORTED_LOCATION_RELEASE "${LIBZIP_BASE_DIR}-Release/lib/zip.lib" # ИСПРАВЛЕНО
INTERFACE_INCLUDE_DIRECTORIES "$<IF:$<CONFIG:Debug>,${LIBZIP_BASE_DIR}-Debug/include,${LIBZIP_BASE_DIR}-Release/include>"
# libzip требует zlib для линковки
INTERFACE_LINK_LIBRARIES zlib_external_lib
)
# ===========================================
# 5) FreeType (2.14.1) - dependency for SDL_ttf
# ===========================================
set(FREETYPE_SRC_DIR "${THIRDPARTY_DIR}/freetype-2.14.1")
set(FREETYPE_BASE_DIR "${FREETYPE_SRC_DIR}/install")
set(_have_freetype TRUE)
foreach(cfg IN LISTS BUILD_CONFIGS)
if(NOT EXISTS "${FREETYPE_BASE_DIR}-${cfg}/lib/freetype.lib" AND
NOT EXISTS "${FREETYPE_BASE_DIR}-${cfg}/lib/freetyped.lib")
set(_have_freetype FALSE)
endif()
endforeach()
if(NOT _have_freetype)
foreach(cfg IN LISTS BUILD_CONFIGS)
log("Configuring FreeType (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
-G "${CMAKE_GENERATOR}"
-S "${FREETYPE_SRC_DIR}"
-B "${FREETYPE_SRC_DIR}/build-${cfg}"
-DCMAKE_INSTALL_PREFIX=${FREETYPE_BASE_DIR}-${cfg}
-DCMAKE_PREFIX_PATH="${ZLIB_INSTALL_DIR};${LIBPNG_INSTALL_DIR}"
-DCMAKE_DISABLE_FIND_PACKAGE_HarfBuzz=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_BZip2=TRUE
-DFT_DISABLE_BROTLI=ON
-DBUILD_SHARED_LIBS=OFF
RESULT_VARIABLE _ft_cfg_res
)
if(NOT _ft_cfg_res EQUAL 0)
message(FATAL_ERROR "FreeType configure failed for ${cfg}")
endif()
log("Building FreeType (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--build "${FREETYPE_SRC_DIR}/build-${cfg}" --config ${cfg}
RESULT_VARIABLE _ft_build_res
)
if(NOT _ft_build_res EQUAL 0)
message(FATAL_ERROR "FreeType build failed for ${cfg}")
endif()
log("Installing FreeType (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--install "${FREETYPE_SRC_DIR}/build-${cfg}" --config ${cfg}
RESULT_VARIABLE _ft_inst_res
)
if(NOT _ft_inst_res EQUAL 0)
message(FATAL_ERROR "FreeType install failed for ${cfg}")
endif()
endforeach()
endif()
set(_ft_debug_lib "")
foreach(cand
"${FREETYPE_BASE_DIR}-Debug/lib/freetyped.lib"
"${FREETYPE_BASE_DIR}-Debug/lib/freetype.lib"
)
if(EXISTS "${cand}")
set(_ft_debug_lib "${cand}")
break()
endif()
endforeach()
set(_ft_release_lib "")
foreach(cand
"${FREETYPE_BASE_DIR}-Release/lib/freetype.lib"
)
if(EXISTS "${cand}")
set(_ft_release_lib "${cand}")
break()
endif()
endforeach()
if(_ft_debug_lib STREQUAL "" OR _ft_release_lib STREQUAL "")
message(FATAL_ERROR "FreeType libs not found in ${FREETYPE_BASE_DIR}-Debug/Release")
endif()
add_library(freetype_external_lib UNKNOWN IMPORTED GLOBAL)
set_target_properties(freetype_external_lib PROPERTIES
IMPORTED_LOCATION_DEBUG "${_ft_debug_lib}"
IMPORTED_LOCATION_RELEASE "${_ft_release_lib}"
)
target_include_directories(freetype_external_lib INTERFACE
"$<IF:$<CONFIG:Debug>,${FREETYPE_BASE_DIR}-Debug/include/freetype2,${FREETYPE_BASE_DIR}-Release/include/freetype2>"
"$<IF:$<CONFIG:Debug>,${FREETYPE_BASE_DIR}-Debug/include,${FREETYPE_BASE_DIR}-Release/include>"
)
# ===========================================
# 6) SDL_ttf (2.24.0)
# ===========================================
set(SDL2TTF_SRC_DIR "${THIRDPARTY_DIR}/SDL_ttf-release-2.24.0")
set(SDL2TTF_BASE_DIR "${SDL2TTF_SRC_DIR}/install")
set(_have_sdl2ttf TRUE)
foreach(cfg IN LISTS BUILD_CONFIGS)
if(NOT EXISTS "${SDL2TTF_BASE_DIR}-${cfg}/lib/SDL2_ttf.lib" AND
NOT EXISTS "${SDL2TTF_BASE_DIR}-${cfg}/lib/SDL2_ttfd.lib")
set(_have_sdl2ttf FALSE)
endif()
endforeach()
if(NOT _have_sdl2ttf)
foreach(cfg IN LISTS BUILD_CONFIGS)
if(cfg STREQUAL "Debug")
set(_SDL2_LIB "${SDL2_INSTALL_DIR}/lib/SDL2d.lib")
else()
set(_SDL2_LIB "${SDL2_INSTALL_DIR}/lib/SDL2.lib")
endif()
set(_FT_PREFIX "${FREETYPE_BASE_DIR}-${cfg}")
set(_FT_LIB "")
foreach(cand
"${_FT_PREFIX}/lib/freetyped.lib"
"${_FT_PREFIX}/lib/freetype.lib"
)
if(EXISTS "${cand}")
set(_FT_LIB "${cand}")
break()
endif()
endforeach()
if(_FT_LIB STREQUAL "")
message(FATAL_ERROR "FreeType library not found for ${cfg}")
endif()
log("Configuring SDL_ttf (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
-G "${CMAKE_GENERATOR}"
-S "${SDL2TTF_SRC_DIR}"
-B "${SDL2TTF_SRC_DIR}/build-${cfg}"
-DCMAKE_INSTALL_PREFIX=${SDL2TTF_BASE_DIR}-${cfg}
-DCMAKE_PREFIX_PATH=${_FT_PREFIX};${SDL2_INSTALL_DIR}
-DSDL2_LIBRARY=${_SDL2_LIB}
-DSDL2_INCLUDE_DIR=${SDL2_INSTALL_DIR}/include/SDL2
-DFREETYPE_LIBRARY=${_FT_LIB}
-DFREETYPE_INCLUDE_DIR=${_FT_PREFIX}/include/freetype2
-DFREETYPE_INCLUDE_DIRS=${_FT_PREFIX}/include/freetype2
-DSDL2TTF_VENDORED=OFF
-DSDL2TTF_SAMPLES=OFF
RESULT_VARIABLE _ttf_cfg_res
)
if(NOT _ttf_cfg_res EQUAL 0)
message(FATAL_ERROR "SDL_ttf configure failed for ${cfg}")
endif()
log("Building SDL_ttf (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--build "${SDL2TTF_SRC_DIR}/build-${cfg}" --config ${cfg}
RESULT_VARIABLE _ttf_build_res
)
if(NOT _ttf_build_res EQUAL 0)
message(FATAL_ERROR "SDL_ttf build failed for ${cfg}")
endif()
log("Installing SDL_ttf (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--install "${SDL2TTF_SRC_DIR}/build-${cfg}" --config ${cfg}
RESULT_VARIABLE _ttf_inst_res
)
if(NOT _ttf_inst_res EQUAL 0)
message(FATAL_ERROR "SDL_ttf install failed for ${cfg}")
endif()
endforeach()
endif()
set(_ttf_debug_lib "")
foreach(cand
"${SDL2TTF_BASE_DIR}-Debug/lib/SDL2_ttfd.lib"
"${SDL2TTF_BASE_DIR}-Debug/lib/SDL2_ttf.lib"
)
if(EXISTS "${cand}")
set(_ttf_debug_lib "${cand}")
break()
endif()
endforeach()
set(_ttf_release_lib "")
foreach(cand
"${SDL2TTF_BASE_DIR}-Release/lib/SDL2_ttf.lib"
)
if(EXISTS "${cand}")
set(_ttf_release_lib "${cand}")
break()
endif()
endforeach()
if(_ttf_debug_lib STREQUAL "" OR _ttf_release_lib STREQUAL "")
message(FATAL_ERROR "SDL_ttf libs not found in install-Debug / install-Release")
endif()
add_library(SDL2_ttf_external_lib UNKNOWN IMPORTED GLOBAL)
set_target_properties(SDL2_ttf_external_lib PROPERTIES
IMPORTED_LOCATION_DEBUG "${_ttf_debug_lib}"
IMPORTED_LOCATION_RELEASE "${_ttf_release_lib}"
INTERFACE_INCLUDE_DIRECTORIES
"$<IF:$<CONFIG:Debug>,${SDL2TTF_BASE_DIR}-Debug/include,${SDL2TTF_BASE_DIR}-Release/include>;$<IF:$<CONFIG:Debug>,${SDL2TTF_BASE_DIR}-Debug/include/SDL2,${SDL2TTF_BASE_DIR}-Release/include/SDL2>"
INTERFACE_LINK_LIBRARIES
"SDL2_external_lib;freetype_external_lib"
)
# ===========================================
# 7) Eigen (5.0.0.zip → eigen-5.0.0) - HEADER-ONLY
# ===========================================
set(EIGEN_SRC_DIR "${THIRDPARTY_DIR}/eigen-5.0.0")
if(NOT TARGET eigen_external_lib)
add_library(eigen_external_lib INTERFACE)
target_include_directories(eigen_external_lib INTERFACE "${EIGEN_SRC_DIR}")
endif()
# ===========================================
# 8) Boost (1.90.0) - HEADER-ONLY
# ===========================================
set(BOOST_VERSION "1.90.0")
set(BOOST_ARCHIVE_NAME "boost_1_90_0.zip")
set(BOOST_ARCHIVE "${THIRDPARTY_DIR}/${BOOST_ARCHIVE_NAME}")
# Внутри архива папка называется boost_1_90_0
set(BOOST_SRC_DIR "${THIRDPARTY_DIR}/boost_1_90_0")
if(NOT TARGET boost_external_lib)
add_library(boost_external_lib INTERFACE)
# Boost заголовки находятся непосредственно в корне распакованной папки
target_include_directories(boost_external_lib INTERFACE "${BOOST_SRC_DIR}")
endif()
# ===========================================
# 9) Lua (5.5.0) - embedded scripting language
# ===========================================
set(LUA_SRC_DIR "${THIRDPARTY_DIR}/lua-5.4.8")
if(NOT TARGET lua_static)
file(GLOB LUA_SOURCES "${LUA_SRC_DIR}/*.c")
# Exclude the standalone interpreter, compiler, and unity-build wrapper.
# onelua.c #includes all other .c files — compiling it alongside them
# causes every symbol to be defined twice.
list(REMOVE_ITEM LUA_SOURCES
"${LUA_SRC_DIR}/lua.c"
"${LUA_SRC_DIR}/luac.c"
"${LUA_SRC_DIR}/onelua.c"
)
add_library(lua_static STATIC ${LUA_SOURCES})
target_include_directories(lua_static PUBLIC "${LUA_SRC_DIR}")
target_compile_definitions(lua_static PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
# ===========================================
# 10) sol2 (3.3.0) - header-only C++ bindings for Lua
# ===========================================
set(SOL2_SRC_DIR "${THIRDPARTY_DIR}/sol2-3.3.0")
# Apply patch for Clang/Emscripten compatibility in optional<T&>::emplace().
# The sentinel file prevents re-applying on subsequent cmake runs.
set(_sol2_sentinel "${SOL2_SRC_DIR}/.patched")
if(NOT EXISTS "${_sol2_sentinel}")
find_package(Git QUIET)
if(GIT_FOUND)
execute_process(
COMMAND ${GIT_EXECUTABLE} apply --ignore-whitespace
"${CMAKE_CURRENT_LIST_DIR}/patches/sol2-3.3.0-clang-optional.patch"
WORKING_DIRECTORY "${SOL2_SRC_DIR}"
RESULT_VARIABLE _sol2_patch_res
)
if(_sol2_patch_res EQUAL 0)
file(WRITE "${_sol2_sentinel}" "patched\n")
message(STATUS "Applied sol2 Clang optional patch")
else()
message(WARNING "sol2 patch failed (exit ${_sol2_patch_res}) — Clang/Emscripten builds may not compile")
endif()
else()
message(WARNING "Git not found — cannot apply sol2 patch automatically. "
"Apply cmake/patches/sol2-3.3.0-clang-optional.patch manually.")
endif()
endif()
if(NOT TARGET sol2_external_lib)
add_library(sol2_external_lib INTERFACE)
target_include_directories(sol2_external_lib INTERFACE "${SOL2_SRC_DIR}/include")
target_link_libraries(sol2_external_lib INTERFACE lua_static)
endif()
# ===========================================
# 11) SDL2_mixer (2.8.0) – сборка из исходников
# ===========================================
set(SDL2MIXER_SRC_DIR "${THIRDPARTY_DIR}/SDL_mixer-release-2.8.0")
set(SDL2MIXER_BASE_DIR "${SDL2MIXER_SRC_DIR}/install")
set(SDL2MIXER_BASE_DIR "${SDL2MIXER_BASE_DIR}" CACHE PATH "SDL2_mixer install base directory" FORCE)
set(_have_sdl2mixer TRUE)
foreach(cfg IN LISTS BUILD_CONFIGS)
if(NOT EXISTS "${SDL2MIXER_BASE_DIR}-${cfg}/lib/SDL2_mixer.lib" AND
NOT EXISTS "${SDL2MIXER_BASE_DIR}-${cfg}/lib/SDL2_mixerd.lib")
set(_have_sdl2mixer FALSE)
endif()
endforeach()
if(NOT _have_sdl2mixer)
foreach(cfg IN LISTS BUILD_CONFIGS)
if(cfg STREQUAL "Debug")
set(_SDL2_LIB "${SDL2_INSTALL_DIR}/lib/SDL2d.lib")
else()
set(_SDL2_LIB "${SDL2_INSTALL_DIR}/lib/SDL2.lib")
endif()
log("Configuring SDL2_mixer (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
-G "${CMAKE_GENERATOR}"
-S "${SDL2MIXER_SRC_DIR}"
-B "${SDL2MIXER_SRC_DIR}/build-${cfg}"
-DCMAKE_INSTALL_PREFIX=${SDL2MIXER_BASE_DIR}-${cfg}
-DCMAKE_PREFIX_PATH=${SDL2_INSTALL_DIR}
-DSDL2_LIBRARY=${_SDL2_LIB}
-DSDL2_INCLUDE_DIR=${SDL2_INSTALL_DIR}/include/SDL2
-DSDL2MIXER_DEPS_SHARED=OFF
-DSDL2MIXER_VENDORED=ON
-DSDL2MIXER_SAMPLES=OFF
-DSDL2MIXER_MUSIC_CMD=OFF
-DSDL2MIXER_MOD=OFF
-DSDL2MIXER_MIDI=OFF
-DSDL2MIXER_OPUS=OFF
-DSDL2MIXER_WAVPACK=OFF
-DSDL2MIXER_MP3_MPG123=OFF
-DSDL2MIXER_MP3_DRMP3=ON
-DSDL2MIXER_FLAC_DRFLAC=ON
-DSDL2MIXER_OGG_STB=ON
-DCMAKE_DISABLE_FIND_PACKAGE_OGG=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_Vorbis=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_FLAC=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_MPG123=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_LibModPlug=TRUE
-DCMAKE_DISABLE_FIND_PACKAGE_FluidLite=TRUE
RESULT_VARIABLE _mixer_cfg_res
OUTPUT_VARIABLE _mixer_cfg_out
ERROR_VARIABLE _mixer_cfg_err
)
if(NOT _mixer_cfg_res EQUAL 0)
message(STATUS "SDL2_mixer configure stdout: ${_mixer_cfg_out}")
message(STATUS "SDL2_mixer configure stderr: ${_mixer_cfg_err}")
message(FATAL_ERROR "SDL2_mixer configure failed for ${cfg}")
endif()
log("Building SDL2_mixer (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--build "${SDL2MIXER_SRC_DIR}/build-${cfg}" --config ${cfg}
RESULT_VARIABLE _mixer_build_res
)
if(NOT _mixer_build_res EQUAL 0)
message(FATAL_ERROR "SDL2_mixer build failed for ${cfg}")
endif()
log("Installing SDL2_mixer (${cfg}) ...")
execute_process(
COMMAND ${CMAKE_COMMAND}
--install "${SDL2MIXER_SRC_DIR}/build-${cfg}" --config ${cfg}
RESULT_VARIABLE _mixer_inst_res
)
if(NOT _mixer_inst_res EQUAL 0)
message(FATAL_ERROR "SDL2_mixer install failed for ${cfg}")
endif()
endforeach()
endif()
set(_mixer_debug_lib "")
foreach(cand
"${SDL2MIXER_BASE_DIR}-Debug/lib/SDL2_mixerd.lib"
"${SDL2MIXER_BASE_DIR}-Debug/lib/SDL2_mixer.lib"
)
if(EXISTS "${cand}")
set(_mixer_debug_lib "${cand}")
break()
endif()
endforeach()
set(_mixer_release_lib "")
foreach(cand
"${SDL2MIXER_BASE_DIR}-Release/lib/SDL2_mixer.lib"
)
if(EXISTS "${cand}")
set(_mixer_release_lib "${cand}")
break()
endif()
endforeach()
if(_mixer_debug_lib STREQUAL "" OR _mixer_release_lib STREQUAL "")
message(FATAL_ERROR "SDL2_mixer libs not found in ${SDL2MIXER_BASE_DIR}-Debug/Release")
endif()
add_library(SDL2_mixer_external_lib UNKNOWN IMPORTED GLOBAL)
set_target_properties(SDL2_mixer_external_lib PROPERTIES
IMPORTED_LOCATION_DEBUG "${_mixer_debug_lib}"
IMPORTED_LOCATION_RELEASE "${_mixer_release_lib}"
INTERFACE_INCLUDE_DIRECTORIES
"$<IF:$<CONFIG:Debug>,${SDL2MIXER_BASE_DIR}-Debug/include,${SDL2MIXER_BASE_DIR}-Release/include>"
INTERFACE_LINK_LIBRARIES
"SDL2_external_lib"
)
@@ -0,0 +1,14 @@
--- a/include/sol/optional_implementation.hpp
+++ b/include/sol/optional_implementation.hpp
@@ -2189,7 +2189,10 @@
template <class... Args>
T& emplace(Args&&... args) noexcept {
static_assert(std::is_constructible<T, Args&&...>::value, "T must be constructible with Args");
*this = nullopt;
- this->construct(std::forward<Args>(args)...);
+ // Reference specialization stores a pointer; set it directly.
+ // construct() only exists in the non-reference specialization.
+ m_value = std::addressof(std::forward<Args>(args)...);
+ return *m_value;
}
+312
View File
@@ -0,0 +1,312 @@
#!/usr/bin/env python3
"""
Convert a text-based bone animation file to the BSAF binary format.
Usage:
python convert_anim_to_binary.py <input.txt> <output.bin>
Binary format (BSAF v1) -- all values little-endian:
HEADER
4 bytes magic "BSAF"
uint32 version (1)
BONES
uint32 numBones
per bone:
3 x float boneStartWorld (from HEAD_LOCAL)
float boneLength
9 x float 3x3 rotation matrix (row-major)
int32 parentIndex (-1 if none)
uint32 numChildren
numChildren x int32 childIndices
VERTICES
uint32 numVertices
numVertices x 3 x float positions
UV COORDINATES
uint32 numFaces
numFaces x 6 x float 3 UV pairs per face (u0,v0,u1,v1,u2,v2)
NORMALS
numVertices x 3 x float normals
TRIANGLES
uint32 numTriangles
numTriangles x 3 x int32 vertex indices
VERTEX WEIGHTS
per vertex (numVertices):
uint32 numGroups
numGroups x (int32 boneIndex, float weight)
ANIMATION KEYFRAMES
uint32 numKeyframes
per keyframe:
int32 frameNumber
per bone (numBones, in index order 0..N-1):
3 x float location
16 x float 4x4 matrix (row-major)
"""
import struct
import re
import sys
def parse_floats(line):
return [float(x) for x in re.findall(r'[-]?\d+\.\d+', line)]
def parse_first_int(line):
m = re.search(r'\d+', line)
if m:
return int(m.group())
raise ValueError(f"No integer found in: {line}")
def parse_children(line):
return re.findall(r"'([^']+)'", line)
def convert(input_path, output_path):
with open(input_path, 'r', encoding='utf-8', errors='replace') as f:
lines = f.readlines()
idx = 0
def next_line():
nonlocal idx
line = lines[idx].rstrip()
idx += 1
return line
# --- Skip armature matrix (5 lines) ---
for _ in range(5):
next_line()
# --- Bone count ---
line = next_line() # "=== Armature Bones: 65"
num_bones = parse_first_int(line)
bone_names = []
bones = []
bone_parent_names = []
bone_children_names = []
for _ in range(num_bones):
bone = {}
# "Bone: mixamorig:Hips"
line = next_line()
bone_name = line[6:]
bone_names.append(bone_name)
# " HEAD_LOCAL: <Vector (x, y, z)>"
line = next_line()
bone['head'] = parse_floats(line)[:3]
# " TAIL_LOCAL: ..." -- skip
next_line()
# " Length: 0.123"
line = next_line()
bone['length'] = parse_floats(line)[0]
# 3x3 matrix (3 rows)
mat = []
for _ in range(3):
mat.extend(parse_floats(next_line()))
bone['matrix_3x3'] = mat
# " Parent: None" or " Parent: boneName"
line = next_line()
if line == " Parent: None":
bone_parent_names.append(None)
else:
bone_parent_names.append(line[10:])
# " Children: ['a', 'b'] or []"
line = next_line()
bone_children_names.append(parse_children(line))
bones.append(bone)
# Build name -> index map
name_to_idx = {name: i for i, name in enumerate(bone_names)}
# Resolve parent / child indices
for i in range(num_bones):
if bone_parent_names[i] is None:
bones[i]['parent'] = -1
else:
bones[i]['parent'] = name_to_idx[bone_parent_names[i]]
bones[i]['children'] = [name_to_idx[c] for c in bone_children_names[i]]
# --- Vertices ---
line = next_line() # "===Vertices: 5140"
num_vertices = parse_first_int(line)
vertices = []
for _ in range(num_vertices):
vertices.append(parse_floats(next_line())[:3])
# --- UV Coordinates ---
next_line() # "===UV Coordinates:"
line = next_line() # "Face count: 8602"
num_faces = parse_first_int(line)
uvs = []
for _ in range(num_faces):
next_line() # "Face N"
next_line() # "UV Count: 3"
face_uvs = []
for _ in range(3):
face_uvs.extend(parse_floats(next_line())[:2])
uvs.append(face_uvs) # 6 floats
# --- Normals ---
next_line() # "===Normals:"
normals = []
for _ in range(num_vertices):
normals.append(parse_floats(next_line())[:3])
# --- Triangles ---
line = next_line() # "===Triangles: 8602"
num_triangles = parse_first_int(line)
triangles = []
for _ in range(num_triangles):
line = next_line()
ints = [int(x) for x in re.findall(r'[-]?\d+', line)]
triangles.append(ints[:3])
# --- Vertex Weights ---
next_line() # "=== Vertex Weights ..."
vertex_weights = []
for _ in range(num_vertices):
next_line() # "Vertex N:"
line = next_line() # "Vertex groups: 2"
num_groups = parse_first_int(line)
groups = []
for _ in range(num_groups):
line = next_line()
m = re.search(r"'([^']+)'.*?([-]?\d+\.\d+)", line)
bone_name = m.group(1)
weight = float(m.group(2))
groups.append((name_to_idx[bone_name], weight))
vertex_weights.append(groups)
# --- Animation Keyframes ---
next_line() # "=== Animation Keyframes ==="
next_line() # "=== Bone Transforms per Keyframe ==="
line = next_line() # "Keyframes: 32"
num_keyframes = parse_first_int(line)
keyframes = []
for _ in range(num_keyframes):
line = next_line() # "Frame: 0"
frame_number = parse_first_int(line)
bone_data = {}
for _ in range(num_bones):
line = next_line() # " Bone: mixamorig:Hips"
bone_name = line.strip()
if bone_name.startswith("Bone: "):
bone_name = bone_name[6:]
bone_idx = name_to_idx[bone_name]
# Location
location = parse_floats(next_line())[:3]
# Rotation (skip)
next_line()
# " Matrix:" (skip header)
next_line()
# 4 rows of 4 floats
matrix = []
for _ in range(4):
matrix.extend(parse_floats(next_line()))
bone_data[bone_idx] = {
'location': location,
'matrix': matrix,
}
keyframes.append((frame_number, bone_data))
# ================================================================
# Write binary file
# ================================================================
with open(output_path, 'wb') as out:
# Header
out.write(b'BSAF')
out.write(struct.pack('<I', 1))
# Bones
out.write(struct.pack('<I', num_bones))
for i in range(num_bones):
b = bones[i]
out.write(struct.pack('<3f', *b['head']))
out.write(struct.pack('<f', b['length']))
out.write(struct.pack('<9f', *b['matrix_3x3']))
out.write(struct.pack('<i', b['parent']))
out.write(struct.pack('<I', len(b['children'])))
for c in b['children']:
out.write(struct.pack('<i', c))
# Vertices
out.write(struct.pack('<I', num_vertices))
for v in vertices:
out.write(struct.pack('<3f', *v))
# UV Coordinates
out.write(struct.pack('<I', num_faces))
for uv in uvs:
out.write(struct.pack('<6f', *uv))
# Normals
for n in normals:
out.write(struct.pack('<3f', *n))
# Triangles
out.write(struct.pack('<I', num_triangles))
for t in triangles:
out.write(struct.pack('<3i', *t))
# Vertex Weights
for vw in vertex_weights:
out.write(struct.pack('<I', len(vw)))
for bone_idx, weight in vw:
out.write(struct.pack('<if', bone_idx, weight))
# Animation Keyframes
out.write(struct.pack('<I', num_keyframes))
for frame_num, bone_data in keyframes:
out.write(struct.pack('<i', frame_num))
for i in range(num_bones):
bd = bone_data[i]
out.write(struct.pack('<3f', *bd['location']))
out.write(struct.pack('<16f', *bd['matrix']))
input_size = sum(len(l) for l in lines)
import os
output_size = os.path.getsize(output_path)
print(f"Converted: {input_path} ({input_size:,} bytes text) -> {output_path} ({output_size:,} bytes binary)")
print(f" Bones: {num_bones}, Vertices: {num_vertices}, Faces: {num_faces}, "
f"Triangles: {num_triangles}, Keyframes: {num_keyframes}")
if __name__ == '__main__':
if len(sys.argv) != 3:
print(f"Usage: {sys.argv[0]} <input.txt> <output.bin>")
sys.exit(1)
convert(sys.argv[1], sys.argv[2])
+359
View File
@@ -0,0 +1,359 @@
#!/usr/bin/env python3
"""
Convert a text-based multi-mesh bone animation file to the BSMF binary format.
Usage:
python convert_anim_to_binary_new.py <input.txt> <output.bin>
Binary format (BSMF v1) -- all values little-endian:
HEADER
4 bytes magic "BSMF"
uint32 version (1)
ARMATURE MATRIX
16 x float 4x4 matrix (row-major)
BONES
uint32 numBones
per bone:
3 x float boneStartWorld (from HEAD_LOCAL)
float boneLength
9 x float 3x3 rotation matrix (row-major)
int32 parentIndex (-1 if none)
uint32 numChildren
numChildren x int32 childIndices
MESHES
uint32 numMeshes
per mesh:
uint32 nameLength
nameLength x char meshName (UTF-8, no null terminator)
VERTICES
uint32 numVertices
numVertices x 3 x float positions
UV COORDINATES
uint32 numFaces
numFaces x 6 x float 3 UV pairs per face (u0,v0,u1,v1,u2,v2)
NORMALS
numVertices x 3 x float normals
TRIANGLES
uint32 numTriangles
numTriangles x 3 x int32 vertex indices
VERTEX WEIGHTS
per vertex (numVertices):
uint32 numGroups
numGroups x (int32 boneIndex, float weight)
ANIMATION KEYFRAMES
uint32 numKeyframes
per keyframe:
int32 frameNumber
per bone (numBones, in index order 0..N-1):
3 x float location
16 x float 4x4 matrix (row-major)
"""
import struct
import re
import sys
def parse_floats(line):
return [float(x) for x in re.findall(r'[-]?\d+\.\d+', line)]
def parse_first_int(line):
m = re.search(r'\d+', line)
if m:
return int(m.group())
raise ValueError(f"No integer found in: {line}")
def parse_children(line):
return re.findall(r"'([^']+)'", line)
def convert(input_path, output_path):
with open(input_path, 'r', encoding='utf-8', errors='replace') as f:
lines = f.readlines()
idx = 0
def next_line():
nonlocal idx
line = lines[idx].rstrip()
idx += 1
return line
# --- Armature matrix header + 4 rows ---
next_line() # "=== Armature Matrix ==="
armature_matrix = []
for _ in range(4):
armature_matrix.extend(parse_floats(next_line())[:4])
# --- Bone count ---
line = next_line() # "=== Armature Bones: 65"
num_bones = parse_first_int(line)
bone_names = []
bones = []
bone_parent_names = []
bone_children_names = []
for _ in range(num_bones):
bone = {}
# "Bone: mixamorig:Hips"
line = next_line()
bone_name = line[6:]
bone_names.append(bone_name)
# " HEAD_LOCAL: <Vector (x, y, z)>"
line = next_line()
bone['head'] = parse_floats(line)[:3]
# " TAIL_LOCAL: ..." -- skip
next_line()
# " Length: 0.123"
line = next_line()
bone['length'] = parse_floats(line)[0]
# 3x3 matrix (3 rows)
mat = []
for _ in range(3):
mat.extend(parse_floats(next_line()))
bone['matrix_3x3'] = mat
# " Parent: None" or " Parent: boneName"
line = next_line()
if line == " Parent: None":
bone_parent_names.append(None)
else:
bone_parent_names.append(line[10:])
# " Children: ['a', 'b'] or []"
line = next_line()
bone_children_names.append(parse_children(line))
bones.append(bone)
# Build name -> index map
name_to_idx = {name: i for i, name in enumerate(bone_names)}
# Resolve parent / child indices
for i in range(num_bones):
if bone_parent_names[i] is None:
bones[i]['parent'] = -1
else:
bones[i]['parent'] = name_to_idx[bone_parent_names[i]]
bones[i]['children'] = [name_to_idx[c] for c in bone_children_names[i]]
# --- Multi-mesh header ---
line = next_line() # "=== TOTAL MESHES TO EXPORT: 7 ==="
num_meshes = parse_first_int(line)
meshes = []
for _ in range(num_meshes):
# "=== Mesh Object: Name ==="
line = next_line()
m = re.match(r"===\s*Mesh Object:\s*(.+?)\s*===$", line)
if not m:
raise ValueError(f"Invalid mesh header: {line}")
mesh_name = m.group(1)
# --- Vertices ---
line = next_line() # "===Vertices: N"
num_vertices = parse_first_int(line)
vertices = []
for _ in range(num_vertices):
vertices.append(parse_floats(next_line())[:3])
# --- UV Coordinates ---
next_line() # "===UV Coordinates:"
line = next_line() # "Face count: M"
num_faces = parse_first_int(line)
uvs = []
for _ in range(num_faces):
next_line() # "Face N"
next_line() # "UV Count: 3"
face_uvs = []
for _ in range(3):
face_uvs.extend(parse_floats(next_line())[:2])
uvs.append(face_uvs)
# --- Normals ---
next_line() # "===Normals:"
normals = []
for _ in range(num_vertices):
normals.append(parse_floats(next_line())[:3])
# --- Triangles ---
line = next_line() # "===Triangles: M"
num_triangles = parse_first_int(line)
triangles = []
for _ in range(num_triangles):
line = next_line()
ints = [int(x) for x in re.findall(r'[-]?\d+', line)]
triangles.append(ints[:3])
# --- Vertex Weights ---
next_line() # "=== Vertex Weights (Max 5 bones per vertex) ==="
vertex_weights = []
for _ in range(num_vertices):
next_line() # "Vertex N:"
line = next_line() # "Vertex groups: K"
num_groups = parse_first_int(line)
groups = []
for _ in range(num_groups):
line = next_line()
m = re.search(r"'([^']+)'.*?([-]?\d+\.\d+)", line)
bone_name = m.group(1)
weight = float(m.group(2))
groups.append((name_to_idx[bone_name], weight))
vertex_weights.append(groups)
meshes.append({
'name': mesh_name,
'num_vertices': num_vertices,
'vertices': vertices,
'num_faces': num_faces,
'uvs': uvs,
'normals': normals,
'num_triangles': num_triangles,
'triangles': triangles,
'vertex_weights': vertex_weights,
})
# --- Animation Keyframes ---
next_line() # "=== Animation Keyframes ==="
next_line() # "=== Bone Transforms per Keyframe ==="
line = next_line() # "Keyframes: N"
num_keyframes = parse_first_int(line)
keyframes = []
for _ in range(num_keyframes):
line = next_line() # "Frame: N"
frame_number = parse_first_int(line)
bone_data = {}
for _ in range(num_bones):
line = next_line() # " Bone: mixamorig:Hips"
bone_name = line.strip()
if bone_name.startswith("Bone: "):
bone_name = bone_name[6:]
bone_idx = name_to_idx[bone_name]
# Location
location = parse_floats(next_line())[:3]
# Rotation (skip)
next_line()
# " Matrix:" (skip header)
next_line()
# 4 rows of 4 floats
matrix = []
for _ in range(4):
matrix.extend(parse_floats(next_line()))
bone_data[bone_idx] = {
'location': location,
'matrix': matrix,
}
keyframes.append((frame_number, bone_data))
# ================================================================
# Write binary file
# ================================================================
with open(output_path, 'wb') as out:
# Header
out.write(b'BSMF')
out.write(struct.pack('<I', 1))
# Armature matrix (16 floats, row-major)
out.write(struct.pack('<16f', *armature_matrix))
# Bones
out.write(struct.pack('<I', num_bones))
for i in range(num_bones):
b = bones[i]
out.write(struct.pack('<3f', *b['head']))
out.write(struct.pack('<f', b['length']))
out.write(struct.pack('<9f', *b['matrix_3x3']))
out.write(struct.pack('<i', b['parent']))
out.write(struct.pack('<I', len(b['children'])))
for c in b['children']:
out.write(struct.pack('<i', c))
# Meshes
out.write(struct.pack('<I', num_meshes))
for md in meshes:
name_bytes = md['name'].encode('utf-8')
out.write(struct.pack('<I', len(name_bytes)))
out.write(name_bytes)
# Vertices
out.write(struct.pack('<I', md['num_vertices']))
for v in md['vertices']:
out.write(struct.pack('<3f', *v))
# UV Coordinates
out.write(struct.pack('<I', md['num_faces']))
for uv in md['uvs']:
out.write(struct.pack('<6f', *uv))
# Normals
for n in md['normals']:
out.write(struct.pack('<3f', *n))
# Triangles
out.write(struct.pack('<I', md['num_triangles']))
for t in md['triangles']:
out.write(struct.pack('<3i', *t))
# Vertex weights
for vw in md['vertex_weights']:
out.write(struct.pack('<I', len(vw)))
for bone_idx, weight in vw:
out.write(struct.pack('<if', bone_idx, weight))
# Animation Keyframes
out.write(struct.pack('<I', num_keyframes))
for frame_num, bone_data in keyframes:
out.write(struct.pack('<i', frame_num))
for i in range(num_bones):
bd = bone_data[i]
out.write(struct.pack('<3f', *bd['location']))
out.write(struct.pack('<16f', *bd['matrix']))
input_size = sum(len(l) for l in lines)
import os
output_size = os.path.getsize(output_path)
print(f"Converted: {input_path} ({input_size:,} bytes text) -> {output_path} ({output_size:,} bytes binary)")
print(f" Bones: {num_bones}, Meshes: {num_meshes}, Keyframes: {num_keyframes}")
for md in meshes:
print(f" - {md['name']}: {md['num_vertices']} verts, "
f"{md['num_faces']} faces, {md['num_triangles']} tris")
if __name__ == '__main__':
if len(sys.argv) != 3:
print(f"Usage: {sys.argv[0]} <input.txt> <output.bin>")
sys.exit(1)
convert(sys.argv[1], sys.argv[2])
-88
View File
@@ -1,88 +0,0 @@
#include "Game.h"
#include "Environment.h"
#include <iostream>
ZL::Game game;
void MainLoop() {
game.update();
}
int main(int argc, char* argv[]) {
try
{
constexpr int CONST_WIDTH = 1280;
constexpr int CONST_HEIGHT = 720;
ZL::Environment::width = CONST_WIDTH;
ZL::Environment::height = CONST_HEIGHT;
#ifdef EMSCRIPTEN
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
std::cerr << "SDL_Init failed: " << SDL_GetError() << std::endl;
return 1;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_Window* win = SDL_CreateWindow("Space Ship Game",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
CONST_WIDTH, CONST_HEIGHT,
SDL_WINDOW_OPENGL);
if (!win) {
std::cerr << "SDL_CreateWindow failed: " << SDL_GetError() << std::endl;
return 1;
}
SDL_GLContext glContext = SDL_GL_CreateContext(win);
if (!glContext) {
std::cerr << "SDL_GL_CreateContext failed: " << SDL_GetError() << std::endl;
return 1;
}
// Привязка контекста к окну — важно!
SDL_GL_MakeCurrent(win, glContext);
ZL::Environment::window = win;
#else
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
SDL_Log("SDL init failed: %s", SDL_GetError());
return 1;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
ZL::Environment::window = SDL_CreateWindow(
"Space Ship Game",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
CONST_WIDTH, CONST_HEIGHT,
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
);
SDL_GLContext ctx = SDL_GL_CreateContext(ZL::Environment::window);
SDL_GL_MakeCurrent(ZL::Environment::window, ctx);
#endif
game.setup();
#ifdef EMSCRIPTEN
emscripten_set_main_loop(MainLoop, 0, 1);
#else
while (!game.shouldExit()) {
game.update();
SDL_Delay(2);
}
#endif
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl;
}
return 0;
}
+65
View File
@@ -0,0 +1,65 @@
# Built application files
*.apk
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Gradle files
.gradle/
build/
.idea/
# Local configuration file (sdk path, etc)
local.properties
# CMake build files
CMakeFiles/
cmake_install.cmake
CMakeCache.txt
Makefile
*.cmake
# Android Studio
*.iml
.idea/
.cxx/
.gradle/
.DS_Store
/captures/
.externalNativeBuild/
# NDK
.obj/
*.o
*.so
# SDL2 (если используешь)
libs/
obj/
# Временные файлы
*.swp
*.swo
*~
.thumbs.db
desktop.ini
# Проектные специфичные
app/build/
*.log
app/jni/libpng
app/jni/SDL
app/jni/zlib
app/src/main/assets/resources
+1
View File
@@ -0,0 +1 @@
### Перед запуском в папке ```app/jni/```(рядом с src) нужно создать три папки с исходниками библиотек ```libpng```, ```SDL```, ```zlib```
+74
View File
@@ -0,0 +1,74 @@
def buildAsLibrary = project.hasProperty('BUILD_AS_LIBRARY');
def buildAsApplication = !buildAsLibrary
if (buildAsApplication) {
apply plugin: 'com.android.application'
}
else {
apply plugin: 'com.android.library'
}
android {
if (buildAsApplication) {
namespace "org.libsdl.app"
}
compileSdkVersion 34
defaultConfig {
minSdkVersion 19
targetSdkVersion 34
versionCode 1
versionName "1.0"
externalNativeBuild {
/*ndkBuild {
arguments "APP_PLATFORM=android-19"
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}*/
cmake {
arguments "-DANDROID_APP_PLATFORM=android-19", "-DANDROID_STL=c++_static"
cppFlags "-std=c++11 -frtti -fexceptions"
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
applicationVariants.all { variant ->
tasks["merge${variant.name.capitalize()}Assets"]
.dependsOn("externalNativeBuild${variant.name.capitalize()}")
}
if (!project.hasProperty('EXCLUDE_NATIVE_LIBS')) {
sourceSets.main {
jniLibs.srcDir 'libs'
}
externalNativeBuild {
/*ndkBuild {
path 'jni/Android.mk'
}*/
cmake {
path 'jni/CMakeLists.txt'
version '3.22.1'
}
}
}
lint {
abortOnError false
}
if (buildAsLibrary) {
libraryVariants.all { variant ->
variant.outputs.all { output ->
if (output.outputFileName != null && output.outputFileName.endsWith(".aar")) {
output.outputFileName = "org.libsdl.app.aar"
}
}
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
}
+68
View File
@@ -0,0 +1,68 @@
cmake_minimum_required(VERSION 3.6)
project(GAME)
if(POLICY CMP0079)
cmake_policy(SET CMP0079 NEW)
endif()
# ==============================================================================
# 1. АВТО-ЗАГРУЗКА ИСХОДНИКОВ
# ==============================================================================
# Путь относительно текущего файла к папке с общими скриптами
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/FetchDependencies.cmake")
# Теперь мы уверены, что папка thirdparty заполнена исходниками.
# Определяем базовый путь для удобства:
set(TP_ROOT "${THIRDPARTY_DIR}")
# ==============================================================================
# 2. НАСТРОЙКА ЗАВИСИМОСТЕЙ (СОБИРАЕМ ИЗ ИСХОДНИКОВ)
# ==============================================================================
# --- ZLIB ---
add_subdirectory("${TP_ROOT}/zlib-1.3.1" zlib-build)
# --- LIBPNG ---
set(PNG_STATIC ON CACHE BOOL "Build static library" FORCE)
set(PNG_SHARED OFF CACHE BOOL "Don't build shared library" FORCE)
set(PNG_TESTS OFF CACHE BOOL "Disable tests" FORCE)
set(PNG_TOOLS OFF CACHE BOOL "Disable tools" FORCE)
set(PNG_EXECUTABLES OFF CACHE BOOL "Disable executables" FORCE)
set(PNG_DEBUG OFF CACHE BOOL "Disable debug" FORCE)
set(SKIP_INSTALL_ALL ON CACHE BOOL "Skip installation" FORCE)
set(PNG_HARDWARE_OPTIMIZATIONS OFF CACHE BOOL "Disable hardware optimizations" FORCE)
set(PNG_ARM_NEON "off" CACHE STRING "Disable ARM NEON" FORCE)
add_subdirectory("${TP_ROOT}/libpng-1.6.51" libpng-build)
# --- SDL2 ---
# Android-версия SDL требует специфичных настроек, но add_subdirectory обычно подхватывает их сама
add_subdirectory("${TP_ROOT}/SDL-release-2.32.10" sdl-build)
# --- LIBZIP ---
# Отключаем поиск системных крипто-библиотек, так как в NDK их может не быть в стандартных путях
set(ENABLE_GNUTLS OFF CACHE BOOL "" FORCE)
set(ENABLE_OPENSSL OFF CACHE BOOL "" FORCE)
set(ENABLE_WINDOWS_CRYPTO OFF CACHE BOOL "" FORCE)
set(ENABLE_COMMONCRYPTO OFF CACHE BOOL "" FORCE)
set(ENABLE_MBEDTLS OFF CACHE BOOL "" FORCE)
add_subdirectory("${TP_ROOT}/libzip-1.11.4" libzip-build)
# --- EIGEN & BOOST (Header-only) ---
# Мы создаем интерфейсные таргеты здесь, чтобы они были доступны в подпапке src
if(NOT TARGET eigen_external_lib)
add_library(eigen_external_lib INTERFACE)
target_include_directories(eigen_external_lib INTERFACE "${TP_ROOT}/eigen-5.0.0")
endif()
if(NOT TARGET boost_external_lib)
add_library(boost_external_lib INTERFACE)
target_include_directories(boost_external_lib INTERFACE "${TP_ROOT}/boost_1_90_0")
endif()
# ==============================================================================
# 3. ОСНОВНОЙ КОД
# ==============================================================================
add_subdirectory(src)
+93
View File
@@ -0,0 +1,93 @@
cmake_minimum_required(VERSION 3.6)
project(MY_APP CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Определение путей
set(SOURCE_RES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../resources")
set(TARGET_RES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../src/main/assets/resources")
# Получаем список всех файлов в исходной директории для отслеживания зависимостей
file(GLOB_RECURSE RES_FILES RELATIVE "${SOURCE_RES_DIR}" "${SOURCE_RES_DIR}/*")
set(COPY_COMMANDS "")
foreach(RES_FILE ${RES_FILES})
set(SRC "${SOURCE_RES_DIR}/${RES_FILE}")
set(DST "${TARGET_RES_DIR}/${RES_FILE}")
# Команда копирования конкретного файла, если он изменился
add_custom_command(
OUTPUT "${DST}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${TARGET_RES_DIR}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${SRC}" "${DST}"
DEPENDS "${SRC}"
COMMENT "Syncing resource: ${RES_FILE}"
)
list(APPEND RES_OUTPUTS "${DST}")
endforeach()
# Создаем кастомную цель, которая будет запускать процесс копирования
add_custom_target(sync_resources ALL DEPENDS ${RES_OUTPUTS})
add_library(main SHARED
SDL_android_main.c
../../../../src/BoneAnimatedModel.cpp
../../../../src/Environment.cpp
../../../../src/Game.cpp
../../../../src/main.cpp
../../../../src/Projectile.cpp
../../../../src/SparkEmitter.cpp
../../../../src/TextModel.cpp
../../../../src/UiManager.cpp
../../../../src/utils/Perlin.cpp
../../../../src/utils/TaskManager.cpp
../../../../src/utils/Utils.cpp
../../../../src/navigation/PathFinder.cpp
../../../../src/planet/PlanetData.cpp
../../../../src/planet/PlanetObject.cpp
../../../../src/planet/StoneObject.cpp
../../../../src/render/FrameBuffer.cpp
../../../../src/render/Renderer.cpp
../../../../src/render/ShaderManager.cpp
../../../../src/render/TextureManager.cpp
../../../../src/render/OpenGlExtensions.cpp
)
# Подключаем заголовки
target_include_directories(main PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../SDL/include
${CMAKE_CURRENT_SOURCE_DIR}/../zlib
${CMAKE_CURRENT_SOURCE_DIR}/../libpng
${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/eigen-5.0.0
${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/boost_1_90_0
${CMAKE_CURRENT_SOURCE_DIR}/../../../../src
${CMAKE_CURRENT_SOURCE_DIR}/../libzip
)
# ВАЖНО: Линкуемся с png_static (статика) или png_shared (динамика)
# Так как мы установили PNG_STATIC=ON и PNG_SHARED=OFF,
# должна создаться цель png_static
target_link_libraries(main
png_static # ← ЭТО ПРАВИЛЬНОЕ ИМЯ ЦЕЛИ!
z
SDL2
)
find_library(OPENGLES2_LIB GLESv2)
target_link_libraries(main
${OPENGLES2_LIB} # OpenGL ES 2.0/3.0
log
android
OpenSLES
dl
zip
)
add_dependencies(main sync_resources)
@@ -0,0 +1,9 @@
#include <jni.h>
#include <android/log.h>
extern int SDL_main(int argc, char *argv[]);
void Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls, jobject obj) {
/* Run the application code! */
SDL_main(0, NULL);
}
+98
View File
@@ -0,0 +1,98 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in [sdk]/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLInputConnection {
void nativeCommitText(java.lang.String, int);
void nativeGenerateScancodeForUnichar(char);
}
-keep,includedescriptorclasses class org.libsdl.app.SDLActivity {
# for some reason these aren't compatible with allowoptimization modifier
boolean supportsRelativeMouse();
void setWindowStyle(boolean);
}
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLActivity {
java.lang.String nativeGetHint(java.lang.String); # Java-side doesn't use this, so it gets minified, but C-side still tries to register it
boolean onNativeSoftReturnKey();
void onNativeKeyboardFocusLost();
boolean isScreenKeyboardShown();
android.util.DisplayMetrics getDisplayDPI();
java.lang.String clipboardGetText();
boolean clipboardHasText();
void clipboardSetText(java.lang.String);
int createCustomCursor(int[], int, int, int, int);
void destroyCustomCursor(int);
android.content.Context getContext();
boolean getManifestEnvironmentVariables();
android.view.Surface getNativeSurface();
void initTouch();
boolean isAndroidTV();
boolean isChromebook();
boolean isDeXMode();
boolean isTablet();
void manualBackButton();
int messageboxShowMessageBox(int, java.lang.String, java.lang.String, int[], int[], java.lang.String[], int[]);
void minimizeWindow();
int openURL(java.lang.String);
void requestPermission(java.lang.String, int);
int showToast(java.lang.String, int, int, int, int);
boolean sendMessage(int, int);
boolean setActivityTitle(java.lang.String);
boolean setCustomCursor(int);
void setOrientation(int, int, boolean, java.lang.String);
boolean setRelativeMouseEnabled(boolean);
boolean setSystemCursor(int);
boolean shouldMinimizeOnFocusLoss();
boolean showTextInput(int, int, int, int);
}
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.HIDDeviceManager {
boolean initialize(boolean, boolean);
boolean openDevice(int);
int sendOutputReport(int, byte[]);
int sendFeatureReport(int, byte[]);
boolean getFeatureReport(int, byte[]);
void closeDevice(int);
}
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLAudioManager {
int[] getAudioOutputDevices();
int[] getAudioInputDevices();
int[] audioOpen(int, int, int, int, int);
void audioWriteFloatBuffer(float[]);
void audioWriteShortBuffer(short[]);
void audioWriteByteBuffer(byte[]);
void audioClose();
int[] captureOpen(int, int, int, int, int);
int captureReadFloatBuffer(float[], boolean);
int captureReadShortBuffer(short[], boolean);
int captureReadByteBuffer(byte[], boolean);
void captureClose();
void audioSetThreadPriority(boolean, int);
native int nativeSetupJNI();
native void removeAudioDevice(boolean, int);
native void addAudioDevice(boolean, int);
}
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLControllerManager {
void pollInputDevices();
void pollHapticDevices();
void hapticRun(int, float, int);
void hapticStop(int);
}
@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Replace com.test.game with the identifier of your game below, e.g.
com.gamemaker.game
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0"
android:installLocation="auto">
<!-- OpenGL ES 2.0 -->
<uses-feature android:glEsVersion="0x00020000" />
<!-- Touchscreen support -->
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<!-- Game controller support -->
<uses-feature
android:name="android.hardware.bluetooth"
android:required="false" />
<uses-feature
android:name="android.hardware.gamepad"
android:required="false" />
<uses-feature
android:name="android.hardware.usb.host"
android:required="false" />
<!-- External mouse input events -->
<uses-feature
android:name="android.hardware.type.pc"
android:required="false" />
<!-- Audio recording support -->
<!-- if you want to capture audio, uncomment this. -->
<!-- <uses-feature
android:name="android.hardware.microphone"
android:required="false" /> -->
<!-- Allow downloading to the external storage on Android 5.1 and older -->
<!-- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="22" /> -->
<!-- Allow access to Bluetooth devices -->
<!-- Currently this is just for Steam Controller support and requires setting SDL_HINT_JOYSTICK_HIDAPI_STEAM -->
<!-- <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> -->
<!-- <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> -->
<!-- Allow access to the vibrator -->
<uses-permission android:name="android.permission.VIBRATE" />
<!-- if you want to capture audio, uncomment this. -->
<!-- <uses-permission android:name="android.permission.RECORD_AUDIO" /> -->
<!-- Create a Java class extending SDLActivity and place it in a
directory under app/src/main/java matching the package, e.g. app/src/main/java/com/gamemaker/game/MyGame.java
then replace "SDLActivity" with the name of your class (e.g. "MyGame")
in the XML below.
An example Java class can be found in README-android.md
-->
<application android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:allowBackup="true"
android:theme="@style/AppTheme"
android:hardwareAccelerated="true" >
<!-- Example of setting SDL hints from AndroidManifest.xml:
<meta-data android:name="SDL_ENV.SDL_ACCELEROMETER_AS_JOYSTICK" android:value="0"/>
-->
<activity android:name="SDLActivity"
android:label="@string/app_name"
android:alwaysRetainTaskState="true"
android:launchMode="singleInstance"
android:configChanges="layoutDirection|locale|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
android:preferMinimalPostProcessing="true"
android:exported="true"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- Let Android know that we can handle some USB devices and should receive this event -->
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<!-- Drop file event -->
<!--
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
-->
</activity>
</application>
</manifest>
@@ -0,0 +1,22 @@
package org.libsdl.app;
import android.hardware.usb.UsbDevice;
interface HIDDevice
{
public int getId();
public int getVendorId();
public int getProductId();
public String getSerialNumber();
public int getVersion();
public String getManufacturerName();
public String getProductName();
public UsbDevice getDevice();
public boolean open();
public int sendFeatureReport(byte[] report);
public int sendOutputReport(byte[] report);
public boolean getFeatureReport(byte[] report);
public void setFrozen(boolean frozen);
public void close();
public void shutdown();
}
@@ -0,0 +1,650 @@
package org.libsdl.app;
import android.content.Context;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothGattService;
import android.hardware.usb.UsbDevice;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.os.*;
//import com.android.internal.util.HexDump;
import java.lang.Runnable;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.UUID;
class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
private static final String TAG = "hidapi";
private HIDDeviceManager mManager;
private BluetoothDevice mDevice;
private int mDeviceId;
private BluetoothGatt mGatt;
private boolean mIsRegistered = false;
private boolean mIsConnected = false;
private boolean mIsChromebook = false;
private boolean mIsReconnecting = false;
private boolean mFrozen = false;
private LinkedList<GattOperation> mOperations;
GattOperation mCurrentOperation = null;
private Handler mHandler;
private static final int TRANSPORT_AUTO = 0;
private static final int TRANSPORT_BREDR = 1;
private static final int TRANSPORT_LE = 2;
private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
static class GattOperation {
private enum Operation {
CHR_READ,
CHR_WRITE,
ENABLE_NOTIFICATION
}
Operation mOp;
UUID mUuid;
byte[] mValue;
BluetoothGatt mGatt;
boolean mResult = true;
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
mGatt = gatt;
mOp = operation;
mUuid = uuid;
}
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
mGatt = gatt;
mOp = operation;
mUuid = uuid;
mValue = value;
}
public void run() {
// This is executed in main thread
BluetoothGattCharacteristic chr;
switch (mOp) {
case CHR_READ:
chr = getCharacteristic(mUuid);
//Log.v(TAG, "Reading characteristic " + chr.getUuid());
if (!mGatt.readCharacteristic(chr)) {
Log.e(TAG, "Unable to read characteristic " + mUuid.toString());
mResult = false;
break;
}
mResult = true;
break;
case CHR_WRITE:
chr = getCharacteristic(mUuid);
//Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value));
chr.setValue(mValue);
if (!mGatt.writeCharacteristic(chr)) {
Log.e(TAG, "Unable to write characteristic " + mUuid.toString());
mResult = false;
break;
}
mResult = true;
break;
case ENABLE_NOTIFICATION:
chr = getCharacteristic(mUuid);
//Log.v(TAG, "Writing descriptor of " + chr.getUuid());
if (chr != null) {
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (cccd != null) {
int properties = chr.getProperties();
byte[] value;
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) {
value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
} else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) {
value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
} else {
Log.e(TAG, "Unable to start notifications on input characteristic");
mResult = false;
return;
}
mGatt.setCharacteristicNotification(chr, true);
cccd.setValue(value);
if (!mGatt.writeDescriptor(cccd)) {
Log.e(TAG, "Unable to write descriptor " + mUuid.toString());
mResult = false;
return;
}
mResult = true;
}
}
}
}
public boolean finish() {
return mResult;
}
private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
BluetoothGattService valveService = mGatt.getService(steamControllerService);
if (valveService == null)
return null;
return valveService.getCharacteristic(uuid);
}
static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) {
return new GattOperation(gatt, Operation.CHR_READ, uuid);
}
static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) {
return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value);
}
static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
}
}
public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
mManager = manager;
mDevice = device;
mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier());
mIsRegistered = false;
mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
mOperations = new LinkedList<GattOperation>();
mHandler = new Handler(Looper.getMainLooper());
mGatt = connectGatt();
// final HIDDeviceBLESteamController finalThis = this;
// mHandler.postDelayed(new Runnable() {
// @Override
// public void run() {
// finalThis.checkConnectionForChromebookIssue();
// }
// }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
}
public String getIdentifier() {
return String.format("SteamController.%s", mDevice.getAddress());
}
public BluetoothGatt getGatt() {
return mGatt;
}
// Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
// of TRANSPORT_LE. Let's force ourselves to connect low energy.
private BluetoothGatt connectGatt(boolean managed) {
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
try {
return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE);
} catch (Exception e) {
return mDevice.connectGatt(mManager.getContext(), managed, this);
}
} else {
return mDevice.connectGatt(mManager.getContext(), managed, this);
}
}
private BluetoothGatt connectGatt() {
return connectGatt(false);
}
protected int getConnectionState() {
Context context = mManager.getContext();
if (context == null) {
// We are lacking any context to get our Bluetooth information. We'll just assume disconnected.
return BluetoothProfile.STATE_DISCONNECTED;
}
BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
if (btManager == null) {
// This device doesn't support Bluetooth. We should never be here, because how did
// we instantiate a device to start with?
return BluetoothProfile.STATE_DISCONNECTED;
}
return btManager.getConnectionState(mDevice, BluetoothProfile.GATT);
}
public void reconnect() {
if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
mGatt.disconnect();
mGatt = connectGatt();
}
}
protected void checkConnectionForChromebookIssue() {
if (!mIsChromebook) {
// We only do this on Chromebooks, because otherwise it's really annoying to just attempt
// over and over.
return;
}
int connectionState = getConnectionState();
switch (connectionState) {
case BluetoothProfile.STATE_CONNECTED:
if (!mIsConnected) {
// We are in the Bad Chromebook Place. We can force a disconnect
// to try to recover.
Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback. Forcing a reconnect.");
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
break;
}
else if (!isRegistered()) {
if (mGatt.getServices().size() > 0) {
Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration. Trying to recover.");
probeService(this);
}
else {
Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services. Trying to recover.");
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
break;
}
}
else {
Log.v(TAG, "Chromebook: We are connected, and registered. Everything's good!");
return;
}
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us. Attempting a disconnect/reconnect, but we may not be able to recover.");
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
break;
case BluetoothProfile.STATE_CONNECTING:
Log.v(TAG, "Chromebook: We're still trying to connect. Waiting a bit longer.");
break;
}
final HIDDeviceBLESteamController finalThis = this;
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
finalThis.checkConnectionForChromebookIssue();
}
}, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
}
private boolean isRegistered() {
return mIsRegistered;
}
private void setRegistered() {
mIsRegistered = true;
}
private boolean probeService(HIDDeviceBLESteamController controller) {
if (isRegistered()) {
return true;
}
if (!mIsConnected) {
return false;
}
Log.v(TAG, "probeService controller=" + controller);
for (BluetoothGattService service : mGatt.getServices()) {
if (service.getUuid().equals(steamControllerService)) {
Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
if (chr.getUuid().equals(inputCharacteristic)) {
Log.v(TAG, "Found input characteristic");
// Start notifications
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (cccd != null) {
enableNotification(chr.getUuid());
}
}
}
return true;
}
}
if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) {
Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us.");
mIsConnected = false;
mIsReconnecting = true;
mGatt.disconnect();
mGatt = connectGatt(false);
}
return false;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
private void finishCurrentGattOperation() {
GattOperation op = null;
synchronized (mOperations) {
if (mCurrentOperation != null) {
op = mCurrentOperation;
mCurrentOperation = null;
}
}
if (op != null) {
boolean result = op.finish(); // TODO: Maybe in main thread as well?
// Our operation failed, let's add it back to the beginning of our queue.
if (!result) {
mOperations.addFirst(op);
}
}
executeNextGattOperation();
}
private void executeNextGattOperation() {
synchronized (mOperations) {
if (mCurrentOperation != null)
return;
if (mOperations.isEmpty())
return;
mCurrentOperation = mOperations.removeFirst();
}
// Run in main thread
mHandler.post(new Runnable() {
@Override
public void run() {
synchronized (mOperations) {
if (mCurrentOperation == null) {
Log.e(TAG, "Current operation null in executor?");
return;
}
mCurrentOperation.run();
// now wait for the GATT callback and when it comes, finish this operation
}
}
});
}
private void queueGattOperation(GattOperation op) {
synchronized (mOperations) {
mOperations.add(op);
}
executeNextGattOperation();
}
private void enableNotification(UUID chrUuid) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid);
queueGattOperation(op);
}
public void writeCharacteristic(UUID uuid, byte[] value) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value);
queueGattOperation(op);
}
public void readCharacteristic(UUID uuid) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid);
queueGattOperation(op);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
////////////// BluetoothGattCallback overridden methods
//////////////////////////////////////////////////////////////////////////////////////////////////////
public void onConnectionStateChange(BluetoothGatt g, int status, int newState) {
//Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState);
mIsReconnecting = false;
if (newState == 2) {
mIsConnected = true;
// Run directly, without GattOperation
if (!isRegistered()) {
mHandler.post(new Runnable() {
@Override
public void run() {
mGatt.discoverServices();
}
});
}
}
else if (newState == 0) {
mIsConnected = false;
}
// Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent.
}
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
//Log.v(TAG, "onServicesDiscovered status=" + status);
if (status == 0) {
if (gatt.getServices().size() == 0) {
Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack.");
mIsReconnecting = true;
mIsConnected = false;
gatt.disconnect();
mGatt = connectGatt(false);
}
else {
probeService(this);
}
}
}
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid());
if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) {
mManager.HIDDeviceFeatureReport(getId(), characteristic.getValue());
}
finishCurrentGattOperation();
}
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid());
if (characteristic.getUuid().equals(reportCharacteristic)) {
// Only register controller with the native side once it has been fully configured
if (!isRegistered()) {
Log.v(TAG, "Registering Steam Controller with ID: " + getId());
mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0);
setRegistered();
}
}
finishCurrentGattOperation();
}
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
// Enable this for verbose logging of controller input reports
//Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) {
mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
}
}
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
//Log.v(TAG, "onDescriptorRead status=" + status);
}
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
//Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
if (chr.getUuid().equals(inputCharacteristic)) {
boolean hasWrittenInputDescriptor = true;
BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
if (reportChr != null) {
Log.v(TAG, "Writing report characteristic to enter valve mode");
reportChr.setValue(enterValveMode);
gatt.writeCharacteristic(reportChr);
}
}
finishCurrentGattOperation();
}
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
//Log.v(TAG, "onReliableWriteCompleted status=" + status);
}
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
//Log.v(TAG, "onReadRemoteRssi status=" + status);
}
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
//Log.v(TAG, "onMtuChanged status=" + status);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////// Public API
//////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
public int getId() {
return mDeviceId;
}
@Override
public int getVendorId() {
// Valve Corporation
final int VALVE_USB_VID = 0x28DE;
return VALVE_USB_VID;
}
@Override
public int getProductId() {
// We don't have an easy way to query from the Bluetooth device, but we know what it is
final int D0G_BLE2_PID = 0x1106;
return D0G_BLE2_PID;
}
@Override
public String getSerialNumber() {
// This will be read later via feature report by Steam
return "12345";
}
@Override
public int getVersion() {
return 0;
}
@Override
public String getManufacturerName() {
return "Valve Corporation";
}
@Override
public String getProductName() {
return "Steam Controller";
}
@Override
public UsbDevice getDevice() {
return null;
}
@Override
public boolean open() {
return true;
}
@Override
public int sendFeatureReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted sendFeatureReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return -1;
}
// We need to skip the first byte, as that doesn't go over the air
byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(actual_report));
writeCharacteristic(reportCharacteristic, actual_report);
return report.length;
}
@Override
public int sendOutputReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted sendOutputReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return -1;
}
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(report));
writeCharacteristic(reportCharacteristic, report);
return report.length;
}
@Override
public boolean getFeatureReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted getFeatureReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return false;
}
//Log.v(TAG, "getFeatureReport");
readCharacteristic(reportCharacteristic);
return true;
}
@Override
public void close() {
}
@Override
public void setFrozen(boolean frozen) {
mFrozen = frozen;
}
@Override
public void shutdown() {
close();
BluetoothGatt g = mGatt;
if (g != null) {
g.disconnect();
g.close();
mGatt = null;
}
mManager = null;
mIsRegistered = false;
mIsConnected = false;
mOperations.clear();
}
}
@@ -0,0 +1,698 @@
package org.libsdl.app;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.os.Build;
import android.util.Log;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.hardware.usb.*;
import android.os.Handler;
import android.os.Looper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
public class HIDDeviceManager {
private static final String TAG = "hidapi";
private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
private static HIDDeviceManager sManager;
private static int sManagerRefCount = 0;
public static HIDDeviceManager acquire(Context context) {
if (sManagerRefCount == 0) {
sManager = new HIDDeviceManager(context);
}
++sManagerRefCount;
return sManager;
}
public static void release(HIDDeviceManager manager) {
if (manager == sManager) {
--sManagerRefCount;
if (sManagerRefCount == 0) {
sManager.close();
sManager = null;
}
}
}
private Context mContext;
private HashMap<Integer, HIDDevice> mDevicesById = new HashMap<Integer, HIDDevice>();
private HashMap<BluetoothDevice, HIDDeviceBLESteamController> mBluetoothDevices = new HashMap<BluetoothDevice, HIDDeviceBLESteamController>();
private int mNextDeviceId = 0;
private SharedPreferences mSharedPreferences = null;
private boolean mIsChromebook = false;
private UsbManager mUsbManager;
private Handler mHandler;
private BluetoothManager mBluetoothManager;
private List<BluetoothDevice> mLastBluetoothDevices;
private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
handleUsbDeviceAttached(usbDevice);
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
handleUsbDeviceDetached(usbDevice);
} else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
}
}
};
private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// Bluetooth device was connected. If it was a Steam Controller, handle it
if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d(TAG, "Bluetooth device connected: " + device);
if (isSteamController(device)) {
connectBluetoothDevice(device);
}
}
// Bluetooth device was disconnected, remove from controller manager (if any)
if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d(TAG, "Bluetooth device disconnected: " + device);
disconnectBluetoothDevice(device);
}
}
};
private HIDDeviceManager(final Context context) {
mContext = context;
HIDDeviceRegisterCallback();
mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
// if (shouldClear) {
// SharedPreferences.Editor spedit = mSharedPreferences.edit();
// spedit.clear();
// spedit.commit();
// }
// else
{
mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0);
}
}
public Context getContext() {
return mContext;
}
public int getDeviceIDForIdentifier(String identifier) {
SharedPreferences.Editor spedit = mSharedPreferences.edit();
int result = mSharedPreferences.getInt(identifier, 0);
if (result == 0) {
result = mNextDeviceId++;
spedit.putInt("next_device_id", mNextDeviceId);
}
spedit.putInt(identifier, result);
spedit.commit();
return result;
}
private void initializeUSB() {
mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
if (mUsbManager == null) {
return;
}
/*
// Logging
for (UsbDevice device : mUsbManager.getDeviceList().values()) {
Log.i(TAG,"Path: " + device.getDeviceName());
Log.i(TAG,"Manufacturer: " + device.getManufacturerName());
Log.i(TAG,"Product: " + device.getProductName());
Log.i(TAG,"ID: " + device.getDeviceId());
Log.i(TAG,"Class: " + device.getDeviceClass());
Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
Log.i(TAG,"Vendor ID " + device.getVendorId());
Log.i(TAG,"Product ID: " + device.getProductId());
Log.i(TAG,"Interface count: " + device.getInterfaceCount());
Log.i(TAG,"---------------------------------------");
// Get interface details
for (int index = 0; index < device.getInterfaceCount(); index++) {
UsbInterface mUsbInterface = device.getInterface(index);
Log.i(TAG," ***** *****");
Log.i(TAG," Interface index: " + index);
Log.i(TAG," Interface ID: " + mUsbInterface.getId());
Log.i(TAG," Interface class: " + mUsbInterface.getInterfaceClass());
Log.i(TAG," Interface subclass: " + mUsbInterface.getInterfaceSubclass());
Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
// Get endpoint details
for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
{
UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
Log.i(TAG," ++++ ++++ ++++");
Log.i(TAG," Endpoint index: " + epi);
Log.i(TAG," Attributes: " + mEndpoint.getAttributes());
Log.i(TAG," Direction: " + mEndpoint.getDirection());
Log.i(TAG," Number: " + mEndpoint.getEndpointNumber());
Log.i(TAG," Interval: " + mEndpoint.getInterval());
Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize());
Log.i(TAG," Type: " + mEndpoint.getType());
}
}
}
Log.i(TAG," No more devices connected.");
*/
// Register for USB broadcasts and permission completions
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
mContext.registerReceiver(mUsbBroadcast, filter);
for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
handleUsbDeviceAttached(usbDevice);
}
}
UsbManager getUSBManager() {
return mUsbManager;
}
private void shutdownUSB() {
try {
mContext.unregisterReceiver(mUsbBroadcast);
} catch (Exception e) {
// We may not have registered, that's okay
}
}
private boolean isHIDDeviceInterface(UsbDevice usbDevice, UsbInterface usbInterface) {
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
return true;
}
if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) {
return true;
}
return false;
}
private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
final int XB360_IFACE_SUBCLASS = 93;
final int XB360_IFACE_PROTOCOL = 1; // Wired
final int XB360W_IFACE_PROTOCOL = 129; // Wireless
final int[] SUPPORTED_VENDORS = {
0x0079, // GPD Win 2
0x044f, // Thrustmaster
0x045e, // Microsoft
0x046d, // Logitech
0x056e, // Elecom
0x06a3, // Saitek
0x0738, // Mad Catz
0x07ff, // Mad Catz
0x0e6f, // PDP
0x0f0d, // Hori
0x1038, // SteelSeries
0x11c9, // Nacon
0x12ab, // Unknown
0x1430, // RedOctane
0x146b, // BigBen
0x1532, // Razer Sabertooth
0x15e4, // Numark
0x162e, // Joytech
0x1689, // Razer Onza
0x1949, // Lab126, Inc.
0x1bad, // Harmonix
0x20d6, // PowerA
0x24c6, // PowerA
0x2c22, // Qanba
0x2dc8, // 8BitDo
0x9886, // ASTRO Gaming
};
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
(usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL ||
usbInterface.getInterfaceProtocol() == XB360W_IFACE_PROTOCOL)) {
int vendor_id = usbDevice.getVendorId();
for (int supportedVid : SUPPORTED_VENDORS) {
if (vendor_id == supportedVid) {
return true;
}
}
}
return false;
}
private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
final int XB1_IFACE_SUBCLASS = 71;
final int XB1_IFACE_PROTOCOL = 208;
final int[] SUPPORTED_VENDORS = {
0x03f0, // HP
0x044f, // Thrustmaster
0x045e, // Microsoft
0x0738, // Mad Catz
0x0b05, // ASUS
0x0e6f, // PDP
0x0f0d, // Hori
0x10f5, // Turtle Beach
0x1532, // Razer Wildcat
0x20d6, // PowerA
0x24c6, // PowerA
0x2dc8, // 8BitDo
0x2e24, // Hyperkin
0x3537, // GameSir
};
if (usbInterface.getId() == 0 &&
usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
int vendor_id = usbDevice.getVendorId();
for (int supportedVid : SUPPORTED_VENDORS) {
if (vendor_id == supportedVid) {
return true;
}
}
}
return false;
}
private void handleUsbDeviceAttached(UsbDevice usbDevice) {
connectHIDDeviceUSB(usbDevice);
}
private void handleUsbDeviceDetached(UsbDevice usbDevice) {
List<Integer> devices = new ArrayList<Integer>();
for (HIDDevice device : mDevicesById.values()) {
if (usbDevice.equals(device.getDevice())) {
devices.add(device.getId());
}
}
for (int id : devices) {
HIDDevice device = mDevicesById.get(id);
mDevicesById.remove(id);
device.shutdown();
HIDDeviceDisconnected(id);
}
}
private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
for (HIDDevice device : mDevicesById.values()) {
if (usbDevice.equals(device.getDevice())) {
boolean opened = false;
if (permission_granted) {
opened = device.open();
}
HIDDeviceOpenResult(device.getId(), opened);
}
}
}
private void connectHIDDeviceUSB(UsbDevice usbDevice) {
synchronized (this) {
int interface_mask = 0;
for (int interface_index = 0; interface_index < usbDevice.getInterfaceCount(); interface_index++) {
UsbInterface usbInterface = usbDevice.getInterface(interface_index);
if (isHIDDeviceInterface(usbDevice, usbInterface)) {
// Check to see if we've already added this interface
// This happens with the Xbox Series X controller which has a duplicate interface 0, which is inactive
int interface_id = usbInterface.getId();
if ((interface_mask & (1 << interface_id)) != 0) {
continue;
}
interface_mask |= (1 << interface_id);
HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index);
int id = device.getId();
mDevicesById.put(id, device);
HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol());
}
}
}
}
private void initializeBluetooth() {
Log.d(TAG, "Initializing Bluetooth");
if (Build.VERSION.SDK_INT >= 31 /* Android 12 */ &&
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH_CONNECT, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH_CONNECT");
return;
}
if (Build.VERSION.SDK_INT <= 30 /* Android 11.0 (R) */ &&
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
return;
}
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18 /* Android 4.3 (JELLY_BEAN_MR2) */)) {
Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE");
return;
}
// Find bonded bluetooth controllers and create SteamControllers for them
mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
// This device doesn't support Bluetooth.
return;
}
BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
if (btAdapter == null) {
// This device has Bluetooth support in the codebase, but has no available adapters.
return;
}
// Get our bonded devices.
for (BluetoothDevice device : btAdapter.getBondedDevices()) {
Log.d(TAG, "Bluetooth device available: " + device);
if (isSteamController(device)) {
connectBluetoothDevice(device);
}
}
// NOTE: These don't work on Chromebooks, to my undying dismay.
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
mContext.registerReceiver(mBluetoothBroadcast, filter);
if (mIsChromebook) {
mHandler = new Handler(Looper.getMainLooper());
mLastBluetoothDevices = new ArrayList<BluetoothDevice>();
// final HIDDeviceManager finalThis = this;
// mHandler.postDelayed(new Runnable() {
// @Override
// public void run() {
// finalThis.chromebookConnectionHandler();
// }
// }, 5000);
}
}
private void shutdownBluetooth() {
try {
mContext.unregisterReceiver(mBluetoothBroadcast);
} catch (Exception e) {
// We may not have registered, that's okay
}
}
// Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
// This function provides a sort of dummy version of that, watching for changes in the
// connected devices and attempting to add controllers as things change.
public void chromebookConnectionHandler() {
if (!mIsChromebook) {
return;
}
ArrayList<BluetoothDevice> disconnected = new ArrayList<BluetoothDevice>();
ArrayList<BluetoothDevice> connected = new ArrayList<BluetoothDevice>();
List<BluetoothDevice> currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
for (BluetoothDevice bluetoothDevice : currentConnected) {
if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
connected.add(bluetoothDevice);
}
}
for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
if (!currentConnected.contains(bluetoothDevice)) {
disconnected.add(bluetoothDevice);
}
}
mLastBluetoothDevices = currentConnected;
for (BluetoothDevice bluetoothDevice : disconnected) {
disconnectBluetoothDevice(bluetoothDevice);
}
for (BluetoothDevice bluetoothDevice : connected) {
connectBluetoothDevice(bluetoothDevice);
}
final HIDDeviceManager finalThis = this;
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
finalThis.chromebookConnectionHandler();
}
}, 10000);
}
public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
synchronized (this) {
if (mBluetoothDevices.containsKey(bluetoothDevice)) {
Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect");
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
device.reconnect();
return false;
}
HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice);
int id = device.getId();
mBluetoothDevices.put(bluetoothDevice, device);
mDevicesById.put(id, device);
// The Steam Controller will mark itself connected once initialization is complete
}
return true;
}
public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
synchronized (this) {
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
if (device == null)
return;
int id = device.getId();
mBluetoothDevices.remove(bluetoothDevice);
mDevicesById.remove(id);
device.shutdown();
HIDDeviceDisconnected(id);
}
}
public boolean isSteamController(BluetoothDevice bluetoothDevice) {
// Sanity check. If you pass in a null device, by definition it is never a Steam Controller.
if (bluetoothDevice == null) {
return false;
}
// If the device has no local name, we really don't want to try an equality check against it.
if (bluetoothDevice.getName() == null) {
return false;
}
return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
}
private void close() {
shutdownUSB();
shutdownBluetooth();
synchronized (this) {
for (HIDDevice device : mDevicesById.values()) {
device.shutdown();
}
mDevicesById.clear();
mBluetoothDevices.clear();
HIDDeviceReleaseCallback();
}
}
public void setFrozen(boolean frozen) {
synchronized (this) {
for (HIDDevice device : mDevicesById.values()) {
device.setFrozen(frozen);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
private HIDDevice getDevice(int id) {
synchronized (this) {
HIDDevice result = mDevicesById.get(id);
if (result == null) {
Log.v(TAG, "No device for id: " + id);
Log.v(TAG, "Available devices: " + mDevicesById.keySet());
}
return result;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
////////// JNI interface functions
//////////////////////////////////////////////////////////////////////////////////////////////////////
public boolean initialize(boolean usb, boolean bluetooth) {
Log.v(TAG, "initialize(" + usb + ", " + bluetooth + ")");
if (usb) {
initializeUSB();
}
if (bluetooth) {
initializeBluetooth();
}
return true;
}
public boolean openDevice(int deviceID) {
Log.v(TAG, "openDevice deviceID=" + deviceID);
HIDDevice device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return false;
}
// Look to see if this is a USB device and we have permission to access it
UsbDevice usbDevice = device.getDevice();
if (usbDevice != null && !mUsbManager.hasPermission(usbDevice)) {
HIDDeviceOpenPending(deviceID);
try {
final int FLAG_MUTABLE = 0x02000000; // PendingIntent.FLAG_MUTABLE, but don't require SDK 31
int flags;
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
flags = FLAG_MUTABLE;
} else {
flags = 0;
}
if (Build.VERSION.SDK_INT >= 33 /* Android 14.0 (U) */) {
Intent intent = new Intent(HIDDeviceManager.ACTION_USB_PERMISSION);
intent.setPackage(mContext.getPackageName());
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, intent, flags));
} else {
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), flags));
}
} catch (Exception e) {
Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
HIDDeviceOpenResult(deviceID, false);
}
return false;
}
try {
return device.open();
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return false;
}
public int sendOutputReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return -1;
}
return device.sendOutputReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return -1;
}
public int sendFeatureReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return -1;
}
return device.sendFeatureReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return -1;
}
public boolean getFeatureReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "getFeatureReport deviceID=" + deviceID);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return false;
}
return device.getFeatureReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return false;
}
public void closeDevice(int deviceID) {
try {
Log.v(TAG, "closeDevice deviceID=" + deviceID);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return;
}
device.close();
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////// Native methods
//////////////////////////////////////////////////////////////////////////////////////////////////////
private native void HIDDeviceRegisterCallback();
private native void HIDDeviceReleaseCallback();
native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol);
native void HIDDeviceOpenPending(int deviceID);
native void HIDDeviceOpenResult(int deviceID, boolean opened);
native void HIDDeviceDisconnected(int deviceID);
native void HIDDeviceInputReport(int deviceID, byte[] report);
native void HIDDeviceFeatureReport(int deviceID, byte[] report);
}
@@ -0,0 +1,309 @@
package org.libsdl.app;
import android.hardware.usb.*;
import android.os.Build;
import android.util.Log;
import java.util.Arrays;
class HIDDeviceUSB implements HIDDevice {
private static final String TAG = "hidapi";
protected HIDDeviceManager mManager;
protected UsbDevice mDevice;
protected int mInterfaceIndex;
protected int mInterface;
protected int mDeviceId;
protected UsbDeviceConnection mConnection;
protected UsbEndpoint mInputEndpoint;
protected UsbEndpoint mOutputEndpoint;
protected InputThread mInputThread;
protected boolean mRunning;
protected boolean mFrozen;
public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) {
mManager = manager;
mDevice = usbDevice;
mInterfaceIndex = interface_index;
mInterface = mDevice.getInterface(mInterfaceIndex).getId();
mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
mRunning = false;
}
public String getIdentifier() {
return String.format("%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex);
}
@Override
public int getId() {
return mDeviceId;
}
@Override
public int getVendorId() {
return mDevice.getVendorId();
}
@Override
public int getProductId() {
return mDevice.getProductId();
}
@Override
public String getSerialNumber() {
String result = null;
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
try {
result = mDevice.getSerialNumber();
}
catch (SecurityException exception) {
//Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage());
}
}
if (result == null) {
result = "";
}
return result;
}
@Override
public int getVersion() {
return 0;
}
@Override
public String getManufacturerName() {
String result = null;
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
result = mDevice.getManufacturerName();
}
if (result == null) {
result = String.format("%x", getVendorId());
}
return result;
}
@Override
public String getProductName() {
String result = null;
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
result = mDevice.getProductName();
}
if (result == null) {
result = String.format("%x", getProductId());
}
return result;
}
@Override
public UsbDevice getDevice() {
return mDevice;
}
public String getDeviceName() {
return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
}
@Override
public boolean open() {
mConnection = mManager.getUSBManager().openDevice(mDevice);
if (mConnection == null) {
Log.w(TAG, "Unable to open USB device " + getDeviceName());
return false;
}
// Force claim our interface
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
if (!mConnection.claimInterface(iface, true)) {
Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
close();
return false;
}
// Find the endpoints
for (int j = 0; j < iface.getEndpointCount(); j++) {
UsbEndpoint endpt = iface.getEndpoint(j);
switch (endpt.getDirection()) {
case UsbConstants.USB_DIR_IN:
if (mInputEndpoint == null) {
mInputEndpoint = endpt;
}
break;
case UsbConstants.USB_DIR_OUT:
if (mOutputEndpoint == null) {
mOutputEndpoint = endpt;
}
break;
}
}
// Make sure the required endpoints were present
if (mInputEndpoint == null || mOutputEndpoint == null) {
Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
close();
return false;
}
// Start listening for input
mRunning = true;
mInputThread = new InputThread();
mInputThread.start();
return true;
}
@Override
public int sendFeatureReport(byte[] report) {
int res = -1;
int offset = 0;
int length = report.length;
boolean skipped_report_id = false;
byte report_number = report[0];
if (report_number == 0x0) {
++offset;
--length;
skipped_report_id = true;
}
res = mConnection.controlTransfer(
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
0x09/*HID set_report*/,
(3/*HID feature*/ << 8) | report_number,
mInterface,
report, offset, length,
1000/*timeout millis*/);
if (res < 0) {
Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName());
return -1;
}
if (skipped_report_id) {
++length;
}
return length;
}
@Override
public int sendOutputReport(byte[] report) {
int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
if (r != report.length) {
Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName());
}
return r;
}
@Override
public boolean getFeatureReport(byte[] report) {
int res = -1;
int offset = 0;
int length = report.length;
boolean skipped_report_id = false;
byte report_number = report[0];
if (report_number == 0x0) {
/* Offset the return buffer by 1, so that the report ID
will remain in byte 0. */
++offset;
--length;
skipped_report_id = true;
}
res = mConnection.controlTransfer(
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
0x01/*HID get_report*/,
(3/*HID feature*/ << 8) | report_number,
mInterface,
report, offset, length,
1000/*timeout millis*/);
if (res < 0) {
Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
return false;
}
if (skipped_report_id) {
++res;
++length;
}
byte[] data;
if (res == length) {
data = report;
} else {
data = Arrays.copyOfRange(report, 0, res);
}
mManager.HIDDeviceFeatureReport(mDeviceId, data);
return true;
}
@Override
public void close() {
mRunning = false;
if (mInputThread != null) {
while (mInputThread.isAlive()) {
mInputThread.interrupt();
try {
mInputThread.join();
} catch (InterruptedException e) {
// Keep trying until we're done
}
}
mInputThread = null;
}
if (mConnection != null) {
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
mConnection.releaseInterface(iface);
mConnection.close();
mConnection = null;
}
}
@Override
public void shutdown() {
close();
mManager = null;
}
@Override
public void setFrozen(boolean frozen) {
mFrozen = frozen;
}
protected class InputThread extends Thread {
@Override
public void run() {
int packetSize = mInputEndpoint.getMaxPacketSize();
byte[] packet = new byte[packetSize];
while (mRunning) {
int r;
try
{
r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
}
catch (Exception e)
{
Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e);
break;
}
if (r < 0) {
// Could be a timeout or an I/O error
}
if (r > 0) {
byte[] data;
if (r == packetSize) {
data = packet;
} else {
data = Arrays.copyOfRange(packet, 0, r);
}
if (!mFrozen) {
mManager.HIDDeviceInputReport(mDeviceId, data);
}
}
}
}
}
}
@@ -0,0 +1,90 @@
package org.libsdl.app;
import android.content.Context;
import java.lang.Class;
import java.lang.reflect.Method;
/**
SDL library initialization
*/
public class SDL {
// This function should be called first and sets up the native code
// so it can call into the Java classes
public static void setupJNI() {
SDLActivity.nativeSetupJNI();
SDLAudioManager.nativeSetupJNI();
SDLControllerManager.nativeSetupJNI();
}
// This function should be called each time the activity is started
public static void initialize() {
setContext(null);
SDLActivity.initialize();
SDLAudioManager.initialize();
SDLControllerManager.initialize();
}
// This function stores the current activity (SDL or not)
public static void setContext(Context context) {
SDLAudioManager.setContext(context);
mContext = context;
}
public static Context getContext() {
return mContext;
}
public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
loadLibrary(libraryName, mContext);
}
public static void loadLibrary(String libraryName, Context context) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
if (libraryName == null) {
throw new NullPointerException("No library name provided.");
}
try {
// Let's see if we have ReLinker available in the project. This is necessary for
// some projects that have huge numbers of local libraries bundled, and thus may
// trip a bug in Android's native library loader which ReLinker works around. (If
// loadLibrary works properly, ReLinker will simply use the normal Android method
// internally.)
//
// To use ReLinker, just add it as a dependency. For more information, see
// https://github.com/KeepSafe/ReLinker for ReLinker's repository.
//
Class<?> relinkClass = context.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker");
Class<?> relinkListenerClass = context.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener");
Class<?> contextClass = context.getClassLoader().loadClass("android.content.Context");
Class<?> stringClass = context.getClassLoader().loadClass("java.lang.String");
// Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if
// they've changed during updates.
Method forceMethod = relinkClass.getDeclaredMethod("force");
Object relinkInstance = forceMethod.invoke(null);
Class<?> relinkInstanceClass = relinkInstance.getClass();
// Actually load the library!
Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass);
loadMethod.invoke(relinkInstance, context, libraryName, null, null);
}
catch (final Throwable e) {
// Fall back
try {
System.loadLibrary(libraryName);
}
catch (final UnsatisfiedLinkError ule) {
throw ule;
}
catch (final SecurityException se) {
throw se;
}
}
}
protected static Context mContext;
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,514 @@
package org.libsdl.app;
import android.content.Context;
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Build;
import android.util.Log;
import java.util.Arrays;
public class SDLAudioManager {
protected static final String TAG = "SDLAudio";
protected static AudioTrack mAudioTrack;
protected static AudioRecord mAudioRecord;
protected static Context mContext;
private static final int[] NO_DEVICES = {};
private static AudioDeviceCallback mAudioDeviceCallback;
public static void initialize() {
mAudioTrack = null;
mAudioRecord = null;
mAudioDeviceCallback = null;
if(Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */)
{
mAudioDeviceCallback = new AudioDeviceCallback() {
@Override
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
Arrays.stream(addedDevices).forEach(deviceInfo -> addAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
}
@Override
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
Arrays.stream(removedDevices).forEach(deviceInfo -> removeAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
}
};
}
}
public static void setContext(Context context) {
mContext = context;
if (context != null) {
registerAudioDeviceCallback();
}
}
public static void release(Context context) {
unregisterAudioDeviceCallback(context);
}
// Audio
protected static String getAudioFormatString(int audioFormat) {
switch (audioFormat) {
case AudioFormat.ENCODING_PCM_8BIT:
return "8-bit";
case AudioFormat.ENCODING_PCM_16BIT:
return "16-bit";
case AudioFormat.ENCODING_PCM_FLOAT:
return "float";
default:
return Integer.toString(audioFormat);
}
}
protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
int channelConfig;
int sampleSize;
int frameSize;
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz");
/* On older devices let's use known good settings */
if (Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
if (desiredChannels > 2) {
desiredChannels = 2;
}
}
/* AudioTrack has sample rate limitation of 48000 (fixed in 5.0.2) */
if (Build.VERSION.SDK_INT < 22 /* Android 5.1 (LOLLIPOP_MR1) */) {
if (sampleRate < 8000) {
sampleRate = 8000;
} else if (sampleRate > 48000) {
sampleRate = 48000;
}
}
if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
int minSDKVersion = (isCapture ? 23 /* Android 6.0 (M) */ : 21 /* Android 5.0 (LOLLIPOP) */);
if (Build.VERSION.SDK_INT < minSDKVersion) {
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
}
}
switch (audioFormat)
{
case AudioFormat.ENCODING_PCM_8BIT:
sampleSize = 1;
break;
case AudioFormat.ENCODING_PCM_16BIT:
sampleSize = 2;
break;
case AudioFormat.ENCODING_PCM_FLOAT:
sampleSize = 4;
break;
default:
Log.v(TAG, "Requested format " + audioFormat + ", getting ENCODING_PCM_16BIT");
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
sampleSize = 2;
break;
}
if (isCapture) {
switch (desiredChannels) {
case 1:
channelConfig = AudioFormat.CHANNEL_IN_MONO;
break;
case 2:
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
break;
default:
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
desiredChannels = 2;
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
break;
}
} else {
switch (desiredChannels) {
case 1:
channelConfig = AudioFormat.CHANNEL_OUT_MONO;
break;
case 2:
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
break;
case 3:
channelConfig = AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
break;
case 4:
channelConfig = AudioFormat.CHANNEL_OUT_QUAD;
break;
case 5:
channelConfig = AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
break;
case 6:
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
break;
case 7:
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
break;
case 8:
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
} else {
Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround");
desiredChannels = 6;
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
}
break;
default:
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
desiredChannels = 2;
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
break;
}
/*
Log.v(TAG, "Speaker configuration (and order of channels):");
if ((channelConfig & 0x00000004) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT");
}
if ((channelConfig & 0x00000008) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT");
}
if ((channelConfig & 0x00000010) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_CENTER");
}
if ((channelConfig & 0x00000020) != 0) {
Log.v(TAG, " CHANNEL_OUT_LOW_FREQUENCY");
}
if ((channelConfig & 0x00000040) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_LEFT");
}
if ((channelConfig & 0x00000080) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_RIGHT");
}
if ((channelConfig & 0x00000100) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT_OF_CENTER");
}
if ((channelConfig & 0x00000200) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT_OF_CENTER");
}
if ((channelConfig & 0x00000400) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_CENTER");
}
if ((channelConfig & 0x00000800) != 0) {
Log.v(TAG, " CHANNEL_OUT_SIDE_LEFT");
}
if ((channelConfig & 0x00001000) != 0) {
Log.v(TAG, " CHANNEL_OUT_SIDE_RIGHT");
}
*/
}
frameSize = (sampleSize * desiredChannels);
// Let the user pick a larger buffer if they really want -- but ye
// gods they probably shouldn't, the minimums are horrifyingly high
// latency already
int minBufferSize;
if (isCapture) {
minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
} else {
minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
}
desiredFrames = Math.max(desiredFrames, (minBufferSize + frameSize - 1) / frameSize);
int[] results = new int[4];
if (isCapture) {
if (mAudioRecord == null) {
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
channelConfig, audioFormat, desiredFrames * frameSize);
// see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
Log.e(TAG, "Failed during initialization of AudioRecord");
mAudioRecord.release();
mAudioRecord = null;
return null;
}
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
mAudioRecord.setPreferredDevice(getOutputAudioDeviceInfo(deviceId));
}
mAudioRecord.startRecording();
}
results[0] = mAudioRecord.getSampleRate();
results[1] = mAudioRecord.getAudioFormat();
results[2] = mAudioRecord.getChannelCount();
} else {
if (mAudioTrack == null) {
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
// Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
/* Try again, with safer values */
Log.e(TAG, "Failed during initialization of Audio Track");
mAudioTrack.release();
mAudioTrack = null;
return null;
}
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
mAudioTrack.setPreferredDevice(getInputAudioDeviceInfo(deviceId));
}
mAudioTrack.play();
}
results[0] = mAudioTrack.getSampleRate();
results[1] = mAudioTrack.getAudioFormat();
results[2] = mAudioTrack.getChannelCount();
}
results[3] = desiredFrames;
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", got " + results[3] + " frames of " + results[2] + " channel " + getAudioFormatString(results[1]) + " audio at " + results[0] + " Hz");
return results;
}
private static AudioDeviceInfo getInputAudioDeviceInfo(int deviceId) {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS))
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
.findFirst()
.orElse(null);
} else {
return null;
}
}
private static AudioDeviceInfo getOutputAudioDeviceInfo(int deviceId) {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS))
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
.findFirst()
.orElse(null);
} else {
return null;
}
}
private static void registerAudioDeviceCallback() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null);
}
}
private static void unregisterAudioDeviceCallback(Context context) {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
audioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] getAudioOutputDevices() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
} else {
return NO_DEVICES;
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] getAudioInputDevices() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
} else {
return NO_DEVICES;
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteFloatBuffer(float[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
if (android.os.Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
Log.e(TAG, "Attempted to make an incompatible audio call with uninitialized audio! (floating-point output is supported since Android 5.0 Lollipop)");
return;
}
for (int i = 0; i < buffer.length;) {
int result = mAudioTrack.write(buffer, i, buffer.length - i, AudioTrack.WRITE_BLOCKING);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(float)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteShortBuffer(short[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length;) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(short)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteByteBuffer(byte[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length; ) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(byte)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
}
/** This method is called by SDL using JNI. */
public static int captureReadFloatBuffer(float[] buffer, boolean blocking) {
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
return 0;
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
}
/** This method is called by SDL using JNI. */
public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
return mAudioRecord.read(buffer, 0, buffer.length);
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
}
/** This method is called by SDL using JNI. */
public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
return mAudioRecord.read(buffer, 0, buffer.length);
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
}
/** This method is called by SDL using JNI. */
public static void audioClose() {
if (mAudioTrack != null) {
mAudioTrack.stop();
mAudioTrack.release();
mAudioTrack = null;
}
}
/** This method is called by SDL using JNI. */
public static void captureClose() {
if (mAudioRecord != null) {
mAudioRecord.stop();
mAudioRecord.release();
mAudioRecord = null;
}
}
/** This method is called by SDL using JNI. */
public static void audioSetThreadPriority(boolean iscapture, int device_id) {
try {
/* Set thread name */
if (iscapture) {
Thread.currentThread().setName("SDLAudioC" + device_id);
} else {
Thread.currentThread().setName("SDLAudioP" + device_id);
}
/* Set thread priority */
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
} catch (Exception e) {
Log.v(TAG, "modify thread properties failed " + e.toString());
}
}
public static native int nativeSetupJNI();
public static native void removeAudioDevice(boolean isCapture, int deviceId);
public static native void addAudioDevice(boolean isCapture, int deviceId);
}
@@ -0,0 +1,856 @@
package org.libsdl.app;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import android.content.Context;
import android.os.Build;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.Log;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
public class SDLControllerManager
{
public static native int nativeSetupJNI();
public static native int nativeAddJoystick(int device_id, String name, String desc,
int vendor_id, int product_id,
boolean is_accelerometer, int button_mask,
int naxes, int axis_mask, int nhats, int nballs);
public static native int nativeRemoveJoystick(int device_id);
public static native int nativeAddHaptic(int device_id, String name);
public static native int nativeRemoveHaptic(int device_id);
public static native int onNativePadDown(int device_id, int keycode);
public static native int onNativePadUp(int device_id, int keycode);
public static native void onNativeJoy(int device_id, int axis,
float value);
public static native void onNativeHat(int device_id, int hat_id,
int x, int y);
protected static SDLJoystickHandler mJoystickHandler;
protected static SDLHapticHandler mHapticHandler;
private static final String TAG = "SDLControllerManager";
public static void initialize() {
if (mJoystickHandler == null) {
if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) {
mJoystickHandler = new SDLJoystickHandler_API19();
} else {
mJoystickHandler = new SDLJoystickHandler_API16();
}
}
if (mHapticHandler == null) {
if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) {
mHapticHandler = new SDLHapticHandler_API26();
} else {
mHapticHandler = new SDLHapticHandler();
}
}
}
// Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
public static boolean handleJoystickMotionEvent(MotionEvent event) {
return mJoystickHandler.handleMotionEvent(event);
}
/**
* This method is called by SDL using JNI.
*/
public static void pollInputDevices() {
mJoystickHandler.pollInputDevices();
}
/**
* This method is called by SDL using JNI.
*/
public static void pollHapticDevices() {
mHapticHandler.pollHapticDevices();
}
/**
* This method is called by SDL using JNI.
*/
public static void hapticRun(int device_id, float intensity, int length) {
mHapticHandler.run(device_id, intensity, length);
}
/**
* This method is called by SDL using JNI.
*/
public static void hapticStop(int device_id)
{
mHapticHandler.stop(device_id);
}
// Check if a given device is considered a possible SDL joystick
public static boolean isDeviceSDLJoystick(int deviceId) {
InputDevice device = InputDevice.getDevice(deviceId);
// We cannot use InputDevice.isVirtual before API 16, so let's accept
// only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
if ((device == null) || (deviceId < 0)) {
return false;
}
int sources = device.getSources();
/* This is called for every button press, so let's not spam the logs */
/*
if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
Log.v(TAG, "Input device " + device.getName() + " has class joystick.");
}
if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
}
if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
}
*/
return ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ||
((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
);
}
}
class SDLJoystickHandler {
/**
* Handles given MotionEvent.
* @param event the event to be handled.
* @return if given event was processed.
*/
public boolean handleMotionEvent(MotionEvent event) {
return false;
}
/**
* Handles adding and removing of input devices.
*/
public void pollInputDevices() {
}
}
/* Actual joystick functionality available for API >= 12 devices */
class SDLJoystickHandler_API16 extends SDLJoystickHandler {
static class SDLJoystick {
public int device_id;
public String name;
public String desc;
public ArrayList<InputDevice.MotionRange> axes;
public ArrayList<InputDevice.MotionRange> hats;
}
static class RangeComparator implements Comparator<InputDevice.MotionRange> {
@Override
public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
// Some controllers, like the Moga Pro 2, return AXIS_GAS (22) for right trigger and AXIS_BRAKE (23) for left trigger - swap them so they're sorted in the right order for SDL
int arg0Axis = arg0.getAxis();
int arg1Axis = arg1.getAxis();
if (arg0Axis == MotionEvent.AXIS_GAS) {
arg0Axis = MotionEvent.AXIS_BRAKE;
} else if (arg0Axis == MotionEvent.AXIS_BRAKE) {
arg0Axis = MotionEvent.AXIS_GAS;
}
if (arg1Axis == MotionEvent.AXIS_GAS) {
arg1Axis = MotionEvent.AXIS_BRAKE;
} else if (arg1Axis == MotionEvent.AXIS_BRAKE) {
arg1Axis = MotionEvent.AXIS_GAS;
}
// Make sure the AXIS_Z is sorted between AXIS_RY and AXIS_RZ.
// This is because the usual pairing are:
// - AXIS_X + AXIS_Y (left stick).
// - AXIS_RX, AXIS_RY (sometimes the right stick, sometimes triggers).
// - AXIS_Z, AXIS_RZ (sometimes the right stick, sometimes triggers).
// This sorts the axes in the above order, which tends to be correct
// for Xbox-ish game pads that have the right stick on RX/RY and the
// triggers on Z/RZ.
//
// Gamepads that don't have AXIS_Z/AXIS_RZ but use
// AXIS_LTRIGGER/AXIS_RTRIGGER are unaffected by this.
//
// References:
// - https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/controller-input
// - https://www.kernel.org/doc/html/latest/input/gamepad.html
if (arg0Axis == MotionEvent.AXIS_Z) {
arg0Axis = MotionEvent.AXIS_RZ - 1;
} else if (arg0Axis > MotionEvent.AXIS_Z && arg0Axis < MotionEvent.AXIS_RZ) {
--arg0Axis;
}
if (arg1Axis == MotionEvent.AXIS_Z) {
arg1Axis = MotionEvent.AXIS_RZ - 1;
} else if (arg1Axis > MotionEvent.AXIS_Z && arg1Axis < MotionEvent.AXIS_RZ) {
--arg1Axis;
}
return arg0Axis - arg1Axis;
}
}
private final ArrayList<SDLJoystick> mJoysticks;
public SDLJoystickHandler_API16() {
mJoysticks = new ArrayList<SDLJoystick>();
}
@Override
public void pollInputDevices() {
int[] deviceIds = InputDevice.getDeviceIds();
for (int device_id : deviceIds) {
if (SDLControllerManager.isDeviceSDLJoystick(device_id)) {
SDLJoystick joystick = getJoystick(device_id);
if (joystick == null) {
InputDevice joystickDevice = InputDevice.getDevice(device_id);
joystick = new SDLJoystick();
joystick.device_id = device_id;
joystick.name = joystickDevice.getName();
joystick.desc = getJoystickDescriptor(joystickDevice);
joystick.axes = new ArrayList<InputDevice.MotionRange>();
joystick.hats = new ArrayList<InputDevice.MotionRange>();
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
Collections.sort(ranges, new RangeComparator());
for (InputDevice.MotionRange range : ranges) {
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
joystick.hats.add(range);
} else {
joystick.axes.add(range);
}
}
}
mJoysticks.add(joystick);
SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc,
getVendorId(joystickDevice), getProductId(joystickDevice), false,
getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, 0);
}
}
}
/* Check removed devices */
ArrayList<Integer> removedDevices = null;
for (SDLJoystick joystick : mJoysticks) {
int device_id = joystick.device_id;
int i;
for (i = 0; i < deviceIds.length; i++) {
if (device_id == deviceIds[i]) break;
}
if (i == deviceIds.length) {
if (removedDevices == null) {
removedDevices = new ArrayList<Integer>();
}
removedDevices.add(device_id);
}
}
if (removedDevices != null) {
for (int device_id : removedDevices) {
SDLControllerManager.nativeRemoveJoystick(device_id);
for (int i = 0; i < mJoysticks.size(); i++) {
if (mJoysticks.get(i).device_id == device_id) {
mJoysticks.remove(i);
break;
}
}
}
}
}
protected SDLJoystick getJoystick(int device_id) {
for (SDLJoystick joystick : mJoysticks) {
if (joystick.device_id == device_id) {
return joystick;
}
}
return null;
}
@Override
public boolean handleMotionEvent(MotionEvent event) {
int actionPointerIndex = event.getActionIndex();
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_MOVE) {
SDLJoystick joystick = getJoystick(event.getDeviceId());
if (joystick != null) {
for (int i = 0; i < joystick.axes.size(); i++) {
InputDevice.MotionRange range = joystick.axes.get(i);
/* Normalize the value to -1...1 */
float value = (event.getAxisValue(range.getAxis(), actionPointerIndex) - range.getMin()) / range.getRange() * 2.0f - 1.0f;
SDLControllerManager.onNativeJoy(joystick.device_id, i, value);
}
for (int i = 0; i < joystick.hats.size() / 2; i++) {
int hatX = Math.round(event.getAxisValue(joystick.hats.get(2 * i).getAxis(), actionPointerIndex));
int hatY = Math.round(event.getAxisValue(joystick.hats.get(2 * i + 1).getAxis(), actionPointerIndex));
SDLControllerManager.onNativeHat(joystick.device_id, i, hatX, hatY);
}
}
}
return true;
}
public String getJoystickDescriptor(InputDevice joystickDevice) {
String desc = joystickDevice.getDescriptor();
if (desc != null && !desc.isEmpty()) {
return desc;
}
return joystickDevice.getName();
}
public int getProductId(InputDevice joystickDevice) {
return 0;
}
public int getVendorId(InputDevice joystickDevice) {
return 0;
}
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
return -1;
}
public int getButtonMask(InputDevice joystickDevice) {
return -1;
}
}
class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 {
@Override
public int getProductId(InputDevice joystickDevice) {
return joystickDevice.getProductId();
}
@Override
public int getVendorId(InputDevice joystickDevice) {
return joystickDevice.getVendorId();
}
@Override
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
// For compatibility, keep computing the axis mask like before,
// only really distinguishing 2, 4 and 6 axes.
int axis_mask = 0;
if (ranges.size() >= 2) {
// ((1 << SDL_GAMEPAD_AXIS_LEFTX) | (1 << SDL_GAMEPAD_AXIS_LEFTY))
axis_mask |= 0x0003;
}
if (ranges.size() >= 4) {
// ((1 << SDL_GAMEPAD_AXIS_RIGHTX) | (1 << SDL_GAMEPAD_AXIS_RIGHTY))
axis_mask |= 0x000c;
}
if (ranges.size() >= 6) {
// ((1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER) | (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER))
axis_mask |= 0x0030;
}
// Also add an indicator bit for whether the sorting order has changed.
// This serves to disable outdated gamecontrollerdb.txt mappings.
boolean have_z = false;
boolean have_past_z_before_rz = false;
for (InputDevice.MotionRange range : ranges) {
int axis = range.getAxis();
if (axis == MotionEvent.AXIS_Z) {
have_z = true;
} else if (axis > MotionEvent.AXIS_Z && axis < MotionEvent.AXIS_RZ) {
have_past_z_before_rz = true;
}
}
if (have_z && have_past_z_before_rz) {
// If both these exist, the compare() function changed sorting order.
// Set a bit to indicate this fact.
axis_mask |= 0x8000;
}
return axis_mask;
}
@Override
public int getButtonMask(InputDevice joystickDevice) {
int button_mask = 0;
int[] keys = new int[] {
KeyEvent.KEYCODE_BUTTON_A,
KeyEvent.KEYCODE_BUTTON_B,
KeyEvent.KEYCODE_BUTTON_X,
KeyEvent.KEYCODE_BUTTON_Y,
KeyEvent.KEYCODE_BACK,
KeyEvent.KEYCODE_MENU,
KeyEvent.KEYCODE_BUTTON_MODE,
KeyEvent.KEYCODE_BUTTON_START,
KeyEvent.KEYCODE_BUTTON_THUMBL,
KeyEvent.KEYCODE_BUTTON_THUMBR,
KeyEvent.KEYCODE_BUTTON_L1,
KeyEvent.KEYCODE_BUTTON_R1,
KeyEvent.KEYCODE_DPAD_UP,
KeyEvent.KEYCODE_DPAD_DOWN,
KeyEvent.KEYCODE_DPAD_LEFT,
KeyEvent.KEYCODE_DPAD_RIGHT,
KeyEvent.KEYCODE_BUTTON_SELECT,
KeyEvent.KEYCODE_DPAD_CENTER,
// These don't map into any SDL controller buttons directly
KeyEvent.KEYCODE_BUTTON_L2,
KeyEvent.KEYCODE_BUTTON_R2,
KeyEvent.KEYCODE_BUTTON_C,
KeyEvent.KEYCODE_BUTTON_Z,
KeyEvent.KEYCODE_BUTTON_1,
KeyEvent.KEYCODE_BUTTON_2,
KeyEvent.KEYCODE_BUTTON_3,
KeyEvent.KEYCODE_BUTTON_4,
KeyEvent.KEYCODE_BUTTON_5,
KeyEvent.KEYCODE_BUTTON_6,
KeyEvent.KEYCODE_BUTTON_7,
KeyEvent.KEYCODE_BUTTON_8,
KeyEvent.KEYCODE_BUTTON_9,
KeyEvent.KEYCODE_BUTTON_10,
KeyEvent.KEYCODE_BUTTON_11,
KeyEvent.KEYCODE_BUTTON_12,
KeyEvent.KEYCODE_BUTTON_13,
KeyEvent.KEYCODE_BUTTON_14,
KeyEvent.KEYCODE_BUTTON_15,
KeyEvent.KEYCODE_BUTTON_16,
};
int[] masks = new int[] {
(1 << 0), // A -> A
(1 << 1), // B -> B
(1 << 2), // X -> X
(1 << 3), // Y -> Y
(1 << 4), // BACK -> BACK
(1 << 6), // MENU -> START
(1 << 5), // MODE -> GUIDE
(1 << 6), // START -> START
(1 << 7), // THUMBL -> LEFTSTICK
(1 << 8), // THUMBR -> RIGHTSTICK
(1 << 9), // L1 -> LEFTSHOULDER
(1 << 10), // R1 -> RIGHTSHOULDER
(1 << 11), // DPAD_UP -> DPAD_UP
(1 << 12), // DPAD_DOWN -> DPAD_DOWN
(1 << 13), // DPAD_LEFT -> DPAD_LEFT
(1 << 14), // DPAD_RIGHT -> DPAD_RIGHT
(1 << 4), // SELECT -> BACK
(1 << 0), // DPAD_CENTER -> A
(1 << 15), // L2 -> ??
(1 << 16), // R2 -> ??
(1 << 17), // C -> ??
(1 << 18), // Z -> ??
(1 << 20), // 1 -> ??
(1 << 21), // 2 -> ??
(1 << 22), // 3 -> ??
(1 << 23), // 4 -> ??
(1 << 24), // 5 -> ??
(1 << 25), // 6 -> ??
(1 << 26), // 7 -> ??
(1 << 27), // 8 -> ??
(1 << 28), // 9 -> ??
(1 << 29), // 10 -> ??
(1 << 30), // 11 -> ??
(1 << 31), // 12 -> ??
// We're out of room...
0xFFFFFFFF, // 13 -> ??
0xFFFFFFFF, // 14 -> ??
0xFFFFFFFF, // 15 -> ??
0xFFFFFFFF, // 16 -> ??
};
boolean[] has_keys = joystickDevice.hasKeys(keys);
for (int i = 0; i < keys.length; ++i) {
if (has_keys[i]) {
button_mask |= masks[i];
}
}
return button_mask;
}
}
class SDLHapticHandler_API26 extends SDLHapticHandler {
@Override
public void run(int device_id, float intensity, int length) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
Log.d("SDL", "Rtest: Vibe with intensity " + intensity + " for " + length);
if (intensity == 0.0f) {
stop(device_id);
return;
}
int vibeValue = Math.round(intensity * 255);
if (vibeValue > 255) {
vibeValue = 255;
}
if (vibeValue < 1) {
stop(device_id);
return;
}
try {
haptic.vib.vibrate(VibrationEffect.createOneShot(length, vibeValue));
}
catch (Exception e) {
// Fall back to the generic method, which uses DEFAULT_AMPLITUDE, but works even if
// something went horribly wrong with the Android 8.0 APIs.
haptic.vib.vibrate(length);
}
}
}
}
class SDLHapticHandler {
static class SDLHaptic {
public int device_id;
public String name;
public Vibrator vib;
}
private final ArrayList<SDLHaptic> mHaptics;
public SDLHapticHandler() {
mHaptics = new ArrayList<SDLHaptic>();
}
public void run(int device_id, float intensity, int length) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
haptic.vib.vibrate(length);
}
}
public void stop(int device_id) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
haptic.vib.cancel();
}
}
public void pollHapticDevices() {
final int deviceId_VIBRATOR_SERVICE = 999999;
boolean hasVibratorService = false;
int[] deviceIds = InputDevice.getDeviceIds();
// It helps processing the device ids in reverse order
// For example, in the case of the XBox 360 wireless dongle,
// so the first controller seen by SDL matches what the receiver
// considers to be the first controller
for (int i = deviceIds.length - 1; i > -1; i--) {
SDLHaptic haptic = getHaptic(deviceIds[i]);
if (haptic == null) {
InputDevice device = InputDevice.getDevice(deviceIds[i]);
Vibrator vib = device.getVibrator();
if (vib != null) {
if (vib.hasVibrator()) {
haptic = new SDLHaptic();
haptic.device_id = deviceIds[i];
haptic.name = device.getName();
haptic.vib = vib;
mHaptics.add(haptic);
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
}
}
}
}
/* Check VIBRATOR_SERVICE */
Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
if (vib != null) {
hasVibratorService = vib.hasVibrator();
if (hasVibratorService) {
SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
if (haptic == null) {
haptic = new SDLHaptic();
haptic.device_id = deviceId_VIBRATOR_SERVICE;
haptic.name = "VIBRATOR_SERVICE";
haptic.vib = vib;
mHaptics.add(haptic);
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
}
}
}
/* Check removed devices */
ArrayList<Integer> removedDevices = null;
for (SDLHaptic haptic : mHaptics) {
int device_id = haptic.device_id;
int i;
for (i = 0; i < deviceIds.length; i++) {
if (device_id == deviceIds[i]) break;
}
if (device_id != deviceId_VIBRATOR_SERVICE || !hasVibratorService) {
if (i == deviceIds.length) {
if (removedDevices == null) {
removedDevices = new ArrayList<Integer>();
}
removedDevices.add(device_id);
}
} // else: don't remove the vibrator if it is still present
}
if (removedDevices != null) {
for (int device_id : removedDevices) {
SDLControllerManager.nativeRemoveHaptic(device_id);
for (int i = 0; i < mHaptics.size(); i++) {
if (mHaptics.get(i).device_id == device_id) {
mHaptics.remove(i);
break;
}
}
}
}
}
protected SDLHaptic getHaptic(int device_id) {
for (SDLHaptic haptic : mHaptics) {
if (haptic.device_id == device_id) {
return haptic;
}
}
return null;
}
}
class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
// Generic Motion (mouse hover, joystick...) events go here
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
float x, y;
int action;
switch ( event.getSource() ) {
case InputDevice.SOURCE_JOYSTICK:
return SDLControllerManager.handleJoystickMotionEvent(event);
case InputDevice.SOURCE_MOUSE:
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
default:
break;
}
break;
default:
break;
}
// Event was not managed
return false;
}
public boolean supportsRelativeMouse() {
return false;
}
public boolean inRelativeMode() {
return false;
}
public boolean setRelativeMouseEnabled(boolean enabled) {
return false;
}
public void reclaimRelativeMouseModeIfNeeded()
{
}
public float getEventX(MotionEvent event) {
return event.getX(0);
}
public float getEventY(MotionEvent event) {
return event.getY(0);
}
}
class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API12 {
// Generic Motion (mouse hover, joystick...) events go here
private boolean mRelativeModeEnabled;
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
// Handle relative mouse mode
if (mRelativeModeEnabled) {
if (event.getSource() == InputDevice.SOURCE_MOUSE) {
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_HOVER_MOVE) {
float x = event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
float y = event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
}
}
}
// Event was not managed, call SDLGenericMotionListener_API12 method
return super.onGenericMotion(v, event);
}
@Override
public boolean supportsRelativeMouse() {
return true;
}
@Override
public boolean inRelativeMode() {
return mRelativeModeEnabled;
}
@Override
public boolean setRelativeMouseEnabled(boolean enabled) {
mRelativeModeEnabled = enabled;
return true;
}
@Override
public float getEventX(MotionEvent event) {
if (mRelativeModeEnabled) {
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
} else {
return event.getX(0);
}
}
@Override
public float getEventY(MotionEvent event) {
if (mRelativeModeEnabled) {
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
} else {
return event.getY(0);
}
}
}
class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
// Generic Motion (mouse hover, joystick...) events go here
private boolean mRelativeModeEnabled;
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
float x, y;
int action;
switch ( event.getSource() ) {
case InputDevice.SOURCE_JOYSTICK:
return SDLControllerManager.handleJoystickMotionEvent(event);
case InputDevice.SOURCE_MOUSE:
// DeX desktop mouse cursor is a separate non-standard input type.
case InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN:
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
default:
break;
}
break;
case InputDevice.SOURCE_MOUSE_RELATIVE:
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
default:
break;
}
break;
default:
break;
}
// Event was not managed
return false;
}
@Override
public boolean supportsRelativeMouse() {
return (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */);
}
@Override
public boolean inRelativeMode() {
return mRelativeModeEnabled;
}
@Override
public boolean setRelativeMouseEnabled(boolean enabled) {
if (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */) {
if (enabled) {
SDLActivity.getContentView().requestPointerCapture();
} else {
SDLActivity.getContentView().releasePointerCapture();
}
mRelativeModeEnabled = enabled;
return true;
} else {
return false;
}
}
@Override
public void reclaimRelativeMouseModeIfNeeded()
{
if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) {
SDLActivity.getContentView().requestPointerCapture();
}
}
@Override
public float getEventX(MotionEvent event) {
// Relative mouse in capture mode will only have relative for X/Y
return event.getX(0);
}
@Override
public float getEventY(MotionEvent event) {
// Relative mouse in capture mode will only have relative for X/Y
return event.getY(0);
}
}
@@ -0,0 +1,405 @@
package org.libsdl.app;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Build;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
/**
SDLSurface. This is what we draw on, so we need to know when it's created
in order to do anything useful.
Because of this, that's where we set up the SDL thread
*/
public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
// Sensors
protected SensorManager mSensorManager;
protected Display mDisplay;
// Keep track of the surface size to normalize touch events
protected float mWidth, mHeight;
// Is SurfaceView ready for rendering
public boolean mIsSurfaceReady;
// Startup
public SDLSurface(Context context) {
super(context);
getHolder().addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnKeyListener(this);
setOnTouchListener(this);
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
setOnGenericMotionListener(SDLActivity.getMotionListener());
// Some arbitrary defaults to avoid a potential division by zero
mWidth = 1.0f;
mHeight = 1.0f;
mIsSurfaceReady = false;
}
public void handlePause() {
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
}
public void handleResume() {
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnKeyListener(this);
setOnTouchListener(this);
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
}
public Surface getNativeSurface() {
return getHolder().getSurface();
}
// Called when we have a valid drawing surface
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.v("SDL", "surfaceCreated()");
SDLActivity.onNativeSurfaceCreated();
}
// Called when we lose the surface
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.v("SDL", "surfaceDestroyed()");
// Transition to pause, if needed
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
SDLActivity.handleNativeState();
mIsSurfaceReady = false;
SDLActivity.onNativeSurfaceDestroyed();
}
// Called when the surface is resized
@Override
public void surfaceChanged(SurfaceHolder holder,
int format, int width, int height) {
Log.v("SDL", "surfaceChanged()");
if (SDLActivity.mSingleton == null) {
return;
}
mWidth = width;
mHeight = height;
int nDeviceWidth = width;
int nDeviceHeight = height;
try
{
if (Build.VERSION.SDK_INT >= 17 /* Android 4.2 (JELLY_BEAN_MR1) */) {
DisplayMetrics realMetrics = new DisplayMetrics();
mDisplay.getRealMetrics( realMetrics );
nDeviceWidth = realMetrics.widthPixels;
nDeviceHeight = realMetrics.heightPixels;
}
} catch(Exception ignored) {
}
synchronized(SDLActivity.getContext()) {
// In case we're waiting on a size change after going fullscreen, send a notification.
SDLActivity.getContext().notifyAll();
}
Log.v("SDL", "Window size: " + width + "x" + height);
Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate());
SDLActivity.onNativeResize();
// Prevent a screen distortion glitch,
// for instance when the device is in Landscape and a Portrait App is resumed.
boolean skip = false;
int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
if (mWidth > mHeight) {
skip = true;
}
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
if (mWidth < mHeight) {
skip = true;
}
}
// Special Patch for Square Resolution: Black Berry Passport
if (skip) {
double min = Math.min(mWidth, mHeight);
double max = Math.max(mWidth, mHeight);
if (max / min < 1.20) {
Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
skip = false;
}
}
// Don't skip in MultiWindow.
if (skip) {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
if (SDLActivity.mSingleton.isInMultiWindowMode()) {
Log.v("SDL", "Don't skip in Multi-Window");
skip = false;
}
}
}
if (skip) {
Log.v("SDL", "Skip .. Surface is not ready.");
mIsSurfaceReady = false;
return;
}
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
SDLActivity.onNativeSurfaceChanged();
/* Surface is ready */
mIsSurfaceReady = true;
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
SDLActivity.handleNativeState();
}
// Key events
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
return SDLActivity.handleKeyEvent(v, keyCode, event, null);
}
// Touch events
@Override
public boolean onTouch(View v, MotionEvent event) {
/* Ref: http://developer.android.com/training/gestures/multi.html */
int touchDevId = event.getDeviceId();
final int pointerCount = event.getPointerCount();
int action = event.getActionMasked();
int pointerFingerId;
int i = -1;
float x,y,p;
/*
* Prevent id to be -1, since it's used in SDL internal for synthetic events
* Appears when using Android emulator, eg:
* adb shell input mouse tap 100 100
* adb shell input touchscreen tap 100 100
*/
if (touchDevId < 0) {
touchDevId -= 1;
}
// 12290 = Samsung DeX mode desktop mouse
// 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN
// 0x2 = SOURCE_CLASS_POINTER
if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) {
int mouseButton = 1;
try {
Object object = event.getClass().getMethod("getButtonState").invoke(event);
if (object != null) {
mouseButton = (Integer) object;
}
} catch(Exception ignored) {
}
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
// if we are. We'll leverage our existing mouse motion listener
SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
x = motionListener.getEventX(event);
y = motionListener.getEventY(event);
SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
} else {
switch(action) {
case MotionEvent.ACTION_MOVE:
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_DOWN:
// Primary pointer up/down, the index is always zero
i = 0;
/* fallthrough */
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN:
// Non primary pointer up/down
if (i == -1) {
i = event.getActionIndex();
}
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
break;
case MotionEvent.ACTION_CANCEL:
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
}
break;
default:
break;
}
}
return true;
}
// Sensor events
public void enableSensor(int sensortype, boolean enabled) {
// TODO: This uses getDefaultSensor - what if we have >1 accels?
if (enabled) {
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(sensortype),
SensorManager.SENSOR_DELAY_GAME, null);
} else {
mSensorManager.unregisterListener(this,
mSensorManager.getDefaultSensor(sensortype));
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
// Since we may have an orientation set, we won't receive onConfigurationChanged events.
// We thus should check here.
int newOrientation;
float x, y;
switch (mDisplay.getRotation()) {
case Surface.ROTATION_90:
x = -event.values[1];
y = event.values[0];
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
break;
case Surface.ROTATION_270:
x = event.values[1];
y = -event.values[0];
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
break;
case Surface.ROTATION_180:
x = -event.values[0];
y = -event.values[1];
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
break;
case Surface.ROTATION_0:
default:
x = event.values[0];
y = event.values[1];
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
break;
}
if (newOrientation != SDLActivity.mCurrentOrientation) {
SDLActivity.mCurrentOrientation = newOrientation;
SDLActivity.onNativeOrientationChanged(newOrientation);
}
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
y / SensorManager.GRAVITY_EARTH,
event.values[2] / SensorManager.GRAVITY_EARTH);
}
}
// Captured pointer events for API 26.
public boolean onCapturedPointerEvent(MotionEvent event)
{
int action = event.getActionMasked();
float x, y;
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
case MotionEvent.ACTION_BUTTON_PRESS:
case MotionEvent.ACTION_BUTTON_RELEASE:
// Change our action value to what SDL's code expects.
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
action = MotionEvent.ACTION_DOWN;
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
action = MotionEvent.ACTION_UP;
}
x = event.getX(0);
y = event.getY(0);
int button = event.getButtonState();
SDLActivity.onNativeMouse(button, action, x, y, true);
return true;
}
return false;
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>
@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Game</string>
</resources>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.NoTitleBar.Fullscreen">
<!-- Customize your theme here. -->
</style>
</resources>
+25
View File
@@ -0,0 +1,25 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
mavenCentral()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.1.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenCentral()
google()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
+17
View File
@@ -0,0 +1,17 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
Binary file not shown.
+6
View File
@@ -0,0 +1,6 @@
#Sat Jan 10 10:31:05 MSK 2026
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+160
View File
@@ -0,0 +1,160 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|grep -E -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|grep -E -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+90
View File
@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+1
View File
@@ -0,0 +1 @@
include ':app'
+231
View File
@@ -0,0 +1,231 @@
cmake_minimum_required(VERSION 3.15)
# Фикс для Ninja на Windows
if(NOT CMAKE_MAKE_PROGRAM AND WIN32)
set(POTENTIAL_NINJA "${CMAKE_CURRENT_SOURCE_DIR}/ninja/ninja.exe")
if(EXISTS "${POTENTIAL_NINJA}")
set(CMAKE_MAKE_PROGRAM "${POTENTIAL_NINJA}" CACHE FILEPATH "Path to ninja" FORCE)
endif()
endif()
project(bishkek-witcher LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# --- АВТО-ЗАГРУЗКА ЗАВИСИМОСТЕЙ ---
include("${CMAKE_CURRENT_SOURCE_DIR}/../cmake/FetchDependencies.cmake")
# Теперь гарантированно есть папка ../thirdparty со всеми исходниками
# ===========================================
# Lua (5.5.0) - embedded scripting, pure C, compiles with Emscripten
# ===========================================
set(LUA_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/lua-5.4.8")
file(GLOB LUA_SOURCES "${LUA_SRC_DIR}/*.c")
list(REMOVE_ITEM LUA_SOURCES
"${LUA_SRC_DIR}/lua.c"
"${LUA_SRC_DIR}/luac.c"
"${LUA_SRC_DIR}/onelua.c"
)
add_library(lua_static STATIC ${LUA_SOURCES})
target_include_directories(lua_static PUBLIC "${LUA_SRC_DIR}")
# sol2 (header-only)
set(SOL2_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/sol2-3.3.0")
# Apply patch for Clang/Emscripten compatibility in optional<T&>::emplace().
set(_sol2_sentinel "${SOL2_SRC_DIR}/.patched")
if(NOT EXISTS "${_sol2_sentinel}")
find_package(Git QUIET)
if(GIT_FOUND)
execute_process(
COMMAND ${GIT_EXECUTABLE} apply --ignore-whitespace
"${CMAKE_CURRENT_SOURCE_DIR}/../cmake/patches/sol2-3.3.0-clang-optional.patch"
WORKING_DIRECTORY "${SOL2_SRC_DIR}"
RESULT_VARIABLE _sol2_patch_res
)
if(_sol2_patch_res EQUAL 0)
file(WRITE "${_sol2_sentinel}" "patched\n")
message(STATUS "Applied sol2 Clang optional patch")
else()
message(WARNING "sol2 patch failed (exit ${_sol2_patch_res}) — Clang/Emscripten builds may not compile")
endif()
else()
message(WARNING "Git not found — cannot apply sol2 patch automatically. "
"Apply cmake/patches/sol2-3.3.0-clang-optional.patch manually.")
endif()
endif()
# Список исходных файлов (синхронизирован с proj-windows/CMakeLists.txt)
set(SOURCES
../src/main.cpp
../src/Game.cpp
../src/Game.h
../src/Character.cpp
../src/Character.h
../src/Environment.cpp
../src/Environment.h
../src/render/Renderer.cpp
../src/render/Renderer.h
../src/render/ShaderManager.cpp
../src/render/ShaderManager.h
../src/render/TextureManager.cpp
../src/render/TextureManager.h
../src/TextModel.cpp
../src/TextModel.h
../src/AudioPlayerAsync.cpp
../src/AudioPlayerAsync.h
../src/BoneAnimatedModel.cpp
../src/BoneAnimatedModel.h
../src/BoneAnimatedModelNew.cpp
../src/BoneAnimatedModelNew.h
../src/render/OpenGlExtensions.cpp
../src/render/OpenGlExtensions.h
../src/utils/Utils.cpp
../src/utils/Utils.h
../src/SparkEmitter.cpp
../src/SparkEmitter.h
../src/utils/Perlin.cpp
../src/utils/Perlin.h
../src/utils/TaskManager.cpp
../src/utils/TaskManager.h
../src/render/FrameBuffer.cpp
../src/render/FrameBuffer.h
../src/render/ShadowMap.cpp
../src/render/ShadowMap.h
../src/UiManager.cpp
../src/UiManager.h
../src/render/TextRenderer.h
../src/render/TextRenderer.cpp
../src/MenuManager.h
../src/MenuManager.cpp
../src/Location.h
../src/Location.cpp
../src/GameConstants.h
../src/GameConstants.cpp
../src/ScriptEngine.h
../src/ScriptEngine.cpp
../src/navigation/PathFinder.h
../src/navigation/PathFinder.cpp
../src/items/GameObjectLoader.h
../src/items/GameObjectLoader.cpp
../src/items/Item.h
../src/items/Item.cpp
../src/items/InteractiveObject.h
../src/items/InteractiveObject.cpp
../src/dialogue/DialogueTypes.h
../src/dialogue/DialogueDatabase.h
../src/dialogue/DialogueDatabase.cpp
../src/dialogue/DialogueRuntime.h
../src/dialogue/DialogueRuntime.cpp
../src/dialogue/DialogueOverlay.h
../src/dialogue/DialogueOverlay.cpp
../src/dialogue/DialogueSystem.h
../src/dialogue/DialogueSystem.cpp
)
add_executable(bishkek-witcher ${SOURCES})
# Настройка путей к инклудам (используем скачанные исходники)
target_include_directories(bishkek-witcher PRIVATE
../src
../thirdparty/eigen-5.0.0
../thirdparty/boost_1_90_0
"${SOL2_SRC_DIR}/include"
)
# Сборка libzip через add_subdirectory (Emscripten соберет её из исходников)
set(ENABLE_GNUTLS OFF CACHE BOOL "" FORCE)
set(ENABLE_OPENSSL OFF CACHE BOOL "" FORCE)
set(ENABLE_WINDOWS_CRYPTO OFF CACHE BOOL "" FORCE)
set(ENABLE_COMMONCRYPTO OFF CACHE BOOL "" FORCE)
add_subdirectory("../thirdparty/libzip-1.11.4" libzip-build)
target_link_libraries(bishkek-witcher PRIVATE zip z lua_static websocket.js)
# Эмскриптен-флаги
set(EMSCRIPTEN_FLAGS
"-sUSE_SDL=2"
"-sUSE_SDL_IMAGE=2"
"-sUSE_LIBPNG=1"
"-sUSE_ZLIB=1"
"-sUSE_SDL_TTF=2"
"-sUSE_SDL_MIXER=2"
"-sSDL2_MIXER_FORMATS=[ogg,mp3]"
#"-pthread"
#"-sUSE_PTHREADS=1"
"-fexceptions"
"-DNETWORK"
)
target_compile_options(bishkek-witcher PRIVATE ${EMSCRIPTEN_FLAGS} "-O2")
# Only loading.png and the shaders used before resources.zip is ready are preloaded.
# resources.zip is downloaded asynchronously at runtime and served as a separate file.
set(EMSCRIPTEN_LINK_FLAGS
${EMSCRIPTEN_FLAGS}
"-O2"
#"-sPTHREAD_POOL_SIZE=4"
"-sALLOW_MEMORY_GROWTH=1"
"-sFULL_ES3=1"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/loading.png@resources/loading.png"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/shaders@resources/shaders"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start.lua@resources/start.lua"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../audio@audio"
)
# Применяем настройки линковки
target_link_options(bishkek-witcher PRIVATE ${EMSCRIPTEN_LINK_FLAGS})
# Для совместимости со старыми версиями CMake, если target_link_options недостаточно
string(REPLACE ";" " " EMSCRIPTEN_LINK_FLAGS_STR "${EMSCRIPTEN_LINK_FLAGS}")
set_target_properties(bishkek-witcher PROPERTIES
LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS_STR}"
SUFFIX ".html"
)
# --- Ресурсы и Деплой (без изменений) ---
set(RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../resources")
set(RESOURCES_ZIP "${CMAKE_CURRENT_BINARY_DIR}/resources.zip")
get_filename_component(RESOURCES_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE)
add_custom_command(
OUTPUT "${RESOURCES_ZIP}"
COMMAND ${CMAKE_COMMAND} -E tar "cf" "${RESOURCES_ZIP}" --format=zip "resources"
WORKING_DIRECTORY "${RESOURCES_PARENT_DIR}"
DEPENDS "${RESOURCES_PARENT_DIR}/resources"
)
add_custom_target(pack_resources DEPENDS "${RESOURCES_ZIP}")
add_dependencies(bishkek-witcher pack_resources)
# Определяем путь к директории установки (относительно папки билда)
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/public")
# 1. Устанавливаем основной HTML файл
install(TARGETS bishkek-witcher
RUNTIME DESTINATION .
)
# 2. Устанавливаем сопутствующие файлы (JS, WASM и сгенерированный архив данных)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.js"
"${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.wasm"
"${CMAKE_CURRENT_BINARY_DIR}/bishkek-witcher.data"
DESTINATION .
)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/index.html"
DESTINATION .
)
# resources.zip is served separately and downloaded asynchronously at runtime
install(FILES "${RESOURCES_ZIP}" DESTINATION .)
add_custom_command(TARGET bishkek-witcher POST_BUILD
COMMAND ${CMAKE_COMMAND} --install .
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
COMMENT "Automatically deploying to public directory..."
)
+39
View File
@@ -0,0 +1,39 @@
# how to build
If emsdk is not installed, you need to clone it from here: https://github.com/emscripten-core/emsdk
and install:
```
C:\Work\Projects\emsdk\emsdk.bat install latest
```
Then activate the environment:
```
C:\Work\Projects\emsdk\emsdk.bat activate latest
C:\Work\Projects\emsdk\emsdk_env.bat
```
Optionally clear cache:
```
emcc --clear-cache
embuilder build sdl2 sdl2_ttf sdl2_image sdl2_image_jpg sdl2_image_png
```
Build:
```
mkdir build
cd build
emcmake cmake -G Ninja ..
cmake --build .
```
Optionally install:
```
cmake --install .
```
Run:
```
emrun --no_browser --port 8080 public
```
+82
View File
@@ -0,0 +1,82 @@
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>Bishkek Witcher</title>
<style>
body, html {
margin: 0; padding: 0; width: 100%; height: 100%;
overflow: hidden; background-color: #000;
position: fixed;
}
#canvas {
display: block;
position: absolute;
top: 0; left: 0;
width: 100vw; height: 100vh;
border: none;
}
#fs-button {
position: absolute;
top: 10px; right: 10px;
padding: 10px;
z-index: 10;
background: rgba(255,255,255,0.3);
color: white; border: 1px solid white;
cursor: pointer;
font-family: sans-serif;
border-radius: 5px;
}
#status { color: white; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
</style>
</head>
<body>
<button id="fs-button">Fullscreen</button>
<div id="status">Downloading...</div>
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
<script>
function prepareModuleEnvironment() {
window.Module = window.Module || {};
var canvasEl = document.getElementById('canvas');
window.Module.canvas = canvasEl;
window.Module.setStatus = window.Module.setStatus || function (text) {
var statusElement = document.getElementById("status");
statusElement.innerHTML = text;
statusElement.style.display = text ? 'block' : 'none';
};
}
function loadGameScript() {
var s = document.createElement('script');
s.src = 'bishkek-witcher.js';
s.async = true;
document.body.appendChild(s);
}
document.getElementById('fs-button').addEventListener('click', function() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen().catch(function(e) {
console.error('Fullscreen error: ' + e.message);
});
} else {
document.exitFullscreen();
}
});
document.addEventListener('DOMContentLoaded', function() {
prepareModuleEnvironment();
loadGameScript();
});
window.addEventListener("orientationchange", function() {
setTimeout(() => {
window.dispatchEvent(new Event('resize'));
}, 200);
});
</script>
</body>
</html>
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
<!doctypehtml><html lang=en-us><head><meta charset=utf-8><meta content="text/html; charset=utf-8"http-equiv=Content-Type><title>Emscripten-Generated Code</title><style>body{font-family:arial;margin:0;padding:none}.emscripten{padding-right:0;margin-left:auto;margin-right:auto;display:block}div.emscripten{text-align:center}div.emscripten_border{border:1px solid #000}canvas.emscripten{border:0 none;background-color:#000}#emscripten_logo{display:inline-block;margin:0;padding:6px;width:265px}.spinner{height:30px;width:30px;margin:0;margin-top:20px;margin-left:20px;display:inline-block;vertical-align:top;-webkit-animation:rotation .8s linear infinite;-moz-animation:rotation .8s linear infinite;-o-animation:rotation .8s linear infinite;animation:rotation .8s linear infinite;border-left:5px solid #ebebeb;border-right:5px solid #ebebeb;border-bottom:5px solid #ebebeb;border-top:5px solid #787878;border-radius:100%;background-color:#bdd72e}@-webkit-keyframes rotation{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(360deg)}}@-moz-keyframes rotation{from{-moz-transform:rotate(0)}to{-moz-transform:rotate(360deg)}}@-o-keyframes rotation{from{-o-transform:rotate(0)}to{-o-transform:rotate(360deg)}}@keyframes rotation{from{transform:rotate(0)}to{transform:rotate(360deg)}}#status{display:inline-block;vertical-align:top;margin-top:30px;margin-left:20px;font-weight:700;color:#787878}#progress{height:20px;width:300px}#controls{display:inline-block;float:right;vertical-align:top;margin-top:30px;margin-right:20px}#output{width:100%;height:200px;margin:0 auto;margin-top:10px;border-left:0;border-right:0px;padding-left:0;padding-right:0;display:block;background-color:#000;color:#fff;font-family:'Lucida Console',Monaco,monospace;outline:0}</style></head><body><script src="https://cdn.jsdelivr.net/npm/eruda"></script>
<script>eruda.init();</script><a href=http://emscripten.org><img id=emscripten_logo src=""></a><div class=spinner id=spinner></div><div class=emscripten id=status>Downloading...</div><span id=controls><span><input type=checkbox id=resize>Resize canvas</span> <span><input type=checkbox id=pointerLock checked>Lock/hide mouse pointer    </span><span><input type=button onclick='Module.requestFullscreen(document.getElementById("pointerLock").checked,document.getElementById("resize").checked)'value=Fullscreen></span></span><div class=emscripten><progress hidden id=progress max=100 value=0></progress></div><div class=emscripten_border><canvas class=emscripten id=canvas oncontextmenu=event.preventDefault() tabindex=-1></canvas></div><textarea id=output rows=8></textarea><script>var statusElement=document.getElementById("status"),progressElement=document.getElementById("progress"),spinnerElement=document.getElementById("spinner"),canvasElement=document.getElementById("canvas"),outputElement=document.getElementById("output");outputElement&&(outputElement.value=""),canvasElement.addEventListener("webglcontextlost",(e=>{alert("WebGL context lost. You will need to reload the page."),e.preventDefault()}),!1);var Module={print(...e){if(console.log(...e),outputElement){var t=e.join(" ");outputElement.value+=t+"\n",outputElement.scrollTop=outputElement.scrollHeight}},canvas:canvasElement,setStatus(e){if(Module.setStatus.last||(Module.setStatus.last={time:Date.now(),text:""}),e!==Module.setStatus.last.text){var t=e.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/),n=Date.now();t&&n-Module.setStatus.last.time<30||(Module.setStatus.last.time=n,Module.setStatus.last.text=e,t?(e=t[1],progressElement.value=100*parseInt(t[2]),progressElement.max=100*parseInt(t[4]),progressElement.hidden=!1,spinnerElement.hidden=!1):(progressElement.value=null,progressElement.max=null,progressElement.hidden=!0,e||(spinnerElement.style.display="none")),statusElement.innerHTML=e)}},totalDependencies:0,monitorRunDependencies(e){this.totalDependencies=Math.max(this.totalDependencies,e),Module.setStatus(e?"Preparing... ("+(this.totalDependencies-e)+"/"+this.totalDependencies+")":"All downloads complete.")}};Module.setStatus("Downloading..."),window.onerror=e=>{Module.setStatus("Exception thrown, see JavaScript console"),spinnerElement.style.display="none",Module.setStatus=e=>{e&&console.error("[post-exception status] "+e)}}</script><script async src="space-game001.js" crossorigin="anonymous"></script></body></html>
+200
View File
@@ -0,0 +1,200 @@
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>Sky Trek Tales</title>
<style>
body, html {
margin: 0; padding: 0; width: 100%; height: 100%;
overflow: hidden; background-color: #000;
position: fixed;
}
#canvas {
display: block;
position: absolute;
top: 0; left: 0;
width: 100vw; height: 100vh;
border: none;
}
#fs-button {
position: absolute;
top: 10px; right: 10px;
padding: 10px;
z-index: 10;
background: rgba(255,255,255,0.3);
color: white; border: 1px solid white;
cursor: pointer;
font-family: sans-serif;
border-radius: 5px;
}
#status { color: white; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
/* Nick modal */
#nickOverlay {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0,0,0,0.85);
z-index: 9999;
}
#nickBox {
background: #111;
border: 1px solid #444;
padding: 24px;
width: 320px;
box-shadow: 0 8px 24px rgba(0,0,0,0.6);
text-align: center;
}
#nickBox h2 { margin: 0 0 12px 0; font-size: 18px; color: #eee; }
#nickBox input[type="text"] {
width: 100%;
padding: 10px;
font-size: 16px;
box-sizing: border-box;
margin-bottom: 12px;
border: 1px solid #333;
background: #000;
color: #fff;
}
#nickBox button {
padding: 10px 16px;
font-size: 16px;
background: #2a9fd6;
border: none;
color: #fff;
cursor: pointer;
}
</style>
</head>
<body>
<button id="fs-button">Fullscreen</button>
<div id="status">Downloading...</div>
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
<div id="nickOverlay" style="display:none;">
<div id="nickBox">
<h2>Enter your nickname</h2>
<input id="nickInput" type="text" maxlength="32" placeholder="Player" />
<div>
<button id="nickSubmit">Start</button>
</div>
</div>
</div>
<script>
// Utility: подготовить глобальный Module до загрузки Emscripten-скрипта
function prepareModuleEnvironment() {
window.Module = window.Module || {};
var canvasEl = document.getElementById('canvas');
// Устанавливаем canvas для Emscripten, чтобы createContext не падал
window.Module.canvas = canvasEl;
// Подготовим заглушку setStatus, если ещё нет
window.Module.setStatus = window.Module.setStatus || function (text) {
var statusElement = document.getElementById("status");
statusElement.innerHTML = text;
statusElement.style.display = text ? 'block' : 'none';
};
}
// Show overlay only if no nickname saved.
function loadGameScript() {
var s = document.createElement('script');
s.src = 'space-game001.js';
s.async = true;
document.body.appendChild(s);
}
function showNickOverlay() {
var overlay = document.getElementById('nickOverlay');
overlay.style.display = 'flex';
var input = document.getElementById('nickInput');
input.focus();
}
function hideNickOverlay() {
var overlay = document.getElementById('nickOverlay');
overlay.style.display = 'none';
}
function saveNickAndStart(nick) {
try {
if (!nick || nick.trim() === '') nick = 'Player';
localStorage.setItem('spacegame_nick', nick);
} catch (e) {
console.warn('localStorage not available', e);
}
hideNickOverlay();
// перед загрузкой скрипта гарантируем, что Module.canvas задан
prepareModuleEnvironment();
loadGameScript();
}
document.getElementById('fs-button').addEventListener('click', function() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen().catch(function(e) {
console.error('Fullscreen error: ' + e.message);
});
} else {
document.exitFullscreen();
}
});
document.addEventListener('DOMContentLoaded', function() {
// Готовим Module сразу — даже если откроется модалка, поле canvas будет доступно для скрипта (если он загружается позже)
prepareModuleEnvironment();
var stored = null;
try {
stored = localStorage.getItem('spacegame_nick');
} catch (e) {
console.warn('localStorage not available', e);
}
if (stored && stored.trim() !== '') {
// Nick is present — start immediately
loadGameScript();
} else {
// Show modal to request nickname before loading WASM
showNickOverlay();
var submit = document.getElementById('nickSubmit');
var input = document.getElementById('nickInput');
submit.addEventListener('click', function() {
saveNickAndStart(input.value);
});
input.addEventListener('input', function() {
// Strip any character that is not a-z, A-Z, 0-9 or space
var pos = this.selectionStart;
var cleaned = this.value.replace(/[^a-zA-Z0-9 ]/g, '');
if (cleaned !== this.value) {
this.value = cleaned;
this.setSelectionRange(pos - 1, pos - 1);
}
});
input.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
saveNickAndStart(input.value);
}
});
}
});
window.addEventListener("orientationchange", function() {
// Chrome на Android обновляет innerWidth/Height не мгновенно.
// Ждем завершения анимации поворота.
setTimeout(() => {
// В Emscripten это вызовет ваш onWindowResized в C++
window.dispatchEvent(new Event('resize'));
}, 200);
});
</script>
</body>
</html>
+194
View File
@@ -0,0 +1,194 @@
cmake_minimum_required(VERSION 3.16)
project(space-game001 LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/ThirdParty.cmake)
# ===========================================
# Основной проект space-game001
# ===========================================
add_executable(space-game001
../src/main.cpp
../src/Game.cpp
../src/Game.h
../src/Character.cpp
../src/Character.h
../src/Environment.cpp
../src/Environment.h
../src/render/Renderer.cpp
../src/render/Renderer.h
../src/render/ShaderManager.cpp
../src/render/ShaderManager.h
../src/render/TextureManager.cpp
../src/render/TextureManager.h
../src/TextModel.cpp
../src/TextModel.h
../src/AudioPlayerAsync.cpp
../src/AudioPlayerAsync.h
../src/BoneAnimatedModel.cpp
../src/BoneAnimatedModel.h
../src/BoneAnimatedModelNew.cpp
../src/BoneAnimatedModelNew.h
../src/render/OpenGlExtensions.cpp
../src/render/OpenGlExtensions.h
../src/utils/Utils.cpp
../src/utils/Utils.h
../src/SparkEmitter.cpp
../src/SparkEmitter.h
../src/utils/Perlin.cpp
../src/utils/Perlin.h
../src/utils/TaskManager.cpp
../src/utils/TaskManager.h
../src/render/FrameBuffer.cpp
../src/render/FrameBuffer.h
../src/render/ShadowMap.cpp
../src/render/ShadowMap.h
../src/UiManager.cpp
../src/UiManager.h
../src/render/TextRenderer.h
../src/render/TextRenderer.cpp
../src/MenuManager.h
../src/MenuManager.cpp
../src/Location.h
../src/Location.cpp
../src/GameConstants.h
../src/GameConstants.cpp
../src/ScriptEngine.h
../src/ScriptEngine.cpp
../src/navigation/PathFinder.h
../src/navigation/PathFinder.cpp
../src/items/GameObjectLoader.h
../src/items/GameObjectLoader.cpp
../src/items/Item.h
../src/items/Item.cpp
../src/items/InteractiveObject.h
../src/items/InteractiveObject.cpp
../src/dialogue/DialogueTypes.h
../src/dialogue/DialogueDatabase.h
../src/dialogue/DialogueDatabase.cpp
../src/dialogue/DialogueRuntime.h
../src/dialogue/DialogueRuntime.cpp
../src/dialogue/DialogueOverlay.h
../src/dialogue/DialogueOverlay.cpp
../src/dialogue/DialogueSystem.h
../src/dialogue/DialogueSystem.cpp
)
# Установка проекта по умолчанию для Visual Studio
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT space-game001)
# include-пути проекта
target_include_directories(space-game001 PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
)
set_target_properties(space-game001 PROPERTIES
OUTPUT_NAME "space-game001"
)
# Определения препроцессора:
# PNG_ENABLED – включает код PNG в TextureManager
# SDL_MAIN_HANDLED – отключает переопределение main -> SDL_main
target_compile_definitions(space-game001 PRIVATE
WIN32_LEAN_AND_MEAN
PNG_ENABLED
SDL_MAIN_HANDLED
# DEBUG_LIGHT
# SHOW_PATH
)
# Линкуем с SDL2main, если он вообще установлен
target_link_libraries(space-game001 PRIVATE SDL2main_external_lib)
# Линкуем сторонние библиотеки
target_link_libraries(space-game001 PRIVATE
SDL2_external_lib
libpng_external_lib
zlib_external_lib
libzip_external_lib
freetype_external_lib
SDL2_ttf_external_lib
eigen_external_lib
boost_external_lib
sol2_external_lib
SDL2_mixer_external_lib
)
# Линкуем OpenGL (Windows)
if(WIN32)
target_link_libraries(space-game001 PRIVATE opengl32)
endif()
# ===========================================
# Копирование SDL2d.dll и zlibd.dll рядом с exe
# ===========================================
if (WIN32)
# SDL2: в Debug - SDL2d.dll, в Release - SDL2.dll
set(SDL2_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2_INSTALL_DIR}/bin/SDL2d.dll,${SDL2_INSTALL_DIR}/bin/SDL2.dll>")
set(SDL2_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:space-game001>/SDL2d.dll,$<TARGET_FILE_DIR:space-game001>/SDL2.dll>")
set(LIBZIP_DLL_SRC "$<IF:$<CONFIG:Debug>,${LIBZIP_BASE_DIR}-Debug/bin/zip.dll,${LIBZIP_BASE_DIR}-Release/bin/zip.dll>")
set(ZLIB_DLL_SRC "$<IF:$<CONFIG:Debug>,${ZLIB_INSTALL_DIR}/bin/zd.dll,${ZLIB_INSTALL_DIR}/bin/z.dll>")
set(ZLIB_DLL_DST "$<IF:$<CONFIG:Debug>,$<TARGET_FILE_DIR:space-game001>/zd.dll,$<TARGET_FILE_DIR:space-game001>/z.dll>")
set(SDL2TTF_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2TTF_BASE_DIR}-Debug/bin/SDL2_ttfd.dll,${SDL2TTF_BASE_DIR}-Release/bin/SDL2_ttf.dll>")
set(SDL2MIXER_DLL_SRC "$<IF:$<CONFIG:Debug>,${SDL2MIXER_BASE_DIR}-Debug/bin/SDL2_mixerd.dll,${SDL2MIXER_BASE_DIR}-Release/bin/SDL2_mixer.dll>")
add_custom_command(TARGET space-game001 POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Copying DLLs to output folder..."
# Копируем SDL2 (целевое имя всегда SDL2.dll)
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${SDL2_DLL_SRC}"
"${SDL2_DLL_DST}"
# Копируем LIBZIP (целевое имя всегда zip.dll)
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${LIBZIP_DLL_SRC}"
"$<TARGET_FILE_DIR:space-game001>/zip.dll"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${ZLIB_DLL_SRC}"
"${ZLIB_DLL_DST}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${SDL2TTF_DLL_SRC}"
"$<TARGET_FILE_DIR:space-game001>"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/SDL_mixer-release-2.8.0/install-$<CONFIG>/bin/SDL2_mixer$<$<CONFIG:Debug>:d>.dll"
"$<TARGET_FILE_DIR:space-game001>/SDL2_mixer$<$<CONFIG:Debug>:d>.dll"
)
endif()
# ===========================================
# Копирование ресурсов после сборки
# ===========================================
# Какие папки с ресурсами нужно копировать
set(RUNTIME_RESOURCE_DIRS
"resources"
"audio"
)
# Копируем ресурсы и шейдеры в папку exe и в корень build/
foreach(resdir IN LISTS RUNTIME_RESOURCE_DIRS)
add_custom_command(TARGET space-game001 POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Copying ${resdir} to runtime folders..."
# 1) туда, где лежит exe (build/Debug, build/Release и т.п.)
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_SOURCE_DIR}/../${resdir}"
"$<TARGET_FILE_DIR:space-game001>/${resdir}"
# 2) в корень build, если захочешь запускать из этой папки
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_SOURCE_DIR}/../${resdir}"
"${CMAKE_BINARY_DIR}/${resdir}"
)
endforeach()
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
+33
View File
@@ -0,0 +1,33 @@
===Vertices (Split by UV/Normal): 20
V 0: Pos(2.172386, 0.169635, 0.221728) Norm(-0.577351, -0.57735, -0.57735) UV(0.014711, 0.022579)
V 1: Pos(2.172386, 0.169635, 0.558757) Norm(-0.578984, -0.585366, 0.56756) UV(0.547197, 0.022579)
V 2: Pos(2.172386, 0.7737, 0.577419) Norm(-0.573104, 0.585688, 0.573167) UV(0.576682, 0.976966)
V 3: Pos(2.172386, 0.755012, 0.221728) Norm(-0.580152, 0.560631, -0.590861) UV(0.014711, 0.94744)
V 4: Pos(2.172386, 0.755012, 0.221728) Norm(-0.580152, 0.560631, -0.590861) UV(0.591716, 0.022579)
V 5: Pos(2.181902, 0.755012, 0.221728) Norm(-1e-06, 0.688307, -0.72542) UV(0.606751, 0.022579)
V 6: Pos(2.181902, 0.169635, 0.221728) Norm(-0.0, -0.707106, -0.707107) UV(0.606751, 0.94744)
V 7: Pos(2.172386, 0.169635, 0.221728) Norm(-0.577351, -0.57735, -0.57735) UV(0.591716, 0.94744)
V 8: Pos(2.172386, 0.169635, 0.221728) Norm(-0.577351, -0.57735, -0.57735) UV(0.63682, 0.555065)
V 9: Pos(2.181902, 0.169635, 0.221728) Norm(-0.0, -0.707106, -0.707107) UV(0.621785, 0.555065)
V 10: Pos(2.181902, 0.169635, 0.558757) Norm(1e-06, -0.717941, 0.696104) UV(0.621785, 0.022579)
V 11: Pos(2.172386, 0.169635, 0.558757) Norm(-0.578984, -0.585366, 0.56756) UV(0.63682, 0.022579)
V 12: Pos(2.172386, 0.7737, 0.577419) Norm(-0.573104, 0.585688, 0.573167) UV(0.606751, 0.022579)
V 13: Pos(2.181902, 0.7737, 0.577419) Norm(0.0, 0.714705, 0.699426) UV(0.621785, 0.022579)
V 14: Pos(2.181902, 0.755012, 0.221728) Norm(-1e-06, 0.688307, -0.72542) UV(0.621785, 0.585325)
V 15: Pos(2.172386, 0.755012, 0.221728) Norm(-0.580152, 0.560631, -0.590861) UV(0.606751, 0.585325)
V 16: Pos(2.172386, 0.169635, 0.558757) Norm(-0.578984, -0.585366, 0.56756) UV(0.591716, 0.977422)
V 17: Pos(2.181902, 0.169635, 0.558757) Norm(1e-06, -0.717941, 0.696104) UV(0.576682, 0.977422)
V 18: Pos(2.181902, 0.7737, 0.577419) Norm(0.0, 0.714705, 0.699426) UV(0.576682, 0.022579)
V 19: Pos(2.172386, 0.7737, 0.577419) Norm(-0.573104, 0.585688, 0.573167) UV(0.591716, 0.022579)
===Triangles (Indices): 10
Tri: 0 1 2
Tri: 0 2 3
Tri: 4 5 6
Tri: 4 6 7
Tri: 8 9 10
Tri: 8 10 11
Tri: 12 13 14
Tri: 12 14 15
Tri: 16 17 18
Tri: 16 18 19
+63
View File
@@ -0,0 +1,63 @@
===Vertices (Split by UV/Normal): 40
V 0: Pos(1.996428, 0.338833, 0.198714) Norm(-0.551234, -0.834351, 0.0) UV(0.971407, 0.5)
V 1: Pos(1.996428, 0.338833, 0.202329) Norm(-0.450081, -0.681244, 0.57735) UV(0.944505, 0.5)
V 2: Pos(1.983381, 0.402683, 0.202329) Norm(-0.681244, 0.450081, 0.57735) UV(0.944505, 0.023447)
V 3: Pos(1.983381, 0.402683, 0.198714) Norm(-0.83435, 0.551234, 0.0) UV(0.971408, 0.023447)
V 4: Pos(1.983381, 0.402683, 0.198714) Norm(-0.83435, 0.551234, 0.0) UV(0.863798, 0.700024)
V 5: Pos(1.983381, 0.402683, 0.202329) Norm(-0.681244, 0.450081, 0.57735) UV(0.836895, 0.700024)
V 6: Pos(2.073946, 0.421188, 0.202329) Norm(0.450082, 0.681244, 0.57735) UV(0.836895, 0.023447)
V 7: Pos(2.073946, 0.421188, 0.198714) Norm(0.551236, 0.834349, 0.0) UV(0.863798, 0.023447)
V 8: Pos(2.073946, 0.421188, 0.198714) Norm(0.551236, 0.834349, 0.0) UV(0.971408, 0.976553)
V 9: Pos(2.073946, 0.421188, 0.202329) Norm(0.450082, 0.681244, 0.57735) UV(0.944505, 0.976553)
V 10: Pos(2.086992, 0.357338, 0.202329) Norm(0.681243, -0.450082, 0.57735) UV(0.944505, 0.5)
V 11: Pos(2.086992, 0.357338, 0.198714) Norm(0.83435, -0.551236, 0.0) UV(0.971408, 0.5)
V 12: Pos(2.086992, 0.357338, 0.198714) Norm(0.83435, -0.551236, 0.0) UV(0.8907, 0.700024)
V 13: Pos(2.086992, 0.357338, 0.202329) Norm(0.681243, -0.450082, 0.57735) UV(0.863798, 0.700024)
V 14: Pos(1.996428, 0.338833, 0.202329) Norm(-0.450081, -0.681244, 0.57735) UV(0.863798, 0.023447)
V 15: Pos(1.996428, 0.338833, 0.198714) Norm(-0.551234, -0.834351, 0.0) UV(0.8907, 0.023447)
V 16: Pos(2.016325, 0.3535, 0.202405) Norm(-0.212218, -0.977222, 0.0) UV(0.917603, 0.911909)
V 17: Pos(2.016325, 0.3535, 0.206019) Norm(-0.173196, -0.797534, 0.577877) UV(0.8907, 0.911909)
V 18: Pos(1.992261, 0.390504, 0.206019) Norm(-0.79826, 0.173353, 0.576827) UV(0.8907, 0.589135)
V 19: Pos(1.992261, 0.390504, 0.202405) Norm(-0.977223, 0.212217, 0.0) UV(0.917603, 0.589135)
V 20: Pos(2.073946, 0.421188, 0.202329) Norm(0.450082, 0.681244, 0.57735) UV(0.508379, 0.023447)
V 21: Pos(1.983381, 0.402683, 0.202329) Norm(-0.681244, 0.450081, 0.57735) UV(0.508379, 0.711448)
V 22: Pos(1.996428, 0.338833, 0.202329) Norm(-0.450081, -0.681244, 0.57735) UV(0.023323, 0.711448)
V 23: Pos(2.086992, 0.357338, 0.202329) Norm(0.681243, -0.450082, 0.57735) UV(0.023323, 0.023447)
V 24: Pos(1.992261, 0.390504, 0.202405) Norm(-0.977223, 0.212217, 0.0) UV(0.917603, 0.023447)
V 25: Pos(1.992261, 0.390504, 0.206019) Norm(-0.79826, 0.173353, 0.576827) UV(0.944505, 0.023447)
V 26: Pos(2.057473, 0.431984, 0.206019) Norm(0.173196, 0.797534, 0.577878) UV(0.944505, 0.589134)
V 27: Pos(2.057473, 0.431984, 0.202405) Norm(0.212218, 0.977222, 0.0) UV(0.917603, 0.589134)
V 28: Pos(2.057473, 0.431984, 0.202405) Norm(0.212218, 0.977222, 0.0) UV(0.508379, 0.601986)
V 29: Pos(2.057473, 0.431984, 0.206019) Norm(0.173196, 0.797534, 0.577878) UV(0.535281, 0.601986)
V 30: Pos(2.081536, 0.39498, 0.206019) Norm(0.79826, -0.173354, 0.576827) UV(0.535281, 0.92476)
V 31: Pos(2.081536, 0.39498, 0.202405) Norm(0.977222, -0.212219, 0.0) UV(0.508379, 0.92476)
V 32: Pos(2.081536, 0.39498, 0.202405) Norm(0.977222, -0.212219, 0.0) UV(0.917603, 0.589135)
V 33: Pos(2.081536, 0.39498, 0.206019) Norm(0.79826, -0.173354, 0.576827) UV(0.8907, 0.589135)
V 34: Pos(2.016325, 0.3535, 0.206019) Norm(-0.173196, -0.797534, 0.577877) UV(0.8907, 0.023447)
V 35: Pos(2.016325, 0.3535, 0.202405) Norm(-0.212218, -0.977222, 0.0) UV(0.917603, 0.023447)
V 36: Pos(2.057473, 0.431984, 0.206019) Norm(0.173196, 0.797534, 0.577878) UV(0.508379, 0.598686)
V 37: Pos(1.992261, 0.390504, 0.206019) Norm(-0.79826, 0.173353, 0.576827) UV(0.508379, 0.023447)
V 38: Pos(2.016325, 0.3535, 0.206019) Norm(-0.173196, -0.797534, 0.577877) UV(0.836895, 0.026746)
V 39: Pos(2.081536, 0.39498, 0.206019) Norm(0.79826, -0.173354, 0.576827) UV(0.836895, 0.601986)
===Triangles (Indices): 20
Tri: 0 1 2
Tri: 0 2 3
Tri: 4 5 6
Tri: 4 6 7
Tri: 8 9 10
Tri: 8 10 11
Tri: 12 13 14
Tri: 12 14 15
Tri: 16 17 18
Tri: 16 18 19
Tri: 20 21 22
Tri: 20 22 23
Tri: 24 25 26
Tri: 24 26 27
Tri: 28 29 30
Tri: 28 30 31
Tri: 32 33 34
Tri: 32 34 35
Tri: 36 37 38
Tri: 36 38 39
+63
View File
@@ -0,0 +1,63 @@
===Vertices (Split by UV/Normal): 40
V 0: Pos(1.884571, 0.582254, 0.198714) Norm(-0.993741, -0.111707, 0.0) UV(0.971407, 0.5)
V 1: Pos(1.884571, 0.582254, 0.202329) Norm(-0.811386, -0.091209, 0.577351) UV(0.944505, 0.5)
V 2: Pos(1.925217, 0.633195, 0.202329) Norm(-0.091209, 0.811387, 0.57735) UV(0.944505, 0.023447)
V 3: Pos(1.925217, 0.633195, 0.198714) Norm(-0.111707, 0.993741, 0.0) UV(0.971408, 0.023447)
V 4: Pos(1.925217, 0.633195, 0.198714) Norm(-0.111707, 0.993741, 0.0) UV(0.863798, 0.700024)
V 5: Pos(1.925217, 0.633195, 0.202329) Norm(-0.091209, 0.811387, 0.57735) UV(0.836895, 0.700024)
V 6: Pos(1.997471, 0.575543, 0.202329) Norm(0.811386, 0.091209, 0.57735) UV(0.836895, 0.023447)
V 7: Pos(1.997471, 0.575543, 0.198714) Norm(0.993741, 0.111708, 0.0) UV(0.863798, 0.023447)
V 8: Pos(1.997471, 0.575543, 0.198714) Norm(0.993741, 0.111708, 0.0) UV(0.971408, 0.976553)
V 9: Pos(1.997471, 0.575543, 0.202329) Norm(0.811386, 0.091209, 0.57735) UV(0.944505, 0.976553)
V 10: Pos(1.956826, 0.524602, 0.202329) Norm(0.091209, -0.811386, 0.57735) UV(0.944505, 0.5)
V 11: Pos(1.956826, 0.524602, 0.198714) Norm(0.111708, -0.993741, 0.0) UV(0.971408, 0.5)
V 12: Pos(1.956826, 0.524602, 0.198714) Norm(0.111708, -0.993741, 0.0) UV(0.8907, 0.700024)
V 13: Pos(1.956826, 0.524602, 0.202329) Norm(0.091209, -0.811386, 0.57735) UV(0.863798, 0.700024)
V 14: Pos(1.884571, 0.582254, 0.202329) Norm(-0.811386, -0.091209, 0.577351) UV(0.863798, 0.023447)
V 15: Pos(1.884571, 0.582254, 0.198714) Norm(-0.993741, -0.111707, 0.0) UV(0.8907, 0.023447)
V 16: Pos(1.908582, 0.576383, 0.202405) Norm(-0.886097, -0.4635, 0.0) UV(0.917603, 0.911909)
V 17: Pos(1.908582, 0.576383, 0.206019) Norm(-0.723165, -0.378274, 0.577877) UV(0.8907, 0.911909)
V 18: Pos(1.921561, 0.618572, 0.206019) Norm(-0.378618, 0.723823, 0.576827) UV(0.8907, 0.589135)
V 19: Pos(1.921561, 0.618572, 0.202405) Norm(-0.4635, 0.886097, 0.0) UV(0.917603, 0.589135)
V 20: Pos(1.997471, 0.575543, 0.202329) Norm(0.811386, 0.091209, 0.57735) UV(0.508379, 0.023447)
V 21: Pos(1.925217, 0.633195, 0.202329) Norm(-0.091209, 0.811387, 0.57735) UV(0.508379, 0.711448)
V 22: Pos(1.884571, 0.582254, 0.202329) Norm(-0.811386, -0.091209, 0.577351) UV(0.023323, 0.711448)
V 23: Pos(1.956826, 0.524602, 0.202329) Norm(0.091209, -0.811386, 0.57735) UV(0.023323, 0.023447)
V 24: Pos(1.921561, 0.618572, 0.202405) Norm(-0.4635, 0.886097, 0.0) UV(0.917603, 0.023447)
V 25: Pos(1.921561, 0.618572, 0.206019) Norm(-0.378618, 0.723823, 0.576827) UV(0.944505, 0.023447)
V 26: Pos(1.995198, 0.595107, 0.206019) Norm(0.723164, 0.378273, 0.577878) UV(0.944505, 0.589134)
V 27: Pos(1.995198, 0.595107, 0.202405) Norm(0.886097, 0.4635, 0.0) UV(0.917603, 0.589134)
V 28: Pos(1.995198, 0.595107, 0.202405) Norm(0.886097, 0.4635, 0.0) UV(0.508379, 0.601986)
V 29: Pos(1.995198, 0.595107, 0.206019) Norm(0.723164, 0.378273, 0.577878) UV(0.535281, 0.601986)
V 30: Pos(1.98222, 0.552918, 0.206019) Norm(0.378618, -0.723823, 0.576827) UV(0.535281, 0.92476)
V 31: Pos(1.98222, 0.552918, 0.202405) Norm(0.4635, -0.886097, 0.0) UV(0.508379, 0.92476)
V 32: Pos(1.98222, 0.552918, 0.202405) Norm(0.4635, -0.886097, 0.0) UV(0.917603, 0.589135)
V 33: Pos(1.98222, 0.552918, 0.206019) Norm(0.378618, -0.723823, 0.576827) UV(0.8907, 0.589135)
V 34: Pos(1.908582, 0.576383, 0.206019) Norm(-0.723165, -0.378274, 0.577877) UV(0.8907, 0.023447)
V 35: Pos(1.908582, 0.576383, 0.202405) Norm(-0.886097, -0.4635, 0.0) UV(0.917603, 0.023447)
V 36: Pos(1.995198, 0.595107, 0.206019) Norm(0.723164, 0.378273, 0.577878) UV(0.508379, 0.598686)
V 37: Pos(1.921561, 0.618572, 0.206019) Norm(-0.378618, 0.723823, 0.576827) UV(0.508379, 0.023447)
V 38: Pos(1.908582, 0.576383, 0.206019) Norm(-0.723165, -0.378274, 0.577877) UV(0.836895, 0.026746)
V 39: Pos(1.98222, 0.552918, 0.206019) Norm(0.378618, -0.723823, 0.576827) UV(0.836895, 0.601986)
===Triangles (Indices): 20
Tri: 0 1 2
Tri: 0 2 3
Tri: 4 5 6
Tri: 4 6 7
Tri: 8 9 10
Tri: 8 10 11
Tri: 12 13 14
Tri: 12 14 15
Tri: 16 17 18
Tri: 16 18 19
Tri: 20 21 22
Tri: 20 22 23
Tri: 24 25 26
Tri: 24 26 27
Tri: 28 29 30
Tri: 28 30 31
Tri: 32 33 34
Tri: 32 34 35
Tri: 36 37 38
Tri: 36 38 39
+29
View File
@@ -0,0 +1,29 @@
===Vertices (Split by UV/Normal): 14
V 0: Pos(1.928162, 0.364475, 0.015009) Norm(-0.690923, -0.414235, -0.592482) UV(0.380616, 0.022463)
V 1: Pos(1.924941, 0.362544, 0.107039) Norm(-0.709291, -0.425247, 0.562202) UV(0.619384, 0.022463)
V 2: Pos(1.948582, 0.45697, 0.107039) Norm(-0.425247, 0.709291, 0.562202) UV(0.619384, 0.261232)
V 3: Pos(1.950513, 0.453749, 0.015009) Norm(-0.414235, 0.690923, -0.592482) UV(0.380616, 0.261232)
V 4: Pos(2.043009, 0.433329, 0.107039) Norm(0.709291, 0.425246, 0.562202) UV(0.619384, 0.5)
V 5: Pos(2.039788, 0.431398, 0.015009) Norm(0.690923, 0.414234, -0.592482) UV(0.380616, 0.5)
V 6: Pos(2.019368, 0.338903, 0.107039) Norm(0.425246, -0.709292, 0.562202) UV(0.619384, 0.738768)
V 7: Pos(2.017437, 0.342124, 0.015009) Norm(0.414235, -0.690924, -0.592481) UV(0.380616, 0.738768)
V 8: Pos(1.924941, 0.362544, 0.107039) Norm(-0.709291, -0.425247, 0.562202) UV(0.619384, 0.977537)
V 9: Pos(1.928162, 0.364475, 0.015009) Norm(-0.690923, -0.414235, -0.592482) UV(0.380616, 0.977537)
V 10: Pos(1.950513, 0.453749, 0.015009) Norm(-0.414235, 0.690923, -0.592482) UV(0.141847, 0.5)
V 11: Pos(1.928162, 0.364475, 0.015009) Norm(-0.690923, -0.414235, -0.592482) UV(0.141847, 0.738768)
V 12: Pos(1.948582, 0.45697, 0.107039) Norm(-0.425247, 0.709291, 0.562202) UV(0.858153, 0.5)
V 13: Pos(1.924941, 0.362544, 0.107039) Norm(-0.709291, -0.425247, 0.562202) UV(0.858153, 0.738768)
===Triangles (Indices): 12
Tri: 0 1 2
Tri: 0 2 3
Tri: 3 2 4
Tri: 3 4 5
Tri: 5 4 6
Tri: 5 6 7
Tri: 7 6 8
Tri: 7 8 9
Tri: 10 5 7
Tri: 10 7 11
Tri: 4 12 13
Tri: 4 13 6
+29
View File
@@ -0,0 +1,29 @@
===Vertices (Split by UV/Normal): 14
V 0: Pos(1.941315, 0.20689, 0.015108) Norm(-0.46303, -0.659218, -0.592482) UV(0.380616, 0.022463)
V 1: Pos(1.938853, 0.203385, 0.120071) Norm(-0.475339, -0.676743, 0.562202) UV(0.619384, 0.022463)
V 2: Pos(1.919735, 0.312747, 0.120071) Norm(-0.676743, 0.475339, 0.562202) UV(0.619384, 0.261232)
V 3: Pos(1.92324, 0.310285, 0.015108) Norm(-0.659218, 0.463029, -0.592482) UV(0.380616, 0.261232)
V 4: Pos(2.029097, 0.331865, 0.120071) Norm(0.475339, 0.676744, 0.562202) UV(0.619384, 0.5)
V 5: Pos(2.026635, 0.32836, 0.015108) Norm(0.46303, 0.659218, -0.592482) UV(0.380616, 0.5)
V 6: Pos(2.048215, 0.222503, 0.120071) Norm(0.676743, -0.475339, 0.562202) UV(0.619384, 0.738768)
V 7: Pos(2.04471, 0.224965, 0.015108) Norm(0.659218, -0.46303, -0.592482) UV(0.380616, 0.738768)
V 8: Pos(1.938853, 0.203385, 0.120071) Norm(-0.475339, -0.676743, 0.562202) UV(0.619384, 0.977537)
V 9: Pos(1.941315, 0.20689, 0.015108) Norm(-0.46303, -0.659218, -0.592482) UV(0.380616, 0.977537)
V 10: Pos(1.92324, 0.310285, 0.015108) Norm(-0.659218, 0.463029, -0.592482) UV(0.141847, 0.5)
V 11: Pos(1.941315, 0.20689, 0.015108) Norm(-0.46303, -0.659218, -0.592482) UV(0.141847, 0.738768)
V 12: Pos(1.919735, 0.312747, 0.120071) Norm(-0.676743, 0.475339, 0.562202) UV(0.858153, 0.5)
V 13: Pos(1.938853, 0.203385, 0.120071) Norm(-0.475339, -0.676743, 0.562202) UV(0.858153, 0.738768)
===Triangles (Indices): 12
Tri: 0 1 2
Tri: 0 2 3
Tri: 3 2 4
Tri: 3 4 5
Tri: 5 4 6
Tri: 5 6 7
Tri: 7 6 8
Tri: 7 8 9
Tri: 10 5 7
Tri: 10 7 11
Tri: 4 12 13
Tri: 4 13 6
+52
View File
@@ -0,0 +1,52 @@
===Vertices (Split by UV/Normal): 29
V 0: Pos(2.144698, 1.006366, 0.014196) Norm(0.403294, 0.189282, -0.89528) UV(0.038866, 0.499333)
V 1: Pos(2.153896, 1.009608, 0.025287) Norm(0.876115, 0.355937, -0.325162) UV(0.0229, 0.474324)
V 2: Pos(2.179514, 0.863888, 0.224368) Norm(0.938676, -0.127483, 0.320367) UV(0.232621, 0.022338)
V 3: Pos(2.16982, 0.849805, 0.224272) Norm(0.452238, -0.834195, 0.315593) UV(0.266966, 0.022338)
V 4: Pos(2.025819, 0.823535, 0.02403) Norm(-0.017565, -0.942434, -0.333931) UV(0.476687, 0.474324)
V 5: Pos(2.025633, 0.833386, 0.013027) Norm(-0.02715, -0.436065, -0.899505) UV(0.460721, 0.499333)
V 6: Pos(2.145396, 1.015422, 0.030637) Norm(-0.022224, 0.970458, 0.240245) UV(0.930474, 0.535066)
V 7: Pos(2.171014, 0.869703, 0.229717) Norm(0.054319, 0.477468, 0.876969) UV(0.930474, 0.036794)
V 8: Pos(2.179514, 0.863888, 0.224368) Norm(0.938676, -0.127483, 0.320367) UV(0.953787, 0.036794)
V 9: Pos(2.153896, 1.009608, 0.025287) Norm(0.876115, 0.355937, -0.325162) UV(0.953787, 0.535066)
V 10: Pos(2.017133, 0.839201, 0.018377) Norm(-0.925489, 0.178454, -0.334101) UV(0.492653, 0.499333)
V 11: Pos(2.017319, 0.829349, 0.02938) Norm(-0.915912, -0.327912, 0.231473) UV(0.476687, 0.474324)
V 12: Pos(2.16132, 0.855619, 0.229622) Norm(-0.432118, -0.229245, 0.872193) UV(0.686408, 0.022338)
V 13: Pos(2.171014, 0.869703, 0.229717) Norm(0.054319, 0.477468, 0.876969) UV(0.720754, 0.022338)
V 14: Pos(2.145396, 1.015422, 0.030637) Norm(-0.022224, 0.970458, 0.240245) UV(0.930474, 0.474324)
V 15: Pos(2.136198, 1.01218, 0.019545) Norm(-0.495043, 0.803811, -0.329878) UV(0.914509, 0.499333)
V 16: Pos(2.025819, 0.823535, 0.02403) Norm(-0.017565, -0.942434, -0.333931) UV(0.953787, 0.520609)
V 17: Pos(2.16982, 0.849805, 0.224272) Norm(0.452238, -0.834195, 0.315593) UV(0.953787, 0.022338)
V 18: Pos(2.16132, 0.855619, 0.229622) Norm(-0.432118, -0.229245, 0.872193) UV(0.9771, 0.022338)
V 19: Pos(2.017319, 0.829349, 0.02938) Norm(-0.915912, -0.327912, 0.231473) UV(0.9771, 0.520609)
V 20: Pos(2.16132, 0.855619, 0.229622) Norm(-0.432118, -0.229245, 0.872193) UV(0.930474, 0.022338)
V 21: Pos(2.017133, 0.839201, 0.018377) Norm(-0.925489, 0.178454, -0.334101) UV(0.046213, 0.515299)
V 22: Pos(2.025633, 0.833386, 0.013027) Norm(-0.02715, -0.436065, -0.899505) UV(0.0229, 0.515299)
V 23: Pos(2.025819, 0.823535, 0.02403) Norm(-0.017565, -0.942434, -0.333931) UV(0.0229, 0.499333)
V 24: Pos(2.017319, 0.829349, 0.02938) Norm(-0.915912, -0.327912, 0.231473) UV(0.046213, 0.499333)
V 25: Pos(2.144698, 1.006366, 0.014196) Norm(0.403294, 0.189282, -0.89528) UV(0.0229, 0.937155)
V 26: Pos(2.136198, 1.01218, 0.019545) Norm(-0.495043, 0.803811, -0.329878) UV(0.046213, 0.937155)
V 27: Pos(2.145396, 1.015422, 0.030637) Norm(-0.022224, 0.970458, 0.240245) UV(0.046213, 0.95312)
V 28: Pos(2.153896, 1.009608, 0.025287) Norm(0.876115, 0.355937, -0.325162) UV(0.0229, 0.95312)
===Triangles (Indices): 20
Tri: 0 1 2
Tri: 0 2 3
Tri: 0 3 4
Tri: 0 4 5
Tri: 6 7 8
Tri: 6 8 9
Tri: 10 11 12
Tri: 10 12 13
Tri: 10 13 14
Tri: 10 14 15
Tri: 16 17 18
Tri: 16 18 19
Tri: 7 20 17
Tri: 7 17 8
Tri: 21 22 23
Tri: 21 23 24
Tri: 25 26 27
Tri: 25 27 28
Tri: 21 26 25
Tri: 21 25 22
+423
View File
@@ -0,0 +1,423 @@
===Vertices (Split by UV/Normal): 226
V 0: Pos(-2.162589, -1.0, 0.753136) Norm(-0.707107, -0.707107, 0.0) UV(0.531013, 0.882126)
V 1: Pos(-2.162589, -1.0, 1.296742) Norm(0.026636, -0.402662, -0.914961) UV(0.491477, 0.882126)
V 2: Pos(-2.162589, 1.963949, 1.296742) Norm(0.026637, 0.402662, -0.914961) UV(0.491477, 0.666563)
V 3: Pos(-2.162589, 1.963949, 0.753136) Norm(-0.707107, 0.707107, 0.0) UV(0.531013, 0.666563)
V 4: Pos(2.283504, 1.963949, 0.753136) Norm(0.707107, 0.707107, 0.0) UV(0.624115, 0.882126)
V 5: Pos(2.283504, 1.963949, 1.296742) Norm(-0.005023, 0.39988, -0.916554) UV(0.584579, 0.882126)
V 6: Pos(2.283504, -1.0, 1.296742) Norm(-0.005023, -0.39988, -0.916554) UV(0.584579, 0.666563)
V 7: Pos(2.283504, -1.0, 0.753136) Norm(0.707107, -0.707107, 0.0) UV(0.624115, 0.666563)
V 8: Pos(-0.787029, -0.902648, 0.016616) Norm(0.408248, 0.408248, 0.816497) UV(0.254904, 0.57906)
V 9: Pos(0.907943, -0.902648, 0.016616) Norm(-0.408248, 0.408248, 0.816497) UV(0.254904, 0.455791)
V 10: Pos(0.907943, 1.866597, 0.016616) Norm(-0.408248, -0.408247, 0.816497) UV(0.456307, 0.455791)
V 11: Pos(-0.787029, 1.866597, 0.016616) Norm(0.408248, -0.408249, 0.816496) UV(0.456307, 0.57906)
V 12: Pos(-0.787029, 1.866597, 0.753136) Norm(0.301511, -0.904534, -0.301511) UV(0.737052, 0.235723)
V 13: Pos(-0.787029, 1.866597, 1.820116) Norm(0.353009, -0.756776, -0.550158) UV(0.814652, 0.235723)
V 14: Pos(-2.060281, 1.866597, 1.337887) Norm(0.65901, -0.598824, -0.4551) UV(0.77958, 0.328324)
V 15: Pos(-2.060281, 1.866597, 0.753136) Norm(0.707107, -0.707107, 0.0) UV(0.737052, 0.328324)
V 16: Pos(-0.787029, -0.902648, 0.753136) Norm(0.301511, 0.904534, -0.301511) UV(0.737052, 0.420926)
V 17: Pos(-0.787029, -0.902648, 0.016616) Norm(0.408248, 0.408248, 0.816497) UV(0.683486, 0.420926)
V 18: Pos(-2.060281, -0.902648, 0.016616) Norm(0.57735, 0.57735, 0.57735) UV(0.683486, 0.328324)
V 19: Pos(-2.060281, -0.902648, 0.753136) Norm(0.707107, 0.707107, 0.0) UV(0.737052, 0.328324)
V 20: Pos(2.181196, 1.866597, 0.016616) Norm(-0.57735, -0.57735, 0.57735) UV(0.456307, 0.363191)
V 21: Pos(2.181196, -0.902648, 0.016616) Norm(-0.57735, 0.57735, 0.57735) UV(0.254904, 0.363191)
V 22: Pos(0.907943, 1.866597, 0.016616) Norm(-0.408248, -0.408247, 0.816497) UV(0.243391, 0.416758)
V 23: Pos(0.907943, 1.866597, 0.753136) Norm(-0.301512, -0.904534, -0.301512) UV(0.243391, 0.363192)
V 24: Pos(0.907944, 1.963949, 0.753136) Norm(-0.301511, 0.904534, -0.301511) UV(0.250471, 0.363192)
V 25: Pos(0.907944, 1.963949, 0.016616) Norm(-0.707106, 0.707108, -0.0) UV(0.250471, 0.416758)
V 26: Pos(0.907944, 1.963949, 0.753136) Norm(-0.301511, 0.904534, -0.301511) UV(0.456307, 0.363191)
V 27: Pos(0.907943, 1.866597, 0.753136) Norm(-0.301512, -0.904534, -0.301512) UV(0.463388, 0.363191)
V 28: Pos(0.060457, 1.866597, 0.753136) Norm(0.0, -0.707107, -0.707107) UV(0.463388, 0.424826)
V 29: Pos(0.060458, 1.963949, 0.753136) Norm(0.0, 0.707107, -0.707107) UV(0.456307, 0.424826)
V 30: Pos(-2.060281, 1.866597, 0.753136) Norm(0.707107, -0.707107, 0.0) UV(0.06086, 0.873065)
V 31: Pos(-2.060281, 1.866597, 1.337887) Norm(0.65901, -0.598824, -0.4551) UV(0.018331, 0.873065)
V 32: Pos(-2.060281, -0.902648, 1.337887) Norm(0.65901, 0.598824, -0.4551) UV(0.018331, 0.671662)
V 33: Pos(-2.060281, -0.902648, 0.753136) Norm(0.707107, 0.707107, 0.0) UV(0.06086, 0.671662)
V 34: Pos(2.181196, 1.866597, 0.753136) Norm(-0.707107, -0.707107, 0.0) UV(0.737052, 0.019848)
V 35: Pos(2.181196, 1.866597, 1.337887) Norm(-0.65901, -0.598824, -0.4551) UV(0.77958, 0.019848)
V 36: Pos(0.907943, 1.866597, 1.820116) Norm(-0.353009, -0.756776, -0.550158) UV(0.814652, 0.11245)
V 37: Pos(0.907943, 1.866597, 0.753136) Norm(-0.301512, -0.904534, -0.301512) UV(0.737052, 0.11245)
V 38: Pos(2.181196, 1.866597, 0.753136) Norm(-0.707107, -0.707107, 0.0) UV(0.297432, 0.67166)
V 39: Pos(2.181196, 1.866597, 0.016616) Norm(-0.57735, -0.57735, 0.57735) UV(0.350998, 0.67166)
V 40: Pos(2.181196, -0.902648, 0.016616) Norm(-0.57735, 0.57735, 0.57735) UV(0.350998, 0.873063)
V 41: Pos(2.181196, -0.902648, 0.753136) Norm(-0.707107, 0.707107, 0.0) UV(0.297432, 0.873063)
V 42: Pos(2.181196, -0.902648, 0.753136) Norm(-0.707107, 0.707107, 0.0) UV(0.737052, 0.6368)
V 43: Pos(2.181196, -0.902648, 0.016616) Norm(-0.57735, 0.57735, 0.57735) UV(0.683486, 0.6368)
V 44: Pos(0.907943, -0.902648, 0.016616) Norm(-0.408248, 0.408248, 0.816497) UV(0.683486, 0.544199)
V 45: Pos(0.907943, -0.902648, 0.753136) Norm(-0.301511, 0.904534, -0.301512) UV(0.737052, 0.544199)
V 46: Pos(-2.162589, 1.963949, 0.753136) Norm(-0.707107, 0.707107, 0.0) UV(0.545043, 0.019848)
V 47: Pos(-2.162589, 1.963949, 1.296742) Norm(0.026637, 0.402662, -0.914961) UV(0.584579, 0.019848)
V 48: Pos(-0.787029, 1.963949, 1.820116) Norm(0.353547, 0.756586, -0.550075) UV(0.622643, 0.119891)
V 49: Pos(-0.787028, 1.963949, 0.753136) Norm(0.301511, 0.904534, -0.301512) UV(0.545043, 0.119891)
V 50: Pos(2.283504, -1.0, 0.753136) Norm(0.707107, -0.707107, 0.0) UV(0.545043, 0.343206)
V 51: Pos(2.283504, -1.0, 1.296742) Norm(-0.005023, -0.39988, -0.916554) UV(0.584579, 0.343206)
V 52: Pos(0.907944, -1.0, 1.820116) Norm(-0.353547, -0.756586, -0.550075) UV(0.622643, 0.443248)
V 53: Pos(0.907944, -1.0, 0.753136) Norm(-0.301511, -0.904534, -0.301511) UV(0.545043, 0.443248)
V 54: Pos(-0.787029, 1.866597, 0.753136) Norm(0.301511, -0.904534, -0.301511) UV(0.463388, 0.486461)
V 55: Pos(-0.787028, 1.963949, 0.753136) Norm(0.301511, 0.904534, -0.301512) UV(0.456307, 0.486461)
V 56: Pos(-0.787028, -1.0, 0.753136) Norm(0.301511, -0.904534, -0.301512) UV(0.545043, 0.566521)
V 57: Pos(-0.787028, -1.0, 1.820116) Norm(0.353547, -0.756586, -0.550075) UV(0.622643, 0.566521)
V 58: Pos(-2.162589, -1.0, 1.296742) Norm(0.026636, -0.402662, -0.914961) UV(0.584579, 0.666563)
V 59: Pos(-2.162589, -1.0, 0.753136) Norm(-0.707107, -0.707107, 0.0) UV(0.545043, 0.666563)
V 60: Pos(0.907944, 1.963949, 0.753136) Norm(-0.301511, 0.904534, -0.301511) UV(0.545043, 0.243163)
V 61: Pos(0.907943, 1.963949, 1.820116) Norm(-0.353548, 0.756586, -0.550075) UV(0.622643, 0.243163)
V 62: Pos(2.283504, 1.963949, 1.296742) Norm(-0.005023, 0.39988, -0.916554) UV(0.584579, 0.343206)
V 63: Pos(2.283504, 1.963949, 0.753136) Norm(0.707107, 0.707107, 0.0) UV(0.545043, 0.343206)
V 64: Pos(-0.787029, 1.866597, 0.753136) Norm(0.301511, -0.904534, -0.301511) UV(0.250471, 0.416758)
V 65: Pos(-0.787029, 1.866597, 0.016616) Norm(0.408248, -0.408249, 0.816496) UV(0.250471, 0.470324)
V 66: Pos(-0.787028, 1.963949, 0.016616) Norm(0.707107, 0.707106, 0.0) UV(0.243391, 0.470324)
V 67: Pos(-0.787028, 1.963949, 0.753136) Norm(0.301511, 0.904534, -0.301512) UV(0.243391, 0.416758)
V 68: Pos(0.060457, 1.866597, 0.753136) Norm(0.0, -0.707107, -0.707107) UV(0.737052, 0.174086)
V 69: Pos(0.060457, 1.866597, 2.656697) Norm(0.0, -0.578056, -0.815997) UV(0.875495, 0.174086)
V 70: Pos(0.907943, -0.902648, 0.753136) Norm(-0.301511, 0.904534, -0.301512) UV(0.470468, 0.363191)
V 71: Pos(0.907943, -0.902648, 0.016616) Norm(-0.408248, 0.408248, 0.816497) UV(0.470468, 0.416757)
V 72: Pos(0.907943, -1.0, 0.016616) Norm(-0.707107, -0.707107, 0.0) UV(0.463388, 0.416757)
V 73: Pos(0.907944, -1.0, 0.753136) Norm(-0.301511, -0.904534, -0.301511) UV(0.463388, 0.363191)
V 74: Pos(2.283504, -1.146237, 1.410701) Norm(0.241334, -0.710196, 0.661346) UV(0.782394, 0.969614)
V 75: Pos(2.283504, -1.146237, 1.296742) Norm(-0.243443, -0.704043, -0.667127) UV(0.790682, 0.969614)
V 76: Pos(2.428398, -1.146237, 1.246102) Norm(0.478888, -0.561687, -0.674666) UV(0.794365, 0.980152)
V 77: Pos(2.428398, -1.146237, 1.36006) Norm(0.654111, -0.597131, 0.464299) UV(0.786077, 0.980152)
V 78: Pos(-2.162589, -1.0, 1.296742) Norm(0.026636, -0.402662, -0.914961) UV(0.244269, 0.353713)
V 79: Pos(-0.787028, -1.0, 1.820116) Norm(0.353547, -0.756586, -0.550075) UV(0.244269, 0.253425)
V 80: Pos(-0.787028, -1.146237, 1.820116) Norm(0.404514, -0.663521, -0.629372) UV(0.254904, 0.253425)
V 81: Pos(-2.162589, -1.146237, 1.296742) Norm(0.253725, -0.70799, -0.65907) UV(0.254904, 0.353713)
V 82: Pos(2.283504, -1.0, 1.410701) Norm(0.342802, 0.0, 0.939408) UV(0.26554, 0.03036)
V 83: Pos(0.907944, -1.0, 1.934074) Norm(0.54068, 0.0, 0.841229) UV(0.26554, 0.130136)
V 84: Pos(0.907944, -1.146237, 1.934074) Norm(0.353547, -0.756586, 0.550075) UV(0.254904, 0.130136)
V 85: Pos(2.283504, -1.146237, 1.410701) Norm(0.241334, -0.710196, 0.661346) UV(0.254904, 0.03036)
V 86: Pos(-0.787029, -0.902648, 0.016616) Norm(0.408248, 0.408248, 0.816497) UV(0.463388, 0.470323)
V 87: Pos(-0.787029, -0.902648, 0.753136) Norm(0.301511, 0.904534, -0.301511) UV(0.463388, 0.416757)
V 88: Pos(-0.787028, -1.0, 0.753136) Norm(0.301511, -0.904534, -0.301512) UV(0.470468, 0.416757)
V 89: Pos(-0.787029, -1.0, 0.016616) Norm(0.707107, -0.707107, -0.0) UV(0.470468, 0.470323)
V 90: Pos(0.060458, -0.902648, 2.656697) Norm(-0.0, 0.578056, -0.815997) UV(0.875495, 0.482562)
V 91: Pos(0.060458, -0.902648, 0.753136) Norm(0.0, 0.707107, -0.707107) UV(0.737052, 0.482562)
V 92: Pos(-0.787029, -0.902648, 1.820116) Norm(0.353009, 0.756776, -0.550158) UV(0.814652, 0.420926)
V 93: Pos(0.907944, -1.0, 0.753136) Norm(-0.301511, -0.904534, -0.301511) UV(0.243391, 0.363192)
V 94: Pos(0.060458, -1.0, 0.753136) Norm(0.0, -0.707107, -0.707107) UV(0.243391, 0.424828)
V 95: Pos(0.060458, -0.902648, 0.753136) Norm(0.0, 0.707107, -0.707107) UV(0.23631, 0.424828)
V 96: Pos(0.907943, -0.902648, 0.753136) Norm(-0.301511, 0.904534, -0.301512) UV(0.23631, 0.363192)
V 97: Pos(0.907944, -1.0, 1.820116) Norm(-0.353547, -0.756586, -0.550075) UV(0.244269, 0.130155)
V 98: Pos(2.283504, -1.0, 1.296742) Norm(-0.005023, -0.39988, -0.916554) UV(0.244269, 0.030362)
V 99: Pos(2.283504, -1.146237, 1.296742) Norm(-0.243443, -0.704043, -0.667127) UV(0.254904, 0.030362)
V 100: Pos(0.907944, -1.146237, 1.820116) Norm(-0.404514, -0.663521, -0.629372) UV(0.254904, 0.130155)
V 101: Pos(-2.292608, -1.146237, 1.246102) Norm(-0.467487, -0.560279, -0.683772) UV(0.254904, 0.363192)
V 102: Pos(-2.292608, -1.0, 1.246102) Norm(-0.564391, 0.0, -0.825508) UV(0.244269, 0.363192)
V 103: Pos(0.060458, -1.0, 2.770656) Norm(0.0, 0.0, 1.0) UV(0.26554, 0.191347)
V 104: Pos(0.060458, -1.146237, 2.770656) Norm(0.0, -0.578056, 0.815997) UV(0.254904, 0.191347)
V 105: Pos(-0.787028, -1.0, 1.934074) Norm(-0.54068, -0.0, 0.841229) UV(0.26554, 0.253405)
V 106: Pos(-0.787028, -1.146237, 1.934074) Norm(-0.353547, -0.756586, 0.550075) UV(0.254904, 0.253405)
V 107: Pos(-2.162589, 1.963949, 1.410701) Norm(-0.359271, -0.0, 0.933233) UV(0.481103, 0.35371)
V 108: Pos(-0.787029, 1.963949, 1.934074) Norm(-0.54068, -0.0, 0.841228) UV(0.481103, 0.253405)
V 109: Pos(-0.787029, 2.10659, 1.934074) Norm(-0.353547, 0.756586, 0.550075) UV(0.491477, 0.253405)
V 110: Pos(-2.162589, 2.10659, 1.410701) Norm(-0.254359, 0.706225, 0.660717) UV(0.491477, 0.35371)
V 111: Pos(-2.162589, -1.0, 1.410701) Norm(-0.359271, -0.0, 0.933233) UV(0.26554, 0.35371)
V 112: Pos(-2.162589, -1.146237, 1.410701) Norm(-0.254359, -0.706225, 0.660717) UV(0.254904, 0.35371)
V 113: Pos(2.283504, 1.963949, 1.410701) Norm(0.342802, 0.0, 0.939408) UV(0.481103, 0.03036)
V 114: Pos(2.428398, -1.0, 1.36006) Norm(0.815454, 0.0, 0.578822) UV(0.26554, 0.019848)
V 115: Pos(2.428398, 1.963949, 1.36006) Norm(0.815454, 0.0, 0.578822) UV(0.481103, 0.019848)
V 116: Pos(-2.162589, 1.963949, 1.296742) Norm(0.026637, 0.402662, -0.914961) UV(0.028706, 0.353713)
V 117: Pos(-2.292608, 1.963949, 1.246102) Norm(-0.56439, 0.0, -0.825508) UV(0.028706, 0.363192)
V 118: Pos(0.060458, -1.0, 2.656697) Norm(0.0, -0.578056, -0.815997) UV(0.244269, 0.191395)
V 119: Pos(0.060458, -1.146237, 2.656697) Norm(0.0, -0.90301, -0.429619) UV(0.254904, 0.191395)
V 120: Pos(0.060457, 1.963949, 2.656697) Norm(0.0, 0.578056, -0.815997) UV(0.028706, 0.191395)
V 121: Pos(-0.787029, 1.963949, 1.820116) Norm(0.353547, 0.756586, -0.550075) UV(0.028706, 0.253425)
V 122: Pos(-0.787029, 2.10659, 1.820116) Norm(0.404514, 0.663521, -0.629372) UV(0.018331, 0.253425)
V 123: Pos(0.060457, 2.10659, 2.656697) Norm(0.0, 0.90301, -0.429619) UV(0.018331, 0.191395)
V 124: Pos(0.907943, 1.963949, 1.820116) Norm(-0.353548, 0.756586, -0.550075) UV(0.028706, 0.130155)
V 125: Pos(0.907943, 2.10659, 1.820116) Norm(-0.404514, 0.663521, -0.629372) UV(0.018331, 0.130155)
V 126: Pos(0.907944, -1.146237, 1.820116) Norm(-0.404514, -0.663521, -0.629372) UV(0.752617, 0.869571)
V 127: Pos(0.907944, -1.146237, 1.934074) Norm(0.353547, -0.756586, 0.550075) UV(0.74433, 0.869571)
V 128: Pos(0.060457, 1.963949, 2.770656) Norm(-0.0, -0.0, 1.0) UV(0.481103, 0.191347)
V 129: Pos(0.907943, 1.963949, 1.934074) Norm(0.54068, 0.0, 0.841228) UV(0.481103, 0.130136)
V 130: Pos(-2.162589, -1.146237, 1.296742) Norm(0.253725, -0.70799, -0.65907) UV(0.790682, 0.646256)
V 131: Pos(-0.787028, -1.146237, 1.820116) Norm(0.404514, -0.663521, -0.629372) UV(0.752618, 0.746299)
V 132: Pos(-0.787028, -1.146237, 1.934074) Norm(-0.353547, -0.756586, 0.550075) UV(0.74433, 0.746299)
V 133: Pos(-2.162589, -1.146237, 1.410701) Norm(-0.254359, -0.706225, 0.660717) UV(0.782394, 0.646256)
V 134: Pos(2.181196, 1.866597, 1.337887) Norm(-0.65901, -0.598824, -0.4551) UV(0.018331, 0.363192)
V 135: Pos(2.181196, -0.902648, 1.337887) Norm(-0.65901, 0.598824, -0.4551) UV(0.219734, 0.363192)
V 136: Pos(0.907943, -0.902648, 1.820116) Norm(-0.353009, 0.756776, -0.550158) UV(0.219734, 0.455564)
V 137: Pos(0.907943, 1.866597, 1.820116) Norm(-0.353009, -0.756776, -0.550158) UV(0.018331, 0.455564)
V 138: Pos(-2.292608, 2.10659, 1.36006) Norm(-0.66075, 0.599445, 0.451747) UV(0.491477, 0.363191)
V 139: Pos(-2.292608, 1.963949, 1.36006) Norm(-0.825508, 0.0, 0.56439) UV(0.481103, 0.363191)
V 140: Pos(0.060458, -1.146237, 2.656697) Norm(0.0, -0.90301, -0.429619) UV(0.691774, 0.807935)
V 141: Pos(0.060458, -1.146237, 2.770656) Norm(0.0, -0.578056, 0.815997) UV(0.683486, 0.807935)
V 142: Pos(0.060458, -0.902648, 2.656697) Norm(-0.0, 0.578056, -0.815997) UV(0.219734, 0.516804)
V 143: Pos(0.060457, 1.866597, 2.656697) Norm(0.0, -0.578056, -0.815997) UV(0.018331, 0.516804)
V 144: Pos(-0.787029, -0.902648, 1.820116) Norm(0.353009, 0.756776, -0.550158) UV(0.219734, 0.578834)
V 145: Pos(-0.787029, 1.866597, 1.820116) Norm(0.353009, -0.756776, -0.550158) UV(0.018331, 0.578834)
V 146: Pos(-2.060281, 1.866597, 1.337887) Norm(0.65901, -0.598824, -0.4551) UV(0.018331, 0.671662)
V 147: Pos(-2.060281, -0.902648, 1.337887) Norm(0.65901, 0.598824, -0.4551) UV(0.219734, 0.671662)
V 148: Pos(2.283504, 2.10659, 1.296742) Norm(-0.243443, 0.704043, -0.667127) UV(0.90156, 0.647338)
V 149: Pos(0.907943, 2.10659, 1.820116) Norm(-0.404514, 0.663521, -0.629372) UV(0.863496, 0.74738)
V 150: Pos(0.907943, 2.10659, 1.934074) Norm(0.353547, 0.756586, 0.550075) UV(0.855208, 0.74738)
V 151: Pos(2.283504, 2.10659, 1.410701) Norm(0.241334, 0.710196, 0.661346) UV(0.893272, 0.647338)
V 152: Pos(-0.787029, 2.10659, 1.820116) Norm(0.404514, 0.663521, -0.629372) UV(0.863496, 0.870653)
V 153: Pos(-2.162589, 2.10659, 1.296742) Norm(0.253725, 0.70799, -0.65907) UV(0.90156, 0.970695)
V 154: Pos(-2.162589, 2.10659, 1.410701) Norm(-0.254359, 0.706225, 0.660717) UV(0.893272, 0.970695)
V 155: Pos(-0.787029, 2.10659, 1.934074) Norm(-0.353547, 0.756586, 0.550075) UV(0.855208, 0.870653)
V 156: Pos(0.060457, 2.10659, 2.656697) Norm(0.0, 0.90301, -0.429619) UV(0.802653, 0.809017)
V 157: Pos(0.060457, 2.10659, 2.770656) Norm(0.0, 0.578056, 0.815997) UV(0.794365, 0.809017)
V 158: Pos(-2.162589, 2.10659, 1.296742) Norm(0.253725, 0.70799, -0.65907) UV(0.018331, 0.353713)
V 159: Pos(0.907943, 2.10659, 1.934074) Norm(0.353547, 0.756586, 0.550075) UV(0.491477, 0.130136)
V 160: Pos(0.060457, 2.10659, 2.770656) Norm(0.0, 0.578056, 0.815997) UV(0.491477, 0.191347)
V 161: Pos(2.283504, 1.963949, 1.296742) Norm(-0.005023, 0.39988, -0.916554) UV(0.028706, 0.030362)
V 162: Pos(2.283504, 2.10659, 1.296742) Norm(-0.243443, 0.704043, -0.667127) UV(0.018331, 0.030362)
V 163: Pos(2.283504, 2.10659, 1.410701) Norm(0.241334, 0.710196, 0.661346) UV(0.491477, 0.03036)
V 164: Pos(2.428398, 2.10659, 1.36006) Norm(0.654111, 0.597131, 0.464299) UV(0.896955, 0.6368)
V 165: Pos(2.428398, 2.10659, 1.246102) Norm(0.478889, 0.561687, -0.674666) UV(0.905243, 0.6368)
V 166: Pos(2.428398, -1.0, 1.246102) Norm(0.578822, 0.0, -0.815454) UV(0.23631, 0.373828)
V 167: Pos(2.428398, -1.0, 1.36006) Norm(0.815454, 0.0, 0.578822) UV(0.228022, 0.373828)
V 168: Pos(2.428398, -1.146237, 1.36006) Norm(0.654111, -0.597131, 0.464299) UV(0.228022, 0.363192)
V 169: Pos(2.428398, -1.146237, 1.246102) Norm(0.478888, -0.561687, -0.674666) UV(0.23631, 0.363192)
V 170: Pos(2.428398, 1.963949, 1.246102) Norm(0.578822, 0.0, -0.815454) UV(0.23631, 0.589391)
V 171: Pos(2.428398, 1.963949, 1.36006) Norm(0.815454, 0.0, 0.578822) UV(0.228022, 0.589391)
V 172: Pos(2.428398, 2.10659, 1.246102) Norm(0.478889, 0.561687, -0.674666) UV(0.23631, 0.599765)
V 173: Pos(2.428398, 2.10659, 1.36006) Norm(0.654111, 0.597131, 0.464299) UV(0.228022, 0.599765)
V 174: Pos(2.428398, -1.0, 1.246102) Norm(0.578822, 0.0, -0.815454) UV(0.244269, 0.019848)
V 175: Pos(2.428398, -1.146237, 1.246102) Norm(0.478888, -0.561687, -0.674666) UV(0.254904, 0.019848)
V 176: Pos(2.428398, 2.10659, 1.246102) Norm(0.478889, 0.561687, -0.674666) UV(0.018331, 0.019848)
V 177: Pos(2.428398, 1.963949, 1.246102) Norm(0.578822, 0.0, -0.815454) UV(0.028706, 0.019848)
V 178: Pos(2.428398, 2.10659, 1.36006) Norm(0.654111, 0.597131, 0.464299) UV(0.491477, 0.019848)
V 179: Pos(2.428398, -1.146237, 1.36006) Norm(0.654111, -0.597131, 0.464299) UV(0.254904, 0.019848)
V 180: Pos(-2.292608, 1.963949, 1.246102) Norm(-0.56439, 0.0, -0.825508) UV(0.228022, 0.373567)
V 181: Pos(-2.292608, -1.0, 1.246102) Norm(-0.564391, 0.0, -0.825508) UV(0.228022, 0.58913)
V 182: Pos(-2.292608, -1.0, 1.36006) Norm(-0.825508, 0.0, 0.56439) UV(0.219734, 0.58913)
V 183: Pos(-2.292608, 1.963949, 1.36006) Norm(-0.825508, 0.0, 0.56439) UV(0.219734, 0.373567)
V 184: Pos(-2.292608, 2.10659, 1.36006) Norm(-0.66075, 0.599445, 0.451747) UV(0.219734, 0.363192)
V 185: Pos(-2.292608, 2.10659, 1.246102) Norm(-0.467487, 0.560279, -0.683772) UV(0.228022, 0.363192)
V 186: Pos(-2.292608, -1.146237, 1.246102) Norm(-0.467487, -0.560279, -0.683772) UV(0.228022, 0.599765)
V 187: Pos(-2.292608, -1.146237, 1.36006) Norm(-0.66075, -0.599445, 0.451747) UV(0.219734, 0.599765)
V 188: Pos(-2.292608, 2.10659, 1.246102) Norm(-0.467487, 0.560279, -0.683772) UV(0.905243, 0.980152)
V 189: Pos(-2.292608, 2.10659, 1.36006) Norm(-0.66075, 0.599445, 0.451747) UV(0.896955, 0.980152)
V 190: Pos(-2.292608, -1.146237, 1.36006) Norm(-0.66075, -0.599445, 0.451747) UV(0.786077, 0.6368)
V 191: Pos(-2.292608, -1.146237, 1.246102) Norm(-0.467487, -0.560279, -0.683772) UV(0.794365, 0.6368)
V 192: Pos(-2.292608, 2.10659, 1.246102) Norm(-0.467487, 0.560279, -0.683772) UV(0.018331, 0.363192)
V 193: Pos(-2.292608, -1.0, 1.36006) Norm(-0.825508, 0.0, 0.56439) UV(0.26554, 0.363191)
V 194: Pos(-2.292608, -1.146237, 1.36006) Norm(-0.66075, -0.599445, 0.451747) UV(0.254904, 0.363191)
V 195: Pos(2.181196, -0.902648, 1.337887) Norm(-0.65901, 0.598824, -0.4551) UV(0.77958, 0.6368)
V 196: Pos(0.907943, -0.902648, 1.820116) Norm(-0.353009, 0.756776, -0.550158) UV(0.814652, 0.544199)
V 197: Pos(2.181196, 1.866597, 1.337887) Norm(-0.65901, -0.598824, -0.4551) UV(0.254904, 0.67166)
V 198: Pos(2.181196, -0.902648, 1.337887) Norm(-0.65901, 0.598824, -0.4551) UV(0.254904, 0.873063)
V 199: Pos(2.181196, 1.866597, 0.016616) Norm(-0.57735, -0.57735, 0.57735) UV(0.683486, 0.019848)
V 200: Pos(0.907943, 1.866597, 0.016616) Norm(-0.408248, -0.408247, 0.816497) UV(0.683486, 0.11245)
V 201: Pos(0.060458, 1.963949, 0.753136) Norm(0.0, 0.707107, -0.707107) UV(0.545043, 0.181527)
V 202: Pos(0.060457, 1.963949, 2.656697) Norm(0.0, 0.578056, -0.815997) UV(0.683486, 0.181527)
V 203: Pos(0.060458, -1.0, 0.753136) Norm(0.0, -0.707107, -0.707107) UV(0.545043, 0.504884)
V 204: Pos(0.060458, -1.0, 2.656697) Norm(0.0, -0.578056, -0.815997) UV(0.683486, 0.504884)
V 205: Pos(0.907944, 1.963949, 0.016616) Norm(-0.707106, 0.707108, -0.0) UV(0.491477, 0.243163)
V 206: Pos(2.283504, 1.963949, 0.016616) Norm(0.707107, 0.707107, 0.0) UV(0.491477, 0.343206)
V 207: Pos(-0.787029, -1.0, 0.016616) Norm(0.707107, -0.707107, -0.0) UV(0.491477, 0.566521)
V 208: Pos(-2.162589, -1.0, 0.016616) Norm(-0.707107, -0.707107, 0.0) UV(0.491477, 0.666563)
V 209: Pos(2.283504, -1.0, 0.016616) Norm(0.707107, -0.707107, 0.0) UV(0.491477, 0.343206)
V 210: Pos(0.907943, -1.0, 0.016616) Norm(-0.707107, -0.707107, 0.0) UV(0.491477, 0.443248)
V 211: Pos(-2.162589, 1.963949, 0.016616) Norm(-0.707107, 0.707107, 0.0) UV(0.491477, 0.019848)
V 212: Pos(-0.787028, 1.963949, 0.016616) Norm(0.707107, 0.707106, 0.0) UV(0.491477, 0.119891)
V 213: Pos(2.283504, 1.963949, 0.016616) Norm(0.707107, 0.707107, 0.0) UV(0.677681, 0.882126)
V 214: Pos(2.283504, -1.0, 0.016616) Norm(0.707107, -0.707107, 0.0) UV(0.677681, 0.666563)
V 215: Pos(-2.162589, -1.0, 0.016616) Norm(-0.707107, -0.707107, 0.0) UV(0.584579, 0.882126)
V 216: Pos(-2.162589, 1.963949, 0.016616) Norm(-0.707107, 0.707107, 0.0) UV(0.584579, 0.666563)
V 217: Pos(-2.060281, 1.866597, 0.016616) Norm(0.57735, -0.57735, 0.57735) UV(0.114425, 0.873065)
V 218: Pos(-2.060281, -0.902648, 0.016616) Norm(0.57735, 0.57735, 0.57735) UV(0.114426, 0.671662)
V 219: Pos(-2.060281, -0.902648, 1.337887) Norm(0.65901, 0.598824, -0.4551) UV(0.77958, 0.328324)
V 220: Pos(-0.787029, 1.866597, 0.016616) Norm(0.408248, -0.408249, 0.816496) UV(0.683486, 0.235723)
V 221: Pos(-2.060281, 1.866597, 0.016616) Norm(0.57735, -0.57735, 0.57735) UV(0.683486, 0.328324)
V 222: Pos(-0.787028, -1.0, 0.753136) Norm(0.301511, -0.904534, -0.301512) UV(0.243391, 0.486463)
V 223: Pos(-0.787029, -0.902648, 0.753136) Norm(0.301511, 0.904534, -0.301511) UV(0.23631, 0.486463)
V 224: Pos(-2.060281, -0.902648, 0.016616) Norm(0.57735, 0.57735, 0.57735) UV(0.254904, 0.67166)
V 225: Pos(-2.060281, 1.866597, 0.016616) Norm(0.57735, -0.57735, 0.57735) UV(0.456307, 0.67166)
===Triangles (Indices): 194
Tri: 0 1 2
Tri: 0 2 3
Tri: 4 5 6
Tri: 4 6 7
Tri: 8 9 10
Tri: 8 10 11
Tri: 12 13 14
Tri: 12 14 15
Tri: 16 17 18
Tri: 16 18 19
Tri: 20 10 9
Tri: 20 9 21
Tri: 22 23 24
Tri: 22 24 25
Tri: 26 27 28
Tri: 26 28 29
Tri: 30 31 32
Tri: 30 32 33
Tri: 34 35 36
Tri: 34 36 37
Tri: 38 39 40
Tri: 38 40 41
Tri: 42 43 44
Tri: 42 44 45
Tri: 46 47 48
Tri: 46 48 49
Tri: 50 51 52
Tri: 50 52 53
Tri: 29 28 54
Tri: 29 54 55
Tri: 56 57 58
Tri: 56 58 59
Tri: 60 61 62
Tri: 60 62 63
Tri: 64 65 66
Tri: 64 66 67
Tri: 13 12 68
Tri: 13 68 69
Tri: 69 68 37
Tri: 69 37 36
Tri: 70 71 72
Tri: 70 72 73
Tri: 74 75 76
Tri: 74 76 77
Tri: 78 79 80
Tri: 78 80 81
Tri: 82 83 84
Tri: 82 84 85
Tri: 86 87 88
Tri: 86 88 89
Tri: 90 91 16
Tri: 90 16 92
Tri: 93 94 95
Tri: 93 95 96
Tri: 97 98 99
Tri: 97 99 100
Tri: 78 81 101
Tri: 78 101 102
Tri: 83 103 104
Tri: 83 104 84
Tri: 103 105 106
Tri: 103 106 104
Tri: 107 108 109
Tri: 107 109 110
Tri: 105 111 112
Tri: 105 112 106
Tri: 113 82 114
Tri: 113 114 115
Tri: 116 78 102
Tri: 116 102 117
Tri: 118 97 100
Tri: 118 100 119
Tri: 120 121 122
Tri: 120 122 123
Tri: 124 120 123
Tri: 124 123 125
Tri: 126 75 74
Tri: 126 74 127
Tri: 103 128 108
Tri: 103 108 105
Tri: 128 103 83
Tri: 128 83 129
Tri: 130 131 132
Tri: 130 132 133
Tri: 111 105 108
Tri: 111 108 107
Tri: 134 135 136
Tri: 134 136 137
Tri: 79 118 119
Tri: 79 119 80
Tri: 107 110 138
Tri: 107 138 139
Tri: 140 126 127
Tri: 140 127 141
Tri: 131 140 141
Tri: 131 141 132
Tri: 142 143 137
Tri: 142 137 136
Tri: 143 142 144
Tri: 143 144 145
Tri: 146 145 144
Tri: 146 144 147
Tri: 113 129 83
Tri: 113 83 82
Tri: 148 149 150
Tri: 148 150 151
Tri: 152 153 154
Tri: 152 154 155
Tri: 156 152 155
Tri: 156 155 157
Tri: 149 156 157
Tri: 149 157 150
Tri: 121 116 158
Tri: 121 158 122
Tri: 128 129 159
Tri: 128 159 160
Tri: 161 124 125
Tri: 161 125 162
Tri: 129 113 163
Tri: 129 163 159
Tri: 148 151 164
Tri: 148 164 165
Tri: 108 128 160
Tri: 108 160 109
Tri: 166 167 168
Tri: 166 168 169
Tri: 166 170 171
Tri: 166 171 167
Tri: 171 170 172
Tri: 171 172 173
Tri: 99 98 174
Tri: 99 174 175
Tri: 161 162 176
Tri: 161 176 177
Tri: 98 161 177
Tri: 98 177 174
Tri: 163 113 115
Tri: 163 115 178
Tri: 82 85 179
Tri: 82 179 114
Tri: 180 181 182
Tri: 180 182 183
Tri: 180 183 184
Tri: 180 184 185
Tri: 182 181 186
Tri: 182 186 187
Tri: 154 153 188
Tri: 154 188 189
Tri: 130 133 190
Tri: 130 190 191
Tri: 158 116 117
Tri: 158 117 192
Tri: 112 111 193
Tri: 112 193 194
Tri: 111 107 139
Tri: 111 139 193
Tri: 195 42 45
Tri: 195 45 196
Tri: 197 38 41
Tri: 197 41 198
Tri: 199 34 37
Tri: 199 37 200
Tri: 61 60 201
Tri: 61 201 202
Tri: 57 56 203
Tri: 57 203 204
Tri: 202 201 49
Tri: 202 49 48
Tri: 204 203 53
Tri: 204 53 52
Tri: 205 60 63
Tri: 205 63 206
Tri: 207 56 59
Tri: 207 59 208
Tri: 209 50 53
Tri: 209 53 210
Tri: 211 46 49
Tri: 211 49 212
Tri: 213 4 7
Tri: 213 7 214
Tri: 215 0 3
Tri: 215 3 216
Tri: 217 30 33
Tri: 217 33 218
Tri: 92 16 19
Tri: 92 19 219
Tri: 220 12 15
Tri: 220 15 221
Tri: 45 91 90
Tri: 45 90 196
Tri: 222 223 95
Tri: 222 95 94
Tri: 224 8 11
Tri: 224 11 225
+587
View File
@@ -0,0 +1,587 @@
===Vertices (Split by UV/Normal): 308
V 0: Pos(1.869281, 0.711913, 0.20808) Norm(-0.761914, -0.471586, 0.443953) UV(0.940254, 0.275626)
V 1: Pos(1.909114, 0.661025, 0.20808) Norm(-0.635819, -0.635639, 0.437832) UV(0.941821, 0.050079)
V 2: Pos(1.911422, 0.662832, 0.209918) Norm(-0.411643, 0.156254, 0.89785) UV(0.952296, 0.050152)
V 3: Pos(1.87159, 0.71372, 0.209918) Norm(0.05145, -0.345966, 0.936835) UV(0.950729, 0.275699)
V 4: Pos(1.913993, 0.664845, 0.208771) Norm(0.18131, 0.807914, 0.560716) UV(0.963526, 0.05023)
V 5: Pos(1.874161, 0.715733, 0.208771) Norm(0.765568, 0.102584, 0.635124) UV(0.961959, 0.275777)
V 6: Pos(1.874161, 0.715733, 0.208771) Norm(0.765568, 0.102584, 0.635124) UV(0.570192, 0.276899)
V 7: Pos(1.913993, 0.664845, 0.208771) Norm(0.18131, 0.807914, 0.560716) UV(0.570192, 0.054444)
V 8: Pos(1.914891, 0.665547, 0.205504) Norm(0.67715, 0.693757, -0.245293) UV(0.582143, 0.054731)
V 9: Pos(1.875058, 0.716435, 0.205504) Norm(0.834547, 0.507059, -0.21546) UV(0.582143, 0.277186)
V 10: Pos(1.913438, 0.66441, 0.202576) Norm(0.516638, 0.080688, -0.852393) UV(0.590955, 0.053367)
V 11: Pos(1.873606, 0.715298, 0.202576) Norm(0.21114, 0.469699, -0.857206) UV(0.590955, 0.275822)
V 12: Pos(1.873606, 0.715298, 0.202576) Norm(0.21114, 0.469699, -0.857206) UV(0.534956, 0.846336)
V 13: Pos(1.913438, 0.66441, 0.202576) Norm(0.516638, 0.080688, -0.852393) UV(0.534956, 0.620784)
V 14: Pos(1.91073, 0.662291, 0.202192) Norm(-0.030373, -0.478808, -0.877394) UV(0.546978, 0.620784)
V 15: Pos(1.870898, 0.713179, 0.202192) Norm(-0.465715, 0.071095, -0.882074) UV(0.546978, 0.846336)
V 16: Pos(1.872261, 0.722571, 0.208011) Norm(0.161414, 0.736744, -0.656623) UV(0.574598, 0.300921)
V 17: Pos(1.874394, 0.719846, 0.208011) Norm(0.875251, 0.274505, -0.398224) UV(0.574598, 0.28901)
V 18: Pos(1.908806, 0.660784, 0.204642) Norm(-0.491082, -0.800738, -0.343011) UV(0.555378, 0.620784)
V 19: Pos(1.868973, 0.711672, 0.204642) Norm(-0.888391, -0.298097, -0.349142) UV(0.555378, 0.846336)
V 20: Pos(1.868973, 0.711672, 0.204642) Norm(-0.888391, -0.298097, -0.349142) UV(0.064087, 0.044995)
V 21: Pos(1.908806, 0.660784, 0.204642) Norm(-0.491082, -0.800738, -0.343011) UV(0.288741, 0.035517)
V 22: Pos(1.909114, 0.661025, 0.20808) Norm(-0.635819, -0.635639, 0.437832) UV(0.289208, 0.047573)
V 23: Pos(1.869281, 0.711913, 0.20808) Norm(-0.761914, -0.471586, 0.443953) UV(0.064555, 0.057052)
V 24: Pos(1.869128, 0.722859, 0.209453) Norm(-0.579803, 0.709466, -0.400608) UV(0.530635, 0.876748)
V 25: Pos(1.872261, 0.722571, 0.208011) Norm(0.161414, 0.736744, -0.656623) UV(0.522723, 0.869218)
V 26: Pos(1.869128, 0.722859, 0.209453) Norm(-0.579803, 0.709466, -0.400608) UV(0.881676, 0.224478)
V 27: Pos(1.867355, 0.720493, 0.211251) Norm(-0.978626, 0.203528, 0.029447) UV(0.893616, 0.224476)
V 28: Pos(1.905059, 0.750005, 0.287268) Norm(-0.726396, 0.413925, 0.548648) UV(0.893569, 0.538039)
V 29: Pos(1.906832, 0.752371, 0.28547) Norm(-0.303191, 0.93752, 0.17068) UV(0.88163, 0.538041)
V 30: Pos(1.912745, 0.662505, 0.211251) Norm(-0.768111, 0.333689, 0.546496) UV(0.292472, 0.059404)
V 31: Pos(1.913666, 0.659267, 0.212051) Norm(-0.641122, -0.621838, 0.449755) UV(0.303777, 0.061406)
V 32: Pos(1.95137, 0.688779, 0.288069) Norm(-0.567274, -0.561274, 0.602638) UV(0.303777, 0.337972)
V 33: Pos(1.950449, 0.692017, 0.287268) Norm(-0.977416, 0.171509, 0.123465) UV(0.292472, 0.33597)
V 34: Pos(1.871199, 0.715583, 0.211251) Norm(0.121887, -0.767984, 0.628763) UV(0.953797, 0.281682)
V 35: Pos(1.873921, 0.716736, 0.209453) Norm(0.873273, -0.419594, 0.247661) UV(0.963526, 0.279062)
V 36: Pos(1.873921, 0.716736, 0.209453) Norm(0.873273, -0.419594, 0.247661) UV(0.568141, 0.280446)
V 37: Pos(1.870898, 0.713179, 0.202192) Norm(-0.465715, 0.071095, -0.882074) UV(0.883904, 0.194467)
V 38: Pos(1.868973, 0.711672, 0.204642) Norm(-0.888391, -0.298097, -0.349142) UV(0.895647, 0.197158)
V 39: Pos(1.868277, 0.717255, 0.212051) Norm(-0.764855, -0.469795, 0.440783) UV(0.047779, 0.072207)
V 40: Pos(1.867355, 0.720493, 0.211251) Norm(-0.978626, 0.203528, 0.029447) UV(0.036474, 0.070205)
V 41: Pos(1.868277, 0.717255, 0.212051) Norm(-0.764855, -0.469795, 0.440783) UV(0.9494, 0.292533)
V 42: Pos(1.913993, 0.664845, 0.208771) Norm(0.18131, 0.807914, 0.560716) UV(0.928243, 0.043066)
V 43: Pos(1.914518, 0.66487, 0.209453) Norm(-0.305786, 0.941985, 0.138418) UV(0.928315, 0.045875)
V 44: Pos(1.917651, 0.664582, 0.208011) Norm(0.44499, 0.797325, -0.407746) UV(0.919448, 0.045841)
V 45: Pos(1.914891, 0.665547, 0.205504) Norm(0.67715, 0.693757, -0.245293) UV(0.91884, 0.035517)
V 46: Pos(1.917651, 0.664582, 0.208011) Norm(0.44499, 0.797325, -0.407746) UV(0.574598, 0.047428)
V 47: Pos(1.919784, 0.661858, 0.208011) Norm(0.764109, -0.017582, -0.644848) UV(0.574598, 0.035517)
V 48: Pos(1.919784, 0.661858, 0.208011) Norm(0.764109, -0.017582, -0.644848) UV(0.522723, 0.600118)
V 49: Pos(1.919311, 0.658748, 0.209453) Norm(0.559032, -0.73323, -0.387114) UV(0.530635, 0.592588)
V 50: Pos(1.91073, 0.662291, 0.202192) Norm(-0.030373, -0.478808, -0.877394) UV(0.907113, 0.412291)
V 51: Pos(1.919311, 0.658748, 0.209453) Norm(0.559032, -0.73323, -0.387114) UV(0.90461, 0.38031)
V 52: Pos(1.916588, 0.657595, 0.211251) Norm(-0.026527, -0.998769, 0.041913) UV(0.916575, 0.380425)
V 53: Pos(1.908806, 0.660784, 0.204642) Norm(-0.491082, -0.800738, -0.343011) UV(0.91884, 0.409524)
V 54: Pos(1.916588, 0.657595, 0.211251) Norm(-0.026527, -0.998769, 0.041913) UV(0.314148, 0.058489)
V 55: Pos(1.913666, 0.659267, 0.212051) Norm(-0.641122, -0.621838, 0.449755) UV(0.951185, 0.035517)
V 56: Pos(1.912745, 0.662505, 0.211251) Norm(-0.768111, 0.333689, 0.546496) UV(0.955431, 0.046428)
V 57: Pos(1.911422, 0.662832, 0.209918) Norm(-0.411643, 0.156254, 0.89785) UV(0.939991, 0.040403)
V 58: Pos(1.912745, 0.662505, 0.211251) Norm(-0.768111, 0.333689, 0.546496) UV(0.940254, 0.045895)
V 59: Pos(1.950787, 0.694725, 0.293158) Norm(-0.542765, -0.570773, 0.616137) UV(0.286249, 0.35723)
V 60: Pos(1.950479, 0.694484, 0.28972) Norm(-0.94827, -0.260746, -0.181095) UV(0.285781, 0.345173)
V 61: Pos(1.950449, 0.692017, 0.287268) Norm(-0.977416, 0.171509, 0.123465) UV(0.939646, 0.359457)
V 62: Pos(1.950479, 0.694484, 0.28972) Norm(-0.94827, -0.260746, -0.181095) UV(0.939135, 0.369584)
V 63: Pos(1.952403, 0.695991, 0.287271) Norm(-0.504568, 0.268536, -0.820548) UV(0.927398, 0.366872)
V 64: Pos(1.952222, 0.694382, 0.28547) Norm(-0.556023, 0.743669, -0.371209) UV(0.927707, 0.359437)
V 65: Pos(1.872261, 0.722571, 0.208011) Norm(0.161414, 0.736744, -0.656623) UV(0.87281, 0.22446)
V 66: Pos(1.909964, 0.752083, 0.284029) Norm(0.355973, 0.897102, -0.261709) UV(0.872763, 0.538023)
V 67: Pos(1.955354, 0.694095, 0.284029) Norm(0.353521, 0.733298, -0.580772) UV(0.91884, 0.359403)
V 68: Pos(1.909964, 0.752083, 0.284029) Norm(0.355973, 0.897102, -0.261709) UV(0.345823, 0.336334)
V 69: Pos(1.912097, 0.749358, 0.284029) Norm(0.796741, 0.167077, -0.580765) UV(0.345823, 0.324423)
V 70: Pos(1.955354, 0.694095, 0.284029) Norm(0.353521, 0.733298, -0.580772) UV(0.345823, 0.082841)
V 71: Pos(1.957487, 0.69137, 0.284029) Norm(0.956337, 0.1301, -0.261713) UV(0.345823, 0.070929)
V 72: Pos(1.873921, 0.716736, 0.209453) Norm(0.873273, -0.419594, 0.247661) UV(0.948579, 0.920279)
V 73: Pos(1.874394, 0.719846, 0.208011) Norm(0.875251, 0.274505, -0.398224) UV(0.939834, 0.920881)
V 74: Pos(1.912097, 0.749358, 0.284029) Norm(0.796741, 0.167077, -0.580765) UV(0.940802, 0.608213)
V 75: Pos(1.911624, 0.746248, 0.28547) Norm(0.588348, -0.71835, -0.37124) UV(0.949547, 0.607611)
V 76: Pos(1.919784, 0.661858, 0.208011) Norm(0.764109, -0.017582, -0.644848) UV(0.895867, 0.380945)
V 77: Pos(1.957487, 0.69137, 0.284029) Norm(0.956337, 0.1301, -0.261713) UV(0.895647, 0.068276)
V 78: Pos(1.957014, 0.68826, 0.28547) Norm(0.83726, -0.519484, 0.170681) UV(0.90439, 0.067641)
V 79: Pos(1.871199, 0.715583, 0.211251) Norm(0.121887, -0.767984, 0.628763) UV(0.960544, 0.92044)
V 80: Pos(1.908902, 0.745095, 0.287268) Norm(-0.068272, -0.989999, 0.12345) UV(0.961512, 0.607772)
V 81: Pos(1.954292, 0.687107, 0.287268) Norm(0.22735, -0.804542, 0.548657) UV(0.916355, 0.067756)
V 82: Pos(1.871199, 0.715583, 0.211251) Norm(0.121887, -0.767984, 0.628763) UV(0.058149, 0.069291)
V 83: Pos(1.908902, 0.745095, 0.287268) Norm(-0.068272, -0.989999, 0.12345) UV(0.058149, 0.345857)
V 84: Pos(1.90598, 0.746767, 0.288069) Norm(-0.681101, -0.415856, 0.602632) UV(0.047779, 0.348773)
V 85: Pos(1.954292, 0.687107, 0.287268) Norm(0.22735, -0.804542, 0.548657) UV(0.314148, 0.335055)
V 86: Pos(1.905059, 0.750005, 0.287268) Norm(-0.726396, 0.413925, 0.548648) UV(0.036474, 0.346771)
V 87: Pos(1.953095, 0.696532, 0.294996) Norm(0.149457, -0.309654, 0.93903) UV(0.298425, 0.453344)
V 88: Pos(1.955666, 0.698545, 0.29385) Norm(0.73947, 0.113211, 0.663602) UV(0.309097, 0.456842)
V 89: Pos(1.952377, 0.702748, 0.29385) Norm(0.053881, 0.831106, 0.553499) UV(0.303295, 0.474544)
V 90: Pos(1.949806, 0.700735, 0.294996) Norm(-0.451341, 0.435647, 0.778783) UV(0.292623, 0.471046)
V 91: Pos(1.913921, 0.74658, 0.294996) Norm(-0.264714, 0.219449, 0.939025) UV(0.298479, 0.388008)
V 92: Pos(1.911613, 0.744773, 0.293158) Norm(-0.684428, -0.389788, 0.616136) UV(0.288518, 0.391248)
V 93: Pos(1.914952, 0.740506, 0.293159) Norm(-0.139196, -0.897892, 0.41763) UV(0.282669, 0.373265)
V 94: Pos(1.917261, 0.742313, 0.294996) Norm(0.314498, -0.542755, 0.778787) UV(0.292631, 0.370025)
V 95: Pos(1.954292, 0.687107, 0.287268) Norm(0.22735, -0.804542, 0.548657) UV(0.290165, 0.420667)
V 96: Pos(1.950787, 0.694725, 0.293158) Norm(-0.542765, -0.570773, 0.616137) UV(0.288471, 0.450082)
V 97: Pos(1.95137, 0.688779, 0.288069) Norm(-0.567274, -0.561274, 0.602638) UV(0.282669, 0.429661)
V 98: Pos(1.955666, 0.698545, 0.29385) Norm(0.73947, 0.113211, 0.663602) UV(0.90711, 0.035517)
V 99: Pos(1.953095, 0.696532, 0.294996) Norm(0.149457, -0.309654, 0.93903) UV(0.91884, 0.03811)
V 100: Pos(1.956563, 0.699247, 0.290583) Norm(0.921035, 0.386812, -0.045518) UV(0.326099, 0.097295)
V 101: Pos(1.955666, 0.698545, 0.29385) Norm(0.73947, 0.113211, 0.663602) UV(0.314148, 0.097008)
V 102: Pos(1.957014, 0.68826, 0.28547) Norm(0.83726, -0.519484, 0.170681) UV(0.339366, 0.062365)
V 103: Pos(1.955111, 0.69811, 0.287655) Norm(0.424828, 0.496372, -0.757057) UV(0.334911, 0.095931)
V 104: Pos(1.952222, 0.694382, 0.28547) Norm(-0.556023, 0.743669, -0.371209) UV(0.298744, 0.536259)
V 105: Pos(1.952403, 0.695991, 0.287271) Norm(-0.504568, 0.268536, -0.820548) UV(0.294691, 0.540289)
V 106: Pos(1.955111, 0.69811, 0.287655) Norm(0.424828, 0.496372, -0.757057) UV(0.282669, 0.540289)
V 107: Pos(1.955354, 0.694095, 0.284029) Norm(0.353521, 0.733298, -0.580772) UV(0.290831, 0.528729)
V 108: Pos(1.913229, 0.746038, 0.287271) Norm(0.13947, -0.554268, -0.82057) UV(0.949217, 0.599902)
V 109: Pos(1.911304, 0.744532, 0.28972) Norm(-0.480863, -0.857888, -0.181104) UV(0.960954, 0.59718)
V 110: Pos(1.911304, 0.744532, 0.28972) Norm(-0.480863, -0.857888, -0.181104) UV(0.06484, 0.354495)
V 111: Pos(1.911613, 0.744773, 0.293158) Norm(-0.684428, -0.389788, 0.616136) UV(0.065307, 0.366552)
V 112: Pos(1.905059, 0.750005, 0.287268) Norm(-0.726396, 0.413925, 0.548648) UV(0.290144, 0.420667)
V 113: Pos(1.90598, 0.746767, 0.288069) Norm(-0.681101, -0.415856, 0.602632) UV(0.282669, 0.411656)
V 114: Pos(1.913921, 0.74658, 0.294996) Norm(-0.264714, 0.219449, 0.939025) UV(0.895647, 0.569857)
V 115: Pos(1.916492, 0.748592, 0.29385) Norm(0.287488, 0.690641, 0.6636) UV(0.883904, 0.572541)
V 116: Pos(1.906832, 0.752371, 0.28547) Norm(-0.303191, 0.93752, 0.17068) UV(0.339366, 0.342623)
V 117: Pos(1.916492, 0.748592, 0.29385) Norm(0.287488, 0.690641, 0.6636) UV(0.314148, 0.315787)
V 118: Pos(1.917389, 0.749294, 0.290583) Norm(0.596697, 0.801175, -0.04552) UV(0.326099, 0.316075)
V 119: Pos(1.915937, 0.748158, 0.287655) Norm(0.583885, 0.293186, -0.757047) UV(0.33491, 0.314711)
V 120: Pos(1.915937, 0.748158, 0.287655) Norm(0.583885, 0.293186, -0.757047) UV(0.939834, 0.592427)
V 121: Pos(1.947189, 0.698687, 0.28972) Norm(-0.965573, 0.033132, -0.258014) UV(0.282669, 0.494161)
V 122: Pos(1.947497, 0.698928, 0.293158) Norm(-0.905044, 0.080519, 0.417626) UV(0.282669, 0.482089)
V 123: Pos(1.949806, 0.700735, 0.294996) Norm(-0.451341, 0.435647, 0.778783) UV(0.29209, 0.474544)
V 124: Pos(1.952377, 0.702748, 0.29385) Norm(0.053881, 0.831106, 0.553499) UV(0.303839, 0.477207)
V 125: Pos(1.953274, 0.70345, 0.290583) Norm(0.23019, 0.969106, -0.088577) UV(0.309067, 0.488073)
V 126: Pos(1.951822, 0.702313, 0.287655) Norm(-0.055188, 0.74573, -0.663958) UV(0.303839, 0.498959)
V 127: Pos(1.949113, 0.700194, 0.287271) Norm(-0.587358, 0.329173, -0.739362) UV(0.29209, 0.501669)
V 128: Pos(1.951822, 0.702313, 0.287655) Norm(-0.055188, 0.74573, -0.663958) UV(0.334911, 0.114304)
V 129: Pos(1.953274, 0.70345, 0.290583) Norm(0.23019, 0.969106, -0.088577) UV(0.326099, 0.115668)
V 130: Pos(1.950479, 0.694484, 0.28972) Norm(-0.94827, -0.260746, -0.181095) UV(0.303091, 0.540289)
V 131: Pos(1.947189, 0.698687, 0.28972) Norm(-0.965573, 0.033132, -0.258014) UV(0.303091, 0.558917)
V 132: Pos(1.949113, 0.700194, 0.287271) Norm(-0.587358, 0.329173, -0.739362) UV(0.294691, 0.558917)
V 133: Pos(1.947497, 0.698928, 0.293158) Norm(-0.905044, 0.080519, 0.417626) UV(0.282669, 0.467784)
V 134: Pos(1.952377, 0.702748, 0.29385) Norm(0.053881, 0.831106, 0.553499) UV(0.314148, 0.11538)
V 135: Pos(1.951822, 0.702313, 0.287655) Norm(-0.055188, 0.74573, -0.663958) UV(0.282669, 0.558917)
V 136: Pos(1.947497, 0.698928, 0.293158) Norm(-0.905044, 0.080519, 0.417626) UV(0.267695, 0.358013)
V 137: Pos(1.947189, 0.698687, 0.28972) Norm(-0.965573, 0.033132, -0.258014) UV(0.267227, 0.345956)
V 138: Pos(1.914952, 0.740506, 0.293159) Norm(-0.139196, -0.897892, 0.41763) UV(0.309023, 0.515086)
V 139: Pos(1.914644, 0.740265, 0.28972) Norm(-0.199734, -0.94527, -0.258013) UV(0.303804, 0.525971)
V 140: Pos(1.916569, 0.741772, 0.287271) Norm(0.178483, -0.649214, -0.739368) UV(0.292075, 0.528729)
V 141: Pos(1.919277, 0.743891, 0.287655) Norm(0.710654, -0.232666, -0.663956) UV(0.282669, 0.521283)
V 142: Pos(1.920729, 0.745028, 0.290583) Norm(0.996025, -0.009297, -0.088583) UV(0.282669, 0.50924)
V 143: Pos(1.919832, 0.744326, 0.29385) Norm(0.819725, -0.147289, 0.553496) UV(0.292075, 0.501669)
V 144: Pos(1.917261, 0.742313, 0.294996) Norm(0.314498, -0.542755, 0.778787) UV(0.303804, 0.504271)
V 145: Pos(1.911304, 0.744532, 0.28972) Norm(-0.480863, -0.857888, -0.181104) UV(0.301579, 0.558917)
V 146: Pos(1.913229, 0.746038, 0.287271) Norm(0.13947, -0.554268, -0.82057) UV(0.301579, 0.567317)
V 147: Pos(1.916569, 0.741772, 0.287271) Norm(0.178483, -0.649214, -0.739368) UV(0.282669, 0.567317)
V 148: Pos(1.914644, 0.740265, 0.28972) Norm(-0.199734, -0.94527, -0.258013) UV(0.282669, 0.558917)
V 149: Pos(1.920729, 0.745028, 0.290583) Norm(0.996025, -0.009297, -0.088583) UV(0.326099, 0.297425)
V 150: Pos(1.919277, 0.743891, 0.287655) Norm(0.710654, -0.232666, -0.663956) UV(0.33491, 0.296061)
V 151: Pos(1.916492, 0.748592, 0.29385) Norm(0.287488, 0.690641, 0.6636) UV(0.309159, 0.384535)
V 152: Pos(1.919832, 0.744326, 0.29385) Norm(0.819725, -0.147289, 0.553496) UV(0.303311, 0.366552)
V 153: Pos(1.914644, 0.740265, 0.28972) Norm(-0.199734, -0.94527, -0.258013) UV(0.083674, 0.353701)
V 154: Pos(1.914952, 0.740506, 0.293159) Norm(-0.139196, -0.897892, 0.41763) UV(0.084142, 0.365757)
V 155: Pos(1.915937, 0.748158, 0.287655) Norm(0.583885, 0.293186, -0.757047) UV(0.301579, 0.579339)
V 156: Pos(1.919277, 0.743891, 0.287655) Norm(0.710654, -0.232666, -0.663956) UV(0.282669, 0.579339)
V 157: Pos(1.919832, 0.744326, 0.29385) Norm(0.819725, -0.147289, 0.553496) UV(0.314148, 0.297137)
V 158: Pos(1.913563, 0.745878, 0.214141) Norm(-0.375685, 0.479958, 0.792781) UV(0.108868, 0.838116)
V 159: Pos(1.913563, 0.745878, 0.277353) Norm(-0.375683, 0.479959, -0.792781) UV(0.108868, 0.617641)
V 160: Pos(1.934118, 0.752578, 0.277353) Norm(0.02072, 0.609155, -0.792781) UV(0.178008, 0.616517)
V 161: Pos(1.934118, 0.752578, 0.214141) Norm(0.02072, 0.609155, 0.792781) UV(0.178008, 0.836992)
V 162: Pos(1.934118, 0.752578, 0.214141) Norm(0.02072, 0.609155, 0.792781) UV(0.751409, 0.601446)
V 163: Pos(1.934118, 0.752578, 0.277353) Norm(0.02072, 0.609155, -0.792781) UV(0.751409, 0.818173)
V 164: Pos(1.954171, 0.744497, 0.277353) Norm(0.40743, 0.453321, -0.792781) UV(0.693332, 0.809153)
V 165: Pos(1.954171, 0.744497, 0.214141) Norm(0.40743, 0.453321, 0.792781) UV(0.693332, 0.592427)
V 166: Pos(1.964339, 0.725417, 0.277353) Norm(0.603497, 0.085372, -0.792782) UV(0.617875, 0.809233)
V 167: Pos(1.964339, 0.725417, 0.214141) Norm(0.603497, 0.085372, 0.792782) UV(0.617875, 0.592506)
V 168: Pos(1.959863, 0.704265, 0.277353) Norm(0.517182, -0.322522, -0.792781) UV(0.560343, 0.818374)
V 169: Pos(1.959863, 0.704265, 0.214141) Norm(0.517182, -0.322522, 0.792781) UV(0.560343, 0.601647)
V 170: Pos(1.959863, 0.704265, 0.214141) Norm(0.517182, -0.322522, 0.792781) UV(0.751409, 0.81317)
V 171: Pos(1.959863, 0.704265, 0.277353) Norm(0.517182, -0.322522, -0.792781) UV(0.751409, 0.592575)
V 172: Pos(1.942838, 0.690939, 0.277353) Norm(0.188873, -0.579505, -0.79278) UV(0.826372, 0.592427)
V 173: Pos(1.942838, 0.690939, 0.214141) Norm(0.188872, -0.579505, 0.792781) UV(0.826372, 0.813022)
V 174: Pos(1.92123, 0.691674, 0.277353) Norm(-0.227816, -0.56533, -0.792781) UV(0.88935, 0.593138)
V 175: Pos(1.92123, 0.691674, 0.214141) Norm(-0.227815, -0.56533, 0.792781) UV(0.88935, 0.813733)
V 176: Pos(1.92123, 0.691674, 0.214141) Norm(-0.227815, -0.56533, 0.792781) UV(0.314148, 0.813623)
V 177: Pos(1.92123, 0.691674, 0.277353) Norm(-0.227816, -0.56533, -0.792781) UV(0.314148, 0.593528)
V 178: Pos(1.905151, 0.706126, 0.277353) Norm(-0.537905, -0.286631, -0.79278) UV(0.387196, 0.594843)
V 179: Pos(1.905151, 0.706126, 0.214141) Norm(-0.537906, -0.286631, 0.79278) UV(0.387196, 0.814937)
V 180: Pos(1.905151, 0.706126, 0.214141) Norm(-0.537906, -0.286631, 0.79278) UV(0.668112, 0.35897)
V 181: Pos(1.902122, 0.727533, 0.214141) Norm(-0.596304, 0.126185, 0.79278) UV(0.602763, 0.396671)
V 182: Pos(1.897466, 0.728518, 0.214141) Norm(-0.753676, 0.159485, 0.637603) UV(0.590038, 0.386001)
V 183: Pos(1.90095, 0.703888, 0.214141) Norm(-0.679865, -0.362278, 0.637603) UV(0.665228, 0.342623)
V 184: Pos(1.902122, 0.727533, 0.277353) Norm(-0.5963, 0.126186, -0.792782) UV(0.455316, 0.592588)
V 185: Pos(1.902122, 0.727533, 0.214141) Norm(-0.596304, 0.126185, 0.79278) UV(0.455316, 0.812682)
V 186: Pos(1.902122, 0.727533, 0.214141) Norm(-0.596304, 0.126185, 0.79278) UV(0.036474, 0.837324)
V 187: Pos(1.902122, 0.727533, 0.277353) Norm(-0.5963, 0.126186, -0.792782) UV(0.036474, 0.616849)
V 188: Pos(1.902122, 0.727533, 0.297259) Norm(-0.348018, 0.073645, 0.934591) UV(0.794731, 0.230825)
V 189: Pos(1.913563, 0.745878, 0.297259) Norm(-0.219259, 0.280117, 0.934591) UV(0.820539, 0.159971)
V 190: Pos(1.910629, 0.749626, 0.293966) Norm(-0.557345, 0.712036, 0.42705) UV(0.83715, 0.160418)
V 191: Pos(1.897466, 0.728518, 0.293966) Norm(-0.884639, 0.187199, 0.427049) UV(0.807456, 0.241942)
V 192: Pos(1.934118, 0.752578, 0.297259) Norm(0.012094, 0.35552, 0.934591) UV(0.794731, 0.089116)
V 193: Pos(1.905151, 0.706126, 0.297259) Norm(-0.313937, -0.167288, 0.93459) UV(0.729381, 0.268526)
V 194: Pos(1.92123, 0.691674, 0.297259) Norm(-0.132961, -0.329945, 0.934589) UV(0.655069, 0.255433)
V 195: Pos(1.942838, 0.690939, 0.297259) Norm(0.110232, -0.338218, 0.934589) UV(0.606564, 0.197672)
V 196: Pos(1.959863, 0.704265, 0.297259) Norm(0.301846, -0.188236, 0.934589) UV(0.606565, 0.12227)
V 197: Pos(1.964339, 0.725417, 0.297259) Norm(0.352223, 0.049827, 0.934589) UV(0.655069, 0.064509)
V 198: Pos(1.954171, 0.744497, 0.297259) Norm(0.23779, 0.264574, 0.934589) UV(0.729381, 0.051416)
V 199: Pos(1.964339, 0.725417, 0.277353) Norm(0.603497, 0.085372, -0.792782) UV(0.218556, 0.396011)
V 200: Pos(1.954171, 0.744497, 0.277353) Norm(0.40743, 0.453321, -0.792781) UV(0.144243, 0.382909)
V 201: Pos(1.957353, 0.748037, 0.277353) Norm(0.514956, 0.572959, -0.637603) UV(0.141358, 0.366552)
V 202: Pos(1.969051, 0.726083, 0.277353) Norm(0.76277, 0.107906, -0.637604) UV(0.226861, 0.381627)
V 203: Pos(1.959863, 0.704265, 0.200236) Norm(1e-06, -0.0, -1.0) UV(0.544734, 0.42988)
V 204: Pos(1.964339, 0.725417, 0.200236) Norm(1e-06, -0.0, -1.0) UV(0.496229, 0.372082)
V 205: Pos(1.969051, 0.726083, 0.200236) Norm(0.762771, 0.107903, -0.637603) UV(0.504535, 0.357698)
V 206: Pos(1.963901, 0.701747, 0.200236) Norm(0.653676, -0.40764, -0.637603) UV(0.560343, 0.4242)
V 207: Pos(1.90095, 0.703888, 0.293966) Norm(-0.798003, -0.425231, 0.427047) UV(0.732266, 0.28532)
V 208: Pos(1.954171, 0.744497, 0.214141) Norm(0.40743, 0.453321, 0.792781) UV(0.668112, 0.57608)
V 209: Pos(1.964339, 0.725417, 0.214141) Norm(0.603497, 0.085372, 0.792782) UV(0.742425, 0.562987)
V 210: Pos(1.969051, 0.726083, 0.214141) Norm(0.76277, 0.107904, 0.637604) UV(0.750731, 0.577362)
V 211: Pos(1.957353, 0.748037, 0.214141) Norm(0.514956, 0.572959, 0.637604) UV(0.665228, 0.592427)
V 212: Pos(1.954171, 0.744497, 0.200236) Norm(0.0, 0.0, -1.0) UV(0.421917, 0.35898)
V 213: Pos(1.957353, 0.748037, 0.200236) Norm(0.514957, 0.572959, -0.637603) UV(0.419032, 0.342623)
V 214: Pos(1.905151, 0.706126, 0.277353) Norm(-0.537905, -0.286631, -0.79278) UV(0.144243, 0.60016)
V 215: Pos(1.92123, 0.691674, 0.277353) Norm(-0.227816, -0.56533, -0.792781) UV(0.218556, 0.587058)
V 216: Pos(1.919451, 0.68726, 0.277353) Norm(-0.287939, -0.71453, -0.637603) UV(0.226861, 0.601442)
V 217: Pos(1.90095, 0.703888, 0.277353) Norm(-0.679865, -0.362279, -0.637603) UV(0.141358, 0.616517)
V 218: Pos(1.919451, 0.68726, 0.293966) Norm(-0.337972, -0.838693, 0.427047) UV(0.646763, 0.270255)
V 219: Pos(1.934118, 0.752578, 0.200236) Norm(1e-06, 1e-06, -1.0) UV(0.356567, 0.396705)
V 220: Pos(1.93428, 0.757334, 0.200236) Norm(0.02619, 0.76992, -0.637603) UV(0.343842, 0.386029)
V 221: Pos(1.913563, 0.745878, 0.200236) Norm(2e-06, 1e-06, -1.0) UV(0.330759, 0.467606)
V 222: Pos(1.942838, 0.690939, 0.200236) Norm(0.0, -0.0, -1.0) UV(0.544734, 0.505331)
V 223: Pos(1.92123, 0.691674, 0.200236) Norm(0.0, 0.0, -1.0) UV(0.496229, 0.563129)
V 224: Pos(1.905151, 0.706126, 0.200236) Norm(-1e-06, 0.0, -1.0) UV(0.421917, 0.576231)
V 225: Pos(1.902122, 0.727533, 0.200236) Norm(-0.0, 0.0, -1.0) UV(0.356567, 0.538506)
V 226: Pos(1.92123, 0.691674, 0.214141) Norm(-0.227815, -0.56533, 0.792781) UV(0.742425, 0.372063)
V 227: Pos(1.919451, 0.68726, 0.214141) Norm(-0.287939, -0.714529, 0.637605) UV(0.750731, 0.357688)
V 228: Pos(1.944313, 0.686414, 0.293966) Norm(0.2802, -0.85972, 0.427047) UV(0.590955, 0.203796)
V 229: Pos(1.934118, 0.752578, 0.277353) Norm(0.02072, 0.609155, -0.792781) UV(0.078894, 0.420634)
V 230: Pos(1.93428, 0.757334, 0.277353) Norm(0.026189, 0.769919, -0.637603) UV(0.066169, 0.409958)
V 231: Pos(1.910629, 0.749626, 0.200236) Norm(-0.474832, 0.606627, -0.637604) UV(0.314148, 0.467606)
V 232: Pos(1.963901, 0.701747, 0.293966) Norm(0.767264, -0.478475, 0.427046) UV(0.590955, 0.117041)
V 233: Pos(1.934118, 0.752578, 0.214141) Norm(0.02072, 0.609155, 0.792781) UV(0.602763, 0.538379)
V 234: Pos(1.93428, 0.757334, 0.214141) Norm(0.026188, 0.76992, 0.637603) UV(0.590038, 0.549049)
V 235: Pos(1.942838, 0.690939, 0.277353) Norm(0.188873, -0.579505, -0.79278) UV(0.26706, 0.52926)
V 236: Pos(1.944313, 0.686414, 0.277353) Norm(0.238719, -0.732445, -0.637603) UV(0.282669, 0.53494)
V 237: Pos(1.969051, 0.726083, 0.293966) Norm(0.895316, 0.126655, 0.427045) UV(0.646763, 0.050582)
V 238: Pos(1.957353, 0.748037, 0.277353) Norm(0.514956, 0.572959, -0.637603) UV(0.522723, 0.745674)
V 239: Pos(1.93428, 0.757334, 0.277353) Norm(0.026189, 0.769919, -0.637603) UV(0.512275, 0.812485)
V 240: Pos(1.93428, 0.757334, 0.293966) Norm(0.030741, 0.903706, 0.427049) UV(0.455316, 0.812425)
V 241: Pos(1.957353, 0.748037, 0.293966) Norm(0.604439, 0.672522, 0.427046) UV(0.465765, 0.745615)
V 242: Pos(1.90095, 0.703888, 0.214141) Norm(-0.679865, -0.362278, 0.637603) UV(0.937762, 0.676785)
V 243: Pos(1.897466, 0.728518, 0.214141) Norm(-0.753676, 0.159485, 0.637603) UV(0.939834, 0.75518)
V 244: Pos(1.897466, 0.728518, 0.200236) Norm(-0.753675, 0.159486, -0.637603) UV(0.891422, 0.754857)
V 245: Pos(1.90095, 0.703888, 0.200236) Norm(-0.679866, -0.362278, -0.637602) UV(0.88935, 0.676463)
V 246: Pos(1.910629, 0.749626, 0.277353) Norm(-0.474833, 0.606626, -0.637604) UV(0.806539, 0.425916)
V 247: Pos(1.897466, 0.728518, 0.277353) Norm(-0.753674, 0.159486, -0.637604) UV(0.807646, 0.342623)
V 248: Pos(1.897466, 0.728518, 0.293966) Norm(-0.884639, 0.187199, 0.427049) UV(0.865589, 0.342759)
V 249: Pos(1.910629, 0.749626, 0.293966) Norm(-0.557345, 0.712036, 0.42705) UV(0.864482, 0.426052)
V 250: Pos(1.897466, 0.728518, 0.277353) Norm(-0.753674, 0.159486, -0.637604) UV(0.238201, 0.778947)
V 251: Pos(1.90095, 0.703888, 0.277353) Norm(-0.679865, -0.362279, -0.637603) UV(0.236128, 0.700553)
V 252: Pos(1.90095, 0.703888, 0.293966) Norm(-0.798003, -0.425231, 0.427047) UV(0.293971, 0.700938)
V 253: Pos(1.897466, 0.728518, 0.293966) Norm(-0.884639, 0.187199, 0.427049) UV(0.296043, 0.779333)
V 254: Pos(1.919451, 0.68726, 0.277353) Norm(-0.287939, -0.71453, -0.637603) UV(0.238201, 0.616517)
V 255: Pos(1.919451, 0.68726, 0.293966) Norm(-0.337972, -0.838693, 0.427047) UV(0.296043, 0.616902)
V 256: Pos(1.919451, 0.68726, 0.277353) Norm(-0.287939, -0.71453, -0.637603) UV(0.83715, 0.194467)
V 257: Pos(1.944313, 0.686414, 0.277353) Norm(0.238719, -0.732445, -0.637603) UV(0.837672, 0.122003)
V 258: Pos(1.944313, 0.686414, 0.293966) Norm(0.2802, -0.85972, 0.427047) UV(0.895647, 0.121766)
V 259: Pos(1.919451, 0.68726, 0.293966) Norm(-0.337972, -0.838693, 0.427047) UV(0.895125, 0.194231)
V 260: Pos(1.963901, 0.701747, 0.277353) Norm(0.653675, -0.407639, -0.637605) UV(0.83715, 0.035753)
V 261: Pos(1.963901, 0.701747, 0.293966) Norm(0.767264, -0.478475, 0.427046) UV(0.895125, 0.035517)
V 262: Pos(1.963901, 0.701747, 0.277353) Norm(0.653675, -0.407639, -0.637605) UV(0.512275, 0.592648)
V 263: Pos(1.969051, 0.726083, 0.277353) Norm(0.76277, 0.107906, -0.637604) UV(0.522723, 0.658854)
V 264: Pos(1.969051, 0.726083, 0.293966) Norm(0.895316, 0.126655, 0.427045) UV(0.465765, 0.658794)
V 265: Pos(1.963901, 0.701747, 0.293966) Norm(0.767264, -0.478475, 0.427046) UV(0.455316, 0.592588)
V 266: Pos(1.93428, 0.757334, 0.277353) Norm(0.026189, 0.769919, -0.637603) UV(0.807646, 0.505471)
V 267: Pos(1.93428, 0.757334, 0.293966) Norm(0.030741, 0.903706, 0.427049) UV(0.865589, 0.505607)
V 268: Pos(1.919451, 0.68726, 0.214141) Norm(-0.287939, -0.714529, 0.637605) UV(0.939834, 0.592749)
V 269: Pos(1.919451, 0.68726, 0.200236) Norm(-0.287938, -0.71453, -0.637604) UV(0.891422, 0.592427)
V 270: Pos(1.944313, 0.686414, 0.214141) Norm(0.238718, -0.732445, 0.637603) UV(0.285173, 0.865582)
V 271: Pos(1.919451, 0.68726, 0.214141) Norm(-0.287939, -0.714529, 0.637605) UV(0.284651, 0.938046)
V 272: Pos(1.919451, 0.68726, 0.200236) Norm(-0.287938, -0.71453, -0.637604) UV(0.236128, 0.938244)
V 273: Pos(1.944313, 0.686414, 0.200236) Norm(0.238718, -0.732445, -0.637603) UV(0.23665, 0.86578)
V 274: Pos(1.963901, 0.701747, 0.214141) Norm(0.653674, -0.407641, 0.637604) UV(0.284651, 0.779332)
V 275: Pos(1.963901, 0.701747, 0.200236) Norm(0.653676, -0.40764, -0.637603) UV(0.236128, 0.779531)
V 276: Pos(1.969051, 0.726083, 0.214141) Norm(0.76277, 0.107904, 0.637604) UV(0.188456, 0.682723)
V 277: Pos(1.963901, 0.701747, 0.214141) Norm(0.653674, -0.407641, 0.637604) UV(0.178008, 0.616517)
V 278: Pos(1.963901, 0.701747, 0.200236) Norm(0.653676, -0.40764, -0.637603) UV(0.22568, 0.616567)
V 279: Pos(1.969051, 0.726083, 0.200236) Norm(0.762771, 0.107903, -0.637603) UV(0.236128, 0.682773)
V 280: Pos(1.910629, 0.749626, 0.214141) Norm(-0.474834, 0.606625, 0.637604) UV(0.937846, 0.838586)
V 281: Pos(1.93428, 0.757334, 0.214141) Norm(0.026188, 0.76992, 0.637603) UV(0.938953, 0.918141)
V 282: Pos(1.93428, 0.757334, 0.200236) Norm(0.02619, 0.76992, -0.637603) UV(0.890457, 0.918027)
V 283: Pos(1.910629, 0.749626, 0.200236) Norm(-0.474832, 0.606627, -0.637604) UV(0.88935, 0.838472)
V 284: Pos(1.957353, 0.748037, 0.214141) Norm(0.514956, 0.572959, 0.637604) UV(0.188456, 0.769543)
V 285: Pos(1.957353, 0.748037, 0.200236) Norm(0.514957, 0.572959, -0.637603) UV(0.236128, 0.769594)
V 286: Pos(1.93428, 0.757334, 0.214141) Norm(0.026188, 0.76992, 0.637603) UV(0.178008, 0.836354)
V 287: Pos(1.93428, 0.757334, 0.200236) Norm(0.02619, 0.76992, -0.637603) UV(0.22568, 0.836404)
V 288: Pos(1.897466, 0.728518, 0.214141) Norm(-0.753676, 0.159485, 0.637603) UV(0.938953, 0.755293)
V 289: Pos(1.897466, 0.728518, 0.200236) Norm(-0.753675, 0.159486, -0.637603) UV(0.890457, 0.75518)
V 290: Pos(1.944313, 0.686414, 0.200236) Norm(0.238718, -0.732445, -0.637603) UV(0.560343, 0.511012)
V 291: Pos(1.902122, 0.727533, 0.277353) Norm(-0.5963, 0.126186, -0.792782) UV(0.078894, 0.562435)
V 292: Pos(1.897466, 0.728518, 0.277353) Norm(-0.753674, 0.159486, -0.637604) UV(0.066169, 0.573111)
V 293: Pos(1.919451, 0.68726, 0.200236) Norm(-0.287938, -0.71453, -0.637604) UV(0.504535, 0.577513)
V 294: Pos(1.959863, 0.704265, 0.214141) Norm(0.517182, -0.322522, 0.792781) UV(0.790929, 0.505226)
V 295: Pos(1.963901, 0.701747, 0.214141) Norm(0.653674, -0.407641, 0.637604) UV(0.806539, 0.510903)
V 296: Pos(1.90095, 0.703888, 0.200236) Norm(-0.679866, -0.362278, -0.637602) UV(0.419032, 0.592588)
V 297: Pos(1.959863, 0.704265, 0.277353) Norm(0.517182, -0.322522, -0.792781) UV(0.26706, 0.453809)
V 298: Pos(1.963901, 0.701747, 0.277353) Norm(0.653675, -0.407639, -0.637605) UV(0.282669, 0.448129)
V 299: Pos(1.913563, 0.745878, 0.214141) Norm(-0.375685, 0.479958, 0.792781) UV(0.576954, 0.467525)
V 300: Pos(1.910629, 0.749626, 0.214141) Norm(-0.474834, 0.606625, 0.637604) UV(0.560343, 0.467525)
V 301: Pos(1.897466, 0.728518, 0.200236) Norm(-0.753675, 0.159486, -0.637603) UV(0.343842, 0.549182)
V 302: Pos(1.913563, 0.745878, 0.277353) Norm(-0.375683, 0.479959, -0.792781) UV(0.053085, 0.491535)
V 303: Pos(1.910629, 0.749626, 0.277353) Norm(-0.474833, 0.606626, -0.637604) UV(0.036474, 0.491535)
V 304: Pos(1.942838, 0.690939, 0.214141) Norm(0.188872, -0.579505, 0.792781) UV(0.790929, 0.429824)
V 305: Pos(1.944313, 0.686414, 0.214141) Norm(0.238718, -0.732445, 0.637603) UV(0.806539, 0.424147)
V 306: Pos(1.93428, 0.757334, 0.293966) Norm(0.030741, 0.903706, 0.427049) UV(0.807456, 0.078895)
V 307: Pos(1.957353, 0.748037, 0.293966) Norm(0.604439, 0.672522, 0.427046) UV(0.732266, 0.035517)
===Triangles (Indices): 276
Tri: 0 1 2
Tri: 0 2 3
Tri: 3 2 4
Tri: 3 4 5
Tri: 6 7 8
Tri: 6 8 9
Tri: 9 8 10
Tri: 9 10 11
Tri: 12 13 14
Tri: 12 14 15
Tri: 9 11 16
Tri: 9 16 17
Tri: 15 14 18
Tri: 15 18 19
Tri: 20 21 22
Tri: 20 22 23
Tri: 12 15 24
Tri: 12 24 25
Tri: 26 27 28
Tri: 26 28 29
Tri: 30 31 32
Tri: 30 32 33
Tri: 34 3 5
Tri: 34 5 35
Tri: 9 17 36
Tri: 9 36 6
Tri: 37 38 27
Tri: 37 27 26
Tri: 20 23 39
Tri: 20 39 40
Tri: 0 3 34
Tri: 0 34 41
Tri: 42 43 44
Tri: 42 44 45
Tri: 8 46 47
Tri: 8 47 10
Tri: 13 48 49
Tri: 13 49 14
Tri: 50 51 52
Tri: 50 52 53
Tri: 21 54 31
Tri: 21 31 22
Tri: 1 55 56
Tri: 1 56 2
Tri: 57 58 43
Tri: 57 43 42
Tri: 32 59 60
Tri: 32 60 33
Tri: 61 62 63
Tri: 61 63 64
Tri: 43 58 61
Tri: 43 61 64
Tri: 65 26 29
Tri: 65 29 66
Tri: 44 43 64
Tri: 44 64 67
Tri: 17 16 68
Tri: 17 68 69
Tri: 47 46 70
Tri: 47 70 71
Tri: 72 73 74
Tri: 72 74 75
Tri: 51 76 77
Tri: 51 77 78
Tri: 79 72 75
Tri: 79 75 80
Tri: 52 51 78
Tri: 52 78 81
Tri: 39 82 83
Tri: 39 83 84
Tri: 31 54 85
Tri: 31 85 32
Tri: 40 39 84
Tri: 40 84 86
Tri: 87 88 89
Tri: 87 89 90
Tri: 91 92 93
Tri: 91 93 94
Tri: 95 87 96
Tri: 95 96 97
Tri: 78 98 99
Tri: 78 99 81
Tri: 71 100 101
Tri: 71 101 102
Tri: 70 103 100
Tri: 70 100 71
Tri: 104 105 106
Tri: 104 106 107
Tri: 80 75 108
Tri: 80 108 109
Tri: 84 83 110
Tri: 84 110 111
Tri: 112 113 92
Tri: 112 92 91
Tri: 29 28 114
Tri: 29 114 115
Tri: 68 116 117
Tri: 68 117 118
Tri: 69 68 118
Tri: 69 118 119
Tri: 75 74 120
Tri: 75 120 108
Tri: 121 122 123
Tri: 121 123 124
Tri: 121 124 125
Tri: 121 125 126
Tri: 121 126 127
Tri: 100 103 128
Tri: 100 128 129
Tri: 105 130 131
Tri: 105 131 132
Tri: 96 87 90
Tri: 96 90 133
Tri: 101 100 129
Tri: 101 129 134
Tri: 106 105 132
Tri: 106 132 135
Tri: 60 59 136
Tri: 60 136 137
Tri: 138 139 140
Tri: 138 140 141
Tri: 138 141 142
Tri: 138 142 143
Tri: 138 143 144
Tri: 145 146 147
Tri: 145 147 148
Tri: 119 118 149
Tri: 119 149 150
Tri: 151 91 94
Tri: 151 94 152
Tri: 111 110 153
Tri: 111 153 154
Tri: 146 155 156
Tri: 146 156 147
Tri: 118 117 157
Tri: 118 157 149
Tri: 158 159 160
Tri: 158 160 161
Tri: 162 163 164
Tri: 162 164 165
Tri: 165 164 166
Tri: 165 166 167
Tri: 167 166 168
Tri: 167 168 169
Tri: 170 171 172
Tri: 170 172 173
Tri: 173 172 174
Tri: 173 174 175
Tri: 176 177 178
Tri: 176 178 179
Tri: 180 181 182
Tri: 180 182 183
Tri: 179 178 184
Tri: 179 184 185
Tri: 186 187 159
Tri: 186 159 158
Tri: 188 189 190
Tri: 188 190 191
Tri: 192 189 188
Tri: 192 188 193
Tri: 192 193 194
Tri: 192 194 195
Tri: 192 195 196
Tri: 192 196 197
Tri: 192 197 198
Tri: 199 200 201
Tri: 199 201 202
Tri: 203 204 205
Tri: 203 205 206
Tri: 193 188 191
Tri: 193 191 207
Tri: 208 209 210
Tri: 208 210 211
Tri: 204 212 213
Tri: 204 213 205
Tri: 214 215 216
Tri: 214 216 217
Tri: 194 193 207
Tri: 194 207 218
Tri: 212 219 220
Tri: 212 220 213
Tri: 221 219 212
Tri: 221 212 204
Tri: 221 204 203
Tri: 221 203 222
Tri: 221 222 223
Tri: 221 223 224
Tri: 221 224 225
Tri: 226 180 183
Tri: 226 183 227
Tri: 195 194 218
Tri: 195 218 228
Tri: 200 229 230
Tri: 200 230 201
Tri: 219 221 231
Tri: 219 231 220
Tri: 196 195 228
Tri: 196 228 232
Tri: 233 208 211
Tri: 233 211 234
Tri: 215 235 236
Tri: 215 236 216
Tri: 197 196 232
Tri: 197 232 237
Tri: 238 239 240
Tri: 238 240 241
Tri: 242 243 244
Tri: 242 244 245
Tri: 246 247 248
Tri: 246 248 249
Tri: 250 251 252
Tri: 250 252 253
Tri: 251 254 255
Tri: 251 255 252
Tri: 256 257 258
Tri: 256 258 259
Tri: 257 260 261
Tri: 257 261 258
Tri: 262 263 264
Tri: 262 264 265
Tri: 266 246 249
Tri: 266 249 267
Tri: 263 238 241
Tri: 263 241 264
Tri: 268 242 245
Tri: 268 245 269
Tri: 270 271 272
Tri: 270 272 273
Tri: 274 270 273
Tri: 274 273 275
Tri: 276 277 278
Tri: 276 278 279
Tri: 280 281 282
Tri: 280 282 283
Tri: 284 276 279
Tri: 284 279 285
Tri: 286 284 285
Tri: 286 285 287
Tri: 288 280 283
Tri: 288 283 289
Tri: 222 203 206
Tri: 222 206 290
Tri: 291 214 217
Tri: 291 217 292
Tri: 223 222 290
Tri: 223 290 293
Tri: 209 294 295
Tri: 209 295 210
Tri: 224 223 293
Tri: 224 293 296
Tri: 297 199 202
Tri: 297 202 298
Tri: 181 299 300
Tri: 181 300 182
Tri: 225 224 296
Tri: 225 296 301
Tri: 302 291 292
Tri: 302 292 303
Tri: 221 225 301
Tri: 221 301 231
Tri: 294 304 305
Tri: 294 305 295
Tri: 299 233 234
Tri: 299 234 300
Tri: 189 192 306
Tri: 189 306 190
Tri: 235 297 298
Tri: 235 298 236
Tri: 229 302 303
Tri: 229 303 230
Tri: 192 198 307
Tri: 192 307 306
Tri: 198 197 237
Tri: 198 237 307
Tri: 304 226 227
Tri: 304 227 305
+21
View File
@@ -0,0 +1,21 @@
===Vertices (Split by UV/Normal): 10
V 0: Pos(2.170956, 0.587034, 0.365303) Norm(-0.995813, -0.029843, 0.0864) UV(0.015946, 0.257699)
V 1: Pos(2.170184, 0.584088, 0.389394) Norm(-0.99931, -0.015281, -0.033867) UV(0.016101, 0.023489)
V 2: Pos(2.168909, 0.649793, 0.397387) Norm(-0.99931, -0.015281, -0.033867) UV(0.65722, 0.023489)
V 3: Pos(2.16968, 0.652739, 0.373296) Norm(-0.995813, -0.029843, 0.0864) UV(0.657065, 0.257699)
V 4: Pos(2.169977, 0.592927, 0.316697) Norm(-0.99906, -0.037007, 0.022573) UV(0.015812, 0.731623)
V 5: Pos(2.165724, 0.589887, 0.341009) Norm(-0.999626, -0.02147, 0.016955) UV(0.016215, 0.497147)
V 6: Pos(2.164448, 0.655593, 0.349002) Norm(-0.999626, -0.02147, 0.016955) UV(0.657334, 0.497147)
V 7: Pos(2.168701, 0.658633, 0.32469) Norm(-0.99906, -0.037007, 0.022573) UV(0.656931, 0.731623)
V 8: Pos(2.166257, 0.595846, 0.292114) Norm(-0.973478, -0.074359, 0.216359) UV(0.015973, 0.973057)
V 9: Pos(2.161023, 0.66149, 0.299973) Norm(-0.973478, -0.074359, 0.216359) UV(0.657373, 0.976511)
===Triangles (Indices): 8
Tri: 0 1 2
Tri: 0 2 3
Tri: 4 5 6
Tri: 4 6 7
Tri: 5 0 3
Tri: 5 3 6
Tri: 8 4 7
Tri: 8 7 9
+21
View File
@@ -0,0 +1,21 @@
===Vertices (Split by UV/Normal): 10
V 0: Pos(2.169542, 0.215532, 0.473265) Norm(-0.991319, 0.000923, 0.131472) UV(0.015946, 0.257699)
V 1: Pos(2.169844, 0.213758, 0.502617) Norm(-0.999907, 0.008286, 0.010808) UV(0.016101, 0.023489)
V 2: Pos(2.170559, 0.293782, 0.507448) Norm(-0.999907, 0.008286, 0.010808) UV(0.65722, 0.023489)
V 3: Pos(2.170257, 0.295557, 0.478097) Norm(-0.99132, 0.000923, 0.131472) UV(0.657065, 0.257699)
V 4: Pos(2.165864, 0.219133, 0.414181) Norm(-0.997607, -0.009991, 0.068407) UV(0.015812, 0.731623)
V 5: Pos(2.161964, 0.217368, 0.443992) Norm(-0.99807, 0.005185, 0.061876) UV(0.016215, 0.497147)
V 6: Pos(2.16268, 0.297392, 0.448823) Norm(-0.99807, 0.005185, 0.061877) UV(0.657334, 0.497147)
V 7: Pos(2.166579, 0.299157, 0.419013) Norm(-0.997607, -0.009991, 0.068407) UV(0.656931, 0.731623)
V 8: Pos(2.160102, 0.220978, 0.384476) Norm(-0.964166, -0.036223, 0.262816) UV(0.015973, 0.973057)
V 9: Pos(2.15602, 0.301042, 0.389359) Norm(-0.964166, -0.036223, 0.262816) UV(0.657373, 0.976511)
===Triangles (Indices): 8
Tri: 0 1 2
Tri: 0 2 3
Tri: 4 5 6
Tri: 4 6 7
Tri: 5 0 3
Tri: 5 3 6
Tri: 8 4 7
Tri: 8 7 9
+21
View File
@@ -0,0 +1,21 @@
===Vertices (Split by UV/Normal): 10
V 0: Pos(2.16925, 0.237091, 0.341346) Norm(-0.995813, -0.002278, 0.09138) UV(0.015946, 0.257699)
V 1: Pos(2.168413, 0.241963, 0.367235) Norm(-0.99931, -0.024824, -0.027645) UV(0.016101, 0.023489)
V 2: Pos(2.167028, 0.312561, 0.353905) Norm(-0.99931, -0.024824, -0.027645) UV(0.65722, 0.023489)
V 3: Pos(2.167865, 0.307689, 0.328015) Norm(-0.995813, -0.002278, 0.09138) UV(0.657065, 0.257699)
V 4: Pos(2.168187, 0.22721, 0.289126) Norm(-0.99906, -0.028434, 0.03272) UV(0.015812, 0.731623)
V 5: Pos(2.163571, 0.232057, 0.315276) Norm(-0.999626, -0.015326, 0.02266) UV(0.016215, 0.497147)
V 6: Pos(2.162186, 0.302655, 0.301945) Norm(-0.999626, -0.015326, 0.022659) UV(0.657334, 0.497147)
V 7: Pos(2.166803, 0.297808, 0.275796) Norm(-0.99906, -0.028434, 0.032721) UV(0.656931, 0.731623)
V 8: Pos(2.16415, 0.222148, 0.262737) Norm(-0.973479, -0.005347, 0.228716) UV(0.015973, 0.973057)
V 9: Pos(2.158468, 0.292639, 0.249288) Norm(-0.973479, -0.005347, 0.228716) UV(0.657373, 0.976511)
===Triangles (Indices): 8
Tri: 0 1 2
Tri: 0 2 3
Tri: 4 5 6
Tri: 4 6 7
Tri: 5 0 3
Tri: 5 3 6
Tri: 8 4 7
Tri: 8 7 9
+21
View File
@@ -0,0 +1,21 @@
===Vertices (Split by UV/Normal): 10
V 0: Pos(2.015285, 0.219518, 0.200815) Norm(0.082364, -0.039648, 0.995813) UV(0.015946, 0.257699)
V 1: Pos(2.040888, 0.213314, 0.201652) Norm(-0.035405, -0.011257, 0.99931) UV(0.016101, 0.023489)
V 2: Pos(2.057765, 0.283149, 0.203037) Norm(-0.035405, -0.011257, 0.99931) UV(0.65722, 0.023489)
V 3: Pos(2.032162, 0.289353, 0.2022) Norm(0.082364, -0.039648, 0.995813) UV(0.657065, 0.257699)
V 4: Pos(1.963621, 0.231982, 0.201878) Norm(0.018136, -0.039371, 0.99906) UV(0.015812, 0.731623)
V 5: Pos(1.989451, 0.225649, 0.206494) Norm(0.014356, -0.023288, 0.999626) UV(0.016215, 0.497147)
V 6: Pos(2.006328, 0.295484, 0.207879) Norm(0.014355, -0.023288, 0.999626) UV(0.657334, 0.497147)
V 7: Pos(1.980498, 0.301817, 0.203262) Norm(0.018136, -0.039371, 0.99906) UV(0.656931, 0.731623)
V 8: Pos(1.937485, 0.238219, 0.205915) Norm(0.20629, -0.098914, 0.973479) UV(0.015973, 0.973057)
V 9: Pos(1.954209, 0.308005, 0.211597) Norm(0.20629, -0.098914, 0.973479) UV(0.657373, 0.976511)
===Triangles (Indices): 8
Tri: 0 1 2
Tri: 0 2 3
Tri: 4 5 6
Tri: 4 6 7
Tri: 5 0 3
Tri: 5 3 6
Tri: 8 4 7
Tri: 8 7 9
+103
View File
@@ -0,0 +1,103 @@
===Vertices (Split by UV/Normal): 60
V 0: Pos(2.04882, 0.593961, 0.247254) Norm(0.15799, -0.805469, -0.571191) UV(0.953767, 0.893055)
V 1: Pos(2.048557, 0.595304, 0.329654) Norm(0.15631, -0.796892, 0.58355) UV(0.960925, 0.457499)
V 2: Pos(2.047151, 0.596248, 0.329654) Norm(-0.796943, -0.156316, 0.583478) UV(0.968094, 0.457499)
V 3: Pos(2.045809, 0.595985, 0.247254) Norm(-0.805479, -0.157991, -0.571177) UV(0.969128, 0.893055)
V 4: Pos(2.045809, 0.595985, 0.247254) Norm(-0.805479, -0.157991, -0.571177) UV(0.953767, 0.457499)
V 5: Pos(2.047151, 0.596248, 0.329654) Norm(-0.796943, -0.156316, 0.583478) UV(0.960925, 0.021942)
V 6: Pos(2.048096, 0.597654, 0.329654) Norm(-0.156311, 0.796894, 0.583547) UV(0.968094, 0.021942)
V 7: Pos(2.047833, 0.598996, 0.247254) Norm(-0.157991, 0.805467, -0.571193) UV(0.969128, 0.457499)
V 8: Pos(2.047833, 0.598996, 0.247254) Norm(-0.157991, 0.805467, -0.571193) UV(0.228451, 0.978058)
V 9: Pos(2.048096, 0.597654, 0.329654) Norm(-0.156311, 0.796894, 0.583547) UV(0.235609, 0.542501)
V 10: Pos(2.049502, 0.596709, 0.329654) Norm(0.796941, 0.156322, 0.583479) UV(0.242779, 0.542501)
V 11: Pos(2.050844, 0.596972, 0.247254) Norm(0.805478, 0.157997, -0.571176) UV(0.243812, 0.978058)
V 12: Pos(2.048557, 0.595304, 0.329654) Norm(0.15631, -0.796892, 0.58355) UV(0.248138, 0.542501)
V 13: Pos(2.04882, 0.593961, 0.247254) Norm(0.15799, -0.805469, -0.571191) UV(0.255297, 0.978058)
V 14: Pos(2.045809, 0.595985, 0.247254) Norm(-0.805479, -0.157991, -0.571177) UV(0.274477, 0.542501)
V 15: Pos(2.047833, 0.598996, 0.247254) Norm(-0.157991, 0.805467, -0.571193) UV(0.274477, 0.561681)
V 16: Pos(2.050844, 0.596972, 0.247254) Norm(0.805478, 0.157997, -0.571176) UV(0.255297, 0.561681)
V 17: Pos(2.04882, 0.593961, 0.247254) Norm(0.15799, -0.805469, -0.571191) UV(0.255297, 0.542501)
V 18: Pos(2.048096, 0.597654, 0.329654) Norm(-0.156311, 0.796894, 0.583547) UV(0.281463, 0.54487)
V 19: Pos(2.047151, 0.596248, 0.329654) Norm(-0.796943, -0.156316, 0.583478) UV(0.281463, 0.55298)
V 20: Pos(2.048557, 0.595304, 0.329654) Norm(0.15631, -0.796892, 0.58355) UV(0.274477, 0.550611)
V 21: Pos(2.049502, 0.596709, 0.329654) Norm(0.796941, 0.156322, 0.583479) UV(0.274477, 0.542501)
V 22: Pos(2.059372, 0.591441, 0.201308) Norm(0.832044, -0.468435, -0.297105) UV(0.540349, 0.551801)
V 23: Pos(2.059372, 0.591441, 0.244588) Norm(0.832044, -0.468435, 0.297105) UV(0.540349, 0.390033)
V 24: Pos(2.022467, 0.578506, 0.244588) Norm(-0.357515, -0.885388, 0.297105) UV(0.747057, 0.390033)
V 25: Pos(2.022467, 0.578506, 0.201308) Norm(-0.357515, -0.885388, -0.297105) UV(0.747057, 0.551801)
V 26: Pos(2.027751, 0.681654, 0.19842) Norm(0.496495, 0.488863, -0.717291) UV(0.540349, 0.527237)
V 27: Pos(2.026796, 0.684379, 0.201308) Norm(0.357517, 0.885386, -0.297108) UV(0.525084, 0.542501)
V 28: Pos(2.026796, 0.684379, 0.244588) Norm(0.357517, 0.885386, 0.297108) UV(0.296309, 0.542501)
V 29: Pos(2.027751, 0.681654, 0.247476) Norm(0.496495, 0.488863, 0.717291) UV(0.281045, 0.527237)
V 30: Pos(2.058416, 0.594166, 0.247476) Norm(0.693054, -0.071917, 0.717289) UV(0.281045, 0.037206)
V 31: Pos(2.059372, 0.591441, 0.244588) Norm(0.832044, -0.468435, 0.297105) UV(0.29631, 0.021942)
V 32: Pos(2.059372, 0.591441, 0.201308) Norm(0.832044, -0.468435, -0.297105) UV(0.525084, 0.021942)
V 33: Pos(2.058416, 0.594166, 0.19842) Norm(0.693054, -0.071917, -0.71729) UV(0.540349, 0.037206)
V 34: Pos(2.027751, 0.681654, 0.247476) Norm(0.496495, 0.488863, 0.717291) UV(0.540349, 0.021942)
V 35: Pos(1.990847, 0.668718, 0.247476) Norm(-0.693054, 0.07191, 0.717291) UV(0.747057, 0.021942)
V 36: Pos(2.021512, 0.581231, 0.247476) Norm(-0.496494, -0.488866, 0.717289) UV(0.747057, 0.368446)
V 37: Pos(2.058416, 0.594166, 0.247476) Norm(0.693054, -0.071917, 0.717289) UV(0.540349, 0.368446)
V 38: Pos(2.021512, 0.581231, 0.19842) Norm(-0.496494, -0.488866, -0.717289) UV(0.281045, 0.527237)
V 39: Pos(2.022467, 0.578506, 0.201308) Norm(-0.357515, -0.885388, -0.297105) UV(0.265781, 0.542501)
V 40: Pos(2.022467, 0.578506, 0.244588) Norm(-0.357515, -0.885388, 0.297105) UV(0.037006, 0.542501)
V 41: Pos(2.021512, 0.581231, 0.247476) Norm(-0.496494, -0.488866, 0.717289) UV(0.021742, 0.527237)
V 42: Pos(1.990847, 0.668718, 0.247476) Norm(-0.693054, 0.07191, 0.717291) UV(0.021742, 0.037206)
V 43: Pos(1.989891, 0.671443, 0.244588) Norm(-0.832042, 0.468434, 0.297111) UV(0.037006, 0.021942)
V 44: Pos(1.989891, 0.671443, 0.201308) Norm(-0.832042, 0.468434, -0.297111) UV(0.265781, 0.021942)
V 45: Pos(1.990847, 0.668718, 0.19842) Norm(-0.693054, 0.07191, -0.71729) UV(0.281045, 0.037206)
V 46: Pos(1.989891, 0.671443, 0.201308) Norm(-0.832042, 0.468434, -0.297111) UV(0.021742, 0.78654)
V 47: Pos(1.989891, 0.671443, 0.244588) Norm(-0.832042, 0.468434, 0.297111) UV(0.021742, 0.557766)
V 48: Pos(2.026796, 0.684379, 0.244588) Norm(0.357517, 0.885386, 0.297108) UV(0.228451, 0.557766)
V 49: Pos(2.026796, 0.684379, 0.201308) Norm(0.357517, 0.885386, -0.297108) UV(0.228451, 0.78654)
V 50: Pos(2.027751, 0.681654, 0.19842) Norm(0.496495, 0.488863, -0.717291) UV(0.747058, 0.527237)
V 51: Pos(1.990847, 0.668718, 0.19842) Norm(-0.693054, 0.07191, -0.71729) UV(0.953767, 0.527237)
V 52: Pos(1.989891, 0.671443, 0.201308) Norm(-0.832042, 0.468434, -0.297111) UV(0.953767, 0.542501)
V 53: Pos(2.026796, 0.684379, 0.201308) Norm(0.357517, 0.885386, -0.297108) UV(0.747058, 0.542501)
V 54: Pos(1.990847, 0.668718, 0.247476) Norm(-0.693054, 0.07191, 0.717291) UV(0.021742, 0.542501)
V 55: Pos(2.027751, 0.681654, 0.247476) Norm(0.496495, 0.488863, 0.717291) UV(0.228451, 0.542501)
V 56: Pos(2.021512, 0.581231, 0.19842) Norm(-0.496494, -0.488866, -0.717289) UV(0.953767, 0.037206)
V 57: Pos(2.058416, 0.594166, 0.19842) Norm(0.693054, -0.071917, -0.71729) UV(0.747058, 0.037206)
V 58: Pos(2.059372, 0.591441, 0.201308) Norm(0.832044, -0.468435, -0.297105) UV(0.747058, 0.021942)
V 59: Pos(2.022467, 0.578506, 0.201308) Norm(-0.357515, -0.885388, -0.297105) UV(0.953767, 0.021942)
===Triangles (Indices): 40
Tri: 0 1 2
Tri: 0 2 3
Tri: 4 5 6
Tri: 4 6 7
Tri: 8 9 10
Tri: 8 10 11
Tri: 11 10 12
Tri: 11 12 13
Tri: 14 15 16
Tri: 14 16 17
Tri: 18 19 20
Tri: 18 20 21
Tri: 22 23 24
Tri: 22 24 25
Tri: 26 27 28
Tri: 26 28 29
Tri: 26 29 30
Tri: 26 30 31
Tri: 26 31 32
Tri: 26 32 33
Tri: 34 35 36
Tri: 34 36 37
Tri: 38 39 40
Tri: 38 40 41
Tri: 38 41 42
Tri: 38 42 43
Tri: 38 43 44
Tri: 38 44 45
Tri: 46 47 48
Tri: 46 48 49
Tri: 50 51 52
Tri: 50 52 53
Tri: 54 55 48
Tri: 54 48 47
Tri: 56 57 58
Tri: 56 58 59
Tri: 37 36 24
Tri: 37 24 23
Tri: 51 50 57
Tri: 51 57 56
+223
View File
@@ -0,0 +1,223 @@
===Vertices (Split by UV/Normal): 120
V 0: Pos(1.857603, 0.794636, 0.177222) Norm(-0.904534, -0.301511, -0.301511) UV(0.628715, 0.045661)
V 1: Pos(1.857603, 0.794636, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.60656, 0.045661)
V 2: Pos(1.857603, 0.816721, 0.200019) Norm(-0.57735, 0.57735, 0.57735) UV(0.60656, 0.024198)
V 3: Pos(1.857603, 0.816721, 0.177222) Norm(-0.707107, 0.707107, 0.0) UV(0.628715, 0.024198)
V 4: Pos(1.857603, 0.816721, 0.177222) Norm(-0.707107, 0.707107, 0.0) UV(0.046149, 0.975802)
V 5: Pos(1.857603, 0.816721, 0.200019) Norm(-0.57735, 0.57735, 0.57735) UV(0.023994, 0.975802)
V 6: Pos(2.157339, 0.816721, 0.200019) Norm(0.57735, 0.57735, 0.57735) UV(0.023994, 0.684519)
V 7: Pos(2.157339, 0.816721, 0.177222) Norm(0.707107, 0.707107, 0.0) UV(0.046149, 0.684519)
V 8: Pos(2.157339, 0.159323, 0.177222) Norm(0.904534, 0.301511, -0.301511) UV(0.809229, 0.045661)
V 9: Pos(2.157339, 0.159323, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.787075, 0.045661)
V 10: Pos(2.157339, 0.137237, 0.200019) Norm(0.57735, -0.57735, 0.57735) UV(0.787075, 0.024198)
V 11: Pos(2.157339, 0.137237, 0.177222) Norm(0.707107, -0.707107, 0.0) UV(0.809229, 0.024198)
V 12: Pos(2.157339, 0.137237, 0.177222) Norm(0.707107, -0.707107, 0.0) UV(0.337432, 0.932876)
V 13: Pos(2.157339, 0.137237, 0.200019) Norm(0.57735, -0.57735, 0.57735) UV(0.315277, 0.932876)
V 14: Pos(1.857603, 0.137237, 0.200019) Norm(-0.57735, -0.57735, 0.57735) UV(0.315277, 0.641593)
V 15: Pos(1.857603, 0.137237, 0.177222) Norm(-0.707107, -0.707107, 0.0) UV(0.337432, 0.641593)
V 16: Pos(1.857603, 0.159323, 0.014266) Norm(-0.57735, 0.57735, -0.57735) UV(0.538718, 0.641593)
V 17: Pos(2.157339, 0.159323, 0.014266) Norm(0.57735, 0.57735, -0.57735) UV(0.538718, 0.932876)
V 18: Pos(2.157339, 0.137237, 0.014266) Norm(0.57735, -0.57735, -0.57735) UV(0.517255, 0.932876)
V 19: Pos(1.857603, 0.137237, 0.014266) Norm(-0.57735, -0.57735, -0.57735) UV(0.517255, 0.641593)
V 20: Pos(2.157339, 0.159323, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.315277, 0.045661)
V 21: Pos(1.857603, 0.159323, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.023994, 0.045661)
V 22: Pos(1.857603, 0.137237, 0.200019) Norm(-0.57735, -0.57735, 0.57735) UV(0.023994, 0.024198)
V 23: Pos(2.157339, 0.137237, 0.200019) Norm(0.57735, -0.57735, 0.57735) UV(0.315277, 0.024198)
V 24: Pos(2.157339, 0.137237, 0.014266) Norm(0.57735, -0.57735, -0.57735) UV(0.495792, 0.932876)
V 25: Pos(2.157339, 0.137237, 0.107142) Norm(0.707107, -0.707107, 0.0) UV(0.405535, 0.932876)
V 26: Pos(1.857603, 0.137237, 0.107142) Norm(-0.707107, -0.707107, 0.0) UV(0.405535, 0.641593)
V 27: Pos(1.857603, 0.137237, 0.014266) Norm(-0.57735, -0.57735, -0.57735) UV(0.495792, 0.641593)
V 28: Pos(2.157339, 0.159323, 0.014266) Norm(0.57735, 0.57735, -0.57735) UV(0.96759, 0.045661)
V 29: Pos(2.157339, 0.159323, 0.107142) Norm(0.707107, 0.707107, 0.0) UV(0.877332, 0.045661)
V 30: Pos(2.157339, 0.137237, 0.107142) Norm(0.707107, -0.707107, 0.0) UV(0.877332, 0.024198)
V 31: Pos(2.157339, 0.137237, 0.014266) Norm(0.57735, -0.57735, -0.57735) UV(0.96759, 0.024198)
V 32: Pos(1.857603, 0.816721, 0.014266) Norm(-0.57735, 0.57735, -0.57735) UV(0.204509, 0.975802)
V 33: Pos(1.857603, 0.816721, 0.107142) Norm(-0.707107, 0.707107, 0.0) UV(0.114252, 0.975802)
V 34: Pos(2.157339, 0.816721, 0.107142) Norm(0.707107, 0.707107, 0.0) UV(0.114252, 0.684519)
V 35: Pos(2.157339, 0.816721, 0.014266) Norm(0.57735, 0.57735, -0.57735) UV(0.204509, 0.684519)
V 36: Pos(1.857603, 0.794636, 0.014266) Norm(-0.57735, -0.57735, -0.57735) UV(0.787075, 0.045661)
V 37: Pos(1.857603, 0.794636, 0.107142) Norm(-0.707107, -0.707107, 0.0) UV(0.696818, 0.045661)
V 38: Pos(1.857603, 0.816721, 0.107142) Norm(-0.707107, 0.707107, 0.0) UV(0.696818, 0.024198)
V 39: Pos(1.857603, 0.816721, 0.014266) Norm(-0.57735, 0.57735, -0.57735) UV(0.787075, 0.024198)
V 40: Pos(2.157339, 0.64685, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.315277, 0.519439)
V 41: Pos(1.857603, 0.64685, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.023994, 0.519439)
V 42: Pos(1.857603, 0.476979, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.023994, 0.354358)
V 43: Pos(2.157339, 0.476979, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.315277, 0.354358)
V 44: Pos(2.157339, 0.64685, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.809229, 0.519439)
V 45: Pos(2.157339, 0.64685, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.787075, 0.519439)
V 46: Pos(2.157339, 0.476979, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.787075, 0.354358)
V 47: Pos(2.157339, 0.476979, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.809229, 0.354358)
V 48: Pos(1.857603, 0.307108, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.628715, 0.519439)
V 49: Pos(1.857603, 0.307108, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.60656, 0.519439)
V 50: Pos(1.857603, 0.476979, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.60656, 0.354358)
V 51: Pos(1.857603, 0.476979, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.628715, 0.354358)
V 52: Pos(2.157339, 0.731786, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.809229, 0.601979)
V 53: Pos(2.157339, 0.731786, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.787075, 0.601979)
V 54: Pos(2.157339, 0.731786, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.315277, 0.601979)
V 55: Pos(1.857603, 0.731786, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.023994, 0.601979)
V 56: Pos(1.857603, 0.64685, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.60656, 0.189278)
V 57: Pos(1.857603, 0.64685, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.628715, 0.189278)
V 58: Pos(2.157339, 0.794636, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.315277, 0.663056)
V 59: Pos(1.857603, 0.794636, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.023994, 0.663056)
V 60: Pos(2.157339, 0.794636, 0.177222) Norm(0.904534, -0.301511, -0.301511) UV(0.809229, 0.663056)
V 61: Pos(2.157339, 0.794636, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.787075, 0.663056)
V 62: Pos(1.857603, 0.731786, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.60656, 0.106738)
V 63: Pos(1.857603, 0.731786, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.628715, 0.106738)
V 64: Pos(2.157339, 0.816721, 0.177222) Norm(0.707107, 0.707107, 0.0) UV(0.809229, 0.684519)
V 65: Pos(2.157339, 0.816721, 0.200019) Norm(0.57735, 0.57735, 0.57735) UV(0.787075, 0.684519)
V 66: Pos(1.857603, 0.816721, 0.014266) Norm(-0.57735, 0.57735, -0.57735) UV(0.517255, 0.641593)
V 67: Pos(2.157339, 0.816721, 0.014266) Norm(0.57735, 0.57735, -0.57735) UV(0.517255, 0.932876)
V 68: Pos(2.157339, 0.794636, 0.014266) Norm(0.57735, -0.57735, -0.57735) UV(0.495792, 0.932876)
V 69: Pos(1.857603, 0.794636, 0.014266) Norm(-0.57735, -0.57735, -0.57735) UV(0.495792, 0.641593)
V 70: Pos(2.157339, 0.816721, 0.200019) Norm(0.57735, 0.57735, 0.57735) UV(0.315277, 0.684519)
V 71: Pos(1.857603, 0.816721, 0.200019) Norm(-0.57735, 0.57735, 0.57735) UV(0.023994, 0.684519)
V 72: Pos(2.157339, 0.816721, 0.014266) Norm(0.57735, 0.57735, -0.57735) UV(0.96759, 0.684519)
V 73: Pos(2.157339, 0.816721, 0.107142) Norm(0.707107, 0.707107, 0.0) UV(0.877332, 0.684519)
V 74: Pos(2.157339, 0.794636, 0.107142) Norm(0.707107, -0.707107, 0.0) UV(0.877332, 0.663056)
V 75: Pos(2.157339, 0.794636, 0.014266) Norm(0.57735, -0.57735, -0.57735) UV(0.96759, 0.663056)
V 76: Pos(1.857603, 0.222172, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.628715, 0.601979)
V 77: Pos(1.857603, 0.222172, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.60656, 0.601979)
V 78: Pos(1.857603, 0.307108, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.023994, 0.189278)
V 79: Pos(2.157339, 0.307108, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.315277, 0.189278)
V 80: Pos(2.157339, 0.307108, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.787075, 0.189278)
V 81: Pos(2.157339, 0.307108, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.809229, 0.189278)
V 82: Pos(1.857603, 0.159323, 0.177222) Norm(-0.904534, 0.301511, -0.301511) UV(0.628715, 0.663056)
V 83: Pos(1.857603, 0.159323, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.60656, 0.663056)
V 84: Pos(1.857603, 0.222172, 0.200019) Norm(-0.707107, 0.0, 0.707107) UV(0.023994, 0.106738)
V 85: Pos(2.157339, 0.222172, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.315277, 0.106738)
V 86: Pos(2.157339, 0.222172, 0.200019) Norm(0.707107, 0.0, 0.707107) UV(0.787075, 0.106738)
V 87: Pos(2.157339, 0.222172, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.809229, 0.106738)
V 88: Pos(1.857603, 0.137237, 0.177222) Norm(-0.707107, -0.707107, 0.0) UV(0.628715, 0.684519)
V 89: Pos(1.857603, 0.137237, 0.200019) Norm(-0.57735, -0.57735, 0.57735) UV(0.60656, 0.684519)
V 90: Pos(1.857603, 0.137237, 0.014266) Norm(-0.57735, -0.57735, -0.57735) UV(0.787075, 0.684519)
V 91: Pos(1.857603, 0.137237, 0.107142) Norm(-0.707107, -0.707107, 0.0) UV(0.696818, 0.684519)
V 92: Pos(1.857603, 0.159323, 0.107142) Norm(-0.707107, 0.707107, 0.0) UV(0.696818, 0.663056)
V 93: Pos(1.857603, 0.159323, 0.014266) Norm(-0.57735, 0.57735, -0.57735) UV(0.787075, 0.663056)
V 94: Pos(1.857603, 0.794636, 0.014266) Norm(-0.57735, -0.57735, -0.57735) UV(0.923281, 0.684519)
V 95: Pos(2.157339, 0.794636, 0.014266) Norm(0.57735, -0.57735, -0.57735) UV(0.923281, 0.975802)
V 96: Pos(2.157339, 0.794636, 0.107142) Norm(0.707107, -0.707107, 0.0) UV(0.833023, 0.975802)
V 97: Pos(1.857603, 0.794636, 0.107142) Norm(-0.707107, -0.707107, 0.0) UV(0.833023, 0.684519)
V 98: Pos(2.157339, 0.794636, 0.177222) Norm(0.904534, -0.301511, -0.301511) UV(0.76492, 0.975802)
V 99: Pos(1.857603, 0.794636, 0.177222) Norm(-0.904534, -0.301511, -0.301511) UV(0.76492, 0.684519)
V 100: Pos(2.157339, 0.794636, 0.177222) Norm(0.904534, -0.301511, -0.301511) UV(0.315277, 0.641593)
V 101: Pos(2.157339, 0.731786, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.315277, 0.580516)
V 102: Pos(1.857603, 0.731786, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.60656, 0.580516)
V 103: Pos(1.857603, 0.794636, 0.177222) Norm(-0.904534, -0.301511, -0.301511) UV(0.60656, 0.641593)
V 104: Pos(2.157339, 0.64685, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.315277, 0.497976)
V 105: Pos(1.857603, 0.64685, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.60656, 0.497976)
V 106: Pos(2.157339, 0.476979, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.315277, 0.332896)
V 107: Pos(1.857603, 0.476979, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.60656, 0.332896)
V 108: Pos(1.857603, 0.307108, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.60656, 0.167815)
V 109: Pos(2.157339, 0.307108, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.315277, 0.167815)
V 110: Pos(1.857603, 0.222172, 0.177222) Norm(-0.707107, 0.0, -0.707107) UV(0.60656, 0.085275)
V 111: Pos(2.157339, 0.222172, 0.177222) Norm(0.707107, 0.0, -0.707107) UV(0.315277, 0.085275)
V 112: Pos(1.857603, 0.159323, 0.177222) Norm(-0.904534, 0.301511, -0.301511) UV(0.60656, 0.024198)
V 113: Pos(2.157339, 0.159323, 0.177222) Norm(0.904534, 0.301511, -0.301511) UV(0.315277, 0.024198)
V 114: Pos(1.857603, 0.159323, 0.107142) Norm(-0.707107, 0.707107, 0.0) UV(0.674663, 0.975802)
V 115: Pos(1.857603, 0.159323, 0.177222) Norm(-0.904534, 0.301511, -0.301511) UV(0.60656, 0.975802)
V 116: Pos(2.157339, 0.159323, 0.177222) Norm(0.904534, 0.301511, -0.301511) UV(0.60656, 0.684519)
V 117: Pos(2.157339, 0.159323, 0.107142) Norm(0.707107, 0.707107, 0.0) UV(0.674663, 0.684519)
V 118: Pos(1.857603, 0.159323, 0.014266) Norm(-0.57735, 0.57735, -0.57735) UV(0.76492, 0.975802)
V 119: Pos(2.157339, 0.159323, 0.014266) Norm(0.57735, 0.57735, -0.57735) UV(0.76492, 0.684519)
===Triangles (Indices): 100
Tri: 0 1 2
Tri: 0 2 3
Tri: 4 5 6
Tri: 4 6 7
Tri: 8 9 10
Tri: 8 10 11
Tri: 12 13 14
Tri: 12 14 15
Tri: 16 17 18
Tri: 16 18 19
Tri: 20 21 22
Tri: 20 22 23
Tri: 24 25 26
Tri: 24 26 27
Tri: 28 29 30
Tri: 28 30 31
Tri: 32 33 34
Tri: 32 34 35
Tri: 36 37 38
Tri: 36 38 39
Tri: 25 12 15
Tri: 25 15 26
Tri: 29 8 11
Tri: 29 11 30
Tri: 33 4 7
Tri: 33 7 34
Tri: 37 0 3
Tri: 37 3 38
Tri: 40 41 42
Tri: 40 42 43
Tri: 44 45 46
Tri: 44 46 47
Tri: 48 49 50
Tri: 48 50 51
Tri: 52 53 45
Tri: 52 45 44
Tri: 54 55 41
Tri: 54 41 40
Tri: 51 50 56
Tri: 51 56 57
Tri: 58 59 55
Tri: 58 55 54
Tri: 60 61 53
Tri: 60 53 52
Tri: 57 56 62
Tri: 57 62 63
Tri: 64 65 61
Tri: 64 61 60
Tri: 66 67 68
Tri: 66 68 69
Tri: 70 71 59
Tri: 70 59 58
Tri: 72 73 74
Tri: 72 74 75
Tri: 73 64 60
Tri: 73 60 74
Tri: 63 62 1
Tri: 63 1 0
Tri: 76 77 49
Tri: 76 49 48
Tri: 43 42 78
Tri: 43 78 79
Tri: 47 46 80
Tri: 47 80 81
Tri: 82 83 77
Tri: 82 77 76
Tri: 79 78 84
Tri: 79 84 85
Tri: 81 80 86
Tri: 81 86 87
Tri: 88 89 83
Tri: 88 83 82
Tri: 90 91 92
Tri: 90 92 93
Tri: 91 88 82
Tri: 91 82 92
Tri: 85 84 21
Tri: 85 21 20
Tri: 87 86 9
Tri: 87 9 8
Tri: 94 95 96
Tri: 94 96 97
Tri: 96 98 99
Tri: 96 99 97
Tri: 100 101 102
Tri: 100 102 103
Tri: 101 104 105
Tri: 101 105 102
Tri: 104 106 107
Tri: 104 107 105
Tri: 108 107 106
Tri: 108 106 109
Tri: 110 108 109
Tri: 110 109 111
Tri: 112 110 111
Tri: 112 111 113
Tri: 114 115 116
Tri: 114 116 117
Tri: 118 114 117
Tri: 118 117 119
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
-29
View File
@@ -1,29 +0,0 @@
===Vertices (Split by UV/Normal): 14
V 0: Pos(1.0, 1.0, 1.0) Norm(0.57735, 0.57735, 0.57735) UV(0.5, 0.5)
V 1: Pos(-1.0, 1.0, 1.0) Norm(-0.57735, 0.57735, 0.57735) UV(0.75, 0.5)
V 2: Pos(-1.0, -1.0, 1.0) Norm(-0.57735, -0.57735, 0.57735) UV(0.75, 0.75)
V 3: Pos(1.0, -1.0, 1.0) Norm(0.57735, -0.57735, 0.57735) UV(0.5, 0.75)
V 4: Pos(1.0, -1.0, -1.0) Norm(0.57735, -0.57735, -0.57735) UV(0.25, 0.75)
V 5: Pos(-1.0, -1.0, 1.0) Norm(-0.57735, -0.57735, 0.57735) UV(0.5, 1.0)
V 6: Pos(-1.0, -1.0, -1.0) Norm(-0.57735, -0.57735, -0.57735) UV(0.25, 1.0)
V 7: Pos(-1.0, -1.0, -1.0) Norm(-0.57735, -0.57735, -0.57735) UV(0.25, 0.0)
V 8: Pos(-1.0, -1.0, 1.0) Norm(-0.57735, -0.57735, 0.57735) UV(0.5, 0.0)
V 9: Pos(-1.0, 1.0, 1.0) Norm(-0.57735, 0.57735, 0.57735) UV(0.5, 0.25)
V 10: Pos(-1.0, 1.0, -1.0) Norm(-0.57735, 0.57735, -0.57735) UV(0.25, 0.25)
V 11: Pos(-1.0, 1.0, -1.0) Norm(-0.57735, 0.57735, -0.57735) UV(0.0, 0.5)
V 12: Pos(1.0, 1.0, -1.0) Norm(0.57735, 0.57735, -0.57735) UV(0.25, 0.5)
V 13: Pos(-1.0, -1.0, -1.0) Norm(-0.57735, -0.57735, -0.57735) UV(0.0, 0.75)
===Triangles (Indices): 12
Tri: 0 1 2
Tri: 0 2 3
Tri: 4 3 5
Tri: 4 5 6
Tri: 7 8 9
Tri: 7 9 10
Tri: 11 12 4
Tri: 11 4 13
Tri: 12 0 3
Tri: 12 3 4
Tri: 10 9 0
Tri: 10 0 12
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Loaded 100 of 2479 files, more files were not shown because too many files have changed in this diff. Show more