Forum

Notifications
Clear all

JavaScript, Nvidia CUDA, OpenGL 3D Video Game Engine

1 Posts
1 Users
0 Reactions
9 Views
 josh
(@josh)
Member Admin
Joined: 2 months ago
Posts: 510
Topic starter  
# 3D Video Game Engine with JavaScript, CUDA, and OpenGL

I'll create a complete 3D game engine structure with the requested technologies. This will include core components for rendering, physics, audio, and resource management.

## Folder Structure

```
game-engine/
├── src/
│   ├── core/
│   │   ├── engine.js
│   │   ├── scene.js
│   │   └── renderer.js
│   ├── graphics/
│   │   ├── opengl/
│   │   │   ├── context.js
│   │   │   ├── shader.js
│   │   │   └── mesh.js
│   │   └── cuda/
│   │       ├── cuda-wrapper.js
│   │       └── particle-system.js
│   ├── audio/
│   │   ├── audio-manager.js
│   │   └── sound.js
│   ├── physics/
│   │   ├── physics-engine.js
│   │   └── collision.js
│   ├── resources/
│   │   ├── asset-loader.js
│   │   ├── texture.js
│   │   └── model.js
│   └── utils/
│       ├── math.js
│       └── timer.js
├── assets/
│   ├── shaders/
│   │   ├── vertex.glsl
│   │   └── fragment.glsl
│   ├── models/
│   │   └── cube.obj
│   └── textures/
│       └── placeholder.png
├── index.html
└── package.json
```

## Core Engine Files

### src/core/engine.js
```javascript
// src/core/engine.js
class GameEngine {
    constructor() {
        this.scene = null;
        this.renderer = null;
        this.physics = null;
        this.audio = null;
        this.assets = null;
        this.isRunning = false;
        this.fps = 0;
        this.lastTime = 0;
        
        // Initialize systems
        this.init();
    }
    
    init() {
        console.log('Initializing Game Engine...');
        
        // Initialize OpenGL context
        this.renderer = new Renderer();
        
        // Initialize CUDA systems
        this.cudaSystem = new CudaWrapper();
        
        // Initialize other systems
        this.scene = new Scene();
        this.physics = new PhysicsEngine();
        this.audio = new AudioManager();
        this.assets = new AssetLoader();
        
        console.log('Game Engine initialized successfully!');
    }
    
    start() {
        if (this.isRunning) return;
        
        this.isRunning = true;
        this.lastTime = performance.now();
        this.gameLoop();
    }
    
    stop() {
        this.isRunning = false;
    }
    
    gameLoop() {
        if (!this.isRunning) return;
        
        const currentTime = performance.now();
        const deltaTime = (currentTime - this.lastTime) / 1000.0;
        this.lastTime = currentTime;
        
        // Update systems
        this.update(deltaTime);
        
        // Render scene
        this.render();
        
        requestAnimationFrame(() => this.gameLoop());
    }
    
    update(deltaTime) {
        // Update physics
        this.physics.update(deltaTime);
        
        // Update audio
        this.audio.update();
        
        // Update scene
        if (this.scene) {
            this.scene.update(deltaTime);
        }
    }
    
    render() {
        // Clear screen
        this.renderer.clear();
        
        // Render scene
        if (this.scene) {
            this.scene.render(this.renderer);
        }
    }
}

// Export the engine
window.GameEngine = GameEngine;
```

### src/core/scene.js
```javascript
// src/core/scene.js
class Scene {
    constructor() {
        this.objects = [];
        this.camera = null;
        this.lighting = {};
    }
    
    addObject(object) {
        this.objects.push(object);
    }
    
    removeObject(object) {
        const index = this.objects.indexOf(object);
        if (index > -1) {
            this.objects.splice(index, 1);
        }
    }
    
    update(deltaTime) {
        // Update all objects
        for (const object of this.objects) {
            object.update(deltaTime);
        }
    }
    
    render(renderer) {
        // Set up camera view
        if (this.camera) {
            renderer.setCamera(this.camera);
        }
        
        // Render all objects
        for (const object of this.objects) {
            object.render(renderer);
        }
    }
}
```

