Blender To Zero-K
Here is a tutorial covering all of the steps to make or edit a model in Blender (a free 3d modelling program) and transfer it to the Spring engine, then have Zero-K load it as a mutator so you can test it in-game.
The contents page here will also act as a quick reference for all of the necessary steps, in order:
Contents
Make or Edit the model in Blender
Setting up Blender for use in Zero-K
Importing a model (optional)
Importing textures
Modelling for Zero-K
Texturing for Zero-K, and Spring texture oddities
Export the model to .dae format
A note on animation
Set up texture associations and .dae.lua metadata
Set up Zero-K mod that contains and uses your files
Run Zero-K and point at your mod
Fix whatever isn't right
The other bits
Ever heard about software development that first you need to complete 90% of the project, and then you need to complete the other 90% of the project? The same sometimes applies to modding.
Even though by this point you should have your unit or building in game and working, there are other things to do!
groundplane decal and ao bake
If your unit is a building, you will need to create a ground plane to go underneath it - at the very least an ambient occlusion baked shadow, usually referred to as an aoplane. This is simply a texture which is sized appropriately for your model and is placed underneath it by the engine. This adds a little soft shadowing around the edge of the building which helps immensely to integrate it with the map.
There are a few ways to make these, but the easiest if following this tutorial is probably just to bake your own in Blender.
unitpic
Debug data:
[SQLBagOStuff] MainObjectStash using store ReplicatedBagOStuff
[objectcache] MainWANObjectCache using store EmptyBagOStuff
IP: 160.79.110.213
Start request GET /mediawiki/index.php?direction=prev&oldid=7008&title=Blender_To_Zero-K
HTTP HEADERS:
CONTENT-TYPE:
CONTENT-LENGTH: 0
USER-AGENT: Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)
HOST: zero-k.info
ACCEPT-ENCODING: gzip, br, zstd, deflate
ACCEPT: */*
CONNECTION: close[localisation] LocalisationCache: using store LCStoreDB
[session] SessionManager using store SqlBagOStuff
[DBReplication] Cannot use ChronologyProtector with EmptyBagOStuff
[DBReplication] Wikimedia\Rdbms\LBFactory::getChronologyProtector: request info {
"IPAddress": "160.79.110.213",
"UserAgent": "Mozilla\/5.0 AppleWebKit\/537.36 (KHTML, like Gecko; compatible; ClaudeBot\/1.0; +claudebot@anthropic.com)",
"ChronologyProtection": false,
"ChronologyPositionIndex": 0,
"ChronologyClientId": false
}[DBConnection] Wikimedia\Rdbms\LoadBalancer::lazyLoadReplicationPositions: executed chronology callback.
[DBConnection] Wikimedia\Rdbms\LoadBalancer::getLocalConnection: connected to database 0 at 'localhost'.
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
[session] SessionBackend "spomhp2utmead2t0bhh1qippbe3h4nc3" is unsaved, marking dirty in constructor
[session] SessionBackend "spomhp2utmead2t0bhh1qippbe3h4nc3" save: dataDirty=1 metaDirty=1 forcePersist=0
[cookie] already deleted setcookie: "wikidb229_mw__session", "", "1713816524", "/", "", "", "1"
[cookie] already deleted setcookie: "wikidb229_mw_UserID", "", "1713816524", "/", "", "", "1"
[cookie] already deleted setcookie: "wikidb229_mw_Token", "", "1713816524", "/", "", "", "1"
[cookie] already deleted setcookie: "forceHTTPS", "", "1713816524", "/", "", "", "1"
[DBConnection] Wikimedia\Rdbms\LoadBalancer::getLocalConnection: connected to database 0 at 'localhost'.
Title::getRestrictionTypes: applicable restrictions to [[Blender To Zero-K]] are {edit,move}
[ContentHandler] Created handler for wikitext: WikitextContentHandler
[MessageCache] MessageCache using store SqlBagOStuff
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
[SQLBagOStuff] SqlBagOStuff::lock failed due to timeout for wikidb229-mw_:messages:en.
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
[MessageCache] MessageCache::load: Loading en... local cache is empty, global cache is expired/volatile, loading from database
ParserFactory: using preprocessor: Preprocessor_Hash
Unstubbing $wgLang on call of $wgLang::_unstub from ParserOptions->__construct
[caches] parser: SqlBagOStuff
Article::view using parser cache: no
Article::view: doing uncached parse
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
[SQLBagOStuff] Connection mysql object #127 (handle id #121) will be used for SqlBagOStuff
Parser cache options found.
[Preprocessor] Cached preprocessor output (key: wikidb229-mw_:preprocess-hash:c0109d64319406e402d70e5e237dbcab:0)
[Preprocessor] Cached preprocessor output (key: wikidb229-mw_:preprocess-hash:c0109d64319406e402d70e5e237dbcab:0)
MediaWiki::preOutputCommit: primary transaction round committed
MediaWiki::preOutputCommit: pre-send deferred updates completed
MediaWiki::preOutputCommit: session changes committed
MediaWiki::preOutputCommit: LBFactory shutdown completed
Title::getRestrictionTypes: applicable restrictions to [[Blender To Zero-K]] are {edit,move}