Compare commits
2 commits
next
...
entities-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ed7041ba6 | ||
|
|
f9a4ccec5c |
3 changed files with 206 additions and 8 deletions
|
|
@ -29,6 +29,19 @@ interface GeoData {
|
|||
skinWeights: number[]
|
||||
}
|
||||
|
||||
interface UVFace {
|
||||
uv: [number, number]
|
||||
}
|
||||
|
||||
interface CubeFaces {
|
||||
north?: UVFace
|
||||
south?: UVFace
|
||||
east?: UVFace
|
||||
west?: UVFace
|
||||
up?: UVFace
|
||||
down?: UVFace
|
||||
}
|
||||
|
||||
interface JsonBone {
|
||||
name: string
|
||||
pivot?: [number, number, number]
|
||||
|
|
@ -42,7 +55,7 @@ interface JsonBone {
|
|||
interface JsonCube {
|
||||
origin: [number, number, number]
|
||||
size: [number, number, number]
|
||||
uv: [number, number]
|
||||
uv?: [number, number] | CubeFaces
|
||||
inflate?: number
|
||||
rotation?: [number, number, number]
|
||||
}
|
||||
|
|
@ -143,6 +156,21 @@ function dot (a: number[], b: number[]): number {
|
|||
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
|
||||
}
|
||||
|
||||
function getFaceUV (cube: JsonCube, face: keyof CubeFaces): [number, number] | undefined {
|
||||
// Handle per-face UV format (new format)
|
||||
if (typeof cube.uv === 'object' && !Array.isArray(cube.uv)) {
|
||||
const faceUV = cube.uv[face.toLowerCase()]
|
||||
if (faceUV?.uv) {
|
||||
return faceUV.uv
|
||||
}
|
||||
}
|
||||
// Handle legacy format (array format)
|
||||
if (Array.isArray(cube.uv)) {
|
||||
return cube.uv
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function addCube (
|
||||
attr: GeoData,
|
||||
boneId: number,
|
||||
|
|
@ -160,27 +188,54 @@ function addCube (
|
|||
cubeRotation.y = -cube.rotation[1] * Math.PI / 180
|
||||
cubeRotation.z = -cube.rotation[2] * Math.PI / 180
|
||||
}
|
||||
|
||||
const faceToDirection: Record<keyof CubeFaces, string> = {
|
||||
up: 'up',
|
||||
down: 'down',
|
||||
north: 'north',
|
||||
south: 'south',
|
||||
east: 'east',
|
||||
west: 'west'
|
||||
}
|
||||
|
||||
for (const { dir, corners, u0, v0, u1, v1 } of Object.values(elemFaces)) {
|
||||
const ndx = Math.floor(attr.positions.length / 3)
|
||||
|
||||
const eastOrWest = dir[0] !== 0
|
||||
|
||||
// Determine which face we're processing based on direction
|
||||
let currentFace: keyof CubeFaces | undefined
|
||||
for (const [face, direction] of Object.entries(faceToDirection)) {
|
||||
if (direction === Object.keys(elemFaces)[Object.values(elemFaces).indexOf({ dir, corners, u0, v0, u1, v1 })]) {
|
||||
currentFace = face
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const faceUvs: number[] = []
|
||||
for (const pos of corners) {
|
||||
let u: number
|
||||
let v: number
|
||||
if (sameTextureForAllFaces) {
|
||||
u = (cube.uv[0] + pos[3] * cube.size[0]) / texWidth
|
||||
v = (cube.uv[1] + pos[4] * cube.size[1]) / texHeight
|
||||
} else {
|
||||
u = (cube.uv[0] + dot(pos[3] ? u1 : u0, cube.size)) / texWidth
|
||||
v = (cube.uv[1] + dot(pos[4] ? v1 : v0, cube.size)) / texHeight
|
||||
|
||||
const uvCoords = currentFace ? getFaceUV(cube, currentFace) : cube.uv
|
||||
if (!uvCoords) {
|
||||
errors.push(`Missing UV coordinates for face ${currentFace || 'unknown'}`)
|
||||
continue
|
||||
}
|
||||
|
||||
if (sameTextureForAllFaces) {
|
||||
u = (uvCoords[0] + pos[3] * cube.size[0]) / texWidth
|
||||
v = (uvCoords[1] + pos[4] * cube.size[1]) / texHeight
|
||||
} else {
|
||||
u = (uvCoords[0] + dot(pos[3] ? u1 : u0, cube.size)) / texWidth
|
||||
v = (uvCoords[1] + dot(pos[4] ? v1 : v0, cube.size)) / texHeight
|
||||
}
|
||||
|
||||
if (isNaN(u) || isNaN(v)) {
|
||||
errors.push(`NaN u: ${u}, v: ${v}`)
|
||||
errors.push(`NaN u: ${u}, v: ${v} for face ${currentFace || 'unknown'}`)
|
||||
continue
|
||||
}
|
||||
if (u < 0 || u > 1 || v < 0 || v > 1) {
|
||||
errors.push(`u: ${u}, v: ${v} out of range`)
|
||||
errors.push(`u: ${u}, v: ${v} out of range for face ${currentFace || 'unknown'}`)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
|||
60
renderer/viewer/lib/entity/models/Arrow.obj
Normal file
60
renderer/viewer/lib/entity/models/Arrow.obj
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Aspose.3D Wavefront OBJ Exporter
|
||||
# Copyright 2004-2024 Aspose Pty Ltd.
|
||||
# File created: 02/12/2025 20:01:28
|
||||
|
||||
mtllib material.lib
|
||||
g Arrow
|
||||
|
||||
#
|
||||
# object Arrow
|
||||
#
|
||||
|
||||
v -160 8.146034E-06 50
|
||||
v 160 8.146034E-06 50
|
||||
v -160 -8.146034E-06 -50
|
||||
v 160 -8.146034E-06 -50
|
||||
v -160 -50 1.1920929E-05
|
||||
v 160 -50 1.1920929E-05
|
||||
v -160 50 -1.1920929E-05
|
||||
v 160 50 -1.1920929E-05
|
||||
v -140 -49.999992 50.000008
|
||||
v -140 50.000008 49.999992
|
||||
v -140 -50.000008 -49.999992
|
||||
v -140 49.999992 -50.000008
|
||||
# 12 vertices
|
||||
|
||||
vn 0 1 -1.6292068E-07
|
||||
vn 0 1 -1.6292068E-07
|
||||
vn 0 1 -1.6292068E-07
|
||||
vn 0 1 -1.6292068E-07
|
||||
vn 0 3.1391647E-07 1
|
||||
vn 0 3.1391647E-07 1
|
||||
vn 0 3.1391647E-07 1
|
||||
vn 0 3.1391647E-07 1
|
||||
vn -1 0 0
|
||||
vn -1 0 0
|
||||
vn -1 0 0
|
||||
vn -1 0 0
|
||||
# 12 vertex normals
|
||||
|
||||
vt 0 0.84375 0
|
||||
vt 0.5 1 0
|
||||
vt 0.5 1 0
|
||||
vt 0.5 0.84375 0
|
||||
vt 0 1 0
|
||||
vt 0.15625 0.84375 0
|
||||
vt 0.15625 0.6875 0
|
||||
vt 0 0.84375 0
|
||||
vt 0.5 0.84375 0
|
||||
vt 0 1 0
|
||||
vt 0 0.6875 0
|
||||
vt 0 0.84375 0
|
||||
# 12 texture coords
|
||||
|
||||
usemtl Arrow
|
||||
s 1
|
||||
f 1/1/1 2/9/2 4/2/3 3/10/4
|
||||
f 5/8/5 6/4/6 8/3/7 7/5/8
|
||||
f 9/11/9 10/7/10 12/6/11 11/12/12
|
||||
#3 polygons
|
||||
|
||||
83
renderer/viewer/lib/entity/testEntities.ts
Normal file
83
renderer/viewer/lib/entity/testEntities.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import { z } from 'zod'
|
||||
import entities from './entities.json'
|
||||
|
||||
// Define Zod schemas matching the TypeScript interfaces
|
||||
const ElemFaceSchema = z.object({
|
||||
dir: z.tuple([z.number(), z.number(), z.number()]),
|
||||
u0: z.tuple([z.number(), z.number(), z.number()]),
|
||||
v0: z.tuple([z.number(), z.number(), z.number()]),
|
||||
u1: z.tuple([z.number(), z.number(), z.number()]),
|
||||
v1: z.tuple([z.number(), z.number(), z.number()]),
|
||||
corners: z.array(z.tuple([z.number(), z.number(), z.number(), z.number(), z.number()]))
|
||||
})
|
||||
|
||||
const UVFaceSchema = z.object({
|
||||
uv: z.tuple([z.number(), z.number()])
|
||||
})
|
||||
|
||||
const CubeFacesSchema = z.object({
|
||||
north: UVFaceSchema.optional(),
|
||||
south: UVFaceSchema.optional(),
|
||||
east: UVFaceSchema.optional(),
|
||||
west: UVFaceSchema.optional(),
|
||||
up: UVFaceSchema.optional(),
|
||||
down: UVFaceSchema.optional()
|
||||
})
|
||||
|
||||
const JsonCubeSchema = z.object({
|
||||
origin: z.tuple([z.number(), z.number(), z.number()]),
|
||||
size: z.tuple([z.number(), z.number(), z.number()]),
|
||||
uv: z.union([
|
||||
z.tuple([z.number(), z.number()]),
|
||||
z.object({
|
||||
north: z.object({ uv: z.tuple([z.number(), z.number()]) }).optional(),
|
||||
south: z.object({ uv: z.tuple([z.number(), z.number()]) }).optional(),
|
||||
east: z.object({ uv: z.tuple([z.number(), z.number()]) }).optional(),
|
||||
west: z.object({ uv: z.tuple([z.number(), z.number()]) }).optional(),
|
||||
up: z.object({ uv: z.tuple([z.number(), z.number()]) }).optional(),
|
||||
down: z.object({ uv: z.tuple([z.number(), z.number()]) }).optional()
|
||||
})
|
||||
]).optional(),
|
||||
inflate: z.number().optional(),
|
||||
rotation: z.tuple([z.number(), z.number(), z.number()]).optional()
|
||||
})
|
||||
|
||||
const JsonBoneSchema = z.object({
|
||||
name: z.string(),
|
||||
pivot: z.tuple([z.number(), z.number(), z.number()]).optional(),
|
||||
bind_pose_rotation: z.tuple([z.number(), z.number(), z.number()]).optional(),
|
||||
rotation: z.tuple([z.number(), z.number(), z.number()]).optional(),
|
||||
parent: z.string().optional(),
|
||||
cubes: z.array(JsonCubeSchema).optional(),
|
||||
mirror: z.boolean().optional()
|
||||
})
|
||||
|
||||
const JsonModelSchema = z.object({
|
||||
texturewidth: z.number().optional(),
|
||||
textureheight: z.number().optional(),
|
||||
bones: z.array(JsonBoneSchema)
|
||||
})
|
||||
|
||||
const EntityGeometrySchema = z.record(JsonModelSchema)
|
||||
|
||||
const EntitiesSchema = z.record(z.object({
|
||||
geometry: EntityGeometrySchema,
|
||||
textures: z.record(z.string())
|
||||
}))
|
||||
|
||||
// Validate entities.json against schema
|
||||
let validatedEntities
|
||||
try {
|
||||
validatedEntities = EntitiesSchema.parse(entities)
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
console.error('Validation errors:')
|
||||
for (const err of error.errors) {
|
||||
console.error(`- Path: ${err.path.join('.')}`)
|
||||
console.error(` Error: ${err.message}`)
|
||||
}
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
export default validatedEntities
|
||||
Loading…
Add table
Add a link
Reference in a new issue