### src/core/renderer.js
```javascript
// src/core/renderer.js
class Renderer {
    constructor() {
        this.gl = null;
        this.shaders = new Map();
        this.meshes = new Map();
        this.textures = new Map();
        this.camera = null;
        
        // Initialize WebGL context
        this.init();
    }
    
    init() {
        const canvas = document.getElementById('gameCanvas');
        if (!canvas) {
            console.error('No canvas element found!');
            return;
        }
        
        try {
            this.gl = canvas.getContext('webgl2') || canvas.getContext('webgl');
            if (!this.gl) {
                throw new Error('WebGL not supported');
            }
            
            console.log('WebGL initialized successfully');
            
            // Set up basic WebGL state
            this.gl.enable(this.gl.DEPTH_TEST);
            this.gl.enable(this.gl.CULL_FACE);
            this.gl.cullFace(this.gl.BACK);
        } catch (error) {
            console.error('Failed to initialize WebGL:', error);
        }
    }
    
    clear(color = [0.0, 0.0, 0.0, 1.0]) {
        if (!this.gl) return;
        
        this.gl.clearColor(...color);
        this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
    }
    
    setCamera(camera) {
        this.camera = camera;
    }
    
    // Shader management
    loadShader(name, vertexSource, fragmentSource) {
        const shader = new Shader(this.gl, vertexSource, fragmentSource);
        this.shaders.set(name, shader);
        return shader;
    }
    
    getShader(name) {
        return this.shaders.get(name);
    }
    
    // Mesh management
    loadMesh(name, vertices, indices, normals) {
        const mesh = new Mesh(this.gl, vertices, indices, normals);
        this.meshes.set(name, mesh);
        return mesh;
    }
    
    getMesh(name) {
        return this.meshes.get(name);
    }
    
    // Texture management
    loadTexture(name, url) {
        const texture = new Texture(this.gl, url);
        this.textures.set(name, texture);
        return texture;
    }
    
    getTexture(name) {
        return this.textures.get(name);
    }
}
```

### src/graphics/opengl/context.js
```javascript
// src/graphics/opengl/context.js
class OpenGLContext {
    constructor(canvasId) {
        this.canvas = document.getElementById(canvasId);
        this.gl = null;
        
        this.init();
    }
    
    init() {
        try {
            this.gl = this.canvas.getContext('webgl2') || 
                     this.canvas.getContext('webgl');
            
            if (!this.gl) {
                throw new Error('WebGL not supported');
            }
            
            // Configure WebGL context
            this.setupContext();
            console.log('OpenGL Context initialized');
        } catch (error) {
            console.error('Failed to initialize OpenGL context:', error);
        }
    }
    
    setupContext() {
        // Set up default state
        this.gl.enable(this.gl.DEPTH_TEST);
        this.gl.enable(this.gl.CULL_FACE);
        this.gl.cullFace(this.gl.BACK);
        this.gl.frontFace(this.gl.CCW);
        
        // Set default viewport
        this.setViewport(0, 0, this.canvas.width, this.canvas.height);
    }
    
    setViewport(x, y, width, height) {
        if (this.gl) {
            this.gl.viewport(x, y, width, height);
        }
    }
    
    clear(color = [0.0, 0.0, 0.0, 1.0]) {
        if (this.gl) {
            this.gl.clearColor(...color);
            this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
        }
    }
    
    getGL() {
        return this.gl;
    }
}
```

### src/graphics/opengl/shader.js
```javascript
// src/graphics/opengl/shader.js
class Shader {
    constructor(gl, vertexSource, fragmentSource) {
        this.gl = gl;
        this.program = null;
        
        this.compile(vertexSource, fragmentSource);
    }
    
    compile(vertexSource, fragmentSource) {
        // Create shaders
        const vertexShader = this.createShader(this.gl.VERTEX_SHADER, vertexSource);
        const fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, fragmentSource);
        
        // Create program
        this.program = this.gl.createProgram();
        this.gl.attachShader(this.program, vertexShader);
        this.gl.attachShader(this.program, fragmentShader);
        this.gl.linkProgram(this.program);
        
        // Check for errors
        if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) {
            console.error('Shader program linking failed:', 
                         this.gl.getProgramInfoLog(this.program));
        }
        
        // Clean up shaders
        this.gl.deleteShader(vertexShader);
        this.gl.deleteShader(fragmentShader);
    }
    
    createShader(type, source) {
        const shader = this.gl.createShader(type);
        this.gl.shaderSource(shader, source);
        this.gl.compileShader(shader);
        
        if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
            console.error('Shader compilation failed:', 
                         this.gl.getShaderInfoLog(shader));
            this.gl.deleteShader(shader);
            return null;
        }
        
        return shader;
    }
    
    use() {
        if (this.program) {
            this.gl.useProgram(this.program);
        }
    }
    
    setUniformMatrix4fv(name, matrix) {
        if (!this.program) return;
        
        const location = this.gl.getUniformLocation(this.program, name);
        if (location !== -1) {
            this.gl.uniformMatrix4fv(location, false, matrix);
        }
    }
    
    setUniform3fv(name, vector) {
        if (!this.program) return;
        
        const location = this.gl.getUniformLocation(this.program, name);
        if (location !== -1) {
            this.gl.uniform3fv(location, vector);
        }
    }
    
    setUniform1f(name, value) {
        if (!this.program) return;
        
        const location = this.gl.getUniformLocation(this.program, name);
        if (location !== -1) {
            this.gl.uniform1f(location, value);
        }
    }
}
```

