Browse Source

implemented morphtargets

Daniel Salvadori 7 years ago
parent
commit
4afe69c400

+ 144 - 0
geometry/morph.go

@@ -0,0 +1,144 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package geometry
+
+import (
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/math32"
+	"strconv"
+	"sort"
+)
+
+// MorphGeometry represents a base geometry and its morph targets.
+type MorphGeometry struct {
+	baseGeometry  *Geometry   // The base geometry
+	targets       []*Geometry // The morph target geometries
+	activeTargets []*Geometry // The morph target geometries
+	weights       []float32   // The weights for each morph target
+	uniWeights    gls.Uniform // Texture unit uniform location cache
+	morphGeom     *Geometry   // Cache of the last CPU-morphed geometry
+}
+
+// NumMorphInfluencers is the maximum number of active morph targets.
+const NumMorphTargets = 8
+
+// NewMorphGeometry creates and returns a pointer to a new MorphGeometry.
+func NewMorphGeometry(baseGeometry *Geometry) *MorphGeometry {
+
+	mg := new(MorphGeometry)
+	mg.baseGeometry = baseGeometry
+
+	mg.targets = make([]*Geometry, 0)
+	mg.activeTargets = make([]*Geometry, 0)
+	mg.weights = make([]float32, NumMorphTargets)
+
+	mg.baseGeometry.ShaderDefines.Set("MORPHTARGETS", strconv.Itoa(NumMorphTargets))
+	mg.uniWeights.Init("morphTargetInfluences")
+	return mg
+}
+
+// GetGeometry satisfies the IGeometry interface.
+func (mg *MorphGeometry) GetGeometry() *Geometry {
+
+	return mg.baseGeometry
+}
+
+// SetWeights sets the morph target weights.
+func (mg *MorphGeometry) SetWeights(weights []float32) {
+
+	mg.weights = weights
+}
+
+// Weights returns the morph target weights.
+func (mg *MorphGeometry) Weights() []float32 {
+
+	return mg.weights
+}
+
+// Weights returns the morph target weights.
+func (mg *MorphGeometry) AddMorphTargets(morphTargets ...*Geometry) {
+
+	log.Error("ADD morph targets")
+	mg.targets = append(mg.targets, morphTargets...)
+}
+
+// ActiveMorphTargets sorts the morph targets by weight and returns the top n morph targets with largest weight.
+func (mg *MorphGeometry) ActiveMorphTargets() []*Geometry {
+
+	numTargets := len(mg.targets)
+	if numTargets == 0 {
+		return nil
+	}
+
+	sortedMorphTargets := make([]*Geometry, numTargets)
+	copy(sortedMorphTargets, mg.targets)
+	sort.Slice(sortedMorphTargets, func(i, j int) bool {
+		return mg.weights[i] > mg.weights[j]
+	})
+
+	// TODO check current 0 weights
+
+	//if len(mg.targets) < NumMorphTargets-1 {
+		return sortedMorphTargets
+	//} else {
+	//	return sortedMorphTargets[:NumMorphTargets-1]
+	//}
+
+}
+
+// SetIndices sets the indices array for this geometry.
+func (mg *MorphGeometry) SetIndices(indices math32.ArrayU32) {
+
+	mg.baseGeometry.SetIndices(indices)
+	for i := range mg.targets {
+		mg.targets[i].SetIndices(indices)
+	}
+}
+
+// ComputeMorphed computes a morphed geometry from the provided morph target weights.
+// Note that morphing is usually computed by the GPU in shaders.
+// This CPU implementation allows users to obtain an instance of a morphed geometry
+// if so desired (loosing morphing ability).
+func (mg *MorphGeometry) ComputeMorphed(weights []float32) *Geometry {
+
+	morphed := NewGeometry()
+	// TODO
+	return morphed
+}
+
+// Dispose releases, if possible, OpenGL resources, C memory
+// and VBOs associated with the base geometry and morph targets.
+func (mg *MorphGeometry) Dispose() {
+
+	mg.baseGeometry.Dispose()
+	for i := range mg.targets {
+		mg.targets[i].Dispose()
+	}
+}
+
+// RenderSetup is called by the renderer before drawing the geometry.
+func (mg *MorphGeometry) RenderSetup(gs *gls.GLS) {
+
+	mg.baseGeometry.RenderSetup(gs)
+
+	// Sort weights and find top 8 morph targets with largest current weight (8 is the max sent to shader)
+	activeMorphTargets := mg.ActiveMorphTargets()
+
+	for i, mt := range activeMorphTargets {
+
+		mt.SetAttributeName(gls.VertexPosition, "MorphPosition"+strconv.Itoa(i))
+		mt.SetAttributeName(gls.VertexNormal, "MorphNormal"+strconv.Itoa(i))
+		//mt.SetAttributeName(vTangent, fmt.Sprintf("MorphTangent[%d]", i))
+
+		// Transfer morphed geometry VBOs
+		for _, vbo := range mt.VBOs() {
+			vbo.Transfer(gs)
+		}
+	}
+
+	// Transfer texture info combined uniform
+	location := mg.uniWeights.Location(gs)
+	gs.Uniform1fv(location, int32(len(activeMorphTargets)), mg.weights)
+}

