@certe/atmos-editor 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -91,4 +91,331 @@ if (!updatedPkg.scripts.build) updatedPkg.scripts.build = 'vite build';
91
91
  if (!updatedPkg.scripts.preview) updatedPkg.scripts.preview = 'vite preview';
92
92
  fs.writeFileSync(pkgPath, JSON.stringify(updatedPkg, null, 2) + '\n');
93
93
 
94
+ // 7. Generate README.md with engine guide
95
+ const readme = `# Atmos Game Project
96
+
97
+ ## Quick Start
98
+
99
+ \`\`\`bash
100
+ npm run dev # Start editor at localhost:5173
101
+ npm run build # Build standalone game (no editor)
102
+ npm run preview # Preview built game
103
+ \`\`\`
104
+
105
+ ## Project Structure
106
+
107
+ \`\`\`
108
+ src/
109
+ main.ts # Editor bootstrap (DO NOT add game logic here)
110
+ scripts/ # Game scripts — all gameplay code goes here
111
+ MyScript.ts
112
+ scenes/
113
+ main.scene.json # Scene files (created/saved from editor)
114
+ materials/
115
+ default.mat.json # Material assets
116
+ textures/ # Image textures (.jpg, .png)
117
+ models/ # 3D models (.glb)
118
+ project-settings.json # Editor settings (default scene, physics)
119
+ vite.config.ts # Vite + Atmos plugin config
120
+ \`\`\`
121
+
122
+ ## Imports — What Comes From Where
123
+
124
+ \`\`\`typescript
125
+ // Core — components, scene, engine, input
126
+ import { Component, GameObject, Scene, Transform, Input, Engine, Time } from '@certe/atmos-core';
127
+ import { serializeScene, deserializeScene } from '@certe/atmos-core';
128
+
129
+ // Math — vectors, quaternions, matrices, rays
130
+ import { Vec3, Quat, Mat4, Ray } from '@certe/atmos-math';
131
+
132
+ // Renderer — camera, lights, meshes, materials
133
+ import { Camera, MeshRenderer } from '@certe/atmos-renderer';
134
+ import { DirectionalLight, PointLight, SpotLight } from '@certe/atmos-renderer';
135
+ import { createMaterial, createMesh } from '@certe/atmos-renderer';
136
+ import { createCubeGeometry, createSphereGeometry, createPlaneGeometry, createCylinderGeometry } from '@certe/atmos-renderer';
137
+
138
+ // Physics — rigid bodies, colliders, joints, queries
139
+ import { RigidBody, Collider, Physics } from '@certe/atmos-physics';
140
+ import { HingeJoint, FixedJoint, SpringJoint } from '@certe/atmos-physics';
141
+
142
+ // Animation — skeletal animation
143
+ import { AnimationMixer } from '@certe/atmos-animation';
144
+ \`\`\`
145
+
146
+ ## Writing Game Scripts
147
+
148
+ All game logic lives in \`src/scripts/\`. Each file exports Component subclasses
149
+ that are auto-discovered by the editor. **Never put game logic in main.ts.**
150
+
151
+ \`\`\`typescript
152
+ import { Component } from '@certe/atmos-core';
153
+ import { Vec3, Quat } from '@certe/atmos-math';
154
+
155
+ export class Rotator extends Component {
156
+ speed = 2;
157
+
158
+ // Expose properties to the editor inspector
159
+ static editorProperties = [
160
+ { key: 'speed', type: 'number' as const, min: 0, max: 20, step: 0.1 },
161
+ ];
162
+
163
+ onUpdate(dt: number) {
164
+ const q = this.transform.rotation;
165
+ const rot = Quat.create();
166
+ Quat.fromAxisAngle(rot, Vec3.fromValues(0, 1, 0), this.speed * dt);
167
+ Quat.multiply(q, rot, q);
168
+ this.transform.setRotationFrom(q);
169
+ }
170
+ }
171
+ \`\`\`
172
+
173
+ ### Component Lifecycle
174
+
175
+ \`\`\`
176
+ onAwake() — Called once when scene starts
177
+ onStart() — Called once after all awake calls
178
+ onUpdate(dt) — Called every frame (dt = seconds since last frame)
179
+ onRender() — Called after update, before GPU rendering
180
+ onDestroy() — Called when component or game object is removed
181
+ \`\`\`
182
+
183
+ ### Accessing Other Components
184
+
185
+ \`\`\`typescript
186
+ // Get sibling component on same GameObject
187
+ const rb = this.getComponent(RigidBody);
188
+
189
+ // Search entire scene
190
+ const cameras = Component.findAll(Camera);
191
+ \`\`\`
192
+
193
+ ### Input
194
+
195
+ \`\`\`typescript
196
+ import { Input } from '@certe/atmos-core';
197
+
198
+ onUpdate(dt: number) {
199
+ const input = Input.current!;
200
+
201
+ if (input.getKey('KeyW')) // Held down
202
+ if (input.getKeyDown('Space')) // Just pressed this frame
203
+ if (input.getKeyUp('KeyE')) // Just released this frame
204
+
205
+ if (input.getMouseButton(0)) // Left mouse held
206
+ const { x, y } = input.mouseDelta; // Mouse movement
207
+ }
208
+ \`\`\`
209
+
210
+ Key codes: \`KeyW\`, \`KeyA\`, \`KeyS\`, \`KeyD\`, \`Space\`, \`ShiftLeft\`, \`ArrowUp\`,
211
+ \`ArrowDown\`, \`ArrowLeft\`, \`ArrowRight\`, \`Digit1\`–\`Digit9\`, \`KeyE\`, \`KeyQ\`, etc.
212
+
213
+ ## Math API
214
+
215
+ All math types are Float32Array. Functions use \`out\` as the first parameter
216
+ (output is written into \`out\` and returned). **No allocations in hot paths.**
217
+
218
+ \`\`\`typescript
219
+ import { Vec3, Quat, Mat4 } from '@certe/atmos-math';
220
+
221
+ // Vec3 — Float32Array[3]
222
+ const v = Vec3.fromValues(1, 2, 3);
223
+ const result = Vec3.create();
224
+ Vec3.add(result, a, b);
225
+ Vec3.sub(result, a, b);
226
+ Vec3.scale(result, v, 2.0);
227
+ Vec3.normalize(result, v);
228
+ Vec3.cross(result, a, b);
229
+ Vec3.lerp(result, a, b, 0.5);
230
+ Vec3.dot(a, b); // returns number
231
+ Vec3.length(v); // returns number
232
+ Vec3.distance(a, b); // returns number
233
+
234
+ // Quat — Float32Array[4] as [x, y, z, w]
235
+ const q = Quat.create(); // identity
236
+ Quat.fromAxisAngle(q, axis, radians);
237
+ Quat.fromEuler(q, xRad, yRad, zRad);
238
+ Quat.multiply(out, a, b);
239
+ Quat.slerp(out, a, b, t);
240
+ Vec3.transformQuat(out, vec, quat); // rotate vector by quaternion
241
+
242
+ // Mat4 — Float32Array[16], column-major
243
+ Mat4.perspective(out, fovY, aspect, near, far);
244
+ Mat4.lookAt(out, eye, center, up);
245
+ Mat4.multiply(out, a, b);
246
+ Mat4.invert(out, a);
247
+ \`\`\`
248
+
249
+ ## Scene Files
250
+
251
+ Scenes are JSON. The editor saves/loads them automatically. You can also
252
+ create GameObjects programmatically in a script's \`onAwake\`, but prefer
253
+ building scenes in the editor and saving to \`scenes/*.scene.json\`.
254
+
255
+ ### Scene JSON format
256
+
257
+ \`\`\`json
258
+ {
259
+ "gameObjects": [
260
+ {
261
+ "name": "Floor",
262
+ "id": 1,
263
+ "parentId": null,
264
+ "components": [
265
+ {
266
+ "type": "Transform",
267
+ "data": { "position": [0, 0, 0], "rotation": [0, 0, 0, 1], "scale": [10, 0.1, 10] }
268
+ },
269
+ {
270
+ "type": "MeshRenderer",
271
+ "data": { "meshSource": "primitive:cube", "materialSource": "materials/default.mat.json" }
272
+ },
273
+ {
274
+ "type": "RigidBody",
275
+ "data": { "bodyType": "fixed" }
276
+ },
277
+ {
278
+ "type": "Collider",
279
+ "data": { "shape": { "type": "box", "halfExtents": { "x": 5, "y": 0.05, "z": 5 } } }
280
+ }
281
+ ]
282
+ }
283
+ ]
284
+ }
285
+ \`\`\`
286
+
287
+ ### Mesh sources (meshSource)
288
+
289
+ - \`"primitive:cube"\` — Unit cube
290
+ - \`"primitive:sphere"\` — Sphere (radius 0.5)
291
+ - \`"primitive:plane"\` — Flat plane (1x1)
292
+ - \`"primitive:cylinder"\` — Cylinder (radius 0.5, height 1)
293
+
294
+ ### Material files (.mat.json)
295
+
296
+ \`\`\`json
297
+ {
298
+ "name": "Metal",
299
+ "shader": "pbr",
300
+ "albedo": [0.8, 0.8, 0.8, 1.0],
301
+ "metallic": 0.9,
302
+ "roughness": 0.2,
303
+ "emissive": [0, 0, 0],
304
+ "emissiveIntensity": 0
305
+ }
306
+ \`\`\`
307
+
308
+ Set \`emissive\` + \`emissiveIntensity\` > 0 for glowing objects (works with bloom).
309
+
310
+ ## Built-in Components
311
+
312
+ ### Rendering
313
+ - **MeshRenderer** — meshSource, materialSource, castShadow, receiveSSAO
314
+ - **Camera** — fovY (radians), near, far, isMainCamera, clearColor
315
+
316
+ ### Lights
317
+ - **DirectionalLight** — color, intensity, castShadows, shadowIntensity
318
+ - **PointLight** — color, intensity, range, castShadows
319
+ - **SpotLight** — color, intensity, range, innerAngle, outerAngle, castShadows
320
+
321
+ ### Physics
322
+ - **RigidBody** — bodyType (dynamic/fixed/kinematic), linearDamping, angularDamping, gravityScale
323
+ - **Collider** — shape (box/sphere/capsule/cylinder), friction, restitution, density, isSensor
324
+ - **HingeJoint** — axis, connectedBody, limits, motor
325
+
326
+ ### Animation
327
+ - **AnimationMixer** — initialClip, speed, loop, autoplay
328
+
329
+ ## Physics
330
+
331
+ \`\`\`typescript
332
+ import { RigidBody, Collider, Physics } from '@certe/atmos-physics';
333
+
334
+ // Apply force
335
+ const rb = this.getComponent(RigidBody);
336
+ rb.body?.applyImpulse({ x: 0, y: 10, z: 0 }, true);
337
+
338
+ // Raycast
339
+ const hit = Physics.raycast(physicsWorld, origin, direction, 100);
340
+ if (hit) {
341
+ console.log(hit.gameObject.name, hit.point, hit.distance);
342
+ }
343
+ \`\`\`
344
+
345
+ ## Editor Properties
346
+
347
+ Expose script fields to the inspector:
348
+
349
+ \`\`\`typescript
350
+ export class Enemy extends Component {
351
+ health = 100;
352
+ speed = 3;
353
+ color = new Float32Array([1, 0, 0]);
354
+ isFlying = false;
355
+ difficulty: string = 'normal';
356
+ target: GameObject | null = null;
357
+
358
+ static editorProperties = [
359
+ { key: 'health', type: 'number' as const, min: 0, max: 1000, step: 10 },
360
+ { key: 'speed', type: 'number' as const, min: 0, max: 20, step: 0.5 },
361
+ { key: 'color', type: 'color' as const },
362
+ { key: 'isFlying', type: 'boolean' as const },
363
+ { key: 'difficulty', type: 'enum' as const, options: ['easy', 'normal', 'hard'] },
364
+ { key: 'target', type: 'gameObjectRef' as const },
365
+ ];
366
+ }
367
+ \`\`\`
368
+
369
+ Property types: \`number\`, \`string\`, \`boolean\`, \`vec3\`, \`quat\`, \`color\`,
370
+ \`enum\`, \`gameObjectRef\`, \`materialAsset\`
371
+
372
+ ## AI Agent Instructions
373
+
374
+ > **WARNING — FILE PLACEMENT IS CRITICAL. Getting this wrong breaks the engine.**
375
+ > The ONLY directory inside \`src/\` is \`src/scripts/\`. Everything else is at the PROJECT ROOT.
376
+ > **NEVER create \`src/scenes/\`, \`src/materials/\`, \`src/textures/\`, or \`src/models/\`.**
377
+
378
+ ### File placement rules
379
+
380
+ | What | Correct path | WRONG — never do this |
381
+ |---|---|---|
382
+ | Game scripts | \`src/scripts/MyScript.ts\` | \`src/MyScript.ts\`, \`main.ts\` |
383
+ | Scene files | \`scenes/main.scene.json\` | \`src/scenes/\`, \`src/main.scene.json\` |
384
+ | Material files | \`materials/metal.mat.json\` | \`src/materials/\`, \`src/metal.mat.json\` |
385
+ | Textures | \`textures/grass.png\` | \`src/textures/\` |
386
+ | Models | \`models/character.glb\` | \`src/models/\` |
387
+
388
+ ### Rules
389
+
390
+ 1. **All game logic goes in \`src/scripts/\`** as Component subclasses
391
+ 2. **Scene files go in \`scenes/*.scene.json\`** at project root — the editor and build system read from this exact path
392
+ 3. **Material files go in \`materials/*.mat.json\`** at project root — reference them in scene JSON and MeshRenderer.materialSource as \`"materials/metal.mat.json"\` (relative to project root)
393
+ 4. **Textures go in \`textures/\`** at project root — reference in .mat.json as \`"textures/grass.png"\`
394
+ 5. **Models go in \`models/\`** at project root
395
+ 6. **main.ts is engine bootstrap only** — never modify it for game logic
396
+ 7. **Use \`static editorProperties\`** so the editor can display and tweak values
397
+ 8. **Math uses out-params** — always \`Vec3.add(out, a, b)\`, never \`a.add(b)\`
398
+ 9. **Scripts are hot-reloaded** — the editor picks up new files in src/scripts/ automatically
399
+ 10. **The default scene is \`scenes/main.scene.json\`** — the editor loads this on startup
400
+
401
+ ### Creating materials
402
+
403
+ Create \`.mat.json\` files in \`materials/\` (project root):
404
+
405
+ materials/floor.mat.json <-- CORRECT
406
+ materials/metal.mat.json <-- CORRECT
407
+ src/materials/floor.mat.json <-- WRONG, engine will NOT find this
408
+
409
+ Then reference them in scene JSON by their project-root-relative path:
410
+
411
+ { "type": "MeshRenderer", "data": { "materialSource": "materials/floor.mat.json" } }
412
+ `;
413
+
414
+ if (!fs.existsSync(path.join(root, 'README.md'))) {
415
+ fs.writeFileSync(path.join(root, 'README.md'), readme);
416
+ console.log('Created README.md');
417
+ } else {
418
+ console.log('README.md already exists, skipping');
419
+ }
420
+
94
421
  console.log('\nDone! Run "npm run dev" to start the editor.');