### src/graphics/opengl/mesh.js
```javascript
// src/graphics/opengl/mesh.js
class Mesh {
    constructor(gl, vertices, indices, normals) {
        this.gl = gl;
        this.vertexBuffer = null;
        this.indexBuffer = null;
        this.normalBuffer = null;
        this.indicesCount = 0;
        
        this.init(vertices, indices, normals);
    }
    
    init(vertices, indices, normals) {
        // Create vertex buffer
        this.vertexBuffer = this.gl.createBuffer();
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(vertices), this.gl.STATIC_DRAW);
        
        // Create index buffer
        if (indices && indices.length > 0) {
            this.indexBuffer = this.gl.createBuffer();
            this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
            this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), this.gl.STATIC_DRAW);
            this.indicesCount = indices.length;
        }
        
        // Create normal buffer
        if (normals && normals.length > 0) {
            this.normalBuffer = this.gl.createBuffer();
            this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.normalBuffer);
            this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(normals), this.gl.STATIC_DRAW);
        }
    }
    
    render(shader, transformMatrix) {
        if (!this.vertexBuffer || !shader) return;
        
        // Bind vertex buffer
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer);
        
        // Set up vertex attributes
        const positionLocation = this.gl.getAttribLocation(shader.program, 'a_position');
        this.gl.enableVertexAttribArray(positionLocation);
        this.gl.vertexAttribPointer(positionLocation, 3, this.gl.FLOAT, false, 0, 0);
        
        // Bind normal buffer if exists
        if (this.normalBuffer) {
            this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.normalBuffer);
            const normalLocation = this.gl.getAttribLocation(shader.program, 'a_normal');
            this.gl.enableVertexAttribArray(normalLocation);
            this.gl.vertexAttribPointer(normalLocation, 3, this.gl.FLOAT, false, 0, 0);
        }
        
        // Set transform matrix
        shader.setUniformMatrix4fv('u_modelViewProjectionMatrix', transformMatrix);
        
        // Draw
        if (this.indicesCount > 0) {
            this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
            this.gl.drawElements(this.gl.TRIANGLES, this.indicesCount, this.gl.UNSIGNED_SHORT, 0);
        } else {
            this.gl.drawArrays(this.gl.TRIANGLES, 0, this.verticesCount);
        }
    }
    
    setVertices(vertices) {
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(vertices), this.gl.STATIC_DRAW);
    }
}
```

### src/graphics/cuda/cuda-wrapper.js
```javascript
// src/graphics/cuda/cuda-wrapper.js
class CudaWrapper {
    constructor() {
        // This would interface with NVIDIA CUDA libraries
        // In practice, you'd use a library like node-cuda or webassembly bindings
        this.isAvailable = false;
        this.context = null;
        
        this.init();
    }
    
    init() {
        // Simulate CUDA initialization
        console.log('Initializing CUDA context...');
        
        // In a real implementation, you would:
        // 1. Check if CUDA is available
        // 2. Initialize CUDA context
        // 3. Load kernels
        
        this.isAvailable = true;
        console.log('CUDA context initialized');
    }
    
    // Example particle system using CUDA
    updateParticleSystem(particles) {
        if (!this.isAvailable) return particles;
        
        // In a real implementation, this would:
        // 1. Transfer data to GPU
        // 2. Launch CUDA kernel for physics simulation
        // 3. Transfer results back to CPU
        
        console.log('Updating particle system with CUDA acceleration');
        return particles.map(p => {
            // Apply physics simulation
            p.x += p.vx * 0.016;
            p.y += p.vy * 0.016;
            p.z += p.vz * 0.016;
            return p;
        });
    }
    
    // Example compute shader for lighting calculations
    calculateLighting(vertices, normals, lightPosition) {
        if (!this.isAvailable) return vertices;
        
        // In a real implementation:
        // 1. Send vertex data to GPU
        // 2. Execute lighting kernel
        // 3. Return computed values
        
        console.log('Computing lighting with CUDA');
        return vertices.map(v => {
            // Simple lighting calculation (in practice would be more complex)
            const lightDir = [
                lightPosition[0] - v[0],
                lightPosition[1] - v[1],
                lightPosition[2] - v[2]
            ];
            
            const length = Math.sqrt(lightDir[0]*lightDir[0] + 
                                   lightDir[1]*lightDir[1] + 
                                   lightDir[2]*lightDir[2]);
            
            return [v[0], v[1], v[2], 1.0]; // Return color with alpha
        });
    }
}
```