+ 11 - 4
renderer/renderer.go

@@ -358,19 +358,26 @@ func (r *Renderer) renderScene(iscene core.INode, icam camera.ICamera) error {
 		// For each *GraphicMaterial
 		for _, grmat := range grmats {
 			mat := grmat.GetMaterial().GetMaterial()
+			geom := grmat.GetGraphic().GetGeometry()
+
+			// Add defines from material and geometry
+			r.specs.Defines = *gls.NewShaderDefines()
+			r.specs.Defines.Add(&mat.ShaderDefines)
+			r.specs.Defines.Add(&geom.ShaderDefines)
 
 			// Sets the shader specs for this material and sets shader program
 			r.specs.Name = mat.Shader()
 			r.specs.ShaderUnique = mat.ShaderUnique()
 			r.specs.UseLights = mat.UseLights()
 			r.specs.MatTexturesMax = mat.TextureCount()
-			r.specs.Defines = mat.ShaderDefines
+
+			// Set active program and apply shader specs
 			_, err = r.shaman.SetProgram(&r.specs)
 			if err != nil {
 				return
 			}
 
-			// Setup lights (transfer lights uniforms)
+			// Setup lights (transfer lights' uniforms)
 			for idx, l := range r.ambLights {
 				l.RenderSetup(r.gs, &r.rinfo, idx)
 				r.stats.Lights++
@@ -394,8 +401,8 @@ func (r *Renderer) renderScene(iscene core.INode, icam camera.ICamera) error {
 		}
 	}
 
-	renderGraphicMaterials(r.grmatsOpaque) // Render opaque objects front to back
-	renderGraphicMaterials(r.grmatsTransp) // Render transparent objects back to front
+	renderGraphicMaterials(r.grmatsOpaque) // Render opaque objects (front to back)
+	renderGraphicMaterials(r.grmatsTransp) // Render transparent objects (back to front)
 
 	return err
 }

+ 7 - 0
renderer/shaders/include/morphtarget_vertex.glsl

@@ -0,0 +1,7 @@
+#ifdef MORPHTARGETS
+	vPosition += (MorphPosition{i} - VertexPosition) * morphTargetInfluences[{i}];
+//    vPosition = MorphPosition1;
+  #ifdef MORPHTARGETS_NORMAL
+	vNormal += (MorphNormal{i} - VertexNormal) * morphTargetInfluences[{i}];
+  #endif
+#endif

+ 4 - 0
renderer/shaders/include/morphtarget_vertex_declaration.glsl

@@ -0,0 +1,4 @@
+#ifdef MORPHTARGETS
+	uniform float morphTargetInfluences[MORPHTARGETS];
+	#include <morphtarget_vertex_declaration2> [MORPHTARGETS]
+#endif

+ 4 - 0
renderer/shaders/include/morphtarget_vertex_declaration2.glsl

@@ -0,0 +1,4 @@
+	in vec3 MorphPosition{i};
+  #ifdef MORPHTARGETS_NORMAL
+	in vec3 MorphNormal{i};
+  #endif

+ 4 - 1
renderer/shaders/phong_vertex.glsl

@@ -9,6 +9,7 @@ uniform mat3 NormalMatrix;
 uniform mat4 MVP;
 
 #include <material>
+#include <morphtarget_vertex_declaration>
 
 // Output variables for Fragment shader
 out vec4 Position;
@@ -36,7 +37,9 @@ void main() {
     }
 #endif
     FragTexcoord = texcoord;
+    vec3 vPosition = VertexPosition;
+    #include <morphtarget_vertex> [MORPHTARGETS]
 
-    gl_Position = MVP * vec4(VertexPosition, 1.0);
+    gl_Position = MVP * vec4(vPosition, 1.0);
 }
 

