2024-07-19 10:21:26 +03:00

230 lines
6.2 KiB
Go

package exporter
import (
"errors"
"fmt"
_ "image/png"
"log"
"os"
"path"
"path/filepath"
"strings"
_ "github.com/ftrvxmtrx/tga"
"github.com/qmuntal/gltf"
"git.influ.su/artmares/digglestool/pkg/threedb"
)
type Exporter struct {
onlyBaseMesh bool
animation bool
texturesBasePath *string
cache *Cache[*CacheItem]
}
type Option func(*Exporter)
func WithOnlyBaseMesh() Option {
return func(e *Exporter) {
e.onlyBaseMesh = true
}
}
func WithTexturesBasePath(basePath string) Option {
return func(e *Exporter) {
if basePath != "" {
e.texturesBasePath = &basePath
}
}
}
func WithAnimation() Option {
return func(e *Exporter) {
e.animation = true
}
}
func NewExporter(options ...Option) *Exporter {
e := &Exporter{
cache: NewCache[*CacheItem](),
}
for _, option := range options {
option(e)
}
return e
}
func (e *Exporter) Export(basePath string, model *threedb.Model) error {
if model == nil {
return errors.New("model is nil")
}
meshesLen := len(model.Meshes)
if meshesLen == 0 {
return errors.New("no meshes to export")
}
if err := e.prepareTextures(); err != nil {
return err
}
if e.onlyBaseMesh {
log.Printf("Export Base Model: %s", model.Name)
doc, err := e.generate(model, 0)
if err != nil {
return err
}
if err = gltf.Save(doc, path.Join(basePath, fmt.Sprintf("%s-base-model.gltf", model.Name))); err != nil {
return err
}
}
if e.animation {
dirPath := path.Join(basePath, "animation", model.Name)
if _, err := os.Stat(dirPath); os.IsNotExist(err) {
if err = os.MkdirAll(dirPath, os.ModeDir); err != nil {
return err
}
}
log.Printf("Export Animation to %s\n", dirPath)
length := len(model.Animations)
for index, animation := range model.Animations {
log.Printf("Export progress: %d/%d\r", index+1, length)
indexes := make([]int, len(animation.MeshIndexes))
for _, index := range animation.MeshIndexes {
indexes = append(indexes, int(index))
}
doc, err := e.generate(model, indexes...)
if err != nil {
return err
}
if err = gltf.Save(doc, path.Join(dirPath, fmt.Sprintf("%s-%s.gltf", model.Name, animation.Name))); err != nil {
return err
}
}
}
return nil
}
func (e *Exporter) generate(model *threedb.Model, meshIndexes ...int) (*gltf.Document, error) {
return new(Model).Generate(model, e.cache, meshIndexes...)
//var err error
//doc := gltf.NewDocument()
//log.Println("Generate", meshIndexes)
//materialsMap := make(map[uint16]int)
//for _, meshIndex := range meshIndexes {
// mesh := model.Meshes[meshIndex]
// var nodeIndexes []int
// for index, meshLink := range mesh.Links {
// points := model.Points[meshLink.Points]
// var vertices [][3]float32
// for _, point := range points {
// vec := point.Transform(100)
// vec = vec.Normalize()
// vertices = append(vertices, vec)
// }
// verticesIndex := modeler.WriteAccessor(doc, gltf.TargetElementArrayBuffer, vertices)
// textureCoordinates := model.TextureCoordinates[meshLink.TextureCoordinates]
// var textureCoord [][2]float32
// for _, txc := range textureCoordinates {
// textureCoord = append(textureCoord, txc)
// }
// textureCoordIndex := modeler.WriteAccessor(doc, gltf.TargetArrayBuffer, textureCoord)
// trianglesIndex := modeler.WriteAccessor(doc, gltf.TargetArrayBuffer, model.Triangles[meshLink.Triangles])
// materialIndex, ok := materialsMap[meshLink.Material]
// if !ok {
// material := model.Materials[meshLink.Material]
// log.Println("Material", material.Name)
// data := e.cache.Bytes(material.Name)
// if data == nil {
// var f *os.File
// if f, err = os.Open(e.cache.Path(material.Name)); err != nil {
// return nil, err
// }
// img, _, err := image.Decode(f)
// if err != nil {
// return nil, err
// }
// buff, putter := helpers.NewBuffer()
// if err = png.Encode(buff, img); err != nil {
// return nil, err
// }
// data = buff.Bytes()
// log.Println(material.Name, len(data))
// putter(buff)
// e.cache.AddBytes(material.Name, data)
// }
// log.Println(material.Name, len(data))
// var imageIndex int
// if imageIndex, err = modeler.WriteImage(doc, material.Name, "image/png", bytes.NewBuffer(data)); err != nil {
// return nil, err
// }
// doc.Textures = append(doc.Textures, &gltf.Texture{
// Name: material.Name,
// Sampler: gltf.Index(0),
// Source: gltf.Index(imageIndex),
// })
// materialIndex = len(doc.Materials)
// doc.Materials = append(doc.Materials, &gltf.Material{
// Name: material.Name,
// AlphaMode: gltf.AlphaOpaque,
// AlphaCutoff: gltf.Float(0.5),
// PBRMetallicRoughness: &gltf.PBRMetallicRoughness{
// BaseColorTexture: &gltf.TextureInfo{Index: imageIndex},
// BaseColorFactor: &[4]float64{0.8, 0.8, 0.8, 1},
// MetallicFactor: gltf.Float(0.1),
// RoughnessFactor: gltf.Float(0.99),
// },
// })
// materialsMap[meshLink.Material] = materialIndex
// }
// log.Println("materialIndex", materialIndex)
// doc.Meshes = append(doc.Meshes, &gltf.Mesh{
// Name: fmt.Sprintf("%s-%d", model.Name, index),
// Primitives: []*gltf.Primitive{{
// Attributes: gltf.PrimitiveAttributes{
// gltf.POSITION: verticesIndex,
// gltf.TEXCOORD_0: textureCoordIndex,
// },
// Indices: gltf.Index(trianglesIndex),
// Material: gltf.Index(materialIndex),
// }},
// })
// nodeIndexes = append(nodeIndexes, len(doc.Nodes))
// doc.Nodes = append(doc.Nodes, &gltf.Node{
// Mesh: gltf.Index(len(doc.Meshes) - 1),
// })
// }
// doc.Scenes = []*gltf.Scene{{
// Name: "Root Scene",
// Nodes: nodeIndexes,
// }}
// doc.Samplers = []*gltf.Sampler{{}}
//}
//return doc, nil
}
func (e *Exporter) prepareTextures() error {
if e.texturesBasePath == nil {
return nil
}
if err := filepath.Walk(*e.texturesBasePath, func(pathFile string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if strings.HasSuffix(info.Name(), ".tga") {
key := strings.TrimSuffix(info.Name(), ".tga")
item, ok := e.cache.Get(key)
if !ok {
item = &CacheItem{}
}
if info.Size() > item.Size {
item.Path = pathFile
}
e.cache.Set(key, item)
}
return nil
}); err != nil {
return err
}
return nil
}