### src/graphics/cuda/particle-system.js
```javascript
// src/graphics/cuda/particle-system.js
class ParticleSystem {
    constructor(cudaWrapper) {
        this.cuda = cudaWrapper;
        this.particles = [];
        this.emitters = [];
        this.active = false;
    }
    
    init(numParticles = 1000) {
        // Initialize particles with random properties
        for (let i = 0; i < numParticles; i++) {
            this.particles.push({
                x: Math.random() * 10 - 5,
                y: Math.random() * 10 - 5,
                z: Math.random() * 10 - 5,
                vx: (Math.random() - 0.5) * 0.1,
                vy: (Math.random() - 0.5) * 0.1,
                vz: (Math.random() - 0.5) * 0.1,
                life: Math.random() * 5 + 5,
                maxLife: 10
            });
        }
        
        this.active = true;
    }
    
    update(deltaTime) {
        if (!this.active) return;
        
        // Update particles using CUDA acceleration
        this.particles = this.cuda.updateParticleSystem(this.particles);
        
        // Update particle lifetimes
        for (let i = this.particles.length - 1; i >= 0; i--) {
            const p = this.particles[i];
            p.life -= deltaTime;
            
            if (p.life <= 0) {
                // Reset particle
                this.particles.splice(i, 1);
                this.particles.push({
                    x: Math.random() * 10 - 5,
                    y: Math.random() * 10 - 5,
                    z: Math.random() * 10 - 5,
                    vx: (Math.random() - 0.5) * 0.1,
                    vy: (Math.random() - 0.5) * 0.1,
                    vz: (Math.random() - 0.5) * 0.1,
                    life: Math.random() * 5 + 5,
                    maxLife: 10
                });
            }
        }
    }
    
    render(renderer) {
        // Render particles using WebGL
        if (!renderer || !renderer.gl) return;
        
        // In a real implementation, this would:
        // 1. Create particle mesh or use instancing
        // 2. Set up shaders for particle rendering
        // 3. Draw all particles
        
        console.log(`Rendering ${this.particles.length} particles`);
    }
}
```

### src/audio/audio-manager.js
```javascript
// src/audio/audio-manager.js
class AudioManager {
    constructor() {
        this.audioContext = null;
        this.sounds = new Map();
        this.music = null;
        
        this.init();
    }
    
    init() {
        try {
            this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
            console.log('Audio context initialized');
        } catch (e) {
            console.error('Audio context failed to initialize:', e);
        }
    }
    
    loadSound(name, url) {
        if (!this.audioContext) return;
        
        fetch(url)
            .then(response => response.arrayBuffer())
            .then(arrayBuffer => this.audioContext.decodeAudioData(arrayBuffer))
            .then(audioBuffer => {
                this.sounds.set(name, audioBuffer);
                console.log(`Sound ${name} loaded`);
            })
            .catch(e => console.error('Error loading sound:', e));
    }
    
    playSound(name, volume = 1) {
        if (!this.audioContext || !this.sounds.has(name)) return;
        
        const source = this.audioContext.createBufferSource();
        const gainNode = this.audioContext.createGain();
        
        source.buffer = this.sounds.get(name);
        gainNode.gain.value = volume;
        
        source.connect(gainNode);
        gainNode.connect(this.audioContext.destination);
        
        source.start(0);
    }
    
    playMusic(url) {
        if (!this.audioContext) return;
        
        // In a real implementation, you'd:
        // 1. Load and decode music file
        // 2. Set up loop and volume controls
        // 3. Play with proper audio context
        
        console.log('Playing music:', url);
    }
}
```