+ 6 - 1
renderer/shaders/physical_vertex.glsl

@@ -9,6 +9,8 @@ uniform mat4 ModelViewMatrix;
 uniform mat3 NormalMatrix;
 uniform mat4 MVP;
 
+#include <morphtarget_vertex_declaration>
+
 // Output variables for Fragment shader
 out vec3 Position;
 out vec3 Normal;
@@ -36,7 +38,10 @@ void main() {
     // #endif
     FragTexcoord = texcoord;
 
-    gl_Position = MVP * vec4(VertexPosition, 1.0);
+    vec3 vPosition = VertexPosition;
+    #include <morphtarget_vertex> [MORPHTARGETS]
+
+    gl_Position = MVP * vec4(vPosition, 1.0);
 }
 
 

+ 40 - 43
renderer/shaders/sources.go

@@ -112,37 +112,24 @@ uniform vec3 Material[6];
 `
 
 const include_morphtarget_vertex_source = `#ifdef MORPHTARGETS
-	uniform float morphTargetInfluences[NUM_MORPH_INFLUENCERS];
+	vPosition += (MorphPosition{i} - VertexPosition) * morphTargetInfluences[{i}];
+//    vPosition = MorphPosition1;
+  #ifdef MORPHTARGETS_NORMAL
+	vNormal += (MorphNormal{i} - VertexNormal) * morphTargetInfluences[{i}];
+  #endif
 #endif
+`
 
-#ifdef MORPHTARGETS
-	attribute vec3 MorphPosition{X};
-
-	#ifdef MORPHTARGETS_NORMAL
-	attribute vec3 MorphNormal{X};
-	#endif
-#endif
-
-#ifdef MORPHTARGETS
-	positionUpdated += (MorphPosition{X} - VertexPosition) * morphTargetInfluences[{X}];
-
-	#ifdef MORPHTARGETS_NORMAL
-	normalUpdated += (MorphNormal{X} - VertexNormal) * morphTargetInfluences[{X}];
-	#endif
+const include_morphtarget_vertex_declaration_source = `#ifdef MORPHTARGETS
+	uniform float morphTargetInfluences[MORPHTARGETS];
+	#include <morphtarget_vertex_declaration2> [MORPHTARGETS]
 #endif