@@ -1 +1 @@
1
- {"version":3,"file":"project-seed.d.ts","sourceRoot":"","sources":["../src/project-seed.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAczD,wBAAsB,WAAW,CAAC,SAAS,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6B7E"}
1
+ {"version":3,"file":"project-seed.d.ts","sourceRoot":"","sources":["../src/project-seed.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAczD,wBAAsB,WAAW,CAAC,SAAS,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+B7E"}
@@ -24,8 +24,10 @@ export async function seedProject(projectFs) {
24
24
  const json = serializeMaterialAsset(data);
25
25
  writes.push(projectFs.writeFile(`materials/${key}.mat.json`, json));
26
26
  }
27
- // Empty scene
28
- writes.push(projectFs.writeFile('scenes/main.scene.json', '{}'));
27
+ // Empty scene (only if none exists)
28
+ if (!(await projectFs.exists('scenes/main.scene.json'))) {
29
+ writes.push(projectFs.writeFile('scenes/main.scene.json', JSON.stringify({ gameObjects: [] }, null, 2)));
30
+ }
29
31
  // Default project settings
30
32
  if (!(await projectFs.exists('project-settings.json'))) {
31
33
  writes.push(projectFs.writeFile('project-settings.json', JSON.stringify(DEFAULT_PROJECT_SETTINGS, null, 2)));
@@ -1 +1 @@
1
- {"version":3,"file":"project-seed.js","sourceRoot":"","sources":["../src/project-seed.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"project-seed.js","sourceRoot":"","sources":["../src/project-seed.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@certe/atmos-editor",
3
3
  "description": "Browser-based Unity-style editor for the Atmos Engine — hierarchy, inspector, gizmos",
4
- "version": "0.6.0",
4
+ "version": "0.7.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/certesolutions-cyber/atmos.git",
@@ -56,11 +56,11 @@
56
56
  }
57
57
  },
58
58
  "dependencies": {
59
- "@certe/atmos-animation": "^0.6.0",
60
- "@certe/atmos-assets": "^0.6.0",
61
- "@certe/atmos-core": "^0.6.0",
62
- "@certe/atmos-math": "^0.6.0",
63
- "@certe/atmos-renderer": "^0.6.0",
59
+ "@certe/atmos-animation": "^0.7.0",
60
+ "@certe/atmos-assets": "^0.7.0",
61
+ "@certe/atmos-core": "^0.7.0",
62
+ "@certe/atmos-math": "^0.7.0",
63
+ "@certe/atmos-renderer": "^0.7.0",
64
64
  "react": "^19.0.0",
65
65
  "react-dom": "^19.0.0"
66
66
  },
package/vite-plugin.cjs CHANGED
@@ -316,12 +316,14 @@ try {
316
316
  async function handleFsAction(action, absPath, filePath, root, exclude, req, res, server) {
317
317
  switch (action) {
318
318
  case '/read': {
319
+ if (!fs.existsSync(absPath)) { res.statusCode = 404; res.end('Not found'); break; }
319
320
  const data = fs.readFileSync(absPath);
320
321
  res.setHeader('Content-Type', 'application/octet-stream');
321
322
  res.end(data);
322
323
  break;
323
324
  }
324
325
  case '/read-text': {
326
+ if (!fs.existsSync(absPath)) { res.statusCode = 404; res.end('Not found'); break; }
325
327
  const text = fs.readFileSync(absPath, 'utf-8');
326
328
  res.setHeader('Content-Type', 'text/plain; charset=utf-8');
327
329
  res.end(text);
package/vite-plugin.mjs CHANGED
@@ -316,12 +316,14 @@ try {
316
316
  async function handleFsAction(action, absPath, filePath, root, exclude, req, res, server) {
317
317
  switch (action) {
318
318
  case '/read': {
319
+ if (!fs.existsSync(absPath)) { res.statusCode = 404; res.end('Not found'); break; }
319
320
  const data = fs.readFileSync(absPath);
320
321
  res.setHeader('Content-Type', 'application/octet-stream');
321
322
  res.end(data);
322
323
  break;
323
324
  }
324
325
  case '/read-text': {
326
+ if (!fs.existsSync(absPath)) { res.statusCode = 404; res.end('Not found'); break; }
325
327
  const text = fs.readFileSync(absPath, 'utf-8');
326
328
  res.setHeader('Content-Type', 'text/plain; charset=utf-8');
327
329
  res.end(text);