diff --git a/go.mod b/go.mod index 0dad11c..b8ed196 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.22.2 require ( github.com/disintegration/imaging v1.6.2 github.com/dustin/go-humanize v1.0.1 + github.com/fogleman/fauxgl v0.0.0-20200818143847-27cddc103802 github.com/gen2brain/go-fitz v1.23.7 github.com/gofiber/fiber/v2 v2.52.4 github.com/u2takey/ffmpeg-go v0.5.0 @@ -13,6 +14,7 @@ require ( require ( github.com/andybalholm/brotli v1.0.5 // indirect github.com/aws/aws-sdk-go v1.38.20 // indirect + github.com/fogleman/simplify v0.0.0-20170216171241-d32f302d5046 // indirect github.com/google/uuid v1.5.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/klauspost/compress v1.17.0 // indirect diff --git a/go.sum b/go.sum index 34e23f9..de1d37e 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,10 @@ github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1 github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/fogleman/fauxgl v0.0.0-20200818143847-27cddc103802 h1:5vdq0jOnV15v1NdZbAcU+dIJ22rFgwaieiFewPvnKCA= +github.com/fogleman/fauxgl v0.0.0-20200818143847-27cddc103802/go.mod h1:7f7F8EvO8MWvDx9sIoloOfZBCKzlWuZV/h3TjpXOO3k= +github.com/fogleman/simplify v0.0.0-20170216171241-d32f302d5046 h1:n3RPbpwXSFT0G8FYslzMUBDO09Ix8/dlqzvUkcJm4Jk= +github.com/fogleman/simplify v0.0.0-20170216171241-d32f302d5046/go.mod h1:KDwyDqFmVUxUmo7tmqXtyaaJMdGon06y8BD2jmh84CQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gen2brain/go-fitz v1.23.7 h1:HPhzEVzmOINvCKqQgB/DwMzYh4ArIgy3tMwq1eJTcbg= github.com/gen2brain/go-fitz v1.23.7/go.mod h1:HU04vc+RisUh/kvEd2pB0LAxmK1oyXdN4ftyshUr9rQ= diff --git a/thumbnail/thumbnail.go b/thumbnail/thumbnail.go index 0285307..9de29c9 100644 --- a/thumbnail/thumbnail.go +++ b/thumbnail/thumbnail.go @@ -13,12 +13,13 @@ import ( "sync" "github.com/disintegration/imaging" + "github.com/fogleman/fauxgl" "github.com/gen2brain/go-fitz" "github.com/gofiber/fiber/v2" ffmpeg "github.com/u2takey/ffmpeg-go" ) -var SupportedFileTypes = []string{".png", ".PNG", ".jpg", ".JPG", ".jpeg", ".JPEG", ".webp", ".webP", ".WEBP", ".pdf", ".PDF", ".mp4", ".MP4", ".webm", ".WEBM", ".mkv", ".MKV"} +var SupportedFileTypes = []string{".png", ".PNG", ".jpg", ".JPG", ".jpeg", ".JPEG", ".webp", ".webP", ".WEBP", ".pdf", ".PDF", ".mp4", ".MP4", ".webm", ".WEBM", ".mkv", ".MKV", ".obj", ".OBJ", ".stl", ".STL", ".ply", ".PLY", ".3ds", ".3DS"} var FileTypesMap = map[string]imaging.Format{ ".png": imaging.PNG, ".PNG": imaging.PNG, @@ -36,6 +37,14 @@ var FileTypesMap = map[string]imaging.Format{ ".WEBM": imaging.PNG, ".mkv": imaging.PNG, ".MKV": imaging.PNG, + ".obj": imaging.PNG, + ".OBJ": imaging.PNG, + ".stl": imaging.PNG, + ".STL": imaging.PNG, + ".ply": imaging.PNG, + ".PLY": imaging.PNG, + ".3ds": imaging.PNG, + ".3DS": imaging.PNG, } var FFMPEGFormats = []string{ @@ -50,7 +59,19 @@ var FFMPEGFormats = []string{ ".MKV", } +var FauxGLFormats = []string{ + ".obj", + ".OBJ", + ".stl", + ".STL", + ".ply", + ".PLY", + ".3ds", + ".3DS", +} + var thumbnailSize = 48 +var AA = 2 var thumbnailCache = map[string][]byte{} var thumbnailCacheMutex = &sync.RWMutex{} @@ -147,6 +168,45 @@ func GetThumbnail(c *fiber.Ctx, completePath string) { Free() return } + } else if slices.Contains(FauxGLFormats, fileExt) { + // load a mesh + var mesh *fauxgl.Mesh + if fileExt == ".obj" || fileExt == ".OBJ" { + mesh, err = fauxgl.LoadOBJ(completePath) + } else if fileExt == ".stl" || fileExt == ".STL" { + mesh, err = fauxgl.LoadSTL(completePath) + } else if fileExt == ".ply" || fileExt == ".PLY" { + mesh, err = fauxgl.LoadPLY(completePath) + } else { + mesh, err = fauxgl.Load3DS(completePath) + } + if err != nil { + Free() + return + } + + // fit mesh in a bi-unit cube centered at the origin + mesh.BiUnitCube() + + // smooth the normals + mesh.SmoothNormalsThreshold(fauxgl.Radians(30)) + + // create a rendering context + context := fauxgl.NewContext(thumbnailSize*AA, thumbnailSize*AA) + /*context.ClearColorBufferWith(fauxgl.HexColor("#FFF8E3"))*/ + + // create transformation matrix and light direction + aspect := 1. + matrix := fauxgl.LookAt(fauxgl.V(-4, 1.5, -2) /* eye */, fauxgl.V(0, -0.07, 0) /* center */, fauxgl.V(0, 1, 0) /* up */).Perspective(33 /*fovy*/, aspect, 1 /* near */, 30 /* far */) + + // use builtin phong shader + shader := fauxgl.NewPhongShader(matrix, fauxgl.V(-0.75, 1, 0.25).Normalize() /* light */, fauxgl.V(-4, 1.5, -2) /* eye */) + shader.ObjectColor = fauxgl.HexColor("#9e5272") + context.Shader = shader + + // render + context.DrawMesh(mesh) + img = context.Image() } else { img, _, err = image.Decode(f) if err != nil {