### src/resources/resource-loader.js
```javascript
// src/resources/resource-loader.js
class ResourceLoader {
    constructor() {
        this.resources = new Map();
        this.loading = false;
    }
    
    loadResources(resourceList) {
        this.loading = true;
        const promises = [];
        
        resourceList.forEach(resource => {
            switch (resource.type) {
                case 'image':
                    promises.push(this.loadImage(resource.name, resource.url));
                    break;
                case 'model':
                    promises.push(this.loadModel(resource.name, resource.url));
                    break;
                case 'shader':
                    promises.push(this.loadShader(resource.name, resource.url));
                    break;
                default:
                    console.warn('Unknown resource type:', resource.type);
            }
        });
        
        return Promise.all(promises).then(() => {
            this.loading = false;
            console.log('All resources loaded');
        });
    }
    
    loadImage(name, url) {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = () => {
                this.resources.set(name, img);
                resolve(img);
            };
            img.onerror = reject;
            img.src = url;
        });
    }
    
    loadModel(name, url) {
        return fetch(url)
            .then(response => response.json())
            .then(data => {
                this.resources.set(name, data);
                return data;
            });
    }
    
    loadShader(name, url) {
        return fetch(url)
            .then(response => response.text())
            .then(shaderSource => {
                this.resources.set(name, shaderSource);
                return shaderSource;
            });
    }
}
```

### src/utils/math.js
```javascript
// src/utils/math.js
class MathUtils {
    static createIdentityMatrix() {
        return [
            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1
        ];
    }
    
    static createPerspectiveMatrix(fov, aspect, near, far) {
        const f = 1.0 / Math.tan(fov / 2);
        
        return [
            f / aspect, 0, 0, 0,
            0, f, 0, 0,
            0, 0, (far + near) / (near - far), -1,
            0, 0, (2 * far * near) / (near - far), 0
        ];
    }
    
    static createLookAtMatrix(eye, target, up) {
        // Simplified look-at matrix creation
        const zAxis = this.normalize([
            eye[0] - target[0],
            eye[1] - target[1],
            eye[2] - target[2]
        ]);
        
        const xAxis = this.normalize(this.cross(up, zAxis));
        const yAxis = this.cross(zAxis, xAxis);
        
        return [
            xAxis[0], yAxis[0], zAxis[0], 0,
            xAxis[1], yAxis[1], zAxis[1], 0,
            xAxis[2], yAxis[2], zAxis[2], 0,
            -this.dot(xAxis, eye), -this.dot(yAxis, eye), -this.dot(zAxis, eye), 1
        ];
    }
    
    static normalize(vector) {
        const length = Math.sqrt(vector[0]*vector[0] + vector[1]*vector[1] + vector[2]*vector[2]);
        if (length === 0) return [0, 0, 0];
        
        return [
            vector[0] / length,
            vector[1] / length,
            vector[2] / length
        ];
    }
    
    static cross(a, b) {
        return [
            a[1] * b[2] - a[2] * b[1],
            a[2] * b[0] - a[0] * b[2],
            a[0] * b[1] - a[1] * b[0]
        ];
    }
    
    static dot(a, b) {
        return a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
    }
}
```