+`
 
-
-//
-// Vertex attributes
-//
-layout(location = 0) in  vec3  VertexPosition;
-layout(location = 1) in  vec3  VertexNormal;
-layout(location = 2) in  vec3  VertexColor;
-layout(location = 3) in  vec2  VertexTexcoord;
-layout(location = 4) in  float VertexDistance;
-layout(location = 5) in  vec4  VertexTexoffsets;
-
-
+const include_morphtarget_vertex_declaration2_source = `	in vec3 MorphPosition{i};
+  #ifdef MORPHTARGETS_NORMAL
+	in vec3 MorphNormal{i};
+  #endif
 `
 
 const include_phong_model_source = `/***
@@ -259,8 +246,6 @@ void phongModel(vec4 position, vec3 normal, vec3 camDir, vec3 matAmbient, vec3 m
     ambdiff = ambientTotal + MatEmissiveColor + diffuseTotal;
     spec = specularTotal;
 }
-
-
 `
 
 const basic_fragment_source = `//
@@ -514,6 +499,7 @@ uniform mat3 NormalMatrix;
 uniform mat4 MVP;
 
 #include <material>
+#include <morphtarget_vertex_declaration>
 
 // Output variables for Fragment shader
 out vec4 Position;
@@ -541,8 +527,10 @@ void main() {
     }
 #endif
     FragTexcoord = texcoord;
+    vec3 vPosition = VertexPosition;
+    #include <morphtarget_vertex> [MORPHTARGETS]
 
-    gl_Position = MVP * vec4(VertexPosition, 1.0);
+    gl_Position = MVP * vec4(vPosition, 1.0);
 }
 
 `
@@ -975,6 +963,8 @@ uniform mat4 ModelViewMatrix;
 uniform mat3 NormalMatrix;
 uniform mat4 MVP;
 
+#include <morphtarget_vertex_declaration>
+
 // Output variables for Fragment shader
 out vec3 Position;
 out vec3 Normal;
@@ -1002,7 +992,10 @@ void main() {
     // #endif
     FragTexcoord = texcoord;
 
-    gl_Position = MVP * vec4(VertexPosition, 1.0);
+    vec3 vPosition = VertexPosition;
+    #include <morphtarget_vertex> [MORPHTARGETS]
+
+    gl_Position = MVP * vec4(vPosition, 1.0);
 }
 
 
@@ -1212,7 +1205,7 @@ uniform mat4 MVP;
 #include <lights>
 #include <material>
 #include <phong_model>
-
+#include <morphtarget_vertex_declaration>
 
 // Outputs for the fragment shader.
 out vec3 ColorFrontAmbdiff;
@@ -1224,19 +1217,19 @@ out vec2 FragTexcoord;
 void main() {
 
     // Transform this vertex normal to camera coordinates.
-    vec3 normal = normalize(NormalMatrix * VertexNormal);
+    vec3 Normal = normalize(NormalMatrix * VertexNormal);
 
     // Calculate this vertex position in camera coordinates
-    vec4 position = ModelViewMatrix * vec4(VertexPosition, 1.0);
+    vec4 Position = ModelViewMatrix * vec4(VertexPosition, 1.0);
 
     // Calculate the direction vector from the vertex to the camera
     // The camera is at 0,0,0
-    vec3 camDir = normalize(-position.xyz);
+    vec3 camDir = normalize(-Position.xyz);
 
     // Calculates the vertex Ambient+Diffuse and Specular colors using the Phong model
     // for the front and back
-    phongModel(position,  normal, camDir, MatAmbientColor, MatDiffuseColor, ColorFrontAmbdiff, ColorFrontSpec);
-    phongModel(position, -normal, camDir, MatAmbientColor, MatDiffuseColor, ColorBackAmbdiff, ColorBackSpec);
+    phongModel(Position,  Normal, camDir, MatAmbientColor, MatDiffuseColor, ColorFrontAmbdiff, ColorFrontSpec);
+    phongModel(Position, -Normal, camDir, MatAmbientColor, MatDiffuseColor, ColorBackAmbdiff, ColorBackSpec);
 
     vec2 texcoord = VertexTexcoord;
 #if MAT_TEXTURES > 0
@@ -1246,8 +1239,10 @@ void main() {
     }
 #endif
     FragTexcoord = texcoord;
+    vec3 vPosition = VertexPosition;
+    #include <morphtarget_vertex> [MORPHTARGETS]
 
-    gl_Position = MVP * vec4(VertexPosition, 1.0);
+    gl_Position = MVP * vec4(vPosition, 1.0);
 }
 
 `
@@ -1255,11 +1250,13 @@ void main() {
 // Maps include name with its source code
 var includeMap = map[string]string{
 
-	"attributes":         include_attributes_source,
-	"lights":             include_lights_source,
-	"material":           include_material_source,
-	"morphtarget_vertex": include_morphtarget_vertex_source,
-	"phong_model":        include_phong_model_source,
+	"attributes":                      include_attributes_source,
+	"lights":                          include_lights_source,
+	"material":                        include_material_source,
+	"morphtarget_vertex":              include_morphtarget_vertex_source,
+	"morphtarget_vertex_declaration":  include_morphtarget_vertex_declaration_source,
+	"morphtarget_vertex_declaration2": include_morphtarget_vertex_declaration2_source,
+	"phong_model":                     include_phong_model_source,
 }
 
 // Maps shader name with its source code

+ 9 - 7
renderer/shaders/standard_vertex.glsl

@@ -11,7 +11,7 @@ uniform mat4 MVP;
 #include <lights>
 #include <material>
 #include <phong_model>
-
+#include <morphtarget_vertex_declaration>
 
 // Outputs for the fragment shader.
 out vec3 ColorFrontAmbdiff;
@@ -23,19 +23,19 @@ out vec2 FragTexcoord;
 void main() {
 
     // Transform this vertex normal to camera coordinates.
-    vec3 normal = normalize(NormalMatrix * VertexNormal);
+    vec3 Normal = normalize(NormalMatrix * VertexNormal);
 
     // Calculate this vertex position in camera coordinates
-    vec4 position = ModelViewMatrix * vec4(VertexPosition, 1.0);
+    vec4 Position = ModelViewMatrix * vec4(VertexPosition, 1.0);
 
     // Calculate the direction vector from the vertex to the camera
     // The camera is at 0,0,0
-    vec3 camDir = normalize(-position.xyz);
+    vec3 camDir = normalize(-Position.xyz);
 
     // Calculates the vertex Ambient+Diffuse and Specular colors using the Phong model
     // for the front and back
-    phongModel(position,  normal, camDir, MatAmbientColor, MatDiffuseColor, ColorFrontAmbdiff, ColorFrontSpec);
-    phongModel(position, -normal, camDir, MatAmbientColor, MatDiffuseColor, ColorBackAmbdiff, ColorBackSpec);
+    phongModel(Position,  Normal, camDir, MatAmbientColor, MatDiffuseColor, ColorFrontAmbdiff, ColorFrontSpec);
+    phongModel(Position, -Normal, camDir, MatAmbientColor, MatDiffuseColor, ColorBackAmbdiff, ColorBackSpec);
 
     vec2 texcoord = VertexTexcoord;
 #if MAT_TEXTURES > 0
@@ -45,7 +45,9 @@ void main() {
     }
 #endif
     FragTexcoord = texcoord;
+    vec3 vPosition = VertexPosition;
+    #include <morphtarget_vertex> [MORPHTARGETS]
 
-    gl_Position = MVP * vec4(VertexPosition, 1.0);
+    gl_Position = MVP * vec4(vPosition, 1.0);
 }
 

+ 63 - 26
renderer/shaman.go

@@ -15,12 +15,15 @@ import (
 	"strconv"
 )
 
-// Regular expression to parse #include <name> directive
+const GLSL_VERSION = "330 core"
+
+// Regular expression to parse #include <name> [quantity] directive
 var rexInclude *regexp.Regexp
+const indexParameter = "{i}"
 
 func init() {
 
-	rexInclude = regexp.MustCompile(`#include\s+<(.*)>`) // TODO indexParameter
+	rexInclude = regexp.MustCompile(`#include\s+<(.*)>\s*(?:\[(.*)]|)`)
 }
 
 // ShaderSpecs describes the specification of a compiled shader program
@@ -115,7 +118,7 @@ func (sm *Shaman) AddProgram(name, vertexName, fragName string, others ...string
 	}
 }
 
-// SetProgram set the shader program to satisfy the specified specs.
+// SetProgram sets the shader program to satisfy the specified specs.
 // Returns an indication if the current shader has changed and a possible error
 // when creating a new shader program.
 // Receives a copy of the specs because it changes the fields which specify the
@@ -177,14 +180,13 @@ func (sm *Shaman) GenProgram(specs *ShaderSpecs) (*gls.Program, error) {
 
 	// Sets the defines map
 	defines := map[string]string{}
-	defines["GLSL_VERSION"] = "330 core"
 	defines["AMB_LIGHTS"] = strconv.Itoa(specs.AmbientLightsMax)
 	defines["DIR_LIGHTS"] = strconv.Itoa(specs.DirLightsMax)
 	defines["POINT_LIGHTS"] = strconv.Itoa(specs.PointLightsMax)
 	defines["SPOT_LIGHTS"] = strconv.Itoa(specs.SpotLightsMax)
 	defines["MAT_TEXTURES"] = strconv.Itoa(specs.MatTexturesMax)
 
-	// Adds additional material defines from the specs parameter
+	// Adds additional material and geometry defines from the specs parameter
 	for name, value := range specs.Defines {
 		defines[name] = value
 	}
@@ -194,7 +196,7 @@ func (sm *Shaman) GenProgram(specs *ShaderSpecs) (*gls.Program, error) {
 	if !ok {
 		return nil, fmt.Errorf("Vertex shader:%s not found", progInfo.Vertex)
 	}
-	// Preprocess vertex shader source
+	// Pre-process vertex shader source
 	vertexSource, err := sm.preprocess(vertexSource, defines)
 	if err != nil {
 		return nil, err
@@ -206,7 +208,7 @@ func (sm *Shaman) GenProgram(specs *ShaderSpecs) (*gls.Program, error) {
 	if err != nil {
 		return nil, fmt.Errorf("Fragment shader:%s not found", progInfo.Fragment)
 	}
-	// Preprocess fragment shader source
+	// Pre-process fragment shader source
 	fragSource, err = sm.preprocess(fragSource, defines)
 	if err != nil {
 		return nil, err
@@ -221,7 +223,7 @@ func (sm *Shaman) GenProgram(specs *ShaderSpecs) (*gls.Program, error) {
 		if !ok {
 			return nil, fmt.Errorf("Geometry shader:%s not found", progInfo.Geometry)
 		}
-		// Preprocess geometry shader source
+		// Pre-process geometry shader source
 		geomSource, err = sm.preprocess(geomSource, defines)
 		if err != nil {
 			return nil, err
@@ -243,48 +245,83 @@ func (sm *Shaman) GenProgram(specs *ShaderSpecs) (*gls.Program, error) {
 	return prog, nil
 }
 
-// preprocess preprocesses the specified source prefixing it with optional defines directives
-// contained in "defines" parameter and replaces '#include <name>' directives
-// by the respective source code of include chunk of the specified name.
-// The included "files" are also processed recursively.
+
 func (sm *Shaman) preprocess(source string, defines map[string]string) (string, error) {
 
-	// If defines map supplied, generates prefix with glsl version directive first,
-	// followed by "#define directives"
+	// If defines map supplied, generate prefix with glsl version directive first,
+	// followed by "#define" directives
 	var prefix = ""
-	if defines != nil {
-		prefix = fmt.Sprintf("#version %s\n", defines["GLSL_VERSION"])
+	if defines != nil { // This is only true for the outer call
+		prefix = fmt.Sprintf("#version %s\n", GLSL_VERSION)
 		for name, value := range defines {
-			if name == "GLSL_VERSION" {
-				continue
-			}
 			prefix = prefix + fmt.Sprintf("#define %s %s\n", name, value)
 		}
 	}
 
+	return sm.processIncludes(prefix + source, defines)
+}
+
+
+// preprocess preprocesses the specified source prefixing it with optional defines directives
+// contained in "defines" parameter and replaces '#include <name>' directives
+// by the respective source code of include chunk of the specified name.
+// The included "files" are also processed recursively.
+func (sm *Shaman) processIncludes(source string, defines map[string]string) (string, error) {
+
 	// Find all string submatches for the "#include <name>" directive
 	matches := rexInclude.FindAllStringSubmatch(source, 100)
 	if len(matches) == 0 {
-		return prefix + source, nil
+		return source, nil
 	}
 
 	// For each directive found, replace the name by the respective include chunk source code
-	var newSource = source
+	//var newSource = source
 	for _, m := range matches {
+		incFullMatch := m[0]
+		incName := m[1]
+		incQuantityVariable := m[2]
+
 		// Get the source of the include chunk with the match <name>
-		incSource := sm.includes[m[1]]
+		incSource := sm.includes[incName]
 		if len(incSource) == 0 {
-			return "", fmt.Errorf("Include:[%s] not found", m[1])
+			return "", fmt.Errorf("Include:[%s] not found", incName)
 		}
+
 		// Preprocess the include chunk source code
-		incSource, err := sm.preprocess(incSource, nil)
+		incSource, err := sm.processIncludes(incSource, defines)
 		if err != nil {
 			return "", err
 		}
+
+		// Skip line
+		incSource = "\n" + incSource
+
+		// Process include quantity variable if provided
+		if incQuantityVariable != "" {
+			incQuantityString, defined := defines[incQuantityVariable]
+			if defined { // Only process #include if quantity variable is defined
+				incQuantity, err := strconv.Atoi(incQuantityString)
+				if err != nil {
+					return "", err
+				}
+				// Check for iterated includes and populate index parameter
+				if incQuantity > 0 {
+					repeatedIncludeSource := ""
+					for i := 0; i < incQuantity; i++ {
+						// Replace all occurrences of the index parameter with the current index i.
+						repeatedIncludeSource += strings.Replace(incSource, indexParameter, strconv.Itoa(i), -1)
+					}
+					incSource = repeatedIncludeSource
+				}
+			} else {
+				incSource = ""
+			}
+		}
+
 		// Replace all occurrences of the include directive with its processed source code
-		newSource = strings.Replace(newSource, m[0], incSource, -1)
+		source = strings.Replace(source, incFullMatch, incSource, -1)
 	}
-	return prefix + newSource, nil
+	return source, nil
 }
 
 // copy copies other spec into this