Skip to content

Commit 0c3fd82

Browse files
committed
fix
1 parent 0dc60bc commit 0c3fd82

File tree

17 files changed

+429
-73
lines changed

17 files changed

+429
-73
lines changed

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
<img src="./public/vibe-coder-3d-logo.png" alt="Vibe Coder 3D Logo" width="300"/>
33
</p>
44

5+
> **⚠️ UNDER ACTIVE DEVELOPMENT**
6+
> This project is currently in early development and is **not yet functional**. Features are incomplete, APIs will change, and many systems are still being implemented. Please check the [ROADMAP.md](./ROADMAP.md) for current progress.
7+
58
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
69
[![TypeScript](https://img.shields.io/badge/TypeScript-5.8-blue.svg)](https://www.typescriptlang.org/)
710
[![Rust](https://img.shields.io/badge/Rust-1.75+-orange.svg)](https://www.rust-lang.org/)
@@ -17,8 +20,9 @@ Vibe Coder 3D is an **AI-first game engine** that combines modern 3D rendering w
1720
## Screenshots
1821

1922
<p align="center">
20-
<img src="./public/screenshots/scene.png" alt="Vibe Coder 3D Scene" width="49%"/>
21-
<img src="./public/screenshots/LOD-wireframe.png" alt="LOD Wireframe View" width="49%"/>
23+
<img src="./public/screenshots/scene.png" alt="Vibe Coder 3D Scene" width="32%"/>
24+
<img src="./public/screenshots/LOD-wireframe.png" alt="LOD Wireframe View" width="32%"/>
25+
<img src="./public/screenshots/alien.png" alt="Alien Character" width="32%"/>
2226
</p>
2327

2428
## Features

REDDIT_POST_DRAFT.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
### Title Option A (Direct & Informative):
2+
**I'm building Vibe Coder 3D: An open-source, AI-first game engine with a React/TS editor and a Rust core.**
3+
4+
### Title Option B (Feature-focused):
5+
**Showoff: I made an open-source game engine that you control with natural language ("create a bouncing ball" -> physics sim). Built with React, Rust, and R3F.**
6+
7+
### Title Option C (Engaging & Personal):
8+
**After months of work, I'm releasing the foundations of my AI-first, open-source game engine. Looking for feedback and contributors!**
9+
10+
---
11+
12+
### Body:
13+
14+
Hey everyone!
15+
16+
I'm incredibly excited to share a project I've been pouring my heart into: **Vibe Coder 3D**. It's an AI-first, open-source game engine designed to make 3D game creation more accessible and intuitive through natural language.
17+
18+
The core idea is simple: describe what you want, and let the engine handle the rest.
19+
20+
- **"Create a bouncing ball"** → A sphere with a rigid body and physics material is spawned.
21+
- **"Add a medieval castle"** → A 3D model is sourced from an asset library and placed in the scene.
22+
- **"Make the player jump on spacebar"** → A controller script is generated and attached.
23+
24+
**(I highly recommend including a short screen recording or GIF here showing the editor in action! Visuals get much more engagement.)**
25+
26+
Here's a peek at the editor and a wireframe view of the LOD system:
27+
28+
<p align="center">
29+
<img src="https://raw.githubusercontent.com/jonit-dev/vibe-coder-3d/main/public/screenshots/scene.png" alt="Vibe Coder 3D Scene" width="49%"/>
30+
<img src="https://raw.githubusercontent.com/jonit-dev/vibe-coder-3d/main/public/screenshots/LOD-wireframe.png" alt="LOD Wireframe View" width="49%"/>
31+
</p>
32+
33+
### Key Features:
34+
35+
- **Dual Architecture**: A web-based editor built with **React, TypeScript, and React Three Fiber** for a modern, fast, and familiar development experience. The rendering and physics are powered by a high-performance **native Rust engine**.
36+
- **Full Physics Simulation**: Integrated with Rapier3D for robust collision detection, rigid bodies, and joints.
37+
- **Advanced Scripting**: Use TypeScript in the editor with a simple API (`useUpdate`, `useCollisionEvents`, etc.) to build game logic. The Rust engine also has Lua scripting via `mlua`.
38+
- **Modern Tooling**: It comes with a visual scene editor, component inspector, PBR material editor, prefab browser, and multiple debug tools (colliders, FPS counter, GPU profiling).
39+
- **Comprehensive Docs**: The project is heavily documented (50+ guides and architectural docs) to make it easy for new contributors to jump in.
40+
- **It's Open Source!**: Licensed under MIT, ready for the community to shape its future.
41+
42+
### Current Status & What's Next
43+
44+
The foundation is solid. The core engine, editor, ECS, physics, and scripting systems are all in place. I'm now beginning development on the core AI Copilot system that will power the natural language commands. You can see the complete feature list and progress on the project **[Roadmap](./ROADMAP.md)**.
45+
46+
### How You Can Help
47+
48+
I'm at a point where community feedback and contributions would be invaluable. Whether you're a Rustacean, a React dev, a game designer, or just someone passionate about open-source, I'd love for you to get involved.
49+
50+
- **Check out the GitHub Repo**: [https://github.com/jonit-dev/vibe-coder-3d](https://github.com/jonit-dev/vibe-coder-3d)
51+
- **Star the project!** It helps a ton with visibility.
52+
- **Read the Contribution Guide**: I've set up a `CONTRIBUTING.md` and `WORKFLOW.md` to make getting started as smooth as possible.
53+
- **Join the Discussion**: Have questions or ideas? Head over to the GitHub Discussions.
54+
55+
Thanks for taking the time to read this. I'm really proud of how far it's come and excited about where it's going with the help of a community!
56+
57+
---
58+
59+
**TL;DR:** I'm building an open-source, AI-first game engine with a React/TypeScript/R3F editor and a high-performance Rust core. You build games by describing what you want in plain English. Check out the [repo](https://github.com/jonit-dev/vibe-coder-3d) and let me know what you think!
60+
61+
### Suggested Subreddits:
62+
63+
- `r/gamedev` (A primary target, be sure to follow their "Show-off Saturday" or other relevant rules)
64+
- `r/rust` (They will love the native engine aspect)
65+
- `r/reactjs` (The modern web-based editor is a great hook)
66+
- `r/opensource` (Perfect for a call for contributors)
67+
- `r/programming`
68+
- `r/typescript`
Binary file not shown.

public/screenshots/alien.png

773 KB
Loading

src/core/types/assets.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { z } from 'zod';
22

33
// Core asset metadata interfaces
44
export enum AssetKeys {
5-
NightStalkerModel = 'NightStalkerModel',
6-
// Add other asset keys as needed
5+
// Add asset keys here as needed
6+
// Note: Custom models imported via drag-and-drop don't use this enum
77
}
88

99
export const AssetTypeSchema = z.enum(['gltf', 'fbx', 'obj', 'dae', 'texture', 'audio']);

src/editor/components/panels/ViewportPanel/components/EntityMesh.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,29 @@ export const EntityMesh: React.FC<IEntityMeshProps> = React.memo(
399399
const meshRendererData = meshRendererComponent?.data;
400400
const modelPath = isMeshRendererData(meshRendererData) ? meshRendererData.modelPath : undefined;
401401

402+
// Check if this is a loading placeholder
403+
const isLoadingPlaceholder = modelPath?.startsWith('__loading__:');
404+
const loadingModelName = isLoadingPlaceholder
405+
? modelPath?.replace('__loading__:', '')
406+
: undefined;
407+
408+
// Render loading placeholder while model is being ingested
409+
if (meshType === 'custom' && isLoadingPlaceholder) {
410+
// Dynamic import to avoid circular dependencies
411+
const ModelLoadingPlaceholder = React.lazy(
412+
() =>
413+
import('@/editor/components/shared/ModelLoadingPlaceholder').then((m) => ({
414+
default: m.ModelLoadingPlaceholder,
415+
})),
416+
);
417+
418+
return (
419+
<React.Suspense fallback={null}>
420+
<ModelLoadingPlaceholder entityId={entityId} modelName={loadingModelName} />
421+
</React.Suspense>
422+
);
423+
}
424+
402425
// If it's a custom model with a valid path, render the custom model
403426
if (meshType === 'custom' && modelPath) {
404427
return (

src/editor/components/panels/ViewportPanel/components/ModelDropZone.tsx

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { useToastStore } from '@/core/stores/toastStore';
22
import { useEntityCreation } from '@/editor/hooks/useEntityCreation';
33
import { useModelIngestion } from '@/editor/hooks/useModelIngestion';
4+
import { useComponentRegistry } from '@/core/hooks/useComponentRegistry';
5+
import { KnownComponentTypes } from '@/core/lib/ecs/IComponent';
46
import { Logger } from '@core/lib/logger';
57
import React from 'react';
68

@@ -10,6 +12,7 @@ export const ModelDropZone: React.FC = () => {
1012
const [isDragging, setIsDragging] = React.useState(false);
1113
const { ingest } = useModelIngestion();
1214
const { createCustomModel } = useEntityCreation();
15+
const { updateComponent } = useComponentRegistry();
1316
const toast = useToastStore();
1417

1518
React.useEffect(() => {
@@ -50,17 +53,35 @@ export const ModelDropZone: React.FC = () => {
5053
files.find((f) => f.name.toLowerCase().endsWith('.obj')) ||
5154
files[0];
5255

53-
const loadingToastId = toast.showLoading('Importing Model...', 'Optimizing and generating LODs');
56+
const filename = file.name.split('.')[0];
57+
58+
// Create entity with loading placeholder using special prefix
59+
const loadingEntity = createCustomModel(`__loading__:${filename}`, filename);
60+
logger.info('Created loading placeholder entity', { entityId: loadingEntity.id, filename });
61+
62+
const loadingToastId = toast.showLoading(
63+
'Importing Model...',
64+
`Optimizing ${filename} and generating LODs`
65+
);
66+
5467
try {
68+
// Ingest and optimize (backend waits for all LOD files)
5569
const result = await ingest(file);
56-
const entity = createCustomModel(result.basePath);
70+
71+
logger.info('Model ready, updating entity', {
72+
entityId: loadingEntity.id,
73+
basePath: result.basePath,
74+
});
75+
76+
// Update the mesh renderer with the real model path
77+
updateComponent(loadingEntity.id, KnownComponentTypes.MESH_RENDERER, {
78+
modelPath: result.basePath,
79+
});
5780

5881
toast.removeToast(loadingToastId);
59-
toast.showSuccess(
60-
'Import Complete',
61-
`Created entity ${entity.id} from ${result.name}`,
62-
{ duration: 3000 }
63-
);
82+
toast.showSuccess('Import Complete', `Loaded ${result.name} with LOD variants`, {
83+
duration: 3000,
84+
});
6485
} catch (err) {
6586
const msg = err instanceof Error ? err.message : String(err);
6687
logger.error('Model ingestion failed', { error: msg });
@@ -81,7 +102,7 @@ export const ModelDropZone: React.FC = () => {
81102
document.removeEventListener('dragleave', handleDragLeave);
82103
document.removeEventListener('drop', handleDrop);
83104
};
84-
}, [ingest, createCustomModel, toast]);
105+
}, [ingest, createCustomModel, updateComponent, toast]);
85106

86107
if (!isDragging) return null;
87108

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React, { useRef } from 'react';
2+
import { useFrame } from '@react-three/fiber';
3+
import type { Mesh } from 'three';
4+
5+
interface IModelLoadingPlaceholderProps {
6+
entityId: number;
7+
modelName?: string;
8+
}
9+
10+
/**
11+
* A spinning wireframe box that appears while a custom model is being ingested and optimized.
12+
* Shows a visual loading indicator in the 3D scene.
13+
*/
14+
export const ModelLoadingPlaceholder: React.FC<IModelLoadingPlaceholderProps> = ({
15+
entityId,
16+
modelName,
17+
}) => {
18+
const meshRef = useRef<Mesh>(null);
19+
20+
// Rotate the box on all axes for a nice loading effect
21+
useFrame((_, delta) => {
22+
if (meshRef.current) {
23+
meshRef.current.rotation.x += delta * 1.5;
24+
meshRef.current.rotation.y += delta * 2.0;
25+
meshRef.current.rotation.z += delta * 0.5;
26+
}
27+
});
28+
29+
return (
30+
<group userData={{ entityId }}>
31+
<mesh ref={meshRef} castShadow={false} receiveShadow={false}>
32+
<boxGeometry args={[1, 1, 1]} />
33+
<meshStandardMaterial
34+
color="#4a9eff"
35+
wireframe
36+
opacity={0.8}
37+
transparent
38+
emissive="#4a9eff"
39+
emissiveIntensity={0.5}
40+
/>
41+
</mesh>
42+
{modelName && (
43+
<mesh position={[0, 1.5, 0]}>
44+
<boxGeometry args={[0.1, 0.1, 0.1]} />
45+
<meshBasicMaterial color="#4a9eff" />
46+
</mesh>
47+
)}
48+
</group>
49+
);
50+
};
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)