skog, mjuka skuggor, jämnare övergång vatten på av avstånd
This commit is contained in:
90
tools/vegetation/download.py
Normal file
90
tools/vegetation/download.py
Normal file
@@ -0,0 +1,90 @@
|
||||
"""Fetch reproducible Poly Haven glTF sources and PBR ground materials (CC0)."""
|
||||
import concurrent.futures, hashlib, json, pathlib, urllib.request, time
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parents[2]
|
||||
DEST = ROOT / 'Content' / 'Vegitations'
|
||||
ASSETS = ['pine_tree_01', 'fir_tree_01', 'tree_small_02', 'fir_sapling_medium',
|
||||
'shrub_01', 'shrub_04', 'fern_02', 'grass_medium_01', 'grass_medium_02', 'weed_plant_02']
|
||||
HEADERS = {'User-Agent': 'UniverseVegetationImporter/1.0 (Poly Haven CC0 asset preparation)'}
|
||||
|
||||
def request(url):
|
||||
return urllib.request.urlopen(urllib.request.Request(url, headers=HEADERS), timeout=120)
|
||||
|
||||
def md5(path):
|
||||
h = hashlib.md5()
|
||||
with path.open('rb') as f:
|
||||
for block in iter(lambda: f.read(4*1024*1024), b''): h.update(block)
|
||||
return h.hexdigest()
|
||||
|
||||
def fetch(item):
|
||||
path = DEST / item['path']
|
||||
if not path.resolve().is_relative_to(DEST.resolve()): raise RuntimeError('Asset path escapes content folder')
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if path.exists() and path.stat().st_size == item['size'] and md5(path) == item['md5']:
|
||||
return
|
||||
partial = path.with_suffix(path.suffix + '.partial')
|
||||
for attempt in range(4):
|
||||
try:
|
||||
with request(item['url']) as response, partial.open('wb') as output:
|
||||
received = 0
|
||||
while True:
|
||||
block = response.read(1024*1024)
|
||||
if not block: break
|
||||
received += len(block)
|
||||
if received > item['size']: raise RuntimeError('Download exceeds declared size: '+str(path))
|
||||
output.write(block)
|
||||
if partial.stat().st_size != item['size'] or md5(partial) != item['md5']:
|
||||
raise RuntimeError('Checksum/length mismatch: ' + str(path))
|
||||
partial.replace(path)
|
||||
print('Downloaded', item['path'], round(item['size']/1048576, 1), 'MiB', flush=True)
|
||||
return
|
||||
except Exception:
|
||||
if attempt == 3: raise
|
||||
time.sleep(attempt + 1)
|
||||
|
||||
def main():
|
||||
DEST.mkdir(parents=True, exist_ok=True)
|
||||
manifest_path = DEST / 'sources.json'
|
||||
if manifest_path.exists():
|
||||
manifest = json.loads(manifest_path.read_text())
|
||||
else:
|
||||
files = []
|
||||
for asset in ASSETS:
|
||||
with request('https://api.polyhaven.com/files/' + asset) as f: data = json.load(f)
|
||||
model = data['gltf']['2k']['gltf']
|
||||
entries = {'model.gltf': model, **model.get('include', {})}
|
||||
for relative, entry in entries.items():
|
||||
files.append({**{k: entry[k] for k in ('url','size','md5')}, 'path': asset + '/source/' + relative})
|
||||
# Ground materials share the same biome field as the plants.
|
||||
for asset in ['forest_ground_04', 'withered_grass', 'snow_02']:
|
||||
with request('https://api.polyhaven.com/files/' + asset) as f: data = json.load(f)
|
||||
for kind in ['diff', 'nor_gl', 'rough']:
|
||||
key = next(k for k in data if k.lower() in ({'diff': ['diff','diffuse'], 'rough':['rough','roughness'], 'nor_gl':['nor_gl']}[kind]))
|
||||
entry = data[key]['2k']['jpg']
|
||||
files.append({**{k:entry[k] for k in ('url','size','md5')}, 'path':'ground/'+asset+'/'+kind+'.jpg'})
|
||||
manifest = {'provider':'Poly Haven', 'license':'CC0', 'source':'https://polyhaven.com', 'files':files}
|
||||
size = sum(f['size'] for f in files)
|
||||
if size > 2_000_000_000: raise RuntimeError('Original asset budget exceeded: '+str(size))
|
||||
manifest_path.write_text(json.dumps(manifest, indent=2)+'\n')
|
||||
if not manifest.get('foliageMasksIncluded'):
|
||||
for asset in ASSETS:
|
||||
with request('https://api.polyhaven.com/files/' + asset) as f: data = json.load(f)
|
||||
for kind, variants in data.items():
|
||||
if 'alpha' not in kind.lower() and 'dry_diff' not in kind.lower(): continue
|
||||
formats=variants.get('2k',{})
|
||||
extension='png' if 'png' in formats else 'jpg'
|
||||
if extension not in formats: continue
|
||||
entry=formats[extension]
|
||||
manifest['files'].append({**{k:entry[k] for k in ('url','size','md5')},
|
||||
'path':asset+'/source/masks/'+kind+'.'+extension})
|
||||
manifest['foliageMasksIncluded']=True
|
||||
if sum(f['size'] for f in manifest['files'])>2_000_000_000: raise RuntimeError('Asset budget exceeded')
|
||||
manifest_path.write_text(json.dumps(manifest,indent=2)+'\n')
|
||||
print('Poly Haven CC0 assets:', len(manifest['files']), 'files,', round(sum(f['size'] for f in manifest['files'])/1048576), 'MiB', flush=True)
|
||||
if any(f['size']<=0 for f in manifest['files']) or sum(f['size'] for f in manifest['files'])>2_000_000_000:
|
||||
raise RuntimeError('Source download budget exceeded')
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
|
||||
list(executor.map(fetch, manifest['files']))
|
||||
print('All source checksums verified.', flush=True)
|
||||
|
||||
if __name__ == '__main__': main()
|
||||
Reference in New Issue
Block a user