feat: test tool write
This commit is contained in:
parent
481a1e65ee
commit
0f6262da8f
4
.gitignore
vendored
4
.gitignore
vendored
@ -154,3 +154,7 @@ Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# Ignore models
|
||||
models/*
|
||||
!models/.keep
|
||||
|
||||
|
||||
220
cmd/decoder/main.go
Normal file
220
cmd/decoder/main.go
Normal file
@ -0,0 +1,220 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand/v2"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/gabriel-vasile/mimetype"
|
||||
"github.com/qmuntal/gltf"
|
||||
"github.com/qmuntal/gltf/modeler"
|
||||
|
||||
"git.influ.su/artmares/digglestool/internal/services/exporter"
|
||||
"git.influ.su/artmares/digglestool/pkg/threedb"
|
||||
)
|
||||
|
||||
const (
|
||||
outputPath = "./models"
|
||||
|
||||
texturesCacheFile = "./textures.cache"
|
||||
)
|
||||
|
||||
func main() {
|
||||
texturesPath := os.Getenv("TEXTURES_PATH")
|
||||
if texturesPath == "" {
|
||||
log.Println("TEXTURES_PATH environment variable not set")
|
||||
return
|
||||
}
|
||||
|
||||
inputPath := "./data"
|
||||
inputModel := "produktionsstaetten"
|
||||
|
||||
f, err := os.Open(path.Join(inputPath, inputModel+".3db"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
model := threedb.Model{}
|
||||
err = threedb.NewDecoder(f).Decode(&model)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
//log.Printf("DB Version: %v\n", model.DBVersion)
|
||||
//log.Printf("Name: %v\n", model.Name)
|
||||
//log.Printf("Materials: %d\n", len(model.Materials))
|
||||
//for _, material := range model.Materials {
|
||||
// log.Printf("\t: %v\n", material)
|
||||
//}
|
||||
//log.Printf("Meshes: %d\n", len(model.Meshes))
|
||||
//log.Printf("Objects: %+v\n", model.Objects)
|
||||
//log.Printf("Animations: %d\n", len(model.Animations))
|
||||
//for _, animation := range model.Animations {
|
||||
// log.Printf("\t: %+v\n", animation)
|
||||
//}
|
||||
//log.Printf("Triangles: %d\n", len(model.Triangles))
|
||||
//log.Printf("Texture Coordinates: %d\n", len(model.TextureCoordinates))
|
||||
//log.Printf("Points: %d\n", len(model.Points))
|
||||
//log.Printf("Brightness: %d\n", len(model.Brightness))
|
||||
|
||||
logFile, err := os.OpenFile(fmt.Sprintf("./%s.json", inputModel), os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer logFile.Close()
|
||||
enc := json.NewEncoder(logFile)
|
||||
enc.SetIndent("", " ")
|
||||
if err = enc.Encode(model); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
export := exporter.NewExporter(
|
||||
exporter.WithTexturesBasePath(texturesPath),
|
||||
exporter.WithOnlyBaseMesh(),
|
||||
exporter.WithAnimation(),
|
||||
)
|
||||
if err = export.Export(outputPath, &model); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
//if err = export(&model, inputModel, texturesCache); err != nil {
|
||||
// log.Println(err)
|
||||
// return
|
||||
//}
|
||||
}
|
||||
|
||||
func exportFn(model *threedb.Model, inputModel string, cache map[string]string) error {
|
||||
//maxMeshIndex := 133
|
||||
//meshIndex := randRange(0, len(model.Meshes)-1)
|
||||
meshIndex := 0
|
||||
//log.Println("Mesh Index:", meshIndex)
|
||||
binaryCache := make(map[string][]byte)
|
||||
|
||||
//for meshIndex := 0; meshIndex < len(model.Meshes); meshIndex++ {
|
||||
doc := gltf.NewDocument()
|
||||
//doc.Materials = []*gltf.Material{{
|
||||
// Name: "Default", AlphaMode: gltf.AlphaOpaque, AlphaCutoff: gltf.Float(0.5),
|
||||
// PBRMetallicRoughness: &gltf.PBRMetallicRoughness{BaseColorFactor: &[4]float64{0.8, 0.8, 0.8, 1}, MetallicFactor: gltf.Float(0.1), RoughnessFactor: gltf.Float(0.99)},
|
||||
//}}
|
||||
for _, material := range model.Materials {
|
||||
data, ok := binaryCache[material.Name]
|
||||
if !ok {
|
||||
texturePath, ok := cache[material.Name]
|
||||
if !ok {
|
||||
log.Printf("Invalid texture cache %q", material.Name)
|
||||
continue
|
||||
return fmt.Errorf("invalid texture cache %q", material.Name)
|
||||
}
|
||||
data, err := os.ReadFile(texturePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
binaryCache[material.Name] = data
|
||||
}
|
||||
mt := mimetype.Detect(data)
|
||||
if mt == nil {
|
||||
return fmt.Errorf("can't geting mimetype %q", material.Name)
|
||||
}
|
||||
imageIndex, err := modeler.WriteImage(doc, material.Name+".tga", mt.String(), bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
doc.Textures = append(doc.Textures, &gltf.Texture{
|
||||
Name: material.Name,
|
||||
//Sampler: gltf.Index(0),
|
||||
Source: gltf.Index(imageIndex),
|
||||
})
|
||||
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),
|
||||
},
|
||||
})
|
||||
}
|
||||
mesh := model.Meshes[meshIndex]
|
||||
for _, meshLink := range mesh.Links {
|
||||
triangles := model.Triangles[meshLink.Triangles]
|
||||
points := model.Points[meshLink.Points]
|
||||
textureCoordinates := model.TextureCoordinates[meshLink.TextureCoordinates]
|
||||
material := model.Materials[meshLink.Material]
|
||||
var vertices [][3]float32
|
||||
var mins []float64
|
||||
var maxs []float64
|
||||
for _, point := range points {
|
||||
vec := point.Transform(100)
|
||||
vec = vec.Normalize()
|
||||
vertices = append(vertices, vec)
|
||||
mins = append(mins, float64(vec.Min()))
|
||||
maxs = append(maxs, float64(vec.Max()))
|
||||
}
|
||||
verticesIndex := modeler.WriteAccessor(doc, gltf.TargetElementArrayBuffer, vertices)
|
||||
var textureCoords [][2]float32
|
||||
for _, txc := range textureCoordinates {
|
||||
textureCoords = append(textureCoords, txc)
|
||||
}
|
||||
textureIndex := modeler.WriteAccessor(doc, gltf.TargetArrayBuffer, textureCoords)
|
||||
trianglesIndex := modeler.WriteAccessor(doc, gltf.TargetArrayBuffer, triangles)
|
||||
materialIndex := len(doc.Materials)
|
||||
doc.Meshes = append(doc.Meshes, &gltf.Mesh{
|
||||
Name: fmt.Sprintf("%s-%d", model.Name, len(doc.Meshes)),
|
||||
Primitives: []*gltf.Primitive{{
|
||||
Attributes: gltf.PrimitiveAttributes{
|
||||
gltf.POSITION: verticesIndex,
|
||||
gltf.TEXCOORD_0: textureIndex,
|
||||
},
|
||||
Indices: gltf.Index(trianglesIndex),
|
||||
Material: gltf.Index(materialIndex),
|
||||
}},
|
||||
})
|
||||
|
||||
//imageIndex, err := modeler.WriteImage(doc)
|
||||
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),
|
||||
},
|
||||
})
|
||||
doc.Nodes = append(doc.Nodes, &gltf.Node{
|
||||
Mesh: gltf.Index(len(doc.Meshes) - 1),
|
||||
})
|
||||
}
|
||||
var nodes []int
|
||||
for index := range doc.Nodes {
|
||||
nodes = append(nodes, index)
|
||||
}
|
||||
|
||||
doc.Scenes = []*gltf.Scene{
|
||||
{Name: "Root Scene", Nodes: nodes},
|
||||
}
|
||||
if err := gltf.Save(doc, path.Join(outputPath, fmt.Sprintf("%s-%d.gltf", inputModel, meshIndex))); err != nil {
|
||||
return err
|
||||
}
|
||||
//if err := gltf.SaveBinary(doc, fmt.Sprintf("./%s.glb", inputModel)); err != nil {
|
||||
// return err
|
||||
//}
|
||||
//}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func randRange(min, max int) int {
|
||||
return rand.IntN(max-min) + min
|
||||
}
|
||||
45
cmd/testgltf/main.go
Normal file
45
cmd/testgltf/main.go
Normal file
@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/qmuntal/gltf"
|
||||
)
|
||||
|
||||
func main() {
|
||||
doc := &gltf.Document{
|
||||
Accessors: []*gltf.Accessor{
|
||||
{BufferView: gltf.Index(0), ComponentType: gltf.ComponentUshort, Count: 36, Type: gltf.AccessorScalar},
|
||||
{BufferView: gltf.Index(1), ComponentType: gltf.ComponentFloat, Count: 24, Max: []float64{0.5, 0.5, 0.5}, Min: []float64{-0.5, -0.5, -0.5}, Type: gltf.AccessorVec3},
|
||||
{BufferView: gltf.Index(2), ComponentType: gltf.ComponentFloat, Count: 24, Type: gltf.AccessorVec3},
|
||||
{BufferView: gltf.Index(3), ComponentType: gltf.ComponentFloat, Count: 24, Type: gltf.AccessorVec4},
|
||||
{BufferView: gltf.Index(4), ComponentType: gltf.ComponentFloat, Count: 24, Type: gltf.AccessorVec2},
|
||||
},
|
||||
Asset: gltf.Asset{Version: "2.0", Generator: "FBX2glTF"},
|
||||
BufferViews: []*gltf.BufferView{
|
||||
{Buffer: 0, ByteLength: 72, ByteOffset: 0, Target: gltf.TargetElementArrayBuffer},
|
||||
{Buffer: 0, ByteLength: 288, ByteOffset: 72, Target: gltf.TargetArrayBuffer},
|
||||
{Buffer: 0, ByteLength: 288, ByteOffset: 360, Target: gltf.TargetArrayBuffer},
|
||||
{Buffer: 0, ByteLength: 384, ByteOffset: 648, Target: gltf.TargetArrayBuffer},
|
||||
{Buffer: 0, ByteLength: 192, ByteOffset: 1032, Target: gltf.TargetArrayBuffer},
|
||||
},
|
||||
Buffers: []*gltf.Buffer{{ByteLength: 1224, Data: []byte{97, 110, 121, 32, 99, 97, 114, 110, 97, 108, 32, 112, 108, 101, 97, 115}}},
|
||||
Materials: []*gltf.Material{{
|
||||
Name: "Default", AlphaMode: gltf.AlphaOpaque, AlphaCutoff: gltf.Float(0.5),
|
||||
PBRMetallicRoughness: &gltf.PBRMetallicRoughness{BaseColorFactor: &[4]float64{0.8, 0.8, 0.8, 1}, MetallicFactor: gltf.Float(0.1), RoughnessFactor: gltf.Float(0.99)},
|
||||
}},
|
||||
Meshes: []*gltf.Mesh{{Name: "Cube", Primitives: []*gltf.Primitive{{Indices: gltf.Index(0), Material: gltf.Index(0), Mode: gltf.PrimitiveTriangles, Attributes: gltf.PrimitiveAttributes{gltf.POSITION: 1, gltf.COLOR_0: 3, gltf.NORMAL: 2, gltf.TEXCOORD_0: 4}}}}},
|
||||
Nodes: []*gltf.Node{
|
||||
{Name: "RootNode", Children: []int{1, 2, 3}},
|
||||
{Name: "Mesh"},
|
||||
{Name: "Cube", Mesh: gltf.Index(0)},
|
||||
{Name: "Texture Group"},
|
||||
},
|
||||
Samplers: []*gltf.Sampler{{WrapS: gltf.WrapRepeat, WrapT: gltf.WrapRepeat}},
|
||||
Scene: gltf.Index(0),
|
||||
Scenes: []*gltf.Scene{{Name: "Root Scene", Nodes: []int{0}}},
|
||||
}
|
||||
if err := gltf.Save(doc, "./test.gltf"); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
12
go.mod
12
go.mod
@ -1,3 +1,15 @@
|
||||
module git.influ.su/artmares/digglestool
|
||||
|
||||
go 1.22.3
|
||||
|
||||
require github.com/stretchr/testify v1.9.0
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/ftrvxmtrx/tga v0.0.0-20150524081124-bd8e8d5be13a // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.4 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/qmuntal/gltf v0.26.1-0.20240704075444-782e57e021e1 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
20
go.sum
Normal file
20
go.sum
Normal file
@ -0,0 +1,20 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/ftrvxmtrx/tga v0.0.0-20150524081124-bd8e8d5be13a h1:eSqaRmdlZ9JsJ7JuWfDr3ym3monToXRczohBOL+heVQ=
|
||||
github.com/ftrvxmtrx/tga v0.0.0-20150524081124-bd8e8d5be13a/go.mod h1:US5WvgEHtG+BvWNNs6gk937h0QL2g2x+r7RH8m3g80Y=
|
||||
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
|
||||
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/qmuntal/gltf v0.26.0 h1:geVxZPBJ43f0ouLyseqMuzgKG6m6DkNH+gI3A63PFas=
|
||||
github.com/qmuntal/gltf v0.26.0/go.mod h1:YoXZOt0Nc0kIfSKOLZIRoV4FycdC+GzE+3JgiAGYoMs=
|
||||
github.com/qmuntal/gltf v0.26.1-0.20240704075444-782e57e021e1 h1:E2QnY6J3DP8tSyAE28BiGcbPFpbIDDtvq3zB6KyBVSI=
|
||||
github.com/qmuntal/gltf v0.26.1-0.20240704075444-782e57e021e1/go.mod h1:YoXZOt0Nc0kIfSKOLZIRoV4FycdC+GzE+3JgiAGYoMs=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
21
internal/pkg/helpers/buffer.go
Normal file
21
internal/pkg/helpers/buffer.go
Normal file
@ -0,0 +1,21 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var pool sync.Pool
|
||||
|
||||
func NewBuffer() (*bytes.Buffer, func(*bytes.Buffer)) {
|
||||
if v := pool.Get(); v != nil {
|
||||
b, _ := v.(*bytes.Buffer)
|
||||
b.Reset()
|
||||
return b, putter
|
||||
}
|
||||
return &bytes.Buffer{}, putter
|
||||
}
|
||||
|
||||
func putter(buff *bytes.Buffer) {
|
||||
pool.Put(buff)
|
||||
}
|
||||
41
internal/services/exporter/cache.go
Normal file
41
internal/services/exporter/cache.go
Normal file
@ -0,0 +1,41 @@
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Cache[T any] struct {
|
||||
data map[string]T
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func NewCache[T any]() *Cache[T] {
|
||||
return &Cache[T]{
|
||||
data: make(map[string]T),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache[T]) Set(key string, value T) {
|
||||
key = strings.ToLower(key)
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
c.data[key] = value
|
||||
}
|
||||
|
||||
func (c *Cache[T]) Get(key string) (T, bool) {
|
||||
key = strings.ToLower(key)
|
||||
c.mutex.RLock()
|
||||
defer c.mutex.RUnlock()
|
||||
|
||||
value, ok := c.data[key]
|
||||
return value, ok
|
||||
}
|
||||
|
||||
type CacheItem struct {
|
||||
Path string
|
||||
Size int64
|
||||
Bytes *bytes.Buffer
|
||||
}
|
||||
229
internal/services/exporter/exporter.go
Normal file
229
internal/services/exporter/exporter.go
Normal file
@ -0,0 +1,229 @@
|
||||
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
|
||||
}
|
||||
141
internal/services/exporter/model.go
Normal file
141
internal/services/exporter/model.go
Normal file
@ -0,0 +1,141 @@
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/png"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
_ "github.com/ftrvxmtrx/tga"
|
||||
"github.com/qmuntal/gltf"
|
||||
"github.com/qmuntal/gltf/modeler"
|
||||
|
||||
"git.influ.su/artmares/digglestool/pkg/threedb"
|
||||
)
|
||||
|
||||
type Model struct {
|
||||
doc *gltf.Document
|
||||
materialMap map[uint16]int
|
||||
}
|
||||
|
||||
func (m *Model) Generate(model *threedb.Model, cache *Cache[*CacheItem], meshIndexes ...int) (*gltf.Document, error) {
|
||||
m.doc = gltf.NewDocument()
|
||||
m.doc.Scenes = []*gltf.Scene{}
|
||||
m.materialMap = make(map[uint16]int)
|
||||
log.Println("Generate use mesh indexes", len(meshIndexes))
|
||||
for i, meshIndex := range meshIndexes {
|
||||
mesh := model.Meshes[meshIndex]
|
||||
var nodeIndexes []int
|
||||
for index, link := range mesh.Links {
|
||||
materialIndex, err := m.materialIndex(model, cache, link.Material)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
verticesIndex := m.generateVertices(model.Points[link.Points])
|
||||
textureCoordIndex := m.generateTextureCoordinate(model.TextureCoordinates[link.TextureCoordinates])
|
||||
trianglesIndex := modeler.WriteAccessor(m.doc, gltf.TargetArrayBuffer, model.Triangles[link.Triangles])
|
||||
m.doc.Meshes = append(m.doc.Meshes, &gltf.Mesh{
|
||||
Name: fmt.Sprintf("%s-%d-%d", model.Name, i, 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(m.doc.Nodes))
|
||||
m.doc.Nodes = append(m.doc.Nodes, &gltf.Node{
|
||||
Mesh: gltf.Index(len(m.doc.Meshes) - 1),
|
||||
})
|
||||
}
|
||||
m.doc.Scenes = append(m.doc.Scenes, &gltf.Scene{
|
||||
Name: fmt.Sprintf("Root Scene-%d", meshIndex),
|
||||
Nodes: nodeIndexes,
|
||||
})
|
||||
}
|
||||
m.doc.Samplers = []*gltf.Sampler{{}}
|
||||
return m.doc, nil
|
||||
}
|
||||
|
||||
func (m *Model) generateVertices(vectors []threedb.Vector) int {
|
||||
var vertices [][3]float32
|
||||
for _, vector := range vectors {
|
||||
vector = vector.Transform(100)
|
||||
vertices = append(vertices, vector.Normalize())
|
||||
}
|
||||
|
||||
return modeler.WriteAccessor(m.doc, gltf.TargetElementArrayBuffer, vertices)
|
||||
}
|
||||
|
||||
func (m *Model) generateTextureCoordinate(coordinates []threedb.Coordinate) int {
|
||||
var coords [][2]float32
|
||||
for _, coordinate := range coordinates {
|
||||
coords = append(coords, coordinate)
|
||||
}
|
||||
return modeler.WriteAccessor(m.doc, gltf.TargetArrayBuffer, coords)
|
||||
}
|
||||
|
||||
func (m *Model) materialIndex(model *threedb.Model, cache *Cache[*CacheItem], meshMaterialIndex uint16) (int, error) {
|
||||
//logger, logPutter := helpers.NewBuffer()
|
||||
//defer func() {
|
||||
// logPutter(logger)
|
||||
//}()
|
||||
materialIndex, ok := m.materialMap[meshMaterialIndex]
|
||||
//logger.WriteString(fmt.Sprintf("Try find material index %d\n", meshMaterialIndex))
|
||||
if !ok {
|
||||
//logger.WriteString(fmt.Sprintf("Material index %d not found\n", meshMaterialIndex))
|
||||
material := model.Materials[meshMaterialIndex]
|
||||
item, ok := cache.Get(material.Name)
|
||||
if !ok {
|
||||
return 0, nil
|
||||
}
|
||||
//logger.WriteString(fmt.Sprintf("Try found material name %s\n", material.Name))
|
||||
if item.Bytes == nil {
|
||||
//logger.WriteString(fmt.Sprintf("Material name %s not found in cache\n", material.Name))
|
||||
f, err := os.Open(item.Path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer f.Close()
|
||||
img, _, err := image.Decode(f)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
item.Bytes = &bytes.Buffer{}
|
||||
if err = png.Encode(item.Bytes, img); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cache.Set(material.Name, item)
|
||||
}
|
||||
//log.Printf("material index: %d, material name: %s, data: %d", meshMaterialIndex, material.Name, len(data))
|
||||
imageIndex, err := modeler.WriteImage(m.doc, material.Name, "image/png", item.Bytes)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
m.doc.Textures = append(m.doc.Textures, &gltf.Texture{
|
||||
Name: material.Name,
|
||||
Sampler: gltf.Index(0),
|
||||
Source: gltf.Index(imageIndex),
|
||||
})
|
||||
materialIndex = len(m.doc.Materials)
|
||||
m.doc.Materials = append(m.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),
|
||||
},
|
||||
})
|
||||
m.materialMap[meshMaterialIndex] = materialIndex
|
||||
}
|
||||
//log.Println(logger.String())
|
||||
|
||||
return materialIndex, nil
|
||||
}
|
||||
0
models/.keep
Normal file
0
models/.keep
Normal file
11
pkg/threedb/animation.go
Normal file
11
pkg/threedb/animation.go
Normal file
@ -0,0 +1,11 @@
|
||||
package threedb
|
||||
|
||||
type Animation struct {
|
||||
Name string
|
||||
MeshIndexes []uint32
|
||||
Unknown, Unknown1, Unknown2 uint16
|
||||
//Unknown1 float32
|
||||
Unknown3 string
|
||||
MoveVector *Vector
|
||||
RotationVector *Vector
|
||||
}
|
||||
12
pkg/threedb/coordinate.go
Normal file
12
pkg/threedb/coordinate.go
Normal file
@ -0,0 +1,12 @@
|
||||
package threedb
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Coordinate [2]float32
|
||||
|
||||
func (m Coordinate) X() float32 { return m[0] }
|
||||
func (m Coordinate) Y() float32 { return m[1] }
|
||||
func (m *Coordinate) Set(x, y float32) { m[0], m[1] = x, y }
|
||||
func (m *Coordinate) SetX(x float32) { m[0] = x }
|
||||
func (m *Coordinate) SetY(y float32) { m[1] = y }
|
||||
func (m Coordinate) String() string { return fmt.Sprintf("x:%f,y:%f", m[0], m[1]) }
|
||||
14
pkg/threedb/coordinate_test.go
Normal file
14
pkg/threedb/coordinate_test.go
Normal file
@ -0,0 +1,14 @@
|
||||
package threedb
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCoordinate_Set(t *testing.T) {
|
||||
expect := Coordinate{1, 1}
|
||||
coord := Coordinate{}
|
||||
coord.Set(1, 1)
|
||||
require.Equal(t, expect, coord)
|
||||
}
|
||||
490
pkg/threedb/decoder.go
Normal file
490
pkg/threedb/decoder.go
Normal file
@ -0,0 +1,490 @@
|
||||
package threedb
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Decoder struct {
|
||||
reader io.ReadSeeker
|
||||
}
|
||||
|
||||
func NewDecoder(reader io.ReadSeeker) *Decoder {
|
||||
return &Decoder{reader: reader}
|
||||
}
|
||||
|
||||
func (dec *Decoder) Decode(model *Model) error {
|
||||
if model == nil {
|
||||
return errors.New("model is nil")
|
||||
}
|
||||
var err error
|
||||
if model.DBVersion, err = dec.readString(); err != nil {
|
||||
return err
|
||||
}
|
||||
if model.Name, err = dec.readString(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = dec.readMaterials(model); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = dec.readMeshes(model); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = dec.readObjects(model); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = dec.readAnimations(model); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = dec.readShadows(model); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = dec.readCubeMaps(model); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = dec.readData(model); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) seek(offset int64) (err error) {
|
||||
_, err = dec.reader.Seek(offset, io.SeekCurrent)
|
||||
return
|
||||
}
|
||||
|
||||
func (dec *Decoder) read(dst any) error {
|
||||
return binary.Read(dec.reader, binary.LittleEndian, dst)
|
||||
}
|
||||
|
||||
func (dec *Decoder) readUInt8() (result uint8, err error) {
|
||||
err = dec.read(&result)
|
||||
return
|
||||
}
|
||||
|
||||
func (dec *Decoder) readUInt16() (result uint16, err error) {
|
||||
err = dec.read(&result)
|
||||
return
|
||||
}
|
||||
|
||||
func (dec *Decoder) readUInt32() (result uint32, err error) {
|
||||
err = dec.read(&result)
|
||||
return
|
||||
}
|
||||
|
||||
func (dec *Decoder) readFloat32() (result float32, err error) {
|
||||
err = dec.read(&result)
|
||||
return
|
||||
}
|
||||
|
||||
func (dec *Decoder) readString() (string, error) {
|
||||
length, err := dec.readUInt32()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf := make([]byte, length)
|
||||
if err = dec.read(&buf); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) readVector() (*Vector, error) {
|
||||
var vec Vector
|
||||
var err error
|
||||
if vec[0], err = dec.readFloat32(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if vec[1], err = dec.readFloat32(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if vec[2], err = dec.readFloat32(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vec, nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) readMaterials(model *Model) error {
|
||||
materialCount, err := dec.readUInt16()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count := int(materialCount)
|
||||
for i := 0; i < count; i++ {
|
||||
material := Material{}
|
||||
if material.Name, err = dec.readString(); err != nil {
|
||||
return err
|
||||
}
|
||||
if material.Path, err = dec.readString(); err != nil {
|
||||
return err
|
||||
}
|
||||
if material.Unknown, err = dec.readUInt32(); err != nil {
|
||||
return err
|
||||
}
|
||||
model.Materials = append(model.Materials, material)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) readMeshes(model *Model) error {
|
||||
meshCount, err := dec.readUInt32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count := int(meshCount)
|
||||
for i := 0; i < count; i++ {
|
||||
mesh := Mesh{}
|
||||
if err = dec.readMeshLink(&mesh); err != nil {
|
||||
return err
|
||||
}
|
||||
if mesh.Vector1, err = dec.readVector(); err != nil {
|
||||
return err
|
||||
}
|
||||
if mesh.Vector2, err = dec.readVector(); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = dec.seek(0x80)
|
||||
if mesh.Shadow, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
} // Mesh Shadow Index ?
|
||||
_ = dec.seek(0x30)
|
||||
if mesh.CMap, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
} // Mesh CMap Index ?
|
||||
model.Meshes = append(model.Meshes, mesh)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) readMeshLink(mesh *Mesh) error {
|
||||
meshLinkCount, err := dec.readUInt16()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count := int(meshLinkCount)
|
||||
for i := 0; i < count; i++ {
|
||||
meshLink := MeshLink{}
|
||||
if meshLink.Material, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
if meshLink.Unknown, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
if meshLink.Triangles, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
if meshLink.TextureCoordinates, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
if meshLink.Points, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
if meshLink.Brightness, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
mesh.Links = append(mesh.Links, meshLink)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) readObjects(model *Model) error {
|
||||
keyValuePairCount, err := dec.readUInt16()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count := int(keyValuePairCount)
|
||||
if count > 0 && model.Objects == nil {
|
||||
model.Objects = make(map[string][]uint32)
|
||||
}
|
||||
for i := 0; i < count; i++ {
|
||||
var key string
|
||||
if key, err = dec.readString(); err != nil {
|
||||
return err
|
||||
}
|
||||
var objectCount uint16
|
||||
if objectCount, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
model.Objects[key] = make([]uint32, objectCount)
|
||||
for j := 0; j < int(objectCount); j++ {
|
||||
var n uint32
|
||||
if n, err = dec.readUInt32(); err != nil {
|
||||
return err
|
||||
}
|
||||
model.Objects[key] = append(model.Objects[key], n)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) readAnimations(model *Model) error {
|
||||
animationCount, err := dec.readUInt16()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count := int(animationCount)
|
||||
for i := 0; i < count; i++ {
|
||||
animation := Animation{}
|
||||
if animation.Name, err = dec.readString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var meshIndexesCount uint16
|
||||
if meshIndexesCount, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
animation.MeshIndexes = make([]uint32, meshIndexesCount)
|
||||
for j := 0; j < int(meshIndexesCount); j++ {
|
||||
var n uint32
|
||||
if n, err = dec.readUInt32(); err != nil {
|
||||
return err
|
||||
}
|
||||
animation.MeshIndexes[j] = n
|
||||
}
|
||||
if animation.Unknown, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
if animation.Unknown1, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
if animation.Unknown2, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
//if animation.Unknown1, err = dec.readFloat32(); err != nil {
|
||||
// return err
|
||||
//}
|
||||
if animation.Unknown3, err = dec.readString(); err != nil {
|
||||
return err
|
||||
}
|
||||
if animation.MoveVector, err = dec.readVector(); err != nil {
|
||||
return err
|
||||
}
|
||||
if animation.RotationVector, err = dec.readVector(); err != nil {
|
||||
return err
|
||||
}
|
||||
model.Animations = append(model.Animations, animation)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) readShadows(_ *Model) error {
|
||||
shadowCount, err := dec.readUInt16()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count := int(shadowCount)
|
||||
for i := 0; i < count; i++ {
|
||||
// Skip // TODO: возможно это картинка 32х32
|
||||
_ = dec.seek(32 * 32)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) readCubeMaps(_ *Model) error {
|
||||
cubeMapCount, err := dec.readUInt16()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count := int(cubeMapCount)
|
||||
for i := 0; i < count; i++ {
|
||||
var width, height uint16
|
||||
if width, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
if height, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = dec.readUInt16()
|
||||
_, _ = dec.readUInt16()
|
||||
// Skip pixel data
|
||||
_ = dec.seek(int64(width * height))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) readData(model *Model) error {
|
||||
var (
|
||||
triangleCount uint16
|
||||
trianglesCounts []uint16
|
||||
textureCoordCount uint16
|
||||
textureCoordCounts []uint16
|
||||
pointCount uint16
|
||||
pointCounts []uint16
|
||||
brightnessCount uint16
|
||||
brightnessCounts []uint16
|
||||
unknownCount uint32
|
||||
|
||||
cnt uint16
|
||||
err error
|
||||
)
|
||||
if triangleCount, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
if textureCoordCount, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
if pointCount, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
if brightnessCount, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
if unknownCount, err = dec.readUInt32(); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println("unknownCount:", unknownCount)
|
||||
for i := 0; i < int(triangleCount); i++ {
|
||||
if cnt, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
trianglesCounts = append(trianglesCounts, cnt)
|
||||
}
|
||||
for i := 0; i < int(textureCoordCount); i++ {
|
||||
if cnt, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
textureCoordCounts = append(textureCoordCounts, cnt)
|
||||
}
|
||||
for i := 0; i < int(pointCount); i++ {
|
||||
if cnt, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
pointCounts = append(pointCounts, cnt)
|
||||
}
|
||||
for i := 0; i < int(brightnessCount); i++ {
|
||||
if cnt, err = dec.readUInt16(); err != nil {
|
||||
return err
|
||||
}
|
||||
brightnessCounts = append(brightnessCounts, cnt)
|
||||
}
|
||||
for i := 0; i < int(unknownCount); i++ {
|
||||
//x, err := dec.readFloat32()
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//y, err := dec.readFloat32()
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//vec, err := dec.readVector()
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//log.Println(Coordinate{x, y}, vec)
|
||||
//if cnt, err = dec.readUInt16(); err != nil {
|
||||
// return err
|
||||
//}
|
||||
//log.Println("cnt", cnt)
|
||||
//buff := make([]byte, 20)
|
||||
//err = dec.read(&buff)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//log.Println(buff)
|
||||
//vec, err := dec.readVector()
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//log.Println(vec)
|
||||
_ = dec.seek(20)
|
||||
}
|
||||
if err = dec.readTriangles(model, int(triangleCount), trianglesCounts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = dec.readTextureCoordinates(model, int(textureCoordCount), textureCoordCounts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = dec.readPoint(model, int(pointCount), pointCounts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = dec.readBrightness(model, int(brightnessCount), brightnessCounts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) readTriangles(model *Model, count int, counts []uint16) error {
|
||||
for i := 0; i < count; i++ {
|
||||
cnt := int(counts[i])
|
||||
var triangles []uint16
|
||||
for j := 0; j < cnt; j++ {
|
||||
n, err := dec.readUInt16()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
triangles = append(triangles, n)
|
||||
}
|
||||
model.Triangles = append(model.Triangles, triangles)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) readTextureCoordinates(model *Model, count int, counts []uint16) error {
|
||||
for i := 0; i < count; i++ {
|
||||
cnt := int(counts[i])
|
||||
var cords []Coordinate
|
||||
for j := 0; j < cnt; j++ {
|
||||
cord := Coordinate{}
|
||||
u, err := dec.readFloat32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v, err := dec.readFloat32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cord.Set(u, v)
|
||||
cords = append(cords, cord)
|
||||
}
|
||||
model.TextureCoordinates = append(model.TextureCoordinates, cords)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) readPoint(model *Model, count int, counts []uint16) error {
|
||||
for i := 0; i < count; i++ {
|
||||
cnt := int(counts[i])
|
||||
var vectors []Vector
|
||||
for j := 0; j < cnt; j++ {
|
||||
vec := Vector{}
|
||||
ux, err := dec.readUInt16()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uy, err := dec.readUInt16()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uz, err := dec.readUInt16()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
vec.Set(float32(ux)/float32(0xffff), float32(uy)/float32(0xffff), float32(uz)/float32(0xffff))
|
||||
vectors = append(vectors, vec)
|
||||
}
|
||||
model.Points = append(model.Points, vectors)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) readBrightness(model *Model, count int, counts []uint16) error {
|
||||
for i := 0; i < count; i++ {
|
||||
cnt := int(counts[i])
|
||||
var brightness []byte
|
||||
for j := 0; j < cnt; j++ {
|
||||
b, err := dec.readUInt8()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
brightness = append(brightness, b)
|
||||
}
|
||||
model.Brightness = append(model.Brightness, brightness)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
6
pkg/threedb/material.go
Normal file
6
pkg/threedb/material.go
Normal file
@ -0,0 +1,6 @@
|
||||
package threedb
|
||||
|
||||
type Material struct {
|
||||
Name, Path string
|
||||
Unknown uint32
|
||||
}
|
||||
9
pkg/threedb/mesh.go
Normal file
9
pkg/threedb/mesh.go
Normal file
@ -0,0 +1,9 @@
|
||||
package threedb
|
||||
|
||||
type Mesh struct {
|
||||
Links []MeshLink
|
||||
Vector1 *Vector
|
||||
Vector2 *Vector
|
||||
Shadow uint16
|
||||
CMap uint16
|
||||
}
|
||||
10
pkg/threedb/meshlink.go
Normal file
10
pkg/threedb/meshlink.go
Normal file
@ -0,0 +1,10 @@
|
||||
package threedb
|
||||
|
||||
type MeshLink struct {
|
||||
Material uint16
|
||||
Triangles uint16
|
||||
TextureCoordinates uint16
|
||||
Points uint16
|
||||
Brightness uint16
|
||||
Unknown uint16
|
||||
}
|
||||
58
pkg/threedb/model.go
Normal file
58
pkg/threedb/model.go
Normal file
@ -0,0 +1,58 @@
|
||||
package threedb
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type Model struct {
|
||||
DBVersion, Name string
|
||||
Materials []Material
|
||||
Meshes []Mesh
|
||||
Objects map[string][]uint32
|
||||
Animations []Animation
|
||||
Triangles [][]uint16
|
||||
TextureCoordinates [][]Coordinate
|
||||
Points [][]Vector
|
||||
Brightness [][]byte
|
||||
|
||||
view ModelView
|
||||
}
|
||||
|
||||
type ModelView interface {
|
||||
Prepare(*Model)
|
||||
}
|
||||
|
||||
func (m Model) MarshalJSON() ([]byte, error) {
|
||||
if m.view == nil {
|
||||
m.view = &DefaultModelView{}
|
||||
}
|
||||
m.view.Prepare(&m)
|
||||
return json.Marshal(m.view)
|
||||
}
|
||||
|
||||
type DefaultModelView struct {
|
||||
DBVersion, Name string
|
||||
MeshesCount int
|
||||
Meshes []int
|
||||
FirstMesh Mesh
|
||||
Materials []Material
|
||||
Animations []Animation
|
||||
Triangles int
|
||||
TextureCoordinates int
|
||||
Points int
|
||||
Brightness int
|
||||
}
|
||||
|
||||
func (v *DefaultModelView) Prepare(m *Model) {
|
||||
v.DBVersion = m.DBVersion
|
||||
v.Name = m.Name
|
||||
v.MeshesCount = len(m.Meshes)
|
||||
for _, mesh := range m.Meshes {
|
||||
v.Meshes = append(v.Meshes, len(mesh.Links))
|
||||
}
|
||||
v.FirstMesh = m.Meshes[0]
|
||||
v.Materials = m.Materials
|
||||
v.Animations = m.Animations
|
||||
v.Triangles = len(m.Triangles)
|
||||
v.TextureCoordinates = len(m.TextureCoordinates)
|
||||
v.Points = len(m.Points)
|
||||
v.Brightness = len(m.Brightness)
|
||||
}
|
||||
32
pkg/threedb/vector.go
Normal file
32
pkg/threedb/vector.go
Normal file
@ -0,0 +1,32 @@
|
||||
package threedb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
)
|
||||
|
||||
type Vector [3]float32
|
||||
|
||||
func (m Vector) X() float32 { return m[0] }
|
||||
func (m Vector) Y() float32 { return m[1] }
|
||||
func (m Vector) Z() float32 { return m[2] }
|
||||
func (m *Vector) Set(x, y, z float32) { m[0], m[1], m[2] = x, y, z }
|
||||
func (m *Vector) SetX(x float32) { m[0] = x }
|
||||
func (m *Vector) SetY(y float32) { m[1] = y }
|
||||
func (m *Vector) SetZ(z float32) { m[2] = z }
|
||||
func (m Vector) String() string { return fmt.Sprintf("x:%f,y:%f,z:%f", m[0], m[1], m[2]) }
|
||||
|
||||
func (m Vector) Transform(scale float32) Vector {
|
||||
return Vector{(m[0] - .5) * scale, (m[1] - .5) * scale, (m[2] - .5) * scale}
|
||||
}
|
||||
|
||||
func (m Vector) Normalize() Vector {
|
||||
return Vector{m[0] * -1, m[1] * -1, m[2]}
|
||||
}
|
||||
|
||||
func (m Vector) Min() float32 { return slices.Min([]float32{m[0], m[1], m[2]}) }
|
||||
func (m Vector) Max() float32 { return slices.Max([]float32{m[0], m[1], m[2]}) }
|
||||
|
||||
func (m Vector) MarshalText() ([]byte, error) {
|
||||
return []byte(m.String()), nil
|
||||
}
|
||||
17
pkg/threedb/vector_test.go
Normal file
17
pkg/threedb/vector_test.go
Normal file
@ -0,0 +1,17 @@
|
||||
package threedb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVector_Array(t *testing.T) {
|
||||
var vecs [][3]float32
|
||||
vecs = append(vecs, Vector{1.0, 2.0, 3.0})
|
||||
vecs = append(vecs, Vector{1.0, 2.0, 3.0})
|
||||
vecs = append(vecs, Vector{1.0, 2.0, 3.0})
|
||||
vecs = append(vecs, Vector{1.0, 2.0, 3.0})
|
||||
fmt.Println(vecs)
|
||||
vec := Vector{1.0, 2.0, 3.0}
|
||||
fmt.Println([3]float32(vec))
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user