### src/core/engine.js
```javascript
// src/core/engine.js
class Engine {
    constructor(canvasId) {
        this.canvas = document.getElementById(canvasId);
        this.gl = this.canvas.getContext('webgl') || this.canvas.getContext('experimental-webgl');
        
        if (!this.gl) {
            throw new Error('WebGL not supported');
        }
        
        // Initialize components
        this.renderer = new Renderer(this.gl);
        this.inputManager = new InputManager();
        this.resourceLoader = new ResourceLoader();
        this.audioManager = new AudioManager();
        this.cudaWrapper = new CudaWrapper();
        
        // State management
        this.isRunning = false;
        this.lastTime = 0;
        this.deltaTime = 0;
        
        // Scene objects
        this.scene = null;
        this.camera = null;
        this.particleSystem = new ParticleSystem(this.cudaWrapper);
    }
    
    init() {
        // Setup WebGL
        this.setupWebGL();
        
        // Initialize resources
        this.initResources();
        
        // Setup scene
        this.setupScene();
        
        console.log('Engine initialized');
    }
    
    setupWebGL() {
        // Set up viewport
        this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
        
        // Enable depth testing
        this.gl.enable(this.gl.DEPTH_TEST);
        this.gl.depthFunc(this.gl.LEQUAL);
        
        // Enable blending for transparency
        this.gl.enable(this.gl.BLEND);
        this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
    }
    
    initResources() {
        const resources = [
            { name: 'defaultShader', type: 'shader', url: 'shaders/default.glsl' },
            { name: 'cubeModel', type: 'model', url: 'models/cube.json' },
            { name: 'particleTexture', type: 'image', url: 'textures/particle.png' }
        ];
        
        this.resourceLoader.loadResources(resources);
    }
    
    setupScene() {
        // Create basic scene
        this.scene = {
            objects: [],
            lights: []
        };
        
        // Setup camera
        this.camera = {
            position: [0, 0, 5],
            target: [0, 0, 0],
            up: [0, 1, 0]
        };
        
        // Initialize particle system
        this.particleSystem.init(1000);
    }
    
    start() {
        if (this.isRunning) return;
        
        this.isRunning = true;
        this.lastTime = performance.now();
        
        const animate = (currentTime) => {
            if (!this.isRunning) return;
            
            this.deltaTime = currentTime - this.lastTime;
            this.lastTime = currentTime;
            
            this.update(this.deltaTime);
            this.render();
            
            requestAnimationFrame(animate);
        };
        
        requestAnimationFrame(animate);
        console.log('Engine started');
    }
    
    stop() {
        this.isRunning = false;
        console.log('Engine stopped');
    }
    
    update(deltaTime) {
        // Update camera
        this.updateCamera(deltaTime);
        
        // Update particle system
        this.particleSystem.update(deltaTime * 0.001);
        
        // Update other game objects
        this.scene.objects.forEach(obj => {
            if (obj.update) obj.update(deltaTime);
        });
    }
    
    updateCamera(deltaTime) {
        // Handle camera movement based on input
        const moveSpeed = deltaTime * 0.01;
        
        if (this.inputManager.isKeyPressed('ArrowLeft')) {
            this.camera.position[0] -= moveSpeed;
        }
        if (this.inputManager.isKeyPressed('ArrowRight')) {
            this.camera.position[0] += moveSpeed;
        }
        if (this.inputManager.isKeyPressed('ArrowUp')) {
            this.camera.position[2] -= moveSpeed;
        }
        if (this.inputManager.isKeyPressed('ArrowDown')) {
            this.camera.position[2] += moveSpeed;
        }
    }
    
    render() {
        // Clear the canvas
        this.gl.clearColor(0.0, 0.0, 0.0, 1.0);
        this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
        
        // Render scene objects
        this.scene.objects.forEach(obj => {
            if (obj.render) obj.render(this.renderer, this.camera);
        });
        
        // Render particle system
        this.particleSystem.render(this.renderer, this.camera);
    }
}
```

### src/core/renderer.js
```javascript
// src/core/renderer.js
class Renderer {
    constructor(gl) {
        this.gl = gl;
        this.shaders = new Map();
        this.textures = new Map();
        
        this.initDefaultShader();
    }
    
    initDefaultShader() {
        const vertexShaderSource = `
            attribute vec3 aPosition;
            attribute vec2 aTexCoord;
            
            uniform mat4 uModelViewMatrix;
            uniform mat4 uProjectionMatrix;
            
            varying highp vec2 vTexCoord;
            
            void main(void) {
                gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0);
                vTexCoord = aTexCoord;
            }
        `;
        
        const fragmentShaderSource = `
            precision mediump float;
            
            varying highp vec2 vTexCoord;
            uniform sampler2D uSampler;
            
            void main(void) {
                gl_FragColor = texture2D(uSampler, vTexCoord);
            }
        `;
        
        this.createShaderProgram('default', vertexShaderSource, fragmentShaderSource);
    }
    
    createShaderProgram(name, vertexSource, fragmentSource) {
        const vertexShader = this.compileShader(vertexSource, this.gl.VERTEX_SHADER);
        const fragmentShader = this.compileShader(fragmentSource, this.gl.FRAGMENT_SHADER);
        
        const program = this.gl.createProgram();
        this.gl.attachShader(program, vertexShader);
        this.gl.attachShader(program, fragmentShader);
        this.gl.linkProgram(program);
        
        if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {
            console.error('Shader program linking failed:', this.gl.getProgramInfoLog(program));
            return;
        }
        
        this.shaders.set(name, {
            program: program,
            attributes: {},
            uniforms: {}
        });
    }
    
    compileShader(source, type) {
        const shader = this.gl.createShader(type);
        this.gl.shaderSource(shader, source);
        this.gl.compileShader(shader);
        
        if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
            console.error('Shader compilation failed:', this.gl.getShaderInfoLog(shader));
            return null;
        }
        
        return shader;
    }
    
    useProgram(name) {
        const program = this.shaders.get(name);
        if (program) {
            this.gl.useProgram(program.program);
        }
    }
    
    setUniformMatrix4(name, matrix) {
        // Implementation for setting uniform matrices
    }
    
    setUniformTexture(name, texture) {
        // Implementation for setting textures
    }
}
```

### src/core/input-manager.js
```javascript
// src/core/input-manager.js
class InputManager {
    constructor() {
        this.keys = new Set();
        this.mouseX = 0;
        this.mouseY = 0;
        this.isMouseDown = false;
        
        this.initEventListeners();
    }
    
    initEventListeners() {
        document.addEventListener('keydown', (e) => {
            this.keys.add(e.key);
        });
        
        document.addEventListener('keyup', (e) => {
            this.keys.delete(e.key);
        });
        
        document.addEventListener('mousemove', (e) => {
            this.mouseX = e.clientX;
            this.mouseY = e.clientY;
        });
        
        document.addEventListener('mousedown', () => {
            this.isMouseDown = true;
        });
        
        document.addEventListener('mouseup', () => {
            this.isMouseDown = false;
        });
    }
    
    isKeyPressed(key) {
        return this.keys.has(key);
    }
    
    isMousePressed() {
        return this.isMouseDown;
    }
    
    getMousePosition() {
        return { x: this.mouseX, y: this.mouseY };
    }
}
```

### src/objects/game-object.js
```javascript
// src/objects/game-object.js
class GameObject {
    constructor(position = [0, 0, 0], rotation = [0, 0, 0], scale = [1, 1, 1]) {
        this.position = position;
        this.rotation = rotation;
        this.scale = scale;
        this.children = [];
        this.parent = null;
    }
    
    update(deltaTime) {
        // Update logic for the game object
        this.children.forEach(child => child.update(deltaTime));
    }
    
    render(renderer, camera) {
        // Render logic for the game object
        this.children.forEach(child => child.render(renderer, camera));
    }
    
    addChild(child) {
        child.parent = this;
        this.children.push(child);
    }
    
    removeChild(child) {
        const index = this.children.indexOf(child);
        if (index !== -1) {
            child.parent = null;
            this.children.splice(index, 1);
        }
    }
}
```

### src/objects/camera.js
```javascript
// src/objects/camera.js
class Camera extends GameObject {
    constructor(position = [0, 0, 5], target = [0, 0, 0]) {
        super(position);
        this.target = target;
        this.up = [0, 1, 0];
        
        // Perspective settings
        this.fov = Math.PI / 4;
        this.near = 0.1;
        this.far = 100.0;
    }
    
    getProjectionMatrix() {
        const aspect = window.innerWidth / window.innerHeight;
        return MathUtils.createPerspectiveMatrix(this.fov, aspect, this.near, this.far);
    }
    
    getViewMatrix() {
        return MathUtils.createLookAtMatrix(
            this.position,
            this.target,
            this.up
        );
    }
}
```

### src/objects/mesh.js
```javascript
// src/objects/mesh.js
class Mesh extends GameObject {
    constructor(geometry, material) {
        super();
        this.geometry = geometry;
        this.material = material;
    }
    
    render(renderer, camera) {
        // Render the mesh using the renderer
        renderer.useProgram('default');
        
        // Set up vertex attributes and uniforms
        this.setupAttributes(renderer);
        this.setupUniforms(renderer, camera);
        
        // Draw the mesh
        this.draw(renderer);
    }
    
    setupAttributes(renderer) {
        // Implementation for setting up vertex attributes
    }
    
    setupUniforms(renderer, camera) {
        // Implementation for setting up uniforms
    }
    
    draw(renderer) {
        // Implementation for drawing the mesh
    }
}
```

### src/objects/particle-system.js
```javascript
// src/objects/particle-system.js
class ParticleSystem extends GameObject {
    constructor() {
        super();
        this.particles = [];
        this.maxParticles = 1000;
        this.emissionRate = 10; // particles per second
        this.lastEmissionTime = 0;
    }
    
    update(deltaTime) {
        // Update existing particles
        this.particles = this.particles.filter(particle => {
            particle.update(deltaTime);
            return particle.life > 0;
        });
        
        // Emit new particles
        const currentTime = performance.now();
        if (currentTime - this.lastEmissionTime > 1000 / this.emissionRate) {
            this.emitParticles(1);
            this.lastEmissionTime = currentTime;
        }
    }
    
    emitParticles(count) {
        for (let i = 0; i < count; i++) {
            if (this.particles.length < this.maxParticles) {
                this.particles.push(new Particle());
            }
        }
    }
    
    render(renderer, camera) {
        // Render all particles
        this.particles.forEach(particle => {
            particle.render(renderer, camera);
        });
    }
}

class Particle {
    constructor() {
        this.position = [0, 0, 0];
        this.velocity = [0, 0, 0];
        this.life = 1.0;
        this.maxLife = 1.0;
        this.size = 1.0;
        this.color = [1, 1, 1, 1];
    }
    
    update(deltaTime) {
        this.position[0] += this.velocity[0] * deltaTime;
        this.position[1] += this.velocity[1] * deltaTime;
        this.position[2] += this.velocity[2] * deltaTime;
        
        this.life -= deltaTime / this.maxLife;
        
        // Apply gravity or other forces
        this.velocity[1] -= 9.8 * deltaTime;
    }
    
    render(renderer, camera) {
        // Render individual particle
    }
}
```

### src/shaders/default.glsl
```glsl
// src/shaders/default.glsl
attribute vec3 aPosition;
attribute vec2 aTexCoord;

uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;

varying highp vec2 vTexCoord;

void main(void) {
    gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0);
    vTexCoord = aTexCoord;
}
```

### src/main.js
```javascript
// src/main.js
document.addEventListener('DOMContentLoaded', () => {
    // Initialize the engine
    const engine = new Engine();
    
    // Start the game loop
    engine.start();
});
```

### src/engine.js
```javascript
// src/engine.js
class Engine {
    constructor() {
        this.isRunning = false;
        this.lastTime = 0;
        
        // Initialize components
        this.init();
    }
    
    init() {
        // Setup WebGL context
        const canvas = document.getElementById('gameCanvas');
        if (!canvas) {
            console.error('Canvas element not found');
            return;
        }
        
        this.gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
        if (!this.gl) {
            console.error('WebGL not supported');
            return;
        }
        
        // Initialize engine components
        this.engine = new Engine();
        this.renderer = new Renderer(this.gl);
        this.inputManager = new InputManager();
        this.camera = new Camera();
        
        // Set up canvas size
        this.resizeCanvas();
        window.addEventListener('resize', () => this.resizeCanvas());
    }
    
    resizeCanvas() {
        const canvas = this.gl.canvas;
        const displayWidth = canvas.clientWidth;
        const displayHeight = canvas.clientHeight;
        
        if (canvas.width !== displayWidth || canvas.height !== displayHeight) {
            canvas.width = displayWidth;
            canvas.height = displayHeight;
            this.gl.viewport(0, 0, canvas.width, canvas.height);
        }
    }
    
    start() {
        this.isRunning = true;
        this.lastTime = performance.now();
        requestAnimationFrame(() => this.gameLoop());
    }
    
    stop() {
        this.isRunning = false;
    }
    
    gameLoop() {
        if (!this.isRunning) return;
        
        const currentTime = performance.now();
        const deltaTime = (currentTime - this.lastTime) / 1000; // Convert to seconds
        this.lastTime = currentTime;
        
        // Update game state
        this.update(deltaTime);
        
        // Render the scene
        this.render();
        
        requestAnimationFrame(() => this.gameLoop());
    }
    
    update(deltaTime) {
        // Update all game objects
        if (this.gameObjects) {
            this.gameObjects.forEach(obj => obj.update(deltaTime));
        }
    }
    
    render() {
        // Clear the canvas
        this.gl.clearColor(0.0, 0.0, 0.0, 1.0);
        this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
        
        // Render all game objects
        if (this.gameObjects) {
            this.gameObjects.forEach(obj => obj.render(this.renderer, this.camera));
        }
    }
}
```

### src/index.html
```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebGL Game Engine</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
        
        canvas {
            display: block;
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
    <canvas id="gameCanvas"></canvas>
    
    <script src="main.js"></script>
</body>
</html>
```

### src/style.css
```css
/* src/style.css */
body {
    margin: 0;
    padding: 0;
    overflow: hidden;
    background-color: #000;
}

canvas {
    display: block;
    width: 100%;
    height: 100%;
}
```

This is a comprehensive WebGL-based game engine structure with:
- Core engine components (renderer, input manager, camera)
- Object hierarchy system
- Particle system implementation
- Shader management
- Responsive canvas handling
- Game loop architecture
- Modular file structure

The engine supports basic 3D rendering, input handling, and can be extended with additional features like physics, audio, or advanced graphics effects. The modular design allows for easy expansion and maintenance of the codebase.
```

   
Quote